* 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.
* 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
-* 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
* Field management: Web interface
* minimal theme
* Automatic upgrade from DB changes
-* PHP 7 syntax ans specifications
+* PHP 7 syntax and specifications
--- /dev/null
+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.
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
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;
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`";
* @param string $entryId Number
* @return array
*/
- public function getEditData($entryId) {
+ public function getEditData(string $entryId): array {
$ret = array();
if(!empty($this->_collectionId) && !empty($entryId)) {
* 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)) {
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)."',
`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)."'";
}
* @param string $entryId Number
* @return bool
*/
- public function canEditEntry($entryId) {
+ public function canEditEntry(string $entryId): bool {
$ret = false;
if(!empty($entryId) && !empty($this->_collectionId)) {
* @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`,
* '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)) {
$result = $this->_mergeEntryWithFields($result, $_entryFields);
$ret['results'][$result['id']] = $result;
+ $ret['ids'][] = $result['id'];
}
$queryStrCount = "SELECT COUNT(t.id) AS amount ".$queryFrom.$queryJoin.$queryWhere;
/**
* 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)) {
* @return array
*/
public function getCollectionFields(): array {
+ if(empty($this->_id)) return array();
+
if(!empty($this->_cacheExistingCollectionFields)) {
return $this->_cacheExistingCollectionFields;
}
</script>
<button class="uk-button uk-button-default uk-button-small"
type="button" uk-toggle="target: #searchFrame; animation: uk-animation-scale-down; cls: uk-hidden">Toggle search</button>
+<?php if(!empty($TemplateData['entries'])) { ?>
+<form method="post" action="index.php?p=bulkedit&collection=<?php echo $TemplateData['loadedCollection']['id']; ?>" class="uk-display-inline-block">
+ <button type="submit" class="uk-button uk-button-default uk-button-small">Bulkedit these results</button>
+ <?php foreach($TemplateData['entries']['ids'] as $f) { ?>
+ <input type="hidden" name="bulkedit[]" value="<?php echo $f; ?>" />
+ <?php } ?>
+</form>
+<?php } ?>
+
<?php } ?>
<div class="uk-grid-small uk-grid-row-small uk-grid-row-small <?php if(!empty($TemplateData['search'])) { ?>uk-hidden<?php } ?>"
--- /dev/null
+<h3 class="uk-h3">Bulkedit these entries in: <a href="index.php?p=colletions&collection<?php echo $TemplateData['loadedCollection']['id']; ?>"><?php echo $TemplateData['loadedCollection']['name']; ?></a></h3>
+
+<?php if(!empty($TemplateData['itemsToWorkWith'])) { ?>
+
+<div class="uk-grid-small uk-grid-row-small" uk-grid>
+ <div class="uk-width-1-2">
+ <form class="uk-form-horizontal uk-margin-small" method="post" enctype="multipart/form-data">
+ <ul>
+ <?php foreach($TemplateData['itemsToWorkWith'] as $entry) { ?>
+ <input type="hidden" name="bulkedit[]" value="<?php echo $entry['id']; ?>" />
+ <li><a href="index.php?p=entry&collection=<?php echo $TemplateData['loadedCollection']['id']; ?>&id=<?php echo $entry['id']; ?>"><?php echo $entry['title']; ?></a></li>
+ <?php } ?>
+ </ul>
+
+<?php
+ foreach($TemplateData['editFields'] as $field) {
+ $field['bulkedit'] = true;
+ $_editFieldView = Summoner::themefile('manageentry/field-'.$field['type'].'.html', UI_THEME);
+ if(file_exists($_editFieldView)) {
+ require $_editFieldView;
+ }
+ else {
+ require $TemplateData['_editFieldViewDefault'];
+ }
+ }
+?>
+ <div class="uk-margin">
+ <button class="uk-button uk-button-primary" type="submit" name="submitForm">
+ Save
+ </button>
+ </div>
+
+ </form>
+ </div>
+ <div class="uk-width-1-2"></div>
+</div>
+<script type="text/javascript" src="view/default/ui/js/suggest-tag.js"></script>
+
+<?php } ?>
--- /dev/null
+<?php
+/**
+ * Bibliotheca
+ *
+ * Copyright 2018-2021 Johannes Keßler
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+require_once 'lib/trite.class.php';
+$Trite = new Trite($DB,$Doomguy);
+require_once 'lib/manageentry.class.php';
+$ManageEntry = new Manageentry($DB,$Doomguy);
+
+$_collection = false;
+if(isset($_GET['collection']) && !empty($_GET['collection'])) {
+ $_collection = trim($_GET['collection']);
+ $_collection = Summoner::validate($_collection,'digit') ? $_collection : false;
+}
+
+$TemplateData['loadedCollection'] = array();
+$TemplateData['pageTitle'] = 'Bulkedit';
+$TemplateData['itemsToWorkWith'] = array();
+
+if(!empty($_collection)) {
+ $TemplateData['loadedCollection'] = $Trite->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("<br />",$_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";
+ }
+}
<input class="uk-input" id="<?php echo $field['identifier']; ?>-input" type="text" autocomplete="off"
name="<?php echo $field['identifier']; ?>-input"
list="<?php echo $field['identifier']; ?>-datalist"
- onkeypress="addTag(event,'<?php echo $field['identifier']; ?>')"
+ onkeypress="addTag(event,'<?php echo $field['identifier']; ?>','<?php echo $field['inputValidation']; ?>')"
placeholder="Write and press enter."
>
+ <?php if(Summoner::ifset($field,'bulkedit')) { ?>
+ <select class="uk-select" name="fdata[additionalEditOption][<?php echo $field['identifier']; ?>]">
+ <option value="">Select bulk edit option</option>
+ <option value="add">Add</option>
+ <option value="replace">Replace</option>
+ <option value="empty">Clear</option>
+ </select>
+ <?php } ?>
</div>
</div>
name="fdata[<?php echo $field['identifier']; ?>]"
value="<?php echo Summoner::ifset($TemplateData['editData'], $field['identifier']); ?>"
>
+ <?php if(Summoner::ifset($field,'bulkedit')) { ?>
+ <select class="uk-select" name="fdata[additionalEditOption][<?php echo $field['identifier']; ?>]">
+ <option value="">Select bulk edit option</option>
+ <option value="replace">Replace</option>
+ <option value="empty">Clear</option>
+ </select>
+ <?php } ?>
</div>
</div>
><?php echo $v; ?></option>
<?php } ?>
</select>
+ <?php if(Summoner::ifset($field,'bulkedit')) { ?>
+ <select class="uk-select" name="fdata[additionalEditOption][<?php echo $field['identifier']; ?>]">
+ <option value="">Select bulk edit option</option>
+ <option value="replace">Replace</option>
+ <option value="empty">Clear</option>
+ </select>
+ <?php } ?>
</div>
</div>
name="fdata[<?php echo $field['identifier']; ?>]"
value="<?php echo Summoner::ifset($TemplateData['editData'], $field['identifier']); ?>"
>
+ <?php if(Summoner::ifset($field,'bulkedit')) { ?>
+ <select class="uk-select" name="fdata[additionalEditOption][<?php echo $field['identifier']; ?>]">
+ <option value="">Select bulk edit option</option>
+ <option value="add">Add</option>
+ <option value="replace">Replace</option>
+ <option value="empty">Clear</option>
+ </select>
+ <?php } ?>
</div>
</div>
<textarea class="uk-textarea" autocomplete="off"
id="<?php echo $field['identifier']; ?>" rows="3"
name="fdata[<?php echo $field['identifier']; ?>]"><?php echo Summoner::ifset($TemplateData['editData'], $field['identifier']); ?></textarea>
+ <?php if(Summoner::ifset($field,'bulkedit')) { ?>
+ <select class="uk-select" name="fdata[additionalEditOption][<?php echo $field['identifier']; ?>]">
+ <option value="">Select bulk edit option</option>
+ <option value="add">Add</option>
+ <option value="replace">Replace</option>
+ <option value="empty">Clear</option>
+ </select>
+ <?php } ?>
</div>
</div>
<textarea class="uk-textarea" autocomplete="off"
id="<?php echo $field['identifier']; ?>" rows="6"
name="fdata[<?php echo $field['identifier']; ?>]"><?php echo Summoner::ifset($TemplateData['editData'], $field['identifier']); ?></textarea>
+ <?php if(Summoner::ifset($field,'bulkedit')) { ?>
+ <select class="uk-select" name="fdata[additionalEditOption][<?php echo $field['identifier']; ?>]">
+ <option value="">Select bulk edit option</option>
+ <option value="add">Add</option>
+ <option value="replace">Replace</option>
+ <option value="empty">Clear</option>
+ </select>
+ <?php } ?>
</div>
</div>
name="fdata[<?php echo $field['identifier']; ?>]"
value="<?php echo Summoner::ifset($TemplateData['editData'], $field['identifier']); ?>"
>
+ <?php if(Summoner::ifset($field,'bulkedit')) { ?>
+ <select class="uk-select" name="fdata[additionalEditOption][<?php echo $field['identifier']; ?>]">
+ <option value="">Select bulk edit option</option>
+ <option value="replace">Replace</option>
+ <option value="empty">Clear</option>
+ </select>
+ <?php } ?>
</div>
</div>
</div>
</form>
</div>
- <div>
+ <div class="uk-width-1-3">
<?php if(!empty($TemplateData['availableTools'])) { ?>
<h4>Available tools</h4>
<ul>
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();
$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');
$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.";
// 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 {
$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 {
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'] = "<a href='index.php?p=manageentry&collection=".$_collection."&id=".$do."'>View your new entry</a>";
$TemplateData['message']['status'] = "success";
$_search = false;
if(isset($_POST['navSearch'])) {
$_search = trim($_POST['navSearch']);
- $_search = Summoner::validate($_search,'text') ? $_search : false;
+ $_search = Summoner::validate($_search) ? $_search : false;
}
* @param Object e
* @param String targetStartString
*/
-function addTag(e,targetStartString) {
+function addTag(e,targetStartString,allowWhiteSpace) {
e = e || window.event;
if(e.keyCode === 13) {
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;
* @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;
}