On March 26, 2019 we launched new MODX Forums. Please join us at the new MODX Community Forums.
Subscribe: RSS
  • I'm interested in adding validation logic to test for uniqueness of records depending on their template and a certain set of fields to test against. The records in question contain data for contacts of two types—individuals and organizations—each having typical contact fields set up as TVs. The validation needs to prompt for a confirmation when a similar (enough) record is found to that which is being added/updated. For instance, a manager might attempt to create a resource for an individual with the same name but different email address as an existing one. In this case, I'd like to trigger a confirmation dialog saying something like:
    "A record with similar information was found: [firstname] [lastname] [email]. If you're sure the record you're saving is unique, click OK. Otherwise, click cancel."

    What I've arrived at as a possible solution is to override the MODx.page.CreateResource and MODx.page.UpdateResource classes to add a handler to the save button to call a custom validation method. What I'm stuck on is figuring out the code needed to continue the normal process should the user press "OK." Here's an outline of the create override:

    (function() {
    	
    	var origGetButtons = MODx.page.CreateResource.prototype.getButtons;
    
    	Ext.override(MODx.page.CreateResource, {
    	    
    	    getButtons: function (cfg) {
    	    	var items = origGetButtons.apply(this, arguments);
    	    	items[0].handler = this.validateContact;
    	    	return items;
    	    },
    	    
    	    validateContact: function () {
    	    	// Ajax request to connector/processor with validation php, returning 'unique' or 'confirm' (with additional data)
    	    	if(unique){
    	    		// code to continue process???
    	    	} else {
    				Ext.Msg.confirm('Validation Message','The record you are saving may be a duplicate. Continue?',function(e) {
    	                if (e == 'yes') {
    	                    // code to continue process???
    	                } else {
    						// not sure if anything is needed here if staying on the current create/edit page
    	                }
    	            },this);
    			}
    	});
    })();
    


    For reference the MODx.page.CreateResource subclass (from modext/sections/resource/create.js):

    MODx.page.CreateResource = function(config) {
        config = config || {};
        Ext.applyIf(config,{
            url: MODx.config.connector_url
            ,formpanel: 'modx-panel-resource'
            ,id: 'modx-page-update-resource'
            ,which_editor: 'none'
            ,action: 'resource/create'
        	,buttons: this.getButtons(config)
            ,components: [{
                xtype: config.panelXType || 'modx-panel-resource'
                ,renderTo: config.panelRenderTo || 'modx-panel-resource-div'
                ,resource: 0
                ,record: config.record
                ,publish_document: config.publish_document
                ,show_tvs: config.show_tvs
                ,mode: config.mode
                ,url: config.url
            }]
        });
        MODx.page.CreateResource.superclass.constructor.call(this,config);
    };
    Ext.extend(MODx.page.CreateResource,MODx.Component,{
        getButtons: function(cfg) {
            var btns = [];
            if (cfg.canSave == 1) {
                btns.push({
                    process: 'resource/create'
                    ,reload: true
                    ,text: _('save')
                    ,id: 'modx-abtn-save'
                    ,cls:'primary-button'
                    ,method: 'remote'
                    ,keys: [{
                        key: MODx.config.keymap_save || 's'
                        ,ctrl: true
                    }]
                });
    
            }
            btns.push({
                text: _('cancel')
                ,id: 'modx-abtn-cancel'
            });
            btns.push({
                text: _('help_ex')
                ,id: 'modx-abtn-help'
                ,handler: MODx.loadHelpPane
            });
            return btns;
        }
    });
    Ext.reg('modx-page-resource-create',MODx.page.CreateResource);
    


    My sense is there's a simple solution here...any pointers are much appreciated!

    This question has been answered by smg6511v2. See the first response.

    • discuss.answer
      OK, after studying the MODext files for quite a while (the modx.component.js one in particular), the Extjs documentation, and Googling the heck out various topics related to Extjs 3.4, I've arrived at a solution that works nicely. Below are the updated bare bones basics of my override, which excludes the additional code to generate the field data to send and the requisite processing of the response from my validator. I mainly wanted to communicate the part that would not be documented elsewhere. I've made some notes in the code to give more clarity to why particular lines are the way they are.

      /*
          Note that XModify is my general-purpose js object for certain customizations. It's inserted OnDocFormRender 
          via a plugin [$modx->regClientStartupHTMLBlock($js)], and is registered before this override file is included
          [$modx->regClientStartupScript($file)]. The modeClass will either be 'CreateResource' or 'UpdateResource'
          depending upon the resource's $mode ('new' or 'upd'). That way, this override can handle both modes without
          duplicating what is mostly the same code for each.
      */
      Ext.override(MODx.page[XModify.config.modeClass], {
          /*
              Set up references to the original methods we want to incorporate
          */
          refMethods: {
              getButtons: MODx.page[XModify.config.modeClass].prototype.getButtons,
              handleClick: MODx.toolbar.ActionButtons.prototype.handleClick
          },
       
          getButtons: function (cfg) {
      
              // get buttons as originally specified
              var items = this.refMethods.getButtons.apply(this,arguments), 
                  idx_btn, 
                  idx_kmap
                  ;
      
              /* 
                  Make adjustment to save button to trigger validation method; assuming save button is always first in the array ...
                  there may be a more robust way to identify the save button
              */
              if(cfg.canSave == 1){
                  /*
                      Get the objects we want to modify by identifying their array indicies based on a specified key's value
                  */
                  idx_btn = items.map(function(o){return o.id;}).indexOf("modx-abtn-save");
                  idx_kmap = items[idx_btn].keys.map(function(o){return o.key;}).indexOf("s");
                  
                  items[idx_btn].text = "Validate and Save";
                  /*
                      As we are replacing the original handler, the original handler's method will need to 
                      be referenced and called from this override should the validation pass or is confirmed below
                  */
                  items[idx_btn].handler = this.validateContact;
                  /*
                      Replace the function triggered by this button's keymap. Note that itm is not the object we need
                      when validateContact gets called from the keymap, so itm is further tested/defined in validateContact's vars below.
                  */
                  items[idx_btn].keys[idx_kmap].fn = this.validateContact;
              }
              return items;
          },
          validateContact: function (itm,e) {
               
              var form = Ext.getCmp(this.formpanel).getForm(),
                  vals = form.getValues(),
                  itm = itm !== null && typeof itm === "object" ? itm : Ext.getCmp("modx-abtn-save"),
                  evt = e
                  ;
               
              MODx.Ajax.request({
                  url: XModify.config.url,
                  params: {
                      action: "mgr/resource/contact/validate"
                  },
                  listeners: {
                      "success": { fn: function(r) {
                          // Get confirmation from user if validation, in this case uniqueness of this record, is questionable
                          switch(r.isUnique){
      
                              case "yes": 
                                  this.handleClick(itm,evt);
                                  break;
      
                              case "confirm":
                                  Ext.Msg.confirm("Validation Message","The record you are saving may be a duplicate. Continue?",function(e) {
                                      if (e == "yes") {
                                          /*
                                              Pass the itm and evt objects in their current state to a method in this override which 
                                              applies the method of the original prototype. Note that "e" cannot be used here as at this point
                                              it contains the event data of this msg; we must pass the original event obj which was copied into
                                              a new variable "evt" earlier in this validation method
                                          */
                                          this.handleClick(itm,evt);
                                      } else {
                                          // optional logic if user presses no/cancel
                                      }
                                  },this);
                                  break;
      
                              case "no":
                                  Ext.Msg.show({
                                      title: "Validation Warning",
                                      msg: "This record duplicates information already present in another document <i>[doc info]</i>. Would you like to edit that record to change or add new information?",
                                      icon: Ext.MessageBox.WARNING,
                                      buttons: {
                                          yes: "Edit Existing", 
                                          cancel: true
                                      }
                                  });
                                  break;
                          }
                           
                      }, scope:this },
                      "failure": { fn: function(r) {
                          // optional logic performed if request fails
                      }, scope:this }
                  }
              });
               
          },
          handleClick: function(itm,e){
              this.refMethods.handleClick.apply(this,arguments);
          }
      });
      


      If anyone sees a better way of coding this, please let me know. What I've arrived at is the result of some reverse engineering, much experimentation and a whole lot of console.log() calls! Thanks, hope this is useful to someone smiley [ed. note: smg6511v2 last edited this post 5 months, 2 weeks ago.]
      • Thanks for posting that. smiley
          Did I help you? Buy me a beer
          Get my Book: MODX:The Official Guide
          MODX info for everyone: http://bobsguides.com/modx.html
          My MODX Extras
          Bob's Guides is now hosted at A2 MODX Hosting
        • Note that the necessary changes were made to the script above to ensure the validation method gets called when the keymap is triggered via the use of the keyboard shortcut(s) for save. Also, it now covers both create and update modes. Lastly, the button object's array index is programmatically identified instead of explicitly given. [ed. note: smg6511v2 last edited this post 5 months, 2 weeks ago.]