diff --git a/mod/admin.php b/mod/admin.php index 9b8a054c42..50244901d0 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -19,6 +19,7 @@ use Friendica\Core\System; use Friendica\Core\Theme; use Friendica\Core\Update; use Friendica\Core\Worker; +use Friendica\Core\StorageManager; use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\Model\Contact; @@ -1170,6 +1171,39 @@ function admin_page_site_post(App $a) $relay_server_tags = (!empty($_POST['relay_server_tags']) ? Strings::escapeTags(trim($_POST['relay_server_tags'])) : ''); $relay_user_tags = !empty($_POST['relay_user_tags']); $active_panel = (!empty($_POST['active_panel']) ? "#" . Strings::escapeTags(trim($_POST['active_panel'])) : ''); + + $storagebackend = Strings::escapeTags(trim(defaults($_POST, 'storagebackend', ''))); + StorageManager::setBackend($storagebackend); + + // save storage backend form + $storage_opts = $storagebackend::getOptions(); + $storage_form_prefix=preg_replace('|[^a-zA-Z0-9]|' ,'', $storagebackend); + $storage_opts_data = []; + foreach($storage_opts as $name => $info) { + $fieldname = $storage_form_prefix . '_' . $name; + switch ($info[0]) { // type + case 'checkbox': + case 'yesno': + $value = !empty($_POST[$fieldname]); + break; + default: + $value = defaults($_POST, $fieldname, ''); + } + $storage_opts_data[$name] = $value; + } + unset($name); + unset($info); + + $storage_form_errors = $storagebackend::saveOptions($storage_opts_data); + if (count($storage_form_errors)) { + foreach($storage_form_errors as $name => $err) { + notice('Storage backend, ' . $storage_opts[$name][1] . ': ' . $err); + } + $a->internalRedirect('admin/site' . $active_panel); + } + + + // Has the directory url changed? If yes, then resubmit the existing profiles there if ($global_directory != Config::get('system', 'directory') && ($global_directory != '')) { @@ -1482,6 +1516,31 @@ function admin_page_site(App $a) $optimize_max_tablesize = -1; } + /* storage backend */ + $storage_backends = StorageManager::listBackends(); + $storage_current_backend = StorageManager::getBackend(); + + $storage_backends_choices = [ + '' => L10n::t('None') + ]; + foreach($storage_backends as $name=>$class) { + $storage_backends_choices[$class] = $name; + } + unset($storage_backends); + + // build storage config form, + $storage_form_prefix=preg_replace('|[^a-zA-Z0-9]|' ,'', $storage_current_backend); + + $storage_form = []; + foreach($storage_current_backend::getOptions() as $name => $info) { + $type = $info[0]; + $info[0] = $storage_form_prefix . '_' . $name; + $info['type'] = $type; + $info['field'] = 'field_' . $type . '.tpl'; + $storage_form[$name] = $info; + } + + $t = Renderer::getMarkupTemplate('admin/site.tpl'); return Renderer::replaceMacros($t, [ '$title' => L10n::t('Administration'), @@ -1515,6 +1574,9 @@ function admin_page_site(App $a) '$force_ssl' => ['force_ssl', L10n::t("Force SSL"), Config::get('system', 'force_ssl'), L10n::t("Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops.")], '$hide_help' => ['hide_help', L10n::t("Hide help entry from navigation menu"), Config::get('system', 'hide_help'), L10n::t("Hides the menu entry for the Help pages from the navigation menu. You can still access it calling /help directly.")], '$singleuser' => ['singleuser', L10n::t("Single user instance"), Config::get('system', 'singleuser', '---'), L10n::t("Make this instance multi-user or single-user for the named user"), $user_names], + + '$storagebackend' => ['storagebackend', L10n::t("File storage backend"), $storage_current_backend, L10n::t('Backend used to store uploaded files data'), $storage_backends_choices], + '$storageform' => $storage_form, '$maximagesize' => ['maximagesize', L10n::t("Maximum image size"), Config::get('system', 'maximagesize'), L10n::t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")], '$maximagelength' => ['maximagelength', L10n::t("Maximum image length"), Config::get('system', 'max_image_length'), L10n::t("Maximum length in pixels of the longest side of uploaded images. Default is -1, which means no limits.")], '$jpegimagequality' => ['jpegimagequality', L10n::t("JPEG image quality"), Config::get('system', 'jpeg_quality'), L10n::t("Uploaded JPEGS will be saved at this quality setting [0-100]. Default is 100, which is full quality.")], diff --git a/src/Model/Storage/Database.php b/src/Model/Storage/Database.php index 39a1cb64ab..cfd4803c8f 100644 --- a/src/Model/Storage/Database.php +++ b/src/Model/Storage/Database.php @@ -51,4 +51,8 @@ class Database implements IStorage { return DBA::delete('storage', ['id' => $ref]); } -} \ No newline at end of file + + public static function getOptions() { return []; } + + public static function saveOptions($data) { return []; } +} diff --git a/src/Model/Storage/Filesystem.php b/src/Model/Storage/Filesystem.php index 1cfb5effe8..2f375491b2 100644 --- a/src/Model/Storage/Filesystem.php +++ b/src/Model/Storage/Filesystem.php @@ -24,11 +24,11 @@ use Friendica\Util\Strings; class Filesystem implements IStorage { // Default base folder - const DEFAULT_BASE_FOLDER = "storage"; + const DEFAULT_BASE_FOLDER = 'storage'; private static function getBasePath() { - return Config::get("storage", "filesystem_path", self::DEFAULT_BASE_FOLDER); + return Config::get('storage', 'filesystem_path', self::DEFAULT_BASE_FOLDER); } /** @@ -43,7 +43,7 @@ class Filesystem implements IStorage $fold2 = substr($ref, 2, 2); $file = substr($ref, 4); - return "{$base}/{$fold1}/{$fold2}/{$file}"; + return implode('/', [$base, $fold1, $fold2, $file]); } @@ -57,8 +57,8 @@ class Filesystem implements IStorage if (!is_dir($path)) { if (!mkdir($path, 0770, true)) { - Logger::log("Failed to create dirs {$path}"); - throw new StorageException(L10n::t("Filesystem storage failed to create '%s'. Check you write permissions.", $path)); + Logger::log('Failed to create dirs ' . $path); + throw new StorageException(L10n::t('Filesystem storage failed to create "%s". Check you write permissions.', $path)); killme(); } } @@ -66,13 +66,13 @@ class Filesystem implements IStorage $base = self::getBasePath(); while ($path !== $base) { - if (!is_file($path . "/index.html")) { - file_put_contents($path . "/index.html", ""); + if (!is_file($path . '/index.html')) { + file_put_contents($path . '/index.html', ''); } $path = dirname($path); } - if (!is_file($path . "/index.html")) { - file_put_contents($path . "/index.html", ""); + if (!is_file($path . '/index.html')) { + file_put_contents($path . '/index.html', ''); } } @@ -80,15 +80,15 @@ class Filesystem implements IStorage { $file = self::pathForRef($ref); if (!is_file($file)) { - return ""; + return ''; } return file_get_contents($file); } - public static function put($data, $ref = "") + public static function put($data, $ref = '') { - if ($ref === "") { + if ($ref === '') { $ref = Strings::getRandomHex(); } $file = self::pathForRef($ref); @@ -97,8 +97,8 @@ class Filesystem implements IStorage $r = file_put_contents($file, $data); if ($r === FALSE) { - Logger::log("Failed to write data to {$file}"); - throw new StorageException(L10n::t("Filesystem storage failed to save data to '%s'. Check your write permissions", $file)); + Logger::log('Failed to write data to ' . $file); + throw new StorageException(L10n::t('Filesystem storage failed to save data to "%s". Check your write permissions', $file)); killme(); } return $ref; @@ -114,4 +114,28 @@ class Filesystem implements IStorage return unlink($file); } + public static function getOptions() + { + return [ + 'storagepath' => [ + 'input', + L10n::t('Storage base path'), + self::getBasePath(), + L10n::t('Folder were uploaded files are saved. For maximum security, This should be a path outside web server folder tree') + ] + ]; + } + + public static function saveOptions($data) + { + $storagepath = defaults($data, 'storagepath', ''); + if ($storagepath === '' || !is_dir($storagepath)) { + return [ + 'storagepath' => L10n::t('Enter a valid existing folder') + ]; + }; + Config::set('storage', 'filesystem_path', $storagepath); + return []; + } + } diff --git a/src/Model/Storage/IStorage.php b/src/Model/Storage/IStorage.php index deacb8ffc9..1b0129e5e6 100644 --- a/src/Model/Storage/IStorage.php +++ b/src/Model/Storage/IStorage.php @@ -32,4 +32,58 @@ interface IStorage * @return boolean True on success */ public static function delete($ref); + + /** + * @brief Get info about storage options + * + * @return array + * + * This method return an array with informations about storage options + * from which the form presented to the user is build. + * + * The returned array is: + * + * [ + * 'option1name' => [ ..info.. ], + * 'option2name' => [ ..info.. ], + * ... + * ] + * + * An empty array can be returned if backend doesn't have any options + * + * The info array for each option MUST be as follows: + * + * [ + * 'type', // define the field used in form, and the type of data. + * // one of 'checkbox', 'combobox', 'custom', 'datetime', + * // 'input', 'intcheckbox', 'password', 'radio', 'richtext' + * // 'select', 'select_raw', 'textarea', 'yesno' + * + * 'label', // Translatable label of the field + * 'value', // Current value + * 'help text', // Translatable description for the field + * extra data // Optional. Depends on 'type': + * // select: array [ value => label ] of choices + * // intcheckbox: value of input element + * // select_raw: prebuild html string of < option > tags + * // yesno: array [ 'label no', 'label yes'] + * ] + * + * See https://github.com/friendica/friendica/wiki/Quick-Template-Guide + */ + public static function getOptions(); + + /** + * @brief Validate and save options + * + * @param array $data Array [optionname => value] to be saved + * + * @return array Validation errors: [optionname => error message] + * + * Return array must be empty if no error. + */ + public static function saveOptions($data); + } + + diff --git a/view/templates/admin/site.tpl b/view/templates/admin/site.tpl index a7adfd5a1a..98d0dadfb1 100644 --- a/view/templates/admin/site.tpl +++ b/view/templates/admin/site.tpl @@ -71,6 +71,11 @@

{{$upload}}

+ {{include file="field_select.tpl" field=$storagebackend}} + {{foreach from=$storageform item=$field}} + {{include file=$field.field field=$field}} + {{/foreach}} +
{{include file="field_input.tpl" field=$maximagesize}} {{include file="field_input.tpl" field=$maximagelength}} {{include file="field_input.tpl" field=$jpegimagequality}} diff --git a/view/theme/frio/templates/admin/site.tpl b/view/theme/frio/templates/admin/site.tpl index 5d4fd2506e..c8ee133a95 100644 --- a/view/theme/frio/templates/admin/site.tpl +++ b/view/theme/frio/templates/admin/site.tpl @@ -135,7 +135,11 @@
- + {{include file="field_select.tpl" field=$storagebackend}} + {{foreach from=$storageform item=$field}} + {{include file=$field.field field=$field}} + {{/foreach}} +
{{include file="field_input.tpl" field=$maximagesize}} {{include file="field_input.tpl" field=$maximagelength}} {{include file="field_input.tpl" field=$jpegimagequality}}