diff --git a/SSI.php b/SSI.php
index b4d9af584a..f72bcad299 100644
--- a/SSI.php
+++ b/SSI.php
@@ -81,6 +81,10 @@
require_once($sourcedir . '/Class-BrowserDetect.php');
require_once($sourcedir . '/Subs-Auth.php');
+// Ensure we don't trip over disabled internal functions
+if (version_compare(PHP_VERSION, '8.0.0', '>='))
+ require_once($sourcedir . '/Subs-Compat.php');
+
// Create a variable to store some SMF specific functions in.
$smcFunc = array();
diff --git a/Sources/Cache/APIs/FileBased.php b/Sources/Cache/APIs/FileBased.php
index f2228371c4..d571bf1f95 100644
--- a/Sources/Cache/APIs/FileBased.php
+++ b/Sources/Cache/APIs/FileBased.php
@@ -224,7 +224,8 @@ public function cacheSettings(array &$config_vars)
if (!isset($context['settings_post_javascript']))
$context['settings_post_javascript'] = '';
- $context['settings_post_javascript'] .= '
+ if (empty($context['settings_not_writable']))
+ $context['settings_post_javascript'] .= '
$("#cache_accelerator").change(function (e) {
var cache_type = e.currentTarget.value;
$("#cachedir").prop("disabled", cache_type != "'. $class_name .'");
diff --git a/Sources/Cache/APIs/MemcacheImplementation.php b/Sources/Cache/APIs/MemcacheImplementation.php
index 2aef2b366d..b065bbe5ce 100644
--- a/Sources/Cache/APIs/MemcacheImplementation.php
+++ b/Sources/Cache/APIs/MemcacheImplementation.php
@@ -164,7 +164,8 @@ public function cacheSettings(array &$config_vars)
if (!isset($context['settings_post_javascript']))
$context['settings_post_javascript'] = '';
- $context['settings_post_javascript'] .= '
+ if (empty($context['settings_not_writable']))
+ $context['settings_post_javascript'] .= '
$("#cache_accelerator").change(function (e) {
var cache_type = e.currentTarget.value;
$("#'. self::CLASS_KEY .'").prop("disabled", cache_type != "MemcacheImplementation" && cache_type != "MemcachedImplementation");
diff --git a/Sources/Cache/APIs/MemcachedImplementation.php b/Sources/Cache/APIs/MemcachedImplementation.php
index 8faace6fce..02e128e9cf 100644
--- a/Sources/Cache/APIs/MemcachedImplementation.php
+++ b/Sources/Cache/APIs/MemcachedImplementation.php
@@ -184,7 +184,8 @@ public function cacheSettings(array &$config_vars)
if (!isset($context['settings_post_javascript']))
$context['settings_post_javascript'] = '';
- $context['settings_post_javascript'] .= '
+ if (empty($context['settings_not_writable']))
+ $context['settings_post_javascript'] .= '
$("#cache_accelerator").change(function (e) {
var cache_type = e.currentTarget.value;
$("#'. self::CLASS_KEY .'").prop("disabled", cache_type != "MemcacheImplementation" && cache_type != "MemcachedImplementation");
diff --git a/Sources/Cache/APIs/Sqlite.php b/Sources/Cache/APIs/Sqlite.php
index 6bfbc92fc5..5c082f1801 100755
--- a/Sources/Cache/APIs/Sqlite.php
+++ b/Sources/Cache/APIs/Sqlite.php
@@ -146,7 +146,8 @@ public function cacheSettings(array &$config_vars)
if (!isset($context['settings_post_javascript']))
$context['settings_post_javascript'] = '';
- $context['settings_post_javascript'] .= '
+ if (empty($context['settings_not_writable']))
+ $context['settings_post_javascript'] .= '
$("#cache_accelerator").change(function (e) {
var cache_type = e.currentTarget.value;
$("#cachedir_'. $class_name_txt_key .'").prop("disabled", cache_type != "'. $class_name .'");
diff --git a/Sources/Calendar.php b/Sources/Calendar.php
index f3c52e93b7..4d6c52d759 100644
--- a/Sources/Calendar.php
+++ b/Sources/Calendar.php
@@ -113,7 +113,7 @@ function CalendarMain()
// Need a start date for all views
if (!empty($_REQUEST['start_date']))
{
- $start_parsed = date_parse(convertDateToEnglish($_REQUEST['start_date']));
+ $start_parsed = date_parse(str_replace(',', '', convertDateToEnglish($_REQUEST['start_date'])));
if (empty($start_parsed['error_count']) && empty($start_parsed['warning_count']))
{
$_REQUEST['year'] = $start_parsed['year'];
@@ -125,12 +125,12 @@ function CalendarMain()
$month = !empty($_REQUEST['month']) ? (int) $_REQUEST['month'] : $today['month'];
$day = !empty($_REQUEST['day']) ? (int) $_REQUEST['day'] : (!empty($_REQUEST['month']) ? 1 : $today['day']);
- $start_object = checkdate($month, $day, $year) === true ? date_create(implode('-', array($year, $month, $day))) : date_create(implode('-', array($today['year'], $today['month'], $today['day'])));
+ $start_object = checkdate($month, $day, $year) === true ? date_create(implode('-', array($year, $month, $day)) . ' ' . getUserTimezone()) : date_create(implode('-', array($today['year'], $today['month'], $today['day'])) . ' ' . getUserTimezone());
// Need an end date for the list view
if (!empty($_REQUEST['end_date']))
{
- $end_parsed = date_parse(convertDateToEnglish($_REQUEST['end_date']));
+ $end_parsed = date_parse(str_replace(',', '', convertDateToEnglish($_REQUEST['end_date'])));
if (empty($end_parsed['error_count']) && empty($end_parsed['warning_count']))
{
$_REQUEST['end_year'] = $end_parsed['year'];
@@ -146,14 +146,14 @@ function CalendarMain()
if (isset($end_month, $end_day, $end_year) && checkdate($end_month, $end_day, $end_year))
{
- $end_object = date_create(implode('-', array($end_year, $end_month, $end_day)));
+ $end_object = date_create(implode('-', array($end_year, $end_month, $end_day)) . ' ' . getUserTimezone());
}
if (empty($end_object) || $start_object >= $end_object)
{
$num_days_shown = empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'];
- $end_object = date_create(date_format($start_object, 'Y-m-d'));
+ $end_object = date_create(date_format($start_object, 'Y-m-d') . ' ' . getUserTimezone());
date_add($end_object, date_interval_create_from_date_string($num_days_shown . ' days'));
}
diff --git a/Sources/Load.php b/Sources/Load.php
index 7545ecfe23..3d0eb4f261 100644
--- a/Sources/Load.php
+++ b/Sources/Load.php
@@ -1850,6 +1850,7 @@ function loadMemberContext($user, $display_custom_fields = false)
$value = $value ? $txt['yes'] : $txt['no'];
// Enclosing the user input within some other text?
+ $simple_value = $value;
if (!empty($custom['enclose']))
$value = strtr($custom['enclose'], array(
'{SCRIPTURL}' => $scripturl,
@@ -1863,6 +1864,7 @@ function loadMemberContext($user, $display_custom_fields = false)
'title' => tokenTxtReplace(!empty($custom['title']) ? $custom['title'] : $custom['col_name']),
'col_name' => tokenTxtReplace($custom['col_name']),
'value' => un_htmlspecialchars(tokenTxtReplace($value)),
+ 'simple' => tokenTxtReplace($simple_value),
'raw' => $profile['options'][$custom['col_name']],
'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
);
diff --git a/Sources/ManageServer.php b/Sources/ManageServer.php
index 763ee8f813..eb06d487b7 100644
--- a/Sources/ManageServer.php
+++ b/Sources/ManageServer.php
@@ -115,13 +115,7 @@ function ModifySettings()
$settings_not_writable = !is_writable($boarddir . '/Settings.php');
$settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
- if ($settings_not_writable)
- $context['settings_message'] = array(
- 'label' => $txt['settings_not_writable'],
- 'tag' => 'div',
- 'class' => 'centertext strong'
- );
- elseif ($settings_backup_fail)
+ if ($settings_backup_fail)
$context['settings_message'] = array(
'label' => $txt['admin_backup_fail'],
'tag' => 'div',
@@ -188,6 +182,7 @@ function ModifyGeneralSettings($return_config = false)
// Setup the template stuff.
$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save';
$context['settings_title'] = $txt['general_settings'];
+ $context['save_disabled'] = $context['settings_not_writable'];
// Saving settings?
if (isset($_REQUEST['save']))
@@ -226,7 +221,8 @@ function ModifyGeneralSettings($return_config = false)
prepareServerSettingsContext($config_vars);
// Some javascript for SSL
- addInlineJavaScript('
+ if (empty($context['settings_not_writable']))
+ addInlineJavaScript('
$(function()
{
$("#force_ssl").change(function()
@@ -535,6 +531,7 @@ function hideGlobalCookies()
$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save';
$context['settings_title'] = $txt['cookies_sessions_settings'];
+ $context['save_disabled'] = $context['settings_not_writable'];
// Saving settings?
if (isset($_REQUEST['save']))
@@ -731,7 +728,7 @@ function ModifyGeneralSecuritySettings($return_config = false)
*/
function ModifyCacheSettings($return_config = false)
{
- global $context, $scripturl, $txt, $cacheAPI, $cache_enable;
+ global $context, $scripturl, $txt, $cacheAPI, $cache_enable, $cache_accelerator;
// Detect all available optimizers
$detectedCacheApis = loadCacheAPIs();
@@ -1041,7 +1038,14 @@ function ModifyLoadBalancingSettings($return_config = false)
*/
function prepareServerSettingsContext(&$config_vars)
{
- global $context, $modSettings, $smcFunc;
+ global $context, $modSettings, $smcFunc, $txt;
+
+ if ($context['settings_not_writable'])
+ $context['settings_message'] = array(
+ 'label' => $txt['settings_not_writable'],
+ 'tag' => 'div',
+ 'class' => 'centertext strong'
+ );
if (isset($_SESSION['adm-save']))
{
diff --git a/Sources/ManageSettings.php b/Sources/ManageSettings.php
index 9e954e45c4..2ec6790545 100644
--- a/Sources/ManageSettings.php
+++ b/Sources/ManageSettings.php
@@ -785,7 +785,7 @@ function ModifyAntispamSettings($return_config = false)
$context['question_answers'][$row['id_question']] = array(
'lngfile' => $lang,
'question' => $row['question'],
- 'answers' => $smcFunc['json_decode']($row['answers'], true),
+ 'answers' => (array) $smcFunc['json_decode']($row['answers'], true),
);
$context['qa_by_lang'][$lang][] = $row['id_question'];
}
diff --git a/Sources/Memberlist.php b/Sources/Memberlist.php
index 185dc31bb7..abdf366ab5 100644
--- a/Sources/Memberlist.php
+++ b/Sources/Memberlist.php
@@ -575,7 +575,7 @@ function MLSearch()
}
foreach ($context['custom_search_fields'] as $field)
- $context['search_fields']['cust_' . $field['colname']] = sprintf($txt['mlist_search_by'], $field['name']);
+ $context['search_fields']['cust_' . $field['colname']] = sprintf($txt['mlist_search_by'], tokenTxtReplace($field['name']));
$context['sub_template'] = 'search';
$context['old_search'] = isset($_GET['search']) ? $_GET['search'] : (isset($_POST['search']) ? $smcFunc['htmlspecialchars']($_POST['search']) : '');
diff --git a/Sources/News.php b/Sources/News.php
index a532f2ea4c..206ce924a9 100644
--- a/Sources/News.php
+++ b/Sources/News.php
@@ -2071,7 +2071,7 @@ function getXmlProfile($xml_format)
$data[] = array(
'tag' => $custom_field['col_name'],
'attributes' => array('label' => $custom_field['title']),
- 'content' => $custom_field['raw'],
+ 'content' => $custom_field['simple'],
'cdata' => true,
);
}
diff --git a/Sources/Post.php b/Sources/Post.php
index 0b84c3c6e8..614bf6fa53 100644
--- a/Sources/Post.php
+++ b/Sources/Post.php
@@ -1234,9 +1234,18 @@ function($m)
foreach ($attachmentRestrictionTypes as $type)
if (!empty($modSettings[$type]))
{
+ $context['attachment_restrictions'][$type] = sprintf($txt['attach_restrict_' . $type . ($modSettings[$type] >= 1024 ? '_MB' : '')], comma_format($modSettings[$type] >= 1024 ? $modSettings[$type] / 1024 : $modSettings[$type], 2));
+
// Show the max number of attachments if not 0.
if ($type == 'attachmentNumPerPostLimit')
- $context['attachment_restrictions'][] = sprintf($txt['attach_remaining'], max($modSettings['attachmentNumPerPostLimit'] - $context['attachments']['quantity'], 0));
+ {
+ $context['attachment_restrictions'][$type] .= ' (' . sprintf($txt['attach_remaining'], max($modSettings['attachmentNumPerPostLimit'] - $context['attachments']['quantity'], 0)) . ')';
+ }
+ elseif ($type == 'attachmentPostLimit' && $context['attachments']['total_size'] > 0)
+ {
+ $context['attachment_restrictions'][$type] .= ' (' . sprintf($txt['attach_available'], max($modSettings['attachmentPostLimit'] - ($context['attachments']['total_size'] / 1024), 0)) . ')';
+ }
+
}
}
@@ -2233,8 +2242,8 @@ function Post2()
unlink($attachment['tmp_name']);
}
}
- unset($_SESSION['temp_attachments']);
}
+ unset($_SESSION['temp_attachments']);
// Make the poll...
if (isset($_REQUEST['poll']))
diff --git a/Sources/Profile-Export.php b/Sources/Profile-Export.php
index 34c9b0428b..8539cba42d 100644
--- a/Sources/Profile-Export.php
+++ b/Sources/Profile-Export.php
@@ -655,9 +655,6 @@ function export_attachment($uid)
send_http_status(403);
exit;
}
-
- // We need the topic.
- list ($_REQUEST['topic']) = $smcFunc['db_fetch_row']($request);
$smcFunc['db_free_result']($request);
// This doesn't count as a normal download.
diff --git a/Sources/Profile-Modify.php b/Sources/Profile-Modify.php
index 09009f4c08..4f45fc3f3d 100644
--- a/Sources/Profile-Modify.php
+++ b/Sources/Profile-Modify.php
@@ -966,25 +966,25 @@ function saveProfileChanges(&$profile_vars, &$post_errors, $memID)
'ignore_boards',
);
- if (isset($_POST['sa']) && $_POST['sa'] == 'ignoreboards' && empty($_POST['ignore_brd']))
- $_POST['ignore_brd'] = array();
+ if (isset($_POST['sa']) && $_POST['sa'] == 'ignoreboards' && empty($_POST['brd']))
+ $_POST['brd'] = array();
unset($_POST['ignore_boards']); // Whatever it is set to is a dirty filthy thing. Kinda like our minds.
- if (isset($_POST['ignore_brd']))
+ if (isset($_POST['brd']))
{
- if (!is_array($_POST['ignore_brd']))
- $_POST['ignore_brd'] = array($_POST['ignore_brd']);
+ if (!is_array($_POST['brd']))
+ $_POST['brd'] = array($_POST['brd']);
- foreach ($_POST['ignore_brd'] as $k => $d)
+ foreach ($_POST['brd'] as $k => $d)
{
$d = (int) $d;
if ($d != 0)
- $_POST['ignore_brd'][$k] = $d;
+ $_POST['brd'][$k] = $d;
else
- unset($_POST['ignore_brd'][$k]);
+ unset($_POST['brd'][$k]);
}
- $_POST['ignore_boards'] = implode(',', $_POST['ignore_brd']);
- unset($_POST['ignore_brd']);
+ $_POST['ignore_boards'] = implode(',', $_POST['brd']);
+ unset($_POST['brd']);
}
// Here's where we sort out all the 'other' values...
@@ -1278,7 +1278,7 @@ function makeCustomFieldChanges($memID, $area, $sanitize = true, $returnErrors =
if (empty($value) && !is_numeric($value))
$value = '';
- if ($row['mask'] == 'nohtml' && ($valueReference != strip_tags($valueReference) || $value != filter_var($value, FILTER_SANITIZE_STRING) || preg_match('/<(.+?)[\s]*\/?[\s]*>/si', $valueReference)))
+ if ($row['mask'] == 'nohtml' && ($valueReference != strip_tags($valueReference) || $value != filter_var($value, FILTER_SANITIZE_FULL_SPECIAL_CHARS) || preg_match('/<(.+?)[\s]*\/?[\s]*>/si', $valueReference)))
{
if ($returnErrors)
$errors[] = 'custom_field_nohtml_fail';
diff --git a/Sources/ShowAttachments.php b/Sources/ShowAttachments.php
index 18e808a003..52560efec4 100644
--- a/Sources/ShowAttachments.php
+++ b/Sources/ShowAttachments.php
@@ -64,12 +64,10 @@ function showAttachment()
}
// A thumbnail has been requested? madness! madness I say!
- $preview = isset($_REQUEST['preview']) ? $_REQUEST['preview'] : (isset($_REQUEST['type']) && $_REQUEST['type'] == 'preview' ? $_REQUEST['type'] : 0);
$showThumb = isset($_REQUEST['thumb']);
- $attachTopic = isset($_REQUEST['topic']) ? (int) $_REQUEST['topic'] : 0;
- // No access in strict maintenance mode or you don't have permission to see attachments.
- if ((!empty($maintenance) && $maintenance == 2) || (!allowedTo('view_attachments') && !isset($_SESSION['attachments_can_preview'][$attachId])))
+ // No access in strict maintenance mode.
+ if (!empty($maintenance) && $maintenance == 2)
{
send_http_status(404, 'File Not Found');
die('404 File Not Found');
@@ -92,13 +90,16 @@ function showAttachment()
// Make sure this attachment is on this board and load its info while we are at it.
$request = $smcFunc['db_query']('', '
SELECT
- id_folder, filename, file_hash, fileext, id_attach,
- id_thumb, attachment_type, mime_type, approved, id_msg
- FROM {db_prefix}attachments
- WHERE id_attach = {int:attach}' . (!empty($context['preview_message']) ? '
- AND a.id_msg != 0' : '') . '
+ {string:source} AS source,
+ a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach,
+ a.id_thumb, a.attachment_type, a.mime_type, a.approved, a.id_msg,
+ m.id_board
+ FROM {db_prefix}attachments AS a
+ LEFT JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
+ WHERE a.id_attach = {int:attach}
LIMIT 1',
array(
+ 'source' => 'SMF',
'attach' => $attachId,
)
);
@@ -114,63 +115,6 @@ function showAttachment()
$file = $smcFunc['db_fetch_assoc']($request);
$smcFunc['db_free_result']($request);
- // If theres a message ID stored, we NEED a topic ID.
- if (!empty($file['id_msg']) && empty($attachTopic) && empty($preview))
- {
- send_http_status(404, 'File Not Found');
- die('404 File Not Found');
- }
-
- // Previews doesn't have this info.
- if (empty($preview) && is_resource($attachRequest))
- {
- $request2 = $smcFunc['db_query']('', '
- SELECT a.id_msg
- FROM {db_prefix}attachments AS a
- INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic})
- WHERE {query_see_message_board}
- AND a.id_attach = {int:attach}
- LIMIT 1',
- array(
- 'attach' => $attachId,
- 'current_topic' => $attachTopic,
- )
- );
-
- // The provided topic must match the one stored in the DB for this particular attachment, also.
- if ($smcFunc['db_num_rows']($request2) == 0)
- {
- send_http_status(404, 'File Not Found');
- die('404 File Not Found');
- }
-
- $smcFunc['db_free_result']($request2);
- }
-
- // If attachment is unapproved, see if user is allowed to approve
- if (!$file['approved'] && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
- {
- $request3 = $smcFunc['db_query']('', '
- SELECT id_member
- FROM {db_prefix}messages
- WHERE id_msg = {int:id_msg}
- LIMIT 1',
- array(
- 'id_msg' => $file['id_msg'],
- )
- );
-
- $id_member = $smcFunc['db_fetch_assoc']($request3)['id_member'];
- $smcFunc['db_free_result']($request3);
-
- // Let users see own unapproved attachments
- if ($id_member != $user_info['id'])
- {
- send_http_status(403, 'Forbidden');
- die('403 Forbidden');
- }
- }
-
// set filePath and ETag time
$file['filePath'] = getAttachmentFilename($file['filename'], $attachId, $file['id_folder'], false, $file['file_hash']);
// ensure variant attachment compatibility
@@ -210,6 +154,63 @@ function showAttachment()
cache_put_data('attachment_lookup_id-' . $file['id_attach'], array($file, $thumbFile), mt_rand(850, 900));
}
+ // No access if you don't have permission to see this attachment.
+ if
+ (
+ // This was from SMF or a hook didn't claim it.
+ (
+ empty($file['source'])
+ || $file['source'] == 'SMF'
+ )
+ && (
+ // No id_msg and no id_member, so we don't know where its from.
+ // Avatars will have id_msg = 0 and id_member > 0.
+ (
+ empty($file['id_msg'])
+ && empty($file['id_member'])
+ )
+ // When we have a message, we need a board and that board needs to
+ // let us view the attachment.
+ || (
+ !empty($file['id_msg'])
+ && (
+ empty($file['id_board'])
+ || !allowedTo('view_attachments', $file['id_board'])
+ )
+ )
+ )
+ // We are not previewing an attachment.
+ && !isset($_SESSION['attachments_can_preview'][$attachId])
+ )
+ {
+ send_http_status(404, 'File Not Found');
+ die('404 File Not Found');
+ }
+
+ // If attachment is unapproved, see if user is allowed to approve
+ if (!$file['approved'] && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
+ {
+ $request = $smcFunc['db_query']('', '
+ SELECT id_member
+ FROM {db_prefix}messages
+ WHERE id_msg = {int:id_msg}
+ LIMIT 1',
+ array(
+ 'id_msg' => $file['id_msg'],
+ )
+ );
+
+ $id_member = $smcFunc['db_fetch_assoc']($request)['id_member'];
+ $smcFunc['db_free_result']($request);
+
+ // Let users see own unapproved attachments
+ if ($id_member != $user_info['id'])
+ {
+ send_http_status(403, 'Forbidden');
+ die('403 Forbidden');
+ }
+ }
+
// Replace the normal file with its thumbnail if it has one!
if (!empty($showThumb) && !empty($thumbFile))
$file = $thumbFile;
diff --git a/Sources/Subs-Attachments.php b/Sources/Subs-Attachments.php
index 7986b5fb39..b017df028e 100644
--- a/Sources/Subs-Attachments.php
+++ b/Sources/Subs-Attachments.php
@@ -1003,7 +1003,10 @@ function parseAttachBBC($attachID = 0)
$attachInfo = getAttachMsgInfo($attachID);
// There is always the chance this attachment no longer exists or isn't associated to a message anymore...
- if (empty($attachInfo) || empty($attachInfo['msg']) && empty($context['preview_message']))
+ if (empty($attachInfo))
+ return 'attachments_no_data_loaded';
+
+ if (empty($attachInfo['msg']) && empty($context['preview_message']))
return 'attachments_no_msg_associated';
// Can the user view attachments on the board that holds the attachment's original post?
@@ -1057,18 +1060,6 @@ function parseAttachBBC($attachID = 0)
if ($check_board_perms && !in_array($attachContext['board'], $view_attachment_boards))
return 'attachments_not_allowed_to_see';
- // Previewing much? No msg ID has been set yet.
- if (!empty($context['preview_message']))
- {
- $attachContext['href'] = $scripturl . '?action=dlattach;attach=' . $attachID . ';type=preview';
-
- $attachContext['link'] = '' . $smcFunc['htmlspecialchars']($attachContext['name']) . '';
-
- // Fix the thumbnail too, if the image has one.
- if (!empty($attachContext['thumbnail']) && !empty($attachContext['thumbnail']['has_thumb']))
- $attachContext['thumbnail']['href'] = $scripturl . '?action=dlattach;attach=' . $attachContext['thumbnail']['id'] . ';image;type=preview';
- }
-
// You may or may not want to show this under the post.
if (!empty($modSettings['dont_show_attach_under_post']) && !isset($context['show_attach_under_post'][$attachID]))
$context['show_attach_under_post'][$attachID] = $attachID;
@@ -1207,8 +1198,8 @@ function loadAttachmentContext($id_msg, $attachments)
'downloads' => $attachment['downloads'],
'size' => ($attachment['filesize'] < 1024000) ? round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'] : round($attachment['filesize'] / 1024 / 1024, 2) . ' ' . $txt['megabyte'],
'byte_size' => $attachment['filesize'],
- 'href' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_attach'],
- 'link' => '' . $smcFunc['htmlspecialchars']($attachment['filename']) . '',
+ 'href' => $scripturl . '?action=dlattach;attach=' . $attachment['id_attach'],
+ 'link' => '' . $smcFunc['htmlspecialchars']($attachment['filename']) . '',
'is_image' => !empty($attachment['width']) && !empty($attachment['height']),
'is_approved' => $attachment['approved'],
'topic' => $attachment['topic'],
@@ -1314,7 +1305,7 @@ function loadAttachmentContext($id_msg, $attachments)
if (!empty($attachment['id_thumb']))
$attachmentData[$i]['thumbnail'] = array(
'id' => $attachment['id_thumb'],
- 'href' => $scripturl . '?action=dlattach;topic=' . $attachment['topic'] . '.0;attach=' . $attachment['id_thumb'] . ';image',
+ 'href' => $scripturl . '?action=dlattach;attach=' . $attachment['id_thumb'] . ';image',
);
$attachmentData[$i]['thumbnail']['has_thumb'] = !empty($attachment['id_thumb']);
diff --git a/Sources/Subs-Calendar.php b/Sources/Subs-Calendar.php
index 2f7688c97e..4b48a3c4d3 100644
--- a/Sources/Subs-Calendar.php
+++ b/Sources/Subs-Calendar.php
@@ -122,10 +122,10 @@ function getEventRange($low_date, $high_date, $use_permissions = true)
require_once($sourcedir . '/Subs.php');
if (empty($timezone_array['default']))
- $timezone_array['default'] = timezone_open(date_default_timezone_get());
+ $timezone_array['default'] = timezone_open(getUserTimezone());
- $low_object = date_create($low_date);
- $high_object = date_create($high_date);
+ $low_object = date_create($low_date, $timezone_array['default']);
+ $high_object = date_create($high_date, $timezone_array['default']);
// Find all the calendar info...
$result = $smcFunc['db_query']('calendar_get_events', '
@@ -372,10 +372,10 @@ function canLinkEvent()
function getTodayInfo()
{
return array(
- 'day' => (int) smf_strftime('%d', time()),
- 'month' => (int) smf_strftime('%m', time()),
- 'year' => (int) smf_strftime('%Y', time()),
- 'date' => smf_strftime('%Y-%m-%d', time()),
+ 'day' => (int) smf_strftime('%d', time(), getUserTimezone()),
+ 'month' => (int) smf_strftime('%m', time(), getUserTimezone()),
+ 'year' => (int) smf_strftime('%Y', time(), getUserTimezone()),
+ 'date' => smf_strftime('%Y-%m-%d', time(), getUserTimezone()),
);
}
@@ -392,12 +392,12 @@ function getCalendarGrid($selected_date, $calendarOptions, $is_previous = false,
{
global $scripturl, $modSettings;
- $selected_object = date_create($selected_date);
+ $selected_object = date_create($selected_date . ' ' . getUserTimezone());
- $next_object = date_create($selected_date);
+ $next_object = date_create($selected_date . ' ' . getUserTimezone());
$next_object->modify('first day of next month');
- $prev_object = date_create($selected_date);
+ $prev_object = date_create($selected_date . ' ' . getUserTimezone());
$prev_object->modify('first day of previous month');
// Eventually this is what we'll be returning.
@@ -431,8 +431,8 @@ function getCalendarGrid($selected_date, $calendarOptions, $is_previous = false,
// Get today's date.
$today = getTodayInfo();
- $first_day_object = date_create(date_format($selected_object, 'Y-m-01'));
- $last_day_object = date_create(date_format($selected_object, 'Y-m-t'));
+ $first_day_object = date_create(date_format($selected_object, 'Y-m-01') . ' ' . getUserTimezone());
+ $last_day_object = date_create(date_format($selected_object, 'Y-m-t') . ' ' . getUserTimezone());
// Get information about this month.
$month_info = array(
@@ -445,8 +445,8 @@ function getCalendarGrid($selected_date, $calendarOptions, $is_previous = false,
'day_of_month' => date_format($last_day_object, 't'),
'date' => date_format($last_day_object, 'Y-m-d'),
),
- 'first_day_of_year' => date_format(date_create(date_format($selected_object, 'Y-01-01')), 'w'),
- 'first_day_of_next_year' => date_format(date_create((date_format($selected_object, 'Y') + 1) . '-01-01'), 'w'),
+ 'first_day_of_year' => date_format(date_create(date_format($selected_object, 'Y-01-01') . ' ' . getUserTimezone()), 'w'),
+ 'first_day_of_next_year' => date_format(date_create((date_format($selected_object, 'Y') + 1) . '-01-01' . ' ' . getUserTimezone()), 'w'),
);
// The number of days the first row is shifted to the right for the starting day.
@@ -545,7 +545,7 @@ function getCalendarWeek($selected_date, $calendarOptions)
{
global $scripturl, $modSettings, $txt;
- $selected_object = date_create($selected_date);
+ $selected_object = date_create($selected_date . ' ' . getUserTimezone());
// Get today's date.
$today = getTodayInfo();
@@ -553,7 +553,7 @@ function getCalendarWeek($selected_date, $calendarOptions)
// What is the actual "start date" for the passed day.
$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
$day_of_week = date_format($selected_object, 'w');
- $first_day_object = date_create($selected_date);
+ $first_day_object = date_create($selected_date . ' ' . getUserTimezone());
if ($day_of_week != $calendarOptions['start_day'])
{
// Here we offset accordingly to get things to the real start of a week.
@@ -564,17 +564,17 @@ function getCalendarWeek($selected_date, $calendarOptions)
date_sub($first_day_object, date_interval_create_from_date_string($date_diff . ' days'));
}
- $last_day_object = date_create(date_format($first_day_object, 'Y-m-d'));
+ $last_day_object = date_create(date_format($first_day_object, 'Y-m-d') . ' ' . getUserTimezone());
date_add($last_day_object, date_interval_create_from_date_string('1 week'));
$month = date_format($first_day_object, 'n');
$year = date_format($first_day_object, 'Y');
$day = date_format($first_day_object, 'd');
- $next_object = date_create($selected_date);
+ $next_object = date_create($selected_date . ' ' . getUserTimezone());
date_add($next_object, date_interval_create_from_date_string('1 week'));
- $prev_object = date_create($selected_date);
+ $prev_object = date_create($selected_date . ' ' . getUserTimezone());
date_sub($prev_object, date_interval_create_from_date_string('1 week'));
// Now start filling in the calendar grid.
@@ -609,7 +609,7 @@ function getCalendarWeek($selected_date, $calendarOptions)
// This holds all the main data - there is at least one month!
$calendarGrid['months'] = array();
- $current_day_object = date_create(date_format($first_day_object, 'Y-m-d'));
+ $current_day_object = date_create(date_format($first_day_object, 'Y-m-d') . ' ' . getUserTimezone());
for ($i = 0; $i < 7; $i++)
{
$current_month = date_format($current_day_object, 'n');
@@ -660,8 +660,8 @@ function getCalendarList($start_date, $end_date, $calendarOptions)
require_once($sourcedir . '/Subs.php');
// DateTime objects make life easier
- $start_object = date_create($start_date);
- $end_object = date_create($end_date);
+ $start_object = date_create($start_date . ' ' . getUserTimezone());
+ $end_object = date_create($end_date . ' ' . getUserTimezone());
$calendarGrid = array(
'start_date' => timeformat(date_format($start_object, 'U'), get_date_or_time_format('date')),
@@ -1053,7 +1053,7 @@ function validateEventPost()
// The 2.1 way
if (isset($_POST['start_date']))
{
- $d = date_parse(convertDateToEnglish($_POST['start_date']));
+ $d = date_parse(str_replace(',', '', convertDateToEnglish($_POST['start_date'])));
if (!empty($d['error_count']) || !empty($d['warning_count']))
fatal_lang_error('invalid_date', false);
if (empty($d['year']))
@@ -1063,7 +1063,7 @@ function validateEventPost()
}
elseif (isset($_POST['start_datetime']))
{
- $d = date_parse(convertDateToEnglish($_POST['start_datetime']));
+ $d = date_parse(str_replace(',', '', convertDateToEnglish($_POST['start_datetime'])));
if (!empty($d['error_count']) || !empty($d['warning_count']))
fatal_lang_error('invalid_date', false);
if (empty($d['year']))
@@ -1572,16 +1572,16 @@ function setEventStartEnd($eventOptions = array())
// If some form of string input was given, override individually defined options with it
if (isset($start_string))
{
- $start_string_parsed = date_parse(convertDateToEnglish($start_string));
+ $start_string_parsed = date_parse(str_replace(',', '', convertDateToEnglish($start_string)));
if (empty($start_string_parsed['error_count']) && empty($start_string_parsed['warning_count']))
{
- if ($start_string_parsed['year'] != false)
+ if ($start_string_parsed['year'] !== false)
{
$start_year = $start_string_parsed['year'];
$start_month = $start_string_parsed['month'];
$start_day = $start_string_parsed['day'];
}
- if ($start_string_parsed['hour'] != false)
+ if ($start_string_parsed['hour'] !== false)
{
$start_hour = $start_string_parsed['hour'];
$start_minute = $start_string_parsed['minute'];
@@ -1591,16 +1591,16 @@ function setEventStartEnd($eventOptions = array())
}
if (isset($end_string))
{
- $end_string_parsed = date_parse(convertDateToEnglish($end_string));
+ $end_string_parsed = date_parse(str_replace(',', '', convertDateToEnglish($end_string)));
if (empty($end_string_parsed['error_count']) && empty($end_string_parsed['warning_count']))
{
- if ($end_string_parsed['year'] != false)
+ if ($end_string_parsed['year'] !== false)
{
$end_year = $end_string_parsed['year'];
$end_month = $end_string_parsed['month'];
$end_day = $end_string_parsed['day'];
}
- if ($end_string_parsed['hour'] != false)
+ if ($end_string_parsed['hour'] !== false)
{
$end_hour = $end_string_parsed['hour'];
$end_minute = $end_string_parsed['minute'];
diff --git a/Sources/Subs-Compat.php b/Sources/Subs-Compat.php
index 5ab4c51bec..69aa512678 100644
--- a/Sources/Subs-Compat.php
+++ b/Sources/Subs-Compat.php
@@ -510,4 +510,34 @@ function idn_to_utf8($domain, $flags = 0, $variant = 1, &$idna_info = null)
}
}
+/**
+ * Prevent fatal errors under PHP 8 when a disabled internal function is called.
+ *
+ * Before PHP 8, calling a disabled internal function merely generated a
+ * warning that could be easily suppressed by the @ operator. But as of PHP 8
+ * a disabled internal function is treated like it is undefined, which means
+ * a fatal error will be thrown and execution will halt. SMF expects the old
+ * behaviour, so these no-op polyfills make sure that is what happens.
+ */
+if (version_compare(PHP_VERSION, '8.0.0', '>='))
+{
+ /*
+ * This array contains function names that meet the following conditions:
+ *
+ * 1. SMF assumes they are defined, even if disabled. Note that prior to
+ * PHP 8, this was always true for internal functions.
+ *
+ * 2. Some hosts are known to disable them.
+ *
+ * 3. SMF can get by without them (as opposed to missing functions that
+ * really SHOULD cause execution to halt).
+ */
+ foreach (array('set_time_limit') as $func)
+ {
+ if (!function_exists($func))
+ eval('function ' . $func . '() { trigger_error("' . $func . '() has been disabled for security reasons", E_USER_WARNING); }');
+ }
+ unset($func);
+}
+
?>
\ No newline at end of file
diff --git a/Sources/Subs-Editor.php b/Sources/Subs-Editor.php
index cf205e9bec..5829f8e93e 100644
--- a/Sources/Subs-Editor.php
+++ b/Sources/Subs-Editor.php
@@ -1759,6 +1759,9 @@ function create_control_richedit($editorOptions)
'hr' => 'horizontalrule',
);
+ // Define this here so mods can add to it via the hook.
+ $context['disabled_tags'] = array();
+
// Allow mods to modify BBC buttons.
// Note: passing the array here is not necessary and is deprecated, but it is kept for backward compatibility with 2.0
call_integration_hook('integrate_bbc_buttons', array(&$context['bbc_tags'], &$editor_tag_map));
@@ -1778,6 +1781,12 @@ function create_control_richedit($editorOptions)
$context['disabled_tags']['orderedlist'] = true;
}
+ if ($tag === 'float')
+ {
+ $context['disabled_tags']['floatleft'] = true;
+ $context['disabled_tags']['floatright'] = true;
+ }
+
foreach ($editor_tag_map as $thisTag => $tagNameBBC)
if ($tag === $thisTag)
$context['disabled_tags'][$tagNameBBC] = true;
@@ -1798,7 +1807,12 @@ function create_control_richedit($editorOptions)
foreach ($tagRow as $tag)
{
- if ((!empty($tag['code'])) && empty($context['disabled_tags'][$tag['code']]))
+ if (empty($tag['code']))
+ {
+ $context['bbc_toolbar'][$row][] = implode(',', $tagsRow);
+ $tagsRow = array();
+ }
+ elseif (empty($context['disabled_tags'][$tag['code']]))
{
$tagsRow[] = $tag['code'];
@@ -1829,11 +1843,6 @@ function create_control_richedit($editorOptions)
$context['bbcodes_handlers'] .= '
});';
}
- else
- {
- $context['bbc_toolbar'][$row][] = implode(',', $tagsRow);
- $tagsRow = array();
- }
}
if (!empty($tagsRow))
@@ -2069,7 +2078,7 @@ function create_control_verification(&$verificationOptions, $do_test = false)
$id_question = $row['id_question'];
unset ($row['id_question']);
// Make them all lowercase. We can't directly use $smcFunc['strtolower'] with array_walk, so do it manually, eh?
- $row['answers'] = $smcFunc['json_decode']($row['answers'], true);
+ $row['answers'] = (array) $smcFunc['json_decode']($row['answers'], true);
foreach ($row['answers'] as $k => $v)
$row['answers'][$k] = $smcFunc['strtolower']($v);
diff --git a/Sources/Subs-Graphics.php b/Sources/Subs-Graphics.php
index cba018c549..7d86e08ba1 100644
--- a/Sources/Subs-Graphics.php
+++ b/Sources/Subs-Graphics.php
@@ -186,6 +186,8 @@ function reencodeImage($fileName, $preferred_format = 0)
if (!rename($fileName . '.tmp', $fileName))
return false;
+
+ return true;
}
/**
diff --git a/Sources/Subs-Timezones.php b/Sources/Subs-Timezones.php
index 03a5a285cc..84085758f3 100644
--- a/Sources/Subs-Timezones.php
+++ b/Sources/Subs-Timezones.php
@@ -486,7 +486,7 @@ function get_tzid_metazones($when = 'now')
'Pacific/Efate' => 'Pacific_Vanuatu',
// No DST
- 'Pacific/Enderbury' => 'Pacific_Phoenix_Islands',
+ 'Pacific/Kanton' => 'Pacific_Phoenix_Islands',
// No DST
'Pacific/Fakaofo' => 'Pacific_Tokelau',
@@ -1065,6 +1065,7 @@ function get_sorted_tzids_for_country($country_code, $when = 'now')
'KI' => array(
'Pacific/Tarawa',
'Pacific/Kiritimati',
+ 'Pacific/Kanton',
'Pacific/Enderbury',
),
'KM' => array(
@@ -1617,6 +1618,12 @@ function get_tzid_fallbacks($tzids, $when = 'now')
'tzid' => 'Pacific/Truk',
),
),
+ 'Pacific/Kanton' => array(
+ array(
+ 'ts' => PHP_INT_MIN,
+ 'tzid' => 'Pacific/Enderbury',
+ ),
+ ),
'Pacific/Pohnpei' => array(
array(
'ts' => PHP_INT_MIN,
diff --git a/Sources/Subs.php b/Sources/Subs.php
index 7992b4800f..dd5fd29a1a 100644
--- a/Sources/Subs.php
+++ b/Sources/Subs.php
@@ -849,18 +849,23 @@ function get_date_or_time_format($type = '', $format = '')
array(
// Anything that isn't a specification, punctuation mark, or whitespace.
'~(?(\1|[^%\P{Po}])\s*(?!$))*~u',
+ // Repeated punctuation marks (except %), possibly separated by whitespace.
+ '~(?'.'>([^%\P{P}])\s*(?=\1))*~u',
+ '~([^%\P{P}])(?'.'>\1(?!$))*~u',
// Unwanted trailing punctuation and whitespace.
'~(?'.'>([\p{Pd}\p{Ps}\p{Pi}\p{Pc}]|[^%\P{Po}])\s*)*$~u',
// Unwanted opening punctuation and whitespace.
'~^\s*(?'.'>([\p{Pd}\p{Pe}\p{Pf}\p{Pc}]|[^%\P{Po}])\s*)*~u',
+ // Runs of horizontal whitespace.
+ '~\s+~',
),
array(
'',
+ '$1',
'$1$2',
'',
'',
+ ' ',
),
$format
);
@@ -1740,7 +1745,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
// parseAttachBBC will return a string ($txt key) rather than dying with a fatal_error. Up to you to decide what to do.
if (is_string($currentAttachment))
- return $data = !empty($txt[$currentAttachment]) ? $txt[$currentAttachment] : $currentAttachment;
+ return $data = '' . (!empty($txt[$currentAttachment]) ? $txt[$currentAttachment] : $currentAttachment) . '';
// We need a display mode.
if (empty($params['{display}']))
diff --git a/Themes/default/Display.template.php b/Themes/default/Display.template.php
index ed2f47fead..bc76e1b136 100644
--- a/Themes/default/Display.template.php
+++ b/Themes/default/Display.template.php
@@ -783,7 +783,7 @@ function template_single_post($message)
';
else
echo '
-
';
+
';
echo '
';
diff --git a/Themes/default/Post.template.php b/Themes/default/Post.template.php
index 15d7f68955..aaf6a19ed7 100644
--- a/Themes/default/Post.template.php
+++ b/Themes/default/Post.template.php
@@ -320,7 +320,7 @@ function addPollOption()
echo '