Merge pull request #22188 from totten/master-uninstall
authorTim Otten <totten@civicrm.org>
Wed, 1 Dec 2021 03:04:32 +0000 (19:04 -0800)
committerGitHub <noreply@github.com>
Wed, 1 Dec 2021 03:04:32 +0000 (19:04 -0800)
ManagedEntities - Fix permission error during uninstallation (regression-fix)

20 files changed:
CRM/Case/BAO/Query.php
CRM/Core/BAO/CustomGroup.php
CRM/Group/Form/Edit.php
ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
ext/search_kit/ang/crmSearchAdmin/crmSearchAdminDisplay.component.js
ext/search_kit/ang/crmSearchAdmin/displays/searchAdminDisplayTable.component.js
ext/search_kit/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html
ext/search_kit/ang/crmSearchDisplay/traits/searchDisplaySortableTrait.service.js
ext/search_kit/ang/crmSearchDisplayTable.ang.php
ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js
ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html
ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTableBody.html
ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTableLoading.html
ext/search_kit/css/crmSearchDisplay.css
templates/CRM/Activity/Form/Selector.tpl
templates/CRM/Case/Form/ActivityTab.tpl
templates/CRM/Case/Form/CaseFilter.tpl
templates/CRM/Dashlet/Page/Blog.tpl
templates/CRM/Group/Form/GroupsCommon.tpl
templates/CRM/Tag/Form/Edit.tpl

index c2855f7b944641a4228f465f081066212446f475..e56ec4663fa5b3eaf29ff2bd2e4d993fd59f3f49 100644 (file)
@@ -676,7 +676,8 @@ case_relation_type.id = case_relationship.relationship_type_id )";
    *
    * @param CRM_Case_Form_Search $form
    */
-  public static function buildSearchForm(&$form) {
+  public static function buildSearchForm(&$form): void {
+    $form->addOptionalQuickFormElement('upcoming');
     //validate case configuration.
     $configured = CRM_Case_BAO_Case::isCaseConfigured();
     $form->assign('notConfigured', !$configured['configured']);
index 0c48e87afe4c6f65c80f1de25b7e0b4a6f368738..8baa7f6bd7c92e62347eefbab51be995349ebb87 100644 (file)
@@ -1827,7 +1827,7 @@ SELECT IF( EXISTS(SELECT name FROM civicrm_contact_type WHERE name like %1), 1,
       $expectedProperties = ['options_per_line', 'help_pre', 'help_post'];
       // add field information
       foreach ($value['fields'] as $k => $properties) {
-        $properties = array_merge(array_fill_keys($expectedProperties, 'NULL'), $properties);
+        $properties = array_merge(array_fill_keys($expectedProperties, NULL), $properties);
         $properties['element_name'] = "custom_{$k}_-{$groupCount}";
         if (isset($properties['customValue']) &&
           !CRM_Utils_System::isNull($properties['customValue']) &&
index 0afebfb8108840ceb44b764a47f8ce36453ca163..6f76bdecb29a41b32ddb15bd49eb2c99e3b0d81f 100644 (file)
@@ -92,6 +92,8 @@ class CRM_Group_Form_Edit extends CRM_Core_Form {
    * Set up variables to build the form.
    */
   public function preProcess() {
+    $this->addOptionalQuickFormElement('parents');
+    $this->addExpectedSmartyVariable('parent_groups');
     $this->_id = $this->get('id');
     if ($this->_id) {
       $breadCrumb = array(
index d705319fbecd26719171f8ba6706abf907ae510a..d71beeddbf78ce4a147fa6d93248840782eb3bb2 100644 (file)
@@ -712,6 +712,11 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction {
    * @return array
    */
   protected function getOrderByFromSort() {
+    // Drag-sortable tables have a forced order
+    if (!empty($this->display['settings']['draggable'])) {
+      return [$this->display['settings']['draggable'] => 'ASC'];
+    }
+
     $defaultSort = $this->display['settings']['sort'] ?? [];
     $currentSort = $this->sort;
 
@@ -745,9 +750,13 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction {
     }, $apiParams['select']);
     $additions = [];
     // Add primary key field if actions are enabled
-    if (!empty($this->display['settings']['actions'])) {
+    if (!empty($this->display['settings']['actions']) || !empty($this->display['settings']['draggable'])) {
       $additions = CoreUtil::getInfoItem($this->savedSearch['api_entity'], 'primary_key');
     }
+    // Add draggable column (typically "weight")
+    if (!empty($this->display['settings']['draggable'])) {
+      $additions[] = $this->display['settings']['draggable'];
+    }
     // Add style conditions for the display
     foreach ($this->getCssRulesSelect($this->display['settings']['cssRules'] ?? []) as $addition) {
       $additions[] = $addition;
index e6d17dd5dc79c27ee209c4943ac23f22ff912858..94d4007ab3ffce8cd74f195974ef14e25521ddff 100644 (file)
@@ -34,8 +34,7 @@
     },
     controller: function($scope, $timeout, searchMeta) {
       var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
-        ctrl = this,
-        afforms;
+        ctrl = this;
 
       this.isSuperAdmin = CRM.checkPerm('all CiviCRM permissions and ACLs');
       this.aclBypassHelp = ts('Only users with "all CiviCRM permissions and ACLs" can disable permission checks.');
       // Checks if a column contains a sortable value
       // Must be a real sql expression (not a pseudo-field like `result_row_num`)
       this.canBeSortable = function(col) {
+        // Column-header sorting is incompatible with draggable sorting
+        if (ctrl.display.settings.draggable) {
+          return false;
+        }
         var expr = ctrl.getExprFromSelect(col.key),
           info = searchMeta.parseExpr(expr),
           arg = (info && info.args && _.findWhere(info.args, {type: 'field'})) || {};
index 55b4fbbd5fa86cb19ecadabab36137c44c11e2f6..b8cdfee5a5b37eb654810728a4cbd4082a7e93d5 100644 (file)
         }
       };
 
+      this.toggleDraggable = function() {
+        if (ctrl.display.settings.draggable) {
+          delete ctrl.display.settings.draggable;
+        } else {
+          ctrl.display.settings.sort = [];
+          ctrl.display.settings.draggable = searchMeta.getEntity(ctrl.apiEntity).order_by;
+        }
+      };
+
       this.$onInit = function () {
         if (!ctrl.display.settings) {
           ctrl.display.settings = _.extend({}, _.cloneDeep(CRM.crmSearchAdmin.defaultDisplay.settings), {columns: null});
@@ -42,6 +51,8 @@
         }
         // Displays created prior to 5.43 may not have this property
         ctrl.display.settings.classes = ctrl.display.settings.classes || [];
+        // Table can be draggable if the main entity is a SortableEntity.
+        ctrl.canBeDraggable = _.includes(searchMeta.getEntity(ctrl.apiEntity).type, 'SortableEntity');
         ctrl.parent.initColumns({label: true, sortable: true});
       };
 
index a3090f9203261d8ac07a97b87ef294639291bdad..4794ea0e409a2f3de6eac023fce7b039c2656267 100644 (file)
@@ -1,5 +1,13 @@
-<fieldset ng-include="'~/crmSearchAdmin/crmSearchAdminDisplaySort.html'"></fieldset>
+<fieldset ng-if="!$ctrl.display.settings.draggable" ng-include="'~/crmSearchAdmin/crmSearchAdminDisplaySort.html'"></fieldset>
 <fieldset>
+  <div ng-if="$ctrl.canBeDraggable" class="form-inline">
+    <div class="checkbox-inline form-control">
+      <label>
+        <input type="checkbox" ng-checked="!!$ctrl.display.settings.draggable" ng-click="$ctrl.toggleDraggable()">
+        <span>{{:: ts('Drag and drop sorting') }}</span>
+      </label>
+    </div>
+  </div>
   <div class="form-inline">
     <div class="checkbox-inline form-control">
       <label>
@@ -44,7 +52,7 @@
           <option value="text-right">{{:: ts('Right') }}</option>
         </select>
       </div>
-      <div class="form-inline" ng-if=":: $ctrl.parent.canBeSortable(col)">
+      <div class="form-inline" ng-if="$ctrl.parent.canBeSortable(col)">
         <label title="{{:: ts('Allow user to click on header to sort table by this column') }}">
           <input type="checkbox" ng-checked="col.sortable !== false" ng-click="col.sortable = col.sortable === false" >
           {{:: ts('Sortable Header') }}
index 403ddeab5398fffd752fc4684ed0053aca39a0ce..b671627c4af8d687058b5d1ccc0c4ce26e0c5927 100644 (file)
@@ -11,7 +11,7 @@
       sort: [],
 
       isSortable: function(col) {
-        return col.type === 'field' && col.sortable !== false;
+        return !this.settings.draggable && col.type === 'field' && col.sortable !== false;
       },
 
       getSort: function(col) {
index d010a2f97d6af24312033a5f6cff79a5b773a235..c52b7f3e173c15e13555ebc2f793b53313e9209e 100644 (file)
@@ -9,7 +9,7 @@ return [
     'ang/crmSearchDisplayTable',
   ],
   'basePages' => ['civicrm/search', 'civicrm/admin/search'],
-  'requires' => ['crmSearchDisplay', 'crmUi', 'crmSearchTasks', 'ui.bootstrap'],
+  'requires' => ['crmSearchDisplay', 'crmUi', 'crmSearchTasks', 'ui.bootstrap', 'ui.sortable'],
   'bundles' => ['bootstrap3'],
   'exports' => [
     'crm-search-display-table' => 'E',
index 096c033d959463dd9a94a836df405a93249fa4ce..d1c859ef49401100e632a6d5fff73e3982d10670 100644 (file)
       afFieldset: '?^^afFieldset'
     },
     templateUrl: '~/crmSearchDisplayTable/crmSearchDisplayTable.html',
-    controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplaySortableTrait) {
+    controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplaySortableTrait, crmApi4, crmStatus) {
       var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
         // Mix in traits to this controller
         ctrl = angular.extend(this, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplaySortableTrait);
 
+
       this.$onInit = function() {
         this.initializeDisplay($scope, $element);
+
+        if (ctrl.settings.draggable) {
+          ctrl.draggableOptions = {
+            containment: 'table',
+            direction: 'vertical',
+            handle: '.crm-draggable',
+            forcePlaceholderSize: true,
+            helper: function(e, ui) {
+              // Prevent table row width from changing during drag
+              ui.children().each(function() {
+                $(this).width($(this).width());
+              });
+              return ui;
+            },
+            stop: function(e, ui) {
+              $scope.$apply(function() {
+                var movedItem = ui.item.sortable.model,
+                  oldPosition = ui.item.sortable.index,
+                  newPosition = ctrl.results.indexOf(movedItem),
+                  displacement = newPosition < oldPosition ? -1 : 1,
+                  displacedItem = ctrl.results[newPosition - displacement],
+                  weightColumn = ctrl.settings.draggable,
+                  updateParams = {where: [['id', '=', movedItem.data.id]], values: {}};
+                if (newPosition > -1 && oldPosition !== newPosition) {
+                  updateParams.values[weightColumn] = displacedItem.data[weightColumn];
+                  ctrl.runSearch([[ctrl.apiEntity, 'update', updateParams]], {}, movedItem);
+                }
+              });
+            }
+          };
+        }
       };
 
     }
index b5e28566f74daaa788b7a9088c40e21d44d66af1..2731e1f056816a3ce263d4d5ae5dd6bdac7d7710 100644 (file)
@@ -6,16 +6,19 @@
   <table class="{{:: $ctrl.settings.classes.join(' ') }}">
     <thead>
       <tr>
-        <th class="crm-search-result-select" ng-if=":: $ctrl.settings.actions">
-          <input type="checkbox" ng-disabled="$ctrl.loading || !$ctrl.results.length" ng-checked="$ctrl.allRowsSelected" ng-click="$ctrl.selectAllRows()" >
+        <th class="crm-search-result-select" ng-if=":: $ctrl.settings.actions || $ctrl.settings.draggable">
+          <i ng-if=":: $ctrl.settings.draggable" class="crm-i fa-sort-amount-asc" title="{{:: ts('Drag columns to reposition') }}"></i>
+          <input type="checkbox" ng-if=":: $ctrl.settings.actions" ng-disabled="$ctrl.loading || !$ctrl.results.length" ng-checked="$ctrl.allRowsSelected" ng-click="$ctrl.selectAllRows()" >
         </th>
-        <th ng-repeat="col in $ctrl.settings.columns" ng-click="$ctrl.setSort(col, $event)" title="{{:: $ctrl.isSortable(col) ? ts('Click to sort results (shift-click to sort by multiple).') : '' }}">
+        <th ng-repeat="col in $ctrl.settings.columns" ng-click="$ctrl.setSort(col, $event)" class="{{:: $ctrl.isSortable(col) ? 'crm-sortable-col' : ''}}" title="{{:: $ctrl.isSortable(col) ? ts('Click to sort results (shift-click to sort by multiple).') : '' }}">
           <i ng-if=":: $ctrl.isSortable(col)" class="crm-i {{ $ctrl.getSort(col) }}"></i>
           <span>{{:: col.label }}</span>
         </th>
       </tr>
     </thead>
-    <tbody ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTable' + ($ctrl.loading ? 'Loading' : 'Body') + '.html'"></tbody>
+    <tbody ng-if="$ctrl.loading" ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTableLoading.html'"></tbody>
+    <tbody ng-if="!$ctrl.loading && !$ctrl.settings.draggable" ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTableBody.html'"></tbody>
+    <tbody ng-if="!$ctrl.loading && $ctrl.settings.draggable" ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTableBody.html'" ui-sortable="$ctrl.draggableOptions" ng-model="$ctrl.results"></tbody>
   </table>
   <div ng-include="'~/crmSearchDisplay/Pager.html'"></div>
 </div>
index f3f525aa6f1115c8330911cf5231bdcb0e6f12df..1bb55193a4f431cb46beef807b508842937716ee 100644 (file)
@@ -1,6 +1,9 @@
 <tr ng-repeat="(rowIndex, row) in $ctrl.results">
-  <td ng-if=":: $ctrl.settings.actions" class="{{:: row.cssClass }}">
-    <input type="checkbox" ng-checked="$ctrl.isRowSelected(row)" ng-click="$ctrl.selectRow(row)" ng-disabled="!!$ctrl.loadingAllRows">
+  <td ng-if=":: $ctrl.settings.actions || $ctrl.settings.draggable" class="{{:: row.cssClass }}">
+    <span ng-if=":: $ctrl.settings.draggable" class="crm-draggable" title="{{:: ts('Drag to reposition') }}">
+      <i class="crm-i fa-arrows-v"></i>
+    </span>
+    <input ng-if=":: $ctrl.settings.actions" type="checkbox" ng-checked="$ctrl.isRowSelected(row)" ng-click="$ctrl.selectRow(row)" ng-disabled="!!$ctrl.loadingAllRows">
   </td>
   <td ng-repeat="(colIndex, colData) in row.columns" ng-include="'~/crmSearchDisplay/colType/' + $ctrl.settings.columns[colIndex].type + '.html'" title="{{:: colData.title }}" class="{{:: row.cssClass }} {{:: colData.cssClass }}">
   </td>
index a3d01656ae73d8c25d98f45bc29e2f4581add756..35738b7edd6b4adae646a79839fc0fa105785d82 100644 (file)
@@ -1,7 +1,7 @@
 <!-- Placeholder table rows shown during ajax loading -->
 <tr ng-repeat="num in [1,2,3,4,5] track by $index">
-  <td ng-if=":: $ctrl.settings.actions">
-    <input type="checkbox" disabled>
+  <td ng-if=":: $ctrl.settings.actions || $ctrl.settings.draggable">
+    <input ng-if=":: $ctrl.settings.actions" type="checkbox" disabled>
   </td>
   <td ng-repeat="col in $ctrl.settings.columns">
     <div class="crm-search-loading-placeholder"></div>
index 631f3d74a008195a014aabd8efea63e66cd00128..a2c84a7a4597b9e1f5a652175f10a4fad865b0de 100644 (file)
@@ -1,5 +1,5 @@
 /* Sortable headers */
-#bootstrap-theme .crm-search-display th[ng-click] {
+#bootstrap-theme .crm-search-display th.crm-sortable-col {
   cursor: pointer;
 }
 #bootstrap-theme .crm-search-display th i.fa-sort-desc,
index 1422fb1f6f1d6ee8459626470cbc4b7d61df72e0..23989568317e9139269eff41494491d0e0571358 100644 (file)
        {/if}
        {foreach from=$columnHeaders item=header}
           <th scope="col">
-          {if isset($header.sort)}
+          {if $header.sort}
             {assign var='key' value=$header.sort}
             {if !empty($sort)}
               {$sort->_response.$key.link}
             {/if}
-          {elseif isset($header.name)}
+          {elseif $header.name}
             {$header.name}
           {/if}
           </th>
@@ -54,7 +54,7 @@
       {/if}
     </td>
 
-  <td>{if isset($row.activity_subject)}{$row.activity_subject|purify}{/if}</td>
+  <td>{$row.activity_subject|purify}</td>
 
     <td>
     {if !$row.source_contact_id}
@@ -67,7 +67,7 @@
     <td>
     {if $row.mailingId}
       <a href="{$row.mailingId}" title="{ts}View Mailing Report{/ts}">{$row.recipients}</a>
-    {elseif isset($row.recipients)}
+    {elseif $row.recipients}
       {$row.recipients}
     {elseif !$row.target_contact_name}
       <em>n/a</em>
index 36c5aa293b180eb13ac969caeec054a8d17808e2..2b778bbcc49471472994854a483a37a8f3a3de96 100644 (file)
@@ -9,7 +9,7 @@
 *}
 {*this template is used for activity accordion*}
 {assign var=caseid value=$caseID}
-{if isset($isForm) and $isForm}
+{if $isForm}
   <div class="crm-accordion-wrapper crm-case_activities-accordion  crm-case-activities-block">
     <div class="crm-accordion-header">
       {ts}Activities{/ts}
     {/foreach}
   </style>
 
-{if isset($isForm) and $isForm}
+{if $isForm}
     </div><!-- /.crm-accordion-body -->
   </div><!-- /.crm-accordion-wrapper -->
 {/if}
index 4b425c00e10e8bb768a088eeba706bf4184f41f1..c078bcee54e136c1dff29454dca56adb98a5a968 100644 (file)
@@ -21,7 +21,7 @@
           <td class="crm-contact-form-block-case_status_id crm-inline-edit-field">
             {$form.case_status_id.label}<br /> {$form.case_status_id.html}
           </td>
-          {if $accessAllCases && isset($form.upcoming)}
+          {if $accessAllCases && $form.upcoming}
             <td class="crm-case-dashboard-switch-view-buttons">
               <br/>
               {$form.upcoming.html}&nbsp;{$form.upcoming.label}
index 45021805988664e7ce273042d18c29bb50b44dc1..00d6ce4633547f92cf70ec057f5cade368c2b5d7 100644 (file)
@@ -52,7 +52,7 @@
         </div>
         <div class="crm-accordion-body">
           <div>{$article.description|smarty:nodefaults|purify}</div>
-          <p class="crm-news-feed-item-link"><a target="_blank" href="{$article.link|smarty:nodefaults|purify}" title="{$article.title|escape}"><i class="crm-i fa-external-link" aria-hidden="true"></i> {ts}read more{/ts}…</a></p>
+          <p class="crm-news-feed-item-link"><a target="_blank" href="{$article.link|smarty:nodefaults|purify}" title="{$article.title|smarty:nodefaults|escape}"><i class="crm-i fa-external-link" aria-hidden="true"></i> {ts}read more{/ts}…</a></p>
         </div>
       </div>
     {/foreach}
index 2be8d82ccdab14c6e2bf62d39b28c292bce9740f..0a78e42db5e5587ce9bbe48f4605d93766c94807 100644 (file)
@@ -8,9 +8,9 @@
  +--------------------------------------------------------------------+
 *}
 {*CRM-14190*}
-{if (isset($parent_groups) and $parent_groups|@count > 0) or !empty($form.parents.html)}
+{if $parent_groups|@count > 0 || !empty($form.parents.html)}
   <h3>{ts}Parent Groups{/ts} {help id="id-group-parent" file="CRM/Group/Page/Group.hlp"}</h3>
-  {if isset($parent_groups) and $parent_groups|@count > 0}
+  {if $parent_groups|@count > 0}
     <table class="form-layout-compressed">
       <tr>
         <td><label>{ts}Remove Parent?{/ts}</label></td>
index ed86047290267a6ad2d3288eb32af713cfc01cc7..f7cbada056f8ddd80cbcb807df2497da8bf0fc92 100644 (file)
           <td class="label">{$form.used_for.label}</td>
           <td>{$form.used_for.html} <br />
             <span class="description">
-              {* @TODO: I don't think is_parent is ever true because this form is never used for editing a tag itself, and you can't nest tagsets. And when used to add a new child tag, the used_for element doesn't exist. *}
-              {if !empty($is_parent)}{ts}You can change the types of records which this tag can be used for by editing the 'Parent' tag.{/ts}
-              {else}{ts}What types of record(s) can this tag be used for?{/ts}
-              {/if}
+              {ts}What types of record(s) can this tag be used for?{/ts}
             </span>
           </td>
         </tr>