Search Ext: Support robust joins in UI
[civicrm-core.git] / ext / search / ang / crmSearchAdmin.module.js
index c0e15b14537796f9c305e1b5834edc9277ef31d5..90d6570598de620835447396d6ea8a082aee10e2 100644 (file)
@@ -3,6 +3,7 @@
 
   // Shared between router and searchMeta service
   var searchEntity,
+    joinIndex,
     undefined;
 
   // Declare module and route/controller/services
@@ -25,7 +26,7 @@
                 'GROUP_CONCAT(display.name ORDER BY display.id) AS display_name',
                 'GROUP_CONCAT(display.label ORDER BY display.id) AS display_label',
                 'GROUP_CONCAT(display.type:icon ORDER BY display.id) AS display_icon',
-                'GROUP_CONCAT(group.title) AS groups'
+                'GROUP_CONCAT(DISTINCT group.title) AS groups'
               ],
               join: [['SearchDisplay AS display'], ['Group AS group']],
               where: [['api_entity', 'IS NOT NULL']],
@@ -36,6 +37,7 @@
       });
       $routeProvider.when('/create/:entity', {
         controller: 'searchCreate',
+        reloadOnSearch: false,
         template: '<crm-search-admin saved-search="$ctrl.savedSearch"></crm-search-admin>',
       });
       $routeProvider.when('/edit/:id', {
     })
 
     .factory('searchMeta', function() {
+      // JoinIndex lists each join by alias. It gets built once then cached.
+      function _getJoinIndex() {
+        if (!joinIndex) {
+          joinIndex = _.transform(CRM.crmSearchAdmin.joins, function(joinIndex, joins, base) {
+            _.each(joins, function(join) {
+              join.base = base;
+              joinIndex[join.alias] = join;
+            });
+          });
+        }
+        return joinIndex;
+      }
       function getEntity(entityName) {
         if (entityName) {
-          return _.find(CRM.vars.search.schema, {name: entityName});
+          return _.find(CRM.crmSearchAdmin.schema, {name: entityName});
         }
       }
+      function getJoin(fullNameOrAlias) {
+        var joinIndex = _getJoinIndex(),
+          alias = _.last(fullNameOrAlias.split(' AS '));
+        return joinIndex[alias];
+      }
       function getField(fieldName, entityName) {
         var dotSplit = fieldName.split('.'),
           joinEntity = dotSplit.length > 1 ? dotSplit[0] : null,
           name = _.last(dotSplit).split(':')[0],
+          join,
           field;
         // Custom fields contain a dot in their fieldname
         // If 3 segments, the first is the joinEntity and the last 2 are the custom field
           }
         }
         if (joinEntity) {
-          entityName = _.find(CRM.vars.search.links[entityName], {alias: joinEntity}).entity;
+          join = getJoin(joinEntity);
+          entityName = getJoin(joinEntity).entity;
         }
         field = _.find(getEntity(entityName).fields, {name: name});
+        if (!field && join && join.bridge) {
+          field = _.find(getEntity(join.bridge).fields, {name: name});
+        }
         if (field) {
           field.entity = entityName;
           return field;
         }
       }
+      function parseExpr(expr) {
+        var result = {fn: null, modifier: ''},
+          fieldName = expr,
+          bracketPos = expr.indexOf('(');
+        if (bracketPos >= 0) {
+          var parsed = expr.substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/);
+          fieldName = parsed[2];
+          result.fn = _.find(CRM.crmSearchAdmin.functions, {name: expr.substring(0, bracketPos)});
+          result.modifier = _.trim(parsed[1]);
+        }
+        result.field = expr ? getField(fieldName, searchEntity) : undefined;
+        if (result.field) {
+          var split = fieldName.split(':'),
+            prefixPos = split[0].lastIndexOf(result.field.name);
+          result.path = split[0];
+          result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : '';
+          result.suffix = !split[1] ? '' : ':' + split[1];
+        }
+        return result;
+      }
       return {
         getEntity: getEntity,
         getField: getField,
-        parseExpr: function(expr) {
-          var result = {fn: null, modifier: ''},
-            fieldName = expr,
-            bracketPos = expr.indexOf('(');
-          if (bracketPos >= 0) {
-            var parsed = expr.substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/);
-            fieldName = parsed[2];
-            result.fn = _.find(CRM.crmSearchAdmin.functions, {name: expr.substring(0, bracketPos)});
-            result.modifier = _.trim(parsed[1]);
-          }
-          result.field = expr ? getField(fieldName, searchEntity) : undefined;
-          if (result.field) {
-            var split = fieldName.split(':'),
-              prefixPos = split[0].lastIndexOf(result.field.name);
-            result.path = split[0];
-            result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : '';
-            result.suffix = !split[1] ? '' : ':' + split[1];
+        getJoin: getJoin,
+        parseExpr: parseExpr,
+        getDefaultLabel: function(col) {
+          var info = parseExpr(col),
+            label = info.field.label;
+          if (info.fn) {
+            label = '(' + info.fn.title + ') ' + label;
           }
-          return result;
+          return label;
         },
         // Find all possible search columns that could serve as contact_id for a smart group
         getSmartGroupColumns: function(api_entity, api_params) {