From 1a8110234f1ff6ad5ea8c5f829e0639ef8b2d2c6 Mon Sep 17 00:00:00 2001
From: Hypolite Petovan <hypolite@mrpetovan.com>
Date: Wed, 19 Feb 2020 22:13:28 -0500
Subject: [PATCH 1/3] Move /settings/display module to src/

---
 mod/settings.php                | 275 +-------------------------------
 src/Core/Theme.php              |  22 +++
 src/Module/Settings/Display.php | 207 ++++++++++++++++++++++++
 static/routes.config.php        |   1 +
 4 files changed, 233 insertions(+), 272 deletions(-)
 create mode 100644 src/Module/Settings/Display.php

diff --git a/mod/settings.php b/mod/settings.php
index 1418a5e6c5..fcbd8208d4 100644
--- a/mod/settings.php
+++ b/mod/settings.php
@@ -27,7 +27,6 @@ use Friendica\Core\ACL;
 use Friendica\Core\Hook;
 use Friendica\Core\Logger;
 use Friendica\Core\Renderer;
-use Friendica\Core\Session;
 use Friendica\Core\Theme;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
@@ -37,131 +36,21 @@ use Friendica\Model\GContact;
 use Friendica\Model\Group;
 use Friendica\Model\Notify\Type;
 use Friendica\Model\User;
+use Friendica\Module\BaseSettings;
 use Friendica\Module\Security\Login;
 use Friendica\Protocol\Email;
 use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 use Friendica\Worker\Delivery;
 
-function get_theme_config_file($theme)
-{
-	$theme = Strings::sanitizeFilePathItem($theme);
-
-	$a = DI::app();
-	$base_theme = $a->theme_info['extends'] ?? '';
-
-	if (file_exists("view/theme/$theme/config.php")) {
-		return "view/theme/$theme/config.php";
-	}
-	if ($base_theme && file_exists("view/theme/$base_theme/config.php")) {
-		return "view/theme/$base_theme/config.php";
-	}
-	return null;
-}
-
 function settings_init(App $a)
 {
 	if (!local_user()) {
-		notice(DI::l10n()->t('Permission denied.') . EOL);
+		notice(DI::l10n()->t('Permission denied.'));
 		return;
 	}
 
-	// These lines provide the javascript needed by the acl selector
-
-	$tpl = Renderer::getMarkupTemplate('settings/head.tpl');
-	DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
-		'$ispublic' => DI::l10n()->t('everybody')
-	]);
-
-	$tabs = [
-		[
-			'label'	=> DI::l10n()->t('Account'),
-			'url' 	=> 'settings',
-			'selected'	=>  (($a->argc == 1) && ($a->argv[0] === 'settings')?'active':''),
-			'accesskey' => 'o',
-		],
-	];
-
-	$tabs[] = [
-		'label' => DI::l10n()->t('Two-factor authentication'),
-		'url' => 'settings/2fa',
-		'selected' => (($a->argc > 1) && ($a->argv[1] === '2fa') ? 'active' : ''),
-		'accesskey' => 'o',
-	];
-
-	$tabs[] =	[
-		'label'	=> DI::l10n()->t('Profile'),
-		'url' 	=> 'settings/profile',
-		'selected'	=> (($a->argc > 1) && ($a->argv[1] === 'profile')?'active':''),
-		'accesskey' => 'p',
-	];
-
-	if (Feature::get()) {
-		$tabs[] =	[
-					'label'	=> DI::l10n()->t('Additional features'),
-					'url' 	=> 'settings/features',
-					'selected'	=> (($a->argc > 1) && ($a->argv[1] === 'features') ? 'active' : ''),
-					'accesskey' => 't',
-				];
-	}
-
-	$tabs[] =	[
-		'label'	=> DI::l10n()->t('Display'),
-		'url' 	=> 'settings/display',
-		'selected'	=> (($a->argc > 1) && ($a->argv[1] === 'display')?'active':''),
-		'accesskey' => 'i',
-	];
-
-	$tabs[] =	[
-		'label'	=> DI::l10n()->t('Social Networks'),
-		'url' 	=> 'settings/connectors',
-		'selected'	=> (($a->argc > 1) && ($a->argv[1] === 'connectors')?'active':''),
-		'accesskey' => 'w',
-	];
-
-	$tabs[] =	[
-		'label'	=> DI::l10n()->t('Addons'),
-		'url' 	=> 'settings/addon',
-		'selected'	=> (($a->argc > 1) && ($a->argv[1] === 'addon')?'active':''),
-		'accesskey' => 'l',
-	];
-
-	$tabs[] =	[
-		'label'	=> DI::l10n()->t('Manage Accounts'),
-		'url' 	=> 'settings/delegation',
-		'selected'	=> (($a->argc > 1) && ($a->argv[1] === 'delegation')?'active':''),
-		'accesskey' => 'd',
-	];
-
-	$tabs[] =	[
-		'label' => DI::l10n()->t('Connected apps'),
-		'url' => 'settings/oauth',
-		'selected' => (($a->argc > 1) && ($a->argv[1] === 'oauth')?'active':''),
-		'accesskey' => 'b',
-	];
-
-	$tabs[] =	[
-		'label' => DI::l10n()->t('Export personal data'),
-		'url' => 'settings/userexport',
-		'selected' => (($a->argc > 1) && ($a->argv[1] === 'userexport')?'active':''),
-		'accesskey' => 'e',
-	];
-
-	$tabs[] =	[
-		'label' => DI::l10n()->t('Remove account'),
-		'url' => 'removeme',
-		'selected' => (($a->argc == 1) && ($a->argv[0] === 'removeme')?'active':''),
-		'accesskey' => 'r',
-	];
-
-
-	$tabtpl = Renderer::getMarkupTemplate("generic_links_widget.tpl");
-	DI::page()['aside'] = Renderer::replaceMacros($tabtpl, [
-		'$title' => DI::l10n()->t('Settings'),
-		'$class' => 'settings-widget',
-		'$items' => $tabs,
-	]);
-
+	BaseSettings::content();
 }
 
 function settings_post(App $a)
@@ -335,69 +224,6 @@ function settings_post(App $a)
 		return;
 	}
 
-	if (($a->argc > 1) && ($a->argv[1] === 'display')) {
-		BaseModule::checkFormSecurityTokenRedirectOnError('/settings/display', 'settings_display');
-
-		$theme              = !empty($_POST['theme'])              ? Strings::escapeTags(trim($_POST['theme']))        : $a->user['theme'];
-		$mobile_theme       = !empty($_POST['mobile_theme'])       ? Strings::escapeTags(trim($_POST['mobile_theme'])) : '';
-		$nosmile            = !empty($_POST['nosmile'])            ? intval($_POST['nosmile'])            : 0;
-		$first_day_of_week  = !empty($_POST['first_day_of_week'])  ? intval($_POST['first_day_of_week'])  : 0;
-		$infinite_scroll    = !empty($_POST['infinite_scroll'])    ? intval($_POST['infinite_scroll'])    : 0;
-		$no_auto_update     = !empty($_POST['no_auto_update'])     ? intval($_POST['no_auto_update'])     : 0;
-		$no_smart_threading = !empty($_POST['no_smart_threading']) ? intval($_POST['no_smart_threading']) : 0;
-		$browser_update     = !empty($_POST['browser_update'])     ? intval($_POST['browser_update'])     : 0;
-		if ($browser_update != -1) {
-			$browser_update = $browser_update * 1000;
-			if ($browser_update < 10000) {
-				$browser_update = 10000;
-			}
-		}
-
-		$itemspage_network = !empty($_POST['itemspage_network']) ?
-			intval($_POST['itemspage_network']) :
-			DI::config()->get('system', 'itemspage_network');
-		if ($itemspage_network > 100) {
-			$itemspage_network = 100;
-		}
-		$itemspage_mobile_network = !empty($_POST['itemspage_mobile_network']) ?
-			intval($_POST['itemspage_mobile_network']) :
-			DI::config()->get('system', 'itemspage_network_mobile');
-		if ($itemspage_mobile_network > 100) {
-			$itemspage_mobile_network = 100;
-		}
-
-		if ($mobile_theme !== '') {
-			DI::pConfig()->set(local_user(), 'system', 'mobile_theme', $mobile_theme);
-		}
-
-		DI::pConfig()->set(local_user(), 'system', 'update_interval'         , $browser_update);
-		DI::pConfig()->set(local_user(), 'system', 'itemspage_network'       , $itemspage_network);
-		DI::pConfig()->set(local_user(), 'system', 'itemspage_mobile_network', $itemspage_mobile_network);
-		DI::pConfig()->set(local_user(), 'system', 'no_smilies'              , $nosmile);
-		DI::pConfig()->set(local_user(), 'system', 'first_day_of_week'       , $first_day_of_week);
-		DI::pConfig()->set(local_user(), 'system', 'infinite_scroll'         , $infinite_scroll);
-		DI::pConfig()->set(local_user(), 'system', 'no_auto_update'          , $no_auto_update);
-		DI::pConfig()->set(local_user(), 'system', 'no_smart_threading'      , $no_smart_threading);
-
-		if (in_array($theme, Theme::getAllowedList())) {
-			if ($theme == $a->user['theme']) {
-				// call theme_post only if theme has not been changed
-				if (($themeconfigfile = get_theme_config_file($theme)) !== null) {
-					require_once $themeconfigfile;
-					theme_post($a);
-				}
-			} else {
-				DBA::update('user', ['theme' => $theme], ['uid' => local_user()]);
-			}
-		} else {
-			notice(DI::l10n()->t('The theme you chose isn\'t available.'));
-		}
-
-		Hook::callAll('display_settings_post', $_POST);
-		DI::baseUrl()->redirect('settings/display');
-		return; // NOTREACHED
-	}
-
 	BaseModule::checkFormSecurityTokenRedirectOnError('/settings', 'settings');
 
 	// Import Contacts from CSV file
@@ -885,101 +711,6 @@ function settings_content(App $a)
 		return $o;
 	}
 
-	/*
-	 * DISPLAY SETTINGS
-	 */
-	if (($a->argc > 1) && ($a->argv[1] === 'display')) {
-		$default_theme = DI::config()->get('system', 'theme');
-		if (!$default_theme) {
-			$default_theme = 'default';
-		}
-		$default_mobile_theme = DI::config()->get('system', 'mobile-theme');
-		if (!$default_mobile_theme) {
-			$default_mobile_theme = 'none';
-		}
-
-		$allowed_themes = Theme::getAllowedList();
-
-		$themes = [];
-		$mobile_themes = ["---" => DI::l10n()->t('No special theme for mobile devices')];
-		foreach ($allowed_themes as $theme) {
-			$is_experimental = file_exists('view/theme/' . $theme . '/experimental');
-			$is_unsupported  = file_exists('view/theme/' . $theme . '/unsupported');
-			$is_mobile       = file_exists('view/theme/' . $theme . '/mobile');
-			if (!$is_experimental || ($is_experimental && (DI::config()->get('experimentals', 'exp_themes')==1 || is_null(DI::config()->get('experimentals', 'exp_themes'))))) {
-				$theme_name = ucfirst($theme);
-				if ($is_unsupported) {
-					$theme_name = DI::l10n()->t('%s - (Unsupported)', $theme_name);
-				} elseif ($is_experimental) {
-					$theme_name = DI::l10n()->t('%s - (Experimental)', $theme_name);
-				}
-
-				if ($is_mobile) {
-					$mobile_themes[$theme] = $theme_name;
-				} else {
-					$themes[$theme] = $theme_name;
-				}
-			}
-		}
-
-		$theme_selected        = $a->user['theme'] ?: $default_theme;
-		$mobile_theme_selected = Session::get('mobile-theme', $default_mobile_theme);
-
-		$browser_update = intval(DI::pConfig()->get(local_user(), 'system', 'update_interval'));
-		if (intval($browser_update) != -1) {
-			$browser_update = (($browser_update == 0) ? 40 : $browser_update / 1000); // default if not set: 40 seconds
-		}
-
-		$itemspage_network = intval(DI::pConfig()->get(local_user(), 'system', 'itemspage_network'));
-		$itemspage_network = (($itemspage_network > 0 && $itemspage_network < 101) ? $itemspage_network : DI::config()->get('system', 'itemspage_network'));
-		$itemspage_mobile_network = intval(DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network'));
-		$itemspage_mobile_network = (($itemspage_mobile_network > 0 && $itemspage_mobile_network < 101) ? $itemspage_mobile_network : DI::config()->get('system', 'itemspage_network_mobile'));
-
-		$nosmile = DI::pConfig()->get(local_user(), 'system', 'no_smilies', 0);
-		$first_day_of_week = DI::pConfig()->get(local_user(), 'system', 'first_day_of_week', 0);
-		$weekdays = [0 => DI::l10n()->t("Sunday"), 1 => DI::l10n()->t("Monday")];
-
-		$infinite_scroll = DI::pConfig()->get(local_user(), 'system', 'infinite_scroll', 0);
-		$no_auto_update = DI::pConfig()->get(local_user(), 'system', 'no_auto_update', 0);
-		$no_smart_threading = DI::pConfig()->get(local_user(), 'system', 'no_smart_threading', 0);
-
-		$theme_config = "";
-		if (($themeconfigfile = get_theme_config_file($theme_selected)) !== null) {
-			require_once $themeconfigfile;
-			$theme_config = theme_content($a);
-		}
-
-		$tpl = Renderer::getMarkupTemplate('settings/display.tpl');
-		$o = Renderer::replaceMacros($tpl, [
-			'$ptitle' 	=> DI::l10n()->t('Display Settings'),
-			'$form_security_token' => BaseModule::getFormSecurityToken("settings_display"),
-			'$submit' 	=> DI::l10n()->t('Save Settings'),
-			'$baseurl' => DI::baseUrl()->get(true),
-			'$uid' => local_user(),
-
-			'$theme'	=> ['theme', DI::l10n()->t('Display Theme:'), $theme_selected, '', $themes, true],
-			'$mobile_theme'	=> ['mobile_theme', DI::l10n()->t('Mobile Theme:'), $mobile_theme_selected, '', $mobile_themes, false],
-			'$ajaxint'   => ['browser_update',  DI::l10n()->t("Update browser every xx seconds"), $browser_update, DI::l10n()->t('Minimum of 10 seconds. Enter -1 to disable it.')],
-			'$itemspage_network'   => ['itemspage_network',  DI::l10n()->t("Number of items to display per page:"), $itemspage_network, DI::l10n()->t('Maximum of 100 items')],
-			'$itemspage_mobile_network'   => ['itemspage_mobile_network',  DI::l10n()->t("Number of items to display per page when viewed from mobile device:"), $itemspage_mobile_network, DI::l10n()->t('Maximum of 100 items')],
-			'$nosmile'	=> ['nosmile', DI::l10n()->t("Don't show emoticons"), $nosmile, DI::l10n()->t('Normally emoticons are replaced with matching symbols. This setting disables this behaviour.')],
-			'$calendar_title' => DI::l10n()->t('Calendar'),
-			'$first_day_of_week'	=> ['first_day_of_week', DI::l10n()->t('Beginning of week:'), $first_day_of_week, '', $weekdays, false],
-			'$infinite_scroll'	=> ['infinite_scroll', DI::l10n()->t("Infinite scroll"), $infinite_scroll, DI::l10n()->t('Automatic fetch new items when reaching the page end.')],
-			'$no_auto_update'	=> ['no_auto_update', DI::l10n()->t("Automatic updates only at the top of the network page"), $no_auto_update, DI::l10n()->t('When disabled, the network page is updated all the time, which could be confusing while reading.')],
-			'$no_smart_threading' => ['no_smart_threading', DI::l10n()->t('Disable Smart Threading'), $no_smart_threading, DI::l10n()->t('Disable the automatic suppression of extraneous thread indentation.')],
-
-			'$d_tset' => DI::l10n()->t('General Theme Settings'),
-			'$d_ctset' => DI::l10n()->t('Custom Theme Settings'),
-			'$d_cset' => DI::l10n()->t('Content Settings'),
-			'stitle' => DI::l10n()->t('Theme settings'),
-			'$theme_config' => $theme_config,
-		]);
-
-		return $o;
-	}
-
-
 	/*
 	 * ACCOUNT SETTINGS
 	 */
diff --git a/src/Core/Theme.php b/src/Core/Theme.php
index f7dce3c298..c17c67c4d9 100644
--- a/src/Core/Theme.php
+++ b/src/Core/Theme.php
@@ -258,6 +258,28 @@ class Theme
 		return 'view/theme/' . $theme . '/style.pcss' . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
 	}
 
+	/**
+	 * Returns the path of the provided theme
+	 *
+	 * @param $theme
+	 * @return string|null
+	 */
+	public static function getConfigFile($theme)
+	{
+		$theme = Strings::sanitizeFilePathItem($theme);
+
+		$a = DI::app();
+		$base_theme = $a->theme_info['extends'] ?? '';
+
+		if (file_exists("view/theme/$theme/config.php")) {
+			return "view/theme/$theme/config.php";
+		}
+		if ($base_theme && file_exists("view/theme/$base_theme/config.php")) {
+			return "view/theme/$base_theme/config.php";
+		}
+		return null;
+	}
+	
 	/**
 	 * Returns the background color of the provided theme if available.
 	 *
diff --git a/src/Module/Settings/Display.php b/src/Module/Settings/Display.php
new file mode 100644
index 0000000000..34817b5fcf
--- /dev/null
+++ b/src/Module/Settings/Display.php
@@ -0,0 +1,207 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Settings;
+
+use Friendica\Core\Hook;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session;
+use Friendica\Core\Theme;
+use Friendica\Database\DBA;
+use Friendica\DI;
+use Friendica\Module\BaseSettings;
+use Friendica\Network\HTTPException;
+use Friendica\Util\Strings;
+
+/**
+ * Module to update user settings
+ */
+class Display extends BaseSettings
+{
+	public static function post(array $parameters = [])
+	{
+		if (!local_user() || !empty(DI::app()->user['uid']) && DI::app()->user['uid'] != local_user()) {
+			throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
+		}
+
+		self::checkFormSecurityTokenRedirectOnError('/settings/display', 'settings_display');
+
+		$theme              = !empty($_POST['theme'])              ? Strings::escapeTags(trim($_POST['theme']))        : DI::app()->user['theme'];
+		$mobile_theme       = !empty($_POST['mobile_theme'])       ? Strings::escapeTags(trim($_POST['mobile_theme'])) : '';
+		$nosmile            = !empty($_POST['nosmile'])            ? intval($_POST['nosmile'])            : 0;
+		$first_day_of_week  = !empty($_POST['first_day_of_week'])  ? intval($_POST['first_day_of_week'])  : 0;
+		$infinite_scroll    = !empty($_POST['infinite_scroll'])    ? intval($_POST['infinite_scroll'])    : 0;
+		$no_auto_update     = !empty($_POST['no_auto_update'])     ? intval($_POST['no_auto_update'])     : 0;
+		$no_smart_threading = !empty($_POST['no_smart_threading']) ? intval($_POST['no_smart_threading']) : 0;
+		$browser_update     = !empty($_POST['browser_update'])     ? intval($_POST['browser_update'])     : 0;
+		if ($browser_update != -1) {
+			$browser_update = $browser_update * 1000;
+			if ($browser_update < 10000) {
+				$browser_update = 10000;
+			}
+		}
+
+		$itemspage_network = !empty($_POST['itemspage_network']) ?
+			intval($_POST['itemspage_network']) :
+			DI::config()->get('system', 'itemspage_network');
+		if ($itemspage_network > 100) {
+			$itemspage_network = 100;
+		}
+		$itemspage_mobile_network = !empty($_POST['itemspage_mobile_network']) ?
+			intval($_POST['itemspage_mobile_network']) :
+			DI::config()->get('system', 'itemspage_network_mobile');
+		if ($itemspage_mobile_network > 100) {
+			$itemspage_mobile_network = 100;
+		}
+
+		if ($mobile_theme !== '') {
+			DI::pConfig()->set(local_user(), 'system', 'mobile_theme', $mobile_theme);
+		}
+
+		DI::pConfig()->set(local_user(), 'system', 'itemspage_network'       , $itemspage_network);
+		DI::pConfig()->set(local_user(), 'system', 'itemspage_mobile_network', $itemspage_mobile_network);
+		DI::pConfig()->set(local_user(), 'system', 'update_interval'         , $browser_update);
+		DI::pConfig()->set(local_user(), 'system', 'no_auto_update'          , $no_auto_update);
+		DI::pConfig()->set(local_user(), 'system', 'no_smilies'              , $nosmile);
+		DI::pConfig()->set(local_user(), 'system', 'infinite_scroll'         , $infinite_scroll);
+		DI::pConfig()->set(local_user(), 'system', 'no_smart_threading'      , $no_smart_threading);
+		DI::pConfig()->set(local_user(), 'system', 'first_day_of_week'       , $first_day_of_week);
+
+		if (in_array($theme, Theme::getAllowedList())) {
+			if ($theme == DI::app()->user['theme']) {
+				// call theme_post only if theme has not been changed
+				if (($themeconfigfile = Theme::getConfigFile($theme)) !== null) {
+					require_once $themeconfigfile;
+					theme_post(DI::app());
+				}
+			} else {
+				DBA::update('user', ['theme' => $theme], ['uid' => local_user()]);
+			}
+		} else {
+			notice(DI::l10n()->t('The theme you chose isn\'t available.'));
+		}
+
+		Hook::callAll('display_settings_post', $_POST);
+
+		DI::baseUrl()->redirect('settings/display');
+	}
+
+	public static function content(array $parameters = [])
+	{
+		parent::content($parameters);
+
+		if (!local_user()) {
+			throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
+		}
+
+		$default_theme = DI::config()->get('system', 'theme');
+		if (!$default_theme) {
+			$default_theme = 'default';
+		}
+
+		$default_mobile_theme = DI::config()->get('system', 'mobile-theme');
+		if (!$default_mobile_theme) {
+			$default_mobile_theme = 'none';
+		}
+
+		$allowed_themes = Theme::getAllowedList();
+
+		$themes = [];
+		$mobile_themes = ["---" => DI::l10n()->t('No special theme for mobile devices')];
+		foreach ($allowed_themes as $theme) {
+			$is_experimental = file_exists('view/theme/' . $theme . '/experimental');
+			$is_unsupported  = file_exists('view/theme/' . $theme . '/unsupported');
+			$is_mobile       = file_exists('view/theme/' . $theme . '/mobile');
+			if (!$is_experimental || (DI::config()->get('experimentals', 'exp_themes') || is_null(DI::config()->get('experimentals', 'exp_themes')))) {
+				$theme_name = ucfirst($theme);
+				if ($is_unsupported) {
+					$theme_name = DI::l10n()->t('%s - (Unsupported)', $theme_name);
+				} elseif ($is_experimental) {
+					$theme_name = DI::l10n()->t('%s - (Experimental)', $theme_name);
+				}
+
+				if ($is_mobile) {
+					$mobile_themes[$theme] = $theme_name;
+				} else {
+					$themes[$theme] = $theme_name;
+				}
+			}
+		}
+
+		$theme_selected        = DI::app()->user['theme'] ?: $default_theme;
+		$mobile_theme_selected = Session::get('mobile-theme', $default_mobile_theme);
+
+		$itemspage_network = intval(DI::pConfig()->get(local_user(), 'system', 'itemspage_network'));
+		$itemspage_network = (($itemspage_network > 0 && $itemspage_network < 101) ? $itemspage_network : DI::config()->get('system', 'itemspage_network'));
+		$itemspage_mobile_network = intval(DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network'));
+		$itemspage_mobile_network = (($itemspage_mobile_network > 0 && $itemspage_mobile_network < 101) ? $itemspage_mobile_network : DI::config()->get('system', 'itemspage_network_mobile'));
+
+		$browser_update = intval(DI::pConfig()->get(local_user(), 'system', 'update_interval'));
+		if (intval($browser_update) != -1) {
+			$browser_update = (($browser_update == 0) ? 40 : $browser_update / 1000); // default if not set: 40 seconds
+		}
+
+		$no_auto_update     = DI::pConfig()->get(local_user(), 'system', 'no_auto_update', 0);
+		$nosmile            = DI::pConfig()->get(local_user(), 'system', 'no_smilies', 0);
+		$infinite_scroll    = DI::pConfig()->get(local_user(), 'system', 'infinite_scroll', 0);
+		$no_smart_threading = DI::pConfig()->get(local_user(), 'system', 'no_smart_threading', 0);
+
+		$first_day_of_week = DI::pConfig()->get(local_user(), 'system', 'first_day_of_week', 0);
+		$weekdays = [0 => DI::l10n()->t("Sunday"), 1 => DI::l10n()->t("Monday")];
+
+		$theme_config = '';
+		if ($themeconfigfile = Theme::getConfigFile($theme_selected)) {
+			require_once $themeconfigfile;
+			$theme_config = theme_content(DI::app());
+		}
+
+		$tpl = Renderer::getMarkupTemplate('settings/display.tpl');
+		$o = Renderer::replaceMacros($tpl, [
+			'$ptitle'         => DI::l10n()->t('Display Settings'),
+			'$submit'         => DI::l10n()->t('Save Settings'),
+			'$d_tset'         => DI::l10n()->t('General Theme Settings'),
+			'$d_ctset'        => DI::l10n()->t('Custom Theme Settings'),
+			'$d_cset'         => DI::l10n()->t('Content Settings'),
+			'$stitle'         => DI::l10n()->t('Theme settings'),
+			'$calendar_title' => DI::l10n()->t('Calendar'),
+
+			'$form_security_token' => self::getFormSecurityToken('settings_display'),
+			'$baseurl' => DI::baseUrl()->get(true),
+			'$uid'     => local_user(),
+
+			'$theme'	    => ['theme', DI::l10n()->t('Display Theme:'), $theme_selected, '', $themes, true],
+			'$mobile_theme'	=> ['mobile_theme', DI::l10n()->t('Mobile Theme:'), $mobile_theme_selected, '', $mobile_themes, false],
+			'$theme_config' => $theme_config,
+
+			'$itemspage_network'        => ['itemspage_network'       , DI::l10n()->t('Number of items to display per page:'), $itemspage_network, DI::l10n()->t('Maximum of 100 items')],
+			'$itemspage_mobile_network' => ['itemspage_mobile_network', DI::l10n()->t('Number of items to display per page when viewed from mobile device:'), $itemspage_mobile_network, DI::l10n()->t('Maximum of 100 items')],
+			'$ajaxint'                  => ['browser_update'          , DI::l10n()->t('Update browser every xx seconds'), $browser_update, DI::l10n()->t('Minimum of 10 seconds. Enter -1 to disable it.')],
+			'$no_auto_update'           => ['no_auto_update'          , DI::l10n()->t('Automatic updates only at the top of the network page'), $no_auto_update, DI::l10n()->t('When disabled, the network page is updated all the time, which could be confusing while reading.')],
+			'$nosmile'	                => ['nosmile'                 , DI::l10n()->t('Don\'t show emoticons'), $nosmile, DI::l10n()->t('Normally emoticons are replaced with matching symbols. This setting disables this behaviour.')],
+			'$infinite_scroll'          => ['infinite_scroll'         , DI::l10n()->t('Infinite scroll'), $infinite_scroll, DI::l10n()->t('Automatic fetch new items when reaching the page end.')],
+			'$no_smart_threading'       => ['no_smart_threading'      , DI::l10n()->t('Disable Smart Threading'), $no_smart_threading, DI::l10n()->t('Disable the automatic suppression of extraneous thread indentation.')],
+
+			'$first_day_of_week' => ['first_day_of_week', DI::l10n()->t('Beginning of week:'), $first_day_of_week, '', $weekdays, false],
+		]);
+
+		return $o;
+	}
+}
diff --git a/static/routes.config.php b/static/routes.config.php
index 6e23e238e6..b6ce9bf629 100644
--- a/static/routes.config.php
+++ b/static/routes.config.php
@@ -273,6 +273,7 @@ return [
 			'/verify'       => [Module\Settings\TwoFactor\Verify::class,      [R::GET, R::POST]],
 		],
 		'/delegation[/{action}/{user_id}]' => [Module\Settings\Delegation::class,       [R::GET, R::POST]],
+		'/display'                 => [Module\Settings\Display::class,             [R::GET, R::POST]],
 		'/profile' => [
 			'[/]'                  => [Module\Settings\Profile\Index::class,       [R::GET, R::POST]],
 			'/photo[/new]'         => [Module\Settings\Profile\Photo\Index::class, [R::GET, R::POST]],

From 96ba2ac00d8120cd9554b8d236ad8f3d6bf4a62a Mon Sep 17 00:00:00 2001
From: Hypolite Petovan <hypolite@mrpetovan.com>
Date: Wed, 19 Feb 2020 22:19:20 -0500
Subject: [PATCH 2/3] Add new hide dislike user setting

---
 src/Module/Settings/Display.php                | 4 ++++
 view/templates/settings/display.tpl            | 3 ++-
 view/theme/frio/templates/settings/display.tpl | 1 +
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/Module/Settings/Display.php b/src/Module/Settings/Display.php
index 34817b5fcf..6444537561 100644
--- a/src/Module/Settings/Display.php
+++ b/src/Module/Settings/Display.php
@@ -51,6 +51,7 @@ class Display extends BaseSettings
 		$infinite_scroll    = !empty($_POST['infinite_scroll'])    ? intval($_POST['infinite_scroll'])    : 0;
 		$no_auto_update     = !empty($_POST['no_auto_update'])     ? intval($_POST['no_auto_update'])     : 0;
 		$no_smart_threading = !empty($_POST['no_smart_threading']) ? intval($_POST['no_smart_threading']) : 0;
+		$hide_dislike       = !empty($_POST['hide_dislike'])       ? intval($_POST['hide_dislike'])       : 0;
 		$browser_update     = !empty($_POST['browser_update'])     ? intval($_POST['browser_update'])     : 0;
 		if ($browser_update != -1) {
 			$browser_update = $browser_update * 1000;
@@ -83,6 +84,7 @@ class Display extends BaseSettings
 		DI::pConfig()->set(local_user(), 'system', 'no_smilies'              , $nosmile);
 		DI::pConfig()->set(local_user(), 'system', 'infinite_scroll'         , $infinite_scroll);
 		DI::pConfig()->set(local_user(), 'system', 'no_smart_threading'      , $no_smart_threading);
+		DI::pConfig()->set(local_user(), 'system', 'hide_dislike'            , $hide_dislike);
 		DI::pConfig()->set(local_user(), 'system', 'first_day_of_week'       , $first_day_of_week);
 
 		if (in_array($theme, Theme::getAllowedList())) {
@@ -163,6 +165,7 @@ class Display extends BaseSettings
 		$nosmile            = DI::pConfig()->get(local_user(), 'system', 'no_smilies', 0);
 		$infinite_scroll    = DI::pConfig()->get(local_user(), 'system', 'infinite_scroll', 0);
 		$no_smart_threading = DI::pConfig()->get(local_user(), 'system', 'no_smart_threading', 0);
+		$hide_dislike       = DI::pConfig()->get(local_user(), 'system', 'hide_dislike', 0);
 
 		$first_day_of_week = DI::pConfig()->get(local_user(), 'system', 'first_day_of_week', 0);
 		$weekdays = [0 => DI::l10n()->t("Sunday"), 1 => DI::l10n()->t("Monday")];
@@ -198,6 +201,7 @@ class Display extends BaseSettings
 			'$nosmile'	                => ['nosmile'                 , DI::l10n()->t('Don\'t show emoticons'), $nosmile, DI::l10n()->t('Normally emoticons are replaced with matching symbols. This setting disables this behaviour.')],
 			'$infinite_scroll'          => ['infinite_scroll'         , DI::l10n()->t('Infinite scroll'), $infinite_scroll, DI::l10n()->t('Automatic fetch new items when reaching the page end.')],
 			'$no_smart_threading'       => ['no_smart_threading'      , DI::l10n()->t('Disable Smart Threading'), $no_smart_threading, DI::l10n()->t('Disable the automatic suppression of extraneous thread indentation.')],
+			'$hide_dislike'             => ['hide_dislike'            , DI::l10n()->t('Hide the Dislike feature'), $hide_dislike, DI::l10n()->t('Hides the Dislike button and dislike reactions on posts and comments.')],
 
 			'$first_day_of_week' => ['first_day_of_week', DI::l10n()->t('Beginning of week:'), $first_day_of_week, '', $weekdays, false],
 		]);
diff --git a/view/templates/settings/display.tpl b/view/templates/settings/display.tpl
index 317bc61046..a9f82c16b1 100644
--- a/view/templates/settings/display.tpl
+++ b/view/templates/settings/display.tpl
@@ -18,10 +18,11 @@
 {{include file="field_checkbox.tpl" field=$nosmile}}
 {{include file="field_checkbox.tpl" field=$infinite_scroll}}
 {{include file="field_checkbox.tpl" field=$no_smart_threading}}
+{{include file="field_checkbox.tpl" field=$hide_dislike}}
+
 <h2>{{$calendar_title}}</h2>
 {{include file="field_select.tpl" field=$first_day_of_week}}
 
-
 <div class="settings-submit-wrapper" >
 <input type="submit" name="submit" class="settings-submit" value="{{$submit}}" />
 </div>
diff --git a/view/theme/frio/templates/settings/display.tpl b/view/theme/frio/templates/settings/display.tpl
index f4670b244f..f67bb3f407 100644
--- a/view/theme/frio/templates/settings/display.tpl
+++ b/view/theme/frio/templates/settings/display.tpl
@@ -73,6 +73,7 @@
 						{{include file="field_checkbox.tpl" field=$nosmile}}
 						{{include file="field_checkbox.tpl" field=$infinite_scroll}}
 						{{include file="field_checkbox.tpl" field=$no_smart_threading}}
+						{{include file="field_checkbox.tpl" field=$hide_dislike}}
 
 						<div class="form-group pull-right settings-submit-wrapper" >
 							<button type="submit" name="submit" class="btn btn-primary" value="{{$submit}}">{{$submit}}</button>

From 637e38e535c0075e0db2cc2d5cf08d938b0e858f Mon Sep 17 00:00:00 2001
From: Hypolite Petovan <hypolite@mrpetovan.com>
Date: Wed, 19 Feb 2020 22:20:26 -0500
Subject: [PATCH 3/3] User hide_dislike user setting to hide dislike button and
 conversation responses

---
 include/conversation.php                     |  8 ++++++++
 mod/photos.php                               | 18 ++++++++++++------
 src/Object/Post.php                          |  4 ++++
 view/templates/like_noshare.tpl              |  4 ++--
 view/theme/frio/templates/like_noshare.tpl   |  4 ++--
 view/theme/quattro/templates/search_item.tpl |  4 +++-
 view/theme/vier/templates/search_item.tpl    |  4 +++-
 7 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/include/conversation.php b/include/conversation.php
index e713994bc6..f29e6d3254 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -587,6 +587,10 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 		'announce' => ['title' => DI::l10n()->t('Reshares','title')]
 	];
 
+	if (DI::pConfig()->get(local_user(), 'system', 'hide_dislike')) {
+		unset($conv_responses['dislike']);
+	}
+
 	// array with html for each thread (parent+comments)
 	$threads = [];
 	$threadsid = -1;
@@ -678,6 +682,10 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 					'share'   => null,
 				];
 
+				if (DI::pConfig()->get(local_user(), 'system', 'hide_dislike')) {
+					unset($likebuttons['dislike']);
+				}
+
 				$body = Item::prepareBody($item, true, $preview);
 
 				list($categories, $folders) = DI::contentItem()->determineCategoriesTerms($item);
diff --git a/mod/photos.php b/mod/photos.php
index 03b78e0e19..9511261553 100644
--- a/mod/photos.php
+++ b/mod/photos.php
@@ -1384,7 +1384,7 @@ function photos_content(App $a)
 				$likebuttons = Renderer::replaceMacros($like_tpl, [
 					'$id' => $link_item['id'],
 					'$likethis' => DI::l10n()->t("I like this \x28toggle\x29"),
-					'$nolike' => DI::l10n()->t("I don't like this \x28toggle\x29"),
+					'$dislike' => DI::pConfig()->get(local_user(), 'system', 'hide_dislike') ? '' : DI::l10n()->t("I don't like this \x28toggle\x29"),
 					'$wait' => DI::l10n()->t('Please wait'),
 					'$return_path' => DI::args()->getQueryString(),
 				]);
@@ -1413,10 +1413,17 @@ function photos_content(App $a)
 			}
 
 			$conv_responses = [
-				'like' => ['title' => DI::l10n()->t('Likes','title')],'dislike' => ['title' => DI::l10n()->t('Dislikes','title')],
-				'attendyes' => ['title' => DI::l10n()->t('Attending','title')], 'attendno' => ['title' => DI::l10n()->t('Not attending','title')], 'attendmaybe' => ['title' => DI::l10n()->t('Might attend','title')]
+				'like'        => ['title' => DI::l10n()->t('Likes','title')],
+				'dislike'     => ['title' => DI::l10n()->t('Dislikes','title')],
+				'attendyes'   => ['title' => DI::l10n()->t('Attending','title')],
+				'attendno'    => ['title' => DI::l10n()->t('Not attending','title')],
+				'attendmaybe' => ['title' => DI::l10n()->t('Might attend','title')]
 			];
 
+			if (DI::pConfig()->get(local_user(), 'system', 'hide_dislike')) {
+				unset($conv_responses['dislike']);
+			}
+
 			// display comments
 			if (DBA::isResult($items)) {
 				foreach ($items as $item) {
@@ -1515,9 +1522,8 @@ function photos_content(App $a)
 					}
 				}
 			}
-			$response_verbs = ['like'];
-			$response_verbs[] = 'dislike';
-			$responses = get_responses($conv_responses, $response_verbs, $link_item);
+
+			$responses = get_responses($conv_responses, ['like', 'dislike'], $link_item);
 
 			$paginate = $pager->renderFull($total);
 		}
diff --git a/src/Object/Post.php b/src/Object/Post.php
index 5f264c988b..c92c5a32bb 100644
--- a/src/Object/Post.php
+++ b/src/Object/Post.php
@@ -373,6 +373,10 @@ class Post
 		$location_e   = $location;
 		$owner_name_e = $this->getOwnerName();
 
+		if (DI::pConfig()->get(local_user(), 'system', 'hide_dislike')) {
+			$buttons['dislike'] = false;
+		}
+
 		// Disable features that aren't available in several networks
 		if ($buttons["dislike"] && !in_array($item["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA])) {
 			$buttons["dislike"] = false;
diff --git a/view/templates/like_noshare.tpl b/view/templates/like_noshare.tpl
index 25e1e09147..3df365e727 100644
--- a/view/templates/like_noshare.tpl
+++ b/view/templates/like_noshare.tpl
@@ -1,8 +1,8 @@
 
 <div class="wall-item-like-buttons" id="wall-item-like-buttons-{{$id}}">
 	<a href="#" class="icon like" title="{{$likethis}}" onclick="dolike({{$id}},'like'); return false"></a>
-	{{if $nolike}}
-	<a href="#" class="icon dislike" title="{{$nolike}}" onclick="dolike({{$id}},'dislike'); return false"></a>
+	{{if $dislike}}
+	<a href="#" class="icon dislike" title="{{$dislike}}" onclick="dolike({{$id}},'dislike'); return false"></a>
 	{{/if}}
 	<img id="like-rotator-{{$id}}" class="like-rotator" src="images/rotator.gif" alt="{{$wait}}" title="{{$wait}}" style="display: none;" />
 </div>
diff --git a/view/theme/frio/templates/like_noshare.tpl b/view/theme/frio/templates/like_noshare.tpl
index deb2383f4b..b2a56292c7 100644
--- a/view/theme/frio/templates/like_noshare.tpl
+++ b/view/theme/frio/templates/like_noshare.tpl
@@ -3,9 +3,9 @@
 	<button type="button" class="btn-link button-likes" id="like-{{$id}}" title="{{$likethis}}" onclick="dolike({{$id}},'like'); return false;" data-toggle="button">
 		<i class="faded-icon page-action fa fa-thumbs-up" aria-hidden="true"></i>
 	</button>
-	{{if $nolike}}
+	{{if $dislike}}
 	<span class="icon-padding"> </span>
-	<button type="button" class="btn-link button-likes" id="dislike-{{$id}}" title="{{$nolike}}" onclick="dolike({{$id}},'dislike'); return false;" data-toggle="button">
+	<button type="button" class="btn-link button-likes" id="dislike-{{$id}}" title="{{$dislike}}" onclick="dolike({{$id}},'dislike'); return false;" data-toggle="button">
 		<i class="faded-icon page-action fa fa-thumbs-down" aria-hidden="true"></i>
 	</button>
 	{{/if}}
diff --git a/view/theme/quattro/templates/search_item.tpl b/view/theme/quattro/templates/search_item.tpl
index 75d5ef566f..38ac6cf63d 100644
--- a/view/theme/quattro/templates/search_item.tpl
+++ b/view/theme/quattro/templates/search_item.tpl
@@ -53,8 +53,10 @@
 				<a href="#" id="tagger-{{$item.id}}" onclick="itemTag({{$item.id}}); return false;" class="{{$item.star.classtagger}}" title="{{$item.star.tagger}}">{{$item.star.tagger}}</a>
 			{{/if}}
 
-			{{if $item.vote}}
+			{{if $item.vote.like}}
 				<a href="#" id="like-{{$item.id}}"{{if $item.responses.like.self}} class="active{{/if}}" title="{{$item.vote.like.0}}" onclick="dolike({{$item.id}},'like'); return false">{{$item.vote.like.1}}</a>
+			{{/if}}
+			{{if $item.vote.dislike}}
 				<a href="#" id="dislike-{{$item.id}}"{{if $item.responses.dislike.self}} class="active{{/if}}" title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}},'dislike'); return false">{{$item.vote.dislike.1}}</a>
 			{{/if}}
 
diff --git a/view/theme/vier/templates/search_item.tpl b/view/theme/vier/templates/search_item.tpl
index bce28962ec..c1bbaf7cf1 100644
--- a/view/theme/vier/templates/search_item.tpl
+++ b/view/theme/vier/templates/search_item.tpl
@@ -60,8 +60,10 @@
 				<a href="#" id="tagger-{{$item.id}}" onclick="itemTag({{$item.id}}); return false;" class="{{$item.star.classtagger}}" title="{{$item.star.tagger}}">{{$item.star.tagger}}</a>
 			{{/if}}
 
-			{{if $item.vote}}
+			{{if $item.vote.like}}
 				<a href="#" id="like-{{$item.id}}"{{if $item.responses.like.self}} class="active"{{/if}} title="{{$item.vote.like.0}}" onclick="dolike({{$item.id}},'like'); return false">{{$item.vote.like.1}}</a>
+			{{/if}}
+			{{if $item.vote.dislike}}
 				<a href="#" id="dislike-{{$item.id}}"{{if $item.responses.dislike.self}} class="active"{{/if}} title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}},'dislike'); return false">{{$item.vote.dislike.1}}</a>
 			{{/if}}