From 284451626a59bc366619c825ddd437b46fbbc990 Mon Sep 17 00:00:00 2001 From: Banana Date: Sun, 28 Feb 2021 18:16:04 +0100 Subject: [PATCH] bulk edit now available --- CHANGELOG | 12 +- TODO | 5 +- documentation/bulk-edit.txt | 9 ++ documentation/fields.txt | 4 + upgrade/from-version-1.0.txt | 8 ++ webclient/lib/manageentry.class.php | 21 +-- webclient/lib/mancubus.class.php | 14 +- webclient/lib/trite.class.php | 2 + .../advancedsearch/advancedsearch.html | 9 ++ webclient/view/default/bulkedit/bulkedit.html | 39 ++++++ webclient/view/default/bulkedit/bulkedit.php | 131 ++++++++++++++++++ .../manageentry/field-lookupmultiple.html | 10 +- .../default/manageentry/field-number.html | 7 + .../default/manageentry/field-selection.html | 7 + .../view/default/manageentry/field-text.html | 8 ++ .../view/default/manageentry/field-text3.html | 8 ++ .../default/manageentry/field-textarea.html | 8 ++ .../view/default/manageentry/field-year.html | 7 + .../view/default/manageentry/manageentry.html | 2 +- .../view/default/manageentry/manageentry.php | 14 +- webclient/view/default/tags/tags.php | 2 +- webclient/view/default/ui/js/suggest-tag.js | 15 +- 22 files changed, 309 insertions(+), 33 deletions(-) create mode 100644 documentation/bulk-edit.txt create mode 100644 webclient/view/default/bulkedit/bulkedit.html create mode 100644 webclient/view/default/bulkedit/bulkedit.php diff --git a/CHANGELOG b/CHANGELOG index 98d1d28..5804c49 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,9 +3,9 @@ * Missing changelog file * api has its own log file now. * User profile for editing own settings. - * Collection management has the option to update entry rights with the + * Collection management has the option to update entry rights with the collection ones. - * Entry rights can now be managed. More info about user and rights can + * Entry rights can now be managed. More info about user and rights can be found in documentation. * User management: Honor rights from current logged in user * Group management now available. But no relation check yet. @@ -16,11 +16,15 @@ * Documentation for tools * Documentation for tool imdbweb grabber. Default config added. * Fixed bug #3 - * Added feature #2. There is now a new definition which defines the default - targets of the imdb grabber values. See the config-imdbweb.php.default + * Added feature #2. There is now a new definition which defines the default + targets of the imdb grabber values. See the config-imdbweb.php.default for more details * Added sort filter. Collection has a default sort field config now. Sort fields are the simple fields. See fields documentation. + * Fields do have a new inputValidation option. See fields documentation for more info. + Currently used to allow whitespace in a tag + * Mass edit of entries. Entries found with advanced search can now be editied. + Upload fields are not supported yet. 1.0 - Castle 20210106 * First usable version diff --git a/TODO b/TODO index 517e11c..3720f2c 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,6 @@ -* Mass edit of entries +* stats overview page. amount of entries. file / cache and db storage. Version info and where to find it. * User and groupmanagement: Check where a user or group is used! * Better error handling and display while adding / update and delete -* stats overview page. amount of entries. file / cache and db storage. Version info and where to find it. * Export of an entry, collection or everything. Stored on disk. * Import of the export @@ -9,4 +8,4 @@ * Field management: Web interface * minimal theme * Automatic upgrade from DB changes -* PHP 7 syntax ans specifications +* PHP 7 syntax and specifications diff --git a/documentation/bulk-edit.txt b/documentation/bulk-edit.txt new file mode 100644 index 0000000..eda6eeb --- /dev/null +++ b/documentation/bulk-edit.txt @@ -0,0 +1,9 @@ +With the help if the advanced search found entries can be edited. +Choose which field should be altered. +Options are displayed below the input fild. +Not every type of field has all options. +Add does add the input to the existing data. +Replace does a complete replace of the existing data. +Clear removes the existing data regardles of input. + +Upload fields are not supported yet. diff --git a/documentation/fields.txt b/documentation/fields.txt index d3279a0..7a56bab 100644 --- a/documentation/fields.txt +++ b/documentation/fields.txt @@ -41,6 +41,10 @@ Every field with entry* is a simple search field and can be used in global searc createstring The SQL create string which is run as you add it to your collection. Not everyone needs one! +inputValidation +Defines the additional input validation. Currently only allowSpace is available. Used for lookupmultiple field to +allow tags with whitespace in its values. + value The value which is displayed as a selection for the user. Needed for a selection type field diff --git a/upgrade/from-version-1.0.txt b/upgrade/from-version-1.0.txt index 86c6e13..70a927d 100644 --- a/upgrade/from-version-1.0.txt +++ b/upgrade/from-version-1.0.txt @@ -24,3 +24,11 @@ INSERT INTO `#REPLACEME#_menu` (`id`, `text`, `action`, `icon`, `owner`, `group` UPDATE `#REPLACEME#_menu` SET `position` = '6' WHERE `#REPLACEME#_menu`.`id` = 16; UPDATE `#REPLACEME#_menu` SET `group` = '2', `rights` = 'rw-rw----' WHERE `#REPLACEME#_menu`.`id` = 14; ALTER TABLE `#REPLACEME#_collection` ADD `defaultSortField` VARCHAR(16) NOT NULL AFTER `defaultSearchField`; +INSERT INTO `#REPLACEME#_menu` (`id`, `text`, `action`, `icon`, `owner`, `group`, `rights`, `position`, `category`) VALUES (NULL, 'Bulkedit', 'bulkedit', '', '1', '2', 'rw-rw----', '0', ''); +ALTER TABLE `#REPLACEME#_sys_fields` ADD `inputValidation` VARCHAR(32) NOT NULL AFTER `createstring`; +UPDATE `#REPLACEME#_sys_fields` SET `inputValidation` = 'allowSpace' WHERE `bib_sys_fields`.`id` = 18; +UPDATE `#REPLACEME#_sys_fields` SET `inputValidation` = 'allowSpace' WHERE `bib_sys_fields`.`id` = 19; +UPDATE `#REPLACEME#_sys_fields` SET `inputValidation` = 'allowSpace' WHERE `bib_sys_fields`.`id` = 7; +UPDATE `#REPLACEME#_sys_fields` SET `inputValidation` = 'allowSpace' WHERE `bib_sys_fields`.`id` = 8; +UPDATE `#REPLACEME#_sys_fields` SET `inputValidation` = 'allowSpace' WHERE `bib_sys_fields`.`id` = 20; +UPDATE `#REPLACEME#_sys_fields` SET `inputValidation` = 'allowSpace' WHERE `bib_sys_fields`.`id` = 26; diff --git a/webclient/lib/manageentry.class.php b/webclient/lib/manageentry.class.php index 82fe6f9..76054a1 100644 --- a/webclient/lib/manageentry.class.php +++ b/webclient/lib/manageentry.class.php @@ -89,7 +89,7 @@ class Manageentry { if(!empty($this->_collectionId)) { $queryStr = "SELECT `cf`.`fk_field_id` AS id, `sf`.`type`, `sf`.`displayname`, `sf`.`identifier`, - `sf`.`value` + `sf`.`value`, `sf`.`inputValidation` FROM `".DB_PREFIX."_collection_fields_".$this->_DB->real_escape_string($this->_collectionId)."` AS cf LEFT JOIN `".DB_PREFIX."_sys_fields` AS sf ON `cf`.`fk_field_id` = `sf`.`id` ORDER BY `cf`.`sort`"; @@ -121,7 +121,7 @@ class Manageentry { * @param string $entryId Number * @return array */ - public function getEditData($entryId) { + public function getEditData(string $entryId): array { $ret = array(); if(!empty($this->_collectionId) && !empty($entryId)) { @@ -157,16 +157,17 @@ class Manageentry { * Create an entry with given data * * @param array $data - * @param number $owner - * @param number $group + * @param string $owner Number + * @param string $group Number * @param string $rights * @param mixed $update Either false for no update or the ID to update - * @return mixed + * @return int */ - public function create($data, $owner, $group, $rights, $update=false) { - $ret = false; + public function create(array $data, string $owner, string $group, string $rights, $update=false): int { + $ret = 0; if(DEBUG) error_log("[DEBUG] ".__METHOD__." data: ".var_export($data,true)); + if(DEBUG) error_log("[DEBUG] ".__METHOD__." update: ".var_export($update,true)); if(!empty($data) && !empty($owner) && !empty($group) && !empty($rights)) { @@ -187,8 +188,7 @@ class Manageentry { if(DEBUG) error_log("[DEBUG] ".__METHOD__." queryData: ".var_export($queryData,true)); - if(!empty($queryData['init'])) { - + if(!empty($queryData['init']) || ($update !== false && is_numeric($update))) { $queryStr = "INSERT INTO `".DB_PREFIX."_collection_entry_".$this->_collectionId."` SET `modificationuser` = '".$this->_DB->real_escape_string($owner)."', @@ -201,6 +201,7 @@ class Manageentry { `rights`= '".$this->_DB->real_escape_string($rights)."',"; } $queryStr .= implode(", ",$queryData['init']); + $queryStr = trim($queryStr,","); if($update !== false && is_numeric($update)) { $queryStr .= " WHERE `id` = '".$this->_DB->real_escape_string($update)."'"; } @@ -313,7 +314,7 @@ class Manageentry { * @param string $entryId Number * @return bool */ - public function canEditEntry($entryId) { + public function canEditEntry(string $entryId): bool { $ret = false; if(!empty($entryId) && !empty($this->_collectionId)) { diff --git a/webclient/lib/mancubus.class.php b/webclient/lib/mancubus.class.php index db754df..a9d65e6 100644 --- a/webclient/lib/mancubus.class.php +++ b/webclient/lib/mancubus.class.php @@ -119,7 +119,7 @@ class Mancubus { * @param string $search Search string to search for * @return array */ - public function getLatest($selections, $entries, $search='') { + public function getLatest(string $selections, string $entries, $search=''): array { $ret = array(); $queryStr = "SELECT `c`.`id`, `c`.`name`, `c`.`description`, `c`.`created`, @@ -183,10 +183,16 @@ class Mancubus { * 'exactTagMatch' => true to make a binary compare. false for match against search * ) * + * return array( + * 'results' => array(), + * 'amount' => int, + * 'ids' => array() + * ) + * * @param array $searchData * @return array */ - public function getEntries($searchData=array()) { + public function getEntries($searchData=array()): array { $ret = array(); if(!empty($this->_collectionId)) { @@ -276,6 +282,7 @@ class Mancubus { $result = $this->_mergeEntryWithFields($result, $_entryFields); $ret['results'][$result['id']] = $result; + $ret['ids'][] = $result['id']; } $queryStrCount = "SELECT COUNT(t.id) AS amount ".$queryFrom.$queryJoin.$queryWhere; @@ -295,10 +302,11 @@ class Mancubus { /** * Retrieve all the data needed to display the entry for given entryId + * * @param string $entryId Number * @return array|mixed */ - public function getEntry($entryId) { + public function getEntry(string $entryId): array { $ret = array(); if(!empty($this->_collectionId) && !empty($entryId)) { diff --git a/webclient/lib/trite.class.php b/webclient/lib/trite.class.php index 3d1c988..718a756 100644 --- a/webclient/lib/trite.class.php +++ b/webclient/lib/trite.class.php @@ -198,6 +198,8 @@ class Trite { * @return array */ public function getCollectionFields(): array { + if(empty($this->_id)) return array(); + if(!empty($this->_cacheExistingCollectionFields)) { return $this->_cacheExistingCollectionFields; } diff --git a/webclient/view/default/advancedsearch/advancedsearch.html b/webclient/view/default/advancedsearch/advancedsearch.html index c89c046..18a07d9 100644 --- a/webclient/view/default/advancedsearch/advancedsearch.html +++ b/webclient/view/default/advancedsearch/advancedsearch.html @@ -12,6 +12,15 @@ + +
+ + + + +
+ +
Bulkedit these entries in: + + + +
+
+
+
    + + +
  • + +
+ + +
+ +
+ +
+
+
+
+ + + diff --git a/webclient/view/default/bulkedit/bulkedit.php b/webclient/view/default/bulkedit/bulkedit.php new file mode 100644 index 0000000..73f7880 --- /dev/null +++ b/webclient/view/default/bulkedit/bulkedit.php @@ -0,0 +1,131 @@ +load($_collection, "write"); + if(!empty($TemplateData['loadedCollection'])) { + $ManageEntry->setCollection($Trite->param('id')); + + if(isset($_POST['bulkedit']) && !empty($_POST['bulkedit'])) { + foreach($_POST['bulkedit'] as $e) { + $TemplateData['itemsToWorkWith'][] = $ManageEntry->getEditData($e); + } + + // needs this editData array since manageentry functionality is used + $TemplateData['editData'] = array(); + $TemplateData['editFields'] = $ManageEntry->getEditFields(); + + // @see manageentry for similar process + if(isset($_POST['submitForm'])) { + $fdata = $_POST['fdata']; + $fupload = array('name' => ''); // match $_FILES + if(!empty($_FILES) && isset($_FILES['fdata'])) { + $fupload = $_FILES['fdata']; + } + $_fieldsToSave = array(); + // default + $_owner = $Doomguy->param('id'); + $_group = $Trite->param('group'); + $_rights = $Trite->param('rights'); + + if (!empty($fdata)) { + foreach ($TemplateData['editFields'] as $fieldId=>$fieldData) { + if(isset($fdata['additionalEditOption'][$fieldData['identifier']]) + && !empty($fdata['additionalEditOption'][$fieldData['identifier']])) { + + $fieldData['bulkeditMethod'] = $fdata['additionalEditOption'][$fieldData['identifier']]; + if(isset($fdata[$fieldData['identifier']])) { + $_value = trim($fdata[$fieldData['identifier']]); + $fieldData['valueToSave'] = trim($fdata[$fieldData['identifier']]); + $_fieldsToSave[$fieldData['identifier']] = $fieldData; + } elseif(isset($fupload['name'][$fieldData['identifier']])) { + // special case upload + // $_FILES data is combined + $fieldData['uploadData'] = $fupload; + + $_fieldsToSave[$fieldData['identifier']] = $fieldData; + } + } + } + } + + // now update the entries with the gathered data to save + if(!empty($_fieldsToSave)) { + $_messages = array(); + foreach ($TemplateData['itemsToWorkWith'] as $entry) { + foreach ($_fieldsToSave as $ident=>$data) { + switch ($data['bulkeditMethod']) { + case 'add': + if(is_array($entry[$ident])) { // lookup multiple + $data['valueToSave'] = implode(",", $entry[$ident]) . $data['valueToSave']; + } + else { + $data['valueToSave'] = $entry[$ident] . $data['valueToSave']; + } + break; + + case 'replace': + // leave it as it is + break; + + case 'empty': + $data['valueToSave'] = ''; + break; + } + + $_fieldsToSave[$ident] = $data; + } + + $do = $ManageEntry->create($_fieldsToSave, $_owner, $_group, $_rights, $entry['id']); + if ($do !== 0) { + $_messages[] = "Entry updated: ".$entry['id']; + } else { + $_messages[] = "Entry could not be updated. See log for more details: ".$entry['id']; + } + } + + $TemplateData['message']['content'] = implode("
",$_messages); + $TemplateData['message']['status'] = "info"; + } + } + } + else { + $TemplateData['message']['content'] = "Missing required search items to work with."; + $TemplateData['message']['status'] = "error"; + } + } + else { + $TemplateData['message']['content'] = "Can not load given collection."; + $TemplateData['message']['status'] = "error"; + } +} diff --git a/webclient/view/default/manageentry/field-lookupmultiple.html b/webclient/view/default/manageentry/field-lookupmultiple.html index b6134a0..d4dbb38 100644 --- a/webclient/view/default/manageentry/field-lookupmultiple.html +++ b/webclient/view/default/manageentry/field-lookupmultiple.html @@ -24,9 +24,17 @@ if(Summoner::ifset($TemplateData['editData'], $field['identifier'])) { + + +
diff --git a/webclient/view/default/manageentry/field-number.html b/webclient/view/default/manageentry/field-number.html index fca6035..1148a10 100644 --- a/webclient/view/default/manageentry/field-number.html +++ b/webclient/view/default/manageentry/field-number.html @@ -5,5 +5,12 @@ name="fdata[]" value="" > + + + diff --git a/webclient/view/default/manageentry/field-selection.html b/webclient/view/default/manageentry/field-selection.html index 65cf8a1..8618bd3 100644 --- a/webclient/view/default/manageentry/field-selection.html +++ b/webclient/view/default/manageentry/field-selection.html @@ -9,5 +9,12 @@ > + + + diff --git a/webclient/view/default/manageentry/field-text.html b/webclient/view/default/manageentry/field-text.html index 82761c6..0c91894 100644 --- a/webclient/view/default/manageentry/field-text.html +++ b/webclient/view/default/manageentry/field-text.html @@ -5,5 +5,13 @@ name="fdata[]" value="" > + + + diff --git a/webclient/view/default/manageentry/field-text3.html b/webclient/view/default/manageentry/field-text3.html index 7256353..d7dcba1 100644 --- a/webclient/view/default/manageentry/field-text3.html +++ b/webclient/view/default/manageentry/field-text3.html @@ -4,5 +4,13 @@ + + + diff --git a/webclient/view/default/manageentry/field-textarea.html b/webclient/view/default/manageentry/field-textarea.html index ca66b43..07189a4 100644 --- a/webclient/view/default/manageentry/field-textarea.html +++ b/webclient/view/default/manageentry/field-textarea.html @@ -4,5 +4,13 @@ + + + diff --git a/webclient/view/default/manageentry/field-year.html b/webclient/view/default/manageentry/field-year.html index 4b0158e..19882b0 100644 --- a/webclient/view/default/manageentry/field-year.html +++ b/webclient/view/default/manageentry/field-year.html @@ -6,5 +6,12 @@ name="fdata[]" value="" > + + + diff --git a/webclient/view/default/manageentry/manageentry.html b/webclient/view/default/manageentry/manageentry.html index 573d720..a8b054f 100644 --- a/webclient/view/default/manageentry/manageentry.html +++ b/webclient/view/default/manageentry/manageentry.html @@ -92,7 +92,7 @@ if(!empty($TemplateData['editFields'])) { -
+

Available tools

    diff --git a/webclient/view/default/manageentry/manageentry.php b/webclient/view/default/manageentry/manageentry.php index ba7f5d9..ea44208 100644 --- a/webclient/view/default/manageentry/manageentry.php +++ b/webclient/view/default/manageentry/manageentry.php @@ -19,7 +19,7 @@ require_once 'lib/trite.class.php'; $Trite = new Trite($DB,$Doomguy); require_once 'lib/manageentry.class.php'; -$ManangeEntry = new Manageentry($DB,$Doomguy); +$ManageEntry = new Manageentry($DB,$Doomguy); $TemplateData['pageTitle'] = 'Manage entry - '; $TemplateData['editFields'] = array(); @@ -46,9 +46,9 @@ if(!empty($_collection)) { $TemplateData['loadedCollection'] = $Trite->load($_collection, "write"); if(!empty($TemplateData['loadedCollection'])) { - $ManangeEntry->setCollection($Trite->param('id')); + $ManageEntry->setCollection($Trite->param('id')); - $TemplateData['editFields'] = $ManangeEntry->getEditFields(); + $TemplateData['editFields'] = $ManageEntry->getEditFields(); $TemplateData['availableTools'] = $Trite->getAvailableTools(); $TemplateData['pageTitle'] = 'Add - '.$Trite->param('name'); @@ -57,7 +57,7 @@ if(!empty($_collection)) { $TemplateData['storagePath'] = PATH_WEB_STORAGE . '/' . $_collection . '/' . $_id; // prefill template data. Used also later to check if on edit mode - $TemplateData['editData'] = $ManangeEntry->getEditData($_id); + $TemplateData['editData'] = $ManageEntry->getEditData($_id); // special case. Title field should be always available. if(!isset($TemplateData['editData']['title'])) { $TemplateData['message']['content'] = "Entry has no value in title field."; @@ -108,7 +108,7 @@ if(!empty($_collection)) { // special case. Title field should be always available. if(!empty($TemplateData['editData']['title'])) { // EDIT if(isset($fdata['doDelete'])) { - $do = $ManangeEntry->delete($_id); + $do = $ManageEntry->delete($_id); if ($do === true) { $TemplateData['refresh'] = 'index.php?p=collections&collection='.$_collection; } else { @@ -116,7 +116,7 @@ if(!empty($_collection)) { $TemplateData['message']['status'] = "error"; } } elseif (!empty($_fieldsToSave) && isset($_fieldsToSave['title'])) { - $do = $ManangeEntry->create($_fieldsToSave, $_owner, $_group, $_rights, $_id); + $do = $ManageEntry->create($_fieldsToSave, $_owner, $_group, $_rights, $_id); if ($do !== 0) { $TemplateData['refresh'] = 'index.php?p=entry&collection='.$_collection.'&id='.$_id; } else { @@ -128,7 +128,7 @@ if(!empty($_collection)) { else { // ADD // special case. Title field should be always available. if (!empty($_fieldsToSave) && isset($_fieldsToSave['title'])) { - $do = $ManangeEntry->create($_fieldsToSave, $_owner, $_group, $_rights); + $do = $ManageEntry->create($_fieldsToSave, $_owner, $_group, $_rights); if (!empty($do)) { $TemplateData['message']['content'] = "View your new entry"; $TemplateData['message']['status'] = "success"; diff --git a/webclient/view/default/tags/tags.php b/webclient/view/default/tags/tags.php index d64bf97..6288b43 100644 --- a/webclient/view/default/tags/tags.php +++ b/webclient/view/default/tags/tags.php @@ -41,7 +41,7 @@ $TemplateData['search'] = false; $_search = false; if(isset($_POST['navSearch'])) { $_search = trim($_POST['navSearch']); - $_search = Summoner::validate($_search,'text') ? $_search : false; + $_search = Summoner::validate($_search) ? $_search : false; } diff --git a/webclient/view/default/ui/js/suggest-tag.js b/webclient/view/default/ui/js/suggest-tag.js index 24b3912..04d5184 100644 --- a/webclient/view/default/ui/js/suggest-tag.js +++ b/webclient/view/default/ui/js/suggest-tag.js @@ -25,7 +25,7 @@ function removeTag(tagString,targetStartString) { * @param Object e * @param String targetStartString */ -function addTag(e,targetStartString) { +function addTag(e,targetStartString,allowWhiteSpace) { e = e || window.event; if(e.keyCode === 13) { @@ -34,7 +34,12 @@ function addTag(e,targetStartString) { let listBox = document.getElementById(targetStartString + '-listbox'); let newTagTemplate = document.getElementById(targetStartString + '-template'); - let checkString = _checkForSpaceString(elem.value,'nospace'); + let validateMethod = false; + if (allowWhiteSpace !== undefined && allowWhiteSpace.length > 0) { + validateMethod = allowWhiteSpace; + } + + let checkString = _checkForSpaceString(elem.value,validateMethod); if(saveInput && listBox && elem && newTagTemplate && checkString) { let toAdd = elem.value; @@ -145,8 +150,12 @@ function _fillTagTemplate(el,newTagString,targetStartString) { * @returns boolean * @private */ -function _checkForSpaceString(stringTocheck) { +function _checkForSpaceString(stringTocheck,validateMethod) { let check = stringTocheck.replace(/\s/gm,''); + if(validateMethod && validateMethod == "allowSpace") { + check = stringTocheck.trim(); + } + if(check === stringTocheck && check.length > 0) { return true; } -- 2.39.5