new: [mailing-list] Added mailing list feature - WiP
parent
b9bcb14bea
commit
fe9fbe2e99
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Migrations\AbstractMigration;
|
||||
use Phinx\Db\Adapter\MysqlAdapter;
|
||||
|
||||
class MailingLists extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Change Method.
|
||||
*
|
||||
* More information on this method is available here:
|
||||
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||
* @return void
|
||||
*/
|
||||
|
||||
public $autoId = false; // turn off automatic `id` column create. We want it to be `int(10) unsigned`
|
||||
|
||||
|
||||
public function change()
|
||||
{
|
||||
$mailinglists = $this->table('mailing_lists', [
|
||||
'signed' => false,
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
]);
|
||||
$mailinglists
|
||||
->addColumn('id', 'integer', [
|
||||
'autoIncrement' => true,
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addPrimaryKey('id')
|
||||
->addColumn('uuid', 'uuid', [
|
||||
'default' => null,
|
||||
'null' => false,
|
||||
])
|
||||
->addColumn('name', 'string', [
|
||||
'default' => null,
|
||||
'null' => false,
|
||||
'limit' => 191,
|
||||
'comment' => 'The name of the mailing list',
|
||||
])
|
||||
->addColumn('recipients', 'string', [
|
||||
'default' => null,
|
||||
'null' => true,
|
||||
'limit' => 191,
|
||||
'comment' => 'Human-readable description of who the intended recipients.',
|
||||
])
|
||||
->addColumn('description', 'text', [
|
||||
'default' => null,
|
||||
'null' => true,
|
||||
'comment' => 'Additional description of the mailing list'
|
||||
])
|
||||
->addColumn('user_id', 'integer', [
|
||||
'default' => null,
|
||||
'null' => true,
|
||||
'signed' => false,
|
||||
'length' => 10,
|
||||
])
|
||||
->addColumn('active', 'boolean', [
|
||||
'default' => 0,
|
||||
'null' => false,
|
||||
])
|
||||
->addColumn('deleted', 'boolean', [
|
||||
'default' => 0,
|
||||
'null' => false,
|
||||
])
|
||||
->addColumn('created', 'datetime', [
|
||||
'default' => null,
|
||||
'null' => false,
|
||||
])
|
||||
->addColumn('modified', 'datetime', [
|
||||
'default' => null,
|
||||
'null' => false,
|
||||
]);
|
||||
|
||||
|
||||
$mailinglists->addForeignKey('user_id', 'users', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE']);
|
||||
|
||||
$mailinglists->addIndex(['uuid'], ['unique' => true])
|
||||
->addIndex('name')
|
||||
->addIndex('recipients')
|
||||
->addIndex('user_id')
|
||||
->addIndex('active')
|
||||
->addIndex('deleted')
|
||||
->addIndex('created')
|
||||
->addIndex('modified');
|
||||
|
||||
$mailinglists->create();
|
||||
|
||||
|
||||
$mailinglists_individuals = $this->table('mailing_lists_individuals', [
|
||||
'signed' => false,
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
]);
|
||||
|
||||
$mailinglists_individuals
|
||||
->addColumn('id', 'integer', [
|
||||
'autoIncrement' => true,
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addPrimaryKey('id')
|
||||
->addColumn('mailing_list_id', 'integer', [
|
||||
'default' => null,
|
||||
'null' => true,
|
||||
'signed' => false,
|
||||
'length' => 10,
|
||||
])
|
||||
->addColumn('individual_id', 'integer', [
|
||||
'default' => null,
|
||||
'null' => true,
|
||||
'signed' => false,
|
||||
'length' => 10,
|
||||
]);
|
||||
|
||||
$mailinglists_individuals->addIndex(['mailing_list_id', 'individual_id'], ['unique' => true]);
|
||||
|
||||
$mailinglists_individuals->create();
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ class TagHelper extends Helper
|
|||
'data-text-colour' => h($tag['text_colour']),
|
||||
];
|
||||
}, $options['allTags']) : [];
|
||||
$classes = ['tag-input', 'flex-grow-1'];
|
||||
$classes = ['select2-input', 'flex-grow-1'];
|
||||
$url = '';
|
||||
if (!empty($this->getConfig('editable'))) {
|
||||
$url = $this->Url->build([
|
||||
|
|
|
@ -23,7 +23,7 @@ function createTagPicker(clicked) {
|
|||
|
||||
const $clicked = $(clicked)
|
||||
const $container = $clicked.closest('.tag-container')
|
||||
const $select = $container.parent().find('select.tag-input').removeClass('d-none')
|
||||
const $select = $container.parent().find('select.select2-input').removeClass('d-none')
|
||||
closePicker($select, $container)
|
||||
const $pickerContainer = $('<div></div>').addClass(['picker-container', 'd-flex'])
|
||||
|
||||
|
@ -90,7 +90,7 @@ function refreshTagList(apiResult, $container) {
|
|||
}
|
||||
|
||||
function initSelect2Pickers() {
|
||||
$('select.tag-input').each(function() {
|
||||
$('select.select2-input').each(function() {
|
||||
if (!$(this).hasClass("select2-hidden-accessible")) {
|
||||
initSelect2Picker($(this))
|
||||
}
|
||||
|
|
|
@ -38,6 +38,11 @@ class Sidemenu {
|
|||
'label' => __('Sharing Groups'),
|
||||
'icon' => $this->iconTable['SharingGroups'],
|
||||
'url' => '/sharingGroups/index',
|
||||
],
|
||||
'MailingLists' => [
|
||||
'label' => __('Mailing Lists'),
|
||||
'icon' => $this->iconTable['MailingLists'],
|
||||
'url' => '/mailingLists/index',
|
||||
]
|
||||
],
|
||||
__('Synchronisation') => [
|
||||
|
|
|
@ -24,6 +24,7 @@ class NavigationComponent extends Component
|
|||
'Organisations' => 'building',
|
||||
'EncryptionKeys' => 'key',
|
||||
'SharingGroups' => 'user-friends',
|
||||
'MailingLists' => 'mail-bulk',
|
||||
'Broods' => 'network-wired',
|
||||
'Roles' => 'id-badge',
|
||||
'Users' => 'users',
|
||||
|
@ -158,6 +159,7 @@ class NavigationComponent extends Component
|
|||
'Tags',
|
||||
'LocalTools',
|
||||
'UserSettings',
|
||||
'MailingLists',
|
||||
];
|
||||
foreach ($CRUDControllers as $controller) {
|
||||
$bcf->setDefaultCRUDForModel($controller);
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Controller\AppController;
|
||||
use Cake\Utility\Inflector;
|
||||
use Cake\Utility\Hash;
|
||||
use Cake\Utility\Text;
|
||||
use \Cake\Database\Expression\QueryExpression;
|
||||
use Cake\Error\Debugger;
|
||||
|
||||
class MailingListsController extends AppController
|
||||
{
|
||||
public $filterFields = ['MailingLists.uuid', 'MailingLists.name', 'description', 'releasability'];
|
||||
public $quickFilterFields = ['MailingLists.uuid', ['MailingLists.name' => true], ['description' => true], ['releasability' => true]];
|
||||
public $containFields = ['Users', 'Individuals'];
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->CRUD->index([
|
||||
'contain' => $this->containFields,
|
||||
'filters' => $this->filterFields,
|
||||
'quickFilters' => $this->quickFilterFields
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$this->CRUD->add([
|
||||
'override' => [
|
||||
'user_id' => $this->ACL->getUser()['id']
|
||||
]
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$this->CRUD->view($id, [
|
||||
'contain' => $this->containFields
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function edit($id = false)
|
||||
{
|
||||
$this->CRUD->edit($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->CRUD->delete($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function listIndividuals($mailinglist_id)
|
||||
{
|
||||
$individuals = $this->MailingLists->get($mailinglist_id, [
|
||||
'contain' => 'Individuals'
|
||||
])->individuals;
|
||||
$params = $this->ParamHandler->harvestParams(['quickFilter']);
|
||||
if (!empty($params['quickFilter'])) {
|
||||
// foreach ($sharingGroup['sharing_group_orgs'] as $k => $org) {
|
||||
// if (strpos($org['name'], $params['quickFilter']) === false) {
|
||||
// unset($sharingGroup['sharing_group_orgs'][$k]);
|
||||
// }
|
||||
// }
|
||||
// $sharingGroup['sharing_group_orgs'] = array_values($sharingGroup['sharing_group_orgs']);
|
||||
}
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->viewData($individuals, 'json');
|
||||
}
|
||||
$this->set('mailing_list_id', $mailinglist_id);
|
||||
$this->set('individuals', $individuals);
|
||||
}
|
||||
|
||||
public function addIndividual($mailinglist_id)
|
||||
{
|
||||
$mailingList = $this->MailingLists->get($mailinglist_id, [
|
||||
'contain' => 'Individuals'
|
||||
]);
|
||||
$conditions = [];
|
||||
$dropdownData = [
|
||||
'individuals' => $this->MailingLists->Individuals->getTarget()->find('list', [
|
||||
'sort' => ['name' => 'asc'],
|
||||
'conditions' => $conditions
|
||||
])
|
||||
];
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$memberIDs = $this->request->getData()['individuals'];
|
||||
$members = $this->MailingLists->Individuals->getTarget()->find()->where([
|
||||
'id IN' => $memberIDs
|
||||
])->all()->toArray();
|
||||
$success = (bool)$this->MailingLists->Individuals->link($mailingList, $members);
|
||||
if ($success) {
|
||||
$message = __n('%s individual added to the mailing list.', '%s Individuals added to the mailing list.', count($members), count($members));
|
||||
$mailingList = $this->MailingLists->get($mailingList->id);
|
||||
} else {
|
||||
$message = __n('The individual could not be added to the mailing list.', 'The Individuals could not be added to the mailing list.', count($members));
|
||||
}
|
||||
$this->CRUD->setResponseForController('add_individuals', $success, $message, $mailingList, $mailingList->getErrors());
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('mailinglist_id', $mailinglist_id);
|
||||
$this->set('mailingList', $mailingList);
|
||||
}
|
||||
|
||||
public function removeIndividual($mailinglist_id, $individual_id=null)
|
||||
{
|
||||
$mailingList = $this->MailingLists->get($mailinglist_id, [
|
||||
'contain' => 'Individuals'
|
||||
]);
|
||||
$individual = [];
|
||||
if (!is_null($individual_id)) {
|
||||
$individual = $this->MailingLists->Individuals->get($individual_id);
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('delete')) {
|
||||
$success = false;
|
||||
if (!is_null($individual_id)) {
|
||||
$individual = $this->MailingLists->Individuals->get($individual_id);
|
||||
$success = (bool)$this->MailingLists->Individuals->unlink($mailingList, [$individual]);
|
||||
if ($success) {
|
||||
$message = __('Individual removed from the mailing list.');
|
||||
$mailingList = $this->MailingLists->get($mailingList->id);
|
||||
} else {
|
||||
$message = __n('Individual could not be removed from the mailing list.');
|
||||
}
|
||||
$this->CRUD->setResponseForController('remove_individuals', $success, $message, $mailingList, $mailingList->getErrors());
|
||||
} else {
|
||||
$params = $this->ParamHandler->harvestParams(['ids']);
|
||||
if (!empty($params['ids'])) {
|
||||
$params['ids'] = json_decode($params['ids']);
|
||||
}
|
||||
if (empty($params['ids'])) {
|
||||
throw new NotFoundException(__('Invalid {0}.', Inflector::singularize($this->MailingLists->Individuals->getAlias())));
|
||||
}
|
||||
$individuals = $this->MailingLists->Individuals->find()->where([
|
||||
'id IN' => array_map('intval', $params['ids'])
|
||||
])->all()->toArray();
|
||||
$unlinkSuccesses = 0;
|
||||
foreach ($individuals as $individual) {
|
||||
$success = (bool)$this->MailingLists->Individuals->unlink($mailingList, [$individual]);
|
||||
$results[] = $success;
|
||||
if ($success) {
|
||||
$unlinkSuccesses++;
|
||||
}
|
||||
}
|
||||
$mailingList = $this->MailingLists->get($mailingList->id);
|
||||
$success = $unlinkSuccesses == count($individuals);
|
||||
$message = __(
|
||||
'{0} {1} have been removed.',
|
||||
$unlinkSuccesses == count($individuals) ? __('All') : sprintf('%s / %s', $unlinkSuccesses, count($individuals)),
|
||||
Inflector::singularize($this->MailingLists->Individuals->getAlias())
|
||||
);
|
||||
$this->CRUD->setResponseForController('remove_individuals', $success, $message, $mailingList, []);
|
||||
}
|
||||
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
$this->set('mailinglist_id', $mailinglist_id);
|
||||
$this->set('mailingList', $mailingList);
|
||||
if (!empty($individual)) {
|
||||
$this->set('deletionText', __('Are you sure you want to remove `{0} ({1})` from the mailing list?', $individual->full_name, $individual->email));
|
||||
} else {
|
||||
$this->set('deletionText', __('Are you sure you want to remove multiples individuals from the mailing list?'));
|
||||
}
|
||||
$this->set('postLinkParameters', ['action' => 'removeIndividual', $mailinglist_id, $individual_id]);
|
||||
$this->viewBuilder()->setLayout('ajax');
|
||||
$this->render('/genericTemplates/delete');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use App\Model\Entity\AppModel;
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class MailingList extends AppModel
|
||||
{
|
||||
protected $_accessible = [
|
||||
'*' => true,
|
||||
'id' => false,
|
||||
'uuid' => false,
|
||||
'user_id' => false,
|
||||
];
|
||||
|
||||
protected $_accessibleOnNew = [
|
||||
'uuid' => true,
|
||||
'user_id' => true,
|
||||
];
|
||||
}
|
|
@ -36,6 +36,7 @@ class IndividualsTable extends AppTable
|
|||
$this->belongsToMany('Organisations', [
|
||||
'through' => 'Alignments',
|
||||
]);
|
||||
$this->belongsToMany('MailingLists');
|
||||
$this->setDisplayField('email');
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\ORM\Table;
|
||||
use Cake\Validation\Validator;
|
||||
use Cake\ORM\RulesChecker;
|
||||
use Cake\ORM\TableRegistry;
|
||||
|
||||
class MailingListsTable extends AppTable
|
||||
{
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
$this->addBehavior('UUID');
|
||||
$this->addBehavior('Timestamp');
|
||||
$this->belongsTo(
|
||||
'Users'
|
||||
);
|
||||
// $this->belongsToMany(
|
||||
// 'Individuals',
|
||||
// [
|
||||
// 'className' => 'Individuals',
|
||||
// 'foreignKey' => 'individual_id',
|
||||
// 'joinTable' => 'sgo',
|
||||
// 'targetForeignKey' => 'organisation_id'
|
||||
// ]
|
||||
// );
|
||||
|
||||
$this->belongsToMany('Individuals', [
|
||||
'joinTable' => 'mailing_lists_individuals',
|
||||
]);
|
||||
|
||||
$this->setDisplayField('name');
|
||||
}
|
||||
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->requirePresence(['name', 'releasability'], 'create');
|
||||
return $validator;
|
||||
}
|
||||
|
||||
public function buildRules(RulesChecker $rules): RulesChecker
|
||||
{
|
||||
return $rules;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => __('Mailing list are email distribution lists containing individuals.'),
|
||||
'model' => 'MailingLists',
|
||||
'fields' => [
|
||||
[
|
||||
'field' => 'name'
|
||||
],
|
||||
[
|
||||
'field' => 'uuid',
|
||||
'label' => 'UUID',
|
||||
'type' => 'uuid'
|
||||
],
|
||||
[
|
||||
'field' => 'releasability',
|
||||
'type' => 'textarea'
|
||||
],
|
||||
[
|
||||
'field' => 'description',
|
||||
'type' => 'textarea'
|
||||
],
|
||||
[
|
||||
'field' => 'active',
|
||||
'type' => 'checkbox',
|
||||
'default' => 1
|
||||
],
|
||||
],
|
||||
'submit' => [
|
||||
'action' => $this->request->getParam('action')
|
||||
]
|
||||
]
|
||||
]);
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'title' => __('Add members to mailing list {0} [{1}]', h($mailingList->name), h($mailingList->id)),
|
||||
// 'description' => __('Mailing list are email distribution lists containing individuals.'),
|
||||
'model' => 'MailingLists',
|
||||
'fields' => [
|
||||
[
|
||||
'field' => 'individuals',
|
||||
'type' => 'dropdown',
|
||||
'multiple' => true,
|
||||
'select2' => true,
|
||||
'label' => __('Members'),
|
||||
'class' => 'select2-input',
|
||||
'options' => $dropdownData['individuals']
|
||||
],
|
||||
],
|
||||
'submit' => [
|
||||
'action' => $this->request->getParam('action')
|
||||
],
|
||||
],
|
||||
]);
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
echo $this->element('genericElements/IndexTable/index_table', [
|
||||
'data' => [
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add mailing list'),
|
||||
'popover_url' => '/MailingLists/add'
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'value'
|
||||
],
|
||||
[
|
||||
'type' => 'table_action',
|
||||
'table_setting_id' => 'mailinglist_index',
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => '#',
|
||||
'sort' => 'id',
|
||||
'class' => 'short',
|
||||
'data_path' => 'id',
|
||||
],
|
||||
[
|
||||
'name' => __('Name'),
|
||||
'sort' => 'name',
|
||||
'data_path' => 'name',
|
||||
],
|
||||
[
|
||||
'name' => __('Owner'),
|
||||
'data_path' => 'user_id',
|
||||
'element' => 'user'
|
||||
],
|
||||
[
|
||||
'name' => __('UUID'),
|
||||
'sort' => 'uuid',
|
||||
'data_path' => 'uuid',
|
||||
],
|
||||
[
|
||||
'name' => __('Members'),
|
||||
'data_path' => 'individuals',
|
||||
'element' => 'count_summary',
|
||||
],
|
||||
[
|
||||
'name' => __('Intended recipients'),
|
||||
'data_path' => 'recipients',
|
||||
],
|
||||
[
|
||||
'name' => __('Description'),
|
||||
'data_path' => 'description',
|
||||
],
|
||||
[
|
||||
'name' => __('Active'),
|
||||
'data_path' => 'active',
|
||||
'sort' => 'active',
|
||||
'element' => 'boolean',
|
||||
],
|
||||
[
|
||||
'name' => __('Deleted'),
|
||||
'data_path' => 'deleted',
|
||||
'sort' => 'deleted',
|
||||
'element' => 'boolean',
|
||||
],
|
||||
|
||||
// [
|
||||
// 'name' => __('Members'),
|
||||
// 'data_path' => 'alignments',
|
||||
// 'element' => 'count_summary',
|
||||
// 'url' => '/sharingGroups/view/{{id}}',
|
||||
// 'url_data_path' => 'id'
|
||||
// ]
|
||||
],
|
||||
'title' => __('Mailing Lists Index'),
|
||||
'description' => __('Mailing list are email distribution lists containing individuals.'),
|
||||
'actions' => [
|
||||
[
|
||||
'url' => '/mailingLists/view',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'icon' => 'eye'
|
||||
],
|
||||
[
|
||||
'open_modal' => '/mailingLists/edit/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'edit'
|
||||
],
|
||||
[
|
||||
'open_modal' => '/mailingLists/delete/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'trash'
|
||||
],
|
||||
]
|
||||
]
|
||||
]);
|
||||
?>
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/IndexTable/index_table', [
|
||||
'data' => [
|
||||
'data' => $individuals,
|
||||
'skip_pagination' => 1,
|
||||
'top_bar' => [
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'multi_select_actions',
|
||||
'children' => [
|
||||
[
|
||||
'text' => __('Remove members'),
|
||||
'variant' => 'danger',
|
||||
'onclick' => 'removeMembers',
|
||||
]
|
||||
],
|
||||
'data' => [
|
||||
'id' => [
|
||||
'value_path' => 'id'
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add member'),
|
||||
'popover_url' => '/mailingLists/addIndividual/' . h($mailing_list_id),
|
||||
'reload_url' => '/mailingLists/listIndividuals/' . h($mailing_list_id)
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'value'
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => '#',
|
||||
'sort' => 'id',
|
||||
'data_path' => 'id',
|
||||
],
|
||||
[
|
||||
'name' => __('First name'),
|
||||
'data_path' => 'first_name',
|
||||
],
|
||||
[
|
||||
'name' => __('Last name'),
|
||||
'data_path' => 'last_name',
|
||||
],
|
||||
[
|
||||
'name' => __('Email'),
|
||||
'data_path' => 'email',
|
||||
],
|
||||
[
|
||||
'name' => __('UUID'),
|
||||
'sort' => 'uuid',
|
||||
'data_path' => 'uuid',
|
||||
]
|
||||
],
|
||||
'actions' => [
|
||||
[
|
||||
'url' => '/individuals/view',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'icon' => 'eye'
|
||||
],
|
||||
[
|
||||
'open_modal' => '/mailingLists/removeIndividual/' . h($mailing_list_id) . '/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'reload_url' => '/mailingLists/listIndividuals/' . h($mailing_list_id),
|
||||
'icon' => 'trash'
|
||||
],
|
||||
]
|
||||
]
|
||||
]);
|
||||
?>
|
||||
|
||||
<script>
|
||||
function removeMembers(idList, selectedData, $table) {
|
||||
const successCallback = function([data, modalObject]) {
|
||||
UI.reload('/mailingLists/listIndividuals/<?= h($mailing_list_id) ?>', UI.getContainerForTable($table), $table)
|
||||
}
|
||||
const failCallback = ([data, modalObject]) => {
|
||||
const tableData = selectedData.map(row => {
|
||||
entryInError = data.filter(error => error.data.id == row.id)[0]
|
||||
$faIcon = $('<i class="fa"></i>').addClass(entryInError.success ? 'fa-check text-success' : 'fa-times text-danger')
|
||||
return [row.id, row.first_name, row.last_name, row.email, entryInError.message, JSON.stringify(entryInError.errors), $faIcon]
|
||||
});
|
||||
handleMessageTable(
|
||||
modalObject.$modal,
|
||||
['<?= __('ID') ?>', '<?= __('First name') ?>', '<?= __('Last name') ?>', '<?= __('email') ?>', '<?= __('Message') ?>', '<?= __('Error') ?>', '<?= __('State') ?>'],
|
||||
tableData
|
||||
)
|
||||
const $footer = $(modalObject.ajaxApi.statusNode).parent()
|
||||
modalObject.ajaxApi.statusNode.remove()
|
||||
const $cancelButton = $footer.find('button[data-bs-dismiss="modal"]')
|
||||
$cancelButton.text('<?= __('OK') ?>').removeClass('btn-secondary').addClass('btn-primary')
|
||||
}
|
||||
UI.submissionModal('/mailingLists/removeIndividual/<?= h($mailing_list_id) ?>', successCallback, failCallback).then(([modalObject, ajaxApi]) => {
|
||||
const $idsInput = modalObject.$modal.find('form').find('input#ids-field')
|
||||
$idsInput.val(JSON.stringify(idList))
|
||||
const tableData = selectedData.map(row => {
|
||||
return [row.id, row.first_name, row.last_name, row.email]
|
||||
});
|
||||
handleMessageTable(
|
||||
modalObject.$modal,
|
||||
['<?= __('ID') ?>', '<?= __('First name') ?>', '<?= __('Last name') ?>', '<?= __('email') ?>'],
|
||||
tableData
|
||||
)
|
||||
})
|
||||
|
||||
function constructMessageTable(header, data) {
|
||||
return HtmlHelper.table(
|
||||
header,
|
||||
data, {
|
||||
small: true,
|
||||
borderless: true,
|
||||
tableClass: ['message-table', 'mt-4 mb-0'],
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function handleMessageTable($modal, header, data) {
|
||||
const $modalBody = $modal.find('.modal-body')
|
||||
const $messageTable = $modalBody.find('table.message-table')
|
||||
const messageTableHTML = constructMessageTable(header, data)[0].outerHTML
|
||||
if ($messageTable.length) {
|
||||
$messageTable.html(messageTableHTML)
|
||||
} else {
|
||||
$modalBody.append(messageTableHTML)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
echo $this->element(
|
||||
'/genericElements/SingleViews/single_view',
|
||||
[
|
||||
'data' => $entity,
|
||||
'fields' => [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => 'id'
|
||||
],
|
||||
[
|
||||
'key' => __('UUID'),
|
||||
'path' => 'uuid'
|
||||
],
|
||||
[
|
||||
'key' => __('Name'),
|
||||
'path' => 'name'
|
||||
],
|
||||
[
|
||||
'key' => __('Owner'),
|
||||
'path' => 'user_id',
|
||||
'url' => '/users/view/{{0}}',
|
||||
'url_vars' => 'user_id'
|
||||
],
|
||||
[
|
||||
'key' => __('Releasability'),
|
||||
'path' => 'releasability'
|
||||
],
|
||||
[
|
||||
'key' => __('Description'),
|
||||
'path' => 'description'
|
||||
],
|
||||
[
|
||||
'key' => __('Active'),
|
||||
'path' => 'active',
|
||||
'type' => 'boolean'
|
||||
],
|
||||
[
|
||||
'key' => __('Deleted'),
|
||||
'path' => 'deleted',
|
||||
'type' => 'boolean'
|
||||
]
|
||||
],
|
||||
'children' => [
|
||||
[
|
||||
'url' => '/mailingLists/listIndividuals/{{0}}',
|
||||
'url_params' => ['id'],
|
||||
'title' => __('Individuals')
|
||||
]
|
||||
]
|
||||
]
|
||||
);
|
|
@ -7,4 +7,7 @@
|
|||
'disabled' => $fieldData['disabled'] ?? false,
|
||||
'class' => ($fieldData['class'] ?? '') . ' formDropdown form-select'
|
||||
];
|
||||
if (!empty($fieldData['label'])) {
|
||||
$controlParams['label'] = $fieldData['label'];
|
||||
}
|
||||
echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $fieldData);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
/*
|
||||
/*
|
||||
* Generic form builder
|
||||
*
|
||||
* Simply pass a JSON with the following keys set:
|
||||
|
@ -26,6 +26,7 @@
|
|||
$data['url'] = ["controller" => $this->request->getParam('controller'), "action" => $this->request->getParam('url')];
|
||||
}
|
||||
$formRandomValue = Cake\Utility\Security::randomString(8);
|
||||
$initSelect2 = false;
|
||||
$formCreate = $this->Form->create($entity, ['id' => 'form-' . $formRandomValue]);
|
||||
$default_template = [
|
||||
'inputContainer' => '<div class="row mb-3">{{content}}</div>',
|
||||
|
@ -52,6 +53,7 @@
|
|||
continue;
|
||||
}
|
||||
}
|
||||
$initSelect2 = $initSelect2 || (!empty($fieldData['type']) && $fieldData['type'] == 'dropdown' && !empty($fieldData['select2']));
|
||||
$formTemplate = $default_template;
|
||||
if (!empty($fieldData['floating-label'])) {
|
||||
$formTemplate['inputContainer'] = '<div class="form-floating input {{type}}{{required}}">{{content}}</div>';
|
||||
|
@ -66,7 +68,8 @@
|
|||
continue;
|
||||
}
|
||||
$fieldsString .= $this->element(
|
||||
'genericElements/Form/fieldScaffold', [
|
||||
'genericElements/Form/fieldScaffold',
|
||||
[
|
||||
'fieldData' => $fieldData,
|
||||
'form' => $this->Form,
|
||||
'simpleFieldWhitelist' => $simpleFieldWhitelist
|
||||
|
@ -76,7 +79,8 @@
|
|||
}
|
||||
if (!empty($data['metaTemplates']) && $data['metaTemplates']->count() > 0) {
|
||||
$metaTemplateString = $this->element(
|
||||
'genericElements/Form/metaTemplateScaffold', [
|
||||
'genericElements/Form/metaTemplateScaffold',
|
||||
[
|
||||
'metaTemplatesData' => $data['metaTemplates'],
|
||||
'form' => $this->Form,
|
||||
]
|
||||
|
@ -100,6 +104,7 @@
|
|||
$actionName = h(\Cake\Utility\Inflector::humanize($this->request->getParam('action')));
|
||||
$modelName = h(\Cake\Utility\Inflector::humanize(\Cake\Utility\Inflector::singularize($this->request->getParam('controller'))));
|
||||
if (!empty($ajax)) {
|
||||
$seedModal = 'mseed-' . mt_rand();
|
||||
echo $this->element('genericElements/genericModal', [
|
||||
'title' => empty($data['title']) ? sprintf('%s %s', $actionName, $modelName) : h($data['title']),
|
||||
'body' => sprintf(
|
||||
|
@ -112,7 +117,8 @@
|
|||
$formCreate,
|
||||
$fieldsString,
|
||||
empty($metaTemplateString) ? '' : $this->element(
|
||||
'genericElements/accordion_scaffold', [
|
||||
'genericElements/accordion_scaffold',
|
||||
[
|
||||
'children' => [
|
||||
[
|
||||
'body' => $metaTemplateString,
|
||||
|
@ -124,7 +130,7 @@
|
|||
$formEnd
|
||||
),
|
||||
'actionButton' => $this->element('genericElements/Form/submitButton', $submitButtonData),
|
||||
'class' => 'modal-lg'
|
||||
'class' => "modal-lg {$seedModal}"
|
||||
]);
|
||||
} else if (!empty($raw)) {
|
||||
echo sprintf(
|
||||
|
@ -137,7 +143,8 @@
|
|||
$formCreate,
|
||||
$fieldsString,
|
||||
empty($metaTemplateString) ? '' : $this->element(
|
||||
'genericElements/accordion_scaffold', [
|
||||
'genericElements/accordion_scaffold',
|
||||
[
|
||||
'children' => [
|
||||
[
|
||||
'body' => $metaTemplateString,
|
||||
|
@ -161,7 +168,8 @@
|
|||
),
|
||||
sprintf('<div class="panel">%s</div>', $fieldsString),
|
||||
empty($metaTemplateString) ? '' : $this->element(
|
||||
'genericElements/accordion_scaffold', [
|
||||
'genericElements/accordion_scaffold',
|
||||
[
|
||||
'children' => [
|
||||
[
|
||||
'body' => $metaTemplateString,
|
||||
|
@ -184,5 +192,14 @@
|
|||
$('.formDropdown').on('change', function() {
|
||||
executeStateDependencyChecks('#' + this.id);
|
||||
})
|
||||
<?php if (!empty($initSelect2)): ?>
|
||||
<?php
|
||||
$dropdownParent = !empty($seedModal) ? sprintf("$('.modal-dialog.%s .modal-body')", $seedModal) : "$(document.body)";
|
||||
?>
|
||||
$('select.select2-input').select2({
|
||||
dropdownParent: <?= $dropdownParent ?>,
|
||||
width: '100%',
|
||||
})
|
||||
<?php endif; ?>
|
||||
});
|
||||
</script>
|
|
@ -96,7 +96,7 @@ echo $this->Bootstrap->modal([
|
|||
activeFilters[fullFilter] = rowData['value']
|
||||
}
|
||||
})
|
||||
$select = modalObject.$modal.find('select.tag-input')
|
||||
$select = modalObject.$modal.find('select.select2-input')
|
||||
activeFilters['filteringTags'] = $select.select2('data').map(tag => tag.text)
|
||||
const searchParam = jQuery.param(activeFilters);
|
||||
const url = `/${controller}/${action}?${searchParam}`
|
||||
|
@ -125,7 +125,7 @@ echo $this->Bootstrap->modal([
|
|||
}
|
||||
setFilteringValues($filteringTable, field, value, operator)
|
||||
}
|
||||
$select = $filteringTable.closest('.modal-body').find('select.tag-input')
|
||||
$select = $filteringTable.closest('.modal-body').find('select.select2-input')
|
||||
let passedTags = []
|
||||
tags.forEach(tagname => {
|
||||
const existingOption = $select.find('option').filter(function() {
|
||||
|
|
Loading…
Reference in New Issue