Update to SMF 2.0.16 - Installation Instructions for 2.0.15

Update to SMF 2.0.16
SMF 2.0.16

SMF 2.0.16 is a significant update for the 2.0.x line.

Most notably, 2.0.16 enables forums comply with the European Union's General Data Protection Regulation. These changes include:
  • A new GDPR Compliance feature in Core Features. Enabling this Core Feature will automatically configure the settings on your forum to make it compliant with the GDPR.
  • User profile data export.
  • Unsubscribe links in email notifications, newsletters, etc., that don't require login.
  • Letting new users choose during registration whether or not to receive newsletters.
  • Automated tracking of revisions to the registration agreement, including requiring users to read and accept updates to the agreement in order to continue using the forum.
  • Support for privacy policy in addition to the registration agreement:
    • Versions of the privacy policy can be created for each installed language.
    • Registration requires acceptance of the privacy policy.
    • Automated tracking of revisions to the privacy policy, including requiring users to read and accept updates to the privacy policy in order to continue using the forum.
    • Links to the privacy policy and registration agreement are included in the footer of every page.
Other notable changes in 2.0.16 include:
  • Fixes and improvements for the image proxy.
  • Security improvements.
  • Suppression of deprecation warnings when running PHP 7.2 or later.
  • Restored support for PHP 5.3. (Do note, however, that the upcoming SMF 2.1 release really does require at least PHP 5.4.)
  • Other bug fixes and improvements.

All users, including the admin, will need to log in again after this patch has been installed.

File Edits ALT + Click to collapse all the operations

./index.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select
// Initate the database connection and define some database functions to use.
Add Before: Select
// Register an error handler.
set_error_handler('error_handler');

Find: Select
$forum_version = 'SMF 2.0.15';
Replace With: Select
$forum_version = 'SMF 2.0.16';
Find: Select
// Register an error handler.
set_error_handler('error_handler');

// Quickly catch random exceptions.
set_exception_handler(function ($e) use ($db_show_debug)
{
if (isset($db_show_debug) && $db_show_debug === true && allowedTo('admin_forum'))
fatal_error(nl2br($e), false);
else
fatal_error($e->getMessage(), false);
});
Replace With: Select
// Quickly catch random exceptions.
set_exception_handler(function ($e) use ($db_show_debug)
{
$error_type = 'general';

// PHP 7 converts fatal errors to special Throwable types. Log them as such.
if (is_a($e, 'Error'))
$error_type = 'critical';

if (isset($db_show_debug) && $db_show_debug === true && allowedTo('admin_forum'))
{
// Only log the message; the rest (such as the stack trace) is just fluff in the log.
log_error($e->getMessage(), $error_type);

fatal_error(nl2br($e), false);
}
else
fatal_error($e->getMessage(), $error_type);
});
Find: Select
// Here's the monstrous $_REQUEST['action'] array - $_REQUEST['action'] => array($file, $function).
$actionArray = array(
Add After: Select

'agreement' => array('Agreement.php', 'Agreement'),
'acceptagreement' => array('Agreement.php', 'AcceptAgreement'),
Find: Select
'notifyboard' => array('Notify.php', 'BoardNotify'),
Add After: Select

'notifyannouncements' => array('Notify.php', 'AnnouncementsNotify'),

./proxy.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
// Try to create the image cache directory if it doesn't exist
if (!file_exists($this->cache))
Add After: Select

{
Find: Select
if (!mkdir($this->cache) || !copy(dirname($this->cache) . '/index.php', $this->cache . '/index.php'))
return false;
Add After: Select

}
Find: Select
if (empty($_GET['hash']) || empty($_GET['request'])
Add After: Select
|| ($_GET['request'] === 'http:') || ($_GET['request'] === 'https:')
Find: Select
if (md5($request . $this->secret) != $hash)
Replace With: Select
if (hash_hmac('sha1', $request, $this->secret) != $hash)
Find: Select
if (!$this->isCached($request)) {
return $this->cacheImage($request);
}
Replace With: Select
if (!$this->isCached($request))
return $this->cacheImage($request);
Find: Select


/**
* Serves the request
Replace With: Select


/**
* Serves the request
Find: Select
if (!$response) {
// Throw a 404
header('HTTP/1.0 404 Not Found');
Replace With: Select
if ($response === null)
{
// Throw a 404
header('HTTP/1.1 404 Not Found');
Find: Select
if (!$response) {
header('Location: ' . $request, false, 301);
}
Replace With: Select
if ($response === false)
header('Location: ' . $request, false, 301);
Find: Select
// Is the cache expired?
Add After: Select
Try to refresh it.
Find: Select
header('Content-type: ' . $cached['content_type']);
header('Content-length: ' . $cached['size']);
Add Before: Select
// Check whether the ETag was sent back, and cache based on that...
$eTag = '"' . substr(sha1($request) . $cached['time'], 0, 64) . '"';
if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'], $eTag) !== false)
{
header('HTTP/1.1 304 Not Modified');
exit;
}

Find: Select

echo base64_decode($cached['body']);
Add Before: Select


// Add some caching
header('Cache-control: public');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($cached_file)) . ' GMT');
header('ETag: ' . $eTag);
Find: Select
* Returns the request's hashed filepath
*
* @access public
Replace With: Select
* Returns the request's hashed filepath
*
* @access protected
Find: Select
* @return bool|int Whether the specified image was cached or error code when accessing
Replace With: Select
* @return bool|null Whether the specified image was cached; null if not found or not an image.
Find: Select

if (empty($response)) {
return false;
}

if ($responseCode != 200) {
return false;
}
Replace With: Select

if (empty($response) || $responseCode != 200)
return null;
Find: Select
if ($contentParts[0] != 'image')
return false;
Replace With: Select
if ($contentParts[0] != 'image')
return null;
Find: Select
))) === false ? 1 : null;;
Replace With: Select
))) !== false;
Find: Select
$proxy->serve();
Add After: Select


?>
This operation isn't vital to the installation of this mod.
Find: Select
\?>\s*$
Replace With: Select
?>

./SSI.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select
global $image_proxy_enabled, $image_proxy_secret, $image_proxy_maxsize;
Add After: Select

global $auth_secret, $cookie_no_auth_secret;

./Themes/default/languages/Admin.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find: Select

$txt['admin_agreement'] = 'Show and require agreement letter when registering';
Replace With: Select

$txt['admin_agreement'] = 'Require new members to accept the registration agreement';
$txt['admin_agreement_minor_edit'] = 'This is a minor edit';
$txt['reset_agreement_desc'] = 'This will force all members to re-read and accept the registration agreement in order to continue using the forum.';
$txt['admin_privacy_policy'] = 'Require new members to accept the privacy policy';
$txt['reset_privacy_policy_desc'] = 'This will force all members to re-read and accept the privacy policy in order to continue using the forum.';
$txt['admin_agreement_info'] = 'Last updated: %1$s.';
Find: Select

$txt['database_prefix']
Add Before: Select

$txt['privacy_policy'] = 'Privacy Policy';
$txt['privacy_policy_desc'] = 'This privacy policy describes the promises you make to your users regarding how you will use their personal data. It is shown when a user registers an account on this forum and has to be accepted before the user can continue registration.';
Find: Select
href="http://www.simplemachines.org/community/index.php"
Replace With: Select
href="https://www.simplemachines.org/community/index.php"
Find: Select
href="http://www.simplemachines.org/"
Replace With: Select
href="https://www.simplemachines.org/"

./Themes/default/languages/EmailTemplates.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find: Select
TOPICSUBJECT: The subject of the topic being announced.
MESSAGE: The message body of the first post of the announced topic.
TOPICLINK: A link to the topic being announced.
Add After: Select

UNSUBSCRIBELINK: Link to unsubscribe from announcements.
Find: Select
'subject' => 'New announcement: {TOPICSUBJECT}',
'body' => '{MESSAGE}

To unsubscribe from these announcements, login to the forum and uncheck "Receive forum announcements and important notifications by email." in your profile.

You can view the full announcement by following this link:
{TOPICLINK}

{REGARDS}',
),
'notify_boards_once_body' => array(
Replace With: Select
'subject' => 'New announcement: {TOPICSUBJECT}',
'body' => '{MESSAGE}

You can view the full announcement by following this link:
{TOPICLINK}

To unsubscribe from these announcements, follow this link:
{UNSUBSCRIBELINK}

For more control over the email notifications you receive, login to the forum and go to the Notification area in your profile.

{REGARDS}',
),
'notify_boards_once_body' => array(
Find: Select
http://www.simplemachines.org/community/?action=profile;u=2676
Replace With: Select
https://www.simplemachines.org/community/?action=profile;u=2676
Find: Select
http://www.simplemachines.org/community/?action=profile;u=63186
Replace With: Select
https://www.simplemachines.org/community/?action=profile;u=63186
Find: Select
http://www.simplemachines.org/community/?action=profile;u=46625
Replace With: Select
https://www.simplemachines.org/community/?action=profile;u=46625
Find: Select
http://www.simplemachines.org/community/?action=profile;u=72038
Replace With: Select
https://www.simplemachines.org/community/?action=profile;u=72038
Find: Select
http://www.simplemachines.org/community/?action=profile;u=48671
Replace With: Select
https://www.simplemachines.org/community/?action=profile;u=48671
Find: Select
http://www.simplemachines.org/community/?action=profile;u=63186
Replace With: Select
https://www.simplemachines.org/community/?action=profile;u=63186

./Themes/default/languages/Errors.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find (at the end of the file): Select
?>
Add Before: Select


// Registration Agreement
$txt['error_no_agreement'] = 'There is no registration agreement to display!';
$txt['error_no_privacy_policy'] = 'A privacy policy has not been created for this forum.';

// Unsubscribe
$txt['unsubscribe_invalid'] = 'The unsubscribe link that brought you here does not appear to be valid.';

./Themes/default/languages/Help.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find: Select

This box shows recently updated announcements from <a href="http://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>.
Replace With: Select

This box shows recently updated announcements from <a href="https://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>.
Find: Select

$helptxt['allow_disableAnnounce'] = 'This will allow users to opt out of notification of topics you announce by checking the &quot;announce topic&quot; checkbox when posting.';
Replace With: Select

$helptxt['allow_disableAnnounce'] = 'This will allow users to opt out of notification of topics you announce by checking the &quot;announce topic&quot; checkbox when posting.<br /><br />This setting must be enabled in order to comply with the rules of the <a href="https://ec.europa.eu/commission/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules_en" class="bbc_link">GDPR</a>.';
$helptxt['announcements_default'] = 'This simply controls whether the the checkbox on the registration form starts as checked or unchecked.<br /><br />This setting must be disabled in order to comply with the rules of the <a href="https://ec.europa.eu/commission/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules_en" class="bbc_link">GDPR</a>.';
$helptxt['notify_tokens'] = 'When this setting is enabled, the unsubscribe link included in every notification email includes a unique token to identify an unsubscribe request as legitimate. Without this, users must instead log into the forum to verify their identities before being allowed to unsubscribe.<br /><br />This setting must be enabled in order to comply with the rules of the <a href="https://ec.europa.eu/commission/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules_en" class="bbc_link">GDPR</a>.';
Find: Select
$helptxt['secureCookies'] = 'Enabling this option will force the cookies created for users on your forum to be marked as secure. Only enable this option if you are using HTTPS throughout your site as it will break cookie handling otherwise!';
Add After: Select

$helptxt['cookie_no_auth_secret'] = 'This option forces SMF to use a weaker form of authentication for login cookies. It should only be enabled if you need backwards compatibility with login integration mods that have not yet been updated to support SMF 2.0.16 and higher.';
Find: Select

$helptxt['coppaAge'] = 'The value specified in this box will determine the minimum age that new members must be to be granted immediate access to the forums.
Add Before: Select

$helptxt['requireAgreement'] = 'This setting must be enabled in order to comply with the rules of the <a href="https://ec.europa.eu/commission/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules_en" class="bbc_link">GDPR</a>.';
$helptxt['requirePolicyAgreement'] = 'This setting must be enabled in order to comply with the rules of the <a href="https://ec.europa.eu/commission/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules_en" class="bbc_link">GDPR</a>.';
Find: Select

$helptxt['latest_support'] = 'This panel shows you some of the most common problems and questions on your server configuration. Don\'t worry, this information isn\'t logged or anything.<br /><br />If this stays as &quot;Retrieving support information...&quot;, your computer probably cannot connect to <a href="http://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>.';
$helptxt['latest_packages'] = 'Here you can see some of the most popular and some random packages or mods, with quick and easy installations.<br /><br />If this section doesn\'t show up, your computer probably cannot connect to <a href="http://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>.';
$helptxt['latest_themes'] = 'This area shows a few of the latest and most popular themes from <a href="http://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>. It may not show up properly if your computer can\'t find <a href="http://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>, though.';
Replace With: Select

$helptxt['latest_support'] = 'This panel shows you some of the most common problems and questions on your server configuration. Don\'t worry, this information isn\'t logged or anything.<br /><br />If this stays as &quot;Retrieving support information...&quot;, your computer probably cannot connect to <a href="https://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>.';
$helptxt['latest_packages'] = 'Here you can see some of the most popular and some random packages or mods, with quick and easy installations.<br /><br />If this section doesn\'t show up, your computer probably cannot connect to <a href="https://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>.';
$helptxt['latest_themes'] = 'This area shows a few of the latest and most popular themes from <a href="https://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>. It may not show up properly if your computer can\'t find <a href="https://www.simplemachines.org/" target="_blank" class="new_win">www.simplemachines.org</a>, though.';

./Themes/default/languages/Install.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find: Select
$txt['db_sqlite_warning'] = 'Only recommended for small, low volume and/or intranet-type forums
Add After: Select
. In future versions, this will be removed!
Find: Select
<a href="http://www.simplemachines.org/community/index.php" target="_blank">
Replace With: Select
<a href="https://www.simplemachines.org/community/index.php" target="_blank">
Find: Select
[url=http://www.simplemachines.org/community/index.php]
Replace With: Select
[url=https://www.simplemachines.org/community/index.php]
Find: Select
http://www.simplemachines.org/about/stats.php
Replace With: Select
https://www.simplemachines.org/about/stats.php
Find: Select
<a href="http://www.simplemachines.org/redirect/mod_security">More information about disabling mod_security</a>';
Replace With: Select
<a href="https://www.simplemachines.org/redirect/mod_security">More information about disabling mod_security</a>';
Find: Select
<a href="http://www.simplemachines.org/redirect/mod_security">More information about disabling mod_security</a><br />
Replace With: Select
<a href="https://www.simplemachines.org/redirect/mod_security">More information about disabling mod_security</a><br />
Find: Select
please visit the <a href="http://www.simplemachines.org">Simple Machines Website</a>
Replace With: Select
please visit the <a href="https://www.simplemachines.org">Simple Machines Website</a>
Find: Select
<a href="http://www.simplemachines.org">Simple Machines</a> website to ensure you are installing
Replace With: Select
<a href="https://www.simplemachines.org">Simple Machines</a> website to ensure you are installing
Find: Select
from the <a href="http://www.simplemachines.org">Simple Machines Website</a>
Replace With: Select
from the <a href="https://www.simplemachines.org">Simple Machines Website</a>
Find: Select
<a href="http://www.simplemachines.org">Simple Machines</a> website to ensure you are upgrading
Replace With: Select
<a href="https://www.simplemachines.org">Simple Machines</a> website to ensure you are upgrading

./Themes/default/languages/Login.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find: Select


// Registration form.
Add Before: Select

$txt['privacy_policy'] = 'Privacy Policy';
$txt['agreement_policy_agree'] = 'I accept the terms of the agreement and privacy policy.';
$txt['agreement_policy_agree_coppa_above'] = 'I accept the terms of the agreement and privacy policy, and I am at least %1$d years old.';
$txt['agreement_policy_agree_coppa_below'] = 'I accept the terms of the agreement and privacy policy, and I am younger than %1$d years old.';
Find: Select

$txt['admin_register']
Add Before: Select

$txt['setting_announcements_default'] = 'Enable "' . $txt['notify_announcements'] . '" by default.';
Find: Select

$txt['coppa_title']
Add Before: Select
$txt['admin_register_require_agreement'] = 'Require user to accept the registration agreement';
$txt['admin_register_require_policy'] = 'Require user to accept the privacy policy';

./Themes/default/languages/ManageSettings.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find: Select

$txt['disallow_sendBody']
Add Before: Select

$txt['notify_tokens'] = 'Use token-based unsubscribe links in notification emails<div class="smalltext">Allows users to unsubscribe without logging in.</div>';
Find: Select

$txt['core_settings_switch_on']
Add Before: Select

$txt['core_settings_item_gdpr'] = 'GDPR Compliance';
$txt['core_settings_item_gdpr_desc'] = 'Enabling this feature will configure a number of settings on your forum to make it compliant with the European Union\'s <a href="https://ec.europa.eu/commission/priorities/justice-and-fundamental-rights/data-protection/2018-reform-eu-data-protection-rules_en" class="bbc_link">General Data Protection Regulation</a>.';
$txt['core_settings_privacy_policy_warning'] = 'The GDPR requires you to have a privacy policy for your forum. After you enable this setting, you will be take to a page where you can create one.';
Find: Select

$txt['securityDisable']
Add Before: Select

$txt['cookie_no_auth_secret'] = 'Use basic cookie authentication';

./Themes/default/languages/Modlog.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find: Select


// Admin type strings.
Add Before: Select

$txt['modlog_admin_log_gdpr_no_delete'] = '<strong>Also note:</strong> Entries for updates to the registration agreement and privacy policy cannot be removed from this log while the GDPR Compliance setting is enabled.';
Find: Select
$txt['modlog_ac_remind_member']
Add Before: Select
$txt['modlog_ac_agreement_updated'] = 'Updated the {language} registration agreement';
$txt['modlog_ac_policy_updated'] = 'Updated the {language} privacy policy';

./Themes/default/languages/Profile.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0;
Replace With: Select
// Version: 2.0.16;
Find (at the end of the file): Select
?>
Add Before: Select

// Registration Agreement
$txt['trackEdit_action_agreement_accepted'] = 'Accepted the registration agreement';
$txt['trackEdit_action_policy_accepted'] = 'Accepted the privacy policy';

$txt['export_profile_data'] = 'Download profile data';

./Themes/default/languages/index.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2\.0\.15;
Replace With: Select
// Version: 2.0.16;
This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0.14;
Replace With: Select
// Version: 2.0.16;
Find: Select

$txt['notify']
Add Before: Select

$txt['terms_and_policies'] = 'Terms and Policies';
Find: Select


$txt['activate_code']
Add Before: Select

$txt['notifyboard_subscribed'] = '%1$s has been subscribed to new topic notifications for this board.';
$txt['notifyboard_unsubscribed'] = '%1$s has been unsubscribed from new topic notifications for this board.';

$txt['notifytopic_subscribed'] = '%1$s has been subscribed to new reply notifications for this topic.';
$txt['notifytopic_unsubscribed'] = '%1$s has been unsubscribed from new reply notifications for this topic.';

$txt['notify_announcements'] = 'Allow the administrators to send me important news by email';
$txt['notifyannouncements_prompt'] = 'Do you want to receive forum newsletters, announcements and important notifications by email?';
$txt['notifyannouncements_subscribed'] = '%1$s has been subscribed to forum newsletters, announcements and important notifications.';
$txt['notifyannouncements_unsubscribed'] = '%1$s has been unsubscribed from forum newsletters, announcements and important notifications.';

$txt['unsubscribe_announcements_plain'] = 'To unsubscribe from forum newsletters, announcements and important notifications, follow this link:<br />%1$s';
$txt['unsubscribe_announcements_html'] = '<span style="font-size:small"><a href="%1$s">Unsubscribe</a> from forum newsletters, announcements and important notifications.</span>';
Find: Select
SMF &copy; 2017
Replace With: Select
SMF &copy; 2019
Find: Select
"http://www.simplemachines.org/about/smf/license.php"
Replace With: Select
"https://www.simplemachines.org/about/smf/license.php"
Find: Select
"http://www.simplemachines.org"
Replace With: Select
"https://www.simplemachines.org"
This operation isn't vital to the installation of this mod.
Find: Select
\?>\s*$
Replace With: Select
?>

./Themes/default/languages/Who.english.php

This operation isn't vital to the installation of this mod.
Find: Select
// Version: 2.0; Who
Replace With: Select
// Version: 2.0.16; Who
Find: Select
$txt['credits_groups_orignal_pm'] = 'Original Project Managers';
Add After: Select

$txt['credits_in_memoriam'] = 'In loving memory of';

./Sources/Admin.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select
strip_tags($helptxt[$item2])
Replace With: Select
strip_tags($helptxt[$item[2]])
Find: Select

'reservednames' =>
Add Before: Select

'policy' => array($txt['privacy_policy'], 'admin_forum'),
Find: Select
'http://wiki.simplemachines.org/'
Replace With: Select
'https://wiki.simplemachines.org/'
Find: Select
'http://wiki.simplemachines.org/smf/features2'
Replace With: Select
'https://wiki.simplemachines.org/smf/features2'
Find: Select
'http://wiki.simplemachines.org/smf/options2'
Replace With: Select
'https://wiki.simplemachines.org/smf/options2'
Find: Select
'http://wiki.simplemachines.org/smf/themes2'
Replace With: Select
'https://wiki.simplemachines.org/smf/themes2'
Find: Select
'http://wiki.simplemachines.org/smf/packages2'
Replace With: Select
'https://wiki.simplemachines.org/smf/packages2'
Find: Select
'http://www.simplemachines.org/community/'
Replace With: Select
'https://www.simplemachines.org/community/'
Find: Select
'http://www.simplemachines.org/redirect/english_support'
Replace With: Select
'https://www.simplemachines.org/redirect/english_support'
Find: Select
'http://www.simplemachines.org/redirect/international_support_boards'
Replace With: Select
'https://www.simplemachines.org/redirect/international_support_boards'
Find: Select
'http://www.simplemachines.org/redirect/smf_support'
Replace With: Select
'https://www.simplemachines.org/redirect/smf_support'
Find: Select
'http://www.simplemachines.org/redirect/customize_support'
Replace With: Select
'https://www.simplemachines.org/redirect/customize_support'

./Sources/Class-CurlFetchWeb.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
?>
Replace With: Select
?>

./Sources/Errors.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.4
Replace With: Select
* @version 2.0.16
Find: Select
// Ignore errors if we're ignoring them or they are strict notices from PHP 5 (which cannot be solved without breaking PHP 4.)
if (error_reporting() == 0 || (defined('E_STRICT') && $error_level == E_STRICT && (empty($modSettings['enableErrorLogging']) || $modSettings['enableErrorLogging'] != 2)))
return;
Replace With: Select
// Error was suppressed with the @-operator.
if (error_reporting() == 0)
return true;

// Ignore errors that should should not be logged.
$error_match = error_reporting() & $error_level;
if (empty($error_match) || empty($modSettings['enableErrorLogging']))
return false;

// Send these notices introduced by PHP 7.2 to where the sun don't shine!
if (strpos($error_string, 'create_function()') !== false)
return true;

./Sources/Groups.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
// Make sure we're dealing with integers only.
Add Before: Select
// Only proven admins can remove admins.
if ($context['group']['id'] == 1)
validateSession();

Find: Select
// Must be adding new members to the group...
elseif (isset($_REQUEST['add']) && (!empty($_REQUEST['toAdd']) || !empty($_REQUEST['member_add'])) && $context['group']['assignable'])
{
checkSession();
Add After: Select


// Demand an admin password before adding new admins -- every time, no matter what.
if ($context['group']['id'] == 1)
validateSession(true);

./Sources/Help.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
$context['wiki_url'] = 'http://wiki.simplemachines.org/smf';
Replace With: Select
$context['wiki_url'] = 'https://wiki.simplemachines.org/smf';

./Sources/Load.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
$check = sha1($user_settings['passwd'] . $user_settings['password_salt']) == $password;
Replace With: Select
$check = hash_hmac('sha1', sha1($user_settings['passwd'] . $user_settings['password_salt']), get_auth_secret()) == $password;
Find: Select
if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($user_settings['avatar'], 'http://') !== false)
$user_settings['avatar'] = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($user_settings['avatar']) . '&hash=' . md5($user_settings['avatar'] . $image_proxy_secret);
Replace With: Select
if (!empty($user_settings['avatar']))
$user_settings['avatar'] = get_proxied_url($user_settings['avatar']);
Find: Select
global $user_profile, $modSettings, $board_info, $smcFunc;
global $boardurl, $image_proxy_enabled, $image_proxy_secret
Add After: Select
, $user_info
Find: Select
if ($image_proxy_enabled && !empty($row['avatar']) && stripos($row['avatar'], 'http://') !== false)
$row['avatar'] = $boardurl . '/proxy.php?request=' . urlencode($row['avatar']) . '&hash=' . md5($row['avatar'] . $image_proxy_secret);
Replace With: Select
if (!empty($row['avatar']))
$row['avatar'] = get_proxied_url($row['avatar']);
Find: Select
if (!$initialize)
return;
Add After: Select

// Perhaps we've changed the agreement or privacy policy? Only redirect if:
// 1. They're not a guest or admin
// 2. This isn't called from SSI
// 3. This isn't an XML request
// 4. They're not trying to do any of the following actions:
// 4a. View or accept the agreement and/or policy
// 4b. Login or logout
// 4c. Get a feed (RSS, ATOM, etc.)
if (!$user_info['is_guest'] && !$user_info['is_admin'] && SMF != 'SSI' && !isset($_REQUEST['xml']) && (!isset($_REQUEST['action']) || !in_array($_REQUEST['action'], array('agreement', 'acceptagreement', 'login2', 'logout', '.xml'))))
{
$agreement_lang = !empty($modSettings['agreement_updated_' . $user_info['language']]) ? $user_info['language'] : 'default';
$policy_lang = !empty($modSettings['policy_' . $user_info['language']]) ? $user_info['language'] : $language;

// Do they need to accept the latest registration agreement?
if (!empty($modSettings['requireAgreement']) && !empty($modSettings['agreement_updated_' . $agreement_lang]) && (empty($options['agreement_accepted']) || $modSettings['agreement_updated_' . $agreement_lang] > $options['agreement_accepted']))
{
$agreement_subaction = 'agreement';
}

// Do they need to consent to the latest privacy policy?
if (!empty($modSettings['requirePolicyAgreement']) && !empty($modSettings['policy_updated_' . $policy_lang]) && (empty($options['policy_accepted']) || $modSettings['policy_updated_' . $policy_lang] > $options['policy_accepted']))
{
$agreement_subaction = !empty($agreement_subaction) ? 'both' : 'policy';
}

// Send them to the right place
if (!empty($agreement_subaction))
redirectexit('action=agreement;sa=' . $agreement_subaction);
}
Find (at the end of the file): Select
?>
Add Before: Select

function get_auth_secret()
{
global $auth_secret, $sourcedir;

if (empty($auth_secret))
{
// Best.
if (function_exists('random_bytes'))
$auth_secret = bin2hex(random_bytes(32));

// Equally good, if installed.
elseif (function_exists('mcrypt_create_iv'))
$auth_secret = bin2hex(mcrypt_create_iv(32));

// Good enough.
elseif (function_exists('openssl_random_pseudo_bytes'))
$auth_secret = bin2hex(openssl_random_pseudo_bytes(32));

// Less than ideal, but we're out of options.
else
$auth_secret = hash('sha256', mt_rand());

// It is important to store this in Settings.php, not the database.
require_once($sourcedir . '/Subs-Admin.php');
updateSettingsFile(array('auth_secret' => '\'' . $auth_secret . '\''));
}

return $auth_secret;
}

./Sources/ManageMaintenance.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.7
Replace With: Select
* @version 2.0.16
Find: Select
// This function will do the conversion later on.
$entity_replace = create_function('$string', '
$num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string;
return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) ? \'\' : ($num < 0x80 ? \'&#\' . $num . \';\' : ($num < 0x800 ? chr(192 | $num >> 6) . chr(128 | $num & 63) : ($num < 0x10000 ? chr(224 | $num >> 12) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63) : chr(240 | $num >> 18) . chr(128 | $num >> 12 & 63) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63))));');

// Loop through all tables that need converting.
Replace With: Select
// Loop through all tables that need converting.
Find: Select
$insertion_variables['changes_' . $column_name] = preg_replace_callback('~&#(\d{1,7}|x[0-9a-fA-F]{1,6});~', 'fixchar__callback', $column_value);
Replace With: Select
$insertion_variables['changes_' . $column_name] = preg_replace_callback('~&#(\d{1,7}|x[0-9a-fA-F]{1,6});~', 'fixchardb__callback', $column_value);

./Sources/ManageNews.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
// Force them to have it?
if (empty($context['email_force']))
Replace With: Select
// Force them to have it?
if (empty($context['email_force']) || !empty($modSettings['force_gdpr']))
Find: Select
// Finally - emails!
if (!empty($_POST['emails']))
Replace With: Select
// Finally - emails!
if (!empty($_POST['emails']) && empty($modSettings['force_gdpr']))
Find: Select
// Prepare the message for sending it as HTML
Add Before: Select
// Include an unsubscribe link if necessary.
if (!$context['send_pm'] && !empty($modSettings['notify_tokens']))
{
require_once($sourcedir . '/Notify.php');
$include_unsubscribe = true;
$_POST['message'] .= "\n\n" . '{$member.unsubscribe}';
}

Find: Select

$from_member = array(
'{$member.email}',
'{$member.link}',
'{$member.id}',
'{$member.name}'
Add After: Select
,
'{$member.unsubscribe}',
Find: Select

$to_member = array(
$email,
Add Before: Select

// Non-members can't subscribe or unsubscribe from anything...
$unsubscribe_link = '';
Find: Select

!empty($_POST['send_html']) ? '<a href="mailto:' . $email . '">' . $email . '</a>' : $email,
'??',
$email
Add After: Select
,
$unsubscribe_link,
Find: Select
// Replace the member-dependant variables
Add Before: Select
if (!empty($include_unsubscribe))
{
$token = createUnsubscribeToken($row['id_member'], $row['email_address'], 'announcements');
$unsubscribe_link = sprintf($txt['unsubscribe_announcements_' . (!empty($_POST['send_html']) ? 'html' : 'plain')], $scripturl . '?action=notifyannouncements;u=' . $row['id_member'] . ';token=' . $token);
}
else
$unsubscribe_link = '';

Find: Select
$row['id_member'],
$cleanMemberName,
), $_POST['message']);
Replace With: Select
$row['id_member'],
$cleanMemberName,
$unsubscribe_link,
), $_POST['message']);

./Sources/ManageRegistration.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
'agreement' => array('EditAgreement', 'admin_forum'),
Add After: Select

'policy' => array('EditPrivacyPolicy', 'admin_forum'),
Find: Select
'agreement' => array(
'description' => $txt['registration_agreement_desc'],
),
Add After: Select

'policy' => array(
'description' => $txt['privacy_policy_desc'],
),
Find: Select
global $txt, $context, $sourcedir, $scripturl, $smcFunc
Add After: Select
, $modSettings
Find: Select
'memberGroup' => empty($_POST['group']) || !allowedTo('manage_membergroups') ? 0 : (int) $_POST['group'],
);
Add After: Select


if (empty($_POST['requireAgreement']) && empty($modSettings['force_gdpr']))
$regOptions['theme_vars']['agreement_accepted'] = time();

if (empty($_POST['requirePolicyAgreement']) && empty($modSettings['force_gdpr']))
$regOptions['theme_vars']['policy_accepted'] = time();
Find: Select
global $txt, $boarddir, $context, $modSettings, $smcFunc, $settings;
Replace With: Select
global $txt, $boarddir, $context, $modSettings, $smcFunc, $user_info;

$context['force_gdpr'] = !empty($modSettings['force_gdpr']);
Find: Select
if (isset($_POST['agreement']))
Replace With: Select
$agreement_lang = empty($context['current_agreement']) ? 'default' : substr($context['current_agreement'], 1);

$context['agreement'] = file_exists($boarddir . '/agreement' . $context['current_agreement'] . '.txt') ? str_replace("\r", '', file_get_contents($boarddir . '/agreement' . $context['current_agreement'] . '.txt')) : '';

if (isset($_POST['agreement']) && str_replace("\r", '', $_POST['agreement']) != $context['agreement'])
Find: Select

updateSettings(array('requireAgreement' => !empty($_POST['requireAgreement'])));
}

$context['agreement'] = file_exists($boarddir . '/agreement' . $context['current_agreement'] . '.txt') ? htmlspecialchars(file_get_contents($boarddir . '/agreement' . $context['current_agreement'] . '.txt')) : '';
$context['warning'] = is_writable($boarddir . '/agreement' . $context['current_agreement'] . '.txt') ? '' : $txt['agreement_not_writable'];
$context['require_agreement'] = !empty($modSettings['requireAgreement']);
Replace With: Select

if (!isset($_POST['minor_edit']) || !empty($modSettings['force_gdpr']))
{
$agreement_settings['agreement_updated_' . $agreement_lang] = time();

// Writing it counts as agreeing to it, right?
$smcFunc['db_insert']('replace',
'{db_prefix}themes',
array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
array($user_info['id'], 1, 'agreement_accepted', time()),
array('id_member', 'id_theme', 'variable')
);
logAction('agreement_updated', array('language' => $context['editable_agreements'][$context['current_agreement']]), 'admin');
logAction('agreement_accepted', array('applicator' => $user_info['id']), 'user');

updateSettings($agreement_settings);
}

$context['agreement'] = str_replace("\r", '', $_POST['agreement']);
}

$context['agreement_info'] = sprintf($txt['admin_agreement_info'], empty($modSettings['agreement_updated_' . $agreement_lang]) ? $txt['never'] : timeformat($modSettings['agreement_updated_' . $agreement_lang]));

$context['agreement'] = $smcFunc['htmlspecialchars']($context['agreement']);
$context['warning'] = is_writable($boarddir . '/agreement' . $context['current_agreement'] . '.txt') ? '' : $txt['agreement_not_writable'];
Find: Select
global $txt, $context, $scripturl, $modSettings, $sourcedir;
Add After: Select

global $language, $boarddir;
Find: Select
// This is really quite wanting.
require_once($sourcedir . '/ManageServer.php');
Add After: Select


// Do we have at least default versions of the agreement and privacy policy?
$agreement = file_exists($boarddir . '/agreement.' . $language . '.txt') || file_exists($boarddir . '/agreement.txt');
$policy = !empty($modSettings['policy_' . $language]);
Find: Select
array('check', 'send_welcomeEmail'),
Add After: Select

'',
array('check', 'requireAgreement', 'text_label' => $txt['admin_agreement'], 'value' => !empty($modSettings['force_gdpr']) ? 1 : $modSettings['requireAgreement'], 'disabled' => !empty($modSettings['force_gdpr'])),
array('warning', empty($agreement) ? 'error_no_agreement' : ''),
array('check', 'requirePolicyAgreement', 'text_label' => $txt['admin_privacy_policy'], 'value' => !empty($modSettings['force_gdpr']) ? 1 : $modSettings['requireAgreement'], 'disabled' => !empty($modSettings['force_gdpr'])),
array('warning', empty($policy) ? 'error_no_privacy_policy' : ''),
Find: Select
array('text', 'coppaPhone'),
Add After: Select

'',
array('check', 'announcements_default', 'disabled' => empty($modSettings['allow_disableAnnounce']) || !empty($modSettings['force_gdpr']), 'value' => !empty($modSettings['force_gdpr']) ? 0 : (empty($modSettings['allow_disableAnnounce']) ? 1 : !empty($modSettings['announcements_default']))),
Find: Select
$_POST['coppaPost'] = str_replace("\n", '<br />', empty($_POST['coppaPost']) ? '' : $_POST['coppaPost']);
Add After: Select


// GDPR requires these settings to have certain values
if (!empty($modSettings['force_gdpr']))
{
$_POST['requireAgreement'] = 1;
$_POST['requirePolicyAgreement'] = 1;
$_POST['announcements_default'] = 0;
}
elseif (empty($modSettings['allow_disableAnnounce']))
$_POST['announcements_default'] = 1;
Find (at the end of the file): Select
?>
Add Before: Select

// Sure, you can sell my personal info for profit (...or not)
function EditPrivacyPolicy()
{
global $txt, $boarddir, $context, $modSettings, $smcFunc, $user_info;

$context['force_gdpr'] = !empty($modSettings['force_gdpr']);

// By default, edit the current language's policy
$context['current_policy_lang'] = $user_info['language'];

// We need a policy for every language
getLanguages();

foreach ($context['languages'] as $lang)
{
$context['editable_policies'][$lang['filename']] = $lang['name'];

// Are we editing this one?
if (isset($_POST['policy_lang']) && $_POST['policy_lang'] == $lang['filename'])
$context['current_policy_lang'] = $lang['filename'];
}

$context['policy'] = empty($modSettings['policy_' . $context['current_policy_lang']]) ? '' : $modSettings['policy_' . $context['current_policy_lang']];

if (isset($_POST['policy']))
{
checkSession();

// Make sure there are no creepy-crawlies in it
$policy_text = $smcFunc['htmlspecialchars'](str_replace("\r", '', $_POST['policy']));

$policy_settings = array(
'policy_' . $context['current_policy_lang'] => $policy_text,
);

if ($policy_text != $context['policy'] && (!isset($_POST['minor_edit']) || !empty($modSettings['force_gdpr'])))
{
$policy_settings['policy_updated_' . $context['current_policy_lang']] = time();

// Writing it counts as agreeing to it, right?
$smcFunc['db_insert']('replace',
'{db_prefix}themes',
array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
array($user_info['id'], 1, 'policy_accepted', time()),
array('id_member', 'id_theme', 'variable')
);
logAction('policy_updated', array('language' => $context['editable_policies'][$context['current_policy_lang']]), 'admin');
logAction('policy_accepted', array('applicator' => $user_info['id']), 'user');
}

updateSettings($policy_settings);

$context['policy'] = $policy_text;
}

$context['policy_info'] = sprintf($txt['admin_agreement_info'], empty($modSettings['policy_updated_' . $context['current_policy_lang']]) ? $txt['never'] : timeformat($modSettings['policy_updated_' . $context['current_policy_lang']]));

$context['sub_template'] = 'edit_privacy_policy';
$context['page_title'] = $txt['privacy_policy'];
}

./Sources/ManageSearch.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.16
Find: Select
$context['fulltext_index'] = '';
Replace With: Select
$context['fulltext_index'] = array();

./Sources/ManageServer.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select
global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename
Add After: Select
, $cookie_no_auth_secret
Find: Select
array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies', 'disabled' => !isset($_SERVER['HTTPS']) || !(strtolower($_SERVER['HTTPS']) == 'on' || strtolower($_SERVER['HTTPS']) == '1')),
Add After: Select

array('cookie_no_auth_secret', $txt['cookie_no_auth_secret'], 'db', 'check', false, 'cookie_no_auth_secret', 'disabled' => empty($modSettings['integrate_verify_user'])),
Find: Select
saveSettings($config_vars);

// If the cookie name was changed, reset the cookie.
Add Before: Select
// If this setting will be ignored anyway, disable it.
if (empty($modSettings['integrate_verify_user']) && !empty($cookie_no_auth_secret))
$_POST['cookie_no_auth_secret'] = 0;

Find: Select
$config_bools = array(
'db_persist', 'db_error_send',
'maintenance', 'image_proxy_enabled',
Add After: Select

'cookie_no_auth_secret',
Find: Select
$url = 'http://download.simplemachines.org/
Replace With: Select
$url = 'https://download.simplemachines.org/
Find: Select
read_tgz_file('http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), $boarddir, false, true, $install_files)
Replace With: Select
read_tgz_file('https://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), $boarddir, false, true, $install_files)
Find: Select
read_tgz_file('http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), null)
Replace With: Select
read_tgz_file('https://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => ''))) . ';fetch=' . urlencode($_GET['did']), null)

./Sources/ManageSettings.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
function ModifyCoreFeatures($return_config = false)
{
global $txt, $scripturl, $context, $settings, $sc, $modSettings;
Add After: Select

global $language;
Find: Select
recacheSpiderNames();
'),
),
Add After: Select

// Quick setting to toggle into GDPR compliance
'gdpr' => array(
'url' => 'action=admin;area=regcenter;sa=policy',
'settings' => array(
'force_gdpr' => 1,
// DEVELOPERS: Add values to toggle here
),
'setting_callback' => create_function('$value', '
global $modSettings;

$returnSettings = array();

if ($value)
{
$returnSettings[\'requireAgreement\'] = 1;
$returnSettings[\'requirePolicyAgreement\'] = 1;
$returnSettings[\'allow_disableAnnounce\'] = 1;
$returnSettings[\'announcements_default\'] = 0;
$returnSettings[\'notify_tokens\'] = 1;
}

return $returnSettings;
'),
'save_callback' => create_function('$value', '
global $modSettings, $language, $context;

if ($value && empty($modSettings[\'policy_\' . $language]))
redirectexit(\'action=admin;area=regcenter;sa=policy;\' . $context[\'session_var\'] . \'=\' . $context[\'session_id\']);
'),
),
Find: Select
if ($context['is_new_install'])
updateSettings(array('admin_features' => ''));
Add After: Select

$context['show_privacy_policy_warning'] = empty($modSettings['policy_' . $language]);
Find: Select
array('check', 'allow_disableAnnounce'),
Replace With: Select
array('check', 'allow_disableAnnounce', 'disabled' => !empty($modSettings['force_gdpr'])),
array('check', 'notify_tokens', 'disabled' => !empty($modSettings['force_gdpr'])),
Find: Select
if (isset($_POST['lastActive']))
$_POST['lastActive'] = min((int) $_POST['lastActive'], 1440);
Add After: Select

// GDPR requires these settings to always be true
if (!empty($modSettings['force_gdpr']))
{
$_POST['allow_disableAnnounce'] = 1;
$_POST['notify_tokens'] = 1;
}

./Sources/Modlog.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.16
Find: Select

$context['hoursdisable'] = 24;
Add After: Select

// Actions whose log entries cannot be deleted.
$context['uneditable_actions'] = !empty($modSettings['force_gdpr']) ? array('agreement_updated', 'policy_updated') : array();
Find: Select
id_log = {int:moderate_log}
AND log_time < {int:twenty_four_hours_wait}
Add After: Select

AND action NOT IN ({array_string:uneditable})
Find: Select
'twenty_four_hours_wait' => time() - $context['hoursdisable'] * 3600,
'moderate_log' => $context['log_type'],
Add After: Select

'uneditable' => $context['uneditable_actions'],
Find: Select
AND id_action IN ({array_string:delete_actions})
AND log_time < {int:twenty_four_hours_wait}
Add After: Select

AND action NOT IN ({array_string:uneditable})
Find: Select
'delete_actions' => array_unique($_POST['delete']),
'moderate_log' => $context['log_type'],
Add After: Select

'uneditable' => $context['uneditable_actions'],
Find: Select
'value' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin' : 'moderation') . '_log_desc']
Add After: Select
. (!empty($modSettings['force_gdpr']) && $context['log_type'] == 3 ? '<br />' . $txt['modlog_admin_log_gdpr_no_delete'] : '')
Find: Select

if (!isset($context['hoursdisable']))
$context['hoursdisable'] = 24;
Add After: Select

if (!isset($context['uneditable_actions']))
$context['uneditable_actions'] = array();
Find: Select
'editable' => time() > $row['log_time'] + $context['hoursdisable'] * 3600
Add After: Select
&& !in_array($row['action'], $context['uneditable_actions'])

./Sources/News.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.8
Replace With: Select
* @version 2.0.16
Find: Select

// First, output the xml header.
Add Before: Select

// Descriptive filenames = good
$xml_filename = array(preg_replace('/\s+/', '_', $feed_title), $_GET['sa']);
if ($_GET['sa'] === 'profile')
$xml_filename[] = 'u=' . (isset($_GET['u']) ? (int) $_GET['u'] : $user_info['id']);
if (!empty($boards))
$xml_filename[] = 'boards=' . implode(',', $boards);
elseif (!empty($board))
$xml_filename[] = 'board=' . $board;
$xml_filename[] = $xml_format;
$xml_filename = strtr(un_htmlspecialchars(implode('-', $xml_filename)), '"', '') ;

header('Content-Disposition: ' . (isset($_GET['download']) ? 'attachment' : 'inline') . '; filename="' . $xml_filename . '.xml"');
Find: Select
if (!empty($cdata_override))
return $data;
Add After: Select

// Do we even need to do this?
if (strpbrk($data, '<>&') == false)
return $data;
Find: Select
elseif ($smcFunc['substr']($data, $pos, 1) == ']')
{
$cdata .= ']]>&#093;<![CDATA[';
$pos++;
Replace With: Select
elseif ($smcFunc['substr']($data, $pos, 3) == ']]>')
{
$cdata .= ']]]]><![CDATA[>';
$pos = $pos + 3;
Find: Select
global $scripturl, $memberContext, $user_profile, $modSettings, $user_info
Add After: Select
, $smcFunc, $language
Find: Select
'language' => cdata_parse($profile['language']),
Replace With: Select
'language' => cdata_parse(!empty($profile['language']) ? $profile['language'] : $smcFunc['ucwords'](strtr($language, array('_' => ' ', '-utf8' => '')))),
Find: Select
<generator uri="http://www.simplemachines.org"
Replace With: Select
<generator uri="https://www.simplemachines.org"

./Sources/Notify.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
*/

// Turn on/off notifications...
Add Before: Select

void AnnouncementsNotify()
- is called to turn off/on notification for newsletters, announcements, etc.
- uses the Notify template. (notify_board sub template.)
- asks for a sub action (on/off) if none was specified.
- shows a success message after it is done.
- is accessed via ?action=notifyannoucements.

void getMemberWithToken(type = '')
- verifies a subscribe/unsubscribe token, then returns some basic member info.
- the type parameter is used to indicate what sort of notification the token is for.
- shows a fatal error message to the user and dies if the token is invalid.

void createUnsubscribeToken(int memID, string email, type = '', itemID = 0)
- builds a subscribe/unsubscribe token.
- each token is unique to a member's email and to the thing they want to unsubscribe from
Find: Select
// Make sure they aren't a guest or something - guests can't really receive notifications!
is_not_guest();
isAllowedTo('mark_any_notify');
Replace With: Select
// Are they trying a token-based anonymous request?
if (isset($_REQUEST['u']) && isset($_REQUEST['token']))
{
$member_info = getMemberWithToken('topic');
$skipCheckSession = true;
}
// Otherwise, this is for the current user.
else
{
// Make sure they aren't a guest or something - guests can't really receive notifications!
is_not_guest();
isAllowedTo('mark_any_notify');

$member_info = $user_info;
}
Find: Select
LIMIT 1',
array(
'current_member' => $user_info['id'],
Replace With: Select
LIMIT 1',
array(
'current_member' => $member_info['id'],
Find: Select
// Set the template variables...
$context['topic_href'] =
Add Before: Select
if ($member_info['id'] !== $user_info['id'])
$context['notify_info'] = array(
'u' => $member_info['id'],
'token' => $_REQUEST['token'],
);

Find: Select
checkSession('get');

// Attempt to turn notifications on.
Add Before: Select
if (empty($skipCheckSession))
Find: Select
$smcFunc['db_insert']('ignore',
'{db_prefix}log_notify',
array('id_member' => 'int', 'id_topic' => 'int'),
array($user_info['id'], $topic),
array('id_member', 'id_topic')
);
Replace With: Select
$smcFunc['db_insert']('ignore',
'{db_prefix}log_notify',
array('id_member' => 'int', 'id_topic' => 'int'),
array($member_info['id'], $topic),
array('id_member', 'id_topic')
);
Find: Select
checkSession('get');

// Just turn notifications off.
Add Before: Select
if (empty($skipCheckSession))
Find: Select
$smcFunc['db_query']('', '
DELETE FROM {db_prefix}log_notify
WHERE id_member = {int:current_member}
AND id_topic = {int:current_topic}',
array(
'current_member' => $user_info['id'],
'current_topic' => $topic,
)
);
Replace With: Select
$smcFunc['db_query']('', '
DELETE FROM {db_prefix}log_notify
WHERE id_member = {int:current_member}
AND id_topic = {int:current_topic}',
array(
'current_member' => $member_info['id'],
'current_topic' => $topic,
)
);
Find: Select
// Send them back to the topic.
Add Before: Select
// If this request wasn't for the current user, just show a confirmation message.
if ($member_info['id'] !== $user_info['id'])
{
loadTemplate('Notify');
$context['page_title'] = $txt['notification'];
$context['sub_template'] = 'notify_pref_changed';
$context['notify_success_msg'] = sprintf($txt['notifytopic' . ($_GET['sa'] == 'on' ? '_subscribed' : '_unsubscribed')], $member_info['email']);
return;
}

Find: Select

// Permissions are an important part of anything ;).
is_not_guest();
isAllowedTo('mark_notify');
Replace With: Select

// Unsubscribing with a token.
if (isset($_REQUEST['u']) && isset($_REQUEST['token']))
{
$member_info = getMemberWithToken('board');
$skipCheckSession = true;
}
// No token, so try with the current user.
else
{
// Permissions are an important part of anything ;).
is_not_guest();
isAllowedTo('mark_notify');

$member_info = $user_info;
}
Find: Select
LIMIT 1',
array(
'current_board' => $board,
'current_member' => $user_info['id'],
Replace With: Select
LIMIT 1',
array(
'current_board' => $board,
'current_member' => $member_info['id'],
Find: Select
// Set the template variables...
$context['board_href'] =
Add Before: Select
if ($member_info['id'] !== $user_info['id'])
$context['notify_info'] = array(
'u' => $member_info['id'],
'token' => $_REQUEST['token'],
);

Find: Select
checkSession('get');

// Turn notification on. (note this just blows smoke if it's already on.)
Add Before: Select
if (empty($skipCheckSession))
Find: Select
$smcFunc['db_insert']('ignore',
'{db_prefix}log_notify',
array('id_member' => 'int', 'id_board' => 'int'),
array($user_info['id'], $board),
array('id_member', 'id_board')
);
Replace With: Select
$smcFunc['db_insert']('ignore',
'{db_prefix}log_notify',
array('id_member' => 'int', 'id_board' => 'int'),
array($member_info['id'], $board),
array('id_member', 'id_board')
);
Find: Select
checkSession('get');

// Turn notification off for this board.
Add Before: Select
if (empty($skipCheckSession))
Find: Select
$smcFunc['db_query']('', '
DELETE FROM {db_prefix}log_notify
WHERE id_member = {int:current_member}
AND id_board = {int:current_board}',
array(
'current_board' => $board,
'current_member' => $user_info['id'],
)
);
Replace With: Select
$smcFunc['db_query']('', '
DELETE FROM {db_prefix}log_notify
WHERE id_member = {int:current_member}
AND id_board = {int:current_board}',
array(
'current_board' => $board,
'current_member' => $member_info['id'],
)
);
Find: Select
// Back to the board!
Add Before: Select
// Probably a guest, so just show a confirmation message.
if ($member_info['id'] !== $user_info['id'])
{
loadTemplate('Notify');
$context['page_title'] = $txt['notification'];
$context['sub_template'] = 'notify_pref_changed';
$context['notify_success_msg'] = sprintf($txt['notifyboard' . ($_GET['sa'] == 'on' ? '_subscribed' : '_unsubscribed')], $member_info['email']);
return;
}

Find (at the end of the file): Select
?>
Add Before: Select

function AnnouncementsNotify()
{
global $scripturl, $txt, $board, $user_info, $context, $smcFunc;

if (isset($_REQUEST['u']) && isset($_REQUEST['token']))
{
$member_info = getMemberWithToken('announcements');
$skipCheckSession = true;
}
else
{
is_not_guest();
$member_info = $user_info;
}

loadTemplate('Notify');
$context['page_title'] = $txt['notification'];

// Ask what they want to do.
if (empty($_GET['sa']))
{
$context['sub_template'] = 'notify_announcements';

if ($member_info['id'] !== $user_info['id'])
$context['notify_info'] = array(
'u' => $member_info['id'],
'token' => $_REQUEST['token'],
);

return;
}

// We don't tolerate imposters around here.
if (empty($skipCheckSession))
checkSession('get');

// Update their announcement notification preference.
updateMemberData($member_info['id'], array('notify_announcements' => $_GET['sa'] == 'on' ? '1' : '0'));

$context['sub_template'] = 'notify_pref_changed';
$context['notify_success_msg'] = sprintf($txt['notifyannouncements' . ($_GET['sa'] == 'on' ? '_subscribed' : '_unsubscribed')], $member_info['email']);
}

// Verifies the token, then returns some member info
function getMemberWithToken($type)
{
global $smcFunc, $board, $topic, $modSettings;

// Keep it sanitary, folks
$id_member = !empty($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0;

// We can't do anything without these
if (empty($id_member) || empty($_REQUEST['token']))
fatal_lang_error('unsubscribe_invalid', false);

// Get the user info we need
$request = $smcFunc['db_query']('', '
SELECT id_member AS id, email_address AS email
FROM {db_prefix}members
WHERE id_member = {int:id_member}',
array(
'id_member' => $id_member,
)
);
if ($smcFunc['db_num_rows']($request) == 0)
fatal_lang_error('unsubscribe_invalid', false);
$member_info = $smcFunc['db_fetch_assoc']($request);
$smcFunc['db_free_result']($request);

// What token are we expecting?
$expected_token = createUnsubscribeToken($member_info['id'], $member_info['email'], $type, in_array($type, array('board', 'topic')) && !empty($$type) ? $$type : 0);

// Don't do anything if the token they gave is wrong
if ($_REQUEST['token'] !== $expected_token)
fatal_lang_error('unsubscribe_invalid', false);

// At this point, we know we have a legitimate unsubscribe request
return $member_info;
}

function createUnsubscribeToken($memID, $email, $type = '', $itemID = 0)
{
$token_items = implode(' ', array($memID, $email, $type, $itemID));

// When the message is public and the key is secret, an HMAC is the appropriate tool.
$token = hash_hmac('sha256', $token_items, get_auth_secret(), true);

// When using an HMAC, 80 bits (10 bytes) is plenty for security.
$token = substr($token, 0, 10);

// Use base64 (with URL-friendly characters) to make the token shorter.
return strtr(base64_encode($token), array('+' => '_', '/' => '-', '=' => ''));
}

./Sources/PersonalMessage.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select
// If we're in non-boring view do something exciting!
Add Before: Select
$context['labels'] = !empty($context['labels']) ? (array) $context['labels'] : array();

Find: Select
'fully_labeled' => count($context['message_labels'][$subject['id_pm']]) == count($context['labels']),
Replace With: Select
'fully_labeled' => (!empty($context['message_labels'][$subject['id_pm']]) ? count($context['message_labels'][$subject['id_pm']]) : 0) == count($context['labels']),
Find: Select
'fully_labeled' => count($context['message_labels'][$message['id_pm']]) == count($context['labels']),
Replace With: Select
'fully_labeled' => (!empty($context['message_labels'][$message['id_pm']]) ? count($context['message_labels'][$message['id_pm']]) : 0) == count($context['labels']),
Find: Select
if ($set > 60)
Replace With: Select
if ($smcFunc['strlen']($set) > 60)
Find: Select

// The link tree - gotta have this :o
Add Before: Select

// Limit the Criteria and Actions to this.
$context['rule_limiters'] = array(
'criteria' => 10,
'actions' => 10,
);
Find: Select


ApplyRules(true);
Add Before: Select

spamProtection('pm');
Find: Select
// Let's do the criteria first - it's also hardest!
$criteria = array();
Add After: Select

$criteriaCount = 0;
Find: Select
// Members need to be found.
Add Before: Select
// Too many rules in this rule.
if ($criteriaCount++ >= $context['rule_limiters']['criteria'])
break;

Find: Select
$isOr = $_POST['rule_logic'] == 'or' ? 1 : 0;
foreach ($_POST['acttype'] as $ind => $type)
{
// Picking a valid label?
if ($type == 'lab' && (!isset($_POST['labdef'][$ind]) || !isset($context['labels'][$_POST['labdef'][$ind] - 1])))
continue;

// Record what we're doing.
Replace With: Select
$isOr = $_POST['rule_logic'] == 'or' ? 1 : 0;
$actionCount = 0;
foreach ($_POST['acttype'] as $ind => $type)
{
// Picking a valid label?
if ($type == 'lab' && (!ctype_digit((string) $ind) || !isset($_POST['labdef'][$ind]) || $_POST['labdef'][$ind] == '' || !isset($context['labels'][$_POST['labdef'][$ind] - 1])))
continue;

// Too many actions in this rule.
if ($actionCount++ >= $context['rule_limiters']['actions'])
break;

// Record what we're doing.

./Sources/Poll.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
$_COOKIE['guest_poll_vote'] .= ';' . $row['id_poll'] . ',' . time() . ',' . (count($pollOptions) > 1 ? explode(',' . $pollOptions) : $pollOptions[0]);
Replace With: Select
$_COOKIE['guest_poll_vote'] .= ';' . $row['id_poll'] . ',' . time() . ',' . implode(',', $pollOptions);

./Sources/Post.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0',
Add After: Select

'UNSUBSCRIBELINK' => $scripturl . '?action=notifyannouncements',
Find: Select
$emaildata = loadEmailTemplate('new_announcement'
Add Before: Select
// Tokens allow people to unsubscribe without logging in
if (!empty($modSettings['notify_tokens']))
{
require_once($sourcedir . '/Notify.php');
$token = createUnsubscribeToken($row['id_member'], $row['email_address'], 'announcements');

$replacements['UNSUBSCRIBELINK'] .= ';u=' . $row['id_member'] . ';token=' . $token;
}

Find: Select
// Figure out which email to send off
Add Before: Select
// Make a token for the unsubscribe link
if (!empty($modSettings['notify_tokens']))
{
require_once($sourcedir . '/Notify.php');
$token = createUnsubscribeToken($rowmember['id_member'], $rowmember['email_address'], 'board', $rowmember['id_board']);

$replacements['UNSUBSCRIBELINK'] .= ';u=' . $rowmember['id_member'] . ';token=' . $token;
}

./Sources/Profile-Modify.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
require_once($sourcedir . '/ManageAttachments.php');
Add After: Select

require_once($sourcedir . '/Subs-Package.php');
Find: Select
require_once($sourcedir . '/Subs-Package.php');

$url = parse_url($_POST['userpicpersonal']);
Replace With: Select
$url = parse_url($_POST['userpicpersonal']);
Find: Select
$profile_vars['avatar'] = str_replace('%20', '', preg_replace('~action(?:=|%3d)(?!dlattach)~i', 'action-', $_POST['userpicpersonal']));
Add After: Select

$mime_valid = check_mime_type($profile_vars['avatar'], 'image/', true);
Find: Select
// Should we check dimensions?
Add Before: Select
elseif (empty($mime_valid))
return 'bad_avatar';
Find: Select
$sizes = @getimagesize($_FILES['attachment']['tmp_name']);

// No size, then it's probably not a valid pic.
// No size, then it's probably not a valid pic.
Replace With: Select
$mime_valid = check_mime_type($_FILES['attachment']['tmp_name'], 'image/', true);
$sizes = empty($mime_valid) ? false : @getimagesize($_FILES['attachment']['tmp_name']);

// No size, then it's probably not a valid pic.

./Sources/Profile.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select

'deleteaccount' => array(
Add Before: Select

'getprofiledata' => array(
'label' => $txt['export_profile_data'],
'custom_url' => $scripturl . '?action=.xml;type=smf;download;sa=profile',
'permission' => array(
'own' => array('profile_view_own'),
'any' => array('moderate_forum'),
),
),

./Sources/QueryString.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.9
Replace With: Select
* @version 2.0.16
Find: Select
$buffer = preg_replace_callback('~"' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+?)(#[^"]*?)?"~', 'pathinfo_insert__preg_callback', $buffer);
}
Add After: Select


// Be nice and try to inject session tokens into login forms since many older themes don't.
if ($user_info['is_guest'])
$buffer = preg_replace_callback(
'~(<form[^<]+action=login2(.+))</form>~iUs' . (!empty($context['utf8']) ? 'u' : ''),
function ($m) use ($context)
{
$repl = '';
if (strpos($m[0], $context['session_var']) === false)
$repl .= '<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '"/>';

return $m[1] . $repl . '</form>';
},
$buffer
);

./Sources/Recent.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
loadTemplate('Recent');
$context['page_title'] = $txt['recent_posts'];
Add After: Select


$_REQUEST['start'] = (int) $_REQUEST['start'];

./Sources/Register.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
$possible_bools = array_diff($possible_bools, $exclude_fields);

// Set the options needed for registration.
Replace With: Select
$possible_bools = array_diff($possible_bools, $exclude_fields);

// Set the options needed for registration.
Find: Select
$context['show_coppa'] = !empty($modSettings['coppaAge']);

// Under age restrictions?
if ($context['show_coppa'])
{
$context['skip_coppa'] = false;
$context['coppa_agree_above'] = sprintf($txt['agreement_agree_coppa_above'], $modSettings['coppaAge']);
$context['coppa_agree_below'] = sprintf($txt['agreement_agree_coppa_below'], $modSettings['coppaAge']);
}
Replace With: Select
$context['show_coppa'] = !empty($modSettings['coppaAge']);
$context['require_policy_agreement'] = !empty($modSettings['requirePolicyAgreement']);

// Under age restrictions?
if ($context['show_coppa'])
{
$context['skip_coppa'] = false;
$context['coppa_agree_above'] = sprintf($txt['agreement' . ($context['require_policy_agreement'] ? '_policy' : '') . '_agree_coppa_above'], $modSettings['coppaAge']);
$context['coppa_agree_below'] = sprintf($txt['agreement' . ($context['require_policy_agreement'] ? '_policy' : '') . '_agree_coppa_below'], $modSettings['coppaAge']);
}
Find: Select


// Any custom fields we want filled in?
require_once($sourcedir . '/Profile.php');
loadCustomFields(0, 'register');
Add Before: Select


// If you have to agree to the privacy policy, it needs to be loaded from the database.
if ($context['require_policy_agreement'])
{
// Have we got a localized one?
if (!empty($modSettings['policy_' . $user_info['language']]))
$context['policy'] = parse_bbc($modSettings['policy_' . $user_info['language']]);
elseif (!empty($modSettings['policy_' . $language]))
$context['policy'] = parse_bbc($modSettings['policy_' . $language]);
else
{
loadLanguage('Errors');
$context['policy'] = $txt['error_no_privacy_policy'];
}
}
Find: Select


// !!! Why isn't this a simple set operation?
// Were there any errors?
$context['registration_errors'] = array();
Add Before: Select


$context['announcements_ask'] = !empty($modSettings['force_gdpr']) || !empty($modSettings['allow_disableAnnounce']);
$context['notify_announcements'] = isset($_POST['notify_announcements']) ? (bool) $_POST['notify_announcements'] : !empty($modSettings['announcements_default']);
Find: Select
if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed']))
Replace With: Select
if ((!empty($modSettings['requireAgreement']) || !empty($modSettings['requirePolicyAgreement'])) && empty($_SESSION['registration_agreed']))
Find: Select


// Make sure they are clean, dammit!
Add Before: Select


// Note when they accepted the agreement and privacy policy
$regOptions['theme_vars']['agreement_accepted'] = $regOptions['theme_vars']['policy_accepted'] = time();

./Sources/ScheduledTasks.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
global $smcFunc, $modSettings, $sourcedir, $db_type
Add After: Select
, $image_proxy_enabled, $cachedir
Find: Select
// Log we've done it...
Add Before: Select
// Cleanup old proxied images.
if (!empty($image_proxy_enabled) && $handle = opendir($cachedir . '/images'))
{
while (false !== ($file = readdir($handle)))
{
// Remove images older than 5 days.
if (filemtime($cachedir . '/images/' . $file) < time() - (5 * 86400))
unlink($cachedir . '/images/' . $file);
}

closedir($handle);
}

Find: Select
$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.simplemachines.org' : '';
Replace With: Select
$server = empty($file['path']) || !in_array(parse_url($file['path'], PHP_URL_SCHEME), array('http', 'https')) ? 'https://www.simplemachines.org' : '';

./Sources/SearchAPI-Fulltext.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select

$query_where[] = 'm.body' . (in_array($regularWord, $query_params['excluded_words']) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:complex_body_' . $count . '}';
$query_params['complex_body_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . '[[:>:]]';
Replace With: Select

$query_where[] = 'm.body' . (in_array($regularWord, $query_params['excluded_words']) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : 'RLIKE') . '{string:complex_body_' . $count . '}';
$query_params['complex_body_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . '[[:>:]]';
Find: Select

$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_phrase_' . $count . '}';
$query_params['exclude_subject_phrase_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]';
Replace With: Select

$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_phrase_' . $count . '}';
$query_params['exclude_subject_phrase_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]';
Find: Select

$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_words_' . $count . '}';
$query_params['exclude_subject_words_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . '[[:>:]]';
Replace With: Select

$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_words_' . $count . '}';
$query_params['exclude_subject_words_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . '[[:>:]]';

./Sources/Security.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.3
Replace With: Select
* @version 2.0.16
Find: Select
function validateSession()
Replace With: Select
function validateSession($force = false)
Find: Select
if (!empty($modSettings['securityDisable']) || (!empty($_SESSION['admin_time']) && $_SESSION['admin_time'] + $refreshTime >= time()))
Replace With: Select
if (!$force && (!empty($modSettings['securityDisable']) || (!empty($_SESSION['admin_time']) && $_SESSION['admin_time'] + $refreshTime >= time())))
Find: Select
if (empty($user_info))
return false;
Add After: Select


// Calling this before permissions have not been set up properly?
// Assume a denial, since we don't know if you can do anything yet.
if (!isset($user_info['permissions']))
return false;

./Sources/Subs-Admin.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select
fclose($fp);
}
}
Add After: Select


// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
// it seems that there are times it might not. So let's MAKE it dump the cache.
if (function_exists('opcache_invalidate'))
opcache_invalidate(dirname(__FILE__) . '/Settings.php', true);

./Sources/Subs-Auth.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select
function setLoginCookie($cookie_length, $id, $password = '')
{
global $cookiename
Add After: Select
, $cookie_no_auth_secret
Find: Select
// Get the data and path to set it on.
Add Before: Select
// Fallback option to support outdated mods. This will be removed in future versions!
$no_auth_secret = !empty($cookie_no_auth_secret) && !empty($modSettings['integrate_verify_user']);

// Ensure the cookie can't be forged.
if ($password !== '' && !$no_auth_secret)
$password = hash_hmac('sha1', $password, get_auth_secret());

./Sources/Subs-BoardIndex.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
c.id_cat, c.name AS cat_name,' : '') . '
Replace With: Select
c.id_cat, c.name AS cat_name, cat_order,' : '') . '
Find: Select
$categories[$row_board['id_cat']] = array(
'id' => $row_board['id_cat'],
'name' => $row_board['cat_name'],
Add After: Select

'order' => $row_board['cat_order'],
Find: Select
if (!empty($boardIndexOptions['set_latest_post']) && !empty($latest_post['ref']))
$context['latest_post'] = $latest_post['ref'];

return $boardIndexOptions['include_categories'] ? $categories : $this_category;
}
Replace With: Select
if (!empty($boardIndexOptions['set_latest_post']) && !empty($latest_post['ref']))
$context['latest_post'] = $latest_post['ref'];

// Sort the categories, maintaining index association.
// Don't do it in the query, that would use a temporary table and be very slow.
if ($boardIndexOptions['include_categories'])
uasort($categories, 'cmpBoardIndex');

return $boardIndexOptions['include_categories'] ? $categories : $this_category;
}

function cmpBoardIndex($a, $b)
{
if ($a['order'] == $b['order'])
{
return 0;
}
return ($a['order'] < $b['order']) ? -1 : 1;
}

./Sources/Subs-Db-mysql.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
// Checck for MySQLi first...
if (function_exists('mysqli_connect'))
Replace With: Select
// Check for MySQLi first...
// !!! This driver does not work on PHP < 5.4.0.
if (function_exists('mysqli_connect') && version_compare(PHP_VERSION, '5.4') >= 0)
Find: Select
// Checck for MySQLi first...
if (function_exists('mysqli_connect'))
Replace With: Select
// Check for MySQLi first...
// !!! This driver does not work on PHP < 5.4.0.
// !!! This passthrough is needed for the search functions.
if (function_exists('mysqli_connect') && version_compare(PHP_VERSION, '5.4') >= 0)
Find: Select
'db_error' => 'mysqli_error',
Replace With: Select
'db_error' => array($this, 'show_error'),
Find: Select
if (isset($mysql_set_mode) && $mysql_set_mode === true)
$smcFunc['db_query']('', 'SET sql_mode = \'\', AUTOCOMMIT = 1',
array(),
false
);

return $connection;
Replace With: Select
if (isset($mysql_set_mode) && $mysql_set_mode === true)
$smcFunc['db_query']('', 'SET sql_mode = \'\', AUTOCOMMIT = 1',
array(),
$connection
);

return $connection;
Find: Select
global $db_unbuffered, $db_callback, $modSettings;
Replace With: Select
global $db_unbuffered, $db_persist, $db_callback, $modSettings;
Find: Select

/**
* Database error!
Replace With: Select

/**
* Database error!
*
* @param object $connection = null
*/
public function show_error($connection = null)
{
global $db_connection;

// Decide which connection to use
$connection = $connection === null ? $db_connection : $connection;

// This is the error message...
return mysqli_errno($connection);
}

/**
* Database error!

./Sources/Subs-Db-sqlite.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
if (preg_match($exp, $match))
return 1;
Replace With: Select
if (preg_match($exp, $search))
return 1;

./Sources/Subs-Graphics.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.16
Find: Select
fwrite($fp_destination, $fileContents);
Add Before: Select
$mime_valid = check_mime_type($fileContents, implode('|', array_map('image_type_to_mime_type', array_keys($default_formats))));
if (empty($mime_valid))
return false;

Find: Select
elseif ($fp_destination)
{
Add After: Select
$mime_valid = check_mime_type($source, implode('|', array_map('image_type_to_mime_type', array_keys($default_formats))), true);
if (empty($mime_valid))
return false;

./Sources/Subs-Members.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
// If it's enabled, increase the registrations for today.
Add Before: Select
// Log their acceptance of the agreement and privacy policy, for future reference.
foreach (array('agreement_accepted', 'policy_accepted') as $key)
if (!empty($theme_vars[$key]))
logAction($key, array('applicator' => $memberID), 'user');

./Sources/Subs-Package.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
'type_major' => !empty($parts[6]) ? (int) $parts[5] : 0,
Replace With: Select
'type_major' => !empty($parts[5]) ? (int) $parts[5] : 0,
Find: Select
if (!is_writable($dir . '/' . $entryname))
$package_ftp->chmod($ftp_file, 0777);
Replace With: Select
if (!is_writable($dir . '/' . $ftp_file))
$package_ftp->chmod($ftp_file, 0777);
Find (at the end of the file): Select
?>
Add Before: Select

// Checks whether a file or data retrieved by fetch_web_data has the expected MIME type.
// Returns 1 if the detected MIME type matches the pattern, 0 if it doesn't, or 2 if we can't check.
function check_mime_type($data, $type_pattern, $is_path = false)
{
// On Windows, the Fileinfo extension may not be enabled by default.
if (!extension_loaded('fileinfo'))
{
// Maybe we can load it dynamically?
$loaded = false;
$safe = ini_get('safe_mode');
$dl_ok = ini_get('enable_dl');
if (empty($safe) && !empty($dl_ok))
$loaded = @dl(((PHP_SHLIB_SUFFIX === 'dll') ? 'php_' : '') . 'fileinfo.' . PHP_SHLIB_SUFFIX);

// Oh well. We tried.
if (!$loaded)
return 2;
}

// Just some nice, simple data to analyze.
if (empty($is_path))
$mime_type = finfo_buffer(finfo_open(FILEINFO_MIME), $data);

// A file, or maybe a URL?
else
{
// Local file.
if (file_exists($data))
$mime_type = mime_content_type($data);

// URL.
elseif (url_exists($data))
$mime_type = finfo_buffer(finfo_open(FILEINFO_MIME), fetch_web_data($data));

// Non-existent files obviously don't have the right MIME type.
else
return 0;
}

return (int) @preg_match('~' . $type_pattern . '~', $mime_type);
}

./Sources/Subs-Post.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select

if ($modSettings['mail_type'] == 1 && $modSettings['smtp_username'] != '' && $modSettings['smtp_password'] != '')
Add Before: Select

// Start off by using the stored mail server.
$helo = $modSettings['smtp_host'];

// Try and determine this server's name.
if (function_exists('gethostname') && gethostname() !== false)
$helo = gethostname();
elseif (function_exists('php_uname'))
$helo = php_uname('n');
elseif (!empty($_SERVER['SERVER_NAME']))
$helo = $_SERVER['SERVER_NAME'];
Find: Select

// !!! These should send the CURRENT server's name, not the mail server's!

// EHLO could be understood to mean encrypted hello...
Replace With: Select

// EHLO could be understood to mean encrypted hello...
Find: Select
// EHLO could be understood to mean encrypted hello...
if (server_parse('EHLO ' . $modSettings['smtp_host'], $socket, null) == '250')
Replace With: Select
// EHLO could be understood to mean encrypted hello...
if (server_parse('EHLO ' . $helo, $socket, null) == '250')
Find: Select
elseif (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250'))
Replace With: Select
elseif (!server_parse('HELO ' . $helo, $socket, '250'))
Find: Select
// Just say "helo".
if (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250'))
Replace With: Select
// Just say "helo".
if (!server_parse('HELO ' . $helo, $socket, '250'))
Find: Select
'UNSUBSCRIBELINK' => $scripturl . '?action=notify;topic=' . $row['id_topic'] . '.0',
);
Add After: Select


// Make a token for the unsubscribe link
if (!empty($modSettings['notify_tokens']))
{
require_once($sourcedir . '/Notify.php');
$token = createUnsubscribeToken($row['id_member'], $row['email_address'], 'topic', $row['id_topic']);

$replacements['UNSUBSCRIBELINK'] .= ';u=' . $row['id_member'] . ';token=' . $token;
}
Find: Select
$message_type = 'notification_reply';
// Do they want the body of the message sent too?
Add Before: Select
// Make a token for the unsubscribe link
if (!empty($modSettings['notify_tokens']))
{
require_once($sourcedir . '/Notify.php');
$token = createUnsubscribeToken($row['id_member'], $row['email_address'], 'topic', $row['id_topic']);

$replacements['UNSUBSCRIBELINK'] .= ';u=' . $row['id_member'] . ';token=' . $token;
}

./Sources/Subs-Sound.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
// Fixate randomization for this word.
mt_srand(end(unpack('n', md5($word . session_id()))));
Replace With: Select
// Fixate randomization for this word.
$unpacked = unpack('n', md5($word . session_id()));
mt_srand(end($unpacked));

./Sources/Subs.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
global $forum_copyright, $context, $boardurl,
Replace With: Select
global $forum_copyright, $context, $scripturl,
Find: Select
$forum_copyright = sprintf($forum_copyright, $forum_version);
Add After: Select


// It feels icky putting this here, but it's the only good way to do this without
// requiring all custom themes to be updated in order to comply with the GDPR.
if (!empty($modSettings['force_gdpr']))
$forum_copyright .= ' | <a id="button_agreement" href="' . $scripturl . '?action=agreement"><span>' . $txt['terms_and_policies'] . '</span></a>';
Find: Select
<noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed></object>'
Replace With: Select
<noembed><a href="$1" target="_blank" rel="noopener noreferrer" class="bbc_link bbc_flash_disabled new_win">$1</a></noembed></object>'
Find: Select
<noembed><a href="$1" target="_blank" class="new_win">$1</a></noembed>'),
Replace With: Select
<noembed><a href="$1" target="_blank" rel="noopener noreferrer" class="bbc_link bbc_flash_disabled new_win">$1</a></noembed>'),
Find: Select
'disabled_content' => '<a href="$1" target="_blank" class="new_win">$1</a>',
Replace With: Select
'disabled_content' => '<a href="$1" target="_blank" rel="noopener noreferrer" class="bbc_link bbc_flash_disabled new_win">$1</a>',
Find: Select
'<a href="$1" class="bbc_ftp new_win" target="_blank">$1</a>',
Replace With: Select
'<a href="$1" class="bbc_ftp new_win" target="_blank" rel="noopener noreferrer">$1</a>',
Find: Select
'<a href="$1" class="bbc_ftp new_win" target="_blank">',
Replace With: Select
'<a href="$1" class="bbc_ftp new_win" target="_blank" rel="noopener noreferrer">',
Find: Select
'content' => '<img src="$1" alt="{alt}"{width}{height} class="bbc_img resized" />',
'validate' => function (&$tag, &$data, $disabled)
{
global $image_proxy_enabled, $image_proxy_secret, $boardurl;

$data = strtr($data, array('<br>' => ''));
if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0)
$data = 'http://' . $data;

if (substr($data, 0, 8) != 'https://' && $image_proxy_enabled)
$data = $boardurl . '/proxy.php?request=' . urlencode($data) . '&hash=' . md5($data . $image_proxy_secret);
},
Replace With: Select
'content' => '<img src="$1" alt="{alt}"{width}{height} class="bbc_img resized" />',
'validate' => function (&$tag, &$data, $disabled)
{
$data = strtr($data, array('<br>' => ''));
if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0)
$data = 'http://' . $data;

$data = get_proxied_url($data);
},
Find: Select
'content' => '<img src="$1" alt="" class="bbc_img" />',
'validate' => function (&$tag, &$data, $disabled)
{
global $image_proxy_enabled, $image_proxy_secret, $boardurl;

$data = strtr($data, array('<br>' => ''));
if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0)
$data = 'http://' . $data;

if (substr($data, 0, 8) != 'https://' && $image_proxy_enabled)
$data = $boardurl . '/proxy.php?request=' . urlencode($data) . '&hash=' . md5($data . $image_proxy_secret);
},
Replace With: Select
'content' => '<img src="$1" alt="" class="bbc_img" />',
'validate' => function (&$tag, &$data, $disabled)
{
$data = strtr($data, array('<br>' => ''));
if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0)
$data = 'http://' . $data;

$data = get_proxied_url($data);
},
Find: Select
'tag' => 'url',
'type' => 'unparsed_content',
'content' => '<a href="$1" class="bbc_link" target="_blank">$1</a>',
Replace With: Select
'tag' => 'url',
'type' => 'unparsed_content',
'content' => '<a href="$1" class="bbc_link" target="_blank" rel="noopener noreferrer">$1</a>',
Find: Select
'tag' => 'url',
'type' => 'unparsed_equals',
'before' => '<a href="$1" class="bbc_link" target="_blank">',
Replace With: Select
'tag' => 'url',
'type' => 'unparsed_equals',
'before' => '<a href="$1" class="bbc_link" target="_blank" rel="noopener noreferrer">',
Find: Select
global $user_info, $user_settings, $context, $modSettings, $settings, $topic, $board, $smcFunc, $sourcedir
Add After: Select
, $remember_old_url;

$remember_old_url = true
Find: Select
global $context, $settings, $modSettings, $txt, $smcFunc
Add After: Select
, $remember_old_url
Find: Select
if (strpos($_SERVER['REQUEST_URL'], 'action=dlattach') === false && strpos($_SERVER['REQUEST_URL'], 'action=viewsmfile') === false)
Replace With: Select
// !!! $remember_old_url is set in writeLog().
if (!empty($remember_old_url))
Find: Select
if (substr($user_info['avatar']['url'], 0, 8) != 'https://' && $image_proxy_enabled)
$context['user']['avatar']['href'] = $boardurl . '/proxy.php?request=' . urlencode($user_info['avatar']['url']) . '&hash=' . md5($user_info['avatar']['url'] . $image_proxy_secret);
else
$context['user']['avatar']['href'] = $user_info['avatar']['url'];
Replace With: Select
$context['user']['avatar']['href'] = get_proxied_url($user_info['avatar']['url']);
Find: Select
// Start up the session URL fixer.
ob_start('ob_sessrewrite');
Add After: Select

ob_start(function ($buffer) {
global $context;
if (!$context['user']['is_guest'])
return $buffer;

return preg_replace_callback('~(<form[^<]+action=login2(.+))</form>~iUs' . (!empty($context['utf8']) ? 'u' : ''), function($m) use ($context) {
$repl = '';

if (strpos($m[0], $context['session_var']) === false)
$repl .= '<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '"/>';

return $m[1] . $repl . '</form>';
}, $buffer);
});
Find: Select
// No point in doing anything else, if the log isn't even enabled.
if (empty($modSettings['modlog_enabled']) || !isset($log_types[$log_type]))
Replace With: Select
// No point in doing anything else, if the log isn't even enabled.
// ...except in certain special cases:
$always_log = !empty($modSettings['force_gdpr']) ? array('agreement_accepted', 'policy_accepted', 'agreement_updated', 'policy_updated') : array();
if ((empty($modSettings['modlog_enabled']) && !in_array($action, $always_log)) || !isset($log_types[$log_type]))
Find: Select
$modSettings['rand_seed'] = microtime() * 1000000;
Replace With: Select
$modSettings['rand_seed'] = (double) microtime() * 1000000;
Find: Select

// Strips out invalid html entities, replaces others with html style &#123; codes.
Add Before: Select

/**
* Converts html entities to utf8 equivalents
* special db wrapper for mysql based on the limitation of mysql/mb3
*
* Callback function for preg_replace_callback
* Uses capture group 1 in the supplied array
* Does basic checks to keep characters inside a viewable range.
*
* @param array $matches An array of matches (relevant info should be the 2nd item in the array)
* @return string The fixed string or return the old when limitation of mysql is hit
*/
function fixchardb__callback($matches)
{
global $db_type;
if (!isset($matches[1]))
return '';
$num = $matches[1][0] === 'x' ? hexdec(substr($matches[1], 1)) : (int) $matches[1];
// it's to big for mb3?
if ($num > 0xFFFF && $db_type == 'mysql')
return $matches[0];
else
return fixchar__callback($matches);
}
Find (at the end of the file): Select
?>
Add Before: Select


/**
* Gets the appropriate URL to use for images (or whatever) when using SSL
*
* The returned URL may or may not be a proxied URL, depending on the situation.
* Mods can implement alternative proxies using the 'integrate_proxy' hook.
*
* @param string $url The original URL of the requested resource
* @return string The URL to use
*/
function get_proxied_url($url)
{
global $boardurl, $image_proxy_enabled, $image_proxy_secret, $user_info;

// Only use the proxy if enabled, and never for robots
if (empty($image_proxy_enabled) || !empty($user_info['possibly_robot']))
return $url;

$parsedurl = parse_url($url);

// Don't bother with HTTPS URLs, schemeless URLs, or obviously invalid URLs
if (empty($parsedurl['scheme']) || empty($parsedurl['host']) || empty($parsedurl['path']) || $parsedurl['scheme'] === 'https')
return $url;

// We don't need to proxy our own resources
if ($parsedurl['host'] === parse_url($boardurl, PHP_URL_HOST))
return strtr($url, array('http://' => 'https://'));

// By default, use SMF's own image proxy script
$proxied_url = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($url) . '&hash=' . hash_hmac('sha1', $url, $image_proxy_secret);

// Allow mods to easily implement an alternative proxy
call_integration_hook('integrate_proxy', array($url, &$proxied_url));

return $proxied_url;
}

./Sources/Themes.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.13
Replace With: Select
* @version 2.0.16
Find: Select
if (isset($_GET['th']) && $_GET['th'] == 0)
$_GET['th'] = $modSettings['theme_guests'];

updateMemberData((int) $_REQUEST['u'], array('id_theme' => (int) $_GET['th']));

if (!empty($_GET['vrt']))
{
Replace With: Select
// An identifier of zero means that the user wants the forum default theme.
updateMemberData((int) $_REQUEST['u'], array('id_theme' => (int) $_GET['th']));

if (!empty($_GET['vrt']))
{
// Set the identifier to the forum default.
if (isset($_GET['th']) && $_GET['th'] == 0)
$_GET['th'] = $modSettings['theme_guests'];
Find: Select
<website>http://www.simplemachines.org/</website>
Replace With: Select
<website>https://www.simplemachines.org/</website>

./Sources/Who.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.12
Replace With: Select
* @version 2.0.16
Find: Select

'Michael &quot;Oldiesmann&quot; Eshom',
Add Before: Select

'Michele &quot;Illori&quot; Davis',
// Former Project Managers
'Jessica &quot;Suki&quot; Gonz&aacute;lez',
'Will &quot;Kindred&quot; Wagner',
Find: Select

'Norv',
Add Before: Select

// Lead Developer
'Jon &quot;Sesquipedalian&quot; Stovell',
// Developers
'John &quot;live627&quot; Rayes',
'Jeremy &quot;SleePy&quot; Darwood',
'Shawn Bulen',

// Former Developers
Find: Select

'Hendrik Jan &quot;Compuart&quot; Visser',
Add Before: Select

'Colin Schoen',
'emanuele',
Find: Select
$user_info['is_admin'] ? 'Matt &quot;Grudge&quot; Wolf': 'Grudge',
Add After: Select

'Michael &quot;Oldiesmann&quot; Eshom',
Find: Select

'JimM',
Add Before: Select

// Lead Support Specialist
'Aleksi "Lex" Kilpinen',
// Support Specialists
'br360',
'GigaWatt',
'Will &quot;Kindred&quot; Wagner',
'Steve',
'ziycon',

// Former Support Specialists
Find: Select

'Chas Large',
Add Before: Select

'Chalky',
Find: Select

'gbsothere',
Add Before: Select

'Gary M. Gadsdon',
Find: Select

'Jessica &quot;Miss All Sunday&quot; Gonzales',
Replace With: Select

'Justyne',
Find: Select
'Kill Em All',
Add After: Select

'lurkalot',
'margarett',
Find: Select

'Brad &quot;IchBin&trade;&quot; Grow',
Add Before: Select

// Lead Customizer
'Sami &quot;SychO&quot; Mazouz',
// Customizers
'Diego Andr&eacute;s',
'Gary M. Gadsdon',
'Jonathan &quot;vbgamer45&quot; Valentin',

// Former Customizers
Find: Select
'Bryan &quot;Runic&quot; Deakin',
Add After: Select

'Bugo',
Find: Select

'Gary M. Gadsdon',
'Jason &quot;JBlaze&quot; Clemons',
Replace With: Select

'Gwenwyfar',
'Jason &quot;JBlaze&quot; Clemons',
Find: Select

'Jerry',
'Jonathan &quot;vbgamer45&quot; Valentin',
Replace With: Select

'Jerry',
'Joker&trade;',
Find: Select
'Matthew &quot;Labradoodle-360&quot; Kerle',
Add After: Select

'Mick.',
'NanoSector',
'nend',
Find: Select
'Peter &quot;Arantor&quot; Spicer',
Add After: Select

'Ricky.',
Find: Select

'Joshua &quot;groundup&quot; Dickerson',
Add Before: Select

// Doc Coordinator
'Irisado',
// Doc Writers

// Former Doc Writers
Find: Select
'AngellinaBelle',
Add After: Select

'Chainy',
Find: Select

'Kindred',
Replace With: Select

// Marketing Coordinator

// Marketing

// Former Marketing
'Will &quot;Kindred&quot; Wagner',
Find: Select
'Tony Reid',
Add After: Select

'Mert &quot;Antes&quot; Al&#x0131;nbay',
Find: Select

'Relyana',
Add Before: Select

// Lead Localizer
'Francisco &quot;d3vcho&quot; Domínguez',
// Localizers
'Nikola &quot;Dzonny&quot; Novaković',
// Former Localizers
Find: Select
'Relyana',
Add After: Select

'Robert.',
Find: Select

'Liroy &quot;CoreISP&quot; van Hoewijk',
Replace With: Select

'Michael Johnson',
'Liroy van Hoewijk',
Find: Select
'David Recordon',
),
),
Add After: Select

array(
'title' => $txt['credits_in_memoriam'],
'members' => array(
'Crip',
'K@',
'metallica48423',
'Paul_Pauline',
),
),

./Themes/default/Admin.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select

document.getElementById("switch_" + itemID).title = itemValueHandle.value == 1 ? \'', $txt['core_settings_switch_off'], '\' : \'', $txt['core_settings_switch_on'], '\';
Replace With: Select

document.getElementById("switch_" + itemID).title = itemValueHandle.value == 1 ? \'', $txt['core_settings_switch_off'], '\' : \'', $txt['core_settings_switch_on'], '\';';
Find: Select

// Don\'t reload.
Add Before: Select

if (!empty($context['show_privacy_policy_warning']))
echo '
if (itemID == "gdpr" && itemValueHandle.value == 1) {
alert("' . $txt['core_settings_privacy_policy_warning'] . '");
}';

echo '

./Themes/default/Display.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank"
Add After: Select
rel="noopener noreferrer"

./Themes/default/Login.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.15
Replace With: Select
* @version 2.0.16
Find: Select

<span class="lowerframe"><span></span></span>
<input type="hidden" name="hash_passwrd" value="" />
Replace With: Select

<span class="lowerframe"><span></span></span>
<input type="hidden" name="hash_passwrd" value="" /><input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />

./Themes/default/ManageNews.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
function template_email_members()
{
global $context, $settings, $options, $txt, $scripturl
Add After: Select
, $modSettings
Find: Select
<input type="text" name="exclude_members" id="exclude_members" value="" size="30" class="input_text" />
<span id="exclude_members_container"></span>
</dd>
</dl>
Add After: Select
';

if (empty($modSettings['force_gdpr']))
echo '
Find: Select

<dt>
<strong>', $txt['admin_news_select_email'], ':</strong><br />
<span class="smalltext">', $txt['admin_news_select_email_desc'], '</span>
</dt>
Add Before: Select
';

if (empty($modSettings['force_gdpr']))
echo '
Find: Select

<dd>
<textarea name="emails" rows="5" cols="30" style="' . ($context['browser']['is_ie8'] ? 'width: 635px; max-width: 98%; min-width: 98%' : 'width: 98%') . ';"></textarea>
</dd>
Add After: Select
';

echo '
Find: Select
<dd>
<input type="checkbox" name="email_force" id="email_force" value="1" class="input_check" />
</dd>
</dl>
Add After: Select
';

echo '

./Themes/default/Memberlist.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
'<a href="' . $member['website']['url'] . '" target="_blank"
Add After: Select
rel="noopener noreferrer"

./Themes/default/Notify.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
href="', $scripturl, '?action=notify;sa=', $context['notification_set'] ? 'off' : 'on', ';topic=', $context['current_topic'], '.', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '"
Replace With: Select
href="', $scripturl, '?action=notify;sa=', $context['notification_set'] ? 'off' : 'on', ';topic=', $context['current_topic'], '.', $context['start'], ';', (!empty($context['notify_info']['token']) ? 'u=' . $context['notify_info']['u'] . ';token=' . $context['notify_info']['token'] : $context['session_var'] . '=' . $context['session_id']), '"
Find: Select
href="', $scripturl, '?action=notifyboard;sa=', $context['notification_set'] ? 'off' : 'on', ';board=', $context['current_board'], '.', $context['start'], ';', $context['session_var'], '=', $context['session_id'], '"
Replace With: Select
href="', $scripturl, '?action=notifyboard;sa=', $context['notification_set'] ? 'off' : 'on', ';board=', $context['current_board'], '.', $context['start'], ';', (!empty($context['notify_info']['token']) ? 'u=' . $context['notify_info']['u'] . ';token=' . $context['notify_info']['token'] : $context['session_var'] . '=' . $context['session_id']), '"
Find (at the end of the file): Select
?>
Add Before: Select

function template_notify_announcements()
{
global $context, $settings, $options, $txt, $scripturl;

echo '
<div class="cat_bar">
<h3 class="catbg">
<span class="ie6_header floatleft"><img src="', $settings['images_url'], '/email_sm.gif" alt="" class="icon" />', $txt['notify'], '</span>
</h3>
</div>
<span class="upperframe"><span></span></span>
<div class="roundframe centertext">
<p>', $txt['notifyannouncements_prompt'], '</p>
<p>
<strong><a href="', $scripturl, '?action=notifyannouncements;sa=on;', (!empty($context['notify_info']['token']) ? 'u=' . $context['notify_info']['u'] . ';token=' . $context['notify_info']['token'] : $context['session_var'] . '=' . $context['session_id']), '">', $txt['yes'], '</a> - <a href="', $scripturl, '?action=notifyannouncements;sa=off;', (!empty($context['notify_info']['token']) ? 'u=' . $context['notify_info']['u'] . ';token=' . $context['notify_info']['token'] : $context['session_var'] . '=' . $context['session_id']), '">', $txt['no'], '</a></strong>
</p>
</div>
<span class="lowerframe"><span></span></span>';
}

function template_notify_pref_changed()
{
global $context, $settings, $options, $txt, $scripturl;

echo '
<div class="cat_bar">
<h3 class="catbg">
<span class="ie6_header floatleft"><img src="', $settings['images_url'], '/email_sm.gif" alt="" class="icon" />', $txt['notify'], '</span>
</h3>
</div>
<span class="upperframe"><span></span></span>
<div class="roundframe centertext">
<p>', $context['notify_success_msg'], '</p>
</div>
<span class="lowerframe"><span></span></span>';
}

./Themes/default/PersonalMessage.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select

criteriaNum++
Replace With: Select


if (criteriaNum++ >= ', $context['rule_limiters']['criteria'], ')
return false;
Find: Select
echo '<\' + \'/select><\' + \'/span><span id="criteriaAddHere"><\' + \'/span>\');
Add After: Select


if (criteriaNum + 1 > ', $context['rule_limiters']['criteria'], ')
document.getElementById(\'addonjs1\').style.display = \'none\';
Find: Select

actionNum++
Replace With: Select

if (actionNum++ >= ', $context['rule_limiters']['actions'], ')
return false;
Find: Select
echo '<\' + \'/select><\' + \'/span><span id="actionAddHere"><\' + \'/span>\');
Add After: Select


if (actionNum + 1 > ', $context['rule_limiters']['actions'], ')
document.getElementById(\'addonjs2\').style.display = \'none\';
Find: Select

echo '
document.getElementById("addonjs1").style.display = "";
Replace With: Select

if (count($context['rule']['criteria']) <= $context['rule_limiters']['criteria'])
echo '
document.getElementById("addonjs1").style.display = "";';

if (count($context['rule']['actions']) <= $context['rule_limiters']['actions'])
echo '

./Themes/default/Profile.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.14
Replace With: Select
* @version 2.0.16
Find: Select
href="', $context['member']['website']['url'], '" title="' . $context['member']['website']['title'] . '" target="_blank"
Add After: Select
rel="noopener noreferrer"

./Themes/default/Register.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
<form action="', $scripturl, '?action=register" method="post" accept-charset="', $context['character_set'], '" id="registration">
Add After: Select
';

if (!empty($context['agreement']))
echo '
Find: Select
<p>', $context['agreement'], '</p>
</div>
<span class="lowerframe"><span></span></span>
Replace With: Select
<div>', $context['agreement'], '</div>
</div>
<span class="lowerframe"><span></span></span>';

if (!empty($context['policy']))
echo '
<div class="cat_bar">
<h3 class="catbg">', $txt['privacy_policy'], '</h3>
</div>
<span class="upperframe"><span></span></span>
<div class="roundframe">
<div>', $context['policy'], '</div>
</div>
<span class="lowerframe"><span></span></span>';

echo '
Find: Select
<input type="submit" name="accept_agreement" value="', $txt['agreement_agree'], '" class="button_submit" />
Replace With: Select
<input type="submit" name="accept_agreement" value="', $txt['agreement' . ($context['require_policy_agreement'] ? '_policy' : '') . '_agree'], '" class="button_submit" />
Find: Select
class="input_text openid_login" />
</dd>
</dl>';

}
Add After: Select


if (!empty($context['announcements_ask']))
echo '
<dl class="register_form" id="notify_announcements_group">
<dt><strong><label for="notify_announcements">', $txt['notify_announcements'], ':</label></strong></dt>
<dd>
<input type="checkbox" name="notify_announcements" id="notify_announcements" tabindex="', $context['tabindex']++, '" class="input_check"', (!empty($context['notify_announcements']) ? ' checked="checked"' : ''), ' />
</dd>
</dl>';
Find: Select
<input type="checkbox" name="emailActivate" id="emailActivate_check" tabindex="', $context['tabindex']++, '"', !empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1 ? ' checked="checked"' : '', ' onclick="onCheckChange();" class="input_check" />
</dd>
Add After: Select

<dt>
<strong><label for="requireAgreement">', $txt['admin_register_require_agreement'], ':</label></strong>
</dt>
<dd>
<input type="checkbox" name="requireAgreement" id="requireAgreement_check" tabindex="', $context['tabindex']++, '"', !empty($modSettings['requireAgreement']) || !empty($modSettings['force_gdpr']) ? ' checked="checked"' : '', !empty($modSettings['force_gdpr']) ? ' disabled="disabled"' : '', ' class="input_check" />', !empty($modSettings['force_gdpr']) ? '
<input type="hidden" name="requireAgreement" value=1>' : '', '
</dd>
<dt>
<strong><label for="requirePolicyAgreement">', $txt['admin_register_require_policy'], ':</label></strong>
</dt>
<dd>
<input type="checkbox" name="requirePolicyAgreement" id="requirePolicyAgreement_check" tabindex="', $context['tabindex']++, '"', !empty($modSettings['requirePolicyAgreement']) || !empty($modSettings['force_gdpr']) ? ' checked="checked"' : '', !empty($modSettings['force_gdpr']) ? ' disabled="disabled"' : '', ' class="input_check" />', !empty($modSettings['force_gdpr']) ? '
<input type="hidden" name="requirePolicyAgreement" value=1>' : '', '
</dd>
Find: Select
<p>
<label for="requireAgreement"><input type="checkbox" name="requireAgreement" id="requireAgreement"', $context['require_agreement'] ? ' checked="checked"' : '', ' tabindex="', $context['tabindex']++, '" value="1" class="input_check" /> ', $txt['admin_agreement'], '.</label>
</p>
<div class="righttext">
<input type="submit" value="', $txt['save'], '" tabindex="', $context['tabindex']++, '" class="button_submit" />
Replace With: Select
<div class="information">
<span>', $context['agreement_info'], '</span>
</div>
<div class="righttext">', empty($context['force_gdpr']) ? '
<label for="minor_edit"><input type="checkbox" value="" id="minor_edit" name="minor_edit" tabindex="' . $context['tabindex']++ . '" class="input_check" /> ' . $txt['admin_agreement_minor_edit'] . '</label>' : '', '
<input type="submit" value="', $txt['save'], '" tabindex="', $context['tabindex']++, '" class="button_submit" onclick="return resetAgreementConfirm()" />
Find: Select
<input type="hidden" name="sa" value="agreement" />
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
Add After: Select

<script>
function resetAgreementConfirm()
{
if (document.getElementById("minor_edit").checked)
return true;
else if (document.getElementById(\'agreement\').value != ' . JavaScriptEscape(un_htmlspecialchars($context['agreement'])) . ')
return confirm(' . JavaScriptEscape($txt['reset_agreement_desc']) . ');
}
</script>
Find (at the end of the file): Select
?>
Add Before: Select

// Form for editing the privacy policy shown to people registering to the forum.
function template_edit_privacy_policy()
{
global $context, $settings, $options, $scripturl, $txt;

// Just a big box to edit the text file ;).
echo '
<div class="cat_bar">
<h3 class="catbg">', $txt['privacy_policy'], '</h3>
</div>';

echo '
<div class="windowbg2" id="privacy_policy">
<span class="topslice"><span></span></span>
<div class="content">';

// Is there more than one language to choose from?
if (count($context['editable_policies']) > 1)
{
echo '
<div class="information">
<form action="', $scripturl, '?action=admin;area=regcenter" id="change_policy" method="post" accept-charset="', $context['character_set'], '" style="display: inline;">
<strong>', $txt['admin_agreement_select_language'], ':</strong>&nbsp;
<select name="policy_lang" onchange="document.getElementById(\'change_policy\').submit();" tabindex="', $context['tabindex']++, '">';

foreach ($context['editable_policies'] as $lang => $name)
echo '
<option value="', $lang, '" ', $context['current_policy_lang'] == $lang ? 'selected="selected"' : '', '>', $name, '</option>';

echo '
</select>
<div class="righttext">
<input type="hidden" name="sa" value="policy" />
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
<input type="submit" name="change" value="', $txt['admin_agreement_select_language_change'], '" tabindex="', $context['tabindex']++, '" class="button_submit" />
</div>
</form>
</div>';
}

echo '
<form action="', $scripturl, '?action=admin;area=regcenter" method="post" accept-charset="', $context['character_set'], '">';

// Show the actual policy in an oversized text box.
echo '
<p class="policy">
<textarea cols="70" rows="20" name="policy" id="agreement">', $context['policy'], '</textarea>
</p>
<div class="information">
<span>', $context['policy_info'], '</span>
</div>
<div class="righttext">', empty($context['force_gdpr']) ? '
<label for="minor_edit"><input type="checkbox" value="" id="minor_edit" name="minor_edit" tabindex="' . $context['tabindex']++ . '" class="input_check" /> ' . $txt['admin_agreement_minor_edit'] . '</label>' : '', '
<input type="submit" value="', $txt['save'], '" tabindex="', $context['tabindex']++, '" class="button_submit" onclick="return resetPolicyConfirm()" />
<input type="hidden" name="policy_lang" value="', $context['current_policy_lang'], '" />
<input type="hidden" name="sa" value="policy" />
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
<script>
function resetPolicyConfirm()
{
if (document.getElementById("minor_edit").checked)
return true;
else if (document.getElementById(\'agreement\').value != ' . JavaScriptEscape(un_htmlspecialchars($context['policy'])) . ')
return confirm(' . JavaScriptEscape($txt['reset_privacy_policy_desc']) . ');
}
</script>
</div>
</form>
</div>
<span class="botslice"><span></span></span>
</div>
<br class="clear" />';
}

./Themes/default/Wireless.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
Find: Select
function template_wap_boardindex()
{
global $context, $settings, $options, $scripturl
Add After: Select
, $txt
Find: Select
<p><a href="', $scripturl, '?board=', $board['id'], '.0;wap">', $board['name'], '</a><br /></p>';

echo '
</card>';
}
Add After: Select


echo '
<card>
<p', $txt['wireless_options'], '</p>';
if ($context['user']['is_guest'])
echo '
<p><a href="', $scripturl, '?action=login;wap">', $txt['wireless_options_login'], '</a></p>';
else
{
if ($context['allow_pm'])
echo '
<p><a href="', $scripturl, '?action=pm;wap">', empty($context['user']['unread_messages']) ? $txt['wireless_pm_inbox'] : sprintf($txt['wireless_pm_inbox_new'], $context['user']['unread_messages']), '</a></p>';
echo '
<p><a href="', $scripturl, '?action=unread;wap">', $txt['wireless_recent_unread_posts'], '</a></p>
<p><a href="', $scripturl, '?action=unreadreplies;wap">', $txt['wireless_recent_unread_replies'], '</a></p>
<p><a href="', $scripturl, '?action=logout;', $context['session_var'], '=', $context['session_id'], ';wap">', $txt['wireless_options_logout'], '</a></p>
</card>';
}
Find: Select
<postfield name="user" value="$user" />
<postfield name="passwrd" value="$passwrd" />
<postfield name="cookieneverexp" value="1" />
Add After: Select

<postfield name="', $context['session_var'], '" value="', $context['session_id'], '" />
Find: Select
<tr><td><input type="submit" value="', $txt['login'], '" class="button_submit" /><input type="hidden" name="cookieneverexp" value="1" />
Add After: Select
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
Find: Select
<p class="windowbg"><input type="submit" value="', $txt['login'], '" class="button_submit" /><input type="hidden" name="cookieneverexp" value="1" />
Add After: Select
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />

./Themes/default/Xml.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
foreach ($year['months'] as $month);
Replace With: Select
foreach ($year['months'] as $month)

./Themes/default/scripts/script.js

Find: Select
var n, sReturn = '';

for (var i = 0, iTextLen = this.length; i < iTextLen; i++)
{
n = this.charCodeAt(i);
if (n < 128)
sReturn += String.fromCharCode(n)
else if (n < 2048)
sReturn += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63);
else if (n < 65536)
sReturn += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63);
else
sReturn += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63);
}

return sReturn;
Replace With: Select
return this;
Find: Select

String.prototype.php_urlencode = function()
{
Add After: Select
if (smf_charset == 'UTF-8')
return encodeURIComponent(this);

Find: Select
if (oCurSelection.setBaseAndExtent)
Add Before: Select
if (oCurSelection.selectAllChildren)
{
oCurSelection.selectAllChildren(oCodeArea);
}
else

./Themes/core/Display.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank"
Add After: Select
rel="noopener noreferrer"

./Themes/core/Memberlist.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
<a href="' . $member['website']['url'] . '" target="_blank"
Add After: Select
rel="noopener noreferrer"

./Themes/core/PersonalMessage.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0.2
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank"
Add After: Select
rel="noopener noreferrer"

./Themes/core/index.template.php

This operation isn't vital to the installation of this mod.
Find: Select
* @version 2.0
Replace With: Select
* @version 2.0.16
This operation isn't vital to the installation of this mod.
Find: Select
<input type="hidden" name="hash_passwrd" value="" />
Add After: Select
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />

./Themes/core/theme_info.xml

This operation isn't vital to the installation of this mod.
Find: Select
<website>http://www.simplemachines.org/</website>
Replace With: Select
<website>https://www.simplemachines.org/</website>

Code

auto_1.php
This file should not be able to execute standalone.You may have to run the following queries manually.
Query: Select
<?php
define('REQUIRED_PHP_VERSION', '5.3.0');
if (version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<'))
fatal_error('This update requires a minimum of PHP ' . REQUIRED_PHP_VERSION . ' in order to function. (You are currently running PHP ' . PHP_VERSION . ')');
foreach (get_included_files() as $settingsFile)
if (basename($settingsFile) === 'Settings.php' && !is_writable($settingsFile))
fatal_error('Settings.php must be writable to install this update.');
?>

File Operations

Move the included file "Agreement.php" to "./Sources".
Move the included file "Agreement.template.php" to "./Themes/default".
Move the included file "Agreement.english.php" to "./Themes/default/languages".
Move the included file "feature_gdpr.png" to "./Themes/default/images/admin".
Advertisement: