From 88faf823e6bf09eb0c04c5e0293d4c3a2c346304 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Wed, 2 Nov 2022 09:57:31 +0100 Subject: [PATCH 001/247] add changes of 5.1.28 --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index aed90953d..c03837ebe 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ with 0 bytes was created by the user - fix repair of wrong file extension - fix regression in password forgotten function +- fix security issue when creating hash in password forgotten operation -------------------------------------------------------------------------------- Changes in version 5.1.27 From fd8de36db8e9d9a6f387184f314ecc0d8b3f113d Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 4 Nov 2022 20:38:09 +0100 Subject: [PATCH 002/247] allow facicon in extensions --- .htaccess | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.htaccess b/.htaccess index 4c5031251..69e29975e 100644 --- a/.htaccess +++ b/.htaccess @@ -8,7 +8,7 @@ Header set X-Content-Type-Options: "nosniff" RewriteEngine On #RewriteRule "^favicon\.ico$" "-" [L] #RewriteRule "^(favicon\.ico)$" %{HTTP_HOST}/views/bootstrap/images/favicon.svg [L,NC] -RewriteRule "(favicon\.ico)" /views/bootstrap/images/favicon.svg [L,NC] +RewriteRule "^(favicon\.ico)" /views/bootstrap/images/favicon.svg [L,NC] # Store the current location in an environment variable CWD to use # mod_rewrite in .htaccess files without knowing the RewriteBase @@ -32,7 +32,7 @@ RewriteRule ^ext/[^/]+/icon.(?:png|svg)$ - [L] RewriteCond %{REQUEST_URI} "ext/[^/]+/" RewriteRule !^ext/[^/]+/.*(?:op|out|res|node_modules) - [F] RewriteCond %{REQUEST_URI} "ext/[^/]+/res/.*$" [NC] -RewriteRule !^ext/[^/]+/res/.*\.(?:css|js|png|gif|svg|html|woff) - [F] +RewriteRule !^ext/[^/]+/res/.*\.(?:css|js|png|gif|svg|ico|html|woff) - [F] RewriteCond %{REQUEST_FILENAME} -f RewriteRule ^ext/.*$ - [L] From 13099d67aa140b0e11a72d3472b5c94eddf323a9 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sat, 5 Nov 2022 16:24:39 +0100 Subject: [PATCH 003/247] add initial support for logging and notifications --- restapi/index.php | 86 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/restapi/index.php b/restapi/index.php index 54306dea7..f80fe80d1 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -1,11 +1,40 @@ preAddService($dms, $notifier); + } + } +} + +if($settings->_enableEmail) { + $notifier->addService(new SeedDMS_EmailNotify($dms, $settings->_smtpSendFrom, $settings->_smtpServer, $settings->_smtpPort, $settings->_smtpUser, $settings->_smtpPassword)); +} + +if(isset($GLOBALS['SEEDDMS_HOOKS']['notification'])) { + foreach($GLOBALS['SEEDDMS_HOOKS']['notification'] as $notificationObj) { + if(method_exists($notificationObj, 'postAddService')) { + $notificationObj->postAddService($dms, $notifier); + } + } +} require "vendor/autoload.php"; @@ -194,10 +223,13 @@ class RestapiController { /* {{{ */ $dms = $this->container->dms; $settings = $this->container->config; + $logger = $this->container->logger; $params = $request->getParsedBody(); - if(empty($params['user']) || empty($params['pass'])) + if(empty($params['user']) || empty($params['pass'])) { + $logger->log("Login without username or password failed", PEAR_LOG_INFO); return $response->withJson(array('success'=>false, 'message'=>'No user or password given', 'data'=>''), 400); + } $username = $params['user']; $password = $params['pass']; @@ -220,12 +252,13 @@ class RestapiController { /* {{{ */ if(!$userobj) { setcookie("mydms_session", '', time()-3600, $settings->_httpRoot); + $logger->log("Login with user name '".$username."' failed", PEAR_LOG_INFO); return $response->withJson(array('success'=>false, 'message'=>'Login failed', 'data'=>''), 403); } else { require_once("../inc/inc.ClassSession.php"); $session = new SeedDMS_Session($dms->getDb()); if(!$id = $session->create(array('userid'=>$userobj->getId(), 'theme'=>$userobj->getTheme(), 'lang'=>$userobj->getLanguage()))) { - exit; + return $response->withJson(array('success'=>false, 'message'=>'Creating session failed', 'data'=>''), 500); } // Set the session cookie. @@ -236,6 +269,7 @@ class RestapiController { /* {{{ */ setcookie("mydms_session", $id, $lifetime, $settings->_httpRoot); $dms->setUser($userobj); + $logger->log("Login with user name '".$username."' successful", PEAR_LOG_INFO); return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$this->__getUserData($userobj)), 200); } } /* }}} */ @@ -445,6 +479,7 @@ class RestapiController { /* {{{ */ $dms = $this->container->dms; $userobj = $this->container->userobj; $settings = $this->container->config; + $logger = $this->container->logger; if(!$userobj) { return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); @@ -472,9 +507,12 @@ class RestapiController { /* {{{ */ $sequence = 1.0; } $newattrs = array(); - if(!empty($params['attributes'])) { - foreach($params['attributes'] as $attrname=>$attrvalue) { - $attrdef = $dms->getAttributeDefinitionByName($attrname); + if(!empty($params['attributes'])) { + foreach($params['attributes'] as $attrname=>$attrvalue) { + if((is_int($attrname) || ctype_digit($attrname)) && ((int) $attrname) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrname); + else + $attrdef = $dms->getAttributeDefinitionByName($attrname); if($attrdef) { $newattrs[$attrdef->getID()] = $attrvalue; } @@ -489,6 +527,7 @@ class RestapiController { /* {{{ */ if($folder = $parent->addSubFolder($params['name'], $comment, $userobj, $sequence, $newattrs)) { $rec = $this->__getFolderData($folder); + $logger->log("Creating folder '".$folder->getName()."' (".$folder->getId().") successful", PEAR_LOG_INFO); return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$rec), 201); } else { return $response->withJson(array('success'=>false, 'message'=>'Could not create folder', 'data'=>''), 500); @@ -644,7 +683,11 @@ class RestapiController { /* {{{ */ } $attributes = isset($params["attributes"]) ? $params["attributes"] : array(); foreach($attributes as $attrdefid=>$attribute) { - if($attrdef = $dms->getAttributeDefinition($attrdefid)) { + if((is_int($attrdefid) || ctype_digit($attrdefid)) && ((int) $attrdefid) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrdefid); + else + $attrdef = $dms->getAttributeDefinitionByName($attrdefid); + if($attrdef) { if($attribute) { if(!$attrdef->validate($attribute)) { return $response->withJson(array('success'=>false, 'message'=>getAttributeValidationText($attrdef->getValidationError(), $attrdef->getName(), $attribute), 'data'=>''), 400); @@ -725,7 +768,11 @@ class RestapiController { /* {{{ */ $comment = isset($params['comment']) ? $params['comment'] : null; $attributes = isset($params["attributes"]) ? $params["attributes"] : array(); foreach($attributes as $attrdefid=>$attribute) { - if($attrdef = $dms->getAttributeDefinition($attrdefid)) { + if((is_int($attrdefid) || ctype_digit($attrdefid)) && ((int) $attrdefid) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrdefid); + else + $attrdef = $dms->getAttributeDefinitionByName($attrdefid); + if($attrdef) { if($attribute) { if(!$attrdef->validate($attribute)) { return $response->withJson(array('success'=>false, 'message'=>getAttributeValidationText($attrdef->getValidationError(), $attrdef->getName(), $attribute), 'data'=>''), 400); @@ -1631,7 +1678,10 @@ class RestapiController { /* {{{ */ $query = $params['value']; if(empty($params['limit']) || !$limit = $params['limit']) $limit = 50; - $attrdef = $dms->getAttributeDefinitionByName($attrname); + if(ctype_digit($attrname) && ((int) $attrname) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrname); + else + $attrdef = $dms->getAttributeDefinitionByName($attrname); $entries = array(); if($attrdef) { $resArr = $attrdef->getObjects($query, $limit); @@ -2167,6 +2217,7 @@ class RestapiController { /* {{{ */ function createCategory($request, $response) { /* {{{ */ $dms = $this->container->dms; $userobj = $this->container->userobj; + $logger = $this->container->logger; $check = $this->checkIfAdmin($request, $response); if($check !== true) @@ -2182,6 +2233,7 @@ class RestapiController { /* {{{ */ return $response->withJson(array('success'=>false, 'message'=>'Category already exists', 'data'=>''), 409); } else { if($data = $dms->addDocumentCategory($params['name'])) { + $logger->log("Creating category '".$data->getName()."' (".$data->getId().") successful", PEAR_LOG_INFO); return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$this->__getCategoryData($data)), 201); } else { return $response->withJson(array('success'=>false, 'message'=>'Could not add category', 'data'=>''), 500); @@ -2381,6 +2433,8 @@ class Auth { /* {{{ */ // $this->container has the DI $dms = $this->container->dms; $settings = $this->container->config; + $logger = $this->container->logger; + $logger->log("Access with method ".$request->getMethod()." on '".$request->getUri()->getPath()."'".(isset($this->container->environment['HTTP_ORIGIN']) ? " with origin ".$this->container->environment['HTTP_ORIGIN'] : ''), PEAR_LOG_INFO); if($settings->_apiOrigin && isset($this->container->environment['HTTP_ORIGIN'])) { $origins = explode(',', $settings->_apiOrigin); if(!in_array($this->container->environment['HTTP_ORIGIN'], $origins)) { @@ -2401,6 +2455,7 @@ class Auth { /* {{{ */ } else { return $response->withStatus(403); } + $logger->log("Login with apikey as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); } else { require_once("../inc/inc.ClassSession.php"); $session = new SeedDMS_Session($dms->getDb()); @@ -2432,6 +2487,7 @@ class Auth { /* {{{ */ return $response->withStatus(403); } } +// $logger->log("Login with user name '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); $dms->setUser($userobj); } else { return $response->withStatus(403); @@ -2448,6 +2504,8 @@ $container = $app->getContainer(); $container['dms'] = $dms; $container['config'] = $settings; $container['conversionmgr'] = $conversionmgr; +$container['logger'] = $logger; +$container['fulltextservice'] = $fulltextservice; $app->add(new Auth($container)); // Make CORS preflighted request possible From 35b83b37dce9150d4fb114e0f81735777acaf497 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sat, 5 Nov 2022 16:29:10 +0100 Subject: [PATCH 004/247] add changes in 5.1.28 --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index c03837ebe..f08cee268 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ - fix repair of wrong file extension - fix regression in password forgotten function - fix security issue when creating hash in password forgotten operation +- add initial support for logging and notifications in rest api -------------------------------------------------------------------------------- Changes in version 5.1.27 From d9ba59990f878ed561c89edf3aaf7801d8309182 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 16:07:04 +0100 Subject: [PATCH 005/247] clear login failures after successful login --- webdav/webdav.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webdav/webdav.php b/webdav/webdav.php index 4aa5fa700..7eed1e7d7 100644 --- a/webdav/webdav.php +++ b/webdav/webdav.php @@ -191,6 +191,9 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if($userobj->isAdmin() && ($_SERVER['REMOTE_ADDR'] != $settings->_adminIP ) && ( $settings->_adminIP != "")) return false; + /* Clear login failures if login was successful */ + $userobj->clearLoginFailures(); + $this->user = $userobj; return true; From 9869c3d8cee3af3ac35c1f0abc472caa5855453f Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 16:07:40 +0100 Subject: [PATCH 006/247] add missing space in log message --- webdav/webdav.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webdav/webdav.php b/webdav/webdav.php index 7eed1e7d7..2393f08bd 100644 --- a/webdav/webdav.php +++ b/webdav/webdav.php @@ -175,7 +175,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if(!$userobj) { if($this->logger) - $this->logger->log('check_auth: No such user'.$user, PEAR_LOG_NOTICE); + $this->logger->log('check_auth: No such user '.$user, PEAR_LOG_NOTICE); return false; } From 57b298c4893bbee4160ffae021d4436afda1193c Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 16:08:57 +0100 Subject: [PATCH 007/247] invoke controller instead of calling run() --- webdav/webdav.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/webdav/webdav.php b/webdav/webdav.php index 2393f08bd..f402a9a8a 100644 --- a/webdav/webdav.php +++ b/webdav/webdav.php @@ -757,7 +757,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server $controller->setParam('attributes', array()); $controller->setParam('workflow', $workflow); - if(!$content = $controller->run()) { + if(!$content = $controller()) { if($this->logger) $this->logger->log('PUT: error adding new version', PEAR_LOG_ERR); unlink($tmpFile); @@ -853,7 +853,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server $controller->setParam('notificationusers', array()); $controller->setParam('maxsizeforfulltext', $settings->_maxSizeForFullText); $controller->setParam('defaultaccessdocs', $settings->_defaultAccessDocs); - if(!$document = $controller->run()) { + if(!$document = $controller()) { // if(!$res = $folder->addDocument($name, '', 0, $this->user, '', array(), $tmpFile, $name, $fileType, $mimetype, 0, array(), array(), 0, "")) { unlink($tmpFile); if($this->logger) @@ -959,7 +959,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server $controller->setParam('attributes', array()); $controller->setParam('notificationgroups', array()); $controller->setParam('notificationusers', array()); - if(!$subFolder = $controller->run()) { + if(!$subFolder = $controller()) { // if (!$folder->addSubFolder($name, '', $this->user, 0)) { return "409 Conflict ".$controller->getErrorMsg(); } @@ -1452,8 +1452,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server $controller->setParam('notificationusers', array()); $controller->setParam('maxsizeforfulltext', $settings->_maxSizeForFullText); $controller->setParam('defaultaccessdocs', $settings->_defaultAccessDocs); - if(!$document = $controller->run()) { -// if(!$newdoc = $objdest->addDocument($newdocname, '', 0, $this->user, '', array(), $fspath, $content->getOriginalFileName(), $content->getFileType(), $content->getMimeType(), 0, array(), array(), 0, "")) { + if(!$document = $controller()) { if($this->logger) $this->logger->log('COPY: error copying object', PEAR_LOG_ERR); return "409 Conflict"; From fbe6e492c8f345e9e4015224a1cdf135c6fd5bdc Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 16:09:27 +0100 Subject: [PATCH 008/247] use methods in inc/inc.ClassNotificationService.php it send notifications --- webdav/webdav.php | 190 ++++------------------------------------------ 1 file changed, 13 insertions(+), 177 deletions(-) diff --git a/webdav/webdav.php b/webdav/webdav.php index f402a9a8a..9382d6321 100644 --- a/webdav/webdav.php +++ b/webdav/webdav.php @@ -767,24 +767,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if($this->notifier) { if($this->logger) $this->logger->log('PUT: Sending Notifications', PEAR_LOG_INFO); - $notifyList = $document->getNotifyList(); - $folder = $document->getFolder(); - - $subject = "document_updated_email_subject"; - $message = "document_updated_email_body"; - $params = array(); - $params['name'] = $document->getName(); - $params['folder_path'] = $folder->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['comment'] = $document->getComment(); - $params['version_comment'] = $content->getComment(); - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewDocument.php?documentid=".$document->getID(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $this->notifier->toList($this->user, $notifyList["users"], $subject, $message, $params); - foreach ($notifyList["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + $this->notifier->sendNewDocumentVersionMail($document, $this->user); } } } @@ -863,29 +846,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if($this->notifier) { if($this->logger) $this->logger->log('PUT: Sending Notifications', PEAR_LOG_INFO); - $fnl = $folder->getNotifyList(); - $dnl = $document->getNotifyList(); - $nl = array( - 'users'=>array_unique(array_merge($dnl['users'], $fnl['users']), SORT_REGULAR), - 'groups'=>array_unique(array_merge($dnl['groups'], $fnl['groups']), SORT_REGULAR) - ); - - $subject = "new_document_email_subject"; - $message = "new_document_email_body"; - $params = array(); - $params['name'] = $name; - $params['folder_name'] = $folder->getName(); - $params['folder_path'] = $folder->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['comment'] = ''; - $params['version_comment'] = ''; - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewDocument.php?documentid=".$document->getID(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $this->notifier->toList($this->user, $nl["users"], $subject, $message, $params); - foreach ($nl["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + $this->notifier->sendNewDocumentMail($document, $this->user); } } @@ -967,28 +928,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if($this->notifier) { if($this->logger) $this->logger->log('MKCOL: Sending Notifications', PEAR_LOG_INFO); - $fnl = $folder->getNotifyList(); - $snl = $subFolder->getNotifyList(); - $nl = array( - 'users'=>array_unique(array_merge($snl['users'], $fnl['users']), SORT_REGULAR), - 'groups'=>array_unique(array_merge($snl['groups'], $fnl['groups']), SORT_REGULAR) - ); - - $subject = "new_subfolder_email_subject"; - $message = "new_subfolder_email_body"; - $params = array(); - $params['name'] = $subFolder->getName(); - $params['folder_name'] = $folder->getName(); - $params['folder_path'] = $folder->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['comment'] = ''; - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewFolder.php?folderid=".$subFolder->getID(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $this->notifier->toList($this->user, $nl["users"], $subject, $message, $params); - foreach ($nl["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + $this->notifier->sendNewFolderMail($subFolder, $this->user); } return ("201 Created"); @@ -1030,79 +970,37 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server return "409 Conflict"; } - $parent = $obj->getParent(); - $fnl = $obj->getNotifyList(); - $pnl = $parent->getNotifyList(); - $nl = array( - 'users'=>array_unique(array_merge($fnl['users'], $pnl['users']), SORT_REGULAR), - 'groups'=>array_unique(array_merge($fnl['groups'], $pnl['groups']), SORT_REGULAR) - ); - $foldername = $obj->getName(); - $controller = Controller::factory('RemoveFolder'); $controller->setParam('dms', $this->dms); $controller->setParam('user', $this->user); $controller->setParam('folder', $obj); $controller->setParam('fulltextservice', $fulltextservice); - if(!$controller->run()) { + if(!$controller()) { return "409 Conflict ".$controller->getErrorMsg(); } if($this->notifier) { if($this->logger) $this->logger->log('DELETE: Sending Notifications', PEAR_LOG_INFO); - $subject = "folder_deleted_email_subject"; - $message = "folder_deleted_email_body"; - $params = array(); - $params['name'] = $foldername; - $params['folder_path'] = $parent->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewFolder.php?folderid=".$parent->getID(); - $this->notifier->toList($this->user, $nl["users"], $subject, $message, $params); - foreach ($nl["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + $this->notifier->sendDeleteFolderMail($obj, $this->user); } } else { - /* Get the notify list before removing the document - * Also inform the users/groups of the parent folder - */ - $folder = $obj->getFolder(); - $dnl = $obj->getNotifyList(); - $fnl = $folder->getNotifyList(); - $nl = array( - 'users'=>array_unique(array_merge($dnl['users'], $fnl['users']), SORT_REGULAR), - 'groups'=>array_unique(array_merge($dnl['groups'], $fnl['groups']), SORT_REGULAR) - ); - $docname = $obj->getName(); - $controller = Controller::factory('RemoveDocument'); $controller->setParam('dms', $this->dms); $controller->setParam('user', $this->user); $controller->setParam('document', $obj); $controller->setParam('fulltextservice', $fulltextservice); - if(!$controller->run()) { + if(!$controller()) { return "409 Conflict ".$controller->getErrorMsg(); } if($this->notifier){ if($this->logger) $this->logger->log('DELETE: Sending Notifications', PEAR_LOG_INFO); - $subject = "document_deleted_email_subject"; - $message = "document_deleted_email_body"; - $params = array(); - $params['name'] = $docname; - $params['folder_path'] = $folder->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewFolder.php?folderid=".$folder->getID(); - $this->notifier->toList($this->user, $nl["users"], $subject, $message, $params); - foreach ($nl["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + /* $obj still has the data from the just deleted document, + * which is just enough to send the email. + */ + $this->notifier->sendDeleteDocumentMail($obj, $this->user); } } @@ -1211,27 +1109,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if($this->notifier) { if($this->logger) $this->logger->log('MOVE: Sending Notifications', PEAR_LOG_INFO); - $nl1 = $oldFolder->getNotifyList(); - $nl2 = $objsource->getNotifyList(); - $nl3 = $objdest->getNotifyList(); - $nl = array( - 'users'=>array_unique(array_merge($nl1['users'], $nl2['users'], $nl3['users']), SORT_REGULAR), - 'groups'=>array_unique(array_merge($nl1['groups'], $nl2['groups'], $nl3['groups']), SORT_REGULAR) - ); - $subject = "document_moved_email_subject"; - $message = "document_moved_email_body"; - $params = array(); - $params['name'] = $objsource->getName(); - $params['old_folder_path'] = $oldFolder->getFolderPathPlain(); - $params['new_folder_path'] = $objdest->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewDocument.php?documentid=".$objsource->getID(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $this->notifier->toList($this->user, $nl["users"], $subject, $message, $params); - foreach ($nl["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + $this->notifier->sendMovedDocumentMail($objsource, $this->user, $oldFolder); } } else { return "500 Internal server error"; @@ -1254,27 +1132,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if($this->notifier) { if($this->logger) $this->logger->log('MOVE: Sending Notifications', PEAR_LOG_INFO); - $nl1 = $oldFolder->getNotifyList(); - $nl2 = $objsource->getNotifyList(); - $nl3 = $objdest->getNotifyList(); - $nl = array( - 'users'=>array_unique(array_merge($nl1['users'], $nl2['users'], $nl3['users']), SORT_REGULAR), - 'groups'=>array_unique(array_merge($nl1['groups'], $nl2['groups'], $nl3['groups']), SORT_REGULAR) - ); - $subject = "folder_moved_email_subject"; - $message = "folder_moved_email_body"; - $params = array(); - $params['name'] = $objsource->getName(); - $params['old_folder_path'] = $oldFolder->getFolderPathPlain(); - $params['new_folder_path'] = $objdest->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewFolder.php?folderid=".$objsource->getID(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $this->notifier->toList($this->user, $nl["users"], $subject, $message, $params); - foreach ($nl["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + $this->notifier->sendMovedFolderMail($objsource, $this->user, $oldFolder); } } else { return "500 Internal server error"; @@ -1461,29 +1319,7 @@ class HTTP_WebDAV_Server_SeedDMS extends HTTP_WebDAV_Server if($this->notifier) { if($this->logger) $this->logger->log('COPY: Sending Notifications', PEAR_LOG_INFO); - $fnl = $objdest->getNotifyList(); - $dnl = $document->getNotifyList(); - $nl = array( - 'users'=>array_unique(array_merge($dnl['users'], $fnl['users']), SORT_REGULAR), - 'groups'=>array_unique(array_merge($dnl['groups'], $fnl['groups']), SORT_REGULAR) - ); - - $subject = "new_document_email_subject"; - $message = "new_document_email_body"; - $params = array(); - $params['name'] = $newdocname; - $params['folder_name'] = $objdest->getName(); - $params['folder_path'] = $objdest->getFolderPathPlain(); - $params['username'] = $this->user->getFullName(); - $params['comment'] = ''; - $params['version_comment'] = ''; - $params['url'] = getBaseUrl().$settings->_httpRoot."out/out.ViewDocument.php?documentid=".$document->getID(); - $params['sitename'] = $settings->_siteName; - $params['http_root'] = $settings->_httpRoot; - $this->notifier->toList($this->user, $nl["users"], $subject, $message, $params); - foreach ($nl["groups"] as $grp) { - $this->notifier->toGroup($this->user, $grp, $subject, $message, $params); - } + $this->notifier->sendNewDocumentMail($document, $this->user); } return "201 Created"; } From 441d8d6c0fda7cd38e6dfebe86a6efdbe0e21088 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 16:09:46 +0100 Subject: [PATCH 009/247] add changes of 5.1.28 --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f08cee268..e43986e67 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,8 @@ - fix regression in password forgotten function - fix security issue when creating hash in password forgotten operation - add initial support for logging and notifications in rest api +- use methods in inc/inc.ClassNotificationService.php for webdav +- clear login failures when login by webdav succeeds -------------------------------------------------------------------------------- Changes in version 5.1.27 From 257c23aead1ec670e3c9ec1fc063bb3e65db0e06 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 16:38:56 +0100 Subject: [PATCH 010/247] fix indenting of lines --- restapi/index.php | 66 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/restapi/index.php b/restapi/index.php index f80fe80d1..db1b6eabc 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -17,23 +17,23 @@ require_once("../inc/inc.ClassController.php"); $notifier = new SeedDMS_NotificationService($logger, $settings); if(isset($GLOBALS['SEEDDMS_HOOKS']['notification'])) { - foreach($GLOBALS['SEEDDMS_HOOKS']['notification'] as $notificationObj) { - if(method_exists($notificationObj, 'preAddService')) { - $notificationObj->preAddService($dms, $notifier); - } - } + foreach($GLOBALS['SEEDDMS_HOOKS']['notification'] as $notificationObj) { + if(method_exists($notificationObj, 'preAddService')) { + $notificationObj->preAddService($dms, $notifier); + } + } } if($settings->_enableEmail) { - $notifier->addService(new SeedDMS_EmailNotify($dms, $settings->_smtpSendFrom, $settings->_smtpServer, $settings->_smtpPort, $settings->_smtpUser, $settings->_smtpPassword)); + $notifier->addService(new SeedDMS_EmailNotify($dms, $settings->_smtpSendFrom, $settings->_smtpServer, $settings->_smtpPort, $settings->_smtpUser, $settings->_smtpPassword)); } if(isset($GLOBALS['SEEDDMS_HOOKS']['notification'])) { - foreach($GLOBALS['SEEDDMS_HOOKS']['notification'] as $notificationObj) { - if(method_exists($notificationObj, 'postAddService')) { - $notificationObj->postAddService($dms, $notifier); - } - } + foreach($GLOBALS['SEEDDMS_HOOKS']['notification'] as $notificationObj) { + if(method_exists($notificationObj, 'postAddService')) { + $notificationObj->postAddService($dms, $notifier); + } + } } require "vendor/autoload.php"; @@ -269,7 +269,7 @@ class RestapiController { /* {{{ */ setcookie("mydms_session", $id, $lifetime, $settings->_httpRoot); $dms->setUser($userobj); - $logger->log("Login with user name '".$username."' successful", PEAR_LOG_INFO); + $logger->log("Login with user name '".$username."' successful", PEAR_LOG_INFO); return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$this->__getUserData($userobj)), 200); } } /* }}} */ @@ -507,12 +507,12 @@ class RestapiController { /* {{{ */ $sequence = 1.0; } $newattrs = array(); - if(!empty($params['attributes'])) { - foreach($params['attributes'] as $attrname=>$attrvalue) { - if((is_int($attrname) || ctype_digit($attrname)) && ((int) $attrname) > 0) - $attrdef = $dms->getAttributeDefinition((int) $attrname); - else - $attrdef = $dms->getAttributeDefinitionByName($attrname); + if(!empty($params['attributes'])) { + foreach($params['attributes'] as $attrname=>$attrvalue) { + if((is_int($attrname) || ctype_digit($attrname)) && ((int) $attrname) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrname); + else + $attrdef = $dms->getAttributeDefinitionByName($attrname); if($attrdef) { $newattrs[$attrdef->getID()] = $attrvalue; } @@ -527,7 +527,7 @@ class RestapiController { /* {{{ */ if($folder = $parent->addSubFolder($params['name'], $comment, $userobj, $sequence, $newattrs)) { $rec = $this->__getFolderData($folder); - $logger->log("Creating folder '".$folder->getName()."' (".$folder->getId().") successful", PEAR_LOG_INFO); + $logger->log("Creating folder '".$folder->getName()."' (".$folder->getId().") successful", PEAR_LOG_INFO); return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$rec), 201); } else { return $response->withJson(array('success'=>false, 'message'=>'Could not create folder', 'data'=>''), 500); @@ -683,10 +683,10 @@ class RestapiController { /* {{{ */ } $attributes = isset($params["attributes"]) ? $params["attributes"] : array(); foreach($attributes as $attrdefid=>$attribute) { - if((is_int($attrdefid) || ctype_digit($attrdefid)) && ((int) $attrdefid) > 0) - $attrdef = $dms->getAttributeDefinition((int) $attrdefid); - else - $attrdef = $dms->getAttributeDefinitionByName($attrdefid); + if((is_int($attrdefid) || ctype_digit($attrdefid)) && ((int) $attrdefid) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrdefid); + else + $attrdef = $dms->getAttributeDefinitionByName($attrdefid); if($attrdef) { if($attribute) { if(!$attrdef->validate($attribute)) { @@ -768,10 +768,10 @@ class RestapiController { /* {{{ */ $comment = isset($params['comment']) ? $params['comment'] : null; $attributes = isset($params["attributes"]) ? $params["attributes"] : array(); foreach($attributes as $attrdefid=>$attribute) { - if((is_int($attrdefid) || ctype_digit($attrdefid)) && ((int) $attrdefid) > 0) - $attrdef = $dms->getAttributeDefinition((int) $attrdefid); - else - $attrdef = $dms->getAttributeDefinitionByName($attrdefid); + if((is_int($attrdefid) || ctype_digit($attrdefid)) && ((int) $attrdefid) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrdefid); + else + $attrdef = $dms->getAttributeDefinitionByName($attrdefid); if($attrdef) { if($attribute) { if(!$attrdef->validate($attribute)) { @@ -1678,10 +1678,10 @@ class RestapiController { /* {{{ */ $query = $params['value']; if(empty($params['limit']) || !$limit = $params['limit']) $limit = 50; - if(ctype_digit($attrname) && ((int) $attrname) > 0) - $attrdef = $dms->getAttributeDefinition((int) $attrname); - else - $attrdef = $dms->getAttributeDefinitionByName($attrname); + if(ctype_digit($attrname) && ((int) $attrname) > 0) + $attrdef = $dms->getAttributeDefinition((int) $attrname); + else + $attrdef = $dms->getAttributeDefinitionByName($attrname); $entries = array(); if($attrdef) { $resArr = $attrdef->getObjects($query, $limit); @@ -2455,7 +2455,7 @@ class Auth { /* {{{ */ } else { return $response->withStatus(403); } - $logger->log("Login with apikey as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + $logger->log("Login with apikey as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); } else { require_once("../inc/inc.ClassSession.php"); $session = new SeedDMS_Session($dms->getDb()); @@ -2487,7 +2487,7 @@ class Auth { /* {{{ */ return $response->withStatus(403); } } -// $logger->log("Login with user name '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); +// $logger->log("Login with user name '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); $dms->setUser($userobj); } else { return $response->withStatus(403); From 0e1b00d0b5fb882855d40b88af4c9ea625f3340b Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 17:02:38 +0100 Subject: [PATCH 011/247] use controller to create subfolder, send notifications --- restapi/index.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/restapi/index.php b/restapi/index.php index db1b6eabc..4ab5d1c26 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -480,6 +480,8 @@ class RestapiController { /* {{{ */ $userobj = $this->container->userobj; $settings = $this->container->config; $logger = $this->container->logger; + $fulltextservice = $this->container->fulltextservice; + $notifier = $this->container->notifier; if(!$userobj) { return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); @@ -524,10 +526,24 @@ class RestapiController { /* {{{ */ return $response->withJson(array('success'=>false, 'message'=>getMLText("subfolder_duplicate_name"), 'data'=>''), 409); } } - if($folder = $parent->addSubFolder($params['name'], $comment, $userobj, $sequence, $newattrs)) { - $rec = $this->__getFolderData($folder); - $logger->log("Creating folder '".$folder->getName()."' (".$folder->getId().") successful", PEAR_LOG_INFO); + $controller = Controller::factory('AddSubFolder'); + $controller->setParam('dms', $dms); + $controller->setParam('user', $userobj); + $controller->setParam('fulltextservice', $fulltextservice); + $controller->setParam('folder', $parent); + $controller->setParam('name', $params['name']); + $controller->setParam('comment', $comment); + $controller->setParam('sequence', $sequence); + $controller->setParam('attributes', $newattrs); + $controller->setParam('notificationgroups', []); + $controller->setParam('notificationusers', []); + if($folder = $controller()) { + $rec = $this->__getFolderData($folder); + $logger->log("Creating folder '".$folder->getName()."' (".$folder->getId().") successful", PEAR_LOG_INFO); + if($notifier) { + $notifier->sendNewFolderMail($folder, $userobj); + } return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$rec), 201); } else { return $response->withJson(array('success'=>false, 'message'=>'Could not create folder', 'data'=>''), 500); @@ -2506,6 +2522,7 @@ $container['config'] = $settings; $container['conversionmgr'] = $conversionmgr; $container['logger'] = $logger; $container['fulltextservice'] = $fulltextservice; +$container['notifier'] = $notifier; $app->add(new Auth($container)); // Make CORS preflighted request possible From 1ec3367695bcf9db90a02ba14c80238999435211 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 20:03:12 +0100 Subject: [PATCH 012/247] fix line indenting --- SeedDMS_Core/Core/inc.ClassObject.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SeedDMS_Core/Core/inc.ClassObject.php b/SeedDMS_Core/Core/inc.ClassObject.php index a2f0b1ae4..ec1fa3124 100644 --- a/SeedDMS_Core/Core/inc.ClassObject.php +++ b/SeedDMS_Core/Core/inc.ClassObject.php @@ -38,10 +38,10 @@ class SeedDMS_Core_Object { /* {{{ */ */ public $_dms; - /** - * SeedDMS_Core_Object constructor. - * @param $id - */ + /** + * SeedDMS_Core_Object constructor. + * @param $id + */ function __construct($id) { /* {{{ */ $this->_id = $id; $this->_dms = null; From a5802ad3e8de492863a95663b78f6535774222b5 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 20:03:58 +0100 Subject: [PATCH 013/247] add route /document/{id}/attribute/{attrdefid} --- restapi/index.php | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/restapi/index.php b/restapi/index.php index 4ab5d1c26..438761b7d 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -1559,6 +1559,56 @@ class RestapiController { /* {{{ */ } } /* }}} */ + function setDocumentAttribute($request, $response, $args) { /* {{{ */ + $dms = $this->container->dms; + $userobj = $this->container->userobj; + + if(!$userobj) { + return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); + return; + } + + if(!ctype_digit($args['id']) || $args['id'] == 0) { + return $response->withJson(array('success'=>false, 'message'=>'No document given', 'data'=>''), 400); + return; + } + if(!ctype_digit($args['attrdefid']) || $args['attrdefid'] == 0) { + return $response->withJson(array('success'=>false, 'message'=>'No attribute definition id given', 'data'=>''), 400); + return; + } + $attrdef = $dms->getAttributeDefinition($args['attrdefid']); + $doc = $dms->getDocument($args['id']); + if($doc && $attrdef) { + if($attrdef->getObjType() !== SeedDMS_Core_AttributeDefinition::objtype_document) { + return $response->withJson(array('success'=>false, 'message'=>'Attribute definition not suitable for documents', 'data'=>''), 500); + } + + $params = $request->getParsedBody(); + $new = $doc->getAttributeValue($attrdef) ? true : false; + if(!$attrdef->validate($params['value'], $doc, $new)) { + return $response->withJson(array('success'=>false, 'message'=>'Validation of attribute value failed: '.$attrdef->getValidationError(), 'data'=>''), 500); + } + if($doc->getAccessMode($userobj, 'setDocumentAttribute') > M_READ) { + if ($doc->setAttributeValue($attrdef, $params['value'])) { + return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>''), 201); + } else { + return $response->withJson(array('success'=>false, 'message'=>'Could not set attribute value of document', 'data'=>''), 500); + } + } else { + return $response->withJson(array('success'=>false, 'message'=>'No access on document', 'data'=>''), 403); + } + } else { + if(!$doc) + return $response->withJson(array('success'=>false, 'message'=>'No such document', 'data'=>''), 404); + if(!$attrdef) + return $response->withJson(array('success'=>false, 'message'=>'No such attr definition', 'data'=>''), 404); + return $response->withJson(array('success'=>false, 'message'=>'Could not find user or document', 'data'=>''), 500); + } + + $data = $this->__getDocumentData($doc); + return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$data), 200); + } /* }}} */ + function getAccount($request, $response) { /* {{{ */ $dms = $this->container->dms; $userobj = $this->container->userobj; @@ -2575,6 +2625,7 @@ $app->delete('/document/{id}/categories', \RestapiController::class.':removeDocu $app->delete('/document/{id}/category/{catid}', \RestapiController::class.':removeDocumentCategory'); $app->post('/document/{id}/category/{catid}', \RestapiController::class.':addDocumentCategory'); $app->put('/document/{id}/owner/{userid}', \RestapiController::class.':setDocumentOwner'); +$app->put('/document/{id}/attribute/{attrdefid}', \RestapiController::class.':setDocumentAttribute'); $app->put('/account/fullname', \RestapiController::class.':setFullName'); $app->put('/account/email', \RestapiController::class.':setEmail'); $app->get('/account/documents/locked', \RestapiController::class.':getLockedDocuments'); From 7cba489f1a18f378ecb2684b353ee054bb0ca89a Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Sun, 6 Nov 2022 20:04:12 +0100 Subject: [PATCH 014/247] fix typo --- SeedDMS_Core/Core/inc.ClassAttribute.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SeedDMS_Core/Core/inc.ClassAttribute.php b/SeedDMS_Core/Core/inc.ClassAttribute.php index a474783b3..779673389 100644 --- a/SeedDMS_Core/Core/inc.ClassAttribute.php +++ b/SeedDMS_Core/Core/inc.ClassAttribute.php @@ -1204,7 +1204,7 @@ class SeedDMS_Core_AttributeDefinition { /* {{{ */ * @param boolean $new set to true if the value is new value and not taken from * an existing attribute * (this will only be passed to the onAttributeValidate callback) - * @return boolean true if validation succeds, otherwise false + * @return boolean true if validation succeeds, otherwise false */ function validate($attrvalue, $object=null, $new=false) { /* {{{ */ /* Check if 'onAttributeValidate' callback is set */ From 239487e198c5c0215eae4417f4dd9d23e33a900f Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:13:15 +0100 Subject: [PATCH 015/247] fix sql error when deleting a folder attribute --- SeedDMS_Core/Core/inc.ClassAttribute.php | 2 +- SeedDMS_Core/package.xml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/SeedDMS_Core/Core/inc.ClassAttribute.php b/SeedDMS_Core/Core/inc.ClassAttribute.php index 779673389..93f8a3e41 100644 --- a/SeedDMS_Core/Core/inc.ClassAttribute.php +++ b/SeedDMS_Core/Core/inc.ClassAttribute.php @@ -247,7 +247,7 @@ class SeedDMS_Core_Attribute { /* {{{ */ break; case $this->_dms->getClassname('folder'): if(trim($value) === '') - $queryStr = "DELETE FROM `tblFolderAttributes WHERE` `folder` = " . $this->_obj->getID() . " AND `attrdef` = " . $this->_attrdef->getId(); + $queryStr = "DELETE FROM `tblFolderAttributes` WHERE `folder` = " . $this->_obj->getID() . " AND `attrdef` = " . $this->_attrdef->getId(); else $queryStr = "UPDATE `tblFolderAttributes` SET `value` = ".$db->qstr($value)." WHERE `folder` = " . $this->_obj->getID() . " AND `attrdef` = " . $this->_attrdef->getId(); break; diff --git a/SeedDMS_Core/package.xml b/SeedDMS_Core/package.xml index e384b9ad2..83473231c 100644 --- a/SeedDMS_Core/package.xml +++ b/SeedDMS_Core/package.xml @@ -12,7 +12,7 @@ uwe@steinmann.cx yes - 2022-09-24 + 2022-11-07 5.1.28 @@ -27,6 +27,7 @@ - fix SeedDMS_Core_User::getDocumentContents() - fix SeedDMS_Core_File::fileExtension() - SeedDMS_Core_DMS::createPasswordRequest() creates a cryptographically secure hash +- fix sql error when deleting a folder attribute From b068c385e7f43ed7523cdb87e9752c660796da4a Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:16:10 +0100 Subject: [PATCH 016/247] add new method __getAttributesData() --- restapi/index.php | 49 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/restapi/index.php b/restapi/index.php index 438761b7d..cb7ad9bbf 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -48,6 +48,22 @@ class RestapiController { /* {{{ */ $this->container = $container; } + protected function __getAttributesData($obj) { /* {{{ */ + $attributes = $obj->getAttributes(); + $attrvalues = array(); + if($attributes) { + foreach($attributes as $attrdefid=>$attribute) { + $attrdef = $attribute->getAttributeDefinition(); + $attrvalues[] = array( + 'id'=>$attrdef->getId(), + 'name'=>$attrdef->getName(), + 'value'=>$attribute->getValue() + ); + } + } + return $attrvalues; + } + protected function __getDocumentData($document) { /* {{{ */ $data = array( 'type'=>'document', @@ -87,19 +103,13 @@ class RestapiController { /* {{{ */ } $data['categories'] = $c; } - $attributes = $document->getAttributes(); + $attributes = $this->__getAttributesData($document); if($attributes) { - $attrvalues = array(); - foreach($attributes as $attrdefid=>$attribute) - $attrvalues[] = array('id'=>(int)$attrdefid, 'value'=>$attribute->getValue()); - $data['attributes'] = $attrvalues; + $data['attributes'] = $attributes; } - $attributes = $lc->getAttributes(); + $attributes = $this->__getAttributesData($lc); if($attributes) { - $attrvalues = array(); - foreach($attributes as $attrdefid=>$attribute) - $attrvalues[] = array('id'=>(int)$attrdefid, 'value'=>$attribute->getValue()); - $data['version-attributes'] = $attrvalues; + $data['version-attributes'] = $attributes; } return $data; } /* }}} */ @@ -146,12 +156,9 @@ class RestapiController { /* {{{ */ 'comment'=>$folder->getComment(), 'date'=>date('Y-m-d H:i:s', $folder->getDate()), ); - $attributes = $folder->getAttributes(); + $attributes = $this->__getAttributesData($folder); if($attributes) { - $attrvalues = array(); - foreach($attributes as $attrdefid=>$attribute) - $attrvalues[] = array('id'=>(int)$attrdefid, 'value'=>$attribute->getValue()); - $data['attributes'] = $attrvalues; + $data['attributes'] = $attributes; } return $data; } /* }}} */ @@ -421,16 +428,8 @@ class RestapiController { /* {{{ */ $folder = $dms->getFolder($args['id']); if($folder) { if ($folder->getAccessMode($userobj) >= M_READ) { - $recs = array(); - $attributes = $folder->getAttributes(); - foreach($attributes as $attribute) { - $recs[] = array( - 'id'=>(int)$attribute->getId(), - 'value'=>$attribute->getValue(), - 'name'=>$attribute->getAttributeDefinition()->getName(), - ); - } - return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$recs), 200); + $attributes = $this->__getAttributesData($folder); + return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$attributes), 200); } else { return $response->withJson(array('success'=>false, 'message'=>'No access', 'data'=>''), 403); } From 8484d3897449fa004acdbcf06a4e5d967ac5cee3 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:17:03 +0100 Subject: [PATCH 017/247] add new method getDocumentContentAttributes() --- restapi/index.php | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/restapi/index.php b/restapi/index.php index cb7ad9bbf..445f7aa72 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -1343,16 +1343,39 @@ class RestapiController { /* {{{ */ $document = $dms->getDocument($args['id']); if($document) { if ($document->getAccessMode($userobj) >= M_READ) { - $recs = array(); - $attributes = $document->getAttributes(); - foreach($attributes as $attribute) { - $recs[] = array( - 'id'=>(int)$attribute->getId(), - 'value'=>$attribute->getValue(), - 'name'=>$attribute->getAttributeDefinition()->getName(), - ); + $attributes = $this->__getAttributesData($document); + return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$attributes), 200); + } else { + return $response->withJson(array('success'=>false, 'message'=>'No access', 'data'=>''), 403); + } + } else { + if($document === null) + $status=404; + else + $status=500; + return $response->withJson(array('success'=>false, 'message'=>'No document', 'data'=>''), $status); + } + } /* }}} */ + + function getDocumentContentAttributes($request, $response, $args) { /* {{{ */ + $dms = $this->container->dms; + $userobj = $this->container->userobj; + + $document = $dms->getDocument($args['id']); + if($document) { + if ($document->getAccessMode($userobj) >= M_READ) { + + $version = $document->getContentByVersion($args['version']); + if($version) { + if($version->getAccessMode($userobj) >= M_READ) { + $attributes = $this->__getAttributesData($version); + return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$attributes), 200); + } else { + return $response->withJson(array('success'=>false, 'message'=>'No access on version', 'data'=>''), 403); + } + } else { + return $response->withJson(array('success'=>false, 'message'=>'No version', 'data'=>''), 404); } - return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$recs), 200); } else { return $response->withJson(array('success'=>false, 'message'=>'No access', 'data'=>''), 403); } From 6d13883e8f392fa74a6176ab9fecd10c912c1d49 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:18:26 +0100 Subject: [PATCH 018/247] add logging and fix status codes in setDocumentAttribute() --- restapi/index.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/restapi/index.php b/restapi/index.php index 445f7aa72..a4e750eb5 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -1584,6 +1584,7 @@ class RestapiController { /* {{{ */ function setDocumentAttribute($request, $response, $args) { /* {{{ */ $dms = $this->container->dms; $userobj = $this->container->userobj; + $logger = $this->container->logger; if(!$userobj) { return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); @@ -1602,16 +1603,20 @@ class RestapiController { /* {{{ */ $doc = $dms->getDocument($args['id']); if($doc && $attrdef) { if($attrdef->getObjType() !== SeedDMS_Core_AttributeDefinition::objtype_document) { - return $response->withJson(array('success'=>false, 'message'=>'Attribute definition not suitable for documents', 'data'=>''), 500); + return $response->withJson(array('success'=>false, 'message'=>'Attribute definition not suitable for documents', 'data'=>''), 409); } $params = $request->getParsedBody(); + if(!isset($params['value'])) { + return $response->withJson(array('success'=>false, 'message'=>'Attribute value not set', 'data'=>''), 400); + } $new = $doc->getAttributeValue($attrdef) ? true : false; if(!$attrdef->validate($params['value'], $doc, $new)) { - return $response->withJson(array('success'=>false, 'message'=>'Validation of attribute value failed: '.$attrdef->getValidationError(), 'data'=>''), 500); + return $response->withJson(array('success'=>false, 'message'=>'Validation of attribute value failed: '.$attrdef->getValidationError(), 'data'=>''), 400); } if($doc->getAccessMode($userobj, 'setDocumentAttribute') > M_READ) { if ($doc->setAttributeValue($attrdef, $params['value'])) { + $logger->log("Setting attribute '".$attrdef->getName()."' (".$attrdef->getId().") to '".$params['value']."' successful", PEAR_LOG_INFO); return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>''), 201); } else { return $response->withJson(array('success'=>false, 'message'=>'Could not set attribute value of document', 'data'=>''), 500); From 196fa91a27d181cd9be48c2a99b36fd36cd5a56a Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:19:07 +0100 Subject: [PATCH 019/247] add method setDocumentContentAttribute() --- restapi/index.php | 114 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/restapi/index.php b/restapi/index.php index a4e750eb5..8774fa5ae 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -1631,9 +1631,119 @@ class RestapiController { /* {{{ */ return $response->withJson(array('success'=>false, 'message'=>'No such attr definition', 'data'=>''), 404); return $response->withJson(array('success'=>false, 'message'=>'Could not find user or document', 'data'=>''), 500); } + } /* }}} */ - $data = $this->__getDocumentData($doc); - return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>$data), 200); + function setDocumentContentAttribute($request, $response, $args) { /* {{{ */ + $dms = $this->container->dms; + $userobj = $this->container->userobj; + $logger = $this->container->logger; + + if(!$userobj) { + return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); + return; + } + + if(!ctype_digit($args['id']) || $args['id'] == 0) { + return $response->withJson(array('success'=>false, 'message'=>'No document given', 'data'=>''), 400); + return; + } + if(!ctype_digit($args['version']) || $args['version'] == 0) { + return $response->withJson(array('success'=>false, 'message'=>'No version number given', 'data'=>''), 400); + return; + } + if(!ctype_digit($args['attrdefid']) || $args['attrdefid'] == 0) { + return $response->withJson(array('success'=>false, 'message'=>'No attribute definition id given', 'data'=>''), 400); + return; + } + $attrdef = $dms->getAttributeDefinition($args['attrdefid']); + if($doc = $dms->getDocument($args['id'])) + $version = $doc->getContentByVersion($args['version']); + if($doc && $attrdef && $version) { + if($attrdef->getObjType() !== SeedDMS_Core_AttributeDefinition::objtype_documentcontent) { + return $response->withJson(array('success'=>false, 'message'=>'Attribute definition not suitable for document versions', 'data'=>''), 409); + } + + $params = $request->getParsedBody(); + if(!isset($params['value'])) { + return $response->withJson(array('success'=>false, 'message'=>'Attribute value not set', 'data'=>''), 400); + } + $new = $version->getAttributeValue($attrdef) ? true : false; + if(!$attrdef->validate($params['value'], $version, $new)) { + return $response->withJson(array('success'=>false, 'message'=>'Validation of attribute value failed: '.$attrdef->getValidationError(), 'data'=>''), 400); + } + if($doc->getAccessMode($userobj, 'setDocumentContentAttribute') > M_READ) { + if ($version->setAttributeValue($attrdef, $params['value'])) { + $logger->log("Setting attribute '".$attrdef->getName()."' (".$attrdef->getId().") to '".$params['value']."' successful", PEAR_LOG_INFO); + return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>''), 201); + } else { + return $response->withJson(array('success'=>false, 'message'=>'Could not set attribute value of document content', 'data'=>''), 500); + } + } else { + return $response->withJson(array('success'=>false, 'message'=>'No access on document', 'data'=>''), 403); + } + } else { + if(!$doc) + return $response->withJson(array('success'=>false, 'message'=>'No such document', 'data'=>''), 404); + if(!$version) + return $response->withJson(array('success'=>false, 'message'=>'No such version', 'data'=>''), 404); + if(!$attrdef) + return $response->withJson(array('success'=>false, 'message'=>'No such attr definition', 'data'=>''), 404); + return $response->withJson(array('success'=>false, 'message'=>'Could not find user or document', 'data'=>''), 500); + } + } /* }}} */ + + function setFolderAttribute($request, $response, $args) { /* {{{ */ + $dms = $this->container->dms; + $userobj = $this->container->userobj; + $logger = $this->container->logger; + + if(!$userobj) { + return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); + return; + } + + if(!ctype_digit($args['id']) || $args['id'] == 0) { + return $response->withJson(array('success'=>false, 'message'=>'No folder given', 'data'=>''), 400); + return; + } + if(!ctype_digit($args['attrdefid']) || $args['attrdefid'] == 0) { + return $response->withJson(array('success'=>false, 'message'=>'No attribute definition id given', 'data'=>''), 400); + return; + } + $attrdef = $dms->getAttributeDefinition($args['attrdefid']); + $obj = $dms->getFolder($args['id']); + if($obj && $attrdef) { + if($attrdef->getObjType() !== SeedDMS_Core_AttributeDefinition::objtype_folder) { + return $response->withJson(array('success'=>false, 'message'=>'Attribute definition not suitable for folders', 'data'=>''), 409); + } + + $params = $request->getParsedBody(); + if(!isset($params['value'])) { + return $response->withJson(array('success'=>false, 'message'=>'Attribute value not set', 'data'=>''.$request->getHeader('Content-Type')[0]), 400); + } + if(strlen($params['value'])) { + $new = $obj->getAttributeValue($attrdef) ? true : false; + if(!$attrdef->validate($params['value'], $obj, $new)) { + return $response->withJson(array('success'=>false, 'message'=>'Validation of attribute value failed: '.$attrdef->getValidationError(), 'data'=>''), 400); + } + } + if($obj->getAccessMode($userobj, 'setFolderAttribute') > M_READ) { + if ($obj->setAttributeValue($attrdef, $params['value'])) { + $logger->log("Setting attribute '".$attrdef->getName()."' (".$attrdef->getId().") to '".$params['value']."' successful", PEAR_LOG_INFO); + return $response->withJson(array('success'=>true, 'message'=>'', 'data'=>''), 201); + } else { + return $response->withJson(array('success'=>false, 'message'=>'Could not set attribute value of folder', 'data'=>''), 500); + } + } else { + return $response->withJson(array('success'=>false, 'message'=>'No access on folder', 'data'=>''), 403); + } + } else { + if(!$obj) + return $response->withJson(array('success'=>false, 'message'=>'No such folder', 'data'=>''), 404); + if(!$attrdef) + return $response->withJson(array('success'=>false, 'message'=>'No such attr definition', 'data'=>''), 404); + return $response->withJson(array('success'=>false, 'message'=>'Could not find user or folder', 'data'=>''), 500); + } } /* }}} */ function getAccount($request, $response) { /* {{{ */ From 31e47d4fbebab360c2667415147efa0acf84cebd Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:19:58 +0100 Subject: [PATCH 020/247] do more logging during authentication --- restapi/index.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/restapi/index.php b/restapi/index.php index 8774fa5ae..354b26e22 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -2639,6 +2639,7 @@ class Auth { /* {{{ */ $logger = $this->container->logger; $logger->log("Access with method ".$request->getMethod()." on '".$request->getUri()->getPath()."'".(isset($this->container->environment['HTTP_ORIGIN']) ? " with origin ".$this->container->environment['HTTP_ORIGIN'] : ''), PEAR_LOG_INFO); if($settings->_apiOrigin && isset($this->container->environment['HTTP_ORIGIN'])) { + $logger->log("Checking origin", PEAR_LOG_DEBUG); $origins = explode(',', $settings->_apiOrigin); if(!in_array($this->container->environment['HTTP_ORIGIN'], $origins)) { return $response->withStatus(403); @@ -2648,9 +2649,11 @@ class Auth { /* {{{ */ * don't even try to authorize. */ if($request->getMethod() == 'OPTIONS') { + $logger->log("Received preflight options request", PEAR_LOG_DEBUG); } elseif(!in_array($request->getUri()->getPath(), array('login')) && substr($request->getUri()->getPath(), 0, 5) != 'echo/') { $userobj = null; if(!empty($this->container->environment['HTTP_AUTHORIZATION']) && !empty($settings->_apiKey) && !empty($settings->_apiUserId)) { + $logger->log("Authorization key: ".$this->container->environment['HTTP_AUTHORIZATION'], PEAR_LOG_DEBUG); if($settings->_apiKey == $this->container->environment['HTTP_AUTHORIZATION']) { if(!($userobj = $dms->getUser($settings->_apiUserId))) { return $response->withStatus(403); From 6f4ec80ad6901a66e0051c81bae7c76a45e9ddbe Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:20:30 +0100 Subject: [PATCH 021/247] add new routes for changing attributes --- restapi/index.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/restapi/index.php b/restapi/index.php index 354b26e22..9d6a8e53f 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -2743,6 +2743,7 @@ $app->get('/folder/{id}/children', \RestapiController::class.':getFolderChildren $app->get('/folder/{id}/parent', \RestapiController::class.':getFolderParent'); $app->get('/folder/{id}/path', \RestapiController::class.':getFolderPath'); $app->get('/folder/{id}/attributes', \RestapiController::class.':getFolderAttributes'); +$app->put('/folder/{id}/attribute/{attrdefid}', \RestapiController::class.':setFolderAttribute'); $app->post('/folder/{id}/folder', \RestapiController::class.':createFolder'); $app->put('/folder/{id}/document', \RestapiController::class.':uploadDocumentPut'); $app->post('/folder/{id}/document', \RestapiController::class.':uploadDocument'); @@ -2755,17 +2756,19 @@ $app->get('/document/{id}/content', \RestapiController::class.':getDocumentConte $app->get('/document/{id}/versions', \RestapiController::class.':getDocumentVersions'); $app->get('/document/{id}/version/{version}', \RestapiController::class.':getDocumentVersion'); $app->put('/document/{id}/version/{version}', \RestapiController::class.':updateDocumentVersion'); +$app->get('/document/{id}/version/{version}/attributes', \RestapiController::class.':getDocumentContentAttributes'); +$app->put('/document/{id}/version/{version}/attribute/{attrdefid}', \RestapiController::class.':setDocumentContentAttribute'); $app->get('/document/{id}/files', \RestapiController::class.':getDocumentFiles'); $app->get('/document/{id}/file/{fileid}', \RestapiController::class.':getDocumentFile'); $app->get('/document/{id}/links', \RestapiController::class.':getDocumentLinks'); $app->post('/document/{id}/link/{documentid}', \RestapiController::class.':addDocumentLink'); $app->get('/document/{id}/attributes', \RestapiController::class.':getDocumentAttributes'); +$app->put('/document/{id}/attribute/{attrdefid}', \RestapiController::class.':setDocumentAttribute'); $app->get('/document/{id}/preview/{version}/{width}', \RestapiController::class.':getDocumentPreview'); $app->delete('/document/{id}/categories', \RestapiController::class.':removeDocumentCategories'); $app->delete('/document/{id}/category/{catid}', \RestapiController::class.':removeDocumentCategory'); $app->post('/document/{id}/category/{catid}', \RestapiController::class.':addDocumentCategory'); $app->put('/document/{id}/owner/{userid}', \RestapiController::class.':setDocumentOwner'); -$app->put('/document/{id}/attribute/{attrdefid}', \RestapiController::class.':setDocumentAttribute'); $app->put('/account/fullname', \RestapiController::class.':setFullName'); $app->put('/account/email', \RestapiController::class.':setEmail'); $app->get('/account/documents/locked', \RestapiController::class.':getLockedDocuments'); From fa56d5735d0b5d4ecddb46152722cccf2eaf62bb Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:20:50 +0100 Subject: [PATCH 022/247] fix various errors, add missing functions --- restapi/swagger.yaml | 252 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 232 insertions(+), 20 deletions(-) diff --git a/restapi/swagger.yaml b/restapi/swagger.yaml index 4375e09e1..f19c5299c 100644 --- a/restapi/swagger.yaml +++ b/restapi/swagger.yaml @@ -13,6 +13,9 @@ info: license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html" +servers: + - url: + description: Current host server host: "" basePath: "_httpRoot; ?>restapi/index.php" tags: @@ -22,6 +25,7 @@ tags: description: "Find out more about our store" url: "https://www.seeddms.org" schemes: +- "http" - "https" paths: /login: @@ -32,9 +36,9 @@ paths: description: "Log in by providing a username and password" operationId: "login" produces: - - "application/json" + - "application/json" consumes: - - application/x-www-form-urlencoded + - "application/x-www-form-urlencoded" parameters: - name: "user" in: "formData" @@ -111,7 +115,7 @@ paths: produces: - "application/json" consumes: - - application/x-www-form-urlencoded + - "application/x-www-form-urlencoded" parameters: - in: formData name: email @@ -138,7 +142,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - in: formData name: email @@ -208,7 +212,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - in: "formData" name: "user" @@ -371,7 +375,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -431,7 +435,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - in: "formData" name: "name" @@ -687,11 +691,106 @@ paths: $ref: "#/definitions/ApiResponse" security: - api_key: [] + /document/{id}/version/{version}/attributes: + get: + tags: + - "document" + summary: "Return attributes of document version" + description: "Returns the attributes of a given document version" + operationId: "getDocumentContentAttributes" + produces: + - "application/json" + parameters: + - name: "id" + in: "path" + description: "ID of document whose attributes to be returned." + type: "integer" + required: true + format: "int64" + - name: "version" + in: "path" + description: "Version number of document" + required: true + type: "integer" + format: "int64" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/ApiResponseAttributes" + "403": + description: "No access" + schema: + $ref: "#/definitions/ApiResponse" + "404": + description: "Document or version not found" + schema: + $ref: "#/definitions/ApiResponse" + "500": + description: "Internal error" + schema: + $ref: "#/definitions/ApiResponse" + security: + - api_key: [] + /document/{id}/version/{version}/attribute/{attrdefid}: + put: + tags: + - "document" + summary: "Set document version attribute" + description: "Sets the attribute value of a document version. If the value is an empty string the attribute will be deleted." + operationId: "setDocumentContentAttribute" + produces: + - "application/json" + consumes: + - "application/x-www-form-urlencoded" + parameters: + - name: "id" + in: "path" + description: "ID of document" + required: true + type: "integer" + format: "int64" + - name: "version" + in: "path" + description: "Version number of document" + required: true + type: "integer" + format: "int64" + - name: "attrdefid" + in: "path" + description: "ID of attribute definition" + required: true + type: "integer" + format: "int64" + - in: "formData" + name: "value" + type: "string" + description: "Value of attribute" + required: true + responses: + "201": + description: "successful operation" + schema: + $ref: "#/definitions/ApiResponse" + "400": + description: "Invalid attribute value, or setting an attribute not allowed for the type of object" + schema: + $ref: "#/definitions/ApiResponse" + "403": + description: "No access" + schema: + $ref: "#/definitions/ApiResponse" + "404": + description: "Document, version or attribute definition not found" + schema: + $ref: "#/definitions/ApiResponse" + security: + - api_key: [] /document/{id}/attributes: get: tags: - "document" - summary: "Return attributes of document by ID" + summary: "Return attributes of document" description: "Returns the attributes of a given document" operationId: "getDocumentAttributes" produces: @@ -722,11 +821,59 @@ paths: $ref: "#/definitions/ApiResponse" security: - api_key: [] + /document/{id}/attribute/{attrdefid}: + put: + tags: + - "document" + summary: "Set document attribute" + description: "Sets the attribute value of a document. If the value is an empty string the attribute will be deleted." + operationId: "setDocumentAttribute" + produces: + - "application/json" + consumes: + - "application/x-www-form-urlencoded" + parameters: + - name: "id" + in: "path" + description: "ID of document" + required: true + type: "integer" + format: "int64" + - name: "attrdefid" + in: "path" + description: "ID of attribute definition" + required: true + type: "integer" + format: "int64" + - in: "formData" + name: "value" + type: "string" + description: "Value of attribute" + required: true + responses: + "201": + description: "successful operation" + schema: + $ref: "#/definitions/ApiResponse" + "400": + description: "Invalid attribute value, or setting an attribute not allowed for the type of object" + schema: + $ref: "#/definitions/ApiResponse" + "403": + description: "No access" + schema: + $ref: "#/definitions/ApiResponse" + "404": + description: "Document or attribute definition not found" + schema: + $ref: "#/definitions/ApiResponse" + security: + - api_key: [] /document/{id}/files: get: tags: - "document" - summary: "Return attached files of document by ID" + summary: "Return attached files of document" description: "Returns the attached files of a given document" operationId: "getDocumentFiles" produces: @@ -802,7 +949,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -920,7 +1067,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -957,7 +1104,6 @@ paths: $ref: "#/definitions/ApiResponse" security: - api_key: [] - /document/{id}/category/{catid}: delete: tags: - "document" @@ -1008,7 +1154,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -1223,7 +1369,7 @@ paths: get: tags: - "folder" - summary: "Return attributes of folder by ID" + summary: "Return attributes of folder" description: "Returns the attributes of a given folder" operationId: "getFolderAttributes" produces: @@ -1254,6 +1400,54 @@ paths: $ref: "#/definitions/ApiResponse" security: - api_key: [] + /folder/{id}/attribute/{attrdefid}: + put: + tags: + - "folder" + summary: "Set folder attribute" + description: "Sets the attribute value of a folder. If the value is an empty string the attribute will be deleted." + operationId: "setFolderAttribute" + produces: + - "application/json" + consumes: + - "application/x-www-form-urlencoded" + parameters: + - name: "id" + in: "path" + description: "ID of folder" + required: true + type: "integer" + format: "int64" + - name: "attrdefid" + in: "path" + description: "ID of attribute definition" + required: true + type: "integer" + format: "int64" + - in: "formData" + name: "value" + type: "string" + description: "Value of attribute" + required: true + responses: + "201": + description: "successful operation" + schema: + $ref: "#/definitions/ApiResponse" + "400": + description: "Invalid attribute value, or setting an attribute not allowed for the type of object" + schema: + $ref: "#/definitions/ApiResponse" + "403": + description: "No access" + schema: + $ref: "#/definitions/ApiResponse" + "404": + description: "Folder or attribute definition not found" + schema: + $ref: "#/definitions/ApiResponse" + security: + - api_key: [] /folder/{id}/folder: post: tags: @@ -1264,7 +1458,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -1316,7 +1510,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -1390,7 +1584,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - in: "formData" name: "name" @@ -1487,7 +1681,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -1543,7 +1737,7 @@ paths: produces: - "application/json" consumes: - - multipart/form-data + - "application/x-www-form-urlencoded" parameters: - name: "id" in: "path" @@ -1570,10 +1764,28 @@ paths: $ref: "#/definitions/ApiResponse" security: - api_key: [] + /statstotal: + get: + tags: + - "misc" + summary: "Return various statistical data" + description: "Just returns the body content" + operationId: "getStatsTotal" + produces: + - "application/json" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/ApiResponse" + "400": + description: "Invalid status value" + security: + - api_key: [] /echo: get: tags: - - "test" + - "misc" summary: "Return what was send in the body" description: "Just returns the body content" operationId: "echoData" From e0cd3fb1e8f938f15e9b4c4b23942c210050ba2d Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Mon, 7 Nov 2022 12:21:16 +0100 Subject: [PATCH 023/247] add changes of 5.1.28 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e43986e67..0e334bfbe 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,9 @@ - fix regression in password forgotten function - fix security issue when creating hash in password forgotten operation - add initial support for logging and notifications in rest api +- add rest api calls to get attributes of a document version and to set + attributes of folders, documents, and document versions +- fixed various errors in swagger.yaml - use methods in inc/inc.ClassNotificationService.php for webdav - clear login failures when login by webdav succeeds From 7dee5cb767508f771eafe37eb69b3aa5a979075e Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 8 Nov 2022 16:45:27 +0100 Subject: [PATCH 024/247] allow to add more tabs with log files with a selectable prefix --- out/out.LogManagement.php | 2 +- views/bootstrap/class.LogManagement.php | 52 +++++++++++++++---------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/out/out.LogManagement.php b/out/out.LogManagement.php index 8f4a95a42..bcf7a699f 100644 --- a/out/out.LogManagement.php +++ b/out/out.LogManagement.php @@ -39,7 +39,7 @@ if (isset($_GET["logname"])) $logname=basename($_GET["logname"], '.log').'.log'; else $logname=NULL; if (isset($_GET["mode"])) $mode=$_GET["mode"]; -else $mode='web'; +else $mode='default'; if($view) { $view->setParam('logname', $logname); diff --git a/views/bootstrap/class.LogManagement.php b/views/bootstrap/class.LogManagement.php index d58a524ce..294242303 100644 --- a/views/bootstrap/class.LogManagement.php +++ b/views/bootstrap/class.LogManagement.php @@ -98,41 +98,51 @@ $("input[type=checkbox]").each(function () { this.checked = !this.checked; }); $this->contentHeading(getMLText("log_management")); - $entries = array(); - $wentries = array(); + $sections = array( + array('default', 'Web'), + array('webdav', 'WebDAV'), + array('restapi', 'RestAPI'), + ); + if($es = $this->callHook('extraSections')) + $sections = array_merge($sections, $es); + $entries = []; + foreach($sections as $section) { + $entries[$section[0]] = array(); + } + $handle = opendir($this->logdir); if($handle) { while ($e = readdir($handle)){ if (is_dir($this->logdir.$e)) continue; if (strpos($e,".log")==FALSE) continue; if (strcmp($e,"current.log")==0) continue; - if(substr($e, 0, 6) == 'webdav') { - $wentries[] = $e; - } else { - $entries[] = $e; - } + $section = strtok($e, '-'); + if(isset($entries[$section])) + $entries[$section][] = $e; + else + $entries['default'][] = $e; } closedir($handle); - sort($entries); - sort($wentries); - $entries = array_reverse($entries); - $wentries = array_reverse($wentries); + foreach($sections as $section) { + sort($entries[$section[0]]); + $entries[$section[0]] = array_reverse($entries[$section[0]]); + } } ?> -