Advertisement:

Navigation

Readme

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

./index.php

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

Operation #2
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');


Operation #3
Find: [Select]
$forum_version = 'SMF 2.0.15';
Replace With: [Select]
$forum_version = 'SMF 2.0.16';

Operation #4
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);
});

Operation #5
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'),

Operation #6
Find: [Select]
'notifyboard' => array('Notify.php', 'BoardNotify'),
Add After: [Select]

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

./proxy.php

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

Operation #2
Find: [Select]
// Try to create the image cache directory if it doesn't exist
if (!file_exists($this->cache))
Add After: [Select]

{

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

}

Operation #4
Find: [Select]
if (empty($_GET['hash']) || empty($_GET['request'])
Add After: [Select]
 || ($_GET['request'] === 'http:') || ($_GET['request'] === 'https:')

Operation #5
Find: [Select]
if (md5($request . $this->secret) != $hash)
Replace With: [Select]
if (hash_hmac('sha1', $request, $this->secret) != $hash)

Operation #6
Find: [Select]
if (!$this->isCached($request)) {
return $this->cacheImage($request);
}
Replace With: [Select]
if (!$this->isCached($request))
return $this->cacheImage($request);

Operation #7
Find: [Select]
	

/**
* Serves the request
Replace With: [Select]


/**
* Serves the request

Operation #8
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');

Operation #9
Find: [Select]
if (!$response) {
header('Location: ' . $request, false, 301);
}
Replace With: [Select]
if ($response === false)
header('Location: ' . $request, false, 301);

Operation #10
Find: [Select]
// Is the cache expired?
Add After: [Select]
 Try to refresh it.

Operation #11
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;
}


Operation #12
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);

Operation #13
Find: [Select]
* Returns the request's hashed filepath
*
* @access public
Replace With: [Select]
* Returns the request's hashed filepath
*
* @access protected

Operation #14
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.

Operation #15
Find: [Select]
		
if (empty($response)) {
return false;
}

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

if (empty($response) || $responseCode != 200)
return null;

Operation #16
Find: [Select]
if ($contentParts[0] != 'image')
return false;
Replace With: [Select]
if ($contentParts[0] != 'image')
return null;

Operation #17
Find: [Select]
))) === false ? 1 : null;;
Replace With: [Select]
))) !== false;

Operation #18
Find: [Select]
$proxy->serve();
Add After: [Select]


?>

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

./SSI.php

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

Operation #2
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

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

Operation #2
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.';

Operation #3
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.';

Operation #4
Find: [Select]
href="http://www.simplemachines.org/community/index.php"
Replace With: [Select]
href="https://www.simplemachines.org/community/index.php"

Operation #5
Find: [Select]
href="http://www.simplemachines.org/"
Replace With: [Select]
href="https://www.simplemachines.org/"

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

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

Operation #2
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.

Operation #3
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(

Operation #4
Find: [Select]
http://www.simplemachines.org/community/?action=profile;u=2676
Replace With: [Select]
https://www.simplemachines.org/community/?action=profile;u=2676

Operation #5
Find: [Select]
http://www.simplemachines.org/community/?action=profile;u=63186
Replace With: [Select]
https://www.simplemachines.org/community/?action=profile;u=63186

Operation #6
Find: [Select]
http://www.simplemachines.org/community/?action=profile;u=46625
Replace With: [Select]
https://www.simplemachines.org/community/?action=profile;u=46625

Operation #7
Find: [Select]
http://www.simplemachines.org/community/?action=profile;u=72038
Replace With: [Select]
https://www.simplemachines.org/community/?action=profile;u=72038

Operation #8
Find: [Select]
http://www.simplemachines.org/community/?action=profile;u=48671
Replace With: [Select]
https://www.simplemachines.org/community/?action=profile;u=48671

Operation #9
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

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

Operation #2
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

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

Operation #2
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>.

Operation #3
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>.';

Operation #4
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.';

Operation #5
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>.';

Operation #6
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

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

Operation #2
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!

Operation #3
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">

Operation #4
Find: [Select]
[url=http://www.simplemachines.org/community/index.php]
Replace With: [Select]
[url=https://www.simplemachines.org/community/index.php]

Operation #5
Find: [Select]
http://www.simplemachines.org/about/stats.php
Replace With: [Select]
https://www.simplemachines.org/about/stats.php

Operation #6
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>';

Operation #7
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 />

Operation #8
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>

Operation #9
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

Operation #10
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>

Operation #11
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

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

Operation #2
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.';

Operation #3
Find: [Select]

$txt['admin_register']
Add Before: [Select]

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

Operation #4
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

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

Operation #2
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>';

Operation #3
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.';

Operation #4
Find: [Select]

$txt['securityDisable']
Add Before: [Select]

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

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

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

Operation #2
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.';

Operation #3
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

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

Operation #2
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

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

Operation #2
Find: [Select]
// Version: 2.0.14;
Replace With: [Select]
// Version: 2.0.16;
This operation isn't vital to the installation of this mod.

Operation #3
Find: [Select]

$txt['notify']
Add Before: [Select]

$txt['terms_and_policies'] = 'Terms and Policies';

Operation #4
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>';

Operation #5
Find: [Select]
SMF &copy; 2017
Replace With: [Select]
SMF &copy; 2019

Operation #6
Find: [Select]
"http://www.simplemachines.org/about/smf/license.php"
Replace With: [Select]
"https://www.simplemachines.org/about/smf/license.php"

Operation #7
Find: [Select]
"http://www.simplemachines.org"
Replace With: [Select]
"https://www.simplemachines.org"

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

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

Operation #1
Find: [Select]
// Version: 2.0; Who
Replace With: [Select]
// Version: 2.0.16; Who
This operation isn't vital to the installation of this mod.

Operation #2
Find: [Select]
$txt['credits_groups_orignal_pm'] = 'Original Project Managers';
Add After: [Select]

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

./Sources/Admin.php

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

Operation #2
Find: [Select]
strip_tags($helptxt[$item2])
Replace With: [Select]
strip_tags($helptxt[$item[2]])

Operation #3
Find: [Select]

'reservednames' =>
Add Before: [Select]

'policy' => array($txt['privacy_policy'], 'admin_forum'),

Operation #4
Find: [Select]
'http://wiki.simplemachines.org/'
Replace With: [Select]
'https://wiki.simplemachines.org/'

Operation #5
Find: [Select]
'http://wiki.simplemachines.org/smf/features2'
Replace With: [Select]
'https://wiki.simplemachines.org/smf/features2'

Operation #6
Find: [Select]
'http://wiki.simplemachines.org/smf/options2'
Replace With: [Select]
'https://wiki.simplemachines.org/smf/options2'

Operation #7
Find: [Select]
'http://wiki.simplemachines.org/smf/themes2'
Replace With: [Select]
'https://wiki.simplemachines.org/smf/themes2'

Operation #8
Find: [Select]
'http://wiki.simplemachines.org/smf/packages2'
Replace With: [Select]
'https://wiki.simplemachines.org/smf/packages2'

Operation #9
Find: [Select]
'http://www.simplemachines.org/community/'
Replace With: [Select]
'https://www.simplemachines.org/community/'

Operation #10
Find: [Select]
'http://www.simplemachines.org/redirect/english_support'
Replace With: [Select]
'https://www.simplemachines.org/redirect/english_support'

Operation #11
Find: [Select]
'http://www.simplemachines.org/redirect/international_support_boards'
Replace With: [Select]
'https://www.simplemachines.org/redirect/international_support_boards'

Operation #12
Find: [Select]
'http://www.simplemachines.org/redirect/smf_support'
Replace With: [Select]
'https://www.simplemachines.org/redirect/smf_support'

Operation #13
Find: [Select]
'http://www.simplemachines.org/redirect/customize_support'
Replace With: [Select]
'https://www.simplemachines.org/redirect/customize_support'

./Sources/Class-CurlFetchWeb.php

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

Operation #2
Find: [Select]
?>
Replace With: [Select]
?>
This operation isn't vital to the installation of this mod.

./Sources/Errors.php

Operation #1
Find: [Select]
 * @version 2.0.4
Replace With: [Select]
 * @version 2.0.16
This operation isn't vital to the installation of this mod.

Operation #2
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

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

Operation #2
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();


Operation #3
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

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

Operation #2
Find: [Select]
$context['wiki_url'] = 'http://wiki.simplemachines.org/smf';
Replace With: [Select]
$context['wiki_url'] = 'https://wiki.simplemachines.org/smf';

./Sources/Load.php

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

Operation #2
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;

Operation #3
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']);

Operation #4
Find: [Select]
global $user_profile, $modSettings, $board_info, $smcFunc;
global $boardurl, $image_proxy_enabled, $image_proxy_secret
Add After: [Select]
, $user_info

Operation #5
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']);

Operation #6
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);
}

Operation #7
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

Operation #1
Find: [Select]
* @version 2.0.7
Replace With: [Select]
* @version 2.0.16
This operation isn't vital to the installation of this mod.

Operation #2
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.

Operation #3
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

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

Operation #2
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']))

Operation #3
Find: [Select]
// Finally - emails!
if (!empty($_POST['emails']))
Replace With: [Select]
// Finally - emails!
if (!empty($_POST['emails']) && empty($modSettings['force_gdpr']))

Operation #4
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}';
}


Operation #5
Find: [Select]

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

Operation #6
Find: [Select]

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

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

Operation #7
Find: [Select]

!empty($_POST['send_html']) ? '<a href="mailto:' . $email . '">' . $email . '</a>' : $email,
'??',
$email
Add After: [Select]
,
$unsubscribe_link,

Operation #8
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 = '';


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

./Sources/ManageRegistration.php

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

Operation #2
Find: [Select]
'agreement' => array('EditAgreement', 'admin_forum'),
Add After: [Select]

'policy' => array('EditPrivacyPolicy', 'admin_forum'),

Operation #3
Find: [Select]
			'agreement' => array(
'description' => $txt['registration_agreement_desc'],
),
Add After: [Select]

'policy' => array(
'description' => $txt['privacy_policy_desc'],
),

Operation #4
Find: [Select]
	global $txt, $context, $sourcedir, $scripturl, $smcFunc
Add After: [Select]
, $modSettings

Operation #5
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();

Operation #6
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']);

Operation #7
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'])

Operation #8
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'];

Operation #9
Find: [Select]
global $txt, $context, $scripturl, $modSettings, $sourcedir;
Add After: [Select]

global $language, $boarddir;

Operation #10
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]);

Operation #11
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' : ''),

Operation #12
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']))),

Operation #13
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;

Operation #14
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

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

Operation #2
Find: [Select]
$context['fulltext_index'] = '';
Replace With: [Select]
$context['fulltext_index'] = array();

./Sources/ManageServer.php

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

Operation #2
Find: [Select]
global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename
Add After: [Select]
, $cookie_no_auth_secret

Operation #3
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'])),

Operation #4
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;


Operation #5
Find: [Select]
$config_bools = array(
'db_persist', 'db_error_send',
'maintenance', 'image_proxy_enabled',
Add After: [Select]

'cookie_no_auth_secret',

Operation #6
Find: [Select]
$url = 'http://download.simplemachines.org/
Replace With: [Select]
$url = 'https://download.simplemachines.org/

Operation #7
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)

Operation #8
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

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

Operation #2
Find: [Select]
function ModifyCoreFeatures($return_config = false)
{
global $txt, $scripturl, $context, $settings, $sc, $modSettings;
Add After: [Select]

global $language;

Operation #3
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\']);
'),
),

Operation #4
Find: [Select]
if ($context['is_new_install'])
updateSettings(array('admin_features' => ''));
Add After: [Select]

$context['show_privacy_policy_warning'] = empty($modSettings['policy_' . $language]);

Operation #5
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'])),

Operation #6
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

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

Operation #2
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();

Operation #3
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})

Operation #4
Find: [Select]
'twenty_four_hours_wait' => time() - $context['hoursdisable'] * 3600,
'moderate_log' => $context['log_type'],
Add After: [Select]

'uneditable' => $context['uneditable_actions'],

Operation #5
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})

Operation #6
Find: [Select]
'delete_actions' => array_unique($_POST['delete']),
'moderate_log' => $context['log_type'],
Add After: [Select]

'uneditable' => $context['uneditable_actions'],

Operation #7
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'] : '')

Operation #8
Find: [Select]

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

if (!isset($context['uneditable_actions']))
$context['uneditable_actions'] = array();

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

./Sources/News.php

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

Operation #2
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"');

Operation #3
Find: [Select]
if (!empty($cdata_override))
return $data;
Add After: [Select]

// Do we even need to do this?
if (strpbrk($data, '<>&') == false)
return $data;

Operation #4
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;

Operation #5
Find: [Select]
global $scripturl, $memberContext, $user_profile, $modSettings, $user_info
Add After: [Select]
, $smcFunc, $language

Operation #6
Find: [Select]
'language' => cdata_parse($profile['language']),
Replace With: [Select]
'language' => cdata_parse(!empty($profile['language']) ? $profile['language'] : $smcFunc['ucwords'](strtr($language, array('_' => ' ', '-utf8' => '')))),

Operation #7
Find: [Select]
<generator uri="http://www.simplemachines.org"
Replace With: [Select]
<generator uri="https://www.simplemachines.org"

./Sources/Notify.php

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

Operation #2
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

Operation #3
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;
}

Operation #4
Find: [Select]
LIMIT 1',
array(
'current_member' => $user_info['id'],
Replace With: [Select]
LIMIT 1',
array(
'current_member' => $member_info['id'],

Operation #5
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'],
);


Operation #6
Find: [Select]
checkSession('get');

// Attempt to turn notifications on.
Add Before: [Select]
if (empty($skipCheckSession))

Operation #7
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')
);

Operation #8
Find: [Select]
checkSession('get');

// Just turn notifications off.
Add Before: [Select]
if (empty($skipCheckSession))

Operation #9
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,
)
);

Operation #10
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;
}


Operation #11
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;
}

Operation #12
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'],

Operation #13
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'],
);


Operation #14
Find: [Select]
checkSession('get');

// Turn notification on. (note this just blows smoke if it's already on.)
Add Before: [Select]
if (empty($skipCheckSession))

Operation #15
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')
);

Operation #16
Find: [Select]
checkSession('get');

// Turn notification off for this board.
Add Before: [Select]
if (empty($skipCheckSession))

Operation #17
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'],
)
);

Operation #18
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;
}


Operation #19
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

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

Operation #2
Find: [Select]
// If we're in non-boring view do something exciting!
Add Before: [Select]
$context['labels'] = !empty($context['labels']) ? (array) $context['labels'] : array();


Operation #3
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']),

Operation #4
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']),

Operation #5
Find: [Select]
if ($set > 60)
Replace With: [Select]
if ($smcFunc['strlen']($set) > 60)

Operation #6
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,
);

Operation #7
Find: [Select]


ApplyRules(true);
Add Before: [Select]

spamProtection('pm');

Operation #8
Find: [Select]
// Let's do the criteria first - it's also hardest!
$criteria = array();
Add After: [Select]

$criteriaCount = 0;

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


Operation #10
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

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

Operation #2
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

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

Operation #2
Find: [Select]
'TOPICLINK' => $scripturl . '?topic=' . $topic . '.0',
Add After: [Select]

'UNSUBSCRIBELINK' => $scripturl . '?action=notifyannouncements',

Operation #3
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;
}


Operation #4
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

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

Operation #2
Find: [Select]
require_once($sourcedir . '/ManageAttachments.php');
Add After: [Select]

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

Operation #3
Find: [Select]
require_once($sourcedir . '/Subs-Package.php');

$url = parse_url($_POST['userpicpersonal']);
Replace With: [Select]
$url = parse_url($_POST['userpicpersonal']);

Operation #4
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);

Operation #5
Find: [Select]
// Should we check dimensions?
Add Before: [Select]
elseif (empty($mime_valid))
return 'bad_avatar';

Operation #6
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

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

Operation #2
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

Operation #1
Find: [Select]
 * @version 2.0.9
Replace With: [Select]
 * @version 2.0.16
This operation isn't vital to the installation of this mod.

Operation #2
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

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

Operation #2
Find: [Select]
loadTemplate('Recent');
$context['page_title'] = $txt['recent_posts'];
Add After: [Select]


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

./Sources/Register.php

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

Operation #2
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.
This operation isn't vital to the installation of this mod.

Operation #3
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']);
}

Operation #4
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'];
}
}

Operation #5
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']);

Operation #6
Find: [Select]
if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed']))
Replace With: [Select]
if ((!empty($modSettings['requireAgreement']) || !empty($modSettings['requirePolicyAgreement'])) && empty($_SESSION['registration_agreed']))

Operation #7
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

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

Operation #2
Find: [Select]
global $smcFunc, $modSettings, $sourcedir, $db_type
Add After: [Select]
, $image_proxy_enabled, $cachedir

Operation #3
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);
}


Operation #4
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

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

Operation #2
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), '\\\'') . '[[:>:]]';

Operation #3
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), '\\\'') . '[[:>:]]';

Operation #4
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

Operation #1
Find: [Select]
 * @version 2.0.3
Replace With: [Select]
 * @version 2.0.16
This operation isn't vital to the installation of this mod.

Operation #2
Find: [Select]
function validateSession()
Replace With: [Select]
function validateSession($force = false)

Operation #3
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())))

Operation #4
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

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

Operation #2
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

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

Operation #2
Find: [Select]
function setLoginCookie($cookie_length, $id, $password = '')
{
global $cookiename
Add After: [Select]
, $cookie_no_auth_secret

Operation #3
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

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

Operation #2
Find: [Select]
c.id_cat, c.name AS cat_name,' : '') . '
Replace With: [Select]
c.id_cat, c.name AS cat_name, cat_order,' : '') . '

Operation #3
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'],

Operation #4
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

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

Operation #2
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)

Operation #3
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)

Operation #4
Find: [Select]
'db_error'                  => 'mysqli_error',
Replace With: [Select]
'db_error'                  => array($this, 'show_error'),

Operation #5
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;

Operation #6
Find: [Select]
global $db_unbuffered, $db_callback, $modSettings;
Replace With: [Select]
global $db_unbuffered, $db_persist, $db_callback, $modSettings;

Operation #7
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

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

Operation #2
Find: [Select]
if (preg_match($exp, $match))
return 1;
Replace With: [Select]
if (preg_match($exp, $search))
return 1;
This operation isn't vital to the installation of this mod.

./Sources/Subs-Graphics.php

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

Operation #2
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;


Operation #3
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

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

Operation #2
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

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

Operation #2
Find: [Select]
'type_major' => !empty($parts[6]) ? (int) $parts[5] : 0,
Replace With: [Select]
'type_major' => !empty($parts[5]) ? (int) $parts[5] : 0,

Operation #3
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);

Operation #4
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

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

Operation #2
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'];

Operation #3
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...

Operation #4
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')

Operation #5
Find: [Select]
elseif (!server_parse('HELO ' . $modSettings['smtp_host'], $socket, '250'))
Replace With: [Select]
elseif (!server_parse('HELO ' . $helo, $socket, '250'))

Operation #6
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'))

Operation #7
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;
}

Operation #8
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

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

Operation #2
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

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

Operation #2
Find: [Select]
global $forum_copyright, $context, $boardurl,
Replace With: [Select]
global $forum_copyright, $context, $scripturl,

Operation #3
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>';

Operation #4
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>'

Operation #5
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>'),

Operation #6
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>',

Operation #7
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>',

Operation #8
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">',

Operation #9
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);
},

Operation #10
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);
},

Operation #11
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>',

Operation #12
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">',

Operation #13
Find: [Select]
global $user_info, $user_settings, $context, $modSettings, $settings, $topic, $board, $smcFunc, $sourcedir
Add After: [Select]
, $remember_old_url;

$remember_old_url = true

Operation #14
Find: [Select]
global $context, $settings, $modSettings, $txt, $smcFunc
Add After: [Select]
, $remember_old_url

Operation #15
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))

Operation #16
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']);

Operation #17
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);
});

Operation #18
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]))

Operation #19
Find: [Select]
$modSettings['rand_seed'] = microtime() * 1000000;
Replace With: [Select]
$modSettings['rand_seed'] = (double) microtime() * 1000000;

Operation #20
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);
}

Operation #21
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

Operation #1
Find: [Select]
 * @version 2.0.13
Replace With: [Select]
 * @version 2.0.16
This operation isn't vital to the installation of this mod.

Operation #2
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'];

Operation #3
Find: [Select]
<website>http://www.simplemachines.org/</website>
Replace With: [Select]
<website>https://www.simplemachines.org/</website>

./Sources/Who.php

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

Operation #2
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',

Operation #3
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

Operation #4
Find: [Select]

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

'Colin Schoen',
'emanuele',

Operation #5
Find: [Select]
$user_info['is_admin'] ? 'Matt &quot;Grudge&quot; Wolf': 'Grudge',
Add After: [Select]

'Michael &quot;Oldiesmann&quot; Eshom',

Operation #6
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

Operation #7
Find: [Select]

'Chas Large',
Add Before: [Select]

'Chalky',

Operation #8
Find: [Select]

'gbsothere',
Add Before: [Select]

'Gary M. Gadsdon',

Operation #9
Find: [Select]

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

'Justyne',

Operation #10
Find: [Select]
'Kill Em All',
Add After: [Select]

'lurkalot',
'margarett',

Operation #11
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

Operation #12
Find: [Select]
'Bryan &quot;Runic&quot; Deakin',
Add After: [Select]

'Bugo',

Operation #13
Find: [Select]

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

'Gwenwyfar',
'Jason &quot;JBlaze&quot; Clemons',

Operation #14
Find: [Select]

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

'Jerry',
'Joker&trade;',

Operation #15
Find: [Select]
'Matthew &quot;Labradoodle-360&quot; Kerle',
Add After: [Select]

'Mick.',
'NanoSector',
'nend',

Operation #16
Find: [Select]
'Peter &quot;Arantor&quot; Spicer',
Add After: [Select]

'Ricky.',

Operation #17
Find: [Select]

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

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

// Former Doc Writers

Operation #18
Find: [Select]
'AngellinaBelle',
Add After: [Select]

'Chainy',

Operation #19
Find: [Select]

'Kindred',
Replace With: [Select]

// Marketing Coordinator

// Marketing

// Former Marketing
'Will &quot;Kindred&quot; Wagner',

Operation #20
Find: [Select]
'Tony Reid',
Add After: [Select]

'Mert &quot;Antes&quot; Al&#x0131;nbay',

Operation #21
Find: [Select]

'Relyana',
Add Before: [Select]

// Lead Localizer
'Francisco &quot;d3vcho&quot; Domínguez',
// Localizers
'Nikola &quot;Dzonny&quot; Novaković',
// Former Localizers

Operation #22
Find: [Select]
'Relyana',
Add After: [Select]

'Robert.',

Operation #23
Find: [Select]

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

'Michael Johnson',
'Liroy van Hoewijk',

Operation #24
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

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

Operation #2
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'], '\';';

Operation #3
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

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

Operation #2
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

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

Operation #2
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

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

Operation #2
Find: [Select]
function template_email_members()
{
global $context, $settings, $options, $txt, $scripturl
Add After: [Select]
, $modSettings

Operation #3
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 '

Operation #4
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 '

Operation #5
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 '

Operation #6
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

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

Operation #2
Find: [Select]
'<a href="' . $member['website']['url'] . '" target="_blank"
Add After: [Select]
 rel="noopener noreferrer"

./Themes/default/Notify.template.php

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

Operation #2
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']), '"

Operation #3
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']), '"

Operation #4
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

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

Operation #2
Find: [Select]

criteriaNum++
Replace With: [Select]


if (criteriaNum++ >= ', $context['rule_limiters']['criteria'], ')
return false;

Operation #3
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\';

Operation #4
Find: [Select]

actionNum++
Replace With: [Select]

if (actionNum++ >= ', $context['rule_limiters']['actions'], ')
return false;

Operation #5
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\';

Operation #6
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

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

Operation #2
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

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

Operation #2
Find: [Select]
<form action="', $scripturl, '?action=register" method="post" accept-charset="', $context['character_set'], '" id="registration">
Add After: [Select]
';

if (!empty($context['agreement']))
echo '

Operation #3
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 '

Operation #4
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" />

Operation #5
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>';

Operation #6
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>

Operation #7
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()" />

Operation #8
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>

Operation #9
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

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

Operation #2
Find: [Select]
function template_wap_boardindex()
{
global $context, $settings, $options, $scripturl
Add After: [Select]
, $txt

Operation #3
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>';
}

Operation #4
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'], '" />

Operation #5
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'], '" />

Operation #6
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

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

Operation #2
Find: [Select]
foreach ($year['months'] as $month);
Replace With: [Select]
foreach ($year['months'] as $month)
This operation isn't vital to the installation of this mod.

./Themes/default/scripts/script.js

Operation #1
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;

Operation #2
Find: [Select]

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


Operation #3
Find: [Select]
if (oCurSelection.setBaseAndExtent)
Add Before: [Select]
if (oCurSelection.selectAllChildren)
{
oCurSelection.selectAllChildren(oCodeArea);
}
else

./Themes/core/Display.template.php

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

Operation #2
Find: [Select]
href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank"
Add After: [Select]
 rel="noopener noreferrer"
This operation isn't vital to the installation of this mod.

./Themes/core/Memberlist.template.php

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

Operation #2
Find: [Select]
<a href="' . $member['website']['url'] . '" target="_blank"
Add After: [Select]
 rel="noopener noreferrer"
This operation isn't vital to the installation of this mod.

./Themes/core/PersonalMessage.template.php

Operation #1
Find: [Select]
 * @version 2.0.2
Replace With: [Select]
 * @version 2.0.16
This operation isn't vital to the installation of this mod.

Operation #2
Find: [Select]
href="', $message['member']['website']['url'], '" title="' . $message['member']['website']['title'] . '" target="_blank"
Add After: [Select]
 rel="noopener noreferrer"
This operation isn't vital to the installation of this mod.

./Themes/core/index.template.php

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

Operation #2
Find: [Select]
<input type="hidden" name="hash_passwrd" value="" />
Add After: [Select]
<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
This operation isn't vital to the installation of this mod.

./Themes/core/theme_info.xml

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

File Operations