new: Various object template improvements

- allow multiple versions of a template to be stored at the same time
- select which version is the primary version of a template
- disable/enable templates
- edit objects with one of the older versions of a template if the object's version requires that

- various UI / bug fixes
pull/2489/head
iglocska 2017-09-18 00:38:30 +02:00
parent 9eb3ea2114
commit bb99706cf3
11 changed files with 183 additions and 67 deletions

View File

@ -513,6 +513,7 @@ CREATE TABLE IF NOT EXISTS object_templates (
`version` int(11) NOT NULL,
`requirements` text COLLATE utf8_bin,
`fixed` tinyint(1) NOT NULL DEFAULT 0,
`active` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
INDEX `user_id` (`user_id`),
INDEX `org_id` (`org_id`),

View File

@ -20,8 +20,10 @@ class ObjectTemplatesController extends AppController {
$this->ObjectTemplate->populateIfEmpty($this->Auth->user());
$templates_raw = $this->ObjectTemplate->find('all', array(
'recursive' => -1,
'conditions' => array('ObjectTemplate.active' => 1),
'fields' => array('id', 'meta-category', 'name', 'description', 'org_id'),
'contain' => array('Organisation.name')
'contain' => array('Organisation.name'),
'sort' => array('ObjectTemplate.name asc')
));
$templates = array('all' => array());
foreach ($templates_raw as $k => $template) {
@ -30,6 +32,9 @@ class ObjectTemplatesController extends AppController {
$templates[$templates_raw[$k]['ObjectTemplate']['meta-category']][] = $template['ObjectTemplate'];
$templates['all'][] = $template['ObjectTemplate'];
}
foreach ($templates as $category => $template_list) {
$templates[$category] = Hash::sort($templates[$category], '{n}.name');
}
$template_categories = array_keys($templates);
$this->layout = false;
$this->set('template_categories', $template_categories);
@ -61,6 +66,29 @@ class ObjectTemplatesController extends AppController {
}
}
public function delete($id) {
if (!$this->request->is('post') && !$this->request->is('put') && !$this->request->is('delete')) {
throw new MethodNotAllowedException();
}
$this->ObjectTemplate->id = $id;
if (!$this->ObjectTemplate->exists()) {
throw new NotFoundException('Invalid ObjectTemplate');
}
if ($this->ObjectTemplate->delete()) {
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('ObjectTemplates', 'admin_delete', $id, $this->response->type());
} else {
$this->Session->setFlash(__('ObjectTemplate deleted'));
}
}
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('ObjectTemplates', 'admin_delete', $id, $this->ObjectTemplate->validationErrors, $this->response->type());
} else {
$this->Session->setFlash('ObjectTemplate could not be deleted');
}
$this->redirect($this->referer());
}
public function viewElements($id, $context = 'all') {
$elements = $this->ObjectTemplate->ObjectTemplateElement->find('all', array(
'conditions' => array('ObjectTemplateElement.object_template_id' => $id)
@ -70,7 +98,13 @@ class ObjectTemplatesController extends AppController {
$this->render('ajax/view_elements');
}
public function index() {
public function index($all = false) {
if (!$all || !$this->_isSiteAdmin()) {
$this->paginate['conditions'][] = array('ObjectTemplate.active' => 1);
$this->set('all', false);
} else {
$this->set('all', true);
}
if ($this->_isRest()) {
$rules = $this->paginate;
unset($rules['limit']);
@ -78,6 +112,7 @@ class ObjectTemplatesController extends AppController {
$objectTemplates = $this->ObjectTemplate->find('all', $rules);
return $this->RestResponse->viewData($objectTemplates, $this->response->type());
} else {
$this->paginate['order'] = array('ObjectTemplate.name' => 'ASC');
$objectTemplates = $this->paginate();
$this->set('list', $objectTemplates);
}
@ -147,4 +182,21 @@ class ObjectTemplatesController extends AppController {
}
$this->redirect(array('controller' => 'ObjectTemplates', 'action' => 'index'));
}
public function activate() {
$id = $this->request->data['ObjectTemplate']['data'];
if (!is_numeric($id)) return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Template not found.')), 'status' => 200, 'type' => 'json'));
$result = $this->ObjectTemplate->setActive($id);
if ($result === false) {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Template\'s state could not be toggeled.')), 'status' => 200, 'type' => 'json'));
}
$message = (($result == 1) ? 'activated' : 'disabled');
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Template ' . $message . '.')), 'status' => 200, 'type' => 'json'));
}
public function getToggleField() {
if (!$this->request->is('ajax')) throw new MethodNotAllowedException('This action is available via AJAX only.');
$this->layout = 'ajax';
$this->render('ajax/getToggleField');
}
}

View File

@ -87,14 +87,29 @@ class ObjectsController extends AppController {
throw new NotFoundException('Invalid event.');
}
$eventId = $event['Event']['id'];
$template = $this->MispObject->ObjectTemplate->find('first', array(
$templates = $this->MispObject->ObjectTemplate->find('all', array(
'conditions' => array('ObjectTemplate.id' => $templateId),
'recursive' => -1,
'contain' => array(
'ObjectTemplateElement'
)
));
$template_version = false;
$template = false;
foreach ($templates as $temp) {
if (!empty($template_version)) {
if (intval($template['ObjectTemplate']['version']) > intval($template_version)) {
$template = $temp;
}
} else {
$template = $temp;
}
}
$error = false;
if (empty($template)) {
$error = 'No valid template found to edit the object.';
}
// If we have received a POST request
if ($this->request->is('post')) {
if (isset($this->request->data['request'])) {

View File

@ -779,6 +779,7 @@ class AppModel extends Model {
`version` int(11) NOT NULL,
`requirements` text COLLATE utf8_bin,
`fixed` tinyint(1) NOT NULL DEFAULT 0,
`active` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
INDEX `user_id` (`user_id`),
INDEX `org_id` (`org_id`),

View File

@ -22,10 +22,6 @@ class ObjectTemplate extends AppModel {
)
);
public $hasMany = array(
'Attribute' => array(
'className' => 'Attribute',
'dependent' => true,
),
'ObjectTemplateElement' => array(
'className' => 'ObjectTemplateElement',
'dependent' => true,
@ -65,14 +61,18 @@ class ObjectTemplate extends AppModel {
$file->close();
if (!isset($template['version'])) $template['version'] = 1;
$current = $this->find('first', array(
'fields' => array('MAX(version) AS version', 'uuid'),
'conditions' => array('uuid' => $template['uuid']),
'recursive' => -1
'recursive' => -1,
'group' => array('uuid')
));
if (!empty($current)) $current['ObjectTemplate']['version'] = $current[0]['version'];
if (empty($current) || $template['version'] > $current['ObjectTemplate']['version']) {
$result = $this->__updateObjectTemplate($template, $current, $user);
if ($result === true) {
$updated['success'][$result] = array('name' => $template['name'], 'new' => $template['version']);
if (!empty($current)) $updated['success'][$result]['old'] = $current['ObjectTemplate']['version'];
$temp = array('name' => $template['name'], 'new' => $template['version']);
if (!empty($current)) $temp['old'] = $current['ObjectTemplate']['version'];
$updated['success'][] = $temp;
} else {
$updated['fails'][] = array('name' => $template['name'], 'fail' => json_encode($result));
}
@ -90,65 +90,23 @@ class ObjectTemplate extends AppModel {
$template['requirements'][$field] = $template[$field];
}
}
if (empty($current)) {
$template['user_id'] = $user['id'];
$template['org_id'] = $user['org_id'];
$template['fixed'] = 1;
$this->create();
$result = $this->save($template);
} else {
$fieldsToUpdate = array('version', 'description', 'meta-category', 'name', 'requirements', 'fixed');
foreach ($fieldsToUpdate as $field) {
if (isset($template[$field]) && $current['ObjectTemplate'][$field] != $template[$field]) {
$current['ObjectTemplate'][$field] = $template[$field];
}
}
$result = $this->save($current);
}
$template['user_id'] = $user['id'];
$template['org_id'] = $user['org_id'];
$template['fixed'] = 1;
$this->create();
$result = $this->save($template);
if (!$result) {
return $this->validationErrors;
}
$id = $this->id;
$existingTemplateElementsTemp = $this->ObjectTemplateElement->find('all', array(
'recursive' => -1,
'conditions' => array('object_template_id' => $id)
));
$existingTemplateElements = array();
if (!empty($existingTemplateElementsTemp)) {
foreach ($existingTemplateElementsTemp as $k => $v) {
$existingTemplateElements[$v['ObjectTemplateElement']['object_relation']] = $v['ObjectTemplateElement'];
}
}
unset($existingTemplateElementsTemp);
$this->setActive($id);
$fieldsToCompare = array('object_relation', 'type', 'ui-priority', 'categories', 'sane_default', 'values_list', 'multiple');
foreach ($template['attributes'] as $k => $attribute) {
$attribute['object_relation'] = $k;
$attribute = $this->__convertJSONToElement($attribute);
if (isset($existingTemplateElements[$k])) {
$update_required = false;
foreach ($fieldsToCompare as $field) {
if (isset($attribute[$field])) {
if ($existingTemplateElements[$k][$field] != $attribute[$field]) {
$update_required = true;
}
}
}
if ($update_required) {
$attribute['id'] = $existingTemplateElements[$k]['id'];
$attribute['object_template_id'] = $id;
$result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute));
}
if (isset($existingTemplateElements[$k])) unset($existingTemplateElements[$k]);
} else {
$this->ObjectTemplateElement->create();
$attribute['object_template_id'] = $id;
$result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute));
}
}
if (!empty($existingTemplateElements)) {
foreach ($existingTemplateElements as $k2 => $v2) {
$this->ObjectTemplateElement->delete($v2['id']);
}
$this->ObjectTemplateElement->create();
$attribute['object_template_id'] = $id;
$result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute));
}
return true;
}
@ -232,4 +190,33 @@ class ObjectTemplate extends AppModel {
}
return true;
}
public function setActive($id) {
$template = $this->find('first', array(
'recursive' => -1,
'conditions' => array('ObjectTemplate.id' => $id)
));
if (empty($template)) return false;
if ($template['ObjectTemplate']['active']) {
$template['ObjectTemplate']['active'] = 0;
$this->save($template);
return 0;
}
$similar_templates = $this->find('all', array(
'recursive' => -1,
'conditions' => array(
'ObjectTemplate.uuid' => $template['ObjectTemplate']['uuid'],
'NOT' => array(
'ObjectTemplate.id' => $template['ObjectTemplate']['id']
)
)
));
$template['ObjectTemplate']['active'] = 1;
$this->save($template);
foreach ($similar_templates as $st) {
$st['ObjectTemplate']['active'] = 0;
$this->save($st);
}
return 1;
}
}

View File

@ -19,9 +19,15 @@ class ObjectTemplateElement extends AppModel {
public function afterFind($results, $primary = false) {
foreach ($results as $k => $result) {
$results[$k]['ObjectTemplateElement']['categories'] = json_decode($results[$k]['ObjectTemplateElement']['categories'], true);
$results[$k]['ObjectTemplateElement']['values_list'] = json_decode($results[$k]['ObjectTemplateElement']['values_list'], true);
$results[$k]['ObjectTemplateElement']['sane_default'] = json_decode($results[$k]['ObjectTemplateElement']['sane_default'], true);
if (isset($results[$k]['ObjectTemplateElement']['categories'])) {
$results[$k]['ObjectTemplateElement']['categories'] = json_decode($results[$k]['ObjectTemplateElement']['categories'], true);
}
if (isset($results[$k]['ObjectTemplateElement']['values_list'])) {
$results[$k]['ObjectTemplateElement']['values_list'] = json_decode($results[$k]['ObjectTemplateElement']['values_list'], true);
}
if (isset($results[$k]['ObjectTemplateElement']['sane_default'])) {
$results[$k]['ObjectTemplateElement']['sane_default'] = json_decode($results[$k]['ObjectTemplateElement']['sane_default'], true);
}
}
return $results;
}

View File

@ -0,0 +1,4 @@
<?php
echo $this->Form->create('ObjectTemplate', array('url' => '/ObjectTemplates/activate', 'id' => 'ObjectTemplateIndexForm'));
echo $this->Form->input('data', array('label' => false, 'style' => 'display:none;'));
echo $this->Form->end();

View File

@ -16,8 +16,34 @@
?>
</ul>
</div>
<div id="hiddenFormDiv">
<?php
if ($isSiteAdmin) {
echo $this->Form->create('ObjectTemplate', array('url' => '/ObjectTemplates/activate'));
echo $this->Form->input('data', array('label' => false, 'style' => 'display:none;'));
echo $this->Form->end();
}
?>
</div>
<div class="tabMenuFixedContainer" style="display:inline-block;">
<?php
if ($isSiteAdmin):
?>
<span role="button" tabindex="0" aria-label="Enabled" title="Enabled" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer <?php if (!$all) echo 'tabMenuActive';?>" onClick="window.location='/objectTemplates/index'">Enabled</span>
<span role="button" tabindex="0" aria-label="All" title="All" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer <?php if ($all) echo 'tabMenuActive';?>" onClick="window.location='/objectTemplates/index/all'">All</span>
<?php
endif;
?>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<?php
if ($isSiteAdmin):
?>
<th><?php echo $this->Paginator->sort('active');?></th>
<?php
endif;
?>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
<th><?php echo $this->Paginator->sort('uuid');?></th>
@ -33,6 +59,15 @@ foreach ($list as $template):
$td_attributes = 'ondblclick="document.location.href =\'/objectTemplates/view/' . h($template['ObjectTemplate']['id']) . '\'"';
?>
<tr>
<?php
if ($isSiteAdmin):
?>
<td class="short" <?php echo $td_attributes; ?>>
<input id="checkBox_<?php echo h($template['ObjectTemplate']['id']); ?>" type="checkbox" onClick="toggleSetting(event, 'activate_object_template', '<?php echo h($template['ObjectTemplate']['id']); ?>')" <?php echo $template['ObjectTemplate']['active'] ? 'checked' : ''; ?>/>
</td>
<?php
endif;
?>
<td class="short" <?php echo $td_attributes; ?>><?php echo h($template['ObjectTemplate']['id']); ?></td>
<td class="shortish" <?php echo $td_attributes; ?>>
<?php
@ -67,6 +102,11 @@ foreach ($list as $template):
</td>
<td class="short action-links">
<a href='/objectTemplates/view/<?php echo $template['ObjectTemplate']['id']; ?>' class = "icon-list-alt" title = "View"></a>
<?php
if ($isSiteAdmin):
echo $this->Form->postLink('', array('action' => 'delete', $template['ObjectTemplate']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete template # %s?', $template['ObjectTemplate']['id']));
endif;
?>
</td>
</tr>
<?php
@ -88,7 +128,6 @@ endforeach; ?>
?>
</ul>
</div>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'objectTemplates', 'menuItem' => 'index'));

View File

@ -9,7 +9,7 @@
<dt>Object Template</dt>
<dd>
<?php
echo Inflector::humanize(h($template['ObjectTemplate']['name']));
echo Inflector::humanize(h($template['ObjectTemplate']['name'])) . ' v' . h($template['ObjectTemplate']['version']);
?>
&nbsp;
</dd>

@ -1 +1 @@
Subproject commit d34dd5fb606f1c4d882733d16c16103fe429991c
Subproject commit 10b21c6aacb536e7646158f950e6ad972293c830

View File

@ -162,6 +162,12 @@ function toggleSetting(e, setting, id) {
replacementForm = '/favourite_tags/getToggleField/';
searchString = 'Adding';
break;
case 'activate_object_template':
formID = '#ObjectTemplateIndexForm';
dataDiv = '#ObjectTemplateData';
replacementForm = '/ObjectTemplates/getToggleField/';
searchString = 'activated';
break;
}
$(dataDiv).val(id);
var formData = $(formID).serialize();
@ -3210,6 +3216,11 @@ $('.add_object_attribute_row').click(function() {
});
});
$('.quickToggleCheckbox').toggle(function() {
var url = $(this).data('checkbox-url');
});
(function(){
"use strict";
$(".datepicker").datepicker({