1
0
Fork 0

Merge remote-tracking branch 'upstream/2023.03-rc' into npf2

This commit is contained in:
Michael 2023-03-31 19:49:51 +00:00
commit 58a4469f25
39 changed files with 587 additions and 316 deletions

View file

@ -77,7 +77,7 @@ pipeline:
when: when:
matrix: matrix:
PHP_MAJOR_VERSION: 7.4 PHP_MAJOR_VERSION: 7.4
PHP_VERSION: 7.4.18 PHP_VERSION: 7.4.33
repo: repo:
- friendica/friendica - friendica/friendica
settings: settings:

View file

@ -76,8 +76,8 @@ DI::config()->reload();
if (empty(DI::config()->get('system', 'pidfile'))) { if (empty(DI::config()->get('system', 'pidfile'))) {
die(<<<TXT die(<<<TXT
Please set system.pidfile in config/local.config.php. For example: Please set system.pidfile in config/local.config.php. For example:
'system' => [ 'system' => [
'pidfile' => '/path/to/daemon.pid', 'pidfile' => '/path/to/daemon.pid',
], ],
TXT TXT
@ -247,5 +247,6 @@ while (true) {
} }
function shutdown() { function shutdown() {
posix_kill(posix_getpid(), SIGTERM);
posix_kill(posix_getpid(), SIGHUP); posix_kill(posix_getpid(), SIGHUP);
} }

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2023.03-rc (Giant Rhubarb) -- Friendica 2023.03-rc (Giant Rhubarb)
-- DB_UPDATE_VERSION 1517 -- DB_UPDATE_VERSION 1518
-- ------------------------------------------ -- ------------------------------------------
@ -2849,7 +2849,9 @@ CREATE VIEW `account-view` AS SELECT
`apcontact`.`statuses_count` AS `ap-statuses_count`, `apcontact`.`statuses_count` AS `ap-statuses_count`,
`gserver`.`site_name` AS `site_name`, `gserver`.`site_name` AS `site_name`,
`gserver`.`platform` AS `platform`, `gserver`.`platform` AS `platform`,
`gserver`.`version` AS `version` `gserver`.`version` AS `version`,
`gserver`.`blocked` AS `server-blocked`,
`gserver`.`failed` AS `server-failed`
FROM `contact` FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
@ -2953,7 +2955,9 @@ CREATE VIEW `account-user-view` AS SELECT
`apcontact`.`statuses_count` AS `ap-statuses_count`, `apcontact`.`statuses_count` AS `ap-statuses_count`,
`gserver`.`site_name` AS `site_name`, `gserver`.`site_name` AS `site_name`,
`gserver`.`platform` AS `platform`, `gserver`.`platform` AS `platform`,
`gserver`.`version` AS `version` `gserver`.`version` AS `version`,
`gserver`.`blocked` AS `server-blocked`,
`gserver`.`failed` AS `server-failed`
FROM `contact` AS `ucontact` FROM `contact` AS `ucontact`
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id`

View file

@ -58,6 +58,16 @@ interface IManageConfigValues
*/ */
public function get(string $cat, string $key = null, $default_value = null); public function get(string $cat, string $key = null, $default_value = null);
/**
* Returns true, if the current config can be changed
*
* @param string $cat The category of the configuration value
* @param string $key The configuration key to query
*
* @return bool true, if writing is possible
*/
public function isWritable(string $cat, string $key): bool;
/** /**
* Sets a configuration value for system config * Sets a configuration value for system config
* *

View file

@ -80,6 +80,12 @@ class DatabaseConfig implements IManageConfigValues
return $this->cache->get($cat, $key) ?? $default_value; return $this->cache->get($cat, $key) ?? $default_value;
} }
/** {@inheritDoc} */
public function isWritable(string $cat, string $key): bool
{
return $this->cache->getSource($cat, $key) < Cache::SOURCE_ENV;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
public function set(string $cat, string $key, $value): bool public function set(string $cat, string $key, $value): bool
{ {

View file

@ -68,6 +68,12 @@ class ReadOnlyFileConfig implements IManageConfigValues
return $this->configCache->get($cat, $key) ?? $default_value; return $this->configCache->get($cat, $key) ?? $default_value;
} }
/** {@inheritDoc} */
public function isWritable(string $cat, string $key): bool
{
return $this->configCache->getSource($cat, $key) < Cache::SOURCE_ENV;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
public function set(string $cat, string $key, $value): bool public function set(string $cat, string $key, $value): bool
{ {

View file

@ -31,6 +31,15 @@ use ParagonIE\HiddenString\HiddenString;
*/ */
class Cache class Cache
{ {
/** @var int[] A list of valid config source */
const VALID_SOURCES = [
self::SOURCE_STATIC,
self::SOURCE_FILE,
self::SOURCE_DATA,
self::SOURCE_ENV,
self::SOURCE_FIX,
];
/** @var int Indicates that the cache entry is a default value - Lowest Priority */ /** @var int Indicates that the cache entry is a default value - Lowest Priority */
const SOURCE_STATIC = 0; const SOURCE_STATIC = 0;
/** @var int Indicates that the cache entry is set by file - Low Priority */ /** @var int Indicates that the cache entry is set by file - Low Priority */

View file

@ -79,7 +79,7 @@ class Search
$user_data['url'] ?? '', $user_data['url'] ?? '',
$user_data['photo'] ?? '', $user_data['photo'] ?? '',
$user_data['network'] ?? '', $user_data['network'] ?? '',
$contactDetails['id'] ?? 0, $contactDetails['cid'] ?? 0,
$user_data['id'] ?? 0, $user_data['id'] ?? 0,
$user_data['tags'] ?? '' $user_data['tags'] ?? ''
); );
@ -146,7 +146,7 @@ class Search
$profile['photo'] ?? '', $profile['photo'] ?? '',
Protocol::DFRN, Protocol::DFRN,
$contactDetails['cid'] ?? 0, $contactDetails['cid'] ?? 0,
0, $contactDetails['zid'] ?? 0,
$profile['tags'] ?? '' $profile['tags'] ?? ''
); );
@ -171,7 +171,7 @@ class Search
{ {
Logger::info('Searching', ['search' => $search, 'type' => $type, 'start' => $start, 'itempage' => $itemPage]); Logger::info('Searching', ['search' => $search, 'type' => $type, 'start' => $start, 'itempage' => $itemPage]);
$contacts = Contact::searchByName($search, $type == self::TYPE_FORUM ? 'community' : ''); $contacts = Contact::searchByName($search, $type == self::TYPE_FORUM ? 'community' : '', true);
$resultList = new ResultList($start, $itemPage, count($contacts)); $resultList = new ResultList($start, $itemPage, count($contacts));
@ -179,12 +179,12 @@ class Search
$result = new ContactResult( $result = new ContactResult(
$contact['name'], $contact['name'],
$contact['addr'], $contact['addr'],
$contact['addr'], $contact['addr'] ?: $contact['url'],
$contact['url'], $contact['url'],
$contact['photo'], $contact['photo'],
$contact['network'], $contact['network'],
$contact['cid'] ?? 0, 0,
$contact['zid'] ?? 0, $contact['pid'],
$contact['keywords'] $contact['keywords']
); );
@ -226,7 +226,7 @@ class Search
// check if searching in the local global contact table is enabled // check if searching in the local global contact table is enabled
if (DI::config()->get('system', 'poco_local_search')) { if (DI::config()->get('system', 'poco_local_search')) {
$return = Contact::searchByName($search, $mode); $return = Contact::searchByName($search, $mode, true);
} else { } else {
$p = $page > 1 ? 'p=' . $page : ''; $p = $page > 1 ? 'p=' . $page : '';
$curlResult = DI::httpClient()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), HttpClientAccept::JSON); $curlResult = DI::httpClient()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), HttpClientAccept::JSON);

View file

@ -109,6 +109,8 @@ interface IHandleUserSessions extends IHandleSessions
/** /**
* Set the session variable that contains the contact IDs for the visitor's contact URL * Set the session variable that contains the contact IDs for the visitor's contact URL
*
* @param string $my_url
*/ */
public function setVisitorsContacts(); public function setVisitorsContacts(string $my_url);
} }

View file

@ -140,9 +140,9 @@ class UserSession implements IHandleUserSessions
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public function setVisitorsContacts() public function setVisitorsContacts(string $my_url)
{ {
$this->session->set('remote', Contact::getVisitorByUrl($this->session->get('my_url'))); $this->session->set('remote', Contact::getVisitorByUrl($my_url));
} }
/** {@inheritDoc} */ /** {@inheritDoc} */

View file

@ -22,6 +22,7 @@
namespace Friendica\Factory\Api\Mastodon; namespace Friendica\Factory\Api\Mastodon;
use Exception; use Exception;
use Friendica\Network\HTTPException;
use Friendica\Object\Api\Mastodon\Relationship as RelationshipEntity; use Friendica\Object\Api\Mastodon\Relationship as RelationshipEntity;
use Friendica\BaseFactory; use Friendica\BaseFactory;
use Friendica\Model\Contact; use Friendica\Model\Contact;
@ -41,9 +42,15 @@ class Relationship extends BaseFactory
$pcid = !empty($cdata['public']) ? $cdata['public'] : $contactId; $pcid = !empty($cdata['public']) ? $cdata['public'] : $contactId;
$cid = !empty($cdata['user']) ? $cdata['user'] : $contactId; $cid = !empty($cdata['user']) ? $cdata['user'] : $contactId;
$contact = Contact::getById($cid);
if (!$contact) {
$this->logger->warning('Target contact not found', ['contactId' => $contactId, 'uid' => $uid, 'pcid' => $pcid, 'cid' => $cid]);
throw new HTTPException\NotFoundException('Contact not found.');
}
return new RelationshipEntity( return new RelationshipEntity(
$pcid, $pcid,
Contact::getById($cid), $contact,
Contact\User::isBlocked($cid, $uid), Contact\User::isBlocked($cid, $uid),
Contact\User::isIgnored($cid, $uid) Contact\User::isIgnored($cid, $uid)
); );

View file

@ -3504,16 +3504,17 @@ class Contact
/** /**
* Search contact table by nick or name * Search contact table by nick or name
* *
* @param string $search Name or nick * @param string $search Name or nick
* @param string $mode Search mode (e.g. "community") * @param string $mode Search mode (e.g. "community")
* @param int $uid User ID * @param bool $show_blocked Show users from blocked servers. Default is false
* @param int $limit Maximum amount of returned values * @param int $uid User ID
* @param int $offset Limit offset * @param int $limit Maximum amount of returned values
* @param int $offset Limit offset
* *
* @return array with search results * @return array with search results
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function searchByName(string $search, string $mode = '', int $uid = 0, int $limit = 0, int $offset = 0): array public static function searchByName(string $search, string $mode = '', bool $show_blocked = false, int $uid = 0, int $limit = 0, int $offset = 0): array
{ {
if (empty($search)) { if (empty($search)) {
return []; return [];
@ -3529,7 +3530,18 @@ class Contact
$networks[] = Protocol::OSTATUS; $networks[] = Protocol::OSTATUS;
} }
$condition = ['network' => $networks, 'failed' => false, 'deleted' => false, 'uid' => $uid]; $condition = [
'network' => $networks,
'server-failed' => false,
'failed' => false,
'deleted' => false,
'unsearchable' => false,
'uid' => $uid
];
if (!$show_blocked) {
$condition['server-blocked'] = true;
}
if ($uid == 0) { if ($uid == 0) {
$condition['blocked'] = false; $condition['blocked'] = false;
@ -3553,10 +3565,9 @@ class Contact
} }
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions($condition,
["(NOT `unsearchable` OR `nurl` IN (SELECT `nurl` FROM `owner-view` WHERE `publish` OR `net-publish`)) ["(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]);
AND (`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]);
return self::selectToArray([], $condition, $params); return DBA::selectToArray('account-user-view', [], $condition, $params);
} }
/** /**

View file

@ -795,14 +795,16 @@ class Profile
$visitor = Contact::getById($cid); $visitor = Contact::getById($cid);
// Authenticate the visitor. // Authenticate the visitor.
$_SESSION['authenticated'] = 1; DI::userSession()->setMultiple([
$_SESSION['visitor_id'] = $visitor['id']; 'authenticated' => 1,
$_SESSION['visitor_handle'] = $visitor['addr']; 'visitor_id' => $visitor['id'],
$_SESSION['visitor_home'] = $visitor['url']; 'visitor_handle' => $visitor['addr'],
$_SESSION['my_url'] = $visitor['url']; 'visitor_home' => $visitor['url'],
$_SESSION['remote_comment'] = $visitor['subscribe']; 'my_url' => $visitor['url'],
'remote_comment' => $visitor['subscribe'],
]);
DI::userSession()->setVisitorsContacts(); DI::userSession()->setVisitorsContacts($visitor['url']);
$a->setContactId($visitor['id']); $a->setContactId($visitor['id']);

View file

@ -48,9 +48,15 @@ class Settings extends BaseAdmin
return; return;
} }
DI::config()->set('system', 'logfile', $logfile); if (DI::config()->isWritable('system', 'logfile')) {
DI::config()->set('system', 'debugging', $debugging); DI::config()->set('system', 'logfile', $logfile);
DI::config()->set('system', 'loglevel', $loglevel); }
if (DI::config()->isWritable('system', 'debugging')) {
DI::config()->set('system', 'debugging', $debugging);
}
if (DI::config()->isWritable('system', 'loglevel')) {
DI::config()->set('system', 'loglevel', $loglevel);
}
DI::baseUrl()->redirect('admin/logs'); DI::baseUrl()->redirect('admin/logs');
} }
@ -82,9 +88,9 @@ class Settings extends BaseAdmin
'$clear' => DI::l10n()->t('Clear'), '$clear' => DI::l10n()->t('Clear'),
'$logname' => DI::config()->get('system', 'logfile'), '$logname' => DI::config()->get('system', 'logfile'),
// see /help/smarty3-templates#1_1 on any Friendica node // see /help/smarty3-templates#1_1 on any Friendica node
'$debugging' => ['debugging', DI::l10n()->t("Enable Debugging"), DI::config()->get('system', 'debugging'), ""], '$debugging' => ['debugging', DI::l10n()->t('Enable Debugging'), DI::config()->get('system', 'debugging'), !DI::config()->isWritable('system', 'debugging') ? DI::l10n()->t('<strong>Read-only</strong> because it is set by an environment variable') : '', !DI::config()->isWritable('system', 'debugging') ? 'disabled' : ''],
'$logfile' => ['logfile', DI::l10n()->t("Log file"), DI::config()->get('system', 'logfile'), DI::l10n()->t("Must be writable by web server. Relative to your Friendica top-level directory.")], '$logfile' => ['logfile', DI::l10n()->t('Log file'), DI::config()->get('system', 'logfile'), DI::l10n()->t('Must be writable by web server. Relative to your Friendica top-level directory.') . (!DI::config()->isWritable('system', 'logfile') ? '<br>' . DI::l10n()->t('<strong>Read-only</strong> because it is set by an environment variable') : ''), '', !DI::config()->isWritable('system', 'logfile') ? 'disabled' : ''],
'$loglevel' => ['loglevel', DI::l10n()->t("Log level"), DI::config()->get('system', 'loglevel'), "", $log_choices], '$loglevel' => ['loglevel', DI::l10n()->t("Log level"), DI::config()->get('system', 'loglevel'), !DI::config()->isWritable('system', 'loglevel') ? DI::l10n()->t('<strong>Read-only</strong> because it is set by an environment variable') : '', $log_choices, !DI::config()->isWritable('system', 'loglevel') ? 'disabled' : ''],
'$form_security_token' => self::getFormSecurityToken("admin_logs"), '$form_security_token' => self::getFormSecurityToken("admin_logs"),
'$phpheader' => DI::l10n()->t("PHP logging"), '$phpheader' => DI::l10n()->t("PHP logging"),
'$phphint' => DI::l10n()->t("To temporarily enable logging of PHP errors and warnings you can prepend the following to the index.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."), '$phphint' => DI::l10n()->t("To temporarily enable logging of PHP errors and warnings you can prepend the following to the index.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."),

View file

@ -165,7 +165,9 @@ class Site extends BaseAdmin
$transactionConfig->set('system', 'poco_discovery' , $poco_discovery); $transactionConfig->set('system', 'poco_discovery' , $poco_discovery);
$transactionConfig->set('system', 'poco_local_search' , $poco_local_search); $transactionConfig->set('system', 'poco_local_search' , $poco_local_search);
$transactionConfig->set('system', 'nodeinfo' , $nodeinfo); $transactionConfig->set('system', 'nodeinfo' , $nodeinfo);
$transactionConfig->set('config', 'sitename' , $sitename); if (DI::config()->isWritable('config', 'sitename')) {
$transactionConfig->set('config', 'sitename', $sitename);
}
$transactionConfig->set('config', 'sender_email' , $sender_email); $transactionConfig->set('config', 'sender_email' , $sender_email);
$transactionConfig->set('system', 'suppress_tags' , $suppress_tags); $transactionConfig->set('system', 'suppress_tags' , $suppress_tags);
$transactionConfig->set('system', 'shortcut_icon' , $shortcut_icon); $transactionConfig->set('system', 'shortcut_icon' , $shortcut_icon);
@ -188,7 +190,9 @@ class Site extends BaseAdmin
} else { } else {
$transactionConfig->set('config', 'info', $additional_info); $transactionConfig->set('config', 'info', $additional_info);
} }
$transactionConfig->set('system', 'language', $language); if (DI::config()->isWritable('system', 'language')) {
$transactionConfig->set('system', 'language', $language);
}
$transactionConfig->set('system', 'theme', $theme); $transactionConfig->set('system', 'theme', $theme);
Theme::install($theme); Theme::install($theme);
@ -413,7 +417,7 @@ class Site extends BaseAdmin
'$relocate_cmd' => DI::l10n()->t('(Friendica directory)# bin/console relocate https://newdomain.com'), '$relocate_cmd' => DI::l10n()->t('(Friendica directory)# bin/console relocate https://newdomain.com'),
// name, label, value, help string, extra data... // name, label, value, help string, extra data...
'$sitename' => ['sitename', DI::l10n()->t('Site name'), DI::config()->get('config', 'sitename'), ''], '$sitename' => ['sitename', DI::l10n()->t('Site name'), DI::config()->get('config', 'sitename'), !DI::config()->isWritable('config', 'sitename') ? DI::l10n()->t('<strong>Read-only</strong> because it is set by an environment variable') : '', '', !DI::config()->isWritable('config', 'sitename') ? 'disabled' : ''],
'$sender_email' => ['sender_email', DI::l10n()->t('Sender Email'), DI::config()->get('config', 'sender_email'), DI::l10n()->t('The email address your server shall use to send notification emails from.'), '', '', 'email'], '$sender_email' => ['sender_email', DI::l10n()->t('Sender Email'), DI::config()->get('config', 'sender_email'), DI::l10n()->t('The email address your server shall use to send notification emails from.'), '', '', 'email'],
'$system_actor_name' => ['system_actor_name', DI::l10n()->t('Name of the system actor'), User::getActorName(), DI::l10n()->t("Name of the internal system account that is used to perform ActivityPub requests. This must be an unused username. If set, this can't be changed again.")], '$system_actor_name' => ['system_actor_name', DI::l10n()->t('Name of the system actor'), User::getActorName(), DI::l10n()->t("Name of the internal system account that is used to perform ActivityPub requests. This must be an unused username. If set, this can't be changed again.")],
'$banner' => ['banner', DI::l10n()->t('Banner/Logo'), $banner, ''], '$banner' => ['banner', DI::l10n()->t('Banner/Logo'), $banner, ''],
@ -421,7 +425,7 @@ class Site extends BaseAdmin
'$shortcut_icon' => ['shortcut_icon', DI::l10n()->t('Shortcut icon'), DI::config()->get('system', 'shortcut_icon'), DI::l10n()->t('Link to an icon that will be used for browsers.')], '$shortcut_icon' => ['shortcut_icon', DI::l10n()->t('Shortcut icon'), DI::config()->get('system', 'shortcut_icon'), DI::l10n()->t('Link to an icon that will be used for browsers.')],
'$touch_icon' => ['touch_icon', DI::l10n()->t('Touch icon'), DI::config()->get('system', 'touch_icon'), DI::l10n()->t('Link to an icon that will be used for tablets and mobiles.')], '$touch_icon' => ['touch_icon', DI::l10n()->t('Touch icon'), DI::config()->get('system', 'touch_icon'), DI::l10n()->t('Link to an icon that will be used for tablets and mobiles.')],
'$additional_info' => ['additional_info', DI::l10n()->t('Additional Info'), $additional_info, DI::l10n()->t('For public servers: you can add additional information here that will be listed at %s/servers.', Search::getGlobalDirectory())], '$additional_info' => ['additional_info', DI::l10n()->t('Additional Info'), $additional_info, DI::l10n()->t('For public servers: you can add additional information here that will be listed at %s/servers.', Search::getGlobalDirectory())],
'$language' => ['language', DI::l10n()->t('System language'), DI::config()->get('system', 'language'), '', $lang_choices], '$language' => ['language', DI::l10n()->t('System language'), DI::config()->get('system', 'language'), !DI::config()->isWritable('system', 'language') ? DI::l10n()->t("<strong>Read-only</strong> because it is set by an environment variable") : '', $lang_choices, !DI::config()->isWritable('system', 'language') ? 'disabled' : ''],
'$theme' => ['theme', DI::l10n()->t('System theme'), DI::config()->get('system', 'theme'), DI::l10n()->t('Default system theme - may be over-ridden by user profiles - <a href="%s" id="cnftheme">Change default theme settings</a>', DI::baseUrl() . '/admin/themes'), $theme_choices], '$theme' => ['theme', DI::l10n()->t('System theme'), DI::config()->get('system', 'theme'), DI::l10n()->t('Default system theme - may be over-ridden by user profiles - <a href="%s" id="cnftheme">Change default theme settings</a>', DI::baseUrl() . '/admin/themes'), $theme_choices],
'$theme_mobile' => ['theme_mobile', DI::l10n()->t('Mobile system theme'), DI::config()->get('system', 'mobile-theme', '---'), DI::l10n()->t('Theme for mobile devices'), $theme_choices_mobile], '$theme_mobile' => ['theme_mobile', DI::l10n()->t('Mobile system theme'), DI::config()->get('system', 'mobile-theme', '---'), DI::l10n()->t('Theme for mobile devices'), $theme_choices_mobile],
'$force_ssl' => ['force_ssl', DI::l10n()->t('Force SSL'), DI::config()->get('system', 'force_ssl'), DI::l10n()->t('Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops.')], '$force_ssl' => ['force_ssl', DI::l10n()->t('Force SSL'), DI::config()->get('system', 'force_ssl'), DI::l10n()->t('Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops.')],

View file

@ -76,7 +76,7 @@ class Storage extends BaseAdmin
} }
} }
if (!empty($_POST['submit_save_set'])) { if (!empty($_POST['submit_save_set']) && DI::config()->isWritable('storage', 'name') ) {
try { try {
$newstorage = DI::storageManager()->getWritableStorageByName($storagebackend); $newstorage = DI::storageManager()->getWritableStorageByName($storagebackend);
@ -145,6 +145,8 @@ class Storage extends BaseAdmin
'$save_reload' => DI::l10n()->t('Save & Reload'), '$save_reload' => DI::l10n()->t('Save & Reload'),
'$noconfig' => DI::l10n()->t('This backend doesn\'t have custom settings'), '$noconfig' => DI::l10n()->t('This backend doesn\'t have custom settings'),
'$form_security_token' => self::getFormSecurityToken("admin_storage"), '$form_security_token' => self::getFormSecurityToken("admin_storage"),
'$storagebackend_ro_txt' => !DI::config()->isWritable('storage', 'name') ? DI::l10n()->t('Changing the current backend is prohibited because it is set by an environment variable') : '',
'$is_writable' => DI::config()->isWritable('storage', 'name'),
'$storagebackend' => $current_storage_backend instanceof ICanWriteToStorage ? $current_storage_backend::getName() : DI::l10n()->t('Database (legacy)'), '$storagebackend' => $current_storage_backend instanceof ICanWriteToStorage ? $current_storage_backend::getName() : DI::l10n()->t('Database (legacy)'),
'$availablestorageforms' => $available_storage_forms, '$availablestorageforms' => $available_storage_forms,
]); ]);

View file

@ -60,7 +60,7 @@ class Search extends BaseApi
} }
if (empty($accounts)) { if (empty($accounts)) {
$contacts = Contact::searchByName($request['q'], '', $request['following'] ? $uid : 0, $request['limit'], $request['offset']); $contacts = Contact::searchByName($request['q'], '', false, $request['following'] ? $uid : 0, $request['limit'], $request['offset']);
foreach ($contacts as $contact) { foreach ($contacts as $contact) {
$accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid); $accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid);
} }

View file

@ -115,7 +115,7 @@ class Search extends BaseApi
} }
$accounts = []; $accounts = [];
foreach (Contact::searchByName($q, '', $following ? $uid : 0, $limit, $offset) as $contact) { foreach (Contact::searchByName($q, '', $following ? $uid : 0, false, $limit, $offset) as $contact) {
$accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid); $accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid);
} }

View file

@ -71,7 +71,7 @@ class Statuses extends BaseApi
'origin' => true, 'origin' => true,
]; ];
$post = Post::selectFirst(['uri-id', 'id', 'gravity', 'uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'network'], $condition); $post = Post::selectFirst(['uri-id', 'id', 'gravity', 'verb', 'uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'network'], $condition);
if (empty($post['id'])) { if (empty($post['id'])) {
throw new HTTPException\NotFoundException('Item with URI ID ' . $this->parameters['id'] . ' not found for user ' . $uid . '.'); throw new HTTPException\NotFoundException('Item with URI ID ' . $this->parameters['id'] . ' not found for user ' . $uid . '.');
} }
@ -87,6 +87,8 @@ class Statuses extends BaseApi
$item['uid'] = $post['uid']; $item['uid'] = $post['uid'];
$item['body'] = $body; $item['body'] = $body;
$item['network'] = $post['network']; $item['network'] = $post['network'];
$item['gravity'] = $post['gravity'];
$item['verb'] = $post['verb'];
$item['app'] = $this->getApp(); $item['app'] = $this->getApp();
if (!empty($request['language'])) { if (!empty($request['language'])) {
@ -108,7 +110,7 @@ class Statuses extends BaseApi
} }
} }
$item = DI::contentItem()->expandTags($item, $request['visibility'] == 'direct'); $item = DI::contentItem()->expandTags($item);
/* /*
The provided ids in the request value consists of these two sources: The provided ids in the request value consists of these two sources:

View file

@ -40,6 +40,7 @@ use Friendica\Network\HTTPException\ForbiddenException;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use GuzzleHttp\Psr7\Uri;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
class Follow extends BaseModule class Follow extends BaseModule
@ -223,17 +224,26 @@ class Follow extends BaseModule
protected function followRemoteItem(string $url) protected function followRemoteItem(string $url)
{ {
$itemId = Item::fetchByLink($url, $this->session->getLocalUserId()); try {
if (!$itemId) { $uri = new Uri($url);
// If the user-specific search failed, we search and probe a public post if (!$uri->getScheme()) {
$itemId = Item::fetchByLink($url); return;
}
if (!empty($itemId)) {
$item = Post::selectFirst(['guid'], ['id' => $itemId]);
if (!empty($item['guid'])) {
$this->baseUrl->redirect('display/' . $item['guid']);
} }
$itemId = Item::fetchByLink($url, $this->session->getLocalUserId());
if (!$itemId) {
// If the user-specific search failed, we search and probe a public post
$itemId = Item::fetchByLink($url);
}
if (!empty($itemId)) {
$item = Post::selectFirst(['guid'], ['id' => $itemId]);
if (!empty($item['guid'])) {
$this->baseUrl->redirect('display/' . $item['guid']);
}
}
} catch (\InvalidArgumentException $e) {
return;
} }
} }
} }

View file

@ -121,11 +121,14 @@ class Contacts extends Module\BaseProfile
['uri-id' => $contact['uri-id'], 'uid' => [0, $this->userSession->getLocalUserId()]], ['uri-id' => $contact['uri-id'], 'uid' => [0, $this->userSession->getLocalUserId()]],
['order' => ['uid' => 'DESC']] ['order' => ['uid' => 'DESC']]
); );
return Module\Contact::getContactTemplateVars($contact); return $contact ? Module\Contact::getContactTemplateVars($contact) : null;
}, },
Model\Contact::selectToArray(['uri-id'], $condition, $params) Model\Contact::selectToArray(['uri-id'], $condition, $params)
); );
// Remove nonexistent contacts
$contacts = array_filter($contacts);
$desc = ''; $desc = '';
switch ($type) { switch ($type) {
case 'followers': case 'followers':

View file

@ -77,7 +77,7 @@ class Relationship extends BaseDataTransferObject
* @param bool $blocked "true" if user is blocked * @param bool $blocked "true" if user is blocked
* @param bool $muted "true" if user is muted * @param bool $muted "true" if user is muted
*/ */
public function __construct(int $contactId, array $contactRecord = [], bool $blocked = false, bool $muted = false) public function __construct(int $contactId, array $contactRecord, bool $blocked = false, bool $muted = false)
{ {
$this->id = (string)$contactId; $this->id = (string)$contactId;
$this->following = false; $this->following = false;

View file

@ -264,7 +264,7 @@ class ClientToServer
$item['contact-id'] = $owner['id']; $item['contact-id'] = $owner['id'];
$item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid); $item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid);
$item['title'] = $object_data['name']; $item['title'] = $object_data['name'];
$item['body'] = Markdown::toBBCode($object_data['content']); $item['body'] = Markdown::toBBCode($object_data['content'] ?? '');
$item['app'] = $application['name'] ?? 'API'; $item['app'] = $application['name'] ?? 'API';
if (!empty($object_data['target'][Receiver::TARGET_GLOBAL])) { if (!empty($object_data['target'][Receiver::TARGET_GLOBAL])) {
@ -354,6 +354,10 @@ class ClientToServer
$apcontact = APContact::getByURL($owner['url']); $apcontact = APContact::getByURL($owner['url']);
if (empty($apcontact)) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
return self::getCollection($condition, DI::baseUrl() . '/outbox/' . $owner['nickname'], $page, $max_id, $uid, $apcontact['statuses_count']); return self::getCollection($condition, DI::baseUrl() . '/outbox/' . $owner['nickname'], $page, $max_id, $uid, $apcontact['statuses_count']);
} }

View file

@ -1278,8 +1278,10 @@ class Processor
$name = Receiver::PUBLIC_COLLECTION; $name = Receiver::PUBLIC_COLLECTION;
} elseif ($path = parse_url($receiver, PHP_URL_PATH)) { } elseif ($path = parse_url($receiver, PHP_URL_PATH)) {
$name = trim($path, '/'); $name = trim($path, '/');
} elseif ($host = parse_url($receiver, PHP_URL_HOST)) {
$name = $host;
} else { } else {
Logger::warning('Unable to coerce name from receiver', ['receiver' => $receiver]); Logger::warning('Unable to coerce name from receiver', ['element' => $element, 'type' => $type, 'receiver' => $receiver]);
$name = ''; $name = '';
} }

View file

@ -323,19 +323,21 @@ class Authentication
*/ */
public function setForUser(App $a, array $user_record, bool $login_initial = false, bool $interactive = false, bool $login_refresh = false) public function setForUser(App $a, array $user_record, bool $login_initial = false, bool $interactive = false, bool $login_refresh = false)
{ {
$my_url = $this->baseUrl . '/profile/' . $user_record['nickname'];
$this->session->setMultiple([ $this->session->setMultiple([
'uid' => $user_record['uid'], 'uid' => $user_record['uid'],
'theme' => $user_record['theme'], 'theme' => $user_record['theme'],
'mobile-theme' => $this->pConfig->get($user_record['uid'], 'system', 'mobile_theme'), 'mobile-theme' => $this->pConfig->get($user_record['uid'], 'system', 'mobile_theme'),
'authenticated' => 1, 'authenticated' => 1,
'page_flags' => $user_record['page-flags'], 'page_flags' => $user_record['page-flags'],
'my_url' => $this->baseUrl . '/profile/' . $user_record['nickname'], 'my_url' => $my_url,
'my_address' => $user_record['nickname'] . '@' . substr($this->baseUrl, strpos($this->baseUrl, '://') + 3), 'my_address' => $user_record['nickname'] . '@' . substr($this->baseUrl, strpos($this->baseUrl, '://') + 3),
'addr' => $this->remoteAddress, 'addr' => $this->remoteAddress,
'nickname' => $user_record['nickname'], 'nickname' => $user_record['nickname'],
]); ]);
$this->session->setVisitorsContacts(); $this->session->setVisitorsContacts($my_url);
$member_since = strtotime($user_record['register_date']); $member_since = strtotime($user_record['register_date']);
$this->session->set('new_member', time() < ($member_since + (60 * 60 * 24 * 14))); $this->session->set('new_member', time() < ($member_since + (60 * 60 * 24 * 14)));

View file

@ -21,8 +21,10 @@
namespace Friendica\Worker; namespace Friendica\Worker;
use Friendica\Core\Logger;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\Database\DBStructure; use Friendica\Database\DBStructure;
use Friendica\Model\Contact;
use Friendica\Model\Photo; use Friendica\Model\Photo;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
@ -55,29 +57,97 @@ class ExpireAndRemoveUsers
// delete user records for recently removed accounts // delete user records for recently removed accounts
$users = DBA::select('user', ['uid'], ["`account_removed` AND `account_expires_on` < ? AND `uid` != ?", DateTimeFormat::utcNow(), 0]); $users = DBA::select('user', ['uid'], ["`account_removed` AND `account_expires_on` < ? AND `uid` != ?", DateTimeFormat::utcNow(), 0]);
while ($user = DBA::fetch($users)) { while ($user = DBA::fetch($users)) {
$pcid = Contact::getPublicIdByUserId($user['uid']);
Logger::info('Removing user - start', ['uid' => $user['uid'], 'pcid' => $pcid]);
// We have to delete photo entries by hand because otherwise the photo data won't be deleted // We have to delete photo entries by hand because otherwise the photo data won't be deleted
Photo::delete(['uid' => $user['uid']]); $result = Photo::delete(['uid' => $user['uid']]);
if ($result) {
Logger::debug('Deleted user photos', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting user photos', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
if (!empty($pcid)) {
$result = DBA::delete('post-tag', ['cid' => $pcid]);
if ($result) {
Logger::debug('Deleted post-tag entries', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting post-tag entries', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
$tables = ['post', 'post-user', 'post-thread', 'post-thread-user'];
if (DBStructure::existsTable('item')) {
$tables[] = 'item';
}
// Delete all entries with the public contact in post related tables
foreach ($tables as $table) {
foreach (['owner-id', 'author-id', 'causer-id'] as $field) {
$result = DBA::delete($table, [$field => $pcid]);
if ($result) {
Logger::debug('Deleted entries', ['table' => $table, 'field' => $field, 'result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting entries', ['table' => $table, 'field' => $field, 'errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
}
}
}
// Delete the contacts of this user // Delete the contacts of this user
$self = DBA::selectFirst('contact', ['nurl'], ['self' => true, 'uid' => $user['uid']]); $self = DBA::selectFirst('contact', ['nurl'], ['self' => true, 'uid' => $user['uid']]);
if (DBA::isResult($self)) { if (DBA::isResult($self)) {
DBA::delete('contact', ['nurl' => $self['nurl'], 'self' => false]); $result = DBA::delete('contact', ['nurl' => $self['nurl'], 'self' => false]);
if ($result) {
Logger::debug('Deleted the user contact for other users', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting the user contact for other users', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
} }
// Delete all contacts of this user // Delete all contacts of this user
DBA::delete('contact', ['uid' => $user['uid']]); $result = DBA::delete('contact', ['uid' => $user['uid']]);
if ($result) {
Logger::debug('Deleted user contacts', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting user contacts', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
// These tables contain the permissionset which will also be deleted when a user is deleted. // These tables contain the permissionset which will also be deleted when a user is deleted.
// It seems that sometimes the system wants to delete the records in the wrong order. // It seems that sometimes the system wants to delete the records in the wrong order.
// So when the permissionset is deleted and these tables are still filled then an error is thrown. // So when the permissionset is deleted and these tables are still filled then an error is thrown.
// So we now delete them before all other user related entries are deleted. // So we now delete them before all other user related entries are deleted.
if (DBStructure::existsTable('item')) { if (DBStructure::existsTable('item')) {
DBA::delete('item', ['uid' => $user['uid']]); $result = DBA::delete('item', ['uid' => $user['uid']]);
if ($result) {
Logger::debug('Deleted user items', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting user items', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
}
$result = DBA::delete('post-user', ['uid' => $user['uid']]);
if ($result) {
Logger::debug('Deleted post-user entries', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting post-user entries', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
} }
DBA::delete('post-user', ['uid' => $user['uid']]);
DBA::delete('profile_field', ['uid' => $user['uid']]);
DBA::delete('user', ['uid' => $user['uid']]); $result = DBA::delete('profile_field', ['uid' => $user['uid']]);
if ($result) {
Logger::debug('Deleted profile_field entries', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting profile_field entries', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
$result = DBA::delete('user', ['uid' => $user['uid']]);
if ($result) {
Logger::debug('Deleted user record', ['result' => $result, 'rows' => DBA::affectedRows()]);
} else {
Logger::warning('Error deleting user record', ['errno' => DBA::errorNo(), 'errmsg' => DBA::errorMessage()]);
}
Logger::info('Removing user - done', ['uid' => $user['uid']]);
} }
DBA::close($users); DBA::close($users);
} }

View file

@ -55,7 +55,7 @@
use Friendica\Database\DBA; use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1517); define('DB_UPDATE_VERSION', 1518);
} }
return [ return [

View file

@ -1009,6 +1009,8 @@
"site_name" => ["gserver", "site_name"], "site_name" => ["gserver", "site_name"],
"platform" => ["gserver", "platform"], "platform" => ["gserver", "platform"],
"version" => ["gserver", "version"], "version" => ["gserver", "version"],
"server-blocked" => ["gserver", "blocked"],
"server-failed" => ["gserver", "failed"],
], ],
"query" => "FROM `contact` "query" => "FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
@ -1111,6 +1113,8 @@
"site_name" => ["gserver", "site_name"], "site_name" => ["gserver", "site_name"],
"platform" => ["gserver", "platform"], "platform" => ["gserver", "platform"],
"version" => ["gserver", "version"], "version" => ["gserver", "version"],
"server-blocked" => ["gserver", "blocked"],
"server-failed" => ["gserver", "failed"],
], ],
"query" => "FROM `contact` AS `ucontact` "query" => "FROM `contact` AS `ucontact`
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0

View file

@ -65,7 +65,7 @@ trait VFSTrait
* @param string $sourceFilePath The filename of the config file * @param string $sourceFilePath The filename of the config file
* @param bool $static True, if the folder `static` instead of `config` should be used * @param bool $static True, if the folder `static` instead of `config` should be used
*/ */
protected function setConfigFile(string $sourceFilePath, bool $static = false, string $targetFileName = null) public function setConfigFile(string $sourceFilePath, bool $static = false, string $targetFileName = null)
{ {
$file = dirname(__DIR__) . DIRECTORY_SEPARATOR . $file = dirname(__DIR__) . DIRECTORY_SEPARATOR .
'..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .

View file

@ -566,4 +566,74 @@ class ConfigTest extends DatabaseTest
$config->set('test', 'it', $value); $config->set('test', 'it', $value);
self:self::assertEquals($assertion, $config->get('test', 'it')); self:self::assertEquals($assertion, $config->get('test', 'it'));
} }
public function dataEnv(): array
{
$data = [
'config' => [
'admin_email' => 'value1',
'timezone' => 'value2',
'language' => 'value3',
'sitename' => 'value',
],
'system' => [
'url' => 'value1a',
'debugging' => true,
'logfile' => 'value4',
'loglevel' => 'notice',
'proflier' => true,
],
'proxy' => [
'trusted_proxies' => 'value5',
],
];
return [
'empty' => [
'data' => $data,
'server' => [],
'assertDisabled' => [],
],
'mixed' => [
'data' => $data,
'server' => [
'FRIENDICA_ADMIN_MAIL' => 'test@friendica.local',
'FRIENDICA_DEBUGGING' => true,
],
'assertDisabled' => [
'config' => [
'admin_email' => true,
],
'system' => [
'debugging' => true,
],
],
],
];
}
/**
* Tests if environment variables can change the permission to write a config key
*
* @dataProvider dataEnv
*/
public function testIsWritable(array $data, array $server, array $assertDisabled)
{
$this->setConfigFile('static' . DIRECTORY_SEPARATOR . 'env.config.php', true);
$this->loadDirectFixture($this->configToDbArray($data), $this->getDbInstance());
$configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/', $server);
$configFileManager->setupCache($this->configCache);
$config = new DatabaseConfig($this->getDbInstance(), $this->configCache);
foreach ($data as $category => $keyvalues) {
foreach ($keyvalues as $key => $value) {
if (empty($assertDisabled[$category][$key])) {
static::assertTrue($config->isWritable($category, $key), sprintf('%s.%s is not true', $category, $key));
} else {
static::assertFalse($config->isWritable($category, $key), sprintf('%s.%s is not false', $category, $key));
}
}
}
}
} }

View file

@ -1315,3 +1315,14 @@ function update_1516()
return Update::SUCCESS; return Update::SUCCESS;
} }
function update_1518()
{
$users = DBA::select('user', ['uid']);
while ($user = DBA::fetch($users)) {
Contact::updateSelfFromUserID($user['uid']);
}
DBA::close($users);
return Update::SUCCESS;
}

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
<h1>{{$title}} - {{$page}}</h1> <h1>{{$title}} - {{$page}}</h1>
<h2>{{$label_current}}: <b>{{$storagebackend}}</b></h2> <h2>{{$label_current}}: <b>{{$storagebackend}}</b></h2>
{{$storagebackend_ro_txt nofilter}}
<h2>{{$label_config}}</h2> <h2>{{$label_config}}</h2>
@ -19,12 +20,14 @@
{{if $storage.form}} {{if $storage.form}}
<input type="submit" name="submit_save" value="{{$save}}"/> <input type="submit" name="submit_save" value="{{$save}}"/>
{{if $storage.active}} {{if $is_writable}}
{{if $storage.active}}
<input type="submit" name="submit_save_set" value="{{$save_reload}}"/> <input type="submit" name="submit_save_set" value="{{$save_reload}}"/>
{{else}} {{else}}
<input type="submit" name="submit_save_set" value="{{$save_use}}"/> <input type="submit" name="submit_save_set" value="{{$save_use}}"/>
{{/if}}
{{/if}} {{/if}}
{{else}} {{elseif $is_writable}}
<br /><input type="submit" name="submit_save_set" {{if $storage.active}}disabled="disabled"{{/if}} value="{{$use}}"/> <br /><input type="submit" name="submit_save_set" {{if $storage.active}}disabled="disabled"{{/if}} value="{{$use}}"/>
{{/if}} {{/if}}
</form> </form>

View file

@ -1,7 +1,7 @@
<div class="field select"> <div class="field select">
<label for="id_{{$field.0}}">{{$field.1}}</label> <label for="id_{{$field.0}}">{{$field.1}}</label>
<select name="{{$field.0}}" id="id_{{$field.0}}" aria-describedby="{{$field.0}}_tip"> <select name="{{$field.0}}" id="id_{{$field.0}}" aria-describedby="{{$field.0}}_tip" {{$field.5 nofilter}}>
{{foreach $field.4 as $opt=>$val}} {{foreach $field.4 as $opt=>$val}}
<option value="{{$opt}}" dir="auto"{{if $opt==$field.2}} selected="selected"{{/if}}>{{$val}}</option> <option value="{{$opt}}" dir="auto"{{if $opt==$field.2}} selected="selected"{{/if}}>{{$val}}</option>
{{/foreach}} {{/foreach}}

View file

@ -201,16 +201,19 @@
} }
.dropzone, .dropzone * { .dropzone, .dropzone * {
box-sizing: border-box; box-sizing: border-box;
overflow: auto;
} }
.dropzone:not(textarea) { .dropzone:not(textarea) {
border: 1px solid rgba(0, 0, 0, 0.3); border: 1px solid rgba(0, 0, 0, 0.3);
padding: 0px 0px 5px 0px; padding: 0px 0px 5px 0px;
color: #999;
} }
.dropzone.dz-clickable { .dropzone.dz-clickable {
cursor: pointer; cursor: pointer;
border-radius: 4px; border-radius: 4px;
background-color: $background_color; background-color: $background_color;
margin-bottom: 10px;
} }
.dropzone.dz-clickable * { .dropzone.dz-clickable * {
cursor: default; cursor: default;

File diff suppressed because one or more lines are too long

View file

@ -5,6 +5,9 @@
<div class="well well-lg"> <div class="well well-lg">
{{$label_current}}: <b>{{$storagebackend}}</b> {{$label_current}}: <b>{{$storagebackend}}</b>
{{if $storagebackend_ro_txt}}
<br><i>{{$storagebackend_ro_txt nofilter}}</i>
{{/if}}
</div> </div>
<h2>{{$label_config}}</h2> <h2>{{$label_config}}</h2>
@ -33,12 +36,14 @@
<div class="panel-footer"> <div class="panel-footer">
{{if $storage.form}} {{if $storage.form}}
<input type="submit" name="submit_save" class="btn btn-primary" value="{{$save}}"/> <input type="submit" name="submit_save" class="btn btn-primary" value="{{$save}}"/>
{{if $storage.active}} {{if $is_writable}}
{{if $storage.active}}
<input type="submit" name="submit_save_set" class="btn btn-primary" value="{{$save_reload}}"/> <input type="submit" name="submit_save_set" class="btn btn-primary" value="{{$save_reload}}"/>
{{else}} {{else}}
<input type="submit" name="submit_save_set" class="btn btn-primary" value="{{$save_use}}"/> <input type="submit" name="submit_save_set" class="btn btn-primary" value="{{$save_use}}"/>
{{/if}}
{{/if}} {{/if}}
{{else}} {{elseif $is_writable}}
<input type="submit" name="submit_save_set" class="btn btn-primary" {{if $storage.active}}disabled="disabled"{{/if}} value="{{$use}}"/> <input type="submit" name="submit_save_set" class="btn btn-primary" {{if $storage.active}}disabled="disabled"{{/if}} value="{{$use}}"/>
{{/if}} {{/if}}
</div> </div>

View file

@ -38,7 +38,7 @@
</button> </button>
</span> </span>
</p> </p>
<div id="dropzone-{{$id}}" class="dropzone" style="overflow:scroll"> <div id="dropzone-{{$id}}" class="dropzone">
<p> <p>
<textarea id="comment-edit-text-{{$id}}" class="dropzone comment-edit-text-empty form-control text-autosize" name="body" placeholder="{{$comment}}" rows="3" data-default="{{$default}}" dir="auto">{{$default}}</textarea> <textarea id="comment-edit-text-{{$id}}" class="dropzone comment-edit-text-empty form-control text-autosize" name="body" placeholder="{{$comment}}" rows="3" data-default="{{$default}}" dir="auto">{{$default}}</textarea>
</p> </p>

View file

@ -1,7 +1,7 @@
<div class="form-group field select"> <div class="form-group field select">
<label for="id_{{$field.0}}">{{$field.1}}</label> <label for="id_{{$field.0}}">{{$field.1}}</label>
<select name="{{$field.0}}" id="id_{{$field.0}}" class="form-control" aria-describedby="{{$field.0}}_tip"> <select name="{{$field.0}}" id="id_{{$field.0}}" class="form-control" aria-describedby="{{$field.0}}_tip" {{$field.5 nofilter}}>
{{foreach $field.4 as $opt=>$val}} {{foreach $field.4 as $opt=>$val}}
<option value="{{$opt}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option> <option value="{{$opt}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>
{{/foreach}} {{/foreach}}