From c32fe1be80b08d1677b0e981c703a20a56760b6e Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 15:40:20 +0100 Subject: [PATCH 01/13] pass $settings as first parameter of getLogger() --- inc/inc.LogInit.php | 3 +-- inc/inc.Utils.php | 6 ++---- restapi/index.php | 6 +++--- webdav/index.php | 4 +++- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/inc/inc.LogInit.php b/inc/inc.LogInit.php index e728f93b1..5d0355497 100644 --- a/inc/inc.LogInit.php +++ b/inc/inc.LogInit.php @@ -18,8 +18,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -require_once("Log.php"); require_once("inc/inc.Utils.php"); -$logger = getLogger('', (int) $settings->_logFileMaxLevel); +$logger = getLogger($settings, '', (int) $settings->_logFileMaxLevel); diff --git a/inc/inc.Utils.php b/inc/inc.Utils.php index 6c1aa9c44..b86b2031c 100644 --- a/inc/inc.Utils.php +++ b/inc/inc.Utils.php @@ -493,10 +493,8 @@ function getStreamContext($proxyurl, $proxyuser, $proxypass) { /* {{{ */ return $context; } /* }}} */ -function getLogger($prefix='', $mask=PEAR_LOG_INFO) { /* {{{ */ - global $settings; - - if($settings->_logFileEnable) { +function getLogger($settings, $prefix='', $mask=PEAR_LOG_INFO) { /* {{{ */ + if(!empty($settings->_logFileEnable)) { if ($settings->_logFileRotation=="h") $logname=date("YmdH", time()); else if ($settings->_logFileRotation=="d") $logname=date("Ymd", time()); else $logname=date("Ym", time()); diff --git a/restapi/index.php b/restapi/index.php index a2d255285..d193761c5 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -1,11 +1,13 @@ _logFileRestApiMaxLevel); +$logger = getLogger($settings, 'restapi-', (int) $settings->_logFileRestApiMaxLevel); require_once("../inc/inc.Init.php"); require_once("../inc/inc.Extension.php"); @@ -15,8 +17,6 @@ require_once("../inc/inc.ClassEmailNotify.php"); require_once("../inc/inc.Notification.php"); require_once("../inc/inc.ClassController.php"); -require "vendor/autoload.php"; - use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; diff --git a/webdav/index.php b/webdav/index.php index a70b701d4..993f59f9e 100644 --- a/webdav/index.php +++ b/webdav/index.php @@ -1,11 +1,13 @@ Date: Tue, 28 Oct 2025 15:41:59 +0100 Subject: [PATCH 02/13] new methods getBaseUrl() and getBaseUrlWithRoot() --- inc/inc.ClassSettings.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/inc/inc.ClassSettings.php b/inc/inc.ClassSettings.php index a01c29d3c..fd09569f9 100644 --- a/inc/inc.ClassSettings.php +++ b/inc/inc.ClassSettings.php @@ -1839,5 +1839,37 @@ class Settings { /* {{{ */ return min($mus); } /* }}} */ + /** + * Get base url + * + * @return string + */ + public function getBaseUrl() { /* {{{ */ + global $_SERVER; + + if(!empty($this->_baseUrl)) + return $this->_baseUrl; + + if(isset($_SERVER['HTTP_X_FORWARDED_HOST'])) + $host = $_SERVER['HTTP_X_FORWARDED_HOST']; + else + $host = $_SERVER['HTTP_HOST']; + if(isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) + $ssl = $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'; + else + $ssl = (isset($_SERVER['HTTPS']) && (strcmp($_SERVER['HTTPS'],'off')!=0)); + + return "http".($ssl ? "s" : "")."://".$host; + } /* }}} */ + + /** + * Get base url with http root folder + * + * @return string + */ + public function getBaseUrlWithRoot() { /* {{{ */ + return $this->getBaseUrl().$this->_httpRoot; + } /* }}} */ + } /* }}} */ From b6bdae551ca3d2d7f9ba97793c41fb53f72769d1 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 15:44:51 +0100 Subject: [PATCH 03/13] rename $extMgr to $extmgr, all global vars can be passed to factory --- inc/inc.ClassController.php | 10 ++++++++-- inc/inc.ClassUI.php | 15 ++++++++++----- inc/inc.Extension.php | 22 +++++----------------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/inc/inc.ClassController.php b/inc/inc.ClassController.php index 4a37d9cbc..7f8aa6620 100644 --- a/inc/inc.ClassController.php +++ b/inc/inc.ClassController.php @@ -30,14 +30,19 @@ class Controller { * @return object an object of a class implementing the view */ static function factory($class, $params=array()) { /* {{{ */ - global $settings, $session, $extMgr, $request, $logger, $notifier; + foreach(['settings', 'session', 'extmgr', 'request', 'logger', 'notifier', 'fulltextservice'] as $vn) { + if(isset($params[$vn])) + ${$vn} = $params[$vn]; + else + ${$vn} = $GLOBALS[$vn] ?? null; + } if(!$class) { return null; } $classname = "SeedDMS_Controller_".$class; $filename = ''; - foreach($extMgr->getExtensionConfiguration() as $extname=>$extconf) { + foreach($extmgr->getExtensionConfiguration() as $extname=>$extconf) { $filename = $settings->_rootDir.'ext/'.$extname.'/controllers/class.'.$class.".php"; if(file_exists($filename)) { break; @@ -60,6 +65,7 @@ class Controller { $controller->setParam('settings', $settings); $controller->setParam('logger', $logger); $controller->setParam('notifier', $notifier); + $controller->setParam('fulltextservice', $fulltextservice); return $controller; } return null; diff --git a/inc/inc.ClassUI.php b/inc/inc.ClassUI.php index 01d37866c..22f2771d3 100644 --- a/inc/inc.ClassUI.php +++ b/inc/inc.ClassUI.php @@ -45,7 +45,12 @@ class UI extends UI_Default { * @return object an object of a class implementing the view */ static public function factory($theme, $class='', $params=array()) { /* {{{ */ - global $settings, $session, $extMgr, $request, $logger, $notifier, $fulltextservice; + foreach(['settings', 'session', 'extmgr', 'request', 'logger', 'notifier', 'fulltextservice'] as $vn) { + if(isset($params[$vn])) + ${$vn} = $params[$vn]; + else + ${$vn} = $GLOBALS[$vn] ?? null; + } if(!$class) { $class = 'Bootstrap'; $class = 'Style'; @@ -55,9 +60,9 @@ class UI extends UI_Default { } /* Collect all decorators */ $decorators = array(); - foreach($extMgr->getExtensionConfiguration() as $extname=>$extconf) { + foreach($extmgr->getExtensionConfiguration() as $extname=>$extconf) { if(!$settings->extensionIsDisabled($extname)) { - if($extMgr->checkExtensionByName($extname, $extconf)) { + if($extmgr->checkExtensionByName($extname, $extconf)) { if(isset($extconf['decorators'][$class])) { $filename = $settings->_rootDir.'ext/'.$extname.'/decorators/'.$theme."/".$extconf['decorators'][$class]['file']; if(file_exists($filename)) { @@ -72,9 +77,9 @@ class UI extends UI_Default { */ $filename = ''; $httpbasedir = ''; - foreach($extMgr->getExtensionConfiguration() as $extname=>$extconf) { + foreach($extmgr->getExtensionConfiguration() as $extname=>$extconf) { if(!$settings->extensionIsDisabled($extname)) { - if($extMgr->checkExtensionByName($extname, $extconf)) { + if($extmgr->checkExtensionByName($extname, $extconf)) { /* Setting the 'views' element in the configuration can be used to * replace an existing view in views/bootstrap/, e.g. class.ViewFolder.php * without providing an out/out.ViewFolder.php. In that case $httpbasedir diff --git a/inc/inc.Extension.php b/inc/inc.Extension.php index ce78fda55..8c4f515dd 100644 --- a/inc/inc.Extension.php +++ b/inc/inc.Extension.php @@ -16,35 +16,23 @@ global $logger; require "inc.ClassExtensionMgr.php"; require_once "inc.ClassExtBase.php"; -$extMgr = new SeedDMS_Extension_Mgr($settings->_rootDir."/ext", $settings->_cacheDir, $settings->_repositoryUrl, $settings->_proxyUrl, $settings->_proxyUser, $settings->_proxyPassword); +$extmgr = new SeedDMS_Extension_Mgr($settings->_rootDir."/ext", $settings->_cacheDir, $settings->_repositoryUrl, $settings->_proxyUrl, $settings->_proxyUser, $settings->_proxyPassword); -$version = new SeedDMS_Version; - -foreach($extMgr->getExtensionConfiguration() as $extname=>$extconf) { +foreach($extmgr->getExtensionConfiguration() as $extname=>$extconf) { if($extconf['disable']) { $settings->disableExtension($extname); continue; } if(!$settings->extensionIsDisabled($extname)) { $disabled = false; - if($extMgr->checkExtensionByName($extname, $extconf)) { + if($extmgr->checkExtensionByName($extname, $extconf)) { $disabled = false; $settings->enableExtension($extname); } else { $disabled = true; $settings->disableExtension($extname); - // echo $extMgr->getErrorMsg(); + // echo $extmgr->getErrorMsg(); } - /* check for requirements */ - /* - if(!empty($extconf['constraints']['depends']['seeddms'])) { - $t = explode('-', $extconf['constraints']['depends']['seeddms'], 2); - if(SeedDMS_Extension_Mgr::cmpVersion($t[0], $version->version()) > 0 || ($t[1] && SeedDMS_Extension_Mgr::cmpVersion($t[1], $version->version()) < 0)) - $disabled = true; - else - $disabled = false; - } - */ if(!$disabled) { if(isset($extconf['class']) && isset($extconf['class']['file']) && isset($extconf['class']['name'])) { $classfile = $settings->_rootDir."/ext/".$extname."/".$extconf['class']['file']; @@ -52,7 +40,7 @@ foreach($extMgr->getExtensionConfiguration() as $extname=>$extconf) { include($classfile); $obj = new $extconf['class']['name']($settings, null, $logger); if(method_exists($obj, 'init')) - $obj->init($extMgr); + $obj->init($extmgr); } } if(isset($extconf['language']['file'])) { From 9275fc1e05d4d3272a1d2304ecd4fcafa21d8e4a Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 15:46:50 +0100 Subject: [PATCH 04/13] no need for 'global $logger' --- inc/inc.AuthenticationInit.php | 1 - 1 file changed, 1 deletion(-) diff --git a/inc/inc.AuthenticationInit.php b/inc/inc.AuthenticationInit.php index f7beb05b6..0c817df95 100644 --- a/inc/inc.AuthenticationInit.php +++ b/inc/inc.AuthenticationInit.php @@ -16,7 +16,6 @@ require_once('inc.ClassAuthenticationService.php'); require_once('inc.ClassDbAuthentication.php'); require_once('inc.ClassLdapAuthentication.php'); -global $logger; $authenticator = new SeedDMS_AuthenticationService($logger, $settings); if(isset($GLOBALS['SEEDDMS_HOOKS']['authentication'])) { From ef49a4364429e488391c86547aa526bb5f840b5e Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 15:47:20 +0100 Subject: [PATCH 05/13] no need for 'global $logger' --- inc/inc.Notification.php | 1 - 1 file changed, 1 deletion(-) diff --git a/inc/inc.Notification.php b/inc/inc.Notification.php index e1a6144ae..86eab8cbe 100644 --- a/inc/inc.Notification.php +++ b/inc/inc.Notification.php @@ -12,7 +12,6 @@ * @version Release: @package_version@ */ -global $logger; $notifier = new SeedDMS_NotificationService($logger, $settings); if(isset($GLOBALS['SEEDDMS_HOOKS']['notification'])) { From 9ac6bca75fdbd501d17bf27776669854e6d39690 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 15:48:10 +0100 Subject: [PATCH 06/13] class Settings has been moved into namespace Seeddms\Seeddms --- inc/inc.ClassSettings.php | 47 ++++++++++++--------------------------- inc/inc.Settings.php | 2 ++ 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/inc/inc.ClassSettings.php b/inc/inc.ClassSettings.php index fd09569f9..2722e07b3 100644 --- a/inc/inc.ClassSettings.php +++ b/inc/inc.ClassSettings.php @@ -11,6 +11,12 @@ * @version Release: @package_version@ */ +namespace Seeddms\Seeddms; + +use SeedDMS_Core_File; +use SeedDMS_Core_DMS; +use SeedDMS_Core_DatabaseAccess; + /** * Class for reading and writing the configuration file * @@ -1595,39 +1601,14 @@ class Settings { /* {{{ */ } if($dsn) { $connTmp = new PDO($dsn, $this->_dbUser, $this->_dbPass); - /* Check if there wasn't a previous error while searching for - * SeedDMS_Core. - */ - if(!isset($result["coreDir"])) { - /* Instanciate SeedDMS_Core to check version */ - if(!empty($this->_coreDir)) - require_once($this->_coreDir.'/Core.php'); - else - require_once($this->_rootDir.'../vendor/seeddms/core/Core.php'); - $tmpcore = new SeedDMS_Core_DMS(null, $this->_contentDir); - $db = new SeedDMS_Core_DatabaseAccess($this->_dbDriver, $this->_dbHostname, $this->_dbUser, $this->_dbPass, $this->_dbDatabase); - if(!$db->connect()) { - $result["dbDatabase"] = array( - "status" => "error", - "type" => "error", - "currentvalue" => '[host, user, database] -> [' . $this->_dbHostname . ',' . $this->_dbUser . ',' . $this->_dbDatabase .']', - "systemerror" => $connTmp->ErrorMsg() - ); - } else { - /* - $dms = new SeedDMS_Core_DMS($db, $this->_contentDir.$this->_contentOffsetDir); - - if(!$dms->checkVersion()) { - $result["dbVersion"] = array( - "status" => "error", - "type" => "error", - "currentvalue" => $dms->version, - "suggestion" => 'updateDatabase' - ); - } - */ - } - $connTmp = null; + $db = new SeedDMS_Core_DatabaseAccess($this->_dbDriver, $this->_dbHostname, $this->_dbUser, $this->_dbPass, $this->_dbDatabase); + if(!$db->connect()) { + $result["dbDatabase"] = array( + "status" => "error", + "type" => "error", + "currentvalue" => '[host, user, database] -> [' . $this->_dbHostname . ',' . $this->_dbUser . ',' . $this->_dbDatabase .']', + "systemerror" => $connTmp->ErrorMsg() + ); } } } catch(Exception $e) { diff --git a/inc/inc.Settings.php b/inc/inc.Settings.php index 07a49b021..5ff65c570 100644 --- a/inc/inc.Settings.php +++ b/inc/inc.Settings.php @@ -18,6 +18,8 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +use Seeddms\Seeddms\Settings; + require_once('inc.ClassSettings.php'); if(defined("SEEDDMS_CONFIG_FILE")) $settings = new Settings(SEEDDMS_CONFIG_FILE); From afd6a8b7f0a337d81b096ae77bf6878c93f55330 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 15:48:52 +0100 Subject: [PATCH 07/13] class Settings has been moved into namespace Seeddms\Seeddms --- install/index.php | 2 +- install/install.php | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/install/index.php b/install/index.php index 1015bd572..a0d2fd422 100644 --- a/install/index.php +++ b/install/index.php @@ -5,7 +5,7 @@ define("SEEDDMS_INSTALL", "on"); define("SEEDDMS_VERSION", $ver->version()); include("../inc/inc.Settings.php"); -$settings = new Settings(); +$settings = new Seeddms\Seeddms\Settings(); $rootDir = realpath (".."); if(file_exists($rootDir.'/../www')) $rootDir = realpath($rootDir.'/..').'/www'; diff --git a/install/install.php b/install/install.php index 24c5e0cf6..62aebae50 100644 --- a/install/install.php +++ b/install/install.php @@ -69,7 +69,7 @@ define("SEEDDMS_VERSION", $ver->version()); require_once('../inc/inc.ClassSettings.php'); -$configDir = Settings::getConfigDir(); +$configDir = Seeddms\Seeddms\Settings::getConfigDir(); /** * Check if ENABLE_INSTALL_TOOL exists in config dir @@ -92,8 +92,7 @@ if (!file_exists($configDir."/settings.xml")) { } // Set folders settings -$settings = new Settings(); -$settings->load($configDir."/settings.xml"); +$settings = new Seeddms\Seeddms\Settings($configDir."/settings.xml"); $rootDir = realpath (".."); $installPath = realpath ("install.php"); From 0e1da1a730d4f82e4d02d5de92e3148abff785f8 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 19:30:29 +0100 Subject: [PATCH 08/13] allow 'onclick' attribute in showMenuwithButton() --- views/bootstrap/class.Bootstrap.php | 7 ++++++- views/bootstrap4/class.Bootstrap4.php | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/views/bootstrap/class.Bootstrap.php b/views/bootstrap/class.Bootstrap.php index e5960f451..19f8c43ae 100644 --- a/views/bootstrap/class.Bootstrap.php +++ b/views/bootstrap/class.Bootstrap.php @@ -655,7 +655,12 @@ background-image: linear-gradient(to bottom, #882222, #111111);; '; foreach($button['menuitems'] as $menuitem) { $content .= ' -
  • '.$menuitem['label'].'
  • +
  • '; } $content .= ' diff --git a/views/bootstrap4/class.Bootstrap4.php b/views/bootstrap4/class.Bootstrap4.php index 3dfe67b17..b38163fd7 100644 --- a/views/bootstrap4/class.Bootstrap4.php +++ b/views/bootstrap4/class.Bootstrap4.php @@ -660,7 +660,12 @@ background-image: linear-gradient(to bottom, #882222, #111111);; '; foreach($button['menuitems'] as $menuitem) { $content .= ' - '.$menuitem['label'].' + '; } $content .= ' From 7a1729e0c8b64b85f908f4580c5ebdad792175d3 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 21:35:17 +0100 Subject: [PATCH 09/13] move all translation methods into class --- inc/inc.ClassTranslator.php | 409 ++++++++++++++++++++++++++++++++++++ inc/inc.Language.php | 329 ++--------------------------- 2 files changed, 432 insertions(+), 306 deletions(-) create mode 100644 inc/inc.ClassTranslator.php diff --git a/inc/inc.ClassTranslator.php b/inc/inc.ClassTranslator.php new file mode 100644 index 000000000..feda07b1d --- /dev/null +++ b/inc/inc.ClassTranslator.php @@ -0,0 +1,409 @@ + + * @copyright Copyright (C) 2025 Uwe Steinmann + * @version Release: @package_version@ + */ + +namespace Seeddms\Seeddms; + +/** + * Class for translation and language handling + * + * @category DMS + * @package SeedDMS + * @author Uwe Steinmann + * @copyright Copyright (C) 2011 Uwe Steinmann + * @version Release: @package_version@ + */ +class Translator { /* {{{ */ + + protected $settings; + + protected $defaultlang; + + protected $lang; + + protected $missinglang; + + /** + * Constructor + * + * @param string $settings SeedDMS configuration + */ + function __construct($settings=null) { /* {{{ */ + $this->settings = $settings; + $this->defaultlang = ''; + + $this->lang = array(); + $this->missinglang = array(); + } /* }}} */ + + function setDefaultLanguage($lang) { /* {{{ */ + $this->defaultlang = $lang; + } /* }}} */ + + public function init() { /* {{{ */ + $__languages = $this->getLanguages(); + if(!in_array($this->settings->_language, $__languages)) + $__languages[] = $this->settings->_language; + foreach($__languages as $_lang) { + if(file_exists($this->settings->_rootDir . "languages/" . $_lang . "/lang.inc")) { + include $this->settings->_rootDir . "languages/" . $_lang . "/lang.inc"; + $this->lang[$_lang] = $text; + } + } + } /* }}} */ + + function addPhrases($lang, &$data) { /* {{{ */ + if(isset($this->lang[$lang])) + $this->lang[$lang] = array_merge($this->lang[$lang], $data); + else + $this->lang[$lang] = $data; + } /* }}} */ + + function getAvailableLanguages() { /* {{{ */ + $languages = array(); + + $path = $this->settings->_rootDir . "languages/"; + $handle = opendir($path); + + while ($entry = readdir($handle) ) + { + if ($entry == ".." || $entry == ".") { + continue; + } elseif (is_dir($path . $entry)) { + array_push($languages, $entry); + } + } + closedir($handle); + + asort($languages); + return $languages; + } /* }}} */ + + function getLanguages() { /* {{{ */ + if($this->settings->_availablelanguages) { + return $this->settings->_availablelanguages; + } + + return getAvailableLanguages(); + } /* }}} */ + + /** + * Get translation + * + * Returns the translation for a given key. It will replace markers + * in the form [xxx] with those elements from the array $replace. + * A default text can be gіven for the case, that there is no translation + * available. The fourth parameter can override the currently set language + * in the session or the default language from the configuration. + * + * @param string $key key of translation text + * @param array $replace list of values that replace markers in the text + * @param string $defaulttext text used if no translation can be found + * @param string $lang use this language instead of the currently set lang + */ + function translate($key, $replace = array(), $defaulttext = null, $lang="") { /* {{{ */ + $trantext = ''; + if(!$lang) { + if($this->defaultlang) + $lang = $this->defaultlang; + else + $lang = $this->settings->_language; + } + + if(!isset($this->lang[$lang][$key]) || !$this->lang[$lang][$key]) { + if ($defaulttext === null) { + $this->missinglang[$key] = $lang; //$_SERVER['SCRIPT_NAME']; + if(!empty($this->lang[$this->settings->_language][$key])) { + $tmpText = $this->lang[$this->settings->_language][$key]; + } else { + $tmpText = '**'.$key.'**'; + } + } else + $tmpText = $defaulttext; + } else + $tmpText = $this->lang[$lang][$key]; + + if (count($replace) == 0) + return $tmpText.$trantext; + + $keys = array_keys($replace); + foreach ($keys as $key) + $tmpText = str_replace("[".$key."]", $replace[$key], $tmpText); + + return $tmpText; + } /* }}} */ + + function print($key, $replace = array(), $defaulttext = null, $lang="") { /* {{{ */ + print $this->translate($key, $replace, $defaulttext, $lang); + } /* }}} */ + + function printReviewStatusText($status, $date=0) { /* {{{ */ + if (is_null($status)) { + print $this->translate("status_unknown"); + } + else { + switch ($status) { + case -2: + print $this->translate("status_reviewer_removed"); + break; + case -1: + print $this->translate("status_reviewer_rejected").($date !=0 ? " ".$date : ""); + break; + case 0: + print $this->translate("status_not_reviewed"); + break; + case 1: + print $this->translate("status_reviewed").($date !=0 ? " ".$date : ""); + break; + default: + print $this->translate("status_unknown"); + break; + } + } + } /* }}} */ + + function getReviewStatusText($status, $date=0) { /* {{{ */ + if (is_null($status)) { + return $this->translate("status_unknown"); + } + else { + switch ($status) { + case -2: + return $this->translate("status_reviewer_removed"); + break; + case -1: + return $this->translate("status_reviewer_rejected").($date !=0 ? " ".$date : ""); + break; + case 0: + return $this->translate("status_not_reviewed"); + break; + case 1: + return $this->translate("status_reviewed").($date !=0 ? " ".$date : ""); + break; + default: + return $this->translate("status_unknown"); + break; + } + } + } /* }}} */ + + function printApprovalStatusText($status, $date=0) { /* {{{ */ + if (is_null($status)) { + print $this->translate("status_unknown"); + } + else { + switch ($status) { + case -2: + print $this->translate("status_approver_removed"); + break; + case -1: + print $this->translate("status_approval_rejected").($date !=0 ? " ".$date : ""); + break; + case 0: + print $this->translate("status_not_approved"); + break; + case 1: + print $this->translate("status_approved").($date !=0 ? " ".$date : ""); + break; + default: + print $this->translate("status_unknown"); + break; + } + } + } /* }}} */ + + function getApprovalStatusText($status, $date=0) { /* {{{ */ + if (is_null($status)) { + return $this->translate("status_unknown"); + } + else { + switch ($status) { + case -2: + return $this->translate("status_approver_removed"); + break; + case -1: + return $this->translate("status_approval_rejected").($date !=0 ? " ".$date : ""); + break; + case 0: + return $this->translate("status_not_approved"); + break; + case 1: + return $this->translate("status_approved").($date !=0 ? " ".$date : ""); + break; + default: + return $this->translate("status_unknown"); + break; + } + } + } /* }}} */ + + function printOverallStatusText($status) { /* {{{ */ + print getOverallStatusText($status); + } /* }}} */ + + function getOverallStatusText($status) { /* {{{ */ + if (is_null($status)) { + return $this->translate("assumed_released"); + } + else { + switch($status) { + case S_IN_WORKFLOW: + return $this->translate("in_workflow"); + break; + case S_DRAFT_REV: + return $this->translate("draft_pending_review"); + break; + case S_DRAFT_APP: + return $this->translate("draft_pending_approval"); + break; + case S_RELEASED: + return $this->translate("released"); + break; + case S_REJECTED: + return $this->translate("rejected"); + break; + case S_OBSOLETE: + return $this->translate("obsolete"); + break; + case S_EXPIRED: + return $this->translate("expired"); + break; + default: + return $this->translate("status_unknown"); + break; + } + } + } /* }}} */ + + function getAttributeTypeText($attrdef) { /* {{{ */ + $t = ''; + switch($attrdef->getType()) { + case SeedDMS_Core_AttributeDefinition::type_int: + $t = $this->translate("attrdef_type_int"); + break; + case SeedDMS_Core_AttributeDefinition::type_float: + $t = $this->translate("attrdef_type_float"); + break; + case SeedDMS_Core_AttributeDefinition::type_string: + $t = $this->translate("attrdef_type_string"); + break; + case SeedDMS_Core_AttributeDefinition::type_boolean: + $t = $this->translate("attrdef_type_boolean"); + break; + case SeedDMS_Core_AttributeDefinition::type_date: + $t = $this->translate("attrdef_type_date"); + break; + case SeedDMS_Core_AttributeDefinition::type_email: + $t = $this->translate("attrdef_type_email"); + break; + case SeedDMS_Core_AttributeDefinition::type_url: + $t = $this->translate("attrdef_type_url"); + break; + case SeedDMS_Core_AttributeDefinition::type_boolean: + $t = $this->translate("attrdef_type_boolean"); + break; + case SeedDMS_Core_AttributeDefinition::type_folder: + $t = $this->translate("attrdef_type_folder"); + break; + case SeedDMS_Core_AttributeDefinition::type_document: + $t = $this->translate("attrdef_type_document"); + break; + case SeedDMS_Core_AttributeDefinition::type_user: + $t = $this->translate("attrdef_type_user"); + break; + case SeedDMS_Core_AttributeDefinition::type_group: + $t = $this->translate("attrdef_type_group"); + break; + } + return $t; + } /* }}} */ + + function getAttributeObjectTypeText($attrdef) { /* {{{ */ + $ot = ''; + switch($attrdef->getObjType()) { + case SeedDMS_Core_AttributeDefinition::objtype_all: + $ot = $this->translate("all"); + break; + case SeedDMS_Core_AttributeDefinition::objtype_folder: + $ot = $this->translate("folder"); + break; + case SeedDMS_Core_AttributeDefinition::objtype_document: + $ot = $this->translate("document"); + break; + case SeedDMS_Core_AttributeDefinition::objtype_documentcontent: + $ot = $this->translate("documentcontent"); + break; + default: + $ot = $this->translate('objtype_unknown'); + } + return $ot; + } /* }}} */ + + function getAttributeValidationText($error, $attrname='', $attrvalue='', $regex='') { /* {{{ */ + $arr = getAttributeValidationError($error, $attrname, $attrvalue, $regex); + + return $this->translate($arr[0], $arr[1]); + } /* }}} */ + + function getAttributeValidationError($error, $attrname='', $attrvalue='', $regex='') { /* {{{ */ + if(is_object($attrvalue)) + $attrvalue = $attrvalue->getId(); + switch($error) { + case 14: + return array("attr_not_in_valueset", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 13: + return array("attr_not_a_group", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 12: + return array("attr_not_a_user", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 11: + return array("attr_not_a_folder", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 10: + return array("attr_not_a_document", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 9: + return array("attr_malformed_date", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 8: + return array("attr_malformed_boolean", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 7: + return array("attr_malformed_float", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 6: + return array("attr_malformed_int", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 5: + return array("attr_malformed_email", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 4: + return array("attr_malformed_url", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 3: + return array("attr_no_regex_match", array('attrname'=>$attrname, 'value'=>$attrvalue, 'regex'=>$regex)); + break; + case 2: + return array("attr_max_values", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + case 1: + return array("attr_min_values", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + default: + return array("attr_validation_error", array('attrname'=>$attrname, 'value'=>$attrvalue)); + break; + } + } /* }}} */ + +} /* }}} */ diff --git a/inc/inc.Language.php b/inc/inc.Language.php index 8c9ceb59d..deb8850dd 100644 --- a/inc/inc.Language.php +++ b/inc/inc.Language.php @@ -18,48 +18,19 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -$LANG = array(); -$MISSING_LANG = array(); -$__languages = getLanguages(); -if(!in_array($settings->_language, $__languages)) - $__languages[] = $settings->_language; -foreach($__languages as $_lang) { - if(file_exists($settings->_rootDir . "languages/" . $_lang . "/lang.inc")) { - include $settings->_rootDir . "languages/" . $_lang . "/lang.inc"; - $LANG[$_lang] = $text; - } -} -unset($text); +use Seeddms\Seeddms\Translator; + +$translator = new Translator($settings); +$translator->init(); function getAvailableLanguages() { /* {{{ */ - global $settings; + error_log('getAvailableLanguages() is deprecated'); - $languages = array(); - - $path = $settings->_rootDir . "languages/"; - $handle = opendir($path); - - while ($entry = readdir($handle) ) - { - if ($entry == ".." || $entry == ".") - continue; - else if (is_dir($path . $entry)) - array_push($languages, $entry); - } - closedir($handle); - - asort($languages); - return $languages; + return $GLOBALS['translator']->getAvailableLanguages(); } /* }}} */ function getLanguages() { /* {{{ */ - global $settings; - - if($settings->_availablelanguages) { - return $settings->_availablelanguages; - } - - return getAvailableLanguages(); + return $GLOBALS['translator']->getLanguages(); } /* }}} */ /** @@ -77,310 +48,56 @@ function getLanguages() { /* {{{ */ * @param string $lang use this language instead of the currently set lang */ function getMLText($key, $replace = array(), $defaulttext = null, $lang="") { /* {{{ */ - GLOBAL $settings, $LANG, $session, $MISSING_LANG; - - $trantext = ''; - if(0 && $settings->_otrance) { - $trantext = '
    '; + trigger_error("getMLText() is deprecated.", E_USER_DEPRECATED); + foreach(debug_backtrace() as $n) { + trigger_error($n['file'].": Line ".$n['line'], E_USER_DEPRECATED); } - if(!$lang) { - if($session) - $lang = $session->getLanguage(); - else - $lang = $settings->_language; - } - - if(!isset($LANG[$lang][$key]) || !$LANG[$lang][$key]) { - if ($defaulttext === null) { - $MISSING_LANG[$key] = $lang; //$_SERVER['SCRIPT_NAME']; - if(!empty($LANG[$settings->_language][$key])) { - $tmpText = $LANG[$settings->_language][$key]; - } else { - $tmpText = '**'.$key.'**'; - } - } else - $tmpText = $defaulttext; - } else - $tmpText = $LANG[$lang][$key]; - - if(0 && $settings->_otrance) { - $_GLOBALS['used_langs'][$key] = $tmpText; - } - - if (count($replace) == 0) - return $tmpText.$trantext; - - $keys = array_keys($replace); - foreach ($keys as $key) - $tmpText = str_replace("[".$key."]", $replace[$key], $tmpText); - - return $tmpText; + return $GLOBALS['translator']->translate($key, $replace, $defaulttext, $lang); } /* }}} */ -function printMLText($key, $replace = array(), $defaulttext = null, $lang="") /* {{{ */ -{ - print getMLText($key, $replace, $defaulttext, $lang); +function printMLText($key, $replace = array(), $defaulttext = null, $lang="") { /* {{{ */ + $GLOBALS['translator']->print($key, $replace, $defaulttext, $lang); } /* }}} */ function printReviewStatusText($status, $date=0) { /* {{{ */ - if (is_null($status)) { - print getMLText("status_unknown"); - } - else { - switch ($status) { - case -2: - print getMLText("status_reviewer_removed"); - break; - case -1: - print getMLText("status_reviewer_rejected").($date !=0 ? " ".$date : ""); - break; - case 0: - print getMLText("status_not_reviewed"); - break; - case 1: - print getMLText("status_reviewed").($date !=0 ? " ".$date : ""); - break; - default: - print getMLText("status_unknown"); - break; - } - } + $GLOBALS['translator']->printReviewStatusText($status, $date); } /* }}} */ function getReviewStatusText($status, $date=0) { /* {{{ */ - if (is_null($status)) { - return getMLText("status_unknown"); - } - else { - switch ($status) { - case -2: - return getMLText("status_reviewer_removed"); - break; - case -1: - return getMLText("status_reviewer_rejected").($date !=0 ? " ".$date : ""); - break; - case 0: - return getMLText("status_not_reviewed"); - break; - case 1: - return getMLText("status_reviewed").($date !=0 ? " ".$date : ""); - break; - default: - return getMLText("status_unknown"); - break; - } - } + return $GLOBALS['translator']->getReviewStatusText($status, $date); } /* }}} */ function printApprovalStatusText($status, $date=0) { /* {{{ */ - if (is_null($status)) { - print getMLText("status_unknown"); - } - else { - switch ($status) { - case -2: - print getMLText("status_approver_removed"); - break; - case -1: - print getMLText("status_approval_rejected").($date !=0 ? " ".$date : ""); - break; - case 0: - print getMLText("status_not_approved"); - break; - case 1: - print getMLText("status_approved").($date !=0 ? " ".$date : ""); - break; - default: - print getMLText("status_unknown"); - break; - } - } + $GLOBALS['translator']->printApprovalStatusText($status, $date); } /* }}} */ function getApprovalStatusText($status, $date=0) { /* {{{ */ - if (is_null($status)) { - return getMLText("status_unknown"); - } - else { - switch ($status) { - case -2: - return getMLText("status_approver_removed"); - break; - case -1: - return getMLText("status_approval_rejected").($date !=0 ? " ".$date : ""); - break; - case 0: - return getMLText("status_not_approved"); - break; - case 1: - return getMLText("status_approved").($date !=0 ? " ".$date : ""); - break; - default: - return getMLText("status_unknown"); - break; - } - } + return $GLOBALS['translator']->getApprovalStatusText($status, $date); } /* }}} */ function printOverallStatusText($status) { /* {{{ */ - print getOverallStatusText($status); + $GLOBALS['translator']->printOverallStatusText($status); } /* }}} */ function getOverallStatusText($status) { /* {{{ */ - if (is_null($status)) { - return getMLText("assumed_released"); - } - else { - switch($status) { - case S_IN_WORKFLOW: - return getMLText("in_workflow"); - break; - case S_DRAFT_REV: - return getMLText("draft_pending_review"); - break; - case S_DRAFT_APP: - return getMLText("draft_pending_approval"); - break; - case S_RELEASED: - return getMLText("released"); - break; - case S_REJECTED: - return getMLText("rejected"); - break; - case S_OBSOLETE: - return getMLText("obsolete"); - break; - case S_EXPIRED: - return getMLText("expired"); - break; - default: - return getMLText("status_unknown"); - break; - } - } + return $GLOBALS['translator']->getOverallStatusText($status); } /* }}} */ function getAttributeTypeText($attrdef) { /* {{{ */ - $t = ''; - switch($attrdef->getType()) { - case SeedDMS_Core_AttributeDefinition::type_int: - $t = getMLText("attrdef_type_int"); - break; - case SeedDMS_Core_AttributeDefinition::type_float: - $t = getMLText("attrdef_type_float"); - break; - case SeedDMS_Core_AttributeDefinition::type_string: - $t = getMLText("attrdef_type_string"); - break; - case SeedDMS_Core_AttributeDefinition::type_boolean: - $t = getMLText("attrdef_type_boolean"); - break; - case SeedDMS_Core_AttributeDefinition::type_date: - $t = getMLText("attrdef_type_date"); - break; - case SeedDMS_Core_AttributeDefinition::type_email: - $t = getMLText("attrdef_type_email"); - break; - case SeedDMS_Core_AttributeDefinition::type_url: - $t = getMLText("attrdef_type_url"); - break; - case SeedDMS_Core_AttributeDefinition::type_boolean: - $t = getMLText("attrdef_type_boolean"); - break; - case SeedDMS_Core_AttributeDefinition::type_folder: - $t = getMLText("attrdef_type_folder"); - break; - case SeedDMS_Core_AttributeDefinition::type_document: - $t = getMLText("attrdef_type_document"); - break; - case SeedDMS_Core_AttributeDefinition::type_user: - $t = getMLText("attrdef_type_user"); - break; - case SeedDMS_Core_AttributeDefinition::type_group: - $t = getMLText("attrdef_type_group"); - break; - } - return $t; + return $GLOBALS['translator']->getAttributeTypeText($attrdef); } /* }}} */ function getAttributeObjectTypeText($attrdef) { /* {{{ */ - $ot = ''; - switch($attrdef->getObjType()) { - case SeedDMS_Core_AttributeDefinition::objtype_all: - $ot = getMLText("all"); - break; - case SeedDMS_Core_AttributeDefinition::objtype_folder: - $ot = getMLText("folder"); - break; - case SeedDMS_Core_AttributeDefinition::objtype_document: - $ot = getMLText("document"); - break; - case SeedDMS_Core_AttributeDefinition::objtype_documentcontent: - $ot = getMLText("documentcontent"); - break; - default: - $ot = getMLText('objtype_unknown'); - } - return $ot; + return $GLOBALS['translator']->getAttributeObjectTypeText($attrdef); } /* }}} */ function getAttributeValidationText($error, $attrname='', $attrvalue='', $regex='') { /* {{{ */ - $arr = getAttributeValidationError($error, $attrname, $attrvalue, $regex); - - return getMLText($arr[0], $arr[1]); + return $GLOBALS['translator']->getAttributeValidationText($error, $attrname, $attrvalue, $regex); } /* }}} */ function getAttributeValidationError($error, $attrname='', $attrvalue='', $regex='') { /* {{{ */ - if(is_object($attrvalue)) - $attrvalue = $attrvalue->getId(); - switch($error) { - case 14: - return array("attr_not_in_valueset", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 13: - return array("attr_not_a_group", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 12: - return array("attr_not_a_user", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 11: - return array("attr_not_a_folder", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 10: - return array("attr_not_a_document", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 9: - return array("attr_malformed_date", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 8: - return array("attr_malformed_boolean", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 7: - return array("attr_malformed_float", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 6: - return array("attr_malformed_int", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 5: - return array("attr_malformed_email", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 4: - return array("attr_malformed_url", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 3: - return array("attr_no_regex_match", array('attrname'=>$attrname, 'value'=>$attrvalue, 'regex'=>$regex)); - break; - case 2: - return array("attr_max_values", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - case 1: - return array("attr_min_values", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - default: - return array("attr_validation_error", array('attrname'=>$attrname, 'value'=>$attrvalue)); - break; - } + return $GLOBALS['translator']->getAttributeValidationText($error, $attrname, $attrvalue, $regex); } /* }}} */ From c649d9e495c036c1ea8ce930524776fb72110392 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 21:36:56 +0100 Subject: [PATCH 10/13] set default language in translator from session --- inc/inc.Authentication.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inc/inc.Authentication.php b/inc/inc.Authentication.php index 49f87a02a..bc132862b 100644 --- a/inc/inc.Authentication.php +++ b/inc/inc.Authentication.php @@ -65,6 +65,8 @@ if (!isset($_COOKIE["mydms_session"])) { } } +$translator->setDefaultLanguage($session->getLanguage()); + /* Update last access time */ if((int)$resArr['lastAccess']+60 < time()) $session->updateAccess($dms_session); From 1b40717192302f843422d33b9eec0b88d9843e48 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 21:37:33 +0100 Subject: [PATCH 11/13] pass translator to view and controller --- inc/inc.ClassController.php | 3 ++- inc/inc.ClassUI.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/inc/inc.ClassController.php b/inc/inc.ClassController.php index 7f8aa6620..7f21f3294 100644 --- a/inc/inc.ClassController.php +++ b/inc/inc.ClassController.php @@ -30,7 +30,7 @@ class Controller { * @return object an object of a class implementing the view */ static function factory($class, $params=array()) { /* {{{ */ - foreach(['settings', 'session', 'extmgr', 'request', 'logger', 'notifier', 'fulltextservice'] as $vn) { + foreach(['settings', 'session', 'extmgr', 'request', 'logger', 'notifier', 'fulltextservice', 'translator'] as $vn) { if(isset($params[$vn])) ${$vn} = $params[$vn]; else @@ -66,6 +66,7 @@ class Controller { $controller->setParam('logger', $logger); $controller->setParam('notifier', $notifier); $controller->setParam('fulltextservice', $fulltextservice); + $controller->setParam('translator', $translator); return $controller; } return null; diff --git a/inc/inc.ClassUI.php b/inc/inc.ClassUI.php index 22f2771d3..e32c41efe 100644 --- a/inc/inc.ClassUI.php +++ b/inc/inc.ClassUI.php @@ -45,7 +45,7 @@ class UI extends UI_Default { * @return object an object of a class implementing the view */ static public function factory($theme, $class='', $params=array()) { /* {{{ */ - foreach(['settings', 'session', 'extmgr', 'request', 'logger', 'notifier', 'fulltextservice'] as $vn) { + foreach(['settings', 'session', 'extmgr', 'request', 'logger', 'notifier', 'fulltextservice', 'translator'] as $vn) { if(isset($params[$vn])) ${$vn} = $params[$vn]; else @@ -142,6 +142,7 @@ class UI extends UI_Default { $view->setParam('logger', $logger); $view->setParam('notifier', $notifier); $view->setParam('fulltextservice', $fulltextservice); + $view->setParam('translator', $translator); // $view->setParam('settings', $settings); $view->setParam('sitename', $settings->_siteName); $view->setParam('rootfolderid', $settings->_rootFolderID); From 8e06a17f628136b2acf5a711df7305611e03390a Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 21:38:12 +0100 Subject: [PATCH 12/13] add translation phrases from extension --- inc/inc.Extension.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/inc/inc.Extension.php b/inc/inc.Extension.php index 8c4f515dd..61e4ac7dd 100644 --- a/inc/inc.Extension.php +++ b/inc/inc.Extension.php @@ -50,10 +50,7 @@ foreach($extmgr->getExtensionConfiguration() as $extname=>$extconf) { include($langfile); if(isset($__lang) && $__lang) { foreach($__lang as $lang=>&$data) { - if(isset($GLOBALS['LANG'][$lang])) - $GLOBALS['LANG'][$lang] = array_merge($GLOBALS['LANG'][$lang], $data); - else - $GLOBALS['LANG'][$lang] = $data; + $translator->addPhrases($lang, $data); } } } From e755423534ed5425cdd84bc73ac82b7de1920186 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Tue, 28 Oct 2025 21:38:46 +0100 Subject: [PATCH 13/13] add autoload section and symfony/console --- composer-dist.json | 9 ++++++++- composer.json | 19 +++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/composer-dist.json b/composer-dist.json index c914d1c32..33f5f9fd4 100644 --- a/composer-dist.json +++ b/composer-dist.json @@ -18,6 +18,12 @@ "wikimedia/composer-merge-plugin": true } }, + "autoload": { + "psr-4": { + "SeedDMS\\Console\\": "seeddms/utils" + }, + "classmap": ["seeddms/inc/inc.ClassTranslator.php", "seeddms/inc/inc.ClassSettings.php", "seeddms/inc/inc.Version.php"] + }, "require": { "pear/http_request2": "^2", "robthree/twofactorauth": "^3.0", @@ -51,7 +57,8 @@ "seeddms/sqlitefts": "dev-master", "seeddms/http_webdav_server": "dev-master", "wikimedia/composer-merge-plugin": "dev-master", - "cache/memcached-adapter": "^1.2" + "cache/memcached-adapter": "^1.2", + "symfony/console": "^7.2" }, "require-dev": { "composer/composer": "dev-main" diff --git a/composer.json b/composer.json index 196257a4f..b9b36fec7 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,12 @@ "wikimedia/composer-merge-plugin": true } }, + "autoload": { + "psr-4": { + "Seeddms\\Console\\": "utils" + }, + "classmap": ["inc/inc.ClassTranslator.php", "inc/inc.ClassSettings.php", "inc/inc.Version.php"] + }, "require": { "php": ">=8.2.0", "phing/phing": "3.*", @@ -54,7 +60,8 @@ "seeddms/sqlitefts": "dev-master", "seeddms/http_webdav_server": "dev-master", "wikimedia/composer-merge-plugin": "dev-master", - "cache/memcached-adapter": "^1.2" + "cache/memcached-adapter": "^1.2", + "symfony/console": "^7.2" }, "require-dev": { "composer/composer": "dev-main", @@ -72,35 +79,35 @@ "type": "path", "url": "../core", "options": { - "symlink": false + "symlink": true } }, { "type": "path", "url": "../lucene", "options": { - "symlink": false + "symlink": true } }, { "type": "path", "url": "../preview", "options": { - "symlink": false + "symlink": true } }, { "type": "path", "url": "../sqlitefts", "options": { - "symlink": false + "symlink": true } }, { "type": "path", "url": "../http_webdav_server", "options": { - "symlink": false + "symlink": true } } ],