mirror of
https://codeberg.org/artfulrobot/contactcats.git
synced 2025-06-25 09:48:05 +02:00
lots
This commit is contained in:
parent
3b393be4f4
commit
0b96026602
17 changed files with 458 additions and 38 deletions
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
* Generated from contactcats/xml/schema/CRM/Contactcats/ContactCategory.xml
|
* Generated from contactcats/xml/schema/CRM/Contactcats/ContactCategory.xml
|
||||||
* DO NOT EDIT. Generated by CRM_Core_CodeGen
|
* DO NOT EDIT. Generated by CRM_Core_CodeGen
|
||||||
* (GenCodeChecksum:c8054b52817309c4eb67221623e1c29e)
|
* (GenCodeChecksum:01a6344ef3d86c080e4362dafa008d13)
|
||||||
*/
|
*/
|
||||||
use CRM_Contactcats_ExtensionUtil as E;
|
use CRM_Contactcats_ExtensionUtil as E;
|
||||||
|
|
||||||
|
@ -97,8 +97,10 @@ class CRM_Contactcats_DAO_ContactCategory extends CRM_Core_DAO {
|
||||||
'entity' => 'ContactCategory',
|
'entity' => 'ContactCategory',
|
||||||
'bao' => 'CRM_Contactcats_DAO_ContactCategory',
|
'bao' => 'CRM_Contactcats_DAO_ContactCategory',
|
||||||
'localizable' => 0,
|
'localizable' => 0,
|
||||||
|
'FKClassName' => 'CRM_Contact_DAO_Contact',
|
||||||
'html' => [
|
'html' => [
|
||||||
'type' => 'Number',
|
'type' => 'EntityRef',
|
||||||
|
'label' => E::ts("Contact"),
|
||||||
],
|
],
|
||||||
'readonly' => TRUE,
|
'readonly' => TRUE,
|
||||||
'add' => NULL,
|
'add' => NULL,
|
||||||
|
@ -124,8 +126,8 @@ class CRM_Contactcats_DAO_ContactCategory extends CRM_Core_DAO {
|
||||||
'type' => 'Select',
|
'type' => 'Select',
|
||||||
],
|
],
|
||||||
'pseudoconstant' => [
|
'pseudoconstant' => [
|
||||||
'optionGroupName' => 'ContactCategories',
|
'optionGroupName' => 'contact_categories',
|
||||||
'optionEditPath' => 'civicrm/admin/options/ContactCategories',
|
'optionEditPath' => 'civicrm/admin/options/contact_categories',
|
||||||
],
|
],
|
||||||
'add' => NULL,
|
'add' => NULL,
|
||||||
],
|
],
|
||||||
|
@ -147,8 +149,8 @@ class CRM_Contactcats_DAO_ContactCategory extends CRM_Core_DAO {
|
||||||
'bao' => 'CRM_Contactcats_DAO_ContactCategory',
|
'bao' => 'CRM_Contactcats_DAO_ContactCategory',
|
||||||
'localizable' => 0,
|
'localizable' => 0,
|
||||||
'pseudoconstant' => [
|
'pseudoconstant' => [
|
||||||
'optionGroupName' => 'ContactCategories',
|
'optionGroupName' => 'contact_categories',
|
||||||
'optionEditPath' => 'civicrm/admin/options/ContactCategories',
|
'optionEditPath' => 'civicrm/admin/options/contact_categories',
|
||||||
],
|
],
|
||||||
'add' => NULL,
|
'add' => NULL,
|
||||||
],
|
],
|
||||||
|
|
17
CRM/Contactcats/Page/Settings.php
Normal file
17
CRM/Contactcats/Page/Settings.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
use CRM_Contactcats_ExtensionUtil as E;
|
||||||
|
|
||||||
|
class CRM_Contactcats_Page_Settings extends CRM_Core_Page {
|
||||||
|
|
||||||
|
public function run() {
|
||||||
|
// Example: Set the page-title dynamically; alternatively, declare a static title in xml/Menu/*.xml
|
||||||
|
CRM_Utils_System::setTitle(E::ts('Contact Category Settings'));
|
||||||
|
|
||||||
|
// Example: Assign a variable for use in a template
|
||||||
|
// $this->assign('currentTime', date('Y-m-d H:i:s'));
|
||||||
|
|
||||||
|
Civi::service('angularjs.loader')->addModules('crmContactcats');
|
||||||
|
parent::run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,9 +2,11 @@
|
||||||
namespace Civi\Api4\Action\ContactCategory;
|
namespace Civi\Api4\Action\ContactCategory;
|
||||||
|
|
||||||
use Civi;
|
use Civi;
|
||||||
use Civi\Api4\Generic\AbstractAction;
|
use Civi\Api4\Activity;
|
||||||
use Civi\Api4\Generic\Result;
|
use Civi\Api4\Generic\Result;
|
||||||
use Civi\Api4\Group;
|
use Civi\Api4\Group;
|
||||||
|
use Civi\Api4\OptionValue;
|
||||||
|
use Civi\Api4\Setting;
|
||||||
use CRM_Core_DAO;
|
use CRM_Core_DAO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,56 +14,120 @@ use CRM_Core_DAO;
|
||||||
*
|
*
|
||||||
* @see \Civi\Api4\Generic\AbstractAction
|
* @see \Civi\Api4\Generic\AbstractAction
|
||||||
*
|
*
|
||||||
* @package ContactCategory
|
|
||||||
*/
|
*/
|
||||||
class Sync extends \Civi\Api4\Generic\AbstractAction {
|
class Sync extends \Civi\Api4\Generic\AbstractAction {
|
||||||
|
|
||||||
public function _run(Result $result) {
|
public function _run(Result $result) {
|
||||||
$map = [
|
$settings = Civi::settings()->get('contact_categories');
|
||||||
['groupID' => 5, 'name' => 'Nice', 'categoryID' => 2],
|
$settings = Setting::get(FALSE)
|
||||||
];
|
->addSelect('contact_categories')
|
||||||
|
->execute()->first()['value'] ?? NULL;
|
||||||
|
if (empty($settings['groupIDs'])) {
|
||||||
|
throw new \API_Exception('Unconfigured');
|
||||||
|
}
|
||||||
|
if ($settings['updateAfter'] > time()) {
|
||||||
|
// not needed yet.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load category names
|
||||||
|
$catNames = OptionValue::get(FALSE)
|
||||||
|
->addWhere('option_group_id:name', '=', 'contact_categories')
|
||||||
|
->addSelect('value', 'label')
|
||||||
|
->execute()->indexBy('value')->column('label');
|
||||||
|
|
||||||
|
$groups = Group::get(FALSE)
|
||||||
|
->addWhere('id', 'IN', $settings['groupIDs'])
|
||||||
|
->execute()->indexBy('id')->getArrayCopy();
|
||||||
|
|
||||||
|
$smartGroups = [];
|
||||||
|
foreach ($groups as $groupID => $group) {
|
||||||
|
if (!empty($group['saved_search_id'])) {
|
||||||
|
$smartGroups[] = $groupID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($smartGroups) {
|
||||||
|
\CRM_Contact_BAO_GroupContactCache::loadAll($smartGroups);
|
||||||
|
}
|
||||||
|
|
||||||
// clear out temp space.
|
// clear out temp space.
|
||||||
CRM_Core_DAO::executeQuery("UPDATE civicrm_contactcategory SET next_category = 1;");
|
CRM_Core_DAO::executeQuery("UPDATE civicrm_contact_category SET next_category = 0;");
|
||||||
// ensure we have all our contacts covered.
|
// ensure we have all our contacts covered.
|
||||||
CRM_Core_DAO::executeQuery(<<<SQL
|
CRM_Core_DAO::executeQuery(<<<SQL
|
||||||
INSERT IGNORE INTO civicrm_contactcategory
|
INSERT IGNORE INTO civicrm_contact_category
|
||||||
SELECT id, 1 as category, 2 next_category
|
SELECT id, 0 as category, 0 next_category
|
||||||
FROM civicrm_contact
|
FROM civicrm_contact
|
||||||
SQL);
|
SQL);
|
||||||
|
|
||||||
foreach ($map as $row) {
|
foreach ($settings['groupIDs'] as $groupID) {
|
||||||
// Get contacts in group.
|
if ($groupID == 0) {
|
||||||
$cat = (int) $row['categoryID'];
|
// The default 'group' isn't a ... group.
|
||||||
$row['groupID'] = (int) $row['groupID'];
|
|
||||||
|
|
||||||
$group = Group::get(FALSE)->addWhere('id', '=', $row['groupID'])->execute()->first();
|
|
||||||
if (!$group) {
|
|
||||||
Civi::warning("Group $row[groupID] used for category $row[name] no longer exists.");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$isSmart = !empty($group['saved_search_id']);
|
// Get contacts in group.
|
||||||
|
if (!$groups[$groupID]) {
|
||||||
|
Civi::log()->warning("Group $groupID no longer exists.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isSmart = !empty($groups[$groupID]['saved_search_id']);
|
||||||
if (!$isSmart) {
|
if (!$isSmart) {
|
||||||
$sql = <<<SQL
|
$sql = <<<SQL
|
||||||
UPDATE civicrm_contactcategory cc
|
UPDATE civicrm_contact_category cc
|
||||||
INNER JOIN civicrm_group_contact gc ON gc.contact_id = cc.id AND gc.status = 'Added' AND group_id = $row[groupID]
|
INNER JOIN civicrm_group_contact gc ON gc.contact_id = cc.id AND gc.status = 'Added' AND group_id = $groupID
|
||||||
SET next_category = $cat
|
SET next_category = $groupID
|
||||||
WHERE next_category = 1
|
WHERE next_category = 0
|
||||||
SQL;
|
SQL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// @todo refresh cache
|
// @todo refresh cache
|
||||||
$sql = <<<SQL
|
$sql = <<<SQL
|
||||||
UPDATE civicrm_contactcategory cc
|
UPDATE civicrm_contact_category cc
|
||||||
INNER JOIN civicrm_group_contact_cache gcc ON gcc.contact_id = cc.id AND group_id = $row[groupID]
|
INNER JOIN civicrm_group_contact_cache gcc ON gcc.contact_id = cc.id AND group_id = $groupID
|
||||||
SET next_category = $cat
|
SET next_category = $groupID
|
||||||
WHERE next_category = 1
|
WHERE next_category = 0
|
||||||
SQL;
|
SQL;
|
||||||
}
|
}
|
||||||
CRM_Core_DAO::executeQuery($sql);
|
CRM_Core_DAO::executeQuery($sql);
|
||||||
}
|
}
|
||||||
|
$changes = CRM_Core_DAO::executeQuery(<<<SQL
|
||||||
|
SELECT id, category, next_category
|
||||||
|
FROM civicrm_contact_category
|
||||||
|
WHERE next_category <> category
|
||||||
|
ORDER BY category, next_category
|
||||||
|
SQL);
|
||||||
|
$lastChange = [0, 0];
|
||||||
|
$batch = [];
|
||||||
|
$n = 0;
|
||||||
|
$writeBatch = function() use (&$lastChange, &$batch, $catNames) {
|
||||||
|
if (empty($batch)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Create activity
|
||||||
|
Activity::create(FALSE)
|
||||||
|
->addValue('target_contact_id', $batch)
|
||||||
|
->addValue('activity_type_id:name', 'changed_contact_category')
|
||||||
|
->addValue('source_contact_id', \CRM_Core_BAO_Domain::getDomain()->contact_id)
|
||||||
|
->addValue('status_id:name', 'Completed')
|
||||||
|
->addValue('subject', $catNames[$lastChange[0] ?? 0] . ' → ' . $catNames[$lastChange[0] ?? 0])
|
||||||
|
->execute();
|
||||||
|
};
|
||||||
|
while ($changes->fetch()) {
|
||||||
|
$n++;
|
||||||
|
if ($lastChange[0] !== $changes->category || $lastChange[1] !== $changes->next_category) {
|
||||||
|
$writeBatch();
|
||||||
|
$lastChange = [$changes->category, $changes->next_category];
|
||||||
|
$batch = [];
|
||||||
|
}
|
||||||
|
$batch[] = $changes->id;
|
||||||
|
}
|
||||||
|
$writeBatch();
|
||||||
|
|
||||||
|
CRM_Core_DAO::executeQuery("UPDATE civicrm_contact_category
|
||||||
|
SET category = next_category WHERE category <> next_category");
|
||||||
|
|
||||||
|
$summary = CRM_Core_DAO::executeQuery("SELECT next_category, count(*) from civicrm_contact_category group by next_category")->fetchAll();
|
||||||
|
$summary['changes'] = $n;
|
||||||
|
$result->exchangeArray($summary);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ContactCategory extends Generic\DAOEntity {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function sync(): Sync {
|
public static function sync(): Sync {
|
||||||
return new Sync('ContactCategory', 'sync');
|
return new Sync('ContactCategory', 'sync');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
ang/crmContactcats.ang.php
Normal file
23
ang/crmContactcats.ang.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
// Angular module crmContactcats.
|
||||||
|
// @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_angularModules
|
||||||
|
return [
|
||||||
|
'js' => [
|
||||||
|
'ang/crmContactcats.js',
|
||||||
|
'ang/crmContactcats/*.js',
|
||||||
|
'ang/crmContactcats/*/*.js',
|
||||||
|
],
|
||||||
|
'css' => [
|
||||||
|
'ang/crmContactcats.css',
|
||||||
|
],
|
||||||
|
'partials' => [
|
||||||
|
'ang/crmContactcats',
|
||||||
|
],
|
||||||
|
'requires' => [
|
||||||
|
'crmUi',
|
||||||
|
'crmUtil',
|
||||||
|
'ngRoute',
|
||||||
|
'api4',
|
||||||
|
],
|
||||||
|
'settings' => [],
|
||||||
|
];
|
18
ang/crmContactcats.css
Normal file
18
ang/crmContactcats.css
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/* Add any CSS rules for Angular module "crmContactcats" */
|
||||||
|
crm-contact-category-settings ol {
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
crm-contact-category-settings ol li {
|
||||||
|
display: flex;
|
||||||
|
padding:0.5em 0;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
crm-contact-category-settings .name-input-wrapper {
|
||||||
|
flex: 0 1 29ch;
|
||||||
|
}
|
||||||
|
crm-contact-category-settings .hint {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
crm-contact-category-settings .name-input-wrapper:focus-within .hint {
|
||||||
|
display:block;
|
||||||
|
}
|
134
ang/crmContactcats.js
Normal file
134
ang/crmContactcats.js
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
(function(angular, $, _) {
|
||||||
|
// Declare a list of dependencies.
|
||||||
|
angular.module('crmContactcats', CRM.angRequires('crmContactcats'));
|
||||||
|
angular.module('crmContactcats').component('crmContactCategorySettings', {
|
||||||
|
templateUrl: '~/crmContactcats/crmContactCategorySettings.html',
|
||||||
|
bindings: {
|
||||||
|
// things listed here become properties on the controller using
|
||||||
|
// values from attributes. @ means a fixed string is passed
|
||||||
|
// e.g. <crm-reset-password token='foo' />
|
||||||
|
// token: '@',
|
||||||
|
// & is special. In the child, call ctrl.onMyAction with an
|
||||||
|
// Object whose keys provide parameter names for the code in the parent.
|
||||||
|
// onMyAction: '&',
|
||||||
|
// '<' means one-way binding (parent»child) and I think is what you are
|
||||||
|
// supposed to use for components.
|
||||||
|
// v: '<'
|
||||||
|
},
|
||||||
|
controller: function($scope, $timeout, crmApi4, $document) {
|
||||||
|
var ts = $scope.ts = CRM.ts(null),
|
||||||
|
ctrl = this;
|
||||||
|
|
||||||
|
// this.$onInit gets run after the this controller is called, and after the bindings have been applied.
|
||||||
|
this.$onInit = async function() {
|
||||||
|
ctrl.saved = false;
|
||||||
|
const various = await crmApi4({
|
||||||
|
settings: ['Setting', 'get', { select: ["contact_categories"] }, 0],
|
||||||
|
groups: ['Group', 'get', {
|
||||||
|
where: [['is_active', '=', 1], ['is_hidden', '=', 0]],
|
||||||
|
orderBy: { "name": "ASC" }
|
||||||
|
}],
|
||||||
|
cats: ['OptionValue', 'get', {
|
||||||
|
select: ['value', "label"],
|
||||||
|
where: [["option_group_id:name", "=", "contact_categories"]],
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
ctrl.catmap = [];
|
||||||
|
if (!various.settings.value || !various.settings.value.groupIDs) {
|
||||||
|
various.settings.value = {
|
||||||
|
groupIDs: ["0"],
|
||||||
|
updateAfter: 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
various.settings.value.groupIDs.forEach(groupID => {
|
||||||
|
let cat = various.cats.find(c => c.value == groupID);
|
||||||
|
ctrl.catmap.push({
|
||||||
|
groupID,
|
||||||
|
name: cat ? cat.label : '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log({ various, catmap: ctrl.catmap });
|
||||||
|
ctrl.groups = various.groups;
|
||||||
|
ctrl.nameKeydown = (keyEvt, idx) => {
|
||||||
|
if (keyEvt.key === 'ArrowUp' || keyEvt.key === 'ArrowDown') {
|
||||||
|
keyEvt.preventDefault();
|
||||||
|
keyEvt.stopPropagation();
|
||||||
|
if (keyEvt.key === 'ArrowUp' && idx > 0 && idx < ctrl.catmap.length - 1) {
|
||||||
|
ctrl.catmap.splice(idx, 0, ...ctrl.catmap.splice(idx - 1, 1));
|
||||||
|
console.log('up', { keyEvt });
|
||||||
|
$timeout(() => keyEvt.target.focus(), 10);
|
||||||
|
}
|
||||||
|
else if (keyEvt.key === 'ArrowDown' && idx < ctrl.catmap.length - 2) {
|
||||||
|
ctrl.catmap.splice(idx + 1, 0, ...ctrl.catmap.splice(idx, 1));
|
||||||
|
console.log('down', ctrl.catmap.map(e => e.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$scope.$digest();
|
||||||
|
ctrl.deleteRow = (idx) => {
|
||||||
|
ctrl.catmap.splice(idx, 1);
|
||||||
|
};
|
||||||
|
ctrl.getGroupsFor = (idx) => {
|
||||||
|
let groupsInUse = ctrl.catmap.map(c => c.groupID);
|
||||||
|
groupsInUse.splice(idx, 1);
|
||||||
|
return ctrl.groups.filter(g => !groupsInUse.includes(g.id.toString()));
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.save = async () => {
|
||||||
|
console.log("save");
|
||||||
|
// reconstruct everything.
|
||||||
|
|
||||||
|
const optValsRecords = [];
|
||||||
|
let isInvalid = false;
|
||||||
|
ctrl.catmap.forEach(r => {
|
||||||
|
if (!(r.name) || !r.groupID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do we have an option value for this group ID?
|
||||||
|
let c = various.cats.find(cat => cat.value == r.groupID);
|
||||||
|
if (c) {
|
||||||
|
if (c.label != r.name) {
|
||||||
|
optValsRecords.push({
|
||||||
|
id: c.id,
|
||||||
|
label: r.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
optValsRecords.push({
|
||||||
|
label: r.name,
|
||||||
|
value: r.groupID,
|
||||||
|
"option_group_id:name": "contact_categories"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log("optionValue updates", optValsRecords, ctrl.catmap);
|
||||||
|
const updates = {
|
||||||
|
saveSetting: ['Setting', 'set', {
|
||||||
|
values: {
|
||||||
|
contact_categories:
|
||||||
|
{
|
||||||
|
groupIDs: ctrl.catmap.map(i => i.groupID),
|
||||||
|
updateAfter: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
if (optValsRecords.length) {
|
||||||
|
updates.saveOptions = ['OptionValue', 'save', { records: optValsRecords }];
|
||||||
|
}
|
||||||
|
await crmApi4(updates);
|
||||||
|
console.log("saved", updates);
|
||||||
|
ctrl.saved = true;
|
||||||
|
$scope.$digest();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// this.$onChange = function(changes) {
|
||||||
|
// // changes is object keyed by name what '<' binding changed in
|
||||||
|
// // the parent (e.g. 'v'), and value is another obj with keys
|
||||||
|
// // something like previous, new, ...?...
|
||||||
|
// };
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(angular, CRM.$, CRM._);
|
35
ang/crmContactcats/crmContactCategorySettings.html
Normal file
35
ang/crmContactcats/crmContactCategorySettings.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<div ng-if="!$ctrl.catmap">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
<form ng-if="$ctrl.catmap" crm-ui-id-scope>
|
||||||
|
<button ng-click="$ctrl.catmap.unshift({groupID: '', name:''})"><i class="crm-i fa-plus"></i> Add category</button>
|
||||||
|
<ol class="crm-catmap">
|
||||||
|
<li ng-repeat="(idx, row) in $ctrl.catmap">
|
||||||
|
<select ng-if="row.groupID === '' || row.groupID > 0"
|
||||||
|
crm-ui-select="{placeholder:'Select group',allowClear:false}" ng-model="$ctrl.catmap[idx].groupID"
|
||||||
|
style="width: 18rem">
|
||||||
|
<option ng-repeat="(grpIdx, grp) in $ctrl.getGroupsFor(idx)" value="{{grp.id}}">{{grp.title}}</option>
|
||||||
|
</select>
|
||||||
|
<div ng-if="row.groupID !== '' && row.groupID == 0" style="width: 18rem;display: inline-block;">
|
||||||
|
Default
|
||||||
|
</div>
|
||||||
|
<div class=name-input-wrapper>
|
||||||
|
<label crm-ui-for="name{{idx}}">Label</label>
|
||||||
|
<input crm-ui-id="name{{idx}}" type=text ng-model="$ctrl.catmap[idx].name"
|
||||||
|
ng-keydown="$ctrl.nameKeydown($event, idx)" />
|
||||||
|
<div class="hint description">
|
||||||
|
{{ts('Use the Up/Down arrow keys to re-order')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button ng-click="$ctrl.deleteRow(idx)"><i class="fa-trash crm-i"></i> Delete</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>
|
||||||
|
<button ng-click="$ctrl.save()"><i class="crm-i fa-save"></i> Save</button>
|
||||||
|
</p>
|
||||||
|
<div ng-if="$ctrl.saved" class="help">Categories saved. Contacts will be updated shortly (when the next Scheduled
|
||||||
|
Job run
|
||||||
|
happens).</div>
|
||||||
|
</form>
|
|
@ -30,3 +30,20 @@ function contactcats_civicrm_install(): void {
|
||||||
function contactcats_civicrm_enable(): void {
|
function contactcats_civicrm_enable(): void {
|
||||||
_contactcats_civix_civicrm_enable();
|
_contactcats_civix_civicrm_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_civicrm_navigationMenu().
|
||||||
|
*
|
||||||
|
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_navigationMenu
|
||||||
|
*/
|
||||||
|
function contactcats_civicrm_navigationMenu(&$menu) {
|
||||||
|
_contactcats_civix_insert_navigation_menu($menu, 'Contacts', [
|
||||||
|
'label' => E::ts('Categories'),
|
||||||
|
'name' => 'contact_categories',
|
||||||
|
'url' => 'civicrm/admin/contactcategory',
|
||||||
|
'permission' => 'edit all contacts',
|
||||||
|
'operator' => 'OR',
|
||||||
|
'separator' => 0,
|
||||||
|
]);
|
||||||
|
_contactcats_civix_navigationMenu($menu);
|
||||||
|
}
|
||||||
|
|
2
info.xml
2
info.xml
|
@ -37,6 +37,8 @@
|
||||||
<mixin>smarty-v2@1.0.1</mixin>
|
<mixin>smarty-v2@1.0.1</mixin>
|
||||||
<mixin>entity-types-php@1.0.0</mixin>
|
<mixin>entity-types-php@1.0.0</mixin>
|
||||||
<mixin>mgd-php@1.0.0</mixin>
|
<mixin>mgd-php@1.0.0</mixin>
|
||||||
|
<mixin>ang-php@1.0.0</mixin>
|
||||||
|
<mixin>menu-xml@1.0.0</mixin>
|
||||||
</mixins>
|
</mixins>
|
||||||
<upgrader>CRM_Contactcats_Upgrader</upgrader>
|
<upgrader>CRM_Contactcats_Upgrader</upgrader>
|
||||||
</extension>
|
</extension>
|
||||||
|
|
27
managed/activity-type-changed-cc.mgd.php
Normal file
27
managed/activity-type-changed-cc.mgd.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
use CRM_Contactcats_ExtensionUtil as E;
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name' => 'OptionValue_Changed_Contact_Category',
|
||||||
|
'entity' => 'OptionValue',
|
||||||
|
'cleanup' => 'unused',
|
||||||
|
'update' => 'unmodified',
|
||||||
|
'params' => [
|
||||||
|
'version' => 4,
|
||||||
|
'values' => [
|
||||||
|
'option_group_id.name' => 'activity_type',
|
||||||
|
'label' => E::ts('Changed Contact Category'),
|
||||||
|
'value' => '57',
|
||||||
|
'name' => 'changed_contact_category',
|
||||||
|
'weight' => 57,
|
||||||
|
'icon' => 'fa-tags',
|
||||||
|
],
|
||||||
|
'match' => [
|
||||||
|
'option_group_id',
|
||||||
|
'name',
|
||||||
|
'value',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
50
managed/options.mgd.php
Normal file
50
managed/options.mgd.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
use CRM_Contactcats_ExtensionUtil as E;
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name' => 'OptionGroup_contact_categories',
|
||||||
|
'entity' => 'OptionGroup',
|
||||||
|
'cleanup' => 'unused',
|
||||||
|
'update' => 'unmodified',
|
||||||
|
'params' => [
|
||||||
|
'version' => 4,
|
||||||
|
'values' => [
|
||||||
|
'name' => 'contact_categories',
|
||||||
|
'title' => E::ts('Contact Categories'),
|
||||||
|
'data_type' => 'Integer',
|
||||||
|
'is_reserved' => FALSE,
|
||||||
|
'is_active' => TRUE,
|
||||||
|
'option_value_fields' => [
|
||||||
|
'name',
|
||||||
|
'label',
|
||||||
|
'description',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'match' => [
|
||||||
|
'name',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'OptionGroup_contact_categories_OptionValue_Default',
|
||||||
|
'entity' => 'OptionValue',
|
||||||
|
'cleanup' => 'unused',
|
||||||
|
'update' => 'unmodified',
|
||||||
|
'params' => [
|
||||||
|
'version' => 4,
|
||||||
|
'values' => [
|
||||||
|
'option_group_id.name' => 'contact_categories',
|
||||||
|
'label' => E::ts('Default'),
|
||||||
|
'is_active' => TRUE,
|
||||||
|
'value' => '0',
|
||||||
|
'name' => 'default',
|
||||||
|
],
|
||||||
|
'match' => [
|
||||||
|
'option_group_id',
|
||||||
|
'name',
|
||||||
|
'value',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
15
settings/contactcategory.setting.php
Normal file
15
settings/contactcategory.setting.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'contact_categories' => [
|
||||||
|
'name' => 'contact_categories',
|
||||||
|
'title' => ts('Contact Category settings'),
|
||||||
|
'description' => ts('JSON encoded settings.'),
|
||||||
|
'group_name' => 'domain',
|
||||||
|
'type' => 'String',
|
||||||
|
'serialize' => CRM_Core_DAO::SERIALIZE_JSON,
|
||||||
|
'default' => FALSE,
|
||||||
|
'add' => '5.70',
|
||||||
|
'is_domain' => 1,
|
||||||
|
'is_contact' => 0,
|
||||||
|
],
|
||||||
|
];
|
|
@ -37,6 +37,7 @@ CREATE TABLE `civicrm_contact_category` (
|
||||||
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique ID, corresponds to contact id',
|
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique ID, corresponds to contact id',
|
||||||
`category` int unsigned NOT NULL DEFAULT 0,
|
`category` int unsigned NOT NULL DEFAULT 0,
|
||||||
`next_category` int unsigned NOT NULL DEFAULT 0,
|
`next_category` int unsigned NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`),
|
||||||
|
CONSTRAINT FK_civicrm_contact_category_id FOREIGN KEY (`id`) REFERENCES `civicrm_contact`(`id`) ON DELETE CASCADE
|
||||||
)
|
)
|
||||||
ENGINE=InnoDB;
|
ENGINE=InnoDB;
|
||||||
|
|
3
templates/CRM/Contactcats/Page/Settings.tpl
Normal file
3
templates/CRM/Contactcats/Page/Settings.tpl
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<crm-angular-js modules="crmContactcats">
|
||||||
|
<crm-contact-category-settings ></crm-contact-category-settings>
|
||||||
|
</crm-angular-js>
|
9
xml/Menu/contactcats.xml
Normal file
9
xml/Menu/contactcats.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<menu>
|
||||||
|
<item>
|
||||||
|
<path>civicrm/admin/contactcategory</path>
|
||||||
|
<page_callback>CRM_Contactcats_Page_Settings</page_callback>
|
||||||
|
<title>Settings</title>
|
||||||
|
<access_arguments>access CiviCRM</access_arguments>
|
||||||
|
</item>
|
||||||
|
</menu>
|
|
@ -13,7 +13,8 @@
|
||||||
<required>true</required>
|
<required>true</required>
|
||||||
<comment>Unique ID, corresponds to contact id</comment>
|
<comment>Unique ID, corresponds to contact id</comment>
|
||||||
<html>
|
<html>
|
||||||
<type>Number</type>
|
<type>EntityRef</type>
|
||||||
|
<label>Contact</label>
|
||||||
</html>
|
</html>
|
||||||
</field>
|
</field>
|
||||||
<primaryKey>
|
<primaryKey>
|
||||||
|
@ -21,7 +22,7 @@
|
||||||
<autoincrement>true</autoincrement>
|
<autoincrement>true</autoincrement>
|
||||||
</primaryKey>
|
</primaryKey>
|
||||||
<foreignKey>
|
<foreignKey>
|
||||||
<name>contact_id</name>
|
<name>id</name>
|
||||||
<table>civicrm_contact</table>
|
<table>civicrm_contact</table>
|
||||||
<key>id</key>
|
<key>id</key>
|
||||||
<onDelete>CASCADE</onDelete>
|
<onDelete>CASCADE</onDelete>
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
<required>true</required>
|
<required>true</required>
|
||||||
<default>0</default>
|
<default>0</default>
|
||||||
<pseudoconstant>
|
<pseudoconstant>
|
||||||
<optionGroupName>ContactCategories</optionGroupName>
|
<optionGroupName>contact_categories</optionGroupName>
|
||||||
</pseudoconstant>
|
</pseudoconstant>
|
||||||
<html>
|
<html>
|
||||||
<type>Select</type>
|
<type>Select</type>
|
||||||
|
@ -46,7 +47,7 @@
|
||||||
<required>true</required>
|
<required>true</required>
|
||||||
<default>0</default>
|
<default>0</default>
|
||||||
<pseudoconstant>
|
<pseudoconstant>
|
||||||
<optionGroupName>ContactCategories</optionGroupName>
|
<optionGroupName>contact_categories</optionGroupName>
|
||||||
</pseudoconstant>
|
</pseudoconstant>
|
||||||
</field>
|
</field>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue