diff --git a/CHANGELOG b/CHANGELOG index 261c04f55..16440afab 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -363,6 +363,8 @@ -------------------------------------------------------------------------------- - fix rest api endpoint PUT /folder/{id}/comment - show document preview when editing attributes of a document +- fix utilities which require translations +- fix potential XSS attack when deleting a folder/document -------------------------------------------------------------------------------- Changes in version 5.1.44 diff --git a/controllers/class.ExtensionMgr.php b/controllers/class.ExtensionMgr.php index 755baf186..c91bb8235 100644 --- a/controllers/class.ExtensionMgr.php +++ b/controllers/class.ExtensionMgr.php @@ -85,13 +85,14 @@ class SeedDMS_Controller_ExtensionMgr extends SeedDMS_Controller_Common { $extmgr = $this->params['extmgr']; $extname = $this->params['extname']; - if($settings->extensionIsDisabled($extname)) + if ($settings->extensionIsDisabled($extname)) { $settings->enableExtension($extname); - else + } else { $settings->disableExtension($extname); - $settings->save(); + } + $ret = $settings->save(); - return true; + return $ret; } /* }}} */ } diff --git a/doc/README.Converters.md b/doc/README.Converters.md index b96b6fc3e..07c980545 100644 --- a/doc/README.Converters.md +++ b/doc/README.Converters.md @@ -53,7 +53,7 @@ extracting text, creating an image, and converting to pdf. Unless you run a very old version of SeedDMS, you will never need this command for converting text files. SeedDMS has this trivial -converter build in. +converter built in. ### application/pdf @@ -63,7 +63,9 @@ If pdftotext takes too long on large document, then you may want to pass parameter `-l` to specify the last page to be converted. `-q` is for suppressing error/warnings send to stderr -`mutool draw -F txt -q -N -o - %s` +`mutool draw -F txt -q -N -o - '%s'` + +`mutool convert -F text -o - '%s'` ### application/vnd.openxmlformats-officedocument.wordprocessingml.document @@ -161,6 +163,10 @@ Converting from application/x-xopp to pdf only works if the xopp file does not use a pdf document as a background, because this pdf is not stored in the xopp fіle. +### application/epub+zip + +`mutool convert -F pdf -o "%o" "%f"` + ### Many office formats As already mentioned above, `unoconv` has some disadvantages. It is @@ -210,6 +216,10 @@ needed if the output goes to stdout. `pdftocairo` needs to output to stdout because the output file name passed to pdftocairo will be suffixed with `.png` +### application/epub+zip + +`mutool draw -F png -w %w -q -N -o '%o' '%f' 1` + ### application/postscript `convert -density 100 -resize %wx '%f[0]' 'png:%o'` diff --git a/inc/inc.ClassSettings.php b/inc/inc.ClassSettings.php index cf62d635d..45262f745 100644 --- a/inc/inc.ClassSettings.php +++ b/inc/inc.ClassSettings.php @@ -13,6 +13,7 @@ namespace Seeddms\Seeddms; +use PDO; use SeedDMS_Core_File; use SeedDMS_Core_DMS; use SeedDMS_Core_DatabaseAccess; @@ -384,6 +385,8 @@ class Settings { /* {{{ */ var $_incItemsPerPage = 0; // parse comments of folders and documents as markdown var $_markdownComments = false; + // show dropdown menu for actions on folders/documents + var $_actiondropdown = false; // Show form to submit missing translations at end of page var $_showMissingTranslations = false; // Extra Path to additional software, will be added to include path @@ -615,6 +618,7 @@ class Settings { /* {{{ */ if(isset($tab["incItemsPerPage"])) $this->_incItemsPerPage = intval($tab["incItemsPerPage"]); $this->_markdownComments = Settings::boolVal($tab["markdownComments"]); + $this->_actiondropdown = Settings::boolVal($tab["actiondropdown"]); // XML Path: /configuration/site/edition $node = $xml->xpath('/configuration/site/edition'); @@ -958,7 +962,7 @@ class Settings { /* {{{ */ $disabled = strval($tmp['disable']); else $disabled = 0; - $this->_extensions[$extname]['__disable__'] = $disabled=='1' || $disabled == 'true' ? true : false; + $this->_extensions[$extname]['__disable__'] = ($disabled=='1' || $disabled == 'true') ? true : false; foreach($extension->children() as $parameter) { $tmp2 = $parameter->attributes(); /* Do not read a parameter with the same name. Just a pre caution */ @@ -1059,6 +1063,7 @@ class Settings { /* {{{ */ $this->setXMLAttributValue($node, "maxItemsPerPage", $this->_maxItemsPerPage); $this->setXMLAttributValue($node, "incItemsPerPage", $this->_incItemsPerPage); $this->setXMLAttributValue($node, "markdownComments", $this->_markdownComments); + $this->setXMLAttributValue($node, "actiondropdown", $this->_actiondropdown); // XML Path: /configuration/site/edition $node = $this->getXMLNode($xml, '/configuration/site', 'edition'); diff --git a/op/op.Settings.php b/op/op.Settings.php index 13210fe97..3054759e6 100644 --- a/op/op.Settings.php +++ b/op/op.Settings.php @@ -110,6 +110,7 @@ if ($action == "saveSettings") setIntValue('maxItemsPerPage'); setIntValue('incItemsPerPage'); setBoolValue('markdownComments'); + setBoolValue('actiondropdown'); // SETTINGS - SITE - EDITION setBoolValue('strictFormCheck'); diff --git a/utils/delete.php b/utils/delete.php index d054ff09b..b272c2fcb 100644 --- a/utils/delete.php +++ b/utils/delete.php @@ -99,7 +99,6 @@ include($myincpath."/inc/inc.Language.php"); include($myincpath."/inc/inc.Init.php"); include($myincpath."/inc/inc.Extension.php"); include($myincpath."/inc/inc.DBInit.php"); -include($myincpath."/inc/inc.ClassNotificationService.php"); include($myincpath."/inc/inc.Notification.php"); include($myincpath."/inc/inc.ClassController.php"); diff --git a/views/bootstrap/class.Bootstrap.php b/views/bootstrap/class.Bootstrap.php index 33fe6f5d1..128b76313 100644 --- a/views/bootstrap/class.Bootstrap.php +++ b/views/bootstrap/class.Bootstrap.php @@ -731,41 +731,68 @@ background-image: linear-gradient(to bottom, #882222, #111111);; echo $content; } /* }}} */ - protected function showButtonwithMenu($button, $options=array()) { /* {{{ */ + protected function getMenu($menuitems=[], $options=[]) { /* {{{ */ $content = ''; $content .= ' + +'; + return $content; + } /* }}} */ + + protected function getButtonWithMenu($button, $options=array()) { /* {{{ */ + $content = ''; + if ($button['menuitems']) { + $content .= '
'.$button['label'].' '; - if($button['menuitems']) { - $content .= ' - -'; - } - $content .= '
'; - echo $content; + } + return $content; + } /* }}} */ + + protected function showButtonWithMenu($button, $options=array()) { /* {{{ */ + echo $this->getButtonWithMenu($button, $options); } /* }}} */ protected function showPaneHeader($name, $title, $isactive) { /* {{{ */ @@ -1183,7 +1210,7 @@ background-image: linear-gradient(to bottom, #882222, #111111);; function pageList($pageNumber, $totalPages, $baseURI, $params, $dataparams=[]) { /* {{{ */ $maxpages = 25; // skip pages when more than this is shown - $range = 5; // pages left and right of current page + $range = 3; // pages left and right of current page if (!is_numeric($pageNumber) || !is_numeric($totalPages) || $totalPages<2) { return; } @@ -1437,7 +1464,7 @@ background-image: linear-gradient(to bottom, #882222, #111111);; default: $class = 'btn-primary'; } - echo "
\n"; + echo "
\n"; if(is_string($value)) { echo "\n"; } else { @@ -1593,8 +1620,9 @@ function getOverallStatusIcon($status) { /* {{{ */ function getModalBoxLinkAttributes($config) { /* {{{ */ $attrs = array(); $attrs[] = array('data-target', '#'.$config['target']); - if(isset($config['remote'])) + if(isset($config['remote'])) { $attrs[] = array('href', $config['remote']); + } $attrs[] = array('data-toggle', 'modal'); $attrs[] = array('role', 'button'); if(isset($config['class'])) { @@ -2531,7 +2559,6 @@ $(document).ready(function() { } else { $tree[] = $node; } - } else { if($root = $this->params['dms']->getFolder($this->params['rootfolderid'])) $tree = array(array('label'=>$root->getName(), 'id'=>$root->getID(), 'load_on_demand'=>false, 'is_folder'=>true)); @@ -2757,7 +2784,7 @@ $(function() { function printDeleteDocumentButton($document, $msg, $return=false){ /* {{{ */ $docid = $document->getID(); $content = ''; - $content .= ' $document->getName())), ENT_QUOTES).'" title="'.getMLText("delete").'">'; + $content .= ' htmlspecialchars($document->getName()))), ENT_QUOTES).'" title="'.getMLText("delete").'">'; if($return) return $content; else @@ -2765,6 +2792,11 @@ $(function() { return ''; } /* }}} */ + function getDeleteDocumentButton($document, $msg, $return=false){ /* {{{ */ + $docid = $document->getID(); + return ['class'=>'delete-document-btn', 'rel'=>$docid, 'msg'=>getMLText($msg), 'confirmmsg'=>htmlspecialchars(getMLText("confirm_rm_document", array ("documentname" => htmlspecialchars($document->getName()))), ENT_QUOTES), 'title'=>getMLText('delete'), 'label'=>getMLText('delete'), 'icon'=>'remove']; + } /* }}} */ + function printDeleteDocumentButtonJs(){ /* {{{ */ echo " $(document).ready(function () { @@ -2831,7 +2863,7 @@ $(function() { function printDeleteFolderButton($folder, $msg, $return=false){ /* {{{ */ $folderid = $folder->getID(); $content = ''; - $content .= ' $folder->getName())), ENT_QUOTES).'" title="'.getMLText("delete").'">'; + $content .= ' htmlspecialchars($folder->getName()))), ENT_QUOTES).'" title="'.getMLText("delete").'">'; if($return) return $content; else @@ -2839,6 +2871,11 @@ $(function() { return ''; } /* }}} */ + function getDeleteFolderButton($folder, $msg, $return=false){ /* {{{ */ + $folderid = $folder->getID(); + return ['class'=>'delete-folder-btn', 'rel'=>$folderid, 'msg'=>getMLText($msg), 'confirmmsg'=>htmlspecialchars(getMLText("confirm_rm_folder", array ("foldername" => htmlspecialchars($folder->getName()))), ENT_QUOTES), 'title'=>getMLText("delete"), 'label'=>getMLText("delete"), 'label'=>getMLText("delete"), 'icon'=>'remove']; + } /* }}} */ + function printDeleteFolderButtonJs(){ /* {{{ */ echo " $(document).ready(function () { @@ -2909,6 +2946,7 @@ $(function() { } $content = ''; $content .= ''; + $content = ['class'=>'lock-document-btn', 'rel'=>$docid, 'msg'=>getMLText($msg), 'title'=>getMLText($title), 'label'=>getMLText($title), 'data-formtoken'=>createFormKey('tooglelockdocument'), 'icon'=>$icon]; if($return) return $content; else @@ -2921,11 +2959,15 @@ $(function() { $content = ''; $objid = $object->getId(); if($object->isType('document')) { - if($accessobject->check_view_access('DocumentAccess')) - $content .= ''; + if($accessobject->check_view_access('DocumentAccess')) { +// $content .= ''; + $content = ['class'=>'access-document-btn', 'link'=>$this->params['settings']->_httpRoot.'out/out.DocumentAccess.php?documentid='.$objid, 'title'=>getMLText('edit_document_access'), 'label'=>getMLText('edit_document_access'), 'icon'=>'bolt']; + } } elseif($object->isType('folder')) { - if($accessobject->check_view_access('FolderAccess')) - $content .= ''; + if($accessobject->check_view_access('FolderAccess')) { +// $content .= ''; + $content = ['class'=>'access-folder-btn', 'link'=>$this->params['settings']->_httpRoot.'out/out.FolderAccess.php?folderid='.$objid, 'title'=>getMLText('edit_folder_access'), 'label'=>getMLText('edit_folder_access'), 'icon'=>'bolt']; + } } if($return) return $content; @@ -3427,14 +3469,15 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) $content .= $extracontent['begin_action_list']; if($accessop->check_view_access('RemoveDocument')) { if($document->getAccessMode($user, 'removeDocument') >= M_ALL) { - $actions['remove_document'] = $this->printDeleteDocumentButton($document, 'splash_rm_document', true); + $actions['remove_document'] = $this->getDeleteDocumentButton($document, 'splash_rm_document', true); } else { $actions['remove_document'] = ''; } } $docID = $document->getID(); if($document->getAccessMode($user) >= M_READWRITE) { - $actions['edit_document'] = ''; +// $actions['edit_document'] = ''; + $actions['edit_document'] = ['link'=>$this->params['settings']->_httpRoot.'out/out.EditDocument.php?documentid='.$docID, 'title'=>getMLText("edit_document_props"), 'label'=>getMLText("edit_document_props"), 'icon'=>'edit']; } else { $actions['edit_document'] = ''; } @@ -3445,13 +3488,18 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) $actions['document_access'] = $this->printAccessButton($document, true); } if($enableClipboard) { - if($session->isOnClipboard($document)) - $actions['remove_from_clipboard'] = ''; - else - $actions['add_to_clipboard'] = ''; + if($session->isOnClipboard($document)) { +// $actions['remove_from_clipboard'] = ''; + $actions['remove_from_clipboard'] = ['class'=>'removefromclipboard', 'rel'=>'D'.$docID, 'msg'=>getMLText('splash_removed_from_clipboard'), 'title'=>getMLText("remove_from_clipboard"), 'label'=>getMLText("remove_from_clipboard"), 'icon'=>'clipboard']; + } else { +// $actions['add_to_clipboard'] = ''; + $actions['add_to_clipboard'] = ['class'=>'addtoclipboard', 'rel'=>'D'.$docID, 'msg'=>getMLText('splash_added_to_clipboard'), 'title'=>getMLText("add_to_clipboard"), 'label'=>getMLText("add_to_clipboard"), 'icon'=>'clipboard']; + } + } + if($onepage) { +// $actions['view_document'] = ''; + $actions['view_document'] = ['link'=>$this->params['settings']->_httpRoot.'out/out.ViewDocument.php?documentid='.$docID, 'title'=>getMLText("view_document"), 'label'=>getMLText("view_document"), 'icon'=>'eye']; } - if($onepage) - $actions['view_document'] = ''; /* Do not use $this->callHook() because $menuitems must be returned by the the * first hook and passed to next hook. $this->callHook() will just pass @@ -3464,14 +3512,61 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) } } - foreach($actions as $action) { - if(is_string($action)) - $content .= $action; + if (empty($this->params['settings']->_actiondropdown)) { + foreach($actions as $action) { + if(is_string($action)) + $content .= $action; + elseif(is_array($action)) { + $content .= ''; + } + } + } else { + $content .= $this->getButtonWithMenu(['label'=>'', 'menuitems'=>$actions], ['class'=>'pull-right']); } if(!empty($extracontent['end_action_list'])) $content .= $extracontent['end_action_list']; $content .= "
"; + + if (0) { + $content .= ''; + } return $content; } /* }}} */ @@ -3480,7 +3575,7 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) * * @param object $document * @param object $previewer - * @param boolean $skipcont set to true if embrasing tr shall be skipped + * @param boolean $skipcont set to true if embrasing tr shall be skipped. * In that case call documentListRowStart() before and documentListRowEnd() * after this method. */ @@ -3652,7 +3747,7 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) $subFolderAccessMode = $subFolder->getAccessMode($user); if ($accessop->check_view_access('RemoveFolder')) { if($subFolderAccessMode >= M_ALL) { - $actions['remove_folder'] = $this->printDeleteFolderButton($subFolder, 'splash_rm_folder', true); + $actions['remove_folder'] = $this->getDeleteFolderButton($subFolder, 'splash_rm_folder', true); } else { $actions['remove_folder'] = ''; } @@ -3660,6 +3755,7 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) if ($accessop->check_view_access('EditFolder')) { if($subFolderAccessMode >= M_READWRITE) { $actions['edit_folder'] = ''; + $actions['edit_folder'] = ['link'=>$this->params['settings']->_httpRoot.'out/out.EditFolder.php?folderid='.$subFolder->getID(), 'title'=>getMLText('edit_folder_props'), 'label'=>getMLText('edit_folder_props'), 'icon'=>'edit']; } else { $actions['edit_folder'] = ''; } @@ -3669,12 +3765,14 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) } if($enableClipboard) { if($session->isOnClipboard($subFolder)) - $actions['add_to_clipboard'] = ''; +// $actions['add_to_clipboard'] = ''; + $actions['add_to_clipboard'] = ['class'=>'removefromclipboard', 'title'=>getMLText('remove_from_clipboard'), 'label'=>getMLText('remove_from_clipboard'), 'rel'=>'F'.$subFolder->getID(), 'msg'=>getMLText('splash_removed_from_clipboard'), 'icon'=>'clipboard']; else - $actions['add_to_clipboard'] = ''; +// $actions['add_to_clipboard'] = ''; + $actions['add_to_clipboard'] = ['class'=>'addtoclipboard', 'title'=>getMLText('add_to_clipboard'), 'label'=>getMLText('add_to_clipboard'), 'rel'=>'F'.$subFolder->getID(), 'msg'=>getMLText('splash_added_to_clipboard'), 'icon'=>'clipboard']; } if($onepage) - $actions['view_folder'] = ''; + $actions['view_folder'] = ['link'=>$this->params['settings']->_httpRoot.'out/out.ViewFolder.php?folderid='.$subFolder->getID(), 'title'=>getMLText("view_folder"), 'label'=>getMLText("view_folder"), 'icon'=>'eye']; /* Do not use $this->callHook() because $menuitems must be returned by the the * first hook and passed to next hook. $this->callHook() will just pass @@ -3687,14 +3785,61 @@ $('body').on('click', '[id^=\"table-row-folder\"] td:nth-child(2)', function(ev) } } - foreach($actions as $action) { - if(is_string($action)) - $content .= $action; + if (empty($this->params['settings']->_actiondropdown)) { + foreach($actions as $action) { + if(is_string($action)) + $content .= $action; + elseif(is_array($action)) { + $content .= ''; + } + } + } else { + $content .= $this->getButtonWithMenu(['label'=>'', 'menuitems'=>$actions], ['class'=>'pull-right']); } if(!empty($extracontent['end_action_list'])) $content .= $extracontent['end_action_list']; $content .= "
"; + + if (0) { + $content .= '"; + } return $content; } /* }}} */ diff --git a/views/bootstrap/class.Categories.php b/views/bootstrap/class.Categories.php index 0ab5fd950..4ebf72eb0 100644 --- a/views/bootstrap/class.Categories.php +++ b/views/bootstrap/class.Categories.php @@ -100,7 +100,7 @@ $(document).ready( function() { function showCategoryForm($category) { /* {{{ */ ?> -
+ diff --git a/views/bootstrap/class.ExtensionMgr.php b/views/bootstrap/class.ExtensionMgr.php index 4c5468b3b..1c0727a00 100644 --- a/views/bootstrap/class.ExtensionMgr.php +++ b/views/bootstrap/class.ExtensionMgr.php @@ -242,7 +242,7 @@ class SeedDMS_View_ExtensionMgr extends SeedDMS_Theme_Style { $httproot = $this->params['httproot']; $extmgr = $this->params['extmgr']; $extdir = $this->params['extdir']; - $extconf = $extmgr->getExtensionConfiguration(); + $extconfall = $extmgr->getExtensionConfiguration(); echo "\n"; print "\n\n"; @@ -251,7 +251,7 @@ class SeedDMS_View_ExtensionMgr extends SeedDMS_Theme_Style { print "\n"; print "\n"; print "\n"; - foreach($extconf as $extname=>$extconf) { + foreach($extconfall as $extname=>$extconf) { $check = $extmgr->checkExtensionByDir($extname); $class = ""; if(!$settings->extensionIsDisabled($extname)) { diff --git a/views/bootstrap/class.Settings.php b/views/bootstrap/class.Settings.php index 05bc176b3..110676797 100644 --- a/views/bootstrap/class.Settings.php +++ b/views/bootstrap/class.Settings.php @@ -653,6 +653,7 @@ $this->showStartPaneContent('site', (!$currenttab || $currenttab == 'site')); showConfigText('settings_maxItemsPerPage', 'maxItemsPerPage'); ?> showConfigText('settings_incItemsPerPage', 'incItemsPerPage'); ?> showConfigCheckbox('settings_markdownComments', 'markdownComments'); ?> +showConfigCheckbox('settings_actiondropdown', 'actiondropdown'); ?>
".getMLText('version')."