';
+
+ if (!$news['is_last'])
+ echo '
+ ';
+ }
+}
+
+// Show the most recent events.
+function ssi_recentEvents($max_events = 7, $output_method = 'echo')
+{
+ global $db_prefix, $user_info, $scripturl, $modSettings, $txt, $context, $smcFunc;
+
+ if (empty($modSettings['cal_enabled']) || !allowedTo('calendar_view'))
+ return;
+
+ // Find all events which are happening in the near future that the member can see.
+ $request = $smcFunc['db_query']('', '
+ SELECT
+ cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, cal.id_topic,
+ cal.id_board, t.id_first_msg, t.approved
+ FROM {db_prefix}calendar AS cal
+ LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board)
+ LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
+ WHERE cal.start_date <= {date:current_date}
+ AND cal.end_date >= {date:current_date}
+ AND (cal.id_board = {int:no_board} OR {query_wanna_see_board})
+ ORDER BY cal.start_date DESC
+ LIMIT ' . $max_events,
+ array(
+ 'current_date' => strftime('%Y-%m-%d', forum_time(false)),
+ 'no_board' => 0,
+ )
+ );
+ $return = array();
+ $duplicates = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ // Check if we've already come by an event linked to this same topic with the same title... and don't display it if we have.
+ if (!empty($duplicates[$row['title'] . $row['id_topic']]))
+ continue;
+
+ // Censor the title.
+ censorText($row['title']);
+
+ if ($row['start_date'] < strftime('%Y-%m-%d', forum_time(false)))
+ $date = strftime('%Y-%m-%d', forum_time(false));
+ else
+ $date = $row['start_date'];
+
+ // If the topic it is attached to is not approved then don't link it.
+ if (!empty($row['id_first_msg']) && !$row['approved'])
+ $row['id_board'] = $row['id_topic'] = $row['id_first_msg'] = 0;
+
+ $return[$date][] = array(
+ 'id' => $row['id_event'],
+ 'title' => $row['title'],
+ 'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
+ 'modify_href' => $scripturl . '?action=' . ($row['id_board'] == 0 ? 'calendar;sa=post;' : 'post;msg=' . $row['id_first_msg'] . ';topic=' . $row['id_topic'] . '.0;calendar;') . 'eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
+ 'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
+ 'link' => $row['id_board'] == 0 ? $row['title'] : '' . $row['title'] . '',
+ 'start_date' => $row['start_date'],
+ 'end_date' => $row['end_date'],
+ 'is_last' => false
+ );
+
+ // Let's not show this one again, huh?
+ $duplicates[$row['title'] . $row['id_topic']] = true;
+ }
+ $smcFunc['db_free_result']($request);
+
+ foreach ($return as $mday => $array)
+ $return[$mday][count($array) - 1]['is_last'] = true;
+
+ if ($output_method != 'echo' || empty($return))
+ return $return;
+
+ // Well the output method is echo.
+ echo '
+ ' . $txt['events'] . ' ';
+ foreach ($return as $mday => $array)
+ foreach ($array as $event)
+ {
+ if ($event['can_edit'])
+ echo '
+ * ';
+
+ echo '
+ ' . $event['link'] . (!$event['is_last'] ? ', ' : '');
+ }
+}
+
+// Check the passed id_member/password. If $is_username is true, treats $id as a username.
+function ssi_checkPassword($id = null, $password = null, $is_username = false)
+{
+ global $db_prefix, $sourcedir, $smcFunc;
+
+ // If $id is null, this was most likely called from a query string and should do nothing.
+ if ($id === null)
+ return;
+
+ $request = $smcFunc['db_query']('', '
+ SELECT passwd, member_name, is_activated
+ FROM {db_prefix}members
+ WHERE ' . ($is_username ? 'member_name' : 'id_member') . ' = {string:id}
+ LIMIT 1',
+ array(
+ 'id' => $id,
+ )
+ );
+ list ($pass, $user, $active) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ return sha1(strtolower($user) . $password) == $pass && $active == 1;
+}
+
+// We want to show the recent attachments outside of the forum.
+function ssi_recentAttachments($num_attachments = 10, $attachment_ext = array(), $output_method = 'echo')
+{
+ global $smcFunc, $context, $modSettings, $scripturl, $txt, $settings;
+
+ // We want to make sure that we only get attachments for boards that we can see *if* any.
+ $attachments_boards = boardsAllowedTo('view_attachments');
+
+ // No boards? Adios amigo.
+ if (empty($attachments_boards))
+ return array();
+
+ // Is it an array?
+ if (!is_array($attachment_ext))
+ $attachment_ext = array($attachment_ext);
+
+ // Lets build the query.
+ $request = $smcFunc['db_query']('', '
+ SELECT
+ att.id_attach, att.id_msg, att.filename, IFNULL(att.size, 0) AS filesize, att.downloads, mem.id_member,
+ IFNULL(mem.real_name, m.poster_name) AS poster_name, m.id_topic, m.subject, t.id_board, m.poster_time,
+ att.width, att.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ', IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
+ FROM {db_prefix}attachments AS att
+ INNER JOIN {db_prefix}messages AS m ON (m.id_msg = att.id_msg)
+ INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
+ LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
+ LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = att.id_thumb)') . '
+ WHERE att.attachment_type = 0' . ($attachments_boards === array(0) ? '' : '
+ AND m.id_board IN ({array_int:boards_can_see})') . (!empty($attachment_ext) ? '
+ AND att.fileext IN ({array_string:attachment_ext})' : '') .
+ (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
+ AND t.approved = {int:is_approved}
+ AND m.approved = {int:is_approved}
+ AND att.approved = {int:is_approved}') . '
+ ORDER BY att.id_attach DESC
+ LIMIT {int:num_attachments}',
+ array(
+ 'boards_can_see' => $attachments_boards,
+ 'attachment_ext' => $attachment_ext,
+ 'num_attachments' => $num_attachments,
+ 'is_approved' => 1,
+ )
+ );
+
+ // We have something.
+ $attachments = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ $filename = preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '\\1;', htmlspecialchars($row['filename']));
+
+ // Is it an image?
+ $attachments[$row['id_attach']] = array(
+ 'member' => array(
+ 'id' => $row['id_member'],
+ 'name' => $row['poster_name'],
+ 'link' => empty($row['id_member']) ? $row['poster_name'] : '' . $row['poster_name'] . '',
+ ),
+ 'file' => array(
+ 'filename' => $filename,
+ 'filesize' => round($row['filesize'] /1024, 2) . $txt['kilobyte'],
+ 'downloads' => $row['downloads'],
+ 'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $row['id_attach'],
+ 'link' => '' . $filename . '',
+ 'is_image' => !empty($row['width']) && !empty($row['height']) && !empty($modSettings['attachmentShowImages']),
+ ),
+ 'topic' => array(
+ 'id' => $row['id_topic'],
+ 'subject' => $row['subject'],
+ 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'],
+ 'link' => '' . $row['subject'] . '',
+ 'time' => timeformat($row['poster_time']),
+ ),
+ );
+
+ // Images.
+ if ($attachments[$row['id_attach']]['file']['is_image'])
+ {
+ $id_thumb = empty($row['id_thumb']) ? $row['id_attach'] : $row['id_thumb'];
+ $attachments[$row['id_attach']]['file']['image'] = array(
+ 'id' => $id_thumb,
+ 'width' => $row['width'],
+ 'height' => $row['height'],
+ 'img' => '',
+ 'thumb' => '',
+ 'href' => $scripturl . '?action=dlattach;topic=' . $row['id_topic'] . '.0;attach=' . $id_thumb . ';image',
+ 'link' => '',
+ );
+ }
+ }
+ $smcFunc['db_free_result']($request);
+
+ // So you just want an array? Here you can have it.
+ if ($output_method == 'array' || empty($attachments))
+ return $attachments;
+
+ // Give them the default.
+ echo '
+
+
+
', $txt['file'], '
+
', $txt['posted_by'], '
+
', $txt['downloads'], '
+
', $txt['filesize'], '
+
';
+ foreach ($attachments as $attach)
+ echo '
+
+
', $attach['file']['link'], '
+
', $attach['member']['link'], '
+
', $attach['file']['downloads'], '
+
', $attach['file']['filesize'], '
+
';
+ echo '
+
';
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Settings.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Settings.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,66 @@
+
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Settings_bak.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Settings_bak.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,67 @@
+
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/afro.gif
Binary file forum/Smileys/aaron/afro.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/angel.gif
Binary file forum/Smileys/aaron/angel.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/angry.gif
Binary file forum/Smileys/aaron/angry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/azn.gif
Binary file forum/Smileys/aaron/azn.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/blank.gif
Binary file forum/Smileys/aaron/blank.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/cheesy.gif
Binary file forum/Smileys/aaron/cheesy.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/cool.gif
Binary file forum/Smileys/aaron/cool.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/cry.gif
Binary file forum/Smileys/aaron/cry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/embarrassed.gif
Binary file forum/Smileys/aaron/embarrassed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/evil.gif
Binary file forum/Smileys/aaron/evil.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/grin.gif
Binary file forum/Smileys/aaron/grin.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/huh.gif
Binary file forum/Smileys/aaron/huh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/index.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Smileys/aaron/index.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/kiss.gif
Binary file forum/Smileys/aaron/kiss.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/laugh.gif
Binary file forum/Smileys/aaron/laugh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/lipsrsealed.gif
Binary file forum/Smileys/aaron/lipsrsealed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/police.gif
Binary file forum/Smileys/aaron/police.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/rolleyes.gif
Binary file forum/Smileys/aaron/rolleyes.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/sad.gif
Binary file forum/Smileys/aaron/sad.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/shocked.gif
Binary file forum/Smileys/aaron/shocked.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/smiley.gif
Binary file forum/Smileys/aaron/smiley.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/tongue.gif
Binary file forum/Smileys/aaron/tongue.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/undecided.gif
Binary file forum/Smileys/aaron/undecided.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/aaron/wink.gif
Binary file forum/Smileys/aaron/wink.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/afro.gif
Binary file forum/Smileys/akyhne/afro.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/angel.gif
Binary file forum/Smileys/akyhne/angel.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/angry.gif
Binary file forum/Smileys/akyhne/angry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/azn.gif
Binary file forum/Smileys/akyhne/azn.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/blank.gif
Binary file forum/Smileys/akyhne/blank.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/cheesy.gif
Binary file forum/Smileys/akyhne/cheesy.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/cool.gif
Binary file forum/Smileys/akyhne/cool.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/cry.gif
Binary file forum/Smileys/akyhne/cry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/embarrassed.gif
Binary file forum/Smileys/akyhne/embarrassed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/evil.gif
Binary file forum/Smileys/akyhne/evil.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/grin.gif
Binary file forum/Smileys/akyhne/grin.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/huh.gif
Binary file forum/Smileys/akyhne/huh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/index.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Smileys/akyhne/index.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/kiss.gif
Binary file forum/Smileys/akyhne/kiss.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/laugh.gif
Binary file forum/Smileys/akyhne/laugh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/lipsrsealed.gif
Binary file forum/Smileys/akyhne/lipsrsealed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/police.gif
Binary file forum/Smileys/akyhne/police.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/rolleyes.gif
Binary file forum/Smileys/akyhne/rolleyes.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/sad.gif
Binary file forum/Smileys/akyhne/sad.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/shocked.gif
Binary file forum/Smileys/akyhne/shocked.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/smiley.gif
Binary file forum/Smileys/akyhne/smiley.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/tongue.gif
Binary file forum/Smileys/akyhne/tongue.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/undecided.gif
Binary file forum/Smileys/akyhne/undecided.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/akyhne/wink.gif
Binary file forum/Smileys/akyhne/wink.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/afro.gif
Binary file forum/Smileys/classic/afro.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/angel.gif
Binary file forum/Smileys/classic/angel.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/angry.gif
Binary file forum/Smileys/classic/angry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/azn.gif
Binary file forum/Smileys/classic/azn.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/cheesy.gif
Binary file forum/Smileys/classic/cheesy.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/cool.gif
Binary file forum/Smileys/classic/cool.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/cry.gif
Binary file forum/Smileys/classic/cry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/embarrassed.gif
Binary file forum/Smileys/classic/embarrassed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/evil.gif
Binary file forum/Smileys/classic/evil.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/grin.gif
Binary file forum/Smileys/classic/grin.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/huh.gif
Binary file forum/Smileys/classic/huh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/index.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Smileys/classic/index.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/kiss.gif
Binary file forum/Smileys/classic/kiss.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/laugh.gif
Binary file forum/Smileys/classic/laugh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/lipsrsealed.gif
Binary file forum/Smileys/classic/lipsrsealed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/police.gif
Binary file forum/Smileys/classic/police.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/rolleyes.gif
Binary file forum/Smileys/classic/rolleyes.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/sad.gif
Binary file forum/Smileys/classic/sad.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/shocked.gif
Binary file forum/Smileys/classic/shocked.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/smiley.gif
Binary file forum/Smileys/classic/smiley.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/tongue.gif
Binary file forum/Smileys/classic/tongue.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/undecided.gif
Binary file forum/Smileys/classic/undecided.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/classic/wink.gif
Binary file forum/Smileys/classic/wink.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/afro.gif
Binary file forum/Smileys/default/afro.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/angel.gif
Binary file forum/Smileys/default/angel.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/angry.gif
Binary file forum/Smileys/default/angry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/azn.gif
Binary file forum/Smileys/default/azn.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/blank.gif
Binary file forum/Smileys/default/blank.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/cheesy.gif
Binary file forum/Smileys/default/cheesy.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/cool.gif
Binary file forum/Smileys/default/cool.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/cry.gif
Binary file forum/Smileys/default/cry.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/embarrassed.gif
Binary file forum/Smileys/default/embarrassed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/evil.gif
Binary file forum/Smileys/default/evil.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/grin.gif
Binary file forum/Smileys/default/grin.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/huh.gif
Binary file forum/Smileys/default/huh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/index.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Smileys/default/index.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/kiss.gif
Binary file forum/Smileys/default/kiss.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/laugh.gif
Binary file forum/Smileys/default/laugh.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/lipsrsealed.gif
Binary file forum/Smileys/default/lipsrsealed.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/police.gif
Binary file forum/Smileys/default/police.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/rolleyes.gif
Binary file forum/Smileys/default/rolleyes.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/sad.gif
Binary file forum/Smileys/default/sad.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/shocked.gif
Binary file forum/Smileys/default/shocked.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/smiley.gif
Binary file forum/Smileys/default/smiley.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/tongue.gif
Binary file forum/Smileys/default/tongue.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/undecided.gif
Binary file forum/Smileys/default/undecided.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/default/wink.gif
Binary file forum/Smileys/default/wink.gif has changed
diff -r 72f59aa7e503 -r e3e11437ecea forum/Smileys/index.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Smileys/index.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Admin.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Admin.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,999 @@
+ array(
+ 'title' => $txt['admin_main'],
+ 'permission' => array('admin_forum', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_boards', 'manage_smileys', 'manage_attachments'),
+ 'areas' => array(
+ 'index' => array(
+ 'label' => $txt['admin_center'],
+ 'function' => 'AdminHome',
+ 'icon' => 'administration.gif',
+ ),
+ 'credits' => array(
+ 'label' => $txt['support_credits_title'],
+ 'function' => 'AdminHome',
+ 'icon' => 'support.gif',
+ ),
+ 'news' => array(
+ 'label' => $txt['news_title'],
+ 'file' => 'ManageNews.php',
+ 'function' => 'ManageNews',
+ 'icon' => 'news.gif',
+ 'permission' => array('edit_news', 'send_mail', 'admin_forum'),
+ 'subsections' => array(
+ 'editnews' => array($txt['admin_edit_news'], 'edit_news'),
+ 'mailingmembers' => array($txt['admin_newsletters'], 'send_mail'),
+ 'settings' => array($txt['settings'], 'admin_forum'),
+ ),
+ ),
+ 'packages' => array(
+ 'label' => $txt['package'],
+ 'file' => 'Packages.php',
+ 'function' => 'Packages',
+ 'permission' => array('admin_forum'),
+ 'icon' => 'packages.gif',
+ 'subsections' => array(
+ 'browse' => array($txt['browse_packages']),
+ 'packageget' => array($txt['download_packages'], 'url' => $scripturl . '?action=admin;area=packages;sa=packageget;get'),
+ 'installed' => array($txt['installed_packages']),
+ 'perms' => array($txt['package_file_perms']),
+ 'options' => array($txt['package_settings']),
+ ),
+ ),
+ 'search' => array(
+ 'function' => 'AdminSearch',
+ 'permission' => array('admin_forum'),
+ 'select' => 'index'
+ ),
+ ),
+ ),
+ 'config' => array(
+ 'title' => $txt['admin_config'],
+ 'permission' => array('admin_forum'),
+ 'areas' => array(
+ 'corefeatures' => array(
+ 'label' => $txt['core_settings_title'],
+ 'file' => 'ManageSettings.php',
+ 'function' => 'ModifyCoreFeatures',
+ 'icon' => 'corefeatures.gif',
+ ),
+ 'featuresettings' => array(
+ 'label' => $txt['modSettings_title'],
+ 'file' => 'ManageSettings.php',
+ 'function' => 'ModifyFeatureSettings',
+ 'icon' => 'features.gif',
+ 'subsections' => array(
+ 'basic' => array($txt['mods_cat_features']),
+ 'layout' => array($txt['mods_cat_layout']),
+ 'karma' => array($txt['karma'], 'enabled' => in_array('k', $context['admin_features'])),
+ 'sig' => array($txt['signature_settings_short']),
+ 'profile' => array($txt['custom_profile_shorttitle'], 'enabled' => in_array('cp', $context['admin_features'])),
+ ),
+ ),
+ 'securitysettings' => array(
+ 'label' => $txt['admin_security_moderation'],
+ 'file' => 'ManageSettings.php',
+ 'function' => 'ModifySecuritySettings',
+ 'icon' => 'security.gif',
+ 'subsections' => array(
+ 'general' => array($txt['mods_cat_security_general']),
+ 'spam' => array($txt['antispam_title']),
+ 'moderation' => array($txt['moderation_settings_short'], 'enabled' => substr($modSettings['warning_settings'], 0, 1) == 1),
+ ),
+ ),
+ 'languages' => array(
+ 'label' => $txt['language_configuration'],
+ 'file' => 'ManageServer.php',
+ 'function' => 'ManageLanguages',
+ 'icon' => 'languages.gif',
+ 'subsections' => array(
+ 'edit' => array($txt['language_edit']),
+ 'add' => array($txt['language_add']),
+ 'settings' => array($txt['language_settings']),
+ ),
+ ),
+ 'serversettings' => array(
+ 'label' => $txt['admin_server_settings'],
+ 'file' => 'ManageServer.php',
+ 'function' => 'ModifySettings',
+ 'icon' => 'server.gif',
+ 'subsections' => array(
+ 'general' => array($txt['general_settings']),
+ 'database' => array($txt['database_paths_settings']),
+ 'cookie' => array($txt['cookies_sessions_settings']),
+ 'cache' => array($txt['caching_settings']),
+ 'loads' => array($txt['load_balancing_settings']),
+ ),
+ ),
+ 'current_theme' => array(
+ 'label' => $txt['theme_current_settings'],
+ 'file' => 'Themes.php',
+ 'function' => 'ThemesMain',
+ 'custom_url' => $scripturl . '?action=admin;area=theme;sa=settings;th=' . $settings['theme_id'],
+ 'icon' => 'current_theme.gif',
+ ),
+ 'theme' => array(
+ 'label' => $txt['theme_admin'],
+ 'file' => 'Themes.php',
+ 'function' => 'ThemesMain',
+ 'custom_url' => $scripturl . '?action=admin;area=theme;sa=admin',
+ 'icon' => 'themes.gif',
+ 'subsections' => array(
+ 'admin' => array($txt['themeadmin_admin_title']),
+ 'list' => array($txt['themeadmin_list_title']),
+ 'reset' => array($txt['themeadmin_reset_title']),
+ 'edit' => array($txt['themeadmin_edit_title']),
+ ),
+ ),
+ 'modsettings' => array(
+ 'label' => $txt['admin_modifications'],
+ 'file' => 'ManageSettings.php',
+ 'function' => 'ModifyModSettings',
+ 'icon' => 'modifications.gif',
+ 'subsections' => array(
+ 'general' => array($txt['mods_cat_modifications_misc']),
+ // Mod Authors for a "ADD AFTER" on this line. Ensure you end your change with a comma. For example:
+ // 'shout' => array($txt['shout']),
+ // Note the comma!! The setting with automatically appear with the first mod to be added.
+ ),
+ ),
+ ),
+ ),
+ 'layout' => array(
+ 'title' => $txt['layout_controls'],
+ 'permission' => array('manage_boards', 'admin_forum', 'manage_smileys', 'manage_attachments', 'moderate_forum'),
+ 'areas' => array(
+ 'manageboards' => array(
+ 'label' => $txt['admin_boards'],
+ 'file' => 'ManageBoards.php',
+ 'function' => 'ManageBoards',
+ 'icon' => 'boards.gif',
+ 'permission' => array('manage_boards'),
+ 'subsections' => array(
+ 'main' => array($txt['boardsEdit']),
+ 'newcat' => array($txt['mboards_new_cat']),
+ 'settings' => array($txt['settings'], 'admin_forum'),
+ ),
+ ),
+ 'postsettings' => array(
+ 'label' => $txt['manageposts'],
+ 'file' => 'ManagePosts.php',
+ 'function' => 'ManagePostSettings',
+ 'permission' => array('admin_forum'),
+ 'icon' => 'posts.gif',
+ 'subsections' => array(
+ 'posts' => array($txt['manageposts_settings']),
+ 'bbc' => array($txt['manageposts_bbc_settings']),
+ 'censor' => array($txt['admin_censored_words']),
+ 'topics' => array($txt['manageposts_topic_settings']),
+ ),
+ ),
+ 'managecalendar' => array(
+ 'label' => $txt['manage_calendar'],
+ 'file' => 'ManageCalendar.php',
+ 'function' => 'ManageCalendar',
+ 'icon' => 'calendar.gif',
+ 'permission' => array('admin_forum'),
+ 'enabled' => in_array('cd', $context['admin_features']),
+ 'subsections' => array(
+ 'holidays' => array($txt['manage_holidays'], 'admin_forum', 'enabled' => !empty($modSettings['cal_enabled'])),
+ 'settings' => array($txt['calendar_settings'], 'admin_forum'),
+ ),
+ ),
+ 'managesearch' => array(
+ 'label' => $txt['manage_search'],
+ 'file' => 'ManageSearch.php',
+ 'function' => 'ManageSearch',
+ 'icon' => 'search.gif',
+ 'permission' => array('admin_forum'),
+ 'subsections' => array(
+ 'weights' => array($txt['search_weights']),
+ 'method' => array($txt['search_method']),
+ 'settings' => array($txt['settings']),
+ ),
+ ),
+ 'smileys' => array(
+ 'label' => $txt['smileys_manage'],
+ 'file' => 'ManageSmileys.php',
+ 'function' => 'ManageSmileys',
+ 'icon' => 'smiley.gif',
+ 'permission' => array('manage_smileys'),
+ 'subsections' => array(
+ 'editsets' => array($txt['smiley_sets']),
+ 'addsmiley' => array($txt['smileys_add'], 'enabled' => !empty($modSettings['smiley_enable'])),
+ 'editsmileys' => array($txt['smileys_edit'], 'enabled' => !empty($modSettings['smiley_enable'])),
+ 'setorder' => array($txt['smileys_set_order'], 'enabled' => !empty($modSettings['smiley_enable'])),
+ 'editicons' => array($txt['icons_edit_message_icons'], 'enabled' => !empty($modSettings['messageIcons_enable'])),
+ 'settings' => array($txt['settings']),
+ ),
+ ),
+ 'manageattachments' => array(
+ 'label' => $txt['attachments_avatars'],
+ 'file' => 'ManageAttachments.php',
+ 'function' => 'ManageAttachments',
+ 'icon' => 'attachment.gif',
+ 'permission' => array('manage_attachments'),
+ 'subsections' => array(
+ 'browse' => array($txt['attachment_manager_browse']),
+ 'attachments' => array($txt['attachment_manager_settings']),
+ 'avatars' => array($txt['attachment_manager_avatar_settings']),
+ 'maintenance' => array($txt['attachment_manager_maintenance']),
+ ),
+ ),
+ ),
+ ),
+ 'members' => array(
+ 'title' => $txt['admin_manage_members'],
+ 'permission' => array('moderate_forum', 'manage_membergroups', 'manage_bans', 'manage_permissions', 'admin_forum'),
+ 'areas' => array(
+ 'viewmembers' => array(
+ 'label' => $txt['admin_users'],
+ 'file' => 'ManageMembers.php',
+ 'function' => 'ViewMembers',
+ 'icon' => 'members.gif',
+ 'permission' => array('moderate_forum'),
+ 'subsections' => array(
+ 'all' => array($txt['view_all_members']),
+ 'search' => array($txt['mlist_search']),
+ ),
+ ),
+ 'membergroups' => array(
+ 'label' => $txt['admin_groups'],
+ 'file' => 'ManageMembergroups.php',
+ 'function' => 'ModifyMembergroups',
+ 'icon' => 'membergroups.gif',
+ 'permission' => array('manage_membergroups'),
+ 'subsections' => array(
+ 'index' => array($txt['membergroups_edit_groups'], 'manage_membergroups'),
+ 'add' => array($txt['membergroups_new_group'], 'manage_membergroups'),
+ 'settings' => array($txt['settings'], 'admin_forum'),
+ ),
+ ),
+ 'permissions' => array(
+ 'label' => $txt['edit_permissions'],
+ 'file' => 'ManagePermissions.php',
+ 'function' => 'ModifyPermissions',
+ 'icon' => 'permissions.gif',
+ 'permission' => array('manage_permissions'),
+ 'subsections' => array(
+ 'index' => array($txt['permissions_groups'], 'manage_permissions'),
+ 'board' => array($txt['permissions_boards'], 'manage_permissions'),
+ 'profiles' => array($txt['permissions_profiles'], 'manage_permissions'),
+ 'postmod' => array($txt['permissions_post_moderation'], 'manage_permissions', 'enabled' => $modSettings['postmod_active']),
+ 'settings' => array($txt['settings'], 'admin_forum'),
+ ),
+ ),
+ 'regcenter' => array(
+ 'label' => $txt['registration_center'],
+ 'file' => 'ManageRegistration.php',
+ 'function' => 'RegCenter',
+ 'icon' => 'regcenter.gif',
+ 'permission' => array('admin_forum', 'moderate_forum'),
+ 'subsections' => array(
+ 'register' => array($txt['admin_browse_register_new'], 'moderate_forum'),
+ 'agreement' => array($txt['registration_agreement'], 'admin_forum'),
+ 'reservednames' => array($txt['admin_reserved_set'], 'admin_forum'),
+ 'settings' => array($txt['settings'], 'admin_forum'),
+ ),
+ ),
+ 'ban' => array(
+ 'label' => $txt['ban_title'],
+ 'file' => 'ManageBans.php',
+ 'function' => 'Ban',
+ 'icon' => 'ban.gif',
+ 'permission' => 'manage_bans',
+ 'subsections' => array(
+ 'list' => array($txt['ban_edit_list']),
+ 'add' => array($txt['ban_add_new']),
+ 'browse' => array($txt['ban_trigger_browse']),
+ 'log' => array($txt['ban_log']),
+ ),
+ ),
+ 'paidsubscribe' => array(
+ 'label' => $txt['paid_subscriptions'],
+ 'enabled' => in_array('ps', $context['admin_features']),
+ 'file' => 'ManagePaid.php',
+ 'icon' => 'paid.gif',
+ 'function' => 'ManagePaidSubscriptions',
+ 'permission' => 'admin_forum',
+ 'subsections' => array(
+ 'view' => array($txt['paid_subs_view']),
+ 'settings' => array($txt['settings']),
+ ),
+ ),
+ 'sengines' => array(
+ 'label' => $txt['search_engines'],
+ 'enabled' => in_array('sp', $context['admin_features']),
+ 'file' => 'ManageSearchEngines.php',
+ 'icon' => 'engines.gif',
+ 'function' => 'SearchEngines',
+ 'permission' => 'admin_forum',
+ 'subsections' => array(
+ 'stats' => array($txt['spider_stats']),
+ 'logs' => array($txt['spider_logs']),
+ 'spiders' => array($txt['spiders']),
+ 'settings' => array($txt['settings']),
+ ),
+ ),
+ ),
+ ),
+ 'maintenance' => array(
+ 'title' => $txt['admin_maintenance'],
+ 'permission' => array('admin_forum'),
+ 'areas' => array(
+ 'maintain' => array(
+ 'label' => $txt['maintain_title'],
+ 'file' => 'ManageMaintenance.php',
+ 'icon' => 'maintain.gif',
+ 'function' => 'ManageMaintenance',
+ 'subsections' => array(
+ 'routine' => array($txt['maintain_sub_routine'], 'admin_forum'),
+ 'database' => array($txt['maintain_sub_database'], 'admin_forum'),
+ 'members' => array($txt['maintain_sub_members'], 'admin_forum'),
+ 'topics' => array($txt['maintain_sub_topics'], 'admin_forum'),
+ ),
+ ),
+ 'scheduledtasks' => array(
+ 'label' => $txt['maintain_tasks'],
+ 'file' => 'ManageScheduledTasks.php',
+ 'icon' => 'scheduled.gif',
+ 'function' => 'ManageScheduledTasks',
+ 'subsections' => array(
+ 'tasks' => array($txt['maintain_tasks'], 'admin_forum'),
+ 'tasklog' => array($txt['scheduled_log'], 'admin_forum'),
+ ),
+ ),
+ 'mailqueue' => array(
+ 'label' => $txt['mailqueue_title'],
+ 'file' => 'ManageMail.php',
+ 'function' => 'ManageMail',
+ 'icon' => 'mail.gif',
+ 'subsections' => array(
+ 'browse' => array($txt['mailqueue_browse'], 'admin_forum'),
+ 'settings' => array($txt['mailqueue_settings'], 'admin_forum'),
+ ),
+ ),
+ 'reports' => array(
+ 'enabled' => in_array('rg', $context['admin_features']),
+ 'label' => $txt['generate_reports'],
+ 'file' => 'Reports.php',
+ 'function' => 'ReportsMain',
+ 'icon' => 'reports.gif',
+ ),
+ 'logs' => array(
+ 'label' => $txt['logs'],
+ 'function' => 'AdminLogs',
+ 'icon' => 'logs.gif',
+ 'subsections' => array(
+ 'errorlog' => array($txt['errlog'], 'admin_forum', 'enabled' => !empty($modSettings['enableErrorLogging']), 'url' => $scripturl . '?action=admin;area=logs;sa=errorlog;desc'),
+ 'adminlog' => array($txt['admin_log'], 'admin_forum', 'enabled' => in_array('ml', $context['admin_features'])),
+ 'modlog' => array($txt['moderation_log'], 'admin_forum', 'enabled' => in_array('ml', $context['admin_features'])),
+ 'banlog' => array($txt['ban_log'], 'manage_bans'),
+ 'spiderlog' => array($txt['spider_logs'], 'admin_forum', 'enabled' => in_array('sp', $context['admin_features'])),
+ 'tasklog' => array($txt['scheduled_log'], 'admin_forum'),
+ 'pruning' => array($txt['pruning_title'], 'admin_forum'),
+ ),
+ ),
+ 'repairboards' => array(
+ 'label' => $txt['admin_repair'],
+ 'file' => 'RepairBoards.php',
+ 'function' => 'RepairBoards',
+ 'select' => 'maintain',
+ 'hidden' => true,
+ ),
+ ),
+ ),
+ );
+
+ // Any files to include for administration?
+ if (!empty($modSettings['integrate_admin_include']))
+ {
+ $admin_includes = explode(',', $modSettings['integrate_admin_include']);
+ foreach ($admin_includes as $include)
+ {
+ $include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
+ if (file_exists($include))
+ require_once($include);
+ }
+ }
+
+ // Let them modify admin areas easily.
+ call_integration_hook('integrate_admin_areas', array(&$admin_areas));
+
+ // Make sure the administrator has a valid session...
+ validateSession();
+
+ // Actually create the menu!
+ $admin_include_data = createMenu($admin_areas);
+ unset($admin_areas);
+
+ // Nothing valid?
+ if ($admin_include_data == false)
+ fatal_lang_error('no_access', false);
+
+ // Build the link tree.
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=admin',
+ 'name' => $txt['admin_center'],
+ );
+ if (isset($admin_include_data['current_area']) && $admin_include_data['current_area'] != 'index')
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=admin;area=' . $admin_include_data['current_area'] . ';' . $context['session_var'] . '=' . $context['session_id'],
+ 'name' => $admin_include_data['label'],
+ );
+ if (!empty($admin_include_data['current_subsection']) && $admin_include_data['subsections'][$admin_include_data['current_subsection']][0] != $admin_include_data['label'])
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=admin;area=' . $admin_include_data['current_area'] . ';sa=' . $admin_include_data['current_subsection'] . ';' . $context['session_var'] . '=' . $context['session_id'],
+ 'name' => $admin_include_data['subsections'][$admin_include_data['current_subsection']][0],
+ );
+
+ // Make a note of the Unique ID for this menu.
+ $context['admin_menu_id'] = $context['max_menu_id'];
+ $context['admin_menu_name'] = 'menu_data_' . $context['admin_menu_id'];
+
+ // Why on the admin are we?
+ $context['admin_area'] = $admin_include_data['current_area'];
+
+ // Now - finally - call the right place!
+ if (isset($admin_include_data['file']))
+ require_once($sourcedir . '/' . $admin_include_data['file']);
+
+ $admin_include_data['function']();
+}
+
+// The main administration section.
+function AdminHome()
+{
+ global $sourcedir, $forum_version, $txt, $scripturl, $context, $user_info, $boardurl, $modSettings, $smcFunc;
+
+ // You have to be able to do at least one of the below to see this page.
+ isAllowedTo(array('admin_forum', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_boards', 'manage_smileys', 'manage_attachments'));
+
+ // Find all of this forum's administrators...
+ require_once($sourcedir . '/Subs-Membergroups.php');
+ if (listMembergroupMembers_Href($context['administrators'], 1, 32) && allowedTo('manage_membergroups'))
+ {
+ // Add a 'more'-link if there are more than 32.
+ $context['more_admins_link'] = '' . $txt['more'] . '';
+ }
+
+ // Load the credits stuff.
+ require_once($sourcedir . '/Who.php');
+ Credits(true);
+
+ // This makes it easier to get the latest news with your time format.
+ $context['time_format'] = urlencode($user_info['time_format']);
+
+ $context['current_versions'] = array(
+ 'php' => array('title' => $txt['support_versions_php'], 'version' => PHP_VERSION),
+ 'db' => array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => ''),
+ 'server' => array('title' => $txt['support_versions_server'], 'version' => $_SERVER['SERVER_SOFTWARE']),
+ );
+ $context['forum_version'] = $forum_version;
+
+ // Get a list of current server versions.
+ require_once($sourcedir . '/Subs-Admin.php');
+ $checkFor = array(
+ 'gd',
+ 'db_server',
+ 'mmcache',
+ 'eaccelerator',
+ 'phpa',
+ 'apc',
+ 'memcache',
+ 'xcache',
+ 'php',
+ 'server',
+ );
+ $context['current_versions'] = getServerVersions($checkFor);
+
+ $context['can_admin'] = allowedTo('admin_forum');
+
+ $context['sub_template'] = $context['admin_area'] == 'credits' ? 'credits' : 'admin';
+ $context['page_title'] = $context['admin_area'] == 'credits' ? $txt['support_credits_title'] : $txt['admin_center'];
+
+ // The format of this array is: permission, action, title, description, icon.
+ $quick_admin_tasks = array(
+ array('', 'credits', 'support_credits_title', 'support_credits_info', 'support_and_credits.png'),
+ array('admin_forum', 'featuresettings', 'modSettings_title', 'modSettings_info', 'features_and_options.png'),
+ array('admin_forum', 'maintain', 'maintain_title', 'maintain_info', 'forum_maintenance.png'),
+ array('manage_permissions', 'permissions', 'edit_permissions', 'edit_permissions_info', 'permissions.png'),
+ array('admin_forum', 'theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id'], 'theme_admin', 'theme_admin_info', 'themes_and_layout.png'),
+ array('admin_forum', 'packages', 'package', 'package_info', 'packages.png'),
+ array('manage_smileys', 'smileys', 'smileys_manage', 'smileys_manage_info', 'smilies_and_messageicons.png'),
+ array('moderate_forum', 'viewmembers', 'admin_users', 'member_center_info', 'members.png'),
+ );
+
+ $context['quick_admin_tasks'] = array();
+ foreach ($quick_admin_tasks as $task)
+ {
+ if (!empty($task[0]) && !allowedTo($task[0]))
+ continue;
+
+ $context['quick_admin_tasks'][] = array(
+ 'href' => $scripturl . '?action=admin;area=' . $task[1],
+ 'link' => '' . $txt[$task[2]] . '',
+ 'title' => $txt[$task[2]],
+ 'description' => $txt[$task[3]],
+ 'icon' => $task[4],
+ 'is_last' => false
+ );
+ }
+
+ if (count($context['quick_admin_tasks']) % 2 == 1)
+ {
+ $context['quick_admin_tasks'][] = array(
+ 'href' => '',
+ 'link' => '',
+ 'title' => '',
+ 'description' => '',
+ 'is_last' => true
+ );
+ $context['quick_admin_tasks'][count($context['quick_admin_tasks']) - 2]['is_last'] = true;
+ }
+ elseif (count($context['quick_admin_tasks']) != 0)
+ {
+ $context['quick_admin_tasks'][count($context['quick_admin_tasks']) - 1]['is_last'] = true;
+ $context['quick_admin_tasks'][count($context['quick_admin_tasks']) - 2]['is_last'] = true;
+ }
+
+ // Lastly, fill in the blanks in the support resources paragraphs.
+ $txt['support_resources_p1'] = sprintf($txt['support_resources_p1'],
+ 'http://wiki.simplemachines.org/',
+ 'http://wiki.simplemachines.org/smf/features2',
+ 'http://wiki.simplemachines.org/smf/options2',
+ 'http://wiki.simplemachines.org/smf/themes2',
+ 'http://wiki.simplemachines.org/smf/packages2'
+ );
+ $txt['support_resources_p2'] = sprintf($txt['support_resources_p2'],
+ 'http://www.simplemachines.org/community/',
+ 'http://www.simplemachines.org/redirect/english_support',
+ 'http://www.simplemachines.org/redirect/international_support_boards',
+ 'http://www.simplemachines.org/redirect/smf_support',
+ 'http://www.simplemachines.org/redirect/customize_support'
+ );
+}
+
+// Get one of the admin information files from Simple Machines.
+function DisplayAdminFile()
+{
+ global $context, $modSettings, $smcFunc;
+
+ @ini_set('memory_limit', '32M');
+
+ if (empty($_REQUEST['filename']) || !is_string($_REQUEST['filename']))
+ fatal_lang_error('no_access', false);
+
+ $request = $smcFunc['db_query']('', '
+ SELECT data, filetype
+ FROM {db_prefix}admin_info_files
+ WHERE filename = {string:current_filename}
+ LIMIT 1',
+ array(
+ 'current_filename' => $_REQUEST['filename'],
+ )
+ );
+
+ if ($smcFunc['db_num_rows']($request) == 0)
+ fatal_lang_error('admin_file_not_found', true, array($_REQUEST['filename']));
+
+ list ($file_data, $filetype) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ // !!! Temp.
+ // Figure out if sesc is still being used.
+ if (strpos($file_data, ';sesc=') !== false)
+ $file_data = '
+if (!(\'smfForum_sessionvar\' in window))
+ window.smfForum_sessionvar = \'sesc\';
+' . strtr($file_data, array(';sesc=' => ';\' + window.smfForum_sessionvar + \'='));
+
+ $context['template_layers'] = array();
+ // Lets make sure we aren't going to output anything nasty.
+ @ob_end_clean();
+ if (!empty($modSettings['enableCompressedOutput']))
+ @ob_start('ob_gzhandler');
+ else
+ @ob_start();
+
+ // Make sure they know what type of file we are.
+ header('Content-Type: ' . $filetype);
+ echo $file_data;
+ obExit(false);
+}
+
+// This allocates out all the search stuff.
+function AdminSearch()
+{
+ global $txt, $context, $smcFunc, $sourcedir;
+
+ isAllowedTo('admin_forum');
+
+ // What can we search for?
+ $subactions = array(
+ 'internal' => 'AdminSearchInternal',
+ 'online' => 'AdminSearchOM',
+ 'member' => 'AdminSearchMember',
+ );
+
+ $context['search_type'] = !isset($_REQUEST['search_type']) || !isset($subactions[$_REQUEST['search_type']]) ? 'internal' : $_REQUEST['search_type'];
+ $context['search_term'] = isset($_REQUEST['search_term']) ? $smcFunc['htmlspecialchars']($_REQUEST['search_term'], ENT_QUOTES) : '';
+
+ $context['sub_template'] = 'admin_search_results';
+ $context['page_title'] = $txt['admin_search_results'];
+
+ // Keep track of what the admin wants.
+ if (empty($context['admin_preferences']['sb']) || $context['admin_preferences']['sb'] != $context['search_type'])
+ {
+ $context['admin_preferences']['sb'] = $context['search_type'];
+
+ // Update the preferences.
+ require_once($sourcedir . '/Subs-Admin.php');
+ updateAdminPreferences();
+ }
+
+ if (trim($context['search_term']) == '')
+ $context['search_results'] = array();
+ else
+ $subactions[$context['search_type']]();
+}
+
+// A complicated but relatively quick internal search.
+function AdminSearchInternal()
+{
+ global $context, $txt, $helptxt, $scripturl, $sourcedir;
+
+ // Try to get some more memory.
+ @ini_set('memory_limit', '128M');
+
+ // Load a lot of language files.
+ $language_files = array(
+ 'Help', 'ManageMail', 'ManageSettings', 'ManageCalendar', 'ManageBoards', 'ManagePaid', 'ManagePermissions', 'Search',
+ 'Login', 'ManageSmileys',
+ );
+ loadLanguage(implode('+', $language_files));
+
+ // All the files we need to include.
+ $include_files = array(
+ 'ManageSettings', 'ManageBoards', 'ManageNews', 'ManageAttachments', 'ManageCalendar', 'ManageMail', 'ManagePaid', 'ManagePermissions',
+ 'ManagePosts', 'ManageRegistration', 'ManageSearch', 'ManageSearchEngines', 'ManageServer', 'ManageSmileys',
+ );
+ foreach ($include_files as $file)
+ require_once($sourcedir . '/' . $file . '.php');
+
+ /* This is the huge array that defines everything... it's a huge array of items formatted as follows:
+ 0 = Language index (Can be array of indexes) to search through for this setting.
+ 1 = URL for this indexes page.
+ 2 = Help index for help associated with this item (If different from 0)
+ */
+
+ $search_data = array(
+ // All the major sections of the forum.
+ 'sections' => array(
+ ),
+ 'settings' => array(
+ array('COPPA', 'area=regcenter;sa=settings'),
+ array('CAPTCHA', 'area=securitysettings;sa=spam'),
+ ),
+ );
+
+ // Go through the admin menu structure trying to find suitably named areas!
+ foreach ($context[$context['admin_menu_name']]['sections'] as $section)
+ {
+ foreach ($section['areas'] as $menu_key => $menu_item)
+ {
+ $search_data['sections'][] = array($menu_item['label'], 'area=' . $menu_key);
+ if (!empty($menu_item['subsections']))
+ foreach ($menu_item['subsections'] as $key => $sublabel)
+ {
+ if (isset($sublabel['label']))
+ $search_data['sections'][] = array($sublabel['label'], 'area=' . $menu_key . ';sa=' . $key);
+ }
+ }
+ }
+
+ // This is a special array of functions that contain setting data - we query all these to simply pull all setting bits!
+ $settings_search = array(
+ array('ModifyCoreFeatures', 'area=corefeatures'),
+ array('ModifyBasicSettings', 'area=featuresettings;sa=basic'),
+ array('ModifyLayoutSettings', 'area=featuresettings;sa=layout'),
+ array('ModifyKarmaSettings', 'area=featuresettings;sa=karma'),
+ array('ModifySignatureSettings', 'area=featuresettings;sa=sig'),
+ array('ModifyGeneralSecuritySettings', 'area=securitysettings;sa=general'),
+ array('ModifySpamSettings', 'area=securitysettings;sa=spam'),
+ array('ModifyModerationSettings', 'area=securitysettings;sa=moderation'),
+ array('ModifyGeneralModSettings', 'area=modsettings;sa=general'),
+ // Mod authors if you want to be "real freaking good" then add any setting pages for your mod BELOW this line!
+ array('ManageAttachmentSettings', 'area=manageattachments;sa=attachments'),
+ array('ManageAvatarSettings', 'area=manageattachments;sa=avatars'),
+ array('ModifyCalendarSettings', 'area=managecalendar;sa=settings'),
+ array('EditBoardSettings', 'area=manageboards;sa=settings'),
+ array('ModifyMailSettings', 'area=mailqueue;sa=settings'),
+ array('ModifyNewsSettings', 'area=news;sa=settings'),
+ array('GeneralPermissionSettings', 'area=permissions;sa=settings'),
+ array('ModifyPostSettings', 'area=postsettings;sa=posts'),
+ array('ModifyBBCSettings', 'area=postsettings;sa=bbc'),
+ array('ModifyTopicSettings', 'area=postsettings;sa=topics'),
+ array('EditSearchSettings', 'area=managesearch;sa=settings'),
+ array('EditSmileySettings', 'area=smileys;sa=settings'),
+ array('ModifyGeneralSettings', 'area=serversettings;sa=general'),
+ array('ModifyDatabaseSettings', 'area=serversettings;sa=database'),
+ array('ModifyCookieSettings', 'area=serversettings;sa=cookie'),
+ array('ModifyCacheSettings', 'area=serversettings;sa=cache'),
+ array('ModifyLanguageSettings', 'area=languages;sa=settings'),
+ array('ModifyRegistrationSettings', 'area=regcenter;sa=settings'),
+ array('ManageSearchEngineSettings', 'area=sengines;sa=settings'),
+ array('ModifySubscriptionSettings', 'area=paidsubscribe;sa=settings'),
+ array('ModifyPruningSettings', 'area=logs;sa=pruning'),
+ );
+
+ foreach ($settings_search as $setting_area)
+ {
+ // Get a list of their variables.
+ $config_vars = $setting_area[0](true);
+
+ foreach ($config_vars as $var)
+ if (!empty($var[1]) && !in_array($var[0], array('permissions', 'switch')))
+ $search_data['settings'][] = array($var[(isset($var[2]) && in_array($var[2], array('file', 'db'))) ? 0 : 1], $setting_area[1]);
+ }
+
+ $context['page_title'] = $txt['admin_search_results'];
+ $context['search_results'] = array();
+
+ $search_term = strtolower($context['search_term']);
+ // Go through all the search data trying to find this text!
+ foreach ($search_data as $section => $data)
+ {
+ foreach ($data as $item)
+ {
+ $found = false;
+ if (!is_array($item[0]))
+ $item[0] = array($item[0]);
+ foreach ($item[0] as $term)
+ {
+ $lc_term = strtolower($term);
+ if (strpos($lc_term, $search_term) !== false || (isset($txt[$term]) && strpos(strtolower($txt[$term]), $search_term) !== false) || (isset($txt['setting_' . $term]) && strpos(strtolower($txt['setting_' . $term]), $search_term) !== false))
+ {
+ $found = $term;
+ break;
+ }
+ }
+
+ if ($found)
+ {
+ // Format the name - and remove any descriptions the entry may have.
+ $name = isset($txt[$found]) ? $txt[$found] : (isset($txt['setting_' . $found]) ? $txt['setting_' . $found] : $found);
+ $name = preg_replace('~<(?:div|span)\sclass="smalltext">.+?(?:div|span)>~', '', $name);
+
+ $context['search_results'][] = array(
+ 'url' => (substr($item[1], 0, 4) == 'area' ? $scripturl . '?action=admin;' . $item[1] : $item[1]) . ';' . $context['session_var'] . '=' . $context['session_id'] . ((substr($item[1], 0, 4) == 'area' && $section == 'settings' ? '#' . $item[0][0] : '')),
+ 'name' => $name,
+ 'type' => $section,
+ 'help' => shorten_subject(isset($item[2]) ? strip_tags($helptxt[$item2]) : (isset($helptxt[$found]) ? strip_tags($helptxt[$found]) : ''), 255),
+ );
+ }
+ }
+ }
+}
+
+// All this does is pass through to manage members.
+function AdminSearchMember()
+{
+ global $context, $sourcedir;
+
+ require_once($sourcedir . '/ManageMembers.php');
+ $_REQUEST['sa'] = 'query';
+
+ $_POST['membername'] = $context['search_term'];
+
+ ViewMembers();
+}
+
+// This file allows the user to search the SM online manual for a little of help.
+function AdminSearchOM()
+{
+ global $context, $sourcedir;
+
+ $docsURL = 'docs.simplemachines.org';
+ $context['doc_scripturl'] = 'http://docs.simplemachines.org/index.php';
+
+ // Set all the parameters search might expect.
+ $postVars = array(
+ 'search' => $context['search_term'],
+ );
+
+ // Encode the search data.
+ foreach ($postVars as $k => $v)
+ $postVars[$k] = urlencode($k) . '=' . urlencode($v);
+
+ // This is what we will send.
+ $postVars = implode('&', $postVars);
+
+ // Get the results from the doc site.
+ require_once($sourcedir . '/Subs-Package.php');
+ $search_results = fetch_web_data($context['doc_scripturl'] . '?action=search2&xml', $postVars);
+
+ // If we didn't get any xml back we are in trouble - perhaps the doc site is overloaded?
+ if (!$search_results || preg_match('~<' . '\?xml\sversion="\d+\.\d+"\sencoding=".+?"\?' . '>\s*(.+?)~is', $search_results, $matches) != true)
+ fatal_lang_error('cannot_connect_doc_site');
+
+ $search_results = $matches[1];
+
+ // Otherwise we simply walk through the XML and stick it in context for display.
+ $context['search_results'] = array();
+ loadClassFile('Class-Package.php');
+
+ // Get the results loaded into an array for processing!
+ $results = new xmlArray($search_results, false);
+
+ // Move through the smf layer.
+ if (!$results->exists('smf'))
+ fatal_lang_error('cannot_connect_doc_site');
+ $results = $results->path('smf[0]');
+
+ // Are there actually some results?
+ if (!$results->exists('noresults') && !$results->exists('results'))
+ fatal_lang_error('cannot_connect_doc_site');
+ elseif ($results->exists('results'))
+ {
+ foreach ($results->set('results/result') as $result)
+ {
+ if (!$result->exists('messages'))
+ continue;
+
+ $context['search_results'][$result->fetch('id')] = array(
+ 'topic_id' => $result->fetch('id'),
+ 'relevance' => $result->fetch('relevance'),
+ 'board' => array(
+ 'id' => $result->fetch('board/id'),
+ 'name' => $result->fetch('board/name'),
+ 'href' => $result->fetch('board/href'),
+ ),
+ 'category' => array(
+ 'id' => $result->fetch('category/id'),
+ 'name' => $result->fetch('category/name'),
+ 'href' => $result->fetch('category/href'),
+ ),
+ 'messages' => array(),
+ );
+
+ // Add the messages.
+ foreach ($result->set('messages/message') as $message)
+ $context['search_results'][$result->fetch('id')]['messages'][] = array(
+ 'id' => $message->fetch('id'),
+ 'subject' => $message->fetch('subject'),
+ 'body' => $message->fetch('body'),
+ 'time' => $message->fetch('time'),
+ 'timestamp' => $message->fetch('timestamp'),
+ 'start' => $message->fetch('start'),
+ 'author' => array(
+ 'id' => $message->fetch('author/id'),
+ 'name' => $message->fetch('author/name'),
+ 'href' => $message->fetch('author/href'),
+ ),
+ );
+ }
+ }
+}
+
+// This function decides which log to load.
+function AdminLogs()
+{
+ global $sourcedir, $context, $txt, $scripturl;
+
+ // These are the logs they can load.
+ $log_functions = array(
+ 'errorlog' => array('ManageErrors.php', 'ViewErrorLog'),
+ 'adminlog' => array('Modlog.php', 'ViewModlog'),
+ 'modlog' => array('Modlog.php', 'ViewModlog'),
+ 'banlog' => array('ManageBans.php', 'BanLog'),
+ 'spiderlog' => array('ManageSearchEngines.php', 'SpiderLogs'),
+ 'tasklog' => array('ManageScheduledTasks.php', 'TaskLog'),
+ 'pruning' => array('ManageSettings.php', 'ModifyPruningSettings'),
+ );
+
+ $sub_action = isset($_REQUEST['sa']) && isset($log_functions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'errorlog';
+ // If it's not got a sa set it must have come here for first time, pretend error log should be reversed.
+ if (!isset($_REQUEST['sa']))
+ $_REQUEST['desc'] = true;
+
+ // Setup some tab stuff.
+ $context[$context['admin_menu_name']]['tab_data'] = array(
+ 'title' => $txt['logs'],
+ 'help' => '',
+ 'description' => $txt['maintain_info'],
+ 'tabs' => array(
+ 'errorlog' => array(
+ 'url' => $scripturl . '?action=admin;area=logs;sa=errorlog;desc',
+ 'description' => sprintf($txt['errlog_desc'], $txt['remove']),
+ ),
+ 'adminlog' => array(
+ 'description' => $txt['admin_log_desc'],
+ ),
+ 'modlog' => array(
+ 'description' => $txt['moderation_log_desc'],
+ ),
+ 'banlog' => array(
+ 'description' => $txt['ban_log_description'],
+ ),
+ 'spiderlog' => array(
+ 'description' => $txt['spider_log_desc'],
+ ),
+ 'tasklog' => array(
+ 'description' => $txt['scheduled_log_desc'],
+ ),
+ 'pruning' => array(
+ 'description' => $txt['pruning_log_desc'],
+ ),
+ ),
+ );
+
+ require_once($sourcedir . '/' . $log_functions[$sub_action][0]);
+ $log_functions[$sub_action][1]();
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/BoardIndex.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/BoardIndex.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,143 @@
+ true,
+ 'base_level' => 0,
+ 'parent_id' => 0,
+ 'set_latest_post' => true,
+ 'countChildPosts' => !empty($modSettings['countChildPosts']),
+ );
+ $context['categories'] = getBoardIndex($boardIndexOptions);
+
+ // Get the user online list.
+ require_once($sourcedir . '/Subs-MembersOnline.php');
+ $membersOnlineOptions = array(
+ 'show_hidden' => allowedTo('moderate_forum'),
+ 'sort' => 'log_time',
+ 'reverse_sort' => true,
+ );
+ $context += getMembersOnlineStats($membersOnlineOptions);
+
+ $context['show_buddies'] = !empty($user_info['buddies']);
+
+ // Are we showing all membergroups on the board index?
+ if (!empty($settings['show_group_key']))
+ $context['membergroups'] = cache_quick_get('membergroup_list', 'Subs-Membergroups.php', 'cache_getMembergroupList', array());
+
+ // Track most online statistics? (Subs-MembersOnline.php)
+ if (!empty($modSettings['trackStats']))
+ trackStatsUsersOnline($context['num_guests'] + $context['num_spiders'] + $context['num_users_online']);
+
+ // Retrieve the latest posts if the theme settings require it.
+ if (isset($settings['number_recent_posts']) && $settings['number_recent_posts'] > 1)
+ {
+ $latestPostOptions = array(
+ 'number_posts' => $settings['number_recent_posts'],
+ );
+ $context['latest_posts'] = cache_quick_get('boardindex-latest_posts:' . md5($user_info['query_wanna_see_board'] . $user_info['language']), 'Subs-Recent.php', 'cache_getLastPosts', array($latestPostOptions));
+ }
+
+ $settings['display_recent_bar'] = !empty($settings['number_recent_posts']) ? $settings['number_recent_posts'] : 0;
+ $settings['show_member_bar'] &= allowedTo('view_mlist');
+ $context['show_stats'] = allowedTo('view_stats') && !empty($modSettings['trackStats']);
+ $context['show_member_list'] = allowedTo('view_mlist');
+ $context['show_who'] = allowedTo('who_view') && !empty($modSettings['who_enabled']);
+
+ // Load the calendar?
+ if (!empty($modSettings['cal_enabled']) && allowedTo('calendar_view'))
+ {
+ // Retrieve the calendar data (events, birthdays, holidays).
+ $eventOptions = array(
+ 'include_holidays' => $modSettings['cal_showholidays'] > 1,
+ 'include_birthdays' => $modSettings['cal_showbdays'] > 1,
+ 'include_events' => $modSettings['cal_showevents'] > 1,
+ 'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'],
+ );
+ $context += cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'Subs-Calendar.php', 'cache_getRecentEvents', array($eventOptions));
+
+ // Whether one or multiple days are shown on the board index.
+ $context['calendar_only_today'] = $modSettings['cal_days_for_index'] == 1;
+
+ // This is used to show the "how-do-I-edit" help.
+ $context['calendar_can_edit'] = allowedTo('calendar_edit_any');
+ }
+ else
+ $context['show_calendar'] = false;
+
+ $context['page_title'] = sprintf($txt['forum_index'], $context['forum_name']);
+}
+
+// Collapse or expand a category
+function CollapseCategory()
+{
+ global $user_info, $sourcedir, $context;
+
+ // Just in case, no need, no need.
+ $context['robot_no_index'] = true;
+
+ checkSession('request');
+
+ if (!isset($_GET['sa']))
+ fatal_lang_error('no_access', false);
+
+ // Check if the input values are correct.
+ if (in_array($_REQUEST['sa'], array('expand', 'collapse', 'toggle')) && isset($_REQUEST['c']))
+ {
+ // And collapse/expand/toggle the category.
+ require_once($sourcedir . '/Subs-Categories.php');
+ collapseCategories(array((int) $_REQUEST['c']), $_REQUEST['sa'], array($user_info['id']));
+ }
+
+ // And go back to the board index.
+ BoardIndex();
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Calendar.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Calendar.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,487 @@
+ 'iCalDownload',
+ 'post' => 'CalendarPost',
+ );
+
+ if (isset($_GET['sa']) && isset($subActions[$_GET['sa']]) && !WIRELESS)
+ return $subActions[$_GET['sa']]();
+
+ // This is gonna be needed...
+ loadTemplate('Calendar');
+
+ // You can't do anything if the calendar is off.
+ if (empty($modSettings['cal_enabled']))
+ fatal_lang_error('calendar_off', false);
+
+ // Set the page title to mention the calendar ;).
+ $context['page_title'] = $txt['calendar'];
+
+ // Is this a week view?
+ $context['view_week'] = isset($_GET['viewweek']);
+
+ // Don't let search engines index weekly calendar pages.
+ if ($context['view_week'])
+ $context['robot_no_index'] = true;
+
+ // Get the current day of month...
+ require_once($sourcedir . '/Subs-Calendar.php');
+ $today = getTodayInfo();
+
+ // If the month and year are not passed in, use today's date as a starting point.
+ $curPage = array(
+ 'day' => isset($_REQUEST['day']) ? (int) $_REQUEST['day'] : $today['day'],
+ 'month' => isset($_REQUEST['month']) ? (int) $_REQUEST['month'] : $today['month'],
+ 'year' => isset($_REQUEST['year']) ? (int) $_REQUEST['year'] : $today['year']
+ );
+
+ // Make sure the year and month are in valid ranges.
+ if ($curPage['month'] < 1 || $curPage['month'] > 12)
+ fatal_lang_error('invalid_month', false);
+ if ($curPage['year'] < $modSettings['cal_minyear'] || $curPage['year'] > $modSettings['cal_maxyear'])
+ fatal_lang_error('invalid_year', false);
+ // If we have a day clean that too.
+ if ($context['view_week'])
+ {
+ // Note $isValid is -1 < PHP 5.1
+ $isValid = mktime(0, 0, 0, $curPage['month'], $curPage['day'], $curPage['year']);
+ if ($curPage['day'] > 31 || !$isValid || $isValid == -1)
+ fatal_lang_error('invalid_day', false);
+ }
+
+ // Load all the context information needed to show the calendar grid.
+ $calendarOptions = array(
+ 'start_day' => !empty($options['calendar_start_day']) ? $options['calendar_start_day'] : 0,
+ 'show_birthdays' => in_array($modSettings['cal_showbdays'], array(1, 2)),
+ 'show_events' => in_array($modSettings['cal_showevents'], array(1, 2)),
+ 'show_holidays' => in_array($modSettings['cal_showholidays'], array(1, 2)),
+ 'show_week_num' => true,
+ 'short_day_titles' => false,
+ 'show_next_prev' => true,
+ 'show_week_links' => true,
+ 'size' => 'large',
+ );
+
+ // Load up the main view.
+ if ($context['view_week'])
+ $context['calendar_grid_main'] = getCalendarWeek($curPage['month'], $curPage['year'], $curPage['day'], $calendarOptions);
+ else
+ $context['calendar_grid_main'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions);
+
+ // Load up the previous and next months.
+ $calendarOptions['show_birthdays'] = $calendarOptions['show_events'] = $calendarOptions['show_holidays'] = false;
+ $calendarOptions['short_day_titles'] = true;
+ $calendarOptions['show_next_prev'] = false;
+ $calendarOptions['show_week_links'] = false;
+ $calendarOptions['size'] = 'small';
+ $context['calendar_grid_current'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions);
+ // Only show previous month if it isn't pre-January of the min-year
+ if ($context['calendar_grid_current']['previous_calendar']['year'] > $modSettings['cal_minyear'] || $curPage['month'] != 1)
+ $context['calendar_grid_prev'] = getCalendarGrid($context['calendar_grid_current']['previous_calendar']['month'], $context['calendar_grid_current']['previous_calendar']['year'], $calendarOptions);
+ // Only show next month if it isn't post-December of the max-year
+ if ($context['calendar_grid_current']['next_calendar']['year'] < $modSettings['cal_maxyear'] || $curPage['month'] != 12)
+ $context['calendar_grid_next'] = getCalendarGrid($context['calendar_grid_current']['next_calendar']['month'], $context['calendar_grid_current']['next_calendar']['year'], $calendarOptions);
+
+ // Basic template stuff.
+ $context['can_post'] = allowedTo('calendar_post');
+ $context['current_day'] = $curPage['day'];
+ $context['current_month'] = $curPage['month'];
+ $context['current_year'] = $curPage['year'];
+ $context['show_all_birthdays'] = isset($_GET['showbd']);
+
+ // Set the page title to mention the month or week, too
+ $context['page_title'] .= ' - ' . ($context['view_week'] ? sprintf($txt['calendar_week_title'], $context['calendar_grid_main']['week_number'], ($context['calendar_grid_main']['week_number'] == 53 ? $context['current_year'] - 1 : $context['current_year'])) : $txt['months'][$context['current_month']] . ' ' . $context['current_year']);
+
+ // Load up the linktree!
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=calendar',
+ 'name' => $txt['calendar']
+ );
+ // Add the current month to the linktree.
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=calendar;year=' . $context['current_year'] . ';month=' . $context['current_month'],
+ 'name' => $txt['months'][$context['current_month']] . ' ' . $context['current_year']
+ );
+ // If applicable, add the current week to the linktree.
+ if ($context['view_week'])
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=calendar;viewweek;year=' . $context['current_year'] . ';month=' . $context['current_month'] . ';day=' . $context['current_day'],
+ 'name' => $txt['calendar_week'] . ' ' . $context['calendar_grid_main']['week_number']
+ );
+}
+
+function CalendarPost()
+{
+ global $context, $txt, $user_info, $sourcedir, $scripturl;
+ global $modSettings, $topic, $smcFunc;
+
+ // Well - can they?
+ isAllowedTo('calendar_post');
+
+ // We need this for all kinds of useful functions.
+ require_once($sourcedir . '/Subs-Calendar.php');
+
+ // Cast this for safety...
+ if (isset($_REQUEST['eventid']))
+ $_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
+
+ // Submitting?
+ if (isset($_POST[$context['session_var']], $_REQUEST['eventid']))
+ {
+ checkSession();
+
+ // Validate the post...
+ if (!isset($_POST['link_to_board']))
+ validateEventPost();
+
+ // If you're not allowed to edit any events, you have to be the poster.
+ if ($_REQUEST['eventid'] > 0 && !allowedTo('calendar_edit_any'))
+ isAllowedTo('calendar_edit_' . (!empty($user_info['id']) && getEventPoster($_REQUEST['eventid']) == $user_info['id'] ? 'own' : 'any'));
+
+ // New - and directing?
+ if ($_REQUEST['eventid'] == -1 && isset($_POST['link_to_board']))
+ {
+ $_REQUEST['calendar'] = 1;
+ require_once($sourcedir . '/Post.php');
+ return Post();
+ }
+ // New...
+ elseif ($_REQUEST['eventid'] == -1)
+ {
+ $eventOptions = array(
+ 'board' => 0,
+ 'topic' => 0,
+ 'title' => substr($_REQUEST['evtitle'], 0, 60),
+ 'member' => $user_info['id'],
+ 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']),
+ 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0,
+ );
+ insertEvent($eventOptions);
+ }
+
+ // Deleting...
+ elseif (isset($_REQUEST['deleteevent']))
+ removeEvent($_REQUEST['eventid']);
+
+ // ... or just update it?
+ else
+ {
+ $eventOptions = array(
+ 'title' => substr($_REQUEST['evtitle'], 0, 60),
+ 'span' => empty($modSettings['cal_allowspan']) || empty($_POST['span']) || $_POST['span'] == 1 || empty($modSettings['cal_maxspan']) || $_POST['span'] > $modSettings['cal_maxspan'] ? 0 : min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1),
+ 'start_date' => strftime('%Y-%m-%d', mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year'])),
+ );
+
+ modifyEvent($_REQUEST['eventid'], $eventOptions);
+ }
+
+ updateSettings(array(
+ 'calendar_updated' => time(),
+ ));
+
+ // No point hanging around here now...
+ redirectexit($scripturl . '?action=calendar;month=' . $_POST['month'] . ';year=' . $_POST['year']);
+ }
+
+ // If we are not enabled... we are not enabled.
+ if (empty($modSettings['cal_allow_unlinked']) && empty($_REQUEST['eventid']))
+ {
+ $_REQUEST['calendar'] = 1;
+ require_once($sourcedir . '/Post.php');
+ return Post();
+ }
+
+ // New?
+ if (!isset($_REQUEST['eventid']))
+ {
+ $today = getdate();
+
+ $context['event'] = array(
+ 'boards' => array(),
+ 'board' => 0,
+ 'new' => 1,
+ 'eventid' => -1,
+ 'year' => isset($_REQUEST['year']) ? $_REQUEST['year'] : $today['year'],
+ 'month' => isset($_REQUEST['month']) ? $_REQUEST['month'] : $today['mon'],
+ 'day' => isset($_REQUEST['day']) ? $_REQUEST['day'] : $today['mday'],
+ 'title' => '',
+ 'span' => 1,
+ );
+ $context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));
+
+ // Get list of boards that can be posted in.
+ $boards = boardsAllowedTo('post_new');
+ if (empty($boards))
+ fatal_lang_error('cannot_post_new', 'permission');
+
+ // Load the list of boards and categories in the context.
+ require_once($sourcedir . '/Subs-MessageIndex.php');
+ $boardListOptions = array(
+ 'included_boards' => in_array(0, $boards) ? null : $boards,
+ 'not_redirection' => true,
+ 'use_permissions' => true,
+ 'selected_board' => $modSettings['cal_defaultboard'],
+ );
+ $context['event']['categories'] = getBoardList($boardListOptions);
+ }
+ else
+ {
+ $context['event'] = getEventProperties($_REQUEST['eventid']);
+
+ if ($context['event'] === false)
+ fatal_lang_error('no_access', false);
+
+ // If it has a board, then they should be editing it within the topic.
+ if (!empty($context['event']['topic']['id']) && !empty($context['event']['topic']['first_msg']))
+ {
+ // We load the board up, for a check on the board access rights...
+ $topic = $context['event']['topic']['id'];
+ loadBoard();
+ }
+
+ // Make sure the user is allowed to edit this event.
+ if ($context['event']['member'] != $user_info['id'])
+ isAllowedTo('calendar_edit_any');
+ elseif (!allowedTo('calendar_edit_any'))
+ isAllowedTo('calendar_edit_own');
+ }
+
+ // Template, sub template, etc.
+ loadTemplate('Calendar');
+ $context['sub_template'] = 'event_post';
+
+ $context['page_title'] = isset($_REQUEST['eventid']) ? $txt['calendar_edit'] : $txt['calendar_post_event'];
+ $context['linktree'][] = array(
+ 'name' => $context['page_title'],
+ );
+}
+
+function iCalDownload()
+{
+ global $smcFunc, $sourcedir, $forum_version, $context, $modSettings;
+
+ // Goes without saying that this is required.
+ if (!isset($_REQUEST['eventid']))
+ fatal_lang_error('no_access', false);
+
+ // This is kinda wanted.
+ require_once($sourcedir . '/Subs-Calendar.php');
+
+ // Load up the event in question and check it exists.
+ $event = getEventProperties($_REQUEST['eventid']);
+
+ if ($event === false)
+ fatal_lang_error('no_access', false);
+
+ // Check the title isn't too long - iCal requires some formatting if so.
+ $title = str_split($event['title'], 30);
+ foreach ($title as $id => $line)
+ {
+ if ($id != 0)
+ $title[$id] = ' ' . $title[$id];
+ $title[$id] .= "\n";
+ }
+
+ // Format the date.
+ $date = $event['year'] . '-' . ($event['month'] < 10 ? '0' . $event['month'] : $event['month']) . '-' . ($event['day'] < 10 ? '0' . $event['day'] : $event['day']) . 'T';
+ $date .= '1200:00:00Z';
+
+ // This is what we will be sending later.
+ $filecontents = '';
+ $filecontents .= 'BEGIN:VCALENDAR' . "\n";
+ $filecontents .= 'VERSION:2.0' . "\n";
+ $filecontents .= 'PRODID:-//SimpleMachines//SMF ' . (empty($forum_version) ? 1.0 : strtr($forum_version, array('SMF ' => ''))) . '//EN' . "\n";
+ $filecontents .= 'BEGIN:VEVENT' . "\n";
+ $filecontents .= 'DTSTART:' . $date . "\n";
+ $filecontents .= 'DTEND:' . $date . "\n";
+ $filecontents .= 'SUMMARY:' . implode('', $title);
+ $filecontents .= 'END:VEVENT' . "\n";
+ $filecontents .= 'END:VCALENDAR';
+
+ // Send some standard headers.
+ ob_end_clean();
+ if (!empty($modSettings['enableCompressedOutput']))
+ @ob_start('ob_gzhandler');
+ else
+ ob_start();
+
+ // Send the file headers
+ header('Pragma: ');
+ header('Cache-Control: no-cache');
+ if (!$context['browser']['is_gecko'])
+ header('Content-Transfer-Encoding: binary');
+ 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', time()) . 'GMT');
+ header('Accept-Ranges: bytes');
+ header('Connection: close');
+ header('Content-Disposition: attachment; filename=' . $event['title'] . '.ics');
+
+ // How big is it?
+ if (empty($modSettings['enableCompressedOutput']))
+ header('Content-Length: ' . $smcFunc['strlen']($filecontents));
+
+ // This is a calendar item!
+ header('Content-Type: text/calendar');
+
+ // Chuck out the card.
+ echo $filecontents;
+
+ // Off we pop - lovely!
+ obExit(false);
+}
+
+// This is not the code you are looking for.
+function clock()
+{
+ global $settings, $context;
+ $context['onimg'] = $settings['images_url'] . '/bbc/bbc_bg.gif';
+ $context['offimg'] = $settings['images_url'] . '/bbc/bbc_hoverbg.gif';
+
+ $context['page_title'] = 'Anyone know what time it is?';
+ $context['robot_no_index'] = true;
+
+ $omfg = isset($_REQUEST['omfg']);
+ $bcd = !isset($_REQUEST['rb']) && !isset($_REQUEST['omfg']) && !isset($_REQUEST['time']);
+
+ loadTemplate('Calendar');
+
+ if ($bcd && !$omfg)
+ {
+ $context['sub_template'] = 'bcd';
+ $context['clockicons'] = unserialize(base64_decode('YTo2OntzOjI6ImgxIjthOjI6e2k6MDtpOjI7aToxO2k6MTt9czoyOiJoMiI7YTo0OntpOjA7aTo4O2k6MTtpOjQ7aToyO2k6MjtpOjM7aToxO31zOjI6Im0xIjthOjM6e2k6MDtpOjQ7aToxO2k6MjtpOjI7aToxO31zOjI6Im0yIjthOjQ6e2k6MDtpOjg7aToxO2k6NDtpOjI7aToyO2k6MztpOjE7fXM6MjoiczEiO2E6Mzp7aTowO2k6NDtpOjE7aToyO2k6MjtpOjE7fXM6MjoiczIiO2E6NDp7aTowO2k6ODtpOjE7aTo0O2k6MjtpOjI7aTozO2k6MTt9fQ=='));
+ }
+ elseif (!$omfg && !isset($_REQUEST['time']))
+ {
+ $context['sub_template'] = 'hms';
+ $context['clockicons'] = unserialize(base64_decode('YTozOntzOjE6ImgiO2E6NTp7aTowO2k6MTY7aToxO2k6ODtpOjI7aTo0O2k6MztpOjI7aTo0O2k6MTt9czoxOiJtIjthOjY6e2k6MDtpOjMyO2k6MTtpOjE2O2k6MjtpOjg7aTozO2k6NDtpOjQ7aToyO2k6NTtpOjE7fXM6MToicyI7YTo2OntpOjA7aTozMjtpOjE7aToxNjtpOjI7aTo4O2k6MztpOjQ7aTo0O2k6MjtpOjU7aToxO319'));
+ }
+ elseif ($omfg)
+ {
+ $context['sub_template'] = 'omfg';
+ $context['clockicons'] = unserialize(base64_decode('YTo2OntzOjQ6InllYXIiO2E6Nzp7aTowO2k6NjQ7aToxO2k6MzI7aToyO2k6MTY7aTozO2k6ODtpOjQ7aTo0O2k6NTtpOjI7aTo2O2k6MTt9czo1OiJtb250aCI7YTo0OntpOjA7aTo4O2k6MTtpOjQ7aToyO2k6MjtpOjM7aToxO31zOjM6ImRheSI7YTo1OntpOjA7aToxNjtpOjE7aTo4O2k6MjtpOjQ7aTozO2k6MjtpOjQ7aToxO31zOjQ6ImhvdXIiO2E6NTp7aTowO2k6MTY7aToxO2k6ODtpOjI7aTo0O2k6MztpOjI7aTo0O2k6MTt9czozOiJtaW4iO2E6Njp7aTowO2k6MzI7aToxO2k6MTY7aToyO2k6ODtpOjM7aTo0O2k6NDtpOjI7aTo1O2k6MTt9czozOiJzZWMiO2E6Njp7aTowO2k6MzI7aToxO2k6MTY7aToyO2k6ODtpOjM7aTo0O2k6NDtpOjI7aTo1O2k6MTt9fQ=='));
+ }
+ elseif (isset($_REQUEST['time']))
+ {
+ $context['sub_template'] = 'thetime';
+ $time = getdate($_REQUEST['time'] == 'now' ? time() : (int) $_REQUEST['time']);
+
+ $context['clockicons'] = array(
+ 'year' => array(
+ 64 => false,
+ 32 => false,
+ 16 => false,
+ 8 => false,
+ 4 => false,
+ 2 => false,
+ 1 => false
+ ),
+ 'month' => array(
+ 8 => false,
+ 4 => false,
+ 2 => false,
+ 1 => false
+ ),
+ 'day' => array(
+ 16 => false,
+ 4 => false,
+ 8 => false,
+ 2 => false,
+ 1 => false
+ ),
+ 'hour' => array(
+ 32 => false,
+ 16 => false,
+ 8 => false,
+ 4 => false,
+ 2 => false,
+ 1 => false
+ ),
+ 'min' => array(
+ 32 => false,
+ 16 => false,
+ 8 => false,
+ 4 => false,
+ 2 => false,
+ 1 => false
+ ),
+ 'sec' => array(
+ 32 => false,
+ 16 => false,
+ 8 => false,
+ 4 => false,
+ 2 => false,
+ 1 => false
+ ),
+ );
+
+ $year = $time['year'] % 100;
+ $month = $time['mon'];
+ $day = $time['mday'];
+ $hour = $time['hours'];
+ $min = $time['minutes'];
+ $sec = $time['seconds'];
+
+ foreach ($context['clockicons'] as $t => $vs)
+ foreach ($vs as $v => $dumb)
+ {
+ if ($$t >= $v)
+ {
+ $$t -= $v;
+ $context['clockicons'][$t][$v] = true;
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Class-Graphics.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Class-Graphics.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,715 @@
+MAX_LZW_BITS = 12;
+ unset($this->Next, $this->Vals, $this->Stack, $this->Buf);
+
+ $this->Next = range(0, (1 << $this->MAX_LZW_BITS) - 1);
+ $this->Vals = range(0, (1 << $this->MAX_LZW_BITS) - 1);
+ $this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1);
+ $this->Buf = range(0, 279);
+ }
+
+ public function decompress($data, &$datLen)
+ {
+ $stLen = strlen($data);
+ $datLen = 0;
+ $ret = '';
+
+ $this->LZWCommand($data, true);
+
+ while (($iIndex = $this->LZWCommand($data, false)) >= 0)
+ $ret .= chr($iIndex);
+
+ $datLen = $stLen - strlen($data);
+
+ if ($iIndex != -2)
+ return false;
+
+ return $ret;
+ }
+
+ public function LZWCommand(&$data, $bInit)
+ {
+ if ($bInit)
+ {
+ $this->SetCodeSize = ord($data[0]);
+ $data = substr($data, 1);
+
+ $this->CodeSize = $this->SetCodeSize + 1;
+ $this->ClearCode = 1 << $this->SetCodeSize;
+ $this->EndCode = $this->ClearCode + 1;
+ $this->MaxCode = $this->ClearCode + 2;
+ $this->MaxCodeSize = $this->ClearCode << 1;
+
+ $this->GetCode($data, $bInit);
+
+ $this->Fresh = 1;
+ for ($i = 0; $i < $this->ClearCode; $i++)
+ {
+ $this->Next[$i] = 0;
+ $this->Vals[$i] = $i;
+ }
+
+ for (; $i < (1 << $this->MAX_LZW_BITS); $i++)
+ {
+ $this->Next[$i] = 0;
+ $this->Vals[$i] = 0;
+ }
+
+ $this->sp = 0;
+ return 1;
+ }
+
+ if ($this->Fresh)
+ {
+ $this->Fresh = 0;
+ do
+ {
+ $this->FirstCode = $this->GetCode($data, $bInit);
+ $this->OldCode = $this->FirstCode;
+ }
+ while ($this->FirstCode == $this->ClearCode);
+
+ return $this->FirstCode;
+ }
+
+ if ($this->sp > 0)
+ {
+ $this->sp--;
+ return $this->Stack[$this->sp];
+ }
+
+ while (($Code = $this->GetCode($data, $bInit)) >= 0)
+ {
+ if ($Code == $this->ClearCode)
+ {
+ for ($i = 0; $i < $this->ClearCode; $i++)
+ {
+ $this->Next[$i] = 0;
+ $this->Vals[$i] = $i;
+ }
+
+ for (; $i < (1 << $this->MAX_LZW_BITS); $i++)
+ {
+ $this->Next[$i] = 0;
+ $this->Vals[$i] = 0;
+ }
+
+ $this->CodeSize = $this->SetCodeSize + 1;
+ $this->MaxCodeSize = $this->ClearCode << 1;
+ $this->MaxCode = $this->ClearCode + 2;
+ $this->sp = 0;
+ $this->FirstCode = $this->GetCode($data, $bInit);
+ $this->OldCode = $this->FirstCode;
+
+ return $this->FirstCode;
+ }
+
+ if ($Code == $this->EndCode)
+ return -2;
+
+ $InCode = $Code;
+ if ($Code >= $this->MaxCode)
+ {
+ $this->Stack[$this->sp] = $this->FirstCode;
+ $this->sp++;
+ $Code = $this->OldCode;
+ }
+
+ while ($Code >= $this->ClearCode)
+ {
+ $this->Stack[$this->sp] = $this->Vals[$Code];
+ $this->sp++;
+
+ if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error!
+ return -1;
+
+ $Code = $this->Next[$Code];
+ }
+
+ $this->FirstCode = $this->Vals[$Code];
+ $this->Stack[$this->sp] = $this->FirstCode;
+ $this->sp++;
+
+ if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS))
+ {
+ $this->Next[$Code] = $this->OldCode;
+ $this->Vals[$Code] = $this->FirstCode;
+ $this->MaxCode++;
+
+ if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS)))
+ {
+ $this->MaxCodeSize *= 2;
+ $this->CodeSize++;
+ }
+ }
+
+ $this->OldCode = $InCode;
+ if ($this->sp > 0)
+ {
+ $this->sp--;
+ return $this->Stack[$this->sp];
+ }
+ }
+
+ return $Code;
+ }
+
+ public function GetCode(&$data, $bInit)
+ {
+ if ($bInit)
+ {
+ $this->CurBit = 0;
+ $this->LastBit = 0;
+ $this->Done = 0;
+ $this->LastByte = 2;
+
+ return 1;
+ }
+
+ if (($this->CurBit + $this->CodeSize) >= $this->LastBit)
+ {
+ if ($this->Done)
+ {
+ // Ran off the end of my bits...
+ if ($this->CurBit >= $this->LastBit)
+ return 0;
+
+ return -1;
+ }
+
+ $this->Buf[0] = $this->Buf[$this->LastByte - 2];
+ $this->Buf[1] = $this->Buf[$this->LastByte - 1];
+
+ $count = ord($data[0]);
+ $data = substr($data, 1);
+
+ if ($count)
+ {
+ for ($i = 0; $i < $count; $i++)
+ $this->Buf[2 + $i] = ord($data{$i});
+
+ $data = substr($data, $count);
+ }
+ else
+ $this->Done = 1;
+
+ $this->LastByte = 2 + $count;
+ $this->CurBit = ($this->CurBit - $this->LastBit) + 16;
+ $this->LastBit = (2 + $count) << 3;
+ }
+
+ $iRet = 0;
+ for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++)
+ $iRet |= (($this->Buf[intval($i / 8)] & (1 << ($i % 8))) != 0) << $j;
+
+ $this->CurBit += $this->CodeSize;
+ return $iRet;
+ }
+}
+
+class gif_color_table
+{
+ public $m_nColors;
+ public $m_arColors;
+
+ public function __construct()
+ {
+ unset($this->m_nColors, $this->m_arColors);
+ }
+
+ public function load($lpData, $num)
+ {
+ $this->m_nColors = 0;
+ $this->m_arColors = array();
+
+ for ($i = 0; $i < $num; $i++)
+ {
+ $rgb = substr($lpData, $i * 3, 3);
+ if (strlen($rgb) < 3)
+ return false;
+
+ $this->m_arColors[] = (ord($rgb[2]) << 16) + (ord($rgb[1]) << 8) + ord($rgb[0]);
+ $this->m_nColors++;
+ }
+
+ return true;
+ }
+
+ public function toString()
+ {
+ $ret = '';
+
+ for ($i = 0; $i < $this->m_nColors; $i++)
+ {
+ $ret .=
+ chr(($this->m_arColors[$i] & 0x000000FF)) . // R
+ chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G
+ chr(($this->m_arColors[$i] & 0x00FF0000) >> 16); // B
+ }
+
+ return $ret;
+ }
+
+ public function colorIndex($rgb)
+ {
+ $rgb = intval($rgb) & 0xFFFFFF;
+ $r1 = ($rgb & 0x0000FF);
+ $g1 = ($rgb & 0x00FF00) >> 8;
+ $b1 = ($rgb & 0xFF0000) >> 16;
+ $idx = -1;
+
+ for ($i = 0; $i < $this->m_nColors; $i++)
+ {
+ $r2 = ($this->m_arColors[$i] & 0x000000FF);
+ $g2 = ($this->m_arColors[$i] & 0x0000FF00) >> 8;
+ $b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16;
+ $d = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1);
+
+ if (($idx == -1) || ($d < $dif))
+ {
+ $idx = $i;
+ $dif = $d;
+ }
+ }
+
+ return $idx;
+ }
+}
+
+class gif_file_header
+{
+ public $m_lpVer, $m_nWidth, $m_nHeight, $m_bGlobalClr, $m_nColorRes;
+ public $m_bSorted, $m_nTableSize, $m_nBgColor, $m_nPixelRatio;
+ public $m_colorTable;
+
+ public function __construct()
+ {
+ unset($this->m_lpVer, $this->m_nWidth, $this->m_nHeight, $this->m_bGlobalClr, $this->m_nColorRes);
+ unset($this->m_bSorted, $this->m_nTableSize, $this->m_nBgColor, $this->m_nPixelRatio, $this->m_colorTable);
+ }
+
+ public function load($lpData, &$hdrLen)
+ {
+ $hdrLen = 0;
+
+ $this->m_lpVer = substr($lpData, 0, 6);
+ if (($this->m_lpVer != 'GIF87a') && ($this->m_lpVer != 'GIF89a'))
+ return false;
+
+ list ($this->m_nWidth, $this->m_nHeight) = array_values(unpack('v2', substr($lpData, 6, 4)));
+
+ if (!$this->m_nWidth || !$this->m_nHeight)
+ return false;
+
+ $b = ord(substr($lpData, 10, 1));
+ $this->m_bGlobalClr = ($b & 0x80) ? true : false;
+ $this->m_nColorRes = ($b & 0x70) >> 4;
+ $this->m_bSorted = ($b & 0x08) ? true : false;
+ $this->m_nTableSize = 2 << ($b & 0x07);
+ $this->m_nBgColor = ord(substr($lpData, 11, 1));
+ $this->m_nPixelRatio = ord(substr($lpData, 12, 1));
+ $hdrLen = 13;
+
+ if ($this->m_bGlobalClr)
+ {
+ $this->m_colorTable = new gif_color_table();
+ if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize))
+ return false;
+
+ $hdrLen += 3 * $this->m_nTableSize;
+ }
+
+ return true;
+ }
+}
+
+class gif_image_header
+{
+ public $m_nLeft, $m_nTop, $m_nWidth, $m_nHeight, $m_bLocalClr;
+ public $m_bInterlace, $m_bSorted, $m_nTableSize, $m_colorTable;
+
+ public function __construct()
+ {
+ unset($this->m_nLeft, $this->m_nTop, $this->m_nWidth, $this->m_nHeight, $this->m_bLocalClr);
+ unset($this->m_bInterlace, $this->m_bSorted, $this->m_nTableSize, $this->m_colorTable);
+ }
+
+ public function load($lpData, &$hdrLen)
+ {
+ $hdrLen = 0;
+
+ // Get the width/height/etc. from the header.
+ list ($this->m_nLeft, $this->m_nTop, $this->m_nWidth, $this->m_nHeight) = array_values(unpack('v4', substr($lpData, 0, 8)));
+
+ if (!$this->m_nWidth || !$this->m_nHeight)
+ return false;
+
+ $b = ord($lpData[8]);
+ $this->m_bLocalClr = ($b & 0x80) ? true : false;
+ $this->m_bInterlace = ($b & 0x40) ? true : false;
+ $this->m_bSorted = ($b & 0x20) ? true : false;
+ $this->m_nTableSize = 2 << ($b & 0x07);
+ $hdrLen = 9;
+
+ if ($this->m_bLocalClr)
+ {
+ $this->m_colorTable = new gif_color_table();
+ if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize))
+ return false;
+
+ $hdrLen += 3 * $this->m_nTableSize;
+ }
+
+ return true;
+ }
+}
+
+class gif_image
+{
+ public $m_disp, $m_bUser, $m_bTrans, $m_nDelay, $m_nTrans, $m_lpComm;
+ public $m_gih, $m_data, $m_lzw;
+
+ public function __construct()
+ {
+ unset($this->m_disp, $this->m_bUser, $this->m_nDelay, $this->m_nTrans, $this->m_lpComm, $this->m_data);
+ $this->m_gih = new gif_image_header();
+ $this->m_lzw = new gif_lzw_compression();
+ }
+
+ public function load($data, &$datLen)
+ {
+ $datLen = 0;
+
+ while (true)
+ {
+ $b = ord($data[0]);
+ $data = substr($data, 1);
+ $datLen++;
+
+ switch ($b)
+ {
+ // Extension...
+ case 0x21:
+ $len = 0;
+ if (!$this->skipExt($data, $len))
+ return false;
+
+ $datLen += $len;
+ break;
+
+ // Image...
+ case 0x2C:
+ // Load the header and color table.
+ $len = 0;
+ if (!$this->m_gih->load($data, $len))
+ return false;
+
+ $data = substr($data, $len);
+ $datLen += $len;
+
+ // Decompress the data, and ride on home ;).
+ $len = 0;
+ if (!($this->m_data = $this->m_lzw->decompress($data, $len)))
+ return false;
+
+ $data = substr($data, $len);
+ $datLen += $len;
+
+ if ($this->m_gih->m_bInterlace)
+ $this->deInterlace();
+
+ return true;
+
+ case 0x3B: // EOF
+ default:
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public function skipExt(&$data, &$extLen)
+ {
+ $extLen = 0;
+
+ $b = ord($data[0]);
+ $data = substr($data, 1);
+ $extLen++;
+
+ switch ($b)
+ {
+ // Graphic Control...
+ case 0xF9:
+ $b = ord($data[1]);
+ $this->m_disp = ($b & 0x1C) >> 2;
+ $this->m_bUser = ($b & 0x02) ? true : false;
+ $this->m_bTrans = ($b & 0x01) ? true : false;
+ list ($this->m_nDelay) = array_values(unpack('v', substr($data, 2, 2)));
+ $this->m_nTrans = ord($data[4]);
+ break;
+
+ // Comment...
+ case 0xFE:
+ $this->m_lpComm = substr($data, 1, ord($data[0]));
+ break;
+
+ // Plain text...
+ case 0x01:
+ break;
+
+ // Application...
+ case 0xFF:
+ break;
+ }
+
+ // Skip default as defs may change.
+ $b = ord($data[0]);
+ $data = substr($data, 1);
+ $extLen++;
+ while ($b > 0)
+ {
+ $data = substr($data, $b);
+ $extLen += $b;
+ $b = ord($data[0]);
+ $data = substr($data, 1);
+ $extLen++;
+ }
+ return true;
+ }
+
+ public function deInterlace()
+ {
+ $data = $this->m_data;
+
+ for ($i = 0; $i < 4; $i++)
+ {
+ switch ($i)
+ {
+ case 0:
+ $s = 8;
+ $y = 0;
+ break;
+
+ case 1:
+ $s = 8;
+ $y = 4;
+ break;
+
+ case 2:
+ $s = 4;
+ $y = 2;
+ break;
+
+ case 3:
+ $s = 2;
+ $y = 1;
+ break;
+ }
+
+ for (; $y < $this->m_gih->m_nHeight; $y += $s)
+ {
+ $lne = substr($this->m_data, 0, $this->m_gih->m_nWidth);
+ $this->m_data = substr($this->m_data, $this->m_gih->m_nWidth);
+
+ $data =
+ substr($data, 0, $y * $this->m_gih->m_nWidth) .
+ $lne .
+ substr($data, ($y + 1) * $this->m_gih->m_nWidth);
+ }
+ }
+
+ $this->m_data = $data;
+ }
+}
+
+class gif_file
+{
+ public $header, $image, $data, $loaded;
+
+ public function __construct()
+ {
+ $this->data = '';
+ $this->loaded = false;
+ $this->header = new gif_file_header();
+ $this->image = new gif_image();
+ }
+
+ public function loadFile($filename, $iIndex)
+ {
+ if ($iIndex < 0)
+ return false;
+
+ $this->data = @file_get_contents($filename);
+ if ($this->data === false)
+ return false;
+
+ // Tell the header to load up....
+ $len = 0;
+ if (!$this->header->load($this->data, $len))
+ return false;
+
+ $this->data = substr($this->data, $len);
+
+ // Keep reading (at least once) so we get to the actual image we're looking for.
+ for ($j = 0; $j <= $iIndex; $j++)
+ {
+ $imgLen = 0;
+ if (!$this->image->load($this->data, $imgLen))
+ return false;
+
+ $this->data = substr($this->data, $imgLen);
+ }
+
+ $this->loaded = true;
+ return true;
+ }
+
+ public function get_png_data($background_color)
+ {
+ if (!$this->loaded)
+ return false;
+
+ // Prepare the color table.
+ if ($this->image->m_gih->m_bLocalClr)
+ {
+ $colors = $this->image->m_gih->m_nTableSize;
+ $pal = $this->image->m_gih->m_colorTable->toString();
+
+ if ($background_color != -1)
+ $background_color = $this->image->m_gih->m_colorTable->colorIndex($background_color);
+ }
+ elseif ($this->header->m_bGlobalClr)
+ {
+ $colors = $this->header->m_nTableSize;
+ $pal = $this->header->m_colorTable->toString();
+
+ if ($background_color != -1)
+ $background_color = $this->header->m_colorTable->colorIndex($background_color);
+ }
+ else
+ {
+ $colors = 0;
+ $background_color = -1;
+ }
+
+ if ($background_color == -1)
+ $background_color = $this->header->m_nBgColor;
+
+ $data = &$this->image->m_data;
+ $header = &$this->image->m_gih;
+
+ $i = 0;
+ $bmp = '';
+
+ // Prepare the bitmap itself.
+ for ($y = 0; $y < $this->header->m_nHeight; $y++)
+ {
+ $bmp .= "\x00";
+
+ for ($x = 0; $x < $this->header->m_nWidth; $x++, $i++)
+ {
+ // Is this in the proper range? If so, get the specific pixel data...
+ if ($x >= $header->m_nLeft && $y >= $header->m_nTop && $x < ($header->m_nLeft + $header->m_nWidth) && $y < ($header->m_nTop + $header->m_nHeight))
+ $bmp .= $data{$i};
+ // Otherwise, this is background...
+ else
+ $bmp .= chr($background_color);
+ }
+ }
+
+ $bmp = gzcompress($bmp, 9);
+
+ // Output the basic signature first of all.
+ $out = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
+
+ // Now, we want the header...
+ $out .= "\x00\x00\x00\x0D";
+ $tmp = 'IHDR' . pack('N', (int) $this->header->m_nWidth) . pack('N', (int) $this->header->m_nHeight) . "\x08\x03\x00\x00\x00";
+ $out .= $tmp . pack('N', smf_crc32($tmp));
+
+ // The palette, assuming we have one to speak of...
+ if ($colors > 0)
+ {
+ $out .= pack('N', (int) $colors * 3);
+ $tmp = 'PLTE' . $pal;
+ $out .= $tmp . pack('N', smf_crc32($tmp));
+ }
+
+ // Do we have any transparency we want to make available?
+ if ($this->image->m_bTrans && $colors > 0)
+ {
+ $out .= pack('N', (int) $colors);
+ $tmp = 'tRNS';
+
+ // Stick each color on - full transparency or none.
+ for ($i = 0; $i < $colors; $i++)
+ $tmp .= $i == $this->image->m_nTrans ? "\x00" : "\xFF";
+
+ $out .= $tmp . pack('N', smf_crc32($tmp));
+ }
+
+ // Here's the data itself!
+ $out .= pack('N', strlen($bmp));
+ $tmp = 'IDAT' . $bmp;
+ $out .= $tmp . pack('N', smf_crc32($tmp));
+
+ // EOF marker...
+ $out .= "\x00\x00\x00\x00" . 'IEND' . "\xAE\x42\x60\x82";
+
+ return $out;
+ }
+}
+
+// crc32 doesn't work as expected on 64-bit functions - make our own.
+// http://www.php.net/crc32#79567
+if (!function_exists('smf_crc32'))
+{
+ function smf_crc32($number)
+ {
+ $crc = crc32($number);
+
+ if ($crc & 0x80000000)
+ {
+ $crc ^= 0xffffffff;
+ $crc += 1;
+ $crc = -$crc;
+ }
+
+ return $crc;
+ }
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Class-Package.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Class-Package.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,1019 @@
+path(path, true) in that instead of an xmlArray
+ of elements, an array of xmlArray's is returned for use with foreach.
+
+ string xmlArray::create_xml(string path = '.')
+ - returns the specified path as an xml file.
+*/
+
+// An xml array. Reads in xml, allows you to access it simply. Version 1.1.
+class xmlArray
+{
+ // The array and debugging output level.
+ public $array, $debug_level, $trim;
+
+ // Create an xml array.
+ // the xml data, trim elements?, debugging output level, reserved.
+ //ie. $xml = new xmlArray(file('data.xml'));
+ public function __construct($data, $auto_trim = false, $level = null, $is_clone = false)
+ {
+ // If we're using this try to get some more memory.
+ @ini_set('memory_limit', '32M');
+
+ // Set the debug level.
+ $this->debug_level = $level !== null ? $level : error_reporting();
+ $this->trim = $auto_trim;
+
+ // Is the data already parsed?
+ if ($is_clone)
+ {
+ $this->array = $data;
+ return;
+ }
+
+ // Is the input an array? (ie. passed from file()?)
+ if (is_array($data))
+ $data = implode('', $data);
+
+ // Remove any xml declaration or doctype, and parse out comments and CDATA.
+ $data = preg_replace('//s', '', $this->_to_cdata(preg_replace(array('/^<\?xml.+?\?' . '>/is', '/]+?' . '>/s'), '', $data)));
+
+ // Now parse the xml!
+ $this->array = $this->_parse($data);
+ }
+
+ // Get the root element's name.
+ //ie. echo $element->name();
+ public function name()
+ {
+ return isset($this->array['name']) ? $this->array['name'] : '';
+ }
+
+ // Get a specified element's value or attribute by path.
+ // the path to the element to fetch, whether to include elements?
+ //ie. $data = $xml->fetch('html/head/title');
+ public function fetch($path, $get_elements = false)
+ {
+ // Get the element, in array form.
+ $array = $this->path($path);
+
+ if ($array === false)
+ return false;
+
+ // Getting elements into this is a bit complicated...
+ if ($get_elements && !is_string($array))
+ {
+ $temp = '';
+
+ // Use the _xml() function to get the xml data.
+ foreach ($array->array as $val)
+ {
+ // Skip the name and any attributes.
+ if (is_array($val))
+ $temp .= $this->_xml($val, null);
+ }
+
+ // Just get the XML data and then take out the CDATAs.
+ return $this->_to_cdata($temp);
+ }
+
+ // Return the value - taking care to pick out all the text values.
+ return is_string($array) ? $array : $this->_fetch($array->array);
+ }
+
+ // Get an element, returns a new xmlArray.
+ // the path to the element to get, always return full result set? (ie. don't contract a single item.)
+ //ie. $element = $xml->path('html/body');
+ public function path($path, $return_full = false)
+ {
+ // Split up the path.
+ $path = explode('/', $path);
+
+ // Start with a base array.
+ $array = $this->array;
+
+ // For each element in the path.
+ foreach ($path as $el)
+ {
+ // Deal with sets....
+ if (strpos($el, '[') !== false)
+ {
+ $lvl = (int) substr($el, strpos($el, '[') + 1);
+ $el = substr($el, 0, strpos($el, '['));
+ }
+ // Find an attribute.
+ elseif (substr($el, 0, 1) == '@')
+ {
+ // It simplifies things if the attribute is already there ;).
+ if (isset($array[$el]))
+ return $array[$el];
+ else
+ {
+ if (function_exists('debug_backtrace'))
+ {
+ $trace = debug_backtrace();
+ $i = 0;
+ while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this))
+ $i++;
+ $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line'];
+ }
+ else
+ $debug = '';
+
+ // Cause an error.
+ if ($this->debug_level & E_NOTICE)
+ trigger_error('Undefined XML attribute: ' . substr($el, 1) . $debug, E_USER_NOTICE);
+ return false;
+ }
+ }
+ else
+ $lvl = null;
+
+ // Find this element.
+ $array = $this->_path($array, $el, $lvl);
+ }
+
+ // Clean up after $lvl, for $return_full.
+ if ($return_full && (!isset($array['name']) || substr($array['name'], -1) != ']'))
+ $array = array('name' => $el . '[]', $array);
+
+ // Create the right type of class...
+ $newClass = get_class($this);
+
+ // Return a new xmlArray for the result.
+ return $array === false ? false : new $newClass($array, $this->trim, $this->debug_level, true);
+ }
+
+ // Check if an element exists.
+ // the path to the element to get.
+ //ie. echo $xml->exists('html/body') ? 'y' : 'n';
+ public function exists($path)
+ {
+ // Split up the path.
+ $path = explode('/', $path);
+
+ // Start with a base array.
+ $array = $this->array;
+
+ // For each element in the path.
+ foreach ($path as $el)
+ {
+ // Deal with sets....
+ if (strpos($el, '[') !== false)
+ {
+ $lvl = (int) substr($el, strpos($el, '[') + 1);
+ $el = substr($el, 0, strpos($el, '['));
+ }
+ // Find an attribute.
+ elseif (substr($el, 0, 1) == '@')
+ return isset($array[$el]);
+ else
+ $lvl = null;
+
+ // Find this element.
+ $array = $this->_path($array, $el, $lvl, true);
+ }
+
+ return $array !== false;
+ }
+
+ // Count the number of occurances of a path.
+ // the path to search for.
+ //ie. echo $xml->count('html/head/meta');
+ public function count($path)
+ {
+ // Get the element, always returning a full set.
+ $temp = $this->path($path, true);
+
+ // Start at zero, then count up all the numeric keys.
+ $i = 0;
+ foreach ($temp->array as $item)
+ {
+ if (is_array($item))
+ $i++;
+ }
+
+ return $i;
+ }
+
+ // Get an array of xmlArray's for use with foreach.
+ // the path to search for.
+ //ie. foreach ($xml->set('html/body/p') as $p)
+ public function set($path)
+ {
+ // None as yet, just get the path.
+ $array = array();
+ $xml = $this->path($path, true);
+
+ foreach ($xml->array as $val)
+ {
+ // Skip these, they aren't elements.
+ if (!is_array($val) || $val['name'] == '!')
+ continue;
+
+ // Create the right type of class...
+ $newClass = get_class($this);
+
+ // Create a new xmlArray and stick it in the array.
+ $array[] = new $newClass($val, $this->trim, $this->debug_level, true);
+ }
+
+ return $array;
+ }
+
+ // Create an xml file from an xml array.
+ // the path to the element. (optional)
+ //ie. echo $this->create_xml()
+ public function create_xml($path = null)
+ {
+ // Was a path specified? If so, use that array.
+ if ($path !== null)
+ {
+ $path = $this->path($path);
+
+ // The path was not found
+ if ($path === false)
+ return false;
+
+ $path = $path->array;
+ }
+ // Just use the current array.
+ else
+ $path = $this->array;
+
+ // Add the xml declaration to the front.
+ return '' . $this->_xml($path, 0);
+ }
+
+ // Output the xml in an array form.
+ // the path to output.
+ //ie. print_r($xml->to_array());
+ public function to_array($path = null)
+ {
+ // Are we doing a specific path?
+ if ($path !== null)
+ {
+ $path = $this->path($path);
+
+ // The path was not found
+ if ($path === false)
+ return false;
+
+ $path = $path->array;
+ }
+ // No, so just use the current array.
+ else
+ $path = $this->array;
+
+ return $this->_array($path);
+ }
+
+ // Parse data into an array. (privately used...)
+ protected function _parse($data)
+ {
+ // Start with an 'empty' array with no data.
+ $current = array(
+ );
+
+ // Loop until we're out of data.
+ while ($data != '')
+ {
+ // Find and remove the next tag.
+ preg_match('/\A<([\w\-:]+)((?:\s+.+?)?)([\s]?\/)?' . '>/', $data, $match);
+ if (isset($match[0]))
+ $data = preg_replace('/' . preg_quote($match[0], '/') . '/s', '', $data, 1);
+
+ // Didn't find a tag? Keep looping....
+ if (!isset($match[1]) || $match[1] == '')
+ {
+ // If there's no <, the rest is data.
+ if (strpos($data, '<') === false)
+ {
+ $text_value = $this->_from_cdata($data);
+ $data = '';
+
+ if ($text_value != '')
+ $current[] = array(
+ 'name' => '!',
+ 'value' => $text_value
+ );
+ }
+ // If the < isn't immediately next to the current position... more data.
+ elseif (strpos($data, '<') > 0)
+ {
+ $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<')));
+ $data = substr($data, strpos($data, '<'));
+
+ if ($text_value != '')
+ $current[] = array(
+ 'name' => '!',
+ 'value' => $text_value
+ );
+ }
+ // If we're looking at a with no start, kill it.
+ elseif (strpos($data, '<') !== false && strpos($data, '<') == 0)
+ {
+ if (strpos($data, '<', 1) !== false)
+ {
+ $text_value = $this->_from_cdata(substr($data, 0, strpos($data, '<', 1)));
+ $data = substr($data, strpos($data, '<', 1));
+
+ if ($text_value != '')
+ $current[] = array(
+ 'name' => '!',
+ 'value' => $text_value
+ );
+ }
+ else
+ {
+ $text_value = $this->_from_cdata($data);
+ $data = '';
+
+ if ($text_value != '')
+ $current[] = array(
+ 'name' => '!',
+ 'value' => $text_value
+ );
+ }
+ }
+
+ // Wait for an actual occurance of an element.
+ continue;
+ }
+
+ // Create a new element in the array.
+ $el = &$current[];
+ $el['name'] = $match[1];
+
+ // If this ISN'T empty, remove the close tag and parse the inner data.
+ if ((!isset($match[3]) || trim($match[3]) != '/') && (!isset($match[2]) || trim($match[2]) != '/'))
+ {
+ // Because PHP 5.2.0+ seems to croak using regex, we'll have to do this the less fun way.
+ $last_tag_end = strpos($data, '' . $match[1]. '>');
+ if ($last_tag_end === false)
+ continue;
+
+ $offset = 0;
+ while (1 == 1)
+ {
+ // Where is the next start tag?
+ $next_tag_start = strpos($data, '<' . $match[1], $offset);
+ // If the next start tag is after the last end tag then we've found the right close.
+ if ($next_tag_start === false || $next_tag_start > $last_tag_end)
+ break;
+
+ // If not then find the next ending tag.
+ $next_tag_end = strpos($data, '' . $match[1]. '>', $offset);
+
+ // Didn't find one? Then just use the last and sod it.
+ if ($next_tag_end === false)
+ break;
+ else
+ {
+ $last_tag_end = $next_tag_end;
+ $offset = $next_tag_start + 1;
+ }
+ }
+ // Parse the insides.
+ $inner_match = substr($data, 0, $last_tag_end);
+ // Data now starts from where this section ends.
+ $data = substr($data, $last_tag_end + strlen('' . $match[1]. '>'));
+
+ if (!empty($inner_match))
+ {
+ // Parse the inner data.
+ if (strpos($inner_match, '<') !== false)
+ $el += $this->_parse($inner_match);
+ elseif (trim($inner_match) != '')
+ {
+ $text_value = $this->_from_cdata($inner_match);
+ if ($text_value != '')
+ $el[] = array(
+ 'name' => '!',
+ 'value' => $text_value
+ );
+ }
+ }
+ }
+
+ // If we're dealing with attributes as well, parse them out.
+ if (isset($match[2]) && $match[2] != '')
+ {
+ // Find all the attribute pairs in the string.
+ preg_match_all('/([\w:]+)="(.+?)"/', $match[2], $attr, PREG_SET_ORDER);
+
+ // Set them as @attribute-name.
+ foreach ($attr as $match_attr)
+ $el['@' . $match_attr[1]] = $match_attr[2];
+ }
+ }
+
+ // Return the parsed array.
+ return $current;
+ }
+
+ // Get a specific element's xml. (privately used...)
+ protected function _xml($array, $indent)
+ {
+ $indentation = $indent !== null ? '
+' . str_repeat(' ', $indent) : '';
+
+ // This is a set of elements, with no name...
+ if (is_array($array) && !isset($array['name']))
+ {
+ $temp = '';
+ foreach ($array as $val)
+ $temp .= $this->_xml($val, $indent);
+ return $temp;
+ }
+
+ // This is just text!
+ if ($array['name'] == '!')
+ return $indentation . '';
+ elseif (substr($array['name'], -2) == '[]')
+ $array['name'] = substr($array['name'], 0, -2);
+
+ // Start the element.
+ $output = $indentation . '<' . $array['name'];
+
+ $inside_elements = false;
+ $output_el = '';
+
+ // Run through and recurively output all the elements or attrbutes inside this.
+ foreach ($array as $k => $v)
+ {
+ if (substr($k, 0, 1) == '@')
+ $output .= ' ' . substr($k, 1) . '="' . $v . '"';
+ elseif (is_array($v))
+ {
+ $output_el .= $this->_xml($v, $indent === null ? null : $indent + 1);
+ $inside_elements = true;
+ }
+ }
+
+ // Indent, if necessary.... then close the tag.
+ if ($inside_elements)
+ $output .= '>' . $output_el . $indentation . '' . $array['name'] . '>';
+ else
+ $output .= ' />';
+
+ return $output;
+ }
+
+ // Return an element as an array...
+ protected function _array($array)
+ {
+ $return = array();
+ $text = '';
+ foreach ($array as $value)
+ {
+ if (!is_array($value) || !isset($value['name']))
+ continue;
+
+ if ($value['name'] == '!')
+ $text .= $value['value'];
+ else
+ $return[$value['name']] = $this->_array($value);
+ }
+
+ if (empty($return))
+ return $text;
+ else
+ return $return;
+ }
+
+ // Parse out CDATA tags. (htmlspecialchars them...)
+ function _to_cdata($data)
+ {
+ $inCdata = $inComment = false;
+ $output = '';
+
+ $parts = preg_split('~(|)~', $data, -1, PREG_SPLIT_DELIM_CAPTURE);
+ foreach ($parts as $part)
+ {
+ // Handle XML comments.
+ if (!$inCdata && $part === '')
+ $inComment = false;
+ elseif ($inComment)
+ continue;
+
+ // Handle Cdata blocks.
+ elseif (!$inComment && $part === '')
+ $inCdata = false;
+ elseif ($inCdata)
+ $output .= htmlentities($part, ENT_QUOTES);
+
+ // Everything else is kept as is.
+ else
+ $output .= $part;
+ }
+
+ return $output;
+ }
+
+ // Turn the CDATAs back to normal text.
+ protected function _from_cdata($data)
+ {
+ // Get the HTML translation table and reverse it.
+ $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES));
+
+ // Translate all the entities out.
+ $data = strtr(preg_replace('~(\d{1,4});~e', "chr('\$1')", $data), $trans_tbl);
+
+ return $this->trim ? trim($data) : $data;
+ }
+
+ // Given an array, return the text from that array. (recursive and privately used.)
+ protected function _fetch($array)
+ {
+ // Don't return anything if this is just a string.
+ if (is_string($array))
+ return '';
+
+ $temp = '';
+ foreach ($array as $text)
+ {
+ // This means it's most likely an attribute or the name itself.
+ if (!isset($text['name']))
+ continue;
+
+ // This is text!
+ if ($text['name'] == '!')
+ $temp .= $text['value'];
+ // Another element - dive in ;).
+ else
+ $temp .= $this->_fetch($text);
+ }
+
+ // Return all the bits and pieces we've put together.
+ return $temp;
+ }
+
+ // Get a specific array by path, one level down. (privately used...)
+ protected function _path($array, $path, $level, $no_error = false)
+ {
+ // Is $array even an array? It might be false!
+ if (!is_array($array))
+ return false;
+
+ // Asking for *no* path?
+ if ($path == '' || $path == '.')
+ return $array;
+ $paths = explode('|', $path);
+
+ // A * means all elements of any name.
+ $show_all = in_array('*', $paths);
+
+ $results = array();
+
+ // Check each element.
+ foreach ($array as $value)
+ {
+ if (!is_array($value) || $value['name'] === '!')
+ continue;
+
+ if ($show_all || in_array($value['name'], $paths))
+ {
+ // Skip elements before "the one".
+ if ($level !== null && $level > 0)
+ $level--;
+ else
+ $results[] = $value;
+ }
+ }
+
+ // No results found...
+ if (empty($results))
+ {
+ if (function_exists('debug_backtrace'))
+ {
+ $trace = debug_backtrace();
+ $i = 0;
+ while ($i < count($trace) && isset($trace[$i]['class']) && $trace[$i]['class'] == get_class($this))
+ $i++;
+ $debug = ' from ' . $trace[$i - 1]['file'] . ' on line ' . $trace[$i - 1]['line'];
+ }
+ else
+ $debug = '';
+
+ // Cause an error.
+ if ($this->debug_level & E_NOTICE && !$no_error)
+ trigger_error('Undefined XML element: ' . $path . $debug, E_USER_NOTICE);
+ return false;
+ }
+ // Only one result.
+ elseif (count($results) == 1 || $level !== null)
+ return $results[0];
+ // Return the result set.
+ else
+ return $results + array('name' => $path . '[]');
+ }
+}
+
+// http://www.faqs.org/rfcs/rfc959.html
+if (!class_exists('ftp_connection'))
+{
+ class ftp_connection
+ {
+ public $connection, $error, $last_message, $pasv;
+
+ // Create a new FTP connection...
+ public function __construct($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org')
+ {
+ // Initialize variables.
+ $this->connection = 'no_connection';
+ $this->error = false;
+ $this->pasv = array();
+
+ if ($ftp_server !== null)
+ $this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass);
+ }
+
+ public function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@simplemachines.org')
+ {
+ if (substr($ftp_server, 0, 6) == 'ftp://')
+ $ftp_server = substr($ftp_server, 6);
+ elseif (substr($ftp_server, 0, 7) == 'ftps://')
+ $ftp_server = 'ssl://' . substr($ftp_server, 7);
+ if (substr($ftp_server, 0, 7) == 'http://')
+ $ftp_server = substr($ftp_server, 7);
+ $ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => ''));
+
+ // Connect to the FTP server.
+ $this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5);
+ if (!$this->connection)
+ {
+ $this->error = 'bad_server';
+ return;
+ }
+
+ // Get the welcome message...
+ if (!$this->check_response(220))
+ {
+ $this->error = 'bad_response';
+ return;
+ }
+
+ // Send the username, it should ask for a password.
+ fwrite($this->connection, 'USER ' . $ftp_user . "\r\n");
+ if (!$this->check_response(331))
+ {
+ $this->error = 'bad_username';
+ return;
+ }
+
+ // Now send the password... and hope it goes okay.
+ fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n");
+ if (!$this->check_response(230))
+ {
+ $this->error = 'bad_password';
+ return;
+ }
+ }
+
+ public function chdir($ftp_path)
+ {
+ if (!is_resource($this->connection))
+ return false;
+
+ // No slash on the end, please...
+ if ($ftp_path !== '/' && substr($ftp_path, -1) === '/')
+ $ftp_path = substr($ftp_path, 0, -1);
+
+ fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n");
+ if (!$this->check_response(250))
+ {
+ $this->error = 'bad_path';
+ return false;
+ }
+
+ return true;
+ }
+
+ public function chmod($ftp_file, $chmod)
+ {
+ if (!is_resource($this->connection))
+ return false;
+
+ if ($ftp_file == '')
+ $ftp_file = '.';
+
+ // Convert the chmod value from octal (0777) to text ("777").
+ fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n");
+ if (!$this->check_response(200))
+ {
+ $this->error = 'bad_file';
+ return false;
+ }
+
+ return true;
+ }
+
+ public function unlink($ftp_file)
+ {
+ // We are actually connected, right?
+ if (!is_resource($this->connection))
+ return false;
+
+ // Delete file X.
+ fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n");
+ if (!$this->check_response(250))
+ {
+ fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n");
+
+ // Still no love?
+ if (!$this->check_response(250))
+ {
+ $this->error = 'bad_file';
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function check_response($desired)
+ {
+ // Wait for a response that isn't continued with -, but don't wait too long.
+ $time = time();
+ do
+ $this->last_message = fgets($this->connection, 1024);
+ while ((strlen($this->last_message) < 4 || substr($this->last_message, 0, 1) == ' ' || substr($this->last_message, 3, 1) != ' ') && time() - $time < 5);
+
+ // Was the desired response returned?
+ return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired;
+ }
+
+ public function passive()
+ {
+ // We can't create a passive data connection without a primary one first being there.
+ if (!is_resource($this->connection))
+ return false;
+
+ // Request a passive connection - this means, we'll talk to you, you don't talk to us.
+ @fwrite($this->connection, 'PASV' . "\r\n");
+ $time = time();
+ do
+ $response = fgets($this->connection, 1024);
+ while (substr($response, 3, 1) != ' ' && time() - $time < 5);
+
+ // If it's not 227, we weren't given an IP and port, which means it failed.
+ if (substr($response, 0, 4) != '227 ')
+ {
+ $this->error = 'bad_response';
+ return false;
+ }
+
+ // Snatch the IP and port information, or die horribly trying...
+ if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0)
+ {
+ $this->error = 'bad_response';
+ return false;
+ }
+
+ // This is pretty simple - store it for later use ;).
+ $this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]);
+
+ return true;
+ }
+
+ public function create_file($ftp_file)
+ {
+ // First, we have to be connected... very important.
+ if (!is_resource($this->connection))
+ return false;
+
+ // I'd like one passive mode, please!
+ if (!$this->passive())
+ return false;
+
+ // Seems logical enough, so far...
+ fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n");
+
+ // Okay, now we connect to the data port. If it doesn't work out, it's probably "file already exists", etc.
+ $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
+ if (!$fp || !$this->check_response(150))
+ {
+ $this->error = 'bad_file';
+ @fclose($fp);
+ return false;
+ }
+
+ // This may look strange, but we're just closing it to indicate a zero-byte upload.
+ fclose($fp);
+ if (!$this->check_response(226))
+ {
+ $this->error = 'bad_response';
+ return false;
+ }
+
+ return true;
+ }
+
+ public function list_dir($ftp_path = '', $search = false)
+ {
+ // Are we even connected...?
+ if (!is_resource($this->connection))
+ return false;
+
+ // Passive... non-agressive...
+ if (!$this->passive())
+ return false;
+
+ // Get the listing!
+ fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n");
+
+ // Connect, assuming we've got a connection.
+ $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
+ if (!$fp || !$this->check_response(array(150, 125)))
+ {
+ $this->error = 'bad_response';
+ @fclose($fp);
+ return false;
+ }
+
+ // Read in the file listing.
+ $data = '';
+ while (!feof($fp))
+ $data .= fread($fp, 4096);
+ fclose($fp);
+
+ // Everything go okay?
+ if (!$this->check_response(226))
+ {
+ $this->error = 'bad_response';
+ return false;
+ }
+
+ return $data;
+ }
+
+ public function locate($file, $listing = null)
+ {
+ if ($listing === null)
+ $listing = $this->list_dir('', true);
+ $listing = explode("\n", $listing);
+
+ @fwrite($this->connection, 'PWD' . "\r\n");
+ $time = time();
+ do
+ $response = fgets($this->connection, 1024);
+ while ($response[3] != ' ' && time() - $time < 5);
+
+ // Check for 257!
+ if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0)
+ $current_dir = strtr($match[1], array('""' => '"'));
+ else
+ $current_dir = '';
+
+ for ($i = 0, $n = count($listing); $i < $n; $i++)
+ {
+ if (trim($listing[$i]) == '' && isset($listing[$i + 1]))
+ {
+ $current_dir = substr(trim($listing[++$i]), 0, -1);
+ $i++;
+ }
+
+ // Okay, this file's name is:
+ $listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]);
+
+ if ($file[0] == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1))
+ return $listing[$i];
+ if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1))
+ return $listing[$i];
+ if (basename($listing[$i]) == $file || $listing[$i] == $file)
+ return $listing[$i];
+ }
+
+ return false;
+ }
+
+ public function create_dir($ftp_dir)
+ {
+ // We must be connected to the server to do something.
+ if (!is_resource($this->connection))
+ return false;
+
+ // Make this new beautiful directory!
+ fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n");
+ if (!$this->check_response(257))
+ {
+ $this->error = 'bad_file';
+ return false;
+ }
+
+ return true;
+ }
+
+ public function detect_path($filesystem_path, $lookup_file = null)
+ {
+ $username = '';
+
+ if (isset($_SERVER['DOCUMENT_ROOT']))
+ {
+ if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match))
+ {
+ $username = $match[1];
+
+ $path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => ''));
+
+ if (substr($path, -1) == '/')
+ $path = substr($path, 0, -1);
+
+ if (strlen(dirname($_SERVER['PHP_SELF'])) > 1)
+ $path .= dirname($_SERVER['PHP_SELF']);
+ }
+ elseif (substr($filesystem_path, 0, 9) == '/var/www/')
+ $path = substr($filesystem_path, 8);
+ else
+ $path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => ''));
+ }
+ else
+ $path = '';
+
+ if (is_resource($this->connection) && $this->list_dir($path) == '')
+ {
+ $data = $this->list_dir('', true);
+
+ if ($lookup_file === null)
+ $lookup_file = $_SERVER['PHP_SELF'];
+
+ $found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data));
+ if ($found_path == false)
+ $found_path = dirname($this->locate(basename($lookup_file)));
+ if ($found_path != false)
+ $path = $found_path;
+ }
+ elseif (is_resource($this->connection))
+ $found_path = true;
+
+ return array($username, $path, isset($found_path));
+ }
+
+ public function close()
+ {
+ // Goodbye!
+ fwrite($this->connection, 'QUIT' . "\r\n");
+ fclose($this->connection);
+
+ return true;
+ }
+ }
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbExtra-mysql.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbExtra-mysql.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,454 @@
+ 'smf_db_backup_table',
+ 'db_optimize_table' => 'smf_db_optimize_table',
+ 'db_insert_sql' => 'smf_db_insert_sql',
+ 'db_table_sql' => 'smf_db_table_sql',
+ 'db_list_tables' => 'smf_db_list_tables',
+ 'db_get_version' => 'smf_db_get_version',
+ );
+}
+
+// Backup $table to $backup_table.
+function smf_db_backup_table($table, $backup_table)
+{
+ global $smcFunc, $db_prefix;
+
+ $table = str_replace('{db_prefix}', $db_prefix, $table);
+
+ // First, get rid of the old table.
+ $smcFunc['db_query']('', '
+ DROP TABLE IF EXISTS {raw:backup_table}',
+ array(
+ 'backup_table' => $backup_table,
+ )
+ );
+
+ // Can we do this the quick way?
+ $result = $smcFunc['db_query']('', '
+ CREATE TABLE {raw:backup_table} LIKE {raw:table}',
+ array(
+ 'backup_table' => $backup_table,
+ 'table' => $table
+ ));
+ // If this failed, we go old school.
+ if ($result)
+ {
+ $request = $smcFunc['db_query']('', '
+ INSERT INTO {raw:backup_table}
+ SELECT *
+ FROM {raw:table}',
+ array(
+ 'backup_table' => $backup_table,
+ 'table' => $table
+ ));
+
+ // Old school or no school?
+ if ($request)
+ return $request;
+ }
+
+ // At this point, the quick method failed.
+ $result = $smcFunc['db_query']('', '
+ SHOW CREATE TABLE {raw:table}',
+ array(
+ 'table' => $table,
+ )
+ );
+ list (, $create) = $smcFunc['db_fetch_row']($result);
+ $smcFunc['db_free_result']($result);
+
+ $create = preg_split('/[\n\r]/', $create);
+
+ $auto_inc = '';
+ // Default engine type.
+ $engine = 'MyISAM';
+ $charset = '';
+ $collate = '';
+
+ foreach ($create as $k => $l)
+ {
+ // Get the name of the auto_increment column.
+ if (strpos($l, 'auto_increment'))
+ $auto_inc = trim($l);
+
+ // For the engine type, see if we can work out what it is.
+ if (strpos($l, 'ENGINE') !== false || strpos($l, 'TYPE') !== false)
+ {
+ // Extract the engine type.
+ preg_match('~(ENGINE|TYPE)=(\w+)(\sDEFAULT)?(\sCHARSET=(\w+))?(\sCOLLATE=(\w+))?~', $l, $match);
+
+ if (!empty($match[1]))
+ $engine = $match[1];
+
+ if (!empty($match[2]))
+ $engine = $match[2];
+
+ if (!empty($match[5]))
+ $charset = $match[5];
+
+ if (!empty($match[7]))
+ $collate = $match[7];
+ }
+
+ // Skip everything but keys...
+ if (strpos($l, 'KEY') === false)
+ unset($create[$k]);
+ }
+
+ if (!empty($create))
+ $create = '(
+ ' . implode('
+ ', $create) . ')';
+ else
+ $create = '';
+
+ $request = $smcFunc['db_query']('', '
+ CREATE TABLE {raw:backup_table} {raw:create}
+ ENGINE={raw:engine}' . (empty($charset) ? '' : ' CHARACTER SET {raw:charset}' . (empty($collate) ? '' : ' COLLATE {raw:collate}')) . '
+ SELECT *
+ FROM {raw:table}',
+ array(
+ 'backup_table' => $backup_table,
+ 'table' => $table,
+ 'create' => $create,
+ 'engine' => $engine,
+ 'charset' => empty($charset) ? '' : $charset,
+ 'collate' => empty($collate) ? '' : $collate,
+ )
+ );
+
+ if ($auto_inc != '')
+ {
+ if (preg_match('~\`(.+?)\`\s~', $auto_inc, $match) != 0 && substr($auto_inc, -1, 1) == ',')
+ $auto_inc = substr($auto_inc, 0, -1);
+
+ $smcFunc['db_query']('', '
+ ALTER TABLE {raw:backup_table}
+ CHANGE COLUMN {raw:column_detail} {raw:auto_inc}',
+ array(
+ 'backup_table' => $backup_table,
+ 'column_detail' => $match[1],
+ 'auto_inc' => $auto_inc,
+ )
+ );
+ }
+
+ return $request;
+}
+
+// Optimize a table - return data freed!
+function smf_db_optimize_table($table)
+{
+ global $smcFunc, $db_name, $db_prefix;
+
+ $table = str_replace('{db_prefix}', $db_prefix, $table);
+
+ // Get how much overhead there is.
+ $request = $smcFunc['db_query']('', '
+ SHOW TABLE STATUS LIKE {string:table_name}',
+ array(
+ 'table_name' => str_replace('_', '\_', $table),
+ )
+ );
+ $row = $smcFunc['db_fetch_assoc']($request);
+ $smcFunc['db_free_result']($request);
+
+ $data_before = isset($row['Data_free']) ? $row['Data_free'] : 0;
+ $request = $smcFunc['db_query']('', '
+ OPTIMIZE TABLE `{raw:table}`',
+ array(
+ 'table' => $table,
+ )
+ );
+ if (!$request)
+ return -1;
+
+ // How much left?
+ $request = $smcFunc['db_query']('', '
+ SHOW TABLE STATUS LIKE {string:table}',
+ array(
+ 'table' => str_replace('_', '\_', $table),
+ )
+ );
+ $row = $smcFunc['db_fetch_assoc']($request);
+ $smcFunc['db_free_result']($request);
+
+ $total_change = isset($row['Data_free']) && $data_before > $row['Data_free'] ? $data_before / 1024 : 0;
+
+ return $total_change;
+}
+
+// List all the tables in the database.
+function smf_db_list_tables($db = false, $filter = false)
+{
+ global $db_name, $smcFunc;
+
+ $db = $db == false ? $db_name : $db;
+ $db = trim($db);
+ $filter = $filter == false ? '' : ' LIKE \'' . $filter . '\'';
+
+ $request = $smcFunc['db_query']('', '
+ SHOW TABLES
+ FROM `{raw:db}`
+ {raw:filter}',
+ array(
+ 'db' => $db[0] == '`' ? strtr($db, array('`' => '')) : $db,
+ 'filter' => $filter,
+ )
+ );
+ $tables = array();
+ while ($row = $smcFunc['db_fetch_row']($request))
+ $tables[] = $row[0];
+ $smcFunc['db_free_result']($request);
+
+ return $tables;
+}
+
+// Get the content (INSERTs) for a table.
+function smf_db_insert_sql($tableName)
+{
+ global $smcFunc, $db_prefix;
+
+ $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+
+ // This will be handy...
+ $crlf = "\r\n";
+
+ // Get everything from the table.
+ $result = $smcFunc['db_query']('', '
+ SELECT /*!40001 SQL_NO_CACHE */ *
+ FROM `{raw:table}`',
+ array(
+ 'table' => $tableName,
+ )
+ );
+
+ // The number of rows, just for record keeping and breaking INSERTs up.
+ $num_rows = $smcFunc['db_num_rows']($result);
+ $current_row = 0;
+
+ if ($num_rows == 0)
+ return '';
+
+ $fields = array_keys($smcFunc['db_fetch_assoc']($result));
+ $smcFunc['db_data_seek']($result, 0);
+
+ // Start it off with the basic INSERT INTO.
+ $data = 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES ';
+
+ // Loop through each row.
+ while ($row = $smcFunc['db_fetch_row']($result))
+ {
+ $current_row++;
+
+ // Get the fields in this row...
+ $field_list = array();
+ for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++)
+ {
+ // Try to figure out the type of each field. (NULL, number, or 'string'.)
+ if (!isset($row[$j]))
+ $field_list[] = 'NULL';
+ elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j])
+ $field_list[] = $row[$j];
+ else
+ $field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\'';
+ }
+
+ // 'Insert' the data.
+ $data .= '(' . implode(', ', $field_list) . ')';
+
+ // All done!
+ if ($current_row == $num_rows)
+ $data .= ';' . $crlf;
+ // Start a new INSERT statement after every 250....
+ elseif ($current_row > 249 && $current_row % 250 == 0)
+ $data .= ';' . $crlf . 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES ';
+ // Otherwise, go to the next line.
+ else
+ $data .= ',' . $crlf . "\t";
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Return an empty string if there were no rows.
+ return $num_rows == 0 ? '' : $data;
+}
+
+// Get the schema (CREATE) for a table.
+function smf_db_table_sql($tableName)
+{
+ global $smcFunc, $db_prefix;
+
+ $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+
+ // This will be needed...
+ $crlf = "\r\n";
+
+ // Drop it if it exists.
+ $schema_create = 'DROP TABLE IF EXISTS `' . $tableName . '`;' . $crlf . $crlf;
+
+ // Start the create table...
+ $schema_create .= 'CREATE TABLE `' . $tableName . '` (' . $crlf;
+
+ // Find all the fields.
+ $result = $smcFunc['db_query']('', '
+ SHOW FIELDS
+ FROM `{raw:table}`',
+ array(
+ 'table' => $tableName,
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ // Make the CREATE for this column.
+ $schema_create .= ' `' . $row['Field'] . '` ' . $row['Type'] . ($row['Null'] != 'YES' ? ' NOT NULL' : '');
+
+ // Add a default...?
+ if (!empty($row['Default']) || $row['Null'] !== 'YES')
+ {
+ // Make a special case of auto-timestamp.
+ if ($row['Default'] == 'CURRENT_TIMESTAMP')
+ $schema_create .= ' /*!40102 NOT NULL default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP */';
+ // Text shouldn't have a default.
+ elseif ($row['Default'] !== null)
+ {
+ // If this field is numeric the default needs no escaping.
+ $type = strtolower($row['Type']);
+ $isNumericColumn = strpos($type, 'int') !== false || strpos($type, 'bool') !== false || strpos($type, 'bit') !== false || strpos($type, 'float') !== false || strpos($type, 'double') !== false || strpos($type, 'decimal') !== false;
+
+ $schema_create .= ' default ' . ($isNumericColumn ? $row['Default'] : '\'' . $smcFunc['db_escape_string']($row['Default']) . '\'');
+ }
+ }
+
+ // And now any extra information. (such as auto_increment.)
+ $schema_create .= ($row['Extra'] != '' ? ' ' . $row['Extra'] : '') . ',' . $crlf;
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Take off the last comma.
+ $schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
+
+ // Find the keys.
+ $result = $smcFunc['db_query']('', '
+ SHOW KEYS
+ FROM `{raw:table}`',
+ array(
+ 'table' => $tableName,
+ )
+ );
+ $indexes = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ // IS this a primary key, unique index, or regular index?
+ $row['Key_name'] = $row['Key_name'] == 'PRIMARY' ? 'PRIMARY KEY' : (empty($row['Non_unique']) ? 'UNIQUE ' : ($row['Comment'] == 'FULLTEXT' || (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT') ? 'FULLTEXT ' : 'KEY ')) . '`' . $row['Key_name'] . '`';
+
+ // Is this the first column in the index?
+ if (empty($indexes[$row['Key_name']]))
+ $indexes[$row['Key_name']] = array();
+
+ // A sub part, like only indexing 15 characters of a varchar.
+ if (!empty($row['Sub_part']))
+ $indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`(' . $row['Sub_part'] . ')';
+ else
+ $indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`';
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Build the CREATEs for the keys.
+ foreach ($indexes as $keyname => $columns)
+ {
+ // Ensure the columns are in proper order.
+ ksort($columns);
+
+ $schema_create .= ',' . $crlf . ' ' . $keyname . ' (' . implode($columns, ', ') . ')';
+ }
+
+ // Now just get the comment and type... (MyISAM, etc.)
+ $result = $smcFunc['db_query']('', '
+ SHOW TABLE STATUS
+ LIKE {string:table}',
+ array(
+ 'table' => strtr($tableName, array('_' => '\\_', '%' => '\\%')),
+ )
+ );
+ $row = $smcFunc['db_fetch_assoc']($result);
+ $smcFunc['db_free_result']($result);
+
+ // Probably MyISAM.... and it might have a comment.
+ $schema_create .= $crlf . ') ENGINE=' . (isset($row['Type']) ? $row['Type'] : $row['Engine']) . ($row['Comment'] != '' ? ' COMMENT="' . $row['Comment'] . '"' : '');
+
+ return $schema_create;
+}
+
+// Get the version number.
+function smf_db_get_version()
+{
+ global $smcFunc;
+
+ $request = $smcFunc['db_query']('', '
+ SELECT VERSION()',
+ array(
+ )
+ );
+ list ($ver) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ return $ver;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbExtra-postgresql.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbExtra-postgresql.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,328 @@
+ 'smf_db_backup_table',
+ 'db_optimize_table' => 'smf_db_optimize_table',
+ 'db_insert_sql' => 'smf_db_insert_sql',
+ 'db_table_sql' => 'smf_db_table_sql',
+ 'db_list_tables' => 'smf_db_list_tables',
+ 'db_get_version' => 'smf_db_get_version',
+ );
+}
+
+// Backup $table to $backup_table.
+function smf_db_backup_table($table, $backup_table)
+{
+ global $smcFunc, $db_prefix;
+
+ $table = str_replace('{db_prefix}', $db_prefix, $table);
+
+ // Do we need to drop it first?
+ $tables = smf_db_list_tables(false, $backup_table);
+ if (!empty($tables))
+ $smcFunc['db_query']('', '
+ DROP TABLE {raw:backup_table}',
+ array(
+ 'backup_table' => $backup_table,
+ )
+ );
+
+ //!!! Should we create backups of sequences as well?
+ $smcFunc['db_query']('', '
+ CREATE TABLE {raw:backup_table}
+ (
+ LIKE {raw:table}
+ INCLUDING DEFAULTS
+ )',
+ array(
+ 'backup_table' => $backup_table,
+ 'table' => $table,
+ )
+ );
+ $smcFunc['db_query']('', '
+ INSERT INTO {raw:backup_table}
+ SELECT * FROM {raw:table}',
+ array(
+ 'backup_table' => $backup_table,
+ 'table' => $table,
+ )
+ );
+}
+
+// Optimize a table - return data freed!
+function smf_db_optimize_table($table)
+{
+ global $smcFunc, $db_prefix;
+
+ $table = str_replace('{db_prefix}', $db_prefix, $table);
+
+ $request = $smcFunc['db_query']('', '
+ VACUUM ANALYZE {raw:table}',
+ array(
+ 'table' => $table,
+ )
+ );
+ if (!$request)
+ return -1;
+
+ $row = $smcFunc['db_fetch_assoc']($request);
+ $smcFunc['db_free_result']($request);
+
+ if (isset($row['Data_free']))
+ return $row['Data_free'] / 1024;
+ else
+ return 0;
+}
+
+// List all the tables in the database.
+function smf_db_list_tables($db = false, $filter = false)
+{
+ global $smcFunc;
+
+ $request = $smcFunc['db_query']('', '
+ SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = {string:schema_public}' . ($filter == false ? '' : '
+ AND tablename LIKE {string:filter}') . '
+ ORDER BY tablename',
+ array(
+ 'schema_public' => 'public',
+ 'filter' => $filter,
+ )
+ );
+
+ $tables = array();
+ while ($row = $smcFunc['db_fetch_row']($request))
+ $tables[] = $row[0];
+ $smcFunc['db_free_result']($request);
+
+ return $tables;
+}
+
+// Get the content (INSERTs) for a table.
+function smf_db_insert_sql($tableName)
+{
+ global $smcFunc, $db_prefix;
+
+ $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+
+ // This will be handy...
+ $crlf = "\r\n";
+
+ // Get everything from the table.
+ $result = $smcFunc['db_query']('', '
+ SELECT *
+ FROM {raw:table}',
+ array(
+ 'table' => $tableName,
+ )
+ );
+
+ // The number of rows, just for record keeping and breaking INSERTs up.
+ $num_rows = $smcFunc['db_num_rows']($result);
+
+ if ($num_rows == 0)
+ return '';
+
+ $fields = array_keys($smcFunc['db_fetch_assoc']($result));
+ $smcFunc['db_data_seek']($result, 0);
+
+ // Start it off with the basic INSERT INTO.
+ $data = '';
+ $insert_msg = $crlf . 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t";
+
+ // Loop through each row.
+ while ($row = $smcFunc['db_fetch_row']($result))
+ {
+ // Get the fields in this row...
+ $field_list = array();
+ for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++)
+ {
+ // Try to figure out the type of each field. (NULL, number, or 'string'.)
+ if (!isset($row[$j]))
+ $field_list[] = 'NULL';
+ elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j])
+ $field_list[] = $row[$j];
+ else
+ $field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\'';
+ }
+
+ // 'Insert' the data.
+ $data .= $insert_msg . '(' . implode(', ', $field_list) . ');';
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Return an empty string if there were no rows.
+ return $num_rows == 0 ? '' : $data;
+}
+
+// Get the schema (CREATE) for a table.
+function smf_db_table_sql($tableName)
+{
+ global $smcFunc, $db_prefix;
+
+ $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+
+ // This will be needed...
+ $crlf = "\r\n";
+
+ // Start the create table...
+ $schema_create = 'CREATE TABLE ' . $tableName . ' (' . $crlf;
+ $index_create = '';
+ $seq_create = '';
+
+ // Find all the fields.
+ $result = $smcFunc['db_query']('', '
+ SELECT column_name, column_default, is_nullable, data_type, character_maximum_length
+ FROM information_schema.columns
+ WHERE table_name = {string:table}
+ ORDER BY ordinal_position',
+ array(
+ 'table' => $tableName,
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if ($row['data_type'] == 'character varying')
+ $row['data_type'] = 'varchar';
+ elseif ($row['data_type'] == 'character')
+ $row['data_type'] = 'char';
+ if ($row['character_maximum_length'])
+ $row['data_type'] .= '(' . $row['character_maximum_length'] . ')';
+
+ // Make the CREATE for this column.
+ $schema_create .= ' "' . $row['column_name'] . '" ' . $row['data_type'] . ($row['is_nullable'] != 'YES' ? ' NOT NULL' : '');
+
+ // Add a default...?
+ if (trim($row['column_default']) != '')
+ {
+ $schema_create .= ' default ' . $row['column_default'] . '';
+
+ // Auto increment?
+ if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0)
+ {
+ // Get to find the next variable first!
+ $count_req = $smcFunc['db_query']('', '
+ SELECT MAX("{raw:column}")
+ FROM {raw:table}',
+ array(
+ 'column' => $row['column_name'],
+ 'table' => $tableName,
+ )
+ );
+ list ($max_ind) = $smcFunc['db_fetch_row']($count_req);
+ $smcFunc['db_free_result']($count_req);
+ // Get the right bloody start!
+ $seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf;
+ }
+ }
+
+ $schema_create .= ',' . $crlf;
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Take off the last comma.
+ $schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
+
+ $result = $smcFunc['db_query']('', '
+ SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, pg_get_indexdef(i.indexrelid) AS inddef
+ FROM pg_class AS c
+ INNER JOIN pg_index AS i ON (i.indrelid = c.oid)
+ INNER JOIN pg_class AS c2 ON (c2.oid = i.indexrelid)
+ WHERE c.relname = {string:table}',
+ array(
+ 'table' => $tableName,
+ )
+ );
+ $indexes = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if ($row['is_primary'])
+ {
+ if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0)
+ continue;
+
+ $index_create .= $crlf . 'ALTER TABLE ' . $tableName . ' ADD PRIMARY KEY ("' . $matches[1] . '");';
+ }
+ else
+ $index_create .= $crlf . $row['inddef'] . ';';
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Finish it off!
+ $schema_create .= $crlf . ');';
+
+ return $seq_create . $schema_create . $index_create;
+}
+
+// Get the version number.
+function smf_db_get_version()
+{
+ global $smcFunc;
+
+ $request = $smcFunc['db_query']('', '
+ SHOW server_version',
+ array(
+ )
+ );
+ list ($ver) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ return $ver;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbExtra-sqlite.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbExtra-sqlite.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,346 @@
+ 'smf_db_backup_table',
+ 'db_optimize_table' => 'smf_db_optimize_table',
+ 'db_insert_sql' => 'smf_db_insert_sql',
+ 'db_table_sql' => 'smf_db_table_sql',
+ 'db_list_tables' => 'smf_db_list_tables',
+ 'db_get_backup' => 'smf_db_get_backup',
+ 'db_get_version' => 'smf_db_get_version',
+ );
+}
+
+// Backup $table to $backup_table.
+function smf_db_backup_table($table, $backup_table)
+{
+ global $smcFunc, $db_prefix;
+
+ $table = str_replace('{db_prefix}', $db_prefix, $table);
+
+ $result = $smcFunc['db_query']('', '
+ SELECT sql
+ FROM sqlite_master
+ WHERE type = {string:txttable}
+ AND name = {string:table}',
+ array(
+ 'table' => $table,
+ 'txttable' => 'table'
+ )
+ );
+ list ($create) = $smcFunc['db_fetch_row']($result);
+ $smcFunc['db_free_result']($result);
+
+ $create = preg_split('/[\n\r]/', $create);
+ $auto_inc = '';
+
+ // Remove the first line and check to see if the second one contain useless info.
+ unset($create[0]);
+ if (trim($create[1]) == '(')
+ unset($create[1]);
+ if (trim($create[count($create)]) == ')')
+ unset($create[count($create)]);
+
+ foreach ($create as $k => $l)
+ {
+ // Get the name of the auto_increment column.
+ if (strpos($l, 'primary') || strpos($l, 'PRIMARY'))
+ $auto_inc = trim($l);
+
+ // Skip everything but keys...
+ if ((strpos($l, 'KEY') !== false && strpos($l, 'PRIMARY KEY') === false) || strpos($l, $table) !== false || strpos(trim($l), 'PRIMARY KEY') === 0)
+ unset($create[$k]);
+ }
+
+ if (!empty($create))
+ $create = '(
+ ' . implode('
+ ', $create) . ')';
+ else
+ $create = '';
+
+ // Is there an extra junk at the end?
+ if (substr($create, -2, 1) == ',')
+ $create = substr($create, 0, -2) . ')';
+ if (substr($create, -2) == '))')
+ $create = substr($create, 0, -1);
+
+ $smcFunc['db_query']('', '
+ DROP TABLE {raw:backup_table}',
+ array(
+ 'backup_table' => $backup_table,
+ 'db_error_skip' => true,
+ )
+ );
+
+ $request = $smcFunc['db_quote']('
+ CREATE TABLE {raw:backup_table} {raw:create}',
+ array(
+ 'backup_table' => $backup_table,
+ 'create' => $create,
+ ));
+
+ $smcFunc['db_query']('', '
+ CREATE TABLE {raw:backup_table} {raw:create}',
+ array(
+ 'backup_table' => $backup_table,
+ 'create' => $create,
+ ));
+
+ $request = $smcFunc['db_query']('', '
+ INSERT INTO {raw:backup_table}
+ SELECT *
+ FROM {raw:table}',
+ array(
+ 'backup_table' => $backup_table,
+ 'table' => $table,
+ ));
+
+ return $request;
+}
+
+// Optimize a table - return data freed!
+function smf_db_optimize_table($table)
+{
+ global $smcFunc, $db_prefix;
+
+ $table = str_replace('{db_prefix}', $db_prefix, $table);
+
+ $request = $smcFunc['db_query']('', '
+ VACUUM {raw:table}',
+ array(
+ 'table' => $table,
+ )
+ );
+ if (!$request)
+ return -1;
+
+ $row = $smcFunc['db_fetch_assoc']($request);
+ $smcFunc['db_free_result']($request);
+
+ // The function returns nothing.
+ return 0;
+}
+
+// List all the tables in the database.
+function smf_db_list_tables($db = false, $filter = false)
+{
+ global $smcFunc;
+
+ $filter = $filter == false ? '' : ' AND name LIKE \'' . str_replace("\_", "_", $filter) . '\'';
+
+ $request = $smcFunc['db_query']('', '
+ SELECT name
+ FROM sqlite_master
+ WHERE type = {string:type}
+ {raw:filter}
+ ORDER BY name',
+ array(
+ 'type' => 'table',
+ 'filter' => $filter,
+ )
+ );
+ $tables = array();
+ while ($row = $smcFunc['db_fetch_row']($request))
+ $tables[] = $row[0];
+ $smcFunc['db_free_result']($request);
+
+ return $tables;
+}
+
+// Get the content (INSERTs) for a table.
+function smf_db_insert_sql($tableName)
+{
+ global $smcFunc, $db_prefix;
+
+ $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+
+ // This will be handy...
+ $crlf = "\r\n";
+
+ // Get everything from the table.
+ $result = $smcFunc['db_query']('', '
+ SELECT *
+ FROM {raw:table}',
+ array(
+ 'table' => $tableName,
+ )
+ );
+
+ // The number of rows, just for record keeping and breaking INSERTs up.
+ $num_rows = $smcFunc['db_num_rows']($result);
+
+ if ($num_rows == 0)
+ return '';
+
+ $fields = array_keys($smcFunc['db_fetch_assoc']($result));
+
+ // SQLite fetches an array so we need to filter out the numberic index for the columns.
+ foreach ($fields as $key => $name)
+ if (is_numeric($name))
+ unset($fields[$key]);
+
+ $smcFunc['db_data_seek']($result, 0);
+
+ // Start it off with the basic INSERT INTO.
+ $data = 'BEGIN TRANSACTION;' . $crlf;
+
+ // Loop through each row.
+ while ($row = $smcFunc['db_fetch_row']($result))
+ {
+ // Get the fields in this row...
+ $field_list = array();
+ for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++)
+ {
+ // Try to figure out the type of each field. (NULL, number, or 'string'.)
+ if (!isset($row[$j]))
+ $field_list[] = 'NULL';
+ elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j])
+ $field_list[] = $row[$j];
+ else
+ $field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\'';
+ }
+ $data .= 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $field_list) . ');' . $crlf;
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Return an empty string if there were no rows.
+ return $num_rows == 0 ? '' : $data . 'COMMIT;' . $crlf;
+}
+
+// Get the schema (CREATE) for a table.
+function smf_db_table_sql($tableName)
+{
+ global $smcFunc, $db_prefix;
+
+ $tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+
+ // This will be needed...
+ $crlf = "\r\n";
+
+ // Start the create table...
+ $schema_create = '';
+ $index_create = '';
+
+ // Let's get the create statement directly from SQLite.
+ $result = $smcFunc['db_query']('', '
+ SELECT sql
+ FROM sqlite_master
+ WHERE type = {string:type}
+ AND name = {string:table_name}',
+ array(
+ 'type' => 'table',
+ 'table_name' => $tableName,
+ )
+ );
+ list ($schema_create) = $smcFunc['db_fetch_row']($result);
+ $smcFunc['db_free_result']($result);
+
+ // Now the indexes.
+ $result = $smcFunc['db_query']('', '
+ SELECT sql
+ FROM sqlite_master
+ WHERE type = {string:type}
+ AND tbl_name = {string:table_name}',
+ array(
+ 'type' => 'index',
+ 'table_name' => $tableName,
+ )
+ );
+ $indexes = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ if (trim($row['sql']) != '')
+ $indexes[] = $row['sql'];
+ $smcFunc['db_free_result']($result);
+
+ $index_create .= implode(';' . $crlf, $indexes);
+ $schema_create = empty($indexes) ? rtrim($schema_create) : $schema_create . ';' . $crlf . $crlf;
+
+ return $schema_create . $index_create;
+}
+
+// Get the version number.
+function smf_db_get_version()
+{
+ return sqlite_libversion();
+}
+
+// Simple return the database - and die!
+function smf_db_get_backup()
+{
+ global $db_name;
+
+ $db_file = substr($db_name, -3) === '.db' ? $db_name : $db_name . '.db';
+
+ // Add more info if zipped...
+ $ext = '';
+ if (isset($_REQUEST['compress']) && function_exists('gzencode'))
+ $ext = '.gz';
+
+ // Do the remaining headers.
+ header('Content-Disposition: attachment; filename="' . $db_file . $ext . '"');
+ header('Cache-Control: private');
+ header('Connection: close');
+
+ // Literally dump the contents. Try reading the file first.
+ if (@readfile($db_file) == null)
+ echo file_get_contents($db_file);
+
+ obExit(false);
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbPackages-mysql.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbPackages-mysql.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,582 @@
+ Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. If not
+ set SMF will pick a size.
+ + 'default' = Default value - do not set if no default required.
+ + 'null' => Can it be null (true or false) - if not set default will be false.
+ + 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set
+ from what it should begin counting.
+ - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are:
+ + 'name' => Index name (If left empty SMF will generate).
+ + 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'.
+ + 'columns' => Array containing columns that form part of key - in the order the index is to be created.
+ - parameters: (None yet)
+ - if_exists values:
+ + 'ignore' will do nothing if the table exists. (And will return true)
+ + 'overwrite' will drop any existing table of the same name.
+ + 'error' will return false if the table already exists.
+
+*/
+
+// Add the file functions to the $smcFunc array.
+function db_packages_init()
+{
+ global $smcFunc, $reservedTables, $db_package_log, $db_prefix;
+
+ if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table')
+ {
+ $smcFunc += array(
+ 'db_add_column' => 'smf_db_add_column',
+ 'db_add_index' => 'smf_db_add_index',
+ 'db_calculate_type' => 'smf_db_calculate_type',
+ 'db_change_column' => 'smf_db_change_column',
+ 'db_create_table' => 'smf_db_create_table',
+ 'db_drop_table' => 'smf_db_drop_table',
+ 'db_table_structure' => 'smf_db_table_structure',
+ 'db_list_columns' => 'smf_db_list_columns',
+ 'db_list_indexes' => 'smf_db_list_indexes',
+ 'db_remove_column' => 'smf_db_remove_column',
+ 'db_remove_index' => 'smf_db_remove_index',
+ );
+ $db_package_log = array();
+ }
+
+ // We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up!
+ $reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items',
+ 'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories', 'collapsed_categories',
+ 'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards',
+ 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_karma', 'log_mark_read',
+ 'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments',
+ 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects',
+ 'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons',
+ 'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages',
+ 'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys',
+ 'themes', 'topics');
+ foreach ($reservedTables as $k => $table_name)
+ $reservedTables[$k] = strtolower($db_prefix . $table_name);
+
+ // We in turn may need the extra stuff.
+ db_extend('extra');
+}
+
+// Create a table.
+function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal')
+{
+ global $reservedTables, $smcFunc, $db_package_log, $db_prefix, $db_character_set;
+
+ // Strip out the table name, we might not need it in some cases
+ $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
+
+ // With or without the database name, the fullname looks like this.
+ $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // First - no way do we touch SMF tables.
+ if (in_array(strtolower($table_name), $reservedTables))
+ return false;
+
+ // Log that we'll want to remove this on uninstall.
+ $db_package_log[] = array('remove_table', $table_name);
+
+ // Slightly easier on MySQL than the others...
+ $tables = $smcFunc['db_list_tables']();
+ if (in_array($full_table_name, $tables))
+ {
+ // This is a sad day... drop the table? If not, return false (error) by default.
+ if ($if_exists == 'overwrite')
+ $smcFunc['db_drop_table']($table_name);
+ else
+ return $if_exists == 'ignore';
+ }
+
+ // Righty - let's do the damn thing!
+ $table_query = 'CREATE TABLE ' . $table_name . "\n" . '(';
+ foreach ($columns as $column)
+ {
+ // Auto increment is easy here!
+ if (!empty($column['auto']))
+ {
+ $default = 'auto_increment';
+ }
+ elseif (isset($column['default']) && $column['default'] !== null)
+ $default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\'';
+ else
+ $default = '';
+
+ // Sort out the size... and stuff...
+ $column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null;
+ list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']);
+
+ // Allow unsigned integers (mysql only)
+ $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column['unsigned']) ? 'unsigned ' : '';
+
+ if ($size !== null)
+ $type = $type . '(' . $size . ')';
+
+ // Now just put it together!
+ $table_query .= "\n\t`" .$column['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default . ',';
+ }
+
+ // Loop through the indexes next...
+ foreach ($indexes as $index)
+ {
+ $columns = implode(',', $index['columns']);
+
+ // Is it the primary?
+ if (isset($index['type']) && $index['type'] == 'primary')
+ $table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),';
+ else
+ {
+ if (empty($index['name']))
+ $index['name'] = implode('_', $index['columns']);
+ $table_query .= "\n\t" . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : 'KEY') . ' ' . $index['name'] . ' (' . $columns . '),';
+ }
+ }
+
+ // No trailing commas!
+ if (substr($table_query, -1) == ',')
+ $table_query = substr($table_query, 0, -1);
+
+ $table_query .= ') ENGINE=MyISAM';
+ if (!empty($db_character_set) && $db_character_set == 'utf8')
+ $table_query .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
+
+ // Create the table!
+ $smcFunc['db_query']('', $table_query,
+ array(
+ 'security_override' => true,
+ )
+ );
+}
+
+// Drop a table.
+function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal')
+{
+ global $reservedTables, $smcFunc, $db_prefix;
+
+ // After stripping away the database name, this is what's left.
+ $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
+
+ // Get some aliases.
+ $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // God no - dropping one of these = bad.
+ if (in_array(strtolower($table_name), $reservedTables))
+ return false;
+
+ // Does it exist?
+ if (in_array($full_table_name, $smcFunc['db_list_tables']()))
+ {
+ $query = 'DROP TABLE ' . $table_name;
+ $smcFunc['db_query']('',
+ $query,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+
+ // Otherwise do 'nout.
+ return false;
+}
+
+// Add a column.
+function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
+{
+ global $smcFunc, $db_package_log, $txt, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Log that we will want to uninstall this!
+ $db_package_log[] = array('remove_column', $table_name, $column_info['name']);
+
+ // Does it exist - if so don't add it again!
+ $columns = $smcFunc['db_list_columns']($table_name, false);
+ foreach ($columns as $column)
+ if ($column == $column_info['name'])
+ {
+ // If we're going to overwrite then use change column.
+ if ($if_exists == 'update')
+ return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info);
+ else
+ return false;
+ }
+
+ // Get the specifics...
+ $column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null;
+ list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']);
+
+ // Allow unsigned integers (mysql only)
+ $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column_info['unsigned']) ? 'unsigned ' : '';
+
+ if ($size !== null)
+ $type = $type . '(' . $size . ')';
+
+ // Now add the thing!
+ $query = '
+ ALTER TABLE ' . $table_name . '
+ ADD `' . $column_info['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (empty($column_info['null']) ? 'NOT NULL' : '') . ' ' .
+ (!isset($column_info['default']) ? '' : 'default \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'') . ' ' .
+ (empty($column_info['auto']) ? '' : 'auto_increment primary key') . ' ';
+ $smcFunc['db_query']('', $query,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+}
+
+// Remove a column.
+function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Does it exist?
+ $columns = $smcFunc['db_list_columns']($table_name, true);
+ foreach ($columns as $column)
+ if ($column['name'] == $column_name)
+ {
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ DROP COLUMN ' . $column_name,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+
+ // If here we didn't have to work - joy!
+ return false;
+}
+
+// Change a column.
+function smf_db_change_column($table_name, $old_column, $column_info, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Check it does exist!
+ $columns = $smcFunc['db_list_columns']($table_name, true);
+ $old_info = null;
+ foreach ($columns as $column)
+ if ($column['name'] == $old_column)
+ $old_info = $column;
+
+ // Nothing?
+ if ($old_info == null)
+ return false;
+
+ // Get the right bits.
+ if (!isset($column_info['name']))
+ $column_info['name'] = $old_column;
+ if (!isset($column_info['default']))
+ $column_info['default'] = $old_info['default'];
+ if (!isset($column_info['null']))
+ $column_info['null'] = $old_info['null'];
+ if (!isset($column_info['auto']))
+ $column_info['auto'] = $old_info['auto'];
+ if (!isset($column_info['type']))
+ $column_info['type'] = $old_info['type'];
+ if (!isset($column_info['size']) || !is_numeric($column_info['size']))
+ $column_info['size'] = $old_info['size'];
+ if (!isset($column_info['unsigned']) || !in_array($column_info['type'], array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')))
+ $column_info['unsigned'] = '';
+
+ list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']);
+
+ // Allow for unsigned integers (mysql only)
+ $unsigned = in_array($type, array('int', 'tinyint', 'smallint', 'mediumint', 'bigint')) && !empty($column_info['unsigned']) ? 'unsigned ' : '';
+
+ if ($size !== null)
+ $type = $type . '(' . $size . ')';
+
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ CHANGE COLUMN `' . $old_column . '` `' . $column_info['name'] . '` ' . $type . ' ' . (!empty($unsigned) ? $unsigned : '') . (empty($column_info['null']) ? 'NOT NULL' : '') . ' ' .
+ (!isset($column_info['default']) ? '' : 'default \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'') . ' ' .
+ (empty($column_info['auto']) ? '' : 'auto_increment') . ' ',
+ array(
+ 'security_override' => true,
+ )
+ );
+}
+
+// Add an index.
+function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
+{
+ global $smcFunc, $db_package_log, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // No columns = no index.
+ if (empty($index_info['columns']))
+ return false;
+ $columns = implode(',', $index_info['columns']);
+
+ // No name - make it up!
+ if (empty($index_info['name']))
+ {
+ // No need for primary.
+ if (isset($index_info['type']) && $index_info['type'] == 'primary')
+ $index_info['name'] = '';
+ else
+ $index_info['name'] = implode('_', $index_info['columns']);
+ }
+ else
+ $index_info['name'] = $index_info['name'];
+
+ // Log that we are going to want to remove this!
+ $db_package_log[] = array('remove_index', $table_name, $index_info['name']);
+
+ // Let's get all our indexes.
+ $indexes = $smcFunc['db_list_indexes']($table_name, true);
+ // Do we already have it?
+ foreach ($indexes as $index)
+ {
+ if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary'))
+ {
+ // If we want to overwrite simply remove the current one then continue.
+ if ($if_exists != 'update' || $index['type'] == 'primary')
+ return false;
+ else
+ $smcFunc['db_remove_index']($table_name, $index_info['name']);
+ }
+ }
+
+ // If we're here we know we don't have the index - so just add it.
+ if (!empty($index_info['type']) && $index_info['type'] == 'primary')
+ {
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ADD PRIMARY KEY (' . $columns . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+ else
+ {
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ADD ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : 'INDEX') . ' ' . $index_info['name'] . ' (' . $columns . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+}
+
+// Remove an index.
+function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Better exist!
+ $indexes = $smcFunc['db_list_indexes']($table_name, true);
+
+ foreach ($indexes as $index)
+ {
+ // If the name is primary we want the primary key!
+ if ($index['type'] == 'primary' && $index_name == 'primary')
+ {
+ // Dropping primary key?
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ DROP PRIMARY KEY',
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+ if ($index['name'] == $index_name)
+ {
+ // Drop the bugger...
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ DROP INDEX ' . $index_name,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+ }
+
+ // Not to be found ;(
+ return false;
+}
+
+// Get the schema formatted name for a type.
+function smf_db_calculate_type($type_name, $type_size = null, $reverse = false)
+{
+ // MySQL is actually the generic baseline.
+ return array($type_name, $type_size);
+}
+
+// Get table structure.
+function smf_db_table_structure($table_name, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ return array(
+ 'name' => $table_name,
+ 'columns' => $smcFunc['db_list_columns']($table_name, true),
+ 'indexes' => $smcFunc['db_list_indexes']($table_name, true),
+ );
+}
+
+// Return column information for a table.
+function smf_db_list_columns($table_name, $detail = false, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ $result = $smcFunc['db_query']('', '
+ SHOW FIELDS
+ FROM {raw:table_name}',
+ array(
+ 'table_name' => substr($table_name, 0, 1) == '`' ? $table_name : '`' . $table_name . '`',
+ )
+ );
+ $columns = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if (!$detail)
+ {
+ $columns[] = $row['Field'];
+ }
+ else
+ {
+ // Is there an auto_increment?
+ $auto = strpos($row['Extra'], 'auto_increment') !== false ? true : false;
+
+ // Can we split out the size?
+ if (preg_match('~(.+?)\s*\((\d+)\)(?:(?:\s*)?(unsigned))?~i', $row['Type'], $matches) === 1)
+ {
+ $type = $matches[1];
+ $size = $matches[2];
+ if (!empty($matches[3]) && $matches[3] == 'unsigned')
+ $unsigned = true;
+ }
+ else
+ {
+ $type = $row['Type'];
+ $size = null;
+ }
+
+ $columns[$row['Field']] = array(
+ 'name' => $row['Field'],
+ 'null' => $row['Null'] != 'YES' ? false : true,
+ 'default' => isset($row['Default']) ? $row['Default'] : null,
+ 'type' => $type,
+ 'size' => $size,
+ 'auto' => $auto,
+ );
+
+ if (isset($unsigned))
+ {
+ $columns[$row['Field']]['unsigned'] = $unsigned;
+ unset($unsigned);
+ }
+ }
+ }
+ $smcFunc['db_free_result']($result);
+
+ return $columns;
+}
+
+// What about some index information?
+function smf_db_list_indexes($table_name, $detail = false, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ $result = $smcFunc['db_query']('', '
+ SHOW KEYS
+ FROM {raw:table_name}',
+ array(
+ 'table_name' => substr($table_name, 0, 1) == '`' ? $table_name : '`' . $table_name . '`',
+ )
+ );
+ $indexes = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if (!$detail)
+ $indexes[] = $row['Key_name'];
+ else
+ {
+ // What is the type?
+ if ($row['Key_name'] == 'PRIMARY')
+ $type = 'primary';
+ elseif (empty($row['Non_unique']))
+ $type = 'unique';
+ elseif (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT')
+ $type = 'fulltext';
+ else
+ $type = 'index';
+
+ // This is the first column we've seen?
+ if (empty($indexes[$row['Key_name']]))
+ {
+ $indexes[$row['Key_name']] = array(
+ 'name' => $row['Key_name'],
+ 'type' => $type,
+ 'columns' => array(),
+ );
+ }
+
+ // Is it a partial index?
+ if (!empty($row['Sub_part']))
+ $indexes[$row['Key_name']]['columns'][] = $row['Column_name'] . '(' . $row['Sub_part'] . ')';
+ else
+ $indexes[$row['Key_name']]['columns'][] = $row['Column_name'];
+ }
+ }
+ $smcFunc['db_free_result']($result);
+
+ return $indexes;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbPackages-postgresql.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbPackages-postgresql.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,746 @@
+ Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. If not
+ set SMF will pick a size.
+ + 'default' = Default value - do not set if no default required.
+ + 'null' => Can it be null (true or false) - if not set default will be false.
+ + 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set
+ from what it should begin counting.
+ - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are:
+ + 'name' => Index name (If left empty SMF will generate).
+ + 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'.
+ + 'columns' => Array containing columns that form part of key - in the order the index is to be created.
+ - parameters: (None yet)
+ - if_exists values:
+ + 'ignore' will do nothing if the table exists. (And will return true)
+ + 'overwrite' will drop any existing table of the same name.
+ + 'error' will return false if the table already exists.
+
+*/
+
+// Add the file functions to the $smcFunc array.
+function db_packages_init()
+{
+ global $smcFunc, $reservedTables, $db_package_log, $db_prefix;
+
+ if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table')
+ {
+ $smcFunc += array(
+ 'db_add_column' => 'smf_db_add_column',
+ 'db_add_index' => 'smf_db_add_index',
+ 'db_calculate_type' => 'smf_db_calculate_type',
+ 'db_change_column' => 'smf_db_change_column',
+ 'db_create_table' => 'smf_db_create_table',
+ 'db_drop_table' => 'smf_db_drop_table',
+ 'db_table_structure' => 'smf_db_table_structure',
+ 'db_list_columns' => 'smf_db_list_columns',
+ 'db_list_indexes' => 'smf_db_list_indexes',
+ 'db_remove_column' => 'smf_db_remove_column',
+ 'db_remove_index' => 'smf_db_remove_index',
+ );
+ $db_package_log = array();
+ }
+
+ // We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up!
+ $reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items',
+ 'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories', 'collapsed_categories',
+ 'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards',
+ 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_karma', 'log_mark_read',
+ 'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments',
+ 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects',
+ 'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons',
+ 'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages',
+ 'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys',
+ 'themes', 'topics');
+ foreach ($reservedTables as $k => $table_name)
+ $reservedTables[$k] = strtolower($db_prefix . $table_name);
+
+ // We in turn may need the extra stuff.
+ db_extend('extra');
+}
+
+// Create a table.
+function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal')
+{
+ global $reservedTables, $smcFunc, $db_package_log, $db_prefix;
+
+ // Strip out the table name, we might not need it in some cases
+ $real_prefix = preg_match('~^("?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
+
+ // With or without the database name, the fullname looks like this.
+ $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // First - no way do we touch SMF tables.
+ if (in_array(strtolower($table_name), $reservedTables))
+ return false;
+
+ // Log that we'll want to remove this on uninstall.
+ $db_package_log[] = array('remove_table', $table_name);
+
+ // This... my friends... is a function in a half - let's start by checking if the table exists!
+ $tables = $smcFunc['db_list_tables']();
+ if (in_array($full_table_name, $tables))
+ {
+ // This is a sad day... drop the table? If not, return false (error) by default.
+ if ($if_exists == 'overwrite')
+ $smcFunc['db_drop_table']($table_name);
+ else
+ return $if_exists == 'ignore';
+ }
+
+ // If we've got this far - good news - no table exists. We can build our own!
+ $smcFunc['db_transaction']('begin');
+ $table_query = 'CREATE TABLE ' . $table_name . "\n" . '(';
+ foreach ($columns as $column)
+ {
+ // If we have an auto increment do it!
+ if (!empty($column['auto']))
+ {
+ $smcFunc['db_query']('', '
+ CREATE SEQUENCE ' . $table_name . '_seq',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $default = 'default nextval(\'' . $table_name . '_seq\')';
+ }
+ elseif (isset($column['default']) && $column['default'] !== null)
+ $default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\'';
+ else
+ $default = '';
+
+ // Sort out the size...
+ $column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null;
+ list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']);
+ if ($size !== null)
+ $type = $type . '(' . $size . ')';
+
+ // Now just put it together!
+ $table_query .= "\n\t\"" . $column['name'] . '" ' . $type . ' ' . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default . ',';
+ }
+
+ // Loop through the indexes a sec...
+ $index_queries = array();
+ foreach ($indexes as $index)
+ {
+ $columns = implode(',', $index['columns']);
+
+ // Primary goes in the table...
+ if (isset($index['type']) && $index['type'] == 'primary')
+ $table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),';
+ else
+ {
+ if (empty($index['name']))
+ $index['name'] = implode('_', $index['columns']);
+ $index_queries[] = 'CREATE ' . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $table_name . '_' . $index['name'] . ' ON ' . $table_name . ' (' . $columns . ')';
+ }
+ }
+
+ // No trailing commas!
+ if (substr($table_query, -1) == ',')
+ $table_query = substr($table_query, 0, -1);
+
+ $table_query .= ')';
+
+ // Create the table!
+ $smcFunc['db_query']('', $table_query,
+ array(
+ 'security_override' => true,
+ )
+ );
+ // And the indexes...
+ foreach ($index_queries as $query)
+ $smcFunc['db_query']('', $query,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ // Go, go power rangers!
+ $smcFunc['db_transaction']('commit');
+}
+
+// Drop a table.
+function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal')
+{
+ global $reservedTables, $smcFunc, $db_prefix;
+
+ // After stripping away the database name, this is what's left.
+ $real_prefix = preg_match('~^("?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
+
+ // Get some aliases.
+ $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // God no - dropping one of these = bad.
+ if (in_array(strtolower($table_name), $reservedTables))
+ return false;
+
+ // Does it exist?
+ if (in_array($full_table_name, $smcFunc['db_list_tables']()))
+ {
+ // We can then drop the table.
+ $smcFunc['db_transaction']('begin');
+
+ // the table
+ $table_query = 'DROP TABLE ' . $table_name;
+
+ // and the assosciated sequence, if any
+ $sequence_query = 'DROP SEQUENCE IF EXISTS ' . $table_name . '_seq';
+
+ // drop them
+ $smcFunc['db_query']('',
+ $table_query,
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_query']('',
+ $sequence_query,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ $smcFunc['db_transaction']('commit');
+
+ return true;
+ }
+
+ // Otherwise do 'nout.
+ return false;
+}
+
+// Add a column.
+function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
+{
+ global $smcFunc, $db_package_log, $txt, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Log that we will want to uninstall this!
+ $db_package_log[] = array('remove_column', $table_name, $column_info['name']);
+
+ // Does it exist - if so don't add it again!
+ $columns = $smcFunc['db_list_columns']($table_name, false);
+ foreach ($columns as $column)
+ if ($column == $column_info['name'])
+ {
+ // If we're going to overwrite then use change column.
+ if ($if_exists == 'update')
+ return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info);
+ else
+ return false;
+ }
+
+ // Get the specifics...
+ $column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null;
+ list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']);
+ if ($size !== null)
+ $type = $type . '(' . $size . ')';
+
+ // Now add the thing!
+ $query = '
+ ALTER TABLE ' . $table_name . '
+ ADD COLUMN ' . $column_info['name'] . ' ' . $type;
+ $smcFunc['db_query']('', $query,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ // If there's more attributes they need to be done via a change on PostgreSQL.
+ unset($column_info['type'], $column_info['size']);
+
+ if (count($column_info) != 1)
+ return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info);
+ else
+ return true;
+}
+
+// Remove a column.
+function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Does it exist?
+ $columns = $smcFunc['db_list_columns']($table_name, true);
+ foreach ($columns as $column)
+ if ($column['name'] == $column_name)
+ {
+ // If there is an auto we need remove it!
+ if ($column['auto'])
+ $smcFunc['db_query']('',
+ 'DROP SEQUENCE ' . $table_name . '_seq',
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ DROP COLUMN ' . $column_name,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+
+ // If here we didn't have to work - joy!
+ return false;
+}
+
+// Change a column.
+function smf_db_change_column($table_name, $old_column, $column_info, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Check it does exist!
+ $columns = $smcFunc['db_list_columns']($table_name, true);
+ $old_info = null;
+ foreach ($columns as $column)
+ if ($column['name'] == $old_column)
+ $old_info = $column;
+
+ // Nothing?
+ if ($old_info == null)
+ return false;
+
+ // Now we check each bit individually and ALTER as required.
+ if (isset($column_info['name']) && $column_info['name'] != $old_column)
+ {
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ RENAME COLUMN ' . $old_column . ' TO ' . $column_info['name'],
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+ // Different default?
+ if (isset($column_info['default']) && $column_info['default'] != $old_info['default'])
+ {
+ $action = $column_info['default'] !== null ? 'SET DEFAULT \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'' : 'DROP DEFAULT';
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ALTER COLUMN ' . $column_info['name'] . ' ' . $action,
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+ // Is it null - or otherwise?
+ if (isset($column_info['null']) && $column_info['null'] != $old_info['null'])
+ {
+ $action = $column_info['null'] ? 'DROP' : 'SET';
+ $smcFunc['db_transaction']('begin');
+ if (!$column_info['null'])
+ {
+ // We have to set it to something if we are making it NOT NULL.
+ $setTo = isset($column_info['default']) ? $column_info['default'] : '';
+ $smcFunc['db_query']('', '
+ UPDATE ' . $table_name . '
+ SET ' . $column_info['name'] . ' = \'' . $setTo . '\'
+ WHERE ' . $column_info['name'] . ' = NULL',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ALTER COLUMN ' . $column_info['name'] . ' ' . $action . ' NOT NULL',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_transaction']('commit');
+ }
+ // What about a change in type?
+ if (isset($column_info['type']) && ($column_info['type'] != $old_info['type'] || (isset($column_info['size']) && $column_info['size'] != $old_info['size'])))
+ {
+ $column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null;
+ list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']);
+ if ($size !== null)
+ $type = $type . '(' . $size . ')';
+
+ // The alter is a pain.
+ $smcFunc['db_transaction']('begin');
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ADD COLUMN ' . $column_info['name'] . '_tempxx ' . $type,
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_query']('', '
+ UPDATE ' . $table_name . '
+ SET ' . $column_info['name'] . '_tempxx = CAST(' . $column_info['name'] . ' AS ' . $type . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ DROP COLUMN ' . $column_info['name'],
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ RENAME COLUMN ' . $column_info['name'] . '_tempxx TO ' . $column_info['name'],
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_transaction']('commit');
+ }
+ // Finally - auto increment?!
+ if (isset($column_info['auto']) && $column_info['auto'] != $old_info['auto'])
+ {
+ // Are we removing an old one?
+ if ($old_info['auto'])
+ {
+ // Alter the table first - then drop the sequence.
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ALTER COLUMN ' . $column_info['name'] . ' SET DEFAULT \'0\'',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_query']('', '
+ DROP SEQUENCE ' . $table_name . '_seq',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+ // Otherwise add it!
+ else
+ {
+ $smcFunc['db_query']('', '
+ CREATE SEQUENCE ' . $table_name . '_seq',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ALTER COLUMN ' . $column_info['name'] . ' SET DEFAULT nextval(\'' . $table_name . '_seq\')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+ }
+}
+
+// Add an index.
+function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
+{
+ global $smcFunc, $db_package_log, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // No columns = no index.
+ if (empty($index_info['columns']))
+ return false;
+ $columns = implode(',', $index_info['columns']);
+
+ // No name - make it up!
+ if (empty($index_info['name']))
+ {
+ // No need for primary.
+ if (isset($index_info['type']) && $index_info['type'] == 'primary')
+ $index_info['name'] = '';
+ else
+ $index_info['name'] = $table_name . implode('_', $index_info['columns']);
+ }
+ else
+ $index_info['name'] = $table_name . $index_info['name'];
+
+ // Log that we are going to want to remove this!
+ $db_package_log[] = array('remove_index', $table_name, $index_info['name']);
+
+ // Let's get all our indexes.
+ $indexes = $smcFunc['db_list_indexes']($table_name, true);
+ // Do we already have it?
+ foreach ($indexes as $index)
+ {
+ if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary'))
+ {
+ // If we want to overwrite simply remove the current one then continue.
+ if ($if_exists != 'update' || $index['type'] == 'primary')
+ return false;
+ else
+ $smcFunc['db_remove_index']($table_name, $index_info['name']);
+ }
+ }
+
+ // If we're here we know we don't have the index - so just add it.
+ if (!empty($index_info['type']) && $index_info['type'] == 'primary')
+ {
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ ADD PRIMARY KEY (' . $columns . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+ else
+ {
+ $smcFunc['db_query']('', '
+ CREATE ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $index_info['name'] . ' ON ' . $table_name . ' (' . $columns . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+}
+
+// Remove an index.
+function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Better exist!
+ $indexes = $smcFunc['db_list_indexes']($table_name, true);
+ if ($index_name != 'primary')
+ $index_name = $table_name . '_' . $index_name;
+
+ foreach ($indexes as $index)
+ {
+ // If the name is primary we want the primary key!
+ if ($index['type'] == 'primary' && $index_name == 'primary')
+ {
+ // Dropping primary key is odd...
+ $smcFunc['db_query']('', '
+ ALTER TABLE ' . $table_name . '
+ DROP CONSTRAINT ' . $index['name'],
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+ if ($index['name'] == $index_name)
+ {
+ // Drop the bugger...
+ $smcFunc['db_query']('', '
+ DROP INDEX ' . $index_name,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+ }
+
+ // Not to be found ;(
+ return false;
+}
+
+// Get the schema formatted name for a type.
+function smf_db_calculate_type($type_name, $type_size = null, $reverse = false)
+{
+ // Generic => Specific.
+ if (!$reverse)
+ {
+ $types = array(
+ 'varchar' => 'character varying',
+ 'char' => 'character',
+ 'mediumint' => 'int',
+ 'tinyint' => 'smallint',
+ 'tinytext' => 'character varying',
+ 'mediumtext' => 'text',
+ 'largetext' => 'text',
+ );
+ }
+ else
+ {
+ $types = array(
+ 'character varying' => 'varchar',
+ 'character' => 'char',
+ 'integer' => 'int',
+ );
+ }
+
+ // Got it? Change it!
+ if (isset($types[$type_name]))
+ {
+ if ($type_name == 'tinytext')
+ $type_size = 255;
+ $type_name = $types[$type_name];
+ }
+ // Numbers don't have a size.
+ if (strpos($type_name, 'int') !== false)
+ $type_size = null;
+
+ return array($type_name, $type_size);
+}
+
+// Get table structure.
+function smf_db_table_structure($table_name, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ return array(
+ 'name' => $table_name,
+ 'columns' => $smcFunc['db_list_columns']($table_name, true),
+ 'indexes' => $smcFunc['db_list_indexes']($table_name, true),
+ );
+}
+
+// Return column information for a table.
+function smf_db_list_columns($table_name, $detail = false, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ $result = $smcFunc['db_query']('', '
+ SELECT column_name, column_default, is_nullable, data_type, character_maximum_length
+ FROM information_schema.columns
+ WHERE table_name = \'' . $table_name . '\'
+ ORDER BY ordinal_position',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $columns = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if (!$detail)
+ {
+ $columns[] = $row['column_name'];
+ }
+ else
+ {
+ $auto = false;
+ // What is the default?
+ if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0)
+ {
+ $default = null;
+ $auto = true;
+ }
+ elseif (trim($row['column_default']) != '')
+ $default = strpos($row['column_default'], '::') === false ? $row['column_default'] : substr($row['column_default'], 0, strpos($row['column_default'], '::'));
+ else
+ $default = null;
+
+ // Make the type generic.
+ list ($type, $size) = $smcFunc['db_calculate_type']($row['data_type'], $row['character_maximum_length'], true);
+
+ $columns[$row['column_name']] = array(
+ 'name' => $row['column_name'],
+ 'null' => $row['is_nullable'] ? true : false,
+ 'default' => $default,
+ 'type' => $type,
+ 'size' => $size,
+ 'auto' => $auto,
+ );
+ }
+ }
+ $smcFunc['db_free_result']($result);
+
+ return $columns;
+}
+
+// What about some index information?
+function smf_db_list_indexes($table_name, $detail = false, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ $result = $smcFunc['db_query']('', '
+ SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary,
+ CASE WHEN i.indisunique THEN 1 ELSE 0 END AS is_unique,
+ c2.relname AS name,
+ pg_get_indexdef(i.indexrelid) AS inddef
+ FROM pg_class AS c, pg_class AS c2, pg_index AS i
+ WHERE c.relname = \'' . $table_name . '\'
+ AND c.oid = i.indrelid
+ AND i.indexrelid = c2.oid',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $indexes = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ // Try get the columns that make it up.
+ if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0)
+ continue;
+
+ $columns = explode(',', $matches[1]);
+
+ if (empty($columns))
+ continue;
+
+ foreach ($columns as $k => $v)
+ $columns[$k] = trim($v);
+
+ // Fix up the name to be consistent cross databases
+ if (substr($row['name'], -5) == '_pkey' && $row['is_primary'] == 1)
+ $row['name'] = 'PRIMARY';
+ else
+ $row['name'] = str_replace($table_name . '_', '', $row['name']);
+
+ if (!$detail)
+ $indexes[] = $row['name'];
+ else
+ {
+ $indexes[$row['name']] = array(
+ 'name' => $row['name'],
+ 'type' => $row['is_primary'] ? 'primary' : ($row['is_unique'] ? 'unique' : 'index'),
+ 'columns' => $columns,
+ );
+ }
+ }
+ $smcFunc['db_free_result']($result);
+
+ return $indexes;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbPackages-sqlite.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbPackages-sqlite.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,736 @@
+ Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc. If not
+ set SMF will pick a size.
+ + 'default' = Default value - do not set if no default required.
+ + 'null' => Can it be null (true or false) - if not set default will be false.
+ + 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set
+ from what it should begin counting.
+ - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are:
+ + 'name' => Index name (If left empty SMF will generate).
+ + 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'.
+ + 'columns' => Array containing columns that form part of key - in the order the index is to be created.
+ - parameters: (None yet)
+ - if_exists values:
+ + 'ignore' will do nothing if the table exists. (And will return true)
+ + 'overwrite' will drop any existing table of the same name.
+ + 'error' will return false if the table already exists.
+
+*/
+
+// Add the file functions to the $smcFunc array.
+function db_packages_init()
+{
+ global $smcFunc, $reservedTables, $db_package_log, $db_prefix;
+
+ if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table')
+ {
+ $smcFunc += array(
+ 'db_add_column' => 'smf_db_add_column',
+ 'db_add_index' => 'smf_db_add_index',
+ 'db_alter_table' => 'smf_db_alter_table',
+ 'db_calculate_type' => 'smf_db_calculate_type',
+ 'db_change_column' => 'smf_db_change_column',
+ 'db_create_table' => 'smf_db_create_table',
+ 'db_drop_table' => 'smf_db_drop_table',
+ 'db_table_structure' => 'smf_db_table_structure',
+ 'db_list_columns' => 'smf_db_list_columns',
+ 'db_list_indexes' => 'smf_db_list_indexes',
+ 'db_remove_column' => 'smf_db_remove_column',
+ 'db_remove_index' => 'smf_db_remove_index',
+ );
+ $db_package_log = array();
+ }
+
+ // We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up!
+ $reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items',
+ 'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories', 'collapsed_categories',
+ 'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards',
+ 'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_karma', 'log_mark_read',
+ 'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments',
+ 'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects',
+ 'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons',
+ 'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages',
+ 'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys',
+ 'themes', 'topics');
+ foreach ($reservedTables as $k => $table_name)
+ $reservedTables[$k] = strtolower($db_prefix . $table_name);
+
+ // We in turn may need the extra stuff.
+ db_extend('extra');
+}
+
+// Create a table.
+function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal')
+{
+ global $reservedTables, $smcFunc, $db_package_log, $db_prefix;
+
+ // With or without the database name, the full name looks like this.
+ $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
+ $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // First - no way do we touch SMF tables.
+ // Commented out for now. We need to alter SMF tables in order to use this in the upgrade.
+/*
+ if (in_array(strtolower($table_name), $reservedTables))
+ return false;
+*/
+
+ // Log that we'll want to remove this on uninstall.
+ $db_package_log[] = array('remove_table', $table_name);
+
+ // Does this table exist or not?
+ $tables = $smcFunc['db_list_tables']();
+ if (in_array($full_table_name, $tables))
+ {
+ // This is a sad day... drop the table? If not, return false (error) by default.
+ if ($if_exists == 'overwrite')
+ $smcFunc['db_drop_table']($table_name);
+ else
+ return $if_exists == 'ignore';
+ }
+
+ // Righty - let's do the damn thing!
+ $table_query = 'CREATE TABLE ' . $table_name . "\n" . '(';
+ $done_primary = false;
+ foreach ($columns as $column)
+ {
+ // Auto increment is special
+ if (!empty($column['auto']))
+ {
+ $table_query .= "\n" . $column['name'] . ' integer PRIMARY KEY,';
+ $done_primary = true;
+ continue;
+ }
+ elseif (isset($column['default']) && $column['default'] !== null)
+ $default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\'';
+ else
+ $default = '';
+
+ // Sort out the size... and stuff...
+ $column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null;
+ list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']);
+ if ($size !== null)
+ $type = $type . '(' . $size . ')';
+
+ // Now just put it together!
+ $table_query .= "\n\t" . $column['name'] . ' ' . $type . ' ' . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default . ',';
+ }
+
+ // Loop through the indexes next...
+ $index_queries = array();
+ foreach ($indexes as $index)
+ {
+ $columns = implode(',', $index['columns']);
+
+ // Is it the primary?
+ if (isset($index['type']) && $index['type'] == 'primary')
+ {
+ // If we've done the primary via auto_inc, don't do it again!
+ if (!$done_primary)
+ $table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),';
+ }
+ else
+ {
+ if (empty($index['name']))
+ $index['name'] = implode('_', $index['columns']);
+ $index_queries[] = 'CREATE ' . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $table_name . '_' . $index['name'] . ' ON ' . $table_name . ' (' . $columns . ')';
+ }
+ }
+
+ // No trailing commas!
+ if (substr($table_query, -1) == ',')
+ $table_query = substr($table_query, 0, -1);
+
+ $table_query .= ')';
+
+ if (empty($parameters['skip_transaction']))
+ $smcFunc['db_transaction']('begin');
+
+ // Do the table and indexes...
+ $smcFunc['db_query']('', $table_query,
+ array(
+ 'security_override' => true,
+ )
+ );
+ foreach ($index_queries as $query)
+ $smcFunc['db_query']('', $query,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ if (empty($parameters['skip_transaction']))
+ $smcFunc['db_transaction']('commit');
+}
+
+// Drop a table.
+function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal')
+{
+ global $reservedTables, $smcFunc, $db_prefix;
+
+ // Strip out the table name, we might not need it in some cases
+ $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
+ $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // God no - dropping one of these = bad.
+ if (in_array(strtolower($table_name), $reservedTables))
+ return false;
+
+ // Does it exist?
+ if (in_array($full_table_name, $smcFunc['db_list_tables']()))
+ {
+ $query = 'DROP TABLE ' . $table_name;
+ $smcFunc['db_query']('', $query,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+
+ // Otherwise do 'nout.
+ return false;
+}
+
+// Add a column.
+function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
+{
+ global $smcFunc, $db_package_log, $txt, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Log that we will want to uninstall this!
+ $db_package_log[] = array('remove_column', $table_name, $column_info['name']);
+
+ // Does it exist - if so don't add it again!
+ $columns = $smcFunc['db_list_columns']($table_name, false);
+ foreach ($columns as $column)
+ if ($column == $column_info['name'])
+ {
+ // If we're going to overwrite then use change column.
+ if ($if_exists == 'update')
+ return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info);
+ else
+ return false;
+ }
+
+ // Alter the table to add the column.
+ if ($smcFunc['db_alter_table']($table_name, array('add' => array($column_info))) === false)
+ return false;
+
+ return true;
+}
+
+// We can't reliably do this on SQLite - damn!
+function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ if ($smcFunc['db_alter_table']($table_name, array('remove' => array(array('name' => $column_name)))))
+ return true;
+ else
+ return false;
+}
+
+// Change a column.
+function smf_db_change_column($table_name, $old_column, $column_info, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ if ($smcFunc['db_alter_table']($table_name, array('change' => array(array('name' => $old_column) + $column_info))))
+ return true;
+ else
+ return false;
+}
+
+// Add an index.
+function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
+{
+ global $smcFunc, $db_package_log, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // No columns = no index.
+ if (empty($index_info['columns']))
+ return false;
+ $columns = implode(',', $index_info['columns']);
+
+ // No name - make it up!
+ if (empty($index_info['name']))
+ {
+ // No need for primary.
+ if (isset($index_info['type']) && $index_info['type'] == 'primary')
+ $index_info['name'] = '';
+ else
+ $index_info['name'] = implode('_', $index_info['columns']);
+ }
+ else
+ $index_info['name'] = $index_info['name'];
+
+ // Log that we are going to want to remove this!
+ $db_package_log[] = array('remove_index', $table_name, $index_info['name']);
+
+ // Let's get all our indexes.
+ $indexes = $smcFunc['db_list_indexes']($table_name, true);
+ // Do we already have it?
+ foreach ($indexes as $index)
+ {
+ if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary'))
+ {
+ // If we want to overwrite simply remove the current one then continue.
+ if ($if_exists != 'update' || $index['type'] == 'primary')
+ return false;
+ else
+ $smcFunc['db_remove_index']($table_name, $index_info['name']);
+ }
+ }
+
+ // If we're here we know we don't have the index - so just add it.
+ if (!empty($index_info['type']) && $index_info['type'] == 'primary')
+ {
+ //!!! Doesn't work with PRIMARY KEY yet.
+ }
+ else
+ {
+ $smcFunc['db_query']('', '
+ CREATE ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $index_info['name'] . ' ON ' . $table_name . ' (' . $columns . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ }
+}
+
+// Remove an index.
+function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal')
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Better exist!
+ $indexes = $smcFunc['db_list_indexes']($table_name, true);
+
+ foreach ($indexes as $index)
+ {
+ //!!! Doesn't do primary key at the moment!
+ if ($index['type'] != 'primary' && $index['name'] == $index_name)
+ {
+ // Drop the bugger...
+ $smcFunc['db_query']('', '
+ DROP INDEX ' . $index_name,
+ array(
+ 'security_override' => true,
+ )
+ );
+
+ return true;
+ }
+ }
+
+ // Not to be found ;(
+ return false;
+}
+
+// Get the schema formatted name for a type.
+function smf_db_calculate_type($type_name, $type_size = null, $reverse = false)
+{
+ // Generic => Specific.
+ if (!$reverse)
+ {
+ $types = array(
+ 'mediumint' => 'int',
+ 'tinyint' => 'smallint',
+ 'mediumtext' => 'text',
+ 'largetext' => 'text',
+ );
+ }
+ else
+ {
+ $types = array(
+ 'integer' => 'int',
+ );
+ }
+
+ // Got it? Change it!
+ if (isset($types[$type_name]))
+ {
+ if ($type_name == 'tinytext')
+ $type_size = 255;
+ $type_name = $types[$type_name];
+ }
+ // Numbers don't have a size.
+ if (strpos($type_name, 'int') !== false)
+ $type_size = null;
+
+ return array($type_name, $type_size);
+}
+
+// Get table structure.
+function smf_db_table_structure($table_name, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ return array(
+ 'name' => $table_name,
+ 'columns' => $smcFunc['db_list_columns']($table_name, true),
+ 'indexes' => $smcFunc['db_list_indexes']($table_name, true),
+ );
+}
+
+// Harder than it should be on sqlite!
+function smf_db_list_columns($table_name, $detail = false, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ $result = $smcFunc['db_query']('', '
+ PRAGMA table_info(' . $table_name . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $columns = array();
+
+ $primaries = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if (!$detail)
+ {
+ $columns[] = $row['name'];
+ }
+ else
+ {
+ // Auto increment is hard to tell really... if there's only one primary it probably is.
+ if ($row['pk'])
+ $primaries[] = $row['name'];
+
+ // Can we split out the size?
+ if (preg_match('~(.+?)\s*\((\d+)\)~i', $row['type'], $matches))
+ {
+ $type = $matches[1];
+ $size = $matches[2];
+ }
+ else
+ {
+ $type = $row['type'];
+ $size = null;
+ }
+
+ $columns[$row['name']] = array(
+ 'name' => $row['name'],
+ 'null' => $row['notnull'] ? false : true,
+ 'default' => $row['dflt_value'],
+ 'type' => $type,
+ 'size' => $size,
+ 'auto' => false,
+ );
+ }
+ }
+ $smcFunc['db_free_result']($result);
+
+ // Put in our guess at auto_inc.
+ if (count($primaries) == 1)
+ $columns[$primaries[0]]['auto'] = true;
+
+ return $columns;
+}
+
+// What about some index information?
+function smf_db_list_indexes($table_name, $detail = false, $parameters = array())
+{
+ global $smcFunc, $db_prefix;
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ $result = $smcFunc['db_query']('', '
+ PRAGMA index_list(' . $table_name . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ $indexes = array();
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if (!$detail)
+ $indexes[] = $row['name'];
+ else
+ {
+ $result2 = $smcFunc['db_query']('', '
+ PRAGMA index_info(' . $row['name'] . ')',
+ array(
+ 'security_override' => true,
+ )
+ );
+ while ($row2 = $smcFunc['db_fetch_assoc']($result2))
+ {
+ // What is the type?
+ if ($row['unique'])
+ $type = 'unique';
+ else
+ $type = 'index';
+
+ // This is the first column we've seen?
+ if (empty($indexes[$row['name']]))
+ {
+ $indexes[$row['name']] = array(
+ 'name' => $row['name'],
+ 'type' => $type,
+ 'columns' => array(),
+ );
+ }
+
+ // Add the column...
+ $indexes[$row['name']]['columns'][] = $row2['name'];
+ }
+ $smcFunc['db_free_result']($result2);
+ }
+ }
+ $smcFunc['db_free_result']($result);
+
+ return $indexes;
+}
+
+function smf_db_alter_table($table_name, $columns)
+{
+ global $smcFunc, $db_prefix, $db_name, $boarddir;
+
+ $db_file = substr($db_name, -3) === '.db' ? $db_name : $db_name . '.db';
+
+ $table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
+
+ // Let's get the current columns for the table.
+ $current_columns = $smcFunc['db_list_columns']($table_name, true);
+
+ // Let's get a list of columns for the temp table.
+ $temp_table_columns = array();
+
+ // Let's see if we have columns to remove or columns that are being added that already exist.
+ foreach ($current_columns as $key => $column)
+ {
+ $exists = false;
+ if (isset($columns['remove']))
+ foreach ($columns['remove'] as $drop)
+ if ($drop['name'] == $column['name'])
+ {
+ $exists = true;
+ break;
+ }
+
+ if (isset($columns['add']))
+ foreach ($columns['add'] as $key2 => $add)
+ if ($add['name'] == $column['name'])
+ {
+ unset($columns['add'][$key2]);
+ break;
+ }
+
+ // Doesn't exist then we 'remove'.
+ if (!$exists)
+ $temp_table_columns[] = $column['name'];
+ }
+
+ // If they are equal then that means that the column that we are adding exists or it doesn't exist and we are not looking to change any one of them.
+ if (count($temp_table_columns) == count($current_columns) && empty($columns['change']) && empty($columns['add']))
+ return true;
+
+ // Drop the temp table.
+ $smcFunc['db_query']('', '
+ DROP TABLE {raw:temp_table_name}',
+ array(
+ 'temp_table_name' => $table_name . '_tmp',
+ 'db_error_skip' => true,
+ )
+ );
+
+ // Let's make a backup of the current database.
+ // We only want the first backup of a table modification. So if there is a backup file and older than an hour just delete and back up again
+ $db_backup_file = $boarddir . '/Packages/backups/backup_' . $table_name . '_' . basename($db_file) . md5($table_name . $db_file);
+ if (file_exists($db_backup_file) && time() - filemtime($db_backup_file) > 3600)
+ {
+ @unlink($db_backup_file);
+ @copy($db_file, $db_backup_file);
+ }
+ elseif (!file_exists($db_backup_file))
+ @copy($db_file, $db_backup_file);
+
+ // If we don't have temp tables then everything crapped out. Just exit.
+ if (empty($temp_table_columns))
+ return false;
+
+ // Start
+ $smcFunc['db_transaction']('begin');
+
+ // Let's create the temporary table.
+ $createTempTable = $smcFunc['db_query']('', '
+ CREATE TEMPORARY TABLE {raw:temp_table_name}
+ (
+ {raw:columns}
+ );',
+ array(
+ 'temp_table_name' => $table_name . '_tmp',
+ 'columns' => implode(', ', $temp_table_columns),
+ 'db_error_skip' => true,
+ )
+ ) !== false;
+
+ if (!$createTempTable)
+ return false;
+
+ // Insert into temp table.
+ $smcFunc['db_query']('', '
+ INSERT INTO {raw:temp_table_name}
+ ({raw:columns})
+ SELECT {raw:columns}
+ FROM {raw:table_name}',
+ array(
+ 'table_name' => $table_name,
+ 'columns' => implode(', ', $temp_table_columns),
+ 'temp_table_name' => $table_name . '_tmp',
+ )
+ );
+
+ // Drop the current table.
+ $dropTable = $smcFunc['db_query']('', '
+ DROP TABLE {raw:table_name}',
+ array(
+ 'table_name' => $table_name,
+ 'db_error_skip' => true,
+ )
+ ) !== false;
+
+ // If you can't drop the main table then there is no where to go from here. Just return.
+ if (!$dropTable)
+ return false;
+
+ // We need to keep track of the structure for the current columns and the new columns.
+ $new_columns = array();
+ $column_names = array();
+
+ // Let's get the ones that we already have first.
+ foreach ($current_columns as $name => $column)
+ {
+ if (in_array($name, $temp_table_columns))
+ {
+ $new_columns[$name] = array(
+ 'name' => $name,
+ 'type' => $column['type'],
+ 'size' => isset($column['size']) ? (int) $column['size'] : null,
+ 'null' => !empty($column['null']),
+ 'auto' => isset($column['auto']) ? $column['auto'] : false,
+ 'default' => isset($column['default']) ? $column['default'] : '',
+ );
+
+ // Lets keep track of the name for the column.
+ $column_names[$name] = $name;
+ }
+ }
+
+ // Now the new.
+ if (!empty($columns['add']))
+ foreach ($columns['add'] as $add)
+ {
+ $new_columns[$add['name']] = array(
+ 'name' => $add['name'],
+ 'type' => $add['type'],
+ 'size' => isset($add['size']) ? (int) $add['size'] : null,
+ 'null' => !empty($add['null']),
+ 'auto' => isset($add['auto']) ? $add['auto'] : false,
+ 'default' => isset($add['default']) ? $add['default'] : '',
+ );
+
+ // Let's keep track of the name for the column.
+ $column_names[$add['name']] = strstr('int', $add['type']) ? ' 0 AS ' . $add['name'] : ' {string:empty_string} AS ' . $add['name'];
+ }
+
+ // Now to change a column. Not drop but change it.
+ if (isset($columns['change']))
+ foreach ($columns['change'] as $change)
+ if (isset($new_columns[$change['name']]))
+ $new_columns[$change['name']] = array(
+ 'name' => $change['name'],
+ 'type' => $change['type'],
+ 'size' => isset($change['size']) ? (int) $change['size'] : null,
+ 'null' => !empty($change['null']),
+ 'auto' => isset($change['auto']) ? $change['auto'] : false,
+ 'default' => isset($change['default']) ? $change['default'] : '',
+ );
+
+ // Now let's create the table.
+ $createTable = $smcFunc['db_create_table']($table_name, $new_columns, array(), array('skip_transaction' => true));
+
+ // Did it create correctly?
+ if ($createTable === false)
+ return false;
+
+ // Back to it's original table.
+ $insertData = $smcFunc['db_query']('', '
+ INSERT INTO {raw:table_name}
+ ({raw:columns})
+ SELECT ' . implode(', ', $column_names) . '
+ FROM {raw:temp_table_name}',
+ array(
+ 'table_name' => $table_name,
+ 'columns' => implode(', ', array_keys($new_columns)),
+ 'columns_select' => implode(', ', $column_names),
+ 'temp_table_name' => $table_name . '_tmp',
+ 'empty_string' => '',
+ )
+ );
+
+ // Did everything insert correctly?
+ if (!$insertData)
+ return false;
+
+ // Drop the temp table.
+ $smcFunc['db_query']('', '
+ DROP TABLE {raw:temp_table_name}',
+ array(
+ 'temp_table_name' => $table_name . '_tmp',
+ 'db_error_skip' => true,
+ )
+ );
+
+ // Commit or else there is no point in doing the previous steps.
+ $smcFunc['db_transaction']('commit');
+
+ // We got here so we're good. The temp table should be deleted, if not it will be gone later on >:D.
+ return true;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbSearch-mysql.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbSearch-mysql.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,77 @@
+ 'smf_db_query',
+ 'db_search_support' => 'smf_db_search_support',
+ 'db_create_word_search' => 'smf_db_create_word_search',
+ 'db_support_ignore' => true,
+ );
+}
+
+// Does this database type support this search type?
+function smf_db_search_support($search_type)
+{
+ $supported_types = array('fulltext');
+
+ return in_array($search_type, $supported_types);
+}
+
+// Highly specific - create the custom word index table!
+function smf_db_create_word_search($size)
+{
+ global $smcFunc;
+
+ if ($size == 'small')
+ $size = 'smallint(5)';
+ elseif ($size == 'medium')
+ $size = 'mediumint(8)';
+ else
+ $size = 'int(10)';
+
+ $smcFunc['db_query']('', '
+ CREATE TABLE {db_prefix}log_search_words (
+ id_word {raw:size} unsigned NOT NULL default {string:string_zero},
+ id_msg int(10) unsigned NOT NULL default {string:string_zero},
+ PRIMARY KEY (id_word, id_msg)
+ ) ENGINE=InnoDB',
+ array(
+ 'string_zero' => '0',
+ 'size' => $size,
+ )
+ );
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbSearch-postgresql.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbSearch-postgresql.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,122 @@
+ 'smf_db_search_query',
+ 'db_search_support' => 'smf_db_search_support',
+ 'db_create_word_search' => 'smf_db_create_word_search',
+ 'db_support_ignore' => false,
+ );
+}
+
+// Does this database type support this search type?
+function smf_db_search_support($search_type)
+{
+ $supported_types = array('custom');
+
+ return in_array($search_type, $supported_types);
+}
+
+// Returns the correct query for this search type.
+function smf_db_search_query($identifier, $db_string, $db_values = array(), $connection = null)
+{
+ global $smcFunc;
+
+ $replacements = array(
+ 'create_tmp_log_search_topics' => array(
+ '~mediumint\(\d\)~i' => 'int',
+ '~unsigned~i' => '',
+ '~TYPE=HEAP~i' => '',
+ ),
+ 'create_tmp_log_search_messages' => array(
+ '~mediumint\(\d\)' => 'int',
+ '~unsigned~i' => '',
+ '~TYPE=HEAP~i' => '',
+ ),
+ 'drop_tmp_log_search_topics' => array(
+ '~IF\sEXISTS~i' => '',
+ ),
+ 'drop_tmp_log_search_messages' => array(
+ '~IF\sEXISTS~i' => '',
+ ),
+ 'insert_into_log_messages_fulltext' => array(
+ '~NOT\sRLIKE~i' => '!~*',
+ '~RLIKE~i' => '~*',
+ ),
+ 'insert_log_search_results_subject' => array(
+ '~NOT\sRLIKE~i' => '!~*',
+ '~RLIKE~i' => '~*',
+ ),
+ );
+
+ if (isset($replacements[$identifier]))
+ $db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
+ elseif (preg_match('~^\s*INSERT\sIGNORE~i', $db_string) != 0)
+ {
+ $db_string = preg_replace('~^\s*INSERT\sIGNORE~i', 'INSERT', $db_string);
+ // Don't error on multi-insert.
+ $db_values['db_error_skip'] = true;
+ }
+
+ $return = $smcFunc['db_query']('', $db_string,
+ $db_values, $connection
+ );
+
+ return $return;
+}
+
+// Highly specific - create the custom word index table!
+function smf_db_create_word_search($size)
+{
+ global $smcFunc;
+
+ $size = 'int';
+
+ $smcFunc['db_query']('', '
+ CREATE TABLE {db_prefix}log_search_words (
+ id_word {raw:size} NOT NULL default {string:string_zero},
+ id_msg int NOT NULL default {string:string_zero},
+ PRIMARY KEY (id_word, id_msg)
+ )',
+ array(
+ 'size' => $size,
+ 'string_zero' => '0',
+ )
+ );
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DbSearch-sqlite.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DbSearch-sqlite.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,106 @@
+ 'smf_db_search_query',
+ 'db_search_support' => 'smf_db_search_support',
+ 'db_create_word_search' => 'smf_db_create_word_search',
+ 'db_support_ignore' => false,
+ );
+}
+
+// Does this database type support this search type?
+function smf_db_search_support($search_type)
+{
+ $supported_types = array('custom');
+
+ return in_array($search_type, $supported_types);
+}
+
+// Returns the correct query for this search type.
+function smf_db_search_query($identifier, $db_string, $db_values = array(), $connection = null)
+{
+ global $smcFunc;
+
+ $replacements = array(
+ 'create_tmp_log_search_topics' => array(
+ '~mediumint\(\d\)~i' => 'int',
+ '~TYPE=HEAP~i' => '',
+ ),
+ 'create_tmp_log_search_messages' => array(
+ '~mediumint\(\d\)~i' => 'int',
+ '~TYPE=HEAP~i' => '',
+ ),
+ );
+
+ if (isset($replacements[$identifier]))
+ $db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
+ elseif (preg_match('~^\s*INSERT\sIGNORE~i', $db_string) != 0)
+ {
+ $db_string = preg_replace('~^\s*INSERT\sIGNORE~i', 'INSERT', $db_string);
+ // Don't error on multi-insert.
+ $db_values['db_error_skip'] = true;
+ }
+
+ $return = $smcFunc['db_query']('', $db_string,
+ $db_values, $connection
+ );
+
+ return $return;
+}
+
+// Highly specific - create the custom word index table!
+function smf_db_create_word_search($size)
+{
+ global $smcFunc;
+
+ $size = 'int';
+
+ $smcFunc['db_query']('', '
+ CREATE TABLE {db_prefix}log_search_words (
+ id_word {raw:size} NOT NULL default {string:string_zero},
+ id_msg int(10) NOT NULL default {string:string_zero},
+ PRIMARY KEY (id_word, id_msg)
+ )',
+ array(
+ 'size' => $size,
+ 'string_zero' => '0',
+ )
+ );
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Display.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Display.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,1727 @@
+ 2)
+ foreach ($_GET as $k => $v)
+ {
+ if (!in_array($k, array('topic', 'board', 'start', session_name())))
+ $context['robot_no_index'] = true;
+ }
+
+ if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0))
+ $context['robot_no_index'] = true;
+
+ // Find the previous or next topic. Make a fuss if there are no more.
+ if (isset($_REQUEST['prev_next']) && ($_REQUEST['prev_next'] == 'prev' || $_REQUEST['prev_next'] == 'next'))
+ {
+ // No use in calculating the next topic if there's only one.
+ if ($board_info['num_topics'] > 1)
+ {
+ // Just prepare some variables that are used in the query.
+ $gt_lt = $_REQUEST['prev_next'] == 'prev' ? '>' : '<';
+ $order = $_REQUEST['prev_next'] == 'prev' ? '' : ' DESC';
+
+ $request = $smcFunc['db_query']('', '
+ SELECT t2.id_topic
+ FROM {db_prefix}topics AS t
+ INNER JOIN {db_prefix}topics AS t2 ON (' . (empty($modSettings['enableStickyTopics']) ? '
+ t2.id_last_msg ' . $gt_lt . ' t.id_last_msg' : '
+ (t2.id_last_msg ' . $gt_lt . ' t.id_last_msg AND t2.is_sticky ' . $gt_lt . '= t.is_sticky) OR t2.is_sticky ' . $gt_lt . ' t.is_sticky') . ')
+ WHERE t.id_topic = {int:current_topic}
+ AND t2.id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
+ AND (t2.approved = {int:is_approved} OR (t2.id_member_started != {int:id_member_started} AND t2.id_member_started = {int:current_member}))') . '
+ ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' t2.is_sticky' . $order . ',') . ' t2.id_last_msg' . $order . '
+ LIMIT 1',
+ array(
+ 'current_board' => $board,
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ 'is_approved' => 1,
+ 'id_member_started' => 0,
+ )
+ );
+
+ // No more left.
+ if ($smcFunc['db_num_rows']($request) == 0)
+ {
+ $smcFunc['db_free_result']($request);
+
+ // Roll over - if we're going prev, get the last - otherwise the first.
+ $request = $smcFunc['db_query']('', '
+ SELECT id_topic
+ FROM {db_prefix}topics
+ WHERE id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
+ AND (approved = {int:is_approved} OR (id_member_started != {int:id_member_started} AND id_member_started = {int:current_member}))') . '
+ ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' is_sticky' . $order . ',') . ' id_last_msg' . $order . '
+ LIMIT 1',
+ array(
+ 'current_board' => $board,
+ 'current_member' => $user_info['id'],
+ 'is_approved' => 1,
+ 'id_member_started' => 0,
+ )
+ );
+ }
+
+ // Now you can be sure $topic is the id_topic to view.
+ list ($topic) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ $context['current_topic'] = $topic;
+ }
+
+ // Go to the newest message on this topic.
+ $_REQUEST['start'] = 'new';
+ }
+
+ // Add 1 to the number of views of this topic.
+ if (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic)
+ {
+ $smcFunc['db_query']('', '
+ UPDATE {db_prefix}topics
+ SET num_views = num_views + 1
+ WHERE id_topic = {int:current_topic}',
+ array(
+ 'current_topic' => $topic,
+ )
+ );
+
+ $_SESSION['last_read_topic'] = $topic;
+ }
+
+ // Get all the important topic info.
+ $request = $smcFunc['db_query']('', '
+ SELECT
+ t.num_replies, t.num_views, t.locked, ms.subject, t.is_sticky, t.id_poll,
+ t.id_member_started, t.id_first_msg, t.id_last_msg, t.approved, t.unapproved_posts,
+ ' . ($user_info['is_guest'] ? 't.id_last_msg + 1' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from
+ ' . (!empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board ? ', id_previous_board, id_previous_topic' : '') . '
+ FROM {db_prefix}topics AS t
+ INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)' . ($user_info['is_guest'] ? '' : '
+ LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
+ LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})') . '
+ WHERE t.id_topic = {int:current_topic}
+ LIMIT 1',
+ array(
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ 'current_board' => $board,
+ )
+ );
+ if ($smcFunc['db_num_rows']($request) == 0)
+ fatal_lang_error('not_a_topic', false);
+ $topicinfo = $smcFunc['db_fetch_assoc']($request);
+ $smcFunc['db_free_result']($request);
+
+ $context['real_num_replies'] = $context['num_replies'] = $topicinfo['num_replies'];
+ $context['topic_first_message'] = $topicinfo['id_first_msg'];
+ $context['topic_last_message'] = $topicinfo['id_last_msg'];
+
+ // Add up unapproved replies to get real number of replies...
+ if ($modSettings['postmod_active'] && allowedTo('approve_posts'))
+ $context['real_num_replies'] += $topicinfo['unapproved_posts'] - ($topicinfo['approved'] ? 0 : 1);
+
+ // If this topic has unapproved posts, we need to work out how many posts the user can see, for page indexing.
+ if ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !$user_info['is_guest'] && !allowedTo('approve_posts'))
+ {
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(id_member) AS my_unapproved_posts
+ FROM {db_prefix}messages
+ WHERE id_topic = {int:current_topic}
+ AND id_member = {int:current_member}
+ AND approved = 0',
+ array(
+ 'current_topic' => $topic,
+ 'current_member' => $user_info['id'],
+ )
+ );
+ list ($myUnapprovedPosts) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ $context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($topicinfo['approved'] ? 1 : 0);
+ }
+ else
+ $context['total_visible_posts'] = $context['num_replies'] + $topicinfo['unapproved_posts'] + ($topicinfo['approved'] ? 1 : 0);
+
+ // When was the last time this topic was replied to? Should we warn them about it?
+ $request = $smcFunc['db_query']('', '
+ SELECT poster_time
+ FROM {db_prefix}messages
+ WHERE id_msg = {int:id_last_msg}
+ LIMIT 1',
+ array(
+ 'id_last_msg' => $topicinfo['id_last_msg'],
+ )
+ );
+
+ list ($lastPostTime) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ $context['oldTopicError'] = !empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky);
+
+ // The start isn't a number; it's information about what to do, where to go.
+ if (!is_numeric($_REQUEST['start']))
+ {
+ // Redirect to the page and post with new messages, originally by Omar Bazavilvazo.
+ if ($_REQUEST['start'] == 'new')
+ {
+ // Guests automatically go to the last post.
+ if ($user_info['is_guest'])
+ {
+ $context['start_from'] = $context['total_visible_posts'] - 1;
+ $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : 0;
+ }
+ else
+ {
+ // Find the earliest unread message in the topic. (the use of topics here is just for both tables.)
+ $request = $smcFunc['db_query']('', '
+ SELECT IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from
+ FROM {db_prefix}topics AS t
+ LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
+ LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})
+ WHERE t.id_topic = {int:current_topic}
+ LIMIT 1',
+ array(
+ 'current_board' => $board,
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ )
+ );
+ list ($new_from) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ // Fall through to the next if statement.
+ $_REQUEST['start'] = 'msg' . $new_from;
+ }
+ }
+
+ // Start from a certain time index, not a message.
+ if (substr($_REQUEST['start'], 0, 4) == 'from')
+ {
+ $timestamp = (int) substr($_REQUEST['start'], 4);
+ if ($timestamp === 0)
+ $_REQUEST['start'] = 0;
+ else
+ {
+ // Find the number of messages posted before said time...
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(*)
+ FROM {db_prefix}messages
+ WHERE poster_time < {int:timestamp}
+ AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? '
+ AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''),
+ array(
+ 'current_topic' => $topic,
+ 'current_member' => $user_info['id'],
+ 'is_approved' => 1,
+ 'timestamp' => $timestamp,
+ )
+ );
+ list ($context['start_from']) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ // Handle view_newest_first options, and get the correct start value.
+ $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1;
+ }
+ }
+
+ // Link to a message...
+ elseif (substr($_REQUEST['start'], 0, 3) == 'msg')
+ {
+ $virtual_msg = (int) substr($_REQUEST['start'], 3);
+ if (!$topicinfo['unapproved_posts'] && $virtual_msg >= $topicinfo['id_last_msg'])
+ $context['start_from'] = $context['total_visible_posts'] - 1;
+ elseif (!$topicinfo['unapproved_posts'] && $virtual_msg <= $topicinfo['id_first_msg'])
+ $context['start_from'] = 0;
+ else
+ {
+ // Find the start value for that message......
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(*)
+ FROM {db_prefix}messages
+ WHERE id_msg < {int:virtual_msg}
+ AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? '
+ AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''),
+ array(
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ 'virtual_msg' => $virtual_msg,
+ 'is_approved' => 1,
+ 'no_member' => 0,
+ )
+ );
+ list ($context['start_from']) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+ }
+
+ // We need to reverse the start as well in this case.
+ $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1;
+ }
+ }
+
+ // Create a previous next string if the selected theme has it as a selected option.
+ $context['previous_next'] = $modSettings['enablePreviousNext'] ? '' . $txt['previous_next_back'] . '' . $txt['previous_next_forward'] . '' : '';
+
+ // Check if spellchecking is both enabled and actually working. (for quick reply.)
+ $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
+
+ // Do we need to show the visual verification image?
+ $context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || ($user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1));
+ if ($context['require_verification'])
+ {
+ require_once($sourcedir . '/Subs-Editor.php');
+ $verificationOptions = array(
+ 'id' => 'post',
+ );
+ $context['require_verification'] = create_control_verification($verificationOptions);
+ $context['visual_verification_id'] = $verificationOptions['id'];
+ }
+
+ // Are we showing signatures - or disabled fields?
+ $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
+ $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
+
+ // Censor the title...
+ censorText($topicinfo['subject']);
+ $context['page_title'] = $topicinfo['subject'];
+
+ // Is this topic sticky, or can it even be?
+ $topicinfo['is_sticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['is_sticky'];
+
+ // Default this topic to not marked for notifications... of course...
+ $context['is_marked_notify'] = false;
+
+ // Did we report a post to a moderator just now?
+ $context['report_sent'] = isset($_GET['reportsent']);
+
+ // Let's get nosey, who is viewing this topic?
+ if (!empty($settings['display_who_viewing']))
+ {
+ // Start out with no one at all viewing it.
+ $context['view_members'] = array();
+ $context['view_members_list'] = array();
+ $context['view_num_hidden'] = 0;
+
+ // Search for members who have this topic set in their GET data.
+ $request = $smcFunc['db_query']('', '
+ SELECT
+ lo.id_member, lo.log_time, mem.real_name, mem.member_name, mem.show_online,
+ mg.online_color, mg.id_group, mg.group_name
+ FROM {db_prefix}log_online AS lo
+ LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lo.id_member)
+ LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_id_group} THEN mem.id_post_group ELSE mem.id_group END)
+ WHERE INSTR(lo.url, {string:in_url_string}) > 0 OR lo.session = {string:session}',
+ array(
+ 'reg_id_group' => 0,
+ 'in_url_string' => 's:5:"topic";i:' . $topic . ';',
+ 'session' => $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id(),
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ if (empty($row['id_member']))
+ continue;
+
+ if (!empty($row['online_color']))
+ $link = '' . $row['real_name'] . '';
+ else
+ $link = '' . $row['real_name'] . '';
+
+ $is_buddy = in_array($row['id_member'], $user_info['buddies']);
+ if ($is_buddy)
+ $link = '' . $link . '';
+
+ // Add them both to the list and to the more detailed list.
+ if (!empty($row['show_online']) || allowedTo('moderate_forum'))
+ $context['view_members_list'][$row['log_time'] . $row['member_name']] = empty($row['show_online']) ? '' . $link . '' : $link;
+ $context['view_members'][$row['log_time'] . $row['member_name']] = array(
+ 'id' => $row['id_member'],
+ 'username' => $row['member_name'],
+ 'name' => $row['real_name'],
+ 'group' => $row['id_group'],
+ 'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
+ 'link' => $link,
+ 'is_buddy' => $is_buddy,
+ 'hidden' => empty($row['show_online']),
+ );
+
+ if (empty($row['show_online']))
+ $context['view_num_hidden']++;
+ }
+
+ // The number of guests is equal to the rows minus the ones we actually used ;).
+ $context['view_num_guests'] = $smcFunc['db_num_rows']($request) - count($context['view_members']);
+ $smcFunc['db_free_result']($request);
+
+ // Sort the list.
+ krsort($context['view_members']);
+ krsort($context['view_members_list']);
+ }
+
+ // If all is set, but not allowed... just unset it.
+ $can_show_all = !empty($modSettings['enableAllMessages']) && $context['total_visible_posts'] > $context['messages_per_page'] && $context['total_visible_posts'] < $modSettings['enableAllMessages'];
+ if (isset($_REQUEST['all']) && !$can_show_all)
+ unset($_REQUEST['all']);
+ // Otherwise, it must be allowed... so pretend start was -1.
+ elseif (isset($_REQUEST['all']))
+ $_REQUEST['start'] = -1;
+
+ // Construct the page index, allowing for the .START method...
+ $context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic . '.%1$d', $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], true);
+ $context['start'] = $_REQUEST['start'];
+
+ // This is information about which page is current, and which page we're on - in case you don't like the constructed page index. (again, wireles..)
+ $context['page_info'] = array(
+ 'current_page' => $_REQUEST['start'] / $context['messages_per_page'] + 1,
+ 'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1,
+ );
+
+ // Figure out all the link to the next/prev/first/last/etc. for wireless mainly.
+ $context['links'] = array(
+ 'first' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.0' : '',
+ 'prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '',
+ 'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic. '.' . ($_REQUEST['start'] + $context['messages_per_page']) : '',
+ 'last' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic. '.' . (floor($context['total_visible_posts'] / $context['messages_per_page']) * $context['messages_per_page']) : '',
+ 'up' => $scripturl . '?board=' . $board . '.0'
+ );
+
+ // If they are viewing all the posts, show all the posts, otherwise limit the number.
+ if ($can_show_all)
+ {
+ if (isset($_REQUEST['all']))
+ {
+ // No limit! (actually, there is a limit, but...)
+ $context['messages_per_page'] = -1;
+ $context['page_index'] .= empty($modSettings['compactTopicPagesEnable']) ? '' . $txt['all'] . ' ' : '[' . $txt['all'] . '] ';
+
+ // Set start back to 0...
+ $_REQUEST['start'] = 0;
+ }
+ // They aren't using it, but the *option* is there, at least.
+ else
+ $context['page_index'] .= ' ' . $txt['all'] . ' ';
+ }
+
+ // Build the link tree.
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?topic=' . $topic . '.0',
+ 'name' => $topicinfo['subject'],
+ 'extra_before' => $settings['linktree_inline'] ? $txt['topic'] . ': ' : ''
+ );
+
+ // Build a list of this board's moderators.
+ $context['moderators'] = &$board_info['moderators'];
+ $context['link_moderators'] = array();
+ if (!empty($board_info['moderators']))
+ {
+ // Add a link for each moderator...
+ foreach ($board_info['moderators'] as $mod)
+ $context['link_moderators'][] = '' . $mod['name'] . '';
+
+ // And show it after the board's name.
+ $context['linktree'][count($context['linktree']) - 2]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')';
+ }
+
+ // Information about the current topic...
+ $context['is_locked'] = $topicinfo['locked'];
+ $context['is_sticky'] = $topicinfo['is_sticky'];
+ $context['is_very_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicVeryPosts'];
+ $context['is_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicPosts'];
+ $context['is_approved'] = $topicinfo['approved'];
+
+ // We don't want to show the poll icon in the topic class here, so pretend it's not one.
+ $context['is_poll'] = false;
+ determineTopicClass($context);
+
+ $context['is_poll'] = $topicinfo['id_poll'] > 0 && $modSettings['pollMode'] == '1' && allowedTo('poll_view');
+
+ // Did this user start the topic or not?
+ $context['user']['started'] = $user_info['id'] == $topicinfo['id_member_started'] && !$user_info['is_guest'];
+ $context['topic_starter_id'] = $topicinfo['id_member_started'];
+
+ // Set the topic's information for the template.
+ $context['subject'] = $topicinfo['subject'];
+ $context['num_views'] = $topicinfo['num_views'];
+ $context['mark_unread_time'] = $topicinfo['new_from'];
+
+ // Set a canonical URL for this page.
+ $context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $context['start'];
+
+ // For quick reply we need a response prefix in the default forum language.
+ if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix', 600)))
+ {
+ if ($language === $user_info['language'])
+ $context['response_prefix'] = $txt['response_prefix'];
+ else
+ {
+ loadLanguage('index', $language, false);
+ $context['response_prefix'] = $txt['response_prefix'];
+ loadLanguage('index');
+ }
+ cache_put_data('response_prefix', $context['response_prefix'], 600);
+ }
+
+ // If we want to show event information in the topic, prepare the data.
+ if (allowedTo('calendar_view') && !empty($modSettings['cal_showInTopic']) && !empty($modSettings['cal_enabled']))
+ {
+ // First, try create a better time format, ignoring the "time" elements.
+ if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
+ $date_string = $user_info['time_format'];
+ else
+ $date_string = $matches[0];
+
+ // Any calendar information for this topic?
+ $request = $smcFunc['db_query']('', '
+ SELECT cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, mem.real_name
+ FROM {db_prefix}calendar AS cal
+ LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = cal.id_member)
+ WHERE cal.id_topic = {int:current_topic}
+ ORDER BY start_date',
+ array(
+ 'current_topic' => $topic,
+ )
+ );
+ $context['linked_calendar_events'] = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ // Prepare the dates for being formatted.
+ $start_date = sscanf($row['start_date'], '%04d-%02d-%02d');
+ $start_date = mktime(12, 0, 0, $start_date[1], $start_date[2], $start_date[0]);
+ $end_date = sscanf($row['end_date'], '%04d-%02d-%02d');
+ $end_date = mktime(12, 0, 0, $end_date[1], $end_date[2], $end_date[0]);
+
+ $context['linked_calendar_events'][] = array(
+ 'id' => $row['id_event'],
+ 'title' => $row['title'],
+ 'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
+ 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
+ 'start_date' => timeformat($start_date, $date_string, 'none'),
+ 'start_timestamp' => $start_date,
+ 'end_date' => timeformat($end_date, $date_string, 'none'),
+ 'end_timestamp' => $end_date,
+ 'is_last' => false
+ );
+ }
+ $smcFunc['db_free_result']($request);
+
+ if (!empty($context['linked_calendar_events']))
+ $context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true;
+ }
+
+ // Create the poll info if it exists.
+ if ($context['is_poll'])
+ {
+ // Get the question and if it's locked.
+ $request = $smcFunc['db_query']('', '
+ SELECT
+ p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
+ p.guest_vote, p.id_member, IFNULL(mem.real_name, p.poster_name) AS poster_name, p.num_guest_voters, p.reset_poll
+ FROM {db_prefix}polls AS p
+ LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member)
+ WHERE p.id_poll = {int:id_poll}
+ LIMIT 1',
+ array(
+ 'id_poll' => $topicinfo['id_poll'],
+ )
+ );
+ $pollinfo = $smcFunc['db_fetch_assoc']($request);
+ $smcFunc['db_free_result']($request);
+
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(DISTINCT id_member) AS total
+ FROM {db_prefix}log_polls
+ WHERE id_poll = {int:id_poll}
+ AND id_member != {int:not_guest}',
+ array(
+ 'id_poll' => $topicinfo['id_poll'],
+ 'not_guest' => 0,
+ )
+ );
+ list ($pollinfo['total']) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ // Total voters needs to include guest voters
+ $pollinfo['total'] += $pollinfo['num_guest_voters'];
+
+ // Get all the options, and calculate the total votes.
+ $request = $smcFunc['db_query']('', '
+ SELECT pc.id_choice, pc.label, pc.votes, IFNULL(lp.id_choice, -1) AS voted_this
+ FROM {db_prefix}poll_choices AS pc
+ LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_choice = pc.id_choice AND lp.id_poll = {int:id_poll} AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
+ WHERE pc.id_poll = {int:id_poll}',
+ array(
+ 'current_member' => $user_info['id'],
+ 'id_poll' => $topicinfo['id_poll'],
+ 'not_guest' => 0,
+ )
+ );
+ $pollOptions = array();
+ $realtotal = 0;
+ $pollinfo['has_voted'] = false;
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ censorText($row['label']);
+ $pollOptions[$row['id_choice']] = $row;
+ $realtotal += $row['votes'];
+ $pollinfo['has_voted'] |= $row['voted_this'] != -1;
+ }
+ $smcFunc['db_free_result']($request);
+
+ // If this is a guest we need to do our best to work out if they have voted, and what they voted for.
+ if ($user_info['is_guest'] && $pollinfo['guest_vote'] && allowedTo('poll_vote'))
+ {
+ if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $topicinfo['id_poll'] . ',') !== false)
+ {
+ // ;id,timestamp,[vote,vote...]; etc
+ $guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
+ // Find the poll we're after.
+ foreach ($guestinfo as $i => $guestvoted)
+ {
+ $guestvoted = explode(',', $guestvoted);
+ if ($guestvoted[0] == $topicinfo['id_poll'])
+ break;
+ }
+ // Has the poll been reset since guest voted?
+ if ($pollinfo['reset_poll'] > $guestvoted[1])
+ {
+ // Remove the poll info from the cookie to allow guest to vote again
+ unset($guestinfo[$i]);
+ if (!empty($guestinfo))
+ $_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
+ else
+ unset($_COOKIE['guest_poll_vote']);
+ }
+ else
+ {
+ // What did they vote for?
+ unset($guestvoted[0], $guestvoted[1]);
+ foreach ($pollOptions as $choice => $details)
+ {
+ $pollOptions[$choice]['voted_this'] = in_array($choice, $guestvoted) ? 1 : -1;
+ $pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1;
+ }
+ unset($choice, $details, $guestvoted);
+ }
+ unset($guestinfo, $guestvoted, $i);
+ }
+ }
+
+ // Set up the basic poll information.
+ $context['poll'] = array(
+ 'id' => $topicinfo['id_poll'],
+ 'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'),
+ 'question' => parse_bbc($pollinfo['question']),
+ 'total_votes' => $pollinfo['total'],
+ 'change_vote' => !empty($pollinfo['change_vote']),
+ 'is_locked' => !empty($pollinfo['voting_locked']),
+ 'options' => array(),
+ 'lock' => allowedTo('poll_lock_any') || ($context['user']['started'] && allowedTo('poll_lock_own')),
+ 'edit' => allowedTo('poll_edit_any') || ($context['user']['started'] && allowedTo('poll_edit_own')),
+ 'allowed_warning' => $pollinfo['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '',
+ 'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(),
+ 'expire_time' => !empty($pollinfo['expire_time']) ? timeformat($pollinfo['expire_time']) : 0,
+ 'has_voted' => !empty($pollinfo['has_voted']),
+ 'starter' => array(
+ 'id' => $pollinfo['id_member'],
+ 'name' => $row['poster_name'],
+ 'href' => $pollinfo['id_member'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['id_member'],
+ 'link' => $pollinfo['id_member'] == 0 ? $row['poster_name'] : '' . $row['poster_name'] . ''
+ )
+ );
+
+ // Make the lock and edit permissions defined above more directly accessible.
+ $context['allow_lock_poll'] = $context['poll']['lock'];
+ $context['allow_edit_poll'] = $context['poll']['edit'];
+
+ // You're allowed to vote if:
+ // 1. the poll did not expire, and
+ // 2. you're either not a guest OR guest voting is enabled... and
+ // 3. you're not trying to view the results, and
+ // 4. the poll is not locked, and
+ // 5. you have the proper permissions, and
+ // 6. you haven't already voted before.
+ $context['allow_vote'] = !$context['poll']['is_expired'] && (!$user_info['is_guest'] || ($pollinfo['guest_vote'] && allowedTo('poll_vote'))) && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && !$context['poll']['has_voted'];
+
+ // You're allowed to view the results if:
+ // 1. you're just a super-nice-guy, or
+ // 2. anyone can see them (hide_results == 0), or
+ // 3. you can see them after you voted (hide_results == 1), or
+ // 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.)
+ $context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || ($pollinfo['hide_results'] == 1 && $context['poll']['has_voted']) || $context['poll']['is_expired'];
+ $context['poll']['show_results'] = $context['allow_poll_view'] && (isset($_REQUEST['viewresults']) || isset($_REQUEST['viewResults']));
+ $context['show_view_results_button'] = $context['allow_vote'] && (!$context['allow_poll_view'] || !$context['poll']['show_results'] || !$context['poll']['has_voted']);
+
+ // You're allowed to change your vote if:
+ // 1. the poll did not expire, and
+ // 2. you're not a guest... and
+ // 3. the poll is not locked, and
+ // 4. you have the proper permissions, and
+ // 5. you have already voted, and
+ // 6. the poll creator has said you can!
+ $context['allow_change_vote'] = !$context['poll']['is_expired'] && !$user_info['is_guest'] && empty($pollinfo['voting_locked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote'];
+
+ // You're allowed to return to voting options if:
+ // 1. you are (still) allowed to vote.
+ // 2. you are currently seeing the results.
+ $context['allow_return_vote'] = $context['allow_vote'] && $context['poll']['show_results'];
+
+ // Calculate the percentages and bar lengths...
+ $divisor = $realtotal == 0 ? 1 : $realtotal;
+
+ // Determine if a decimal point is needed in order for the options to add to 100%.
+ $precision = $realtotal == 100 ? 0 : 1;
+
+ // Now look through each option, and...
+ foreach ($pollOptions as $i => $option)
+ {
+ // First calculate the percentage, and then the width of the bar...
+ $bar = round(($option['votes'] * 100) / $divisor, $precision);
+ $barWide = $bar == 0 ? 1 : floor(($bar * 8) / 3);
+
+ // Now add it to the poll's contextual theme data.
+ $context['poll']['options'][$i] = array(
+ 'id' => 'options-' . $i,
+ 'percent' => $bar,
+ 'votes' => $option['votes'],
+ 'voted_this' => $option['voted_this'] != -1,
+ 'bar' => '',
+ // Note: IE < 8 requires us to set a width on the container, too.
+ 'bar_ndt' => $bar > 0 ? '
' : '',
+ 'bar_width' => $barWide,
+ 'option' => parse_bbc($option['label']),
+ 'vote_button' => ''
+ );
+ }
+ }
+
+ // Calculate the fastest way to get the messages!
+ $ascending = empty($options['view_newest_first']);
+ $start = $_REQUEST['start'];
+ $limit = $context['messages_per_page'];
+ $firstIndex = 0;
+ if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1)
+ {
+ $ascending = !$ascending;
+ $limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit;
+ $start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit;
+ $firstIndex = $limit - 1;
+ }
+
+ // Get each post and poster in this topic.
+ $request = $smcFunc['db_query']('display_get_post_poster', '
+ SELECT id_msg, id_member, approved
+ FROM {db_prefix}messages
+ WHERE id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : (!empty($modSettings['db_mysql_group_by_fix']) ? '' : '
+ GROUP BY id_msg') . '
+ HAVING (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . '
+ ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : '
+ LIMIT ' . $start . ', ' . $limit),
+ array(
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ 'is_approved' => 1,
+ 'blank_id_member' => 0,
+ )
+ );
+
+ $messages = array();
+ $all_posters = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ if (!empty($row['id_member']))
+ $all_posters[$row['id_msg']] = $row['id_member'];
+ $messages[] = $row['id_msg'];
+ }
+ $smcFunc['db_free_result']($request);
+ $posters = array_unique($all_posters);
+
+ // Guests can't mark topics read or for notifications, just can't sorry.
+ if (!$user_info['is_guest'])
+ {
+ $mark_at_msg = max($messages);
+ if ($mark_at_msg >= $topicinfo['id_last_msg'])
+ $mark_at_msg = $modSettings['maxMsgID'];
+ if ($mark_at_msg >= $topicinfo['new_from'])
+ {
+ $smcFunc['db_insert']($topicinfo['new_from'] == 0 ? 'ignore' : 'replace',
+ '{db_prefix}log_topics',
+ array(
+ 'id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int',
+ ),
+ array(
+ $user_info['id'], $topic, $mark_at_msg,
+ ),
+ array('id_member', 'id_topic')
+ );
+ }
+
+ // Check for notifications on this topic OR board.
+ $request = $smcFunc['db_query']('', '
+ SELECT sent, id_topic
+ FROM {db_prefix}log_notify
+ WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
+ AND id_member = {int:current_member}
+ LIMIT 2',
+ array(
+ 'current_board' => $board,
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ )
+ );
+ $do_once = true;
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ // Find if this topic is marked for notification...
+ if (!empty($row['id_topic']))
+ $context['is_marked_notify'] = true;
+
+ // Only do this once, but mark the notifications as "not sent yet" for next time.
+ if (!empty($row['sent']) && $do_once)
+ {
+ $smcFunc['db_query']('', '
+ UPDATE {db_prefix}log_notify
+ SET sent = {int:is_not_sent}
+ WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
+ AND id_member = {int:current_member}',
+ array(
+ 'current_board' => $board,
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ 'is_not_sent' => 0,
+ )
+ );
+ $do_once = false;
+ }
+ }
+
+ // Have we recently cached the number of new topics in this board, and it's still a lot?
+ if (isset($_REQUEST['topicseen']) && isset($_SESSION['topicseen_cache'][$board]) && $_SESSION['topicseen_cache'][$board] > 5)
+ $_SESSION['topicseen_cache'][$board]--;
+ // Mark board as seen if this is the only new topic.
+ elseif (isset($_REQUEST['topicseen']))
+ {
+ // Use the mark read tables... and the last visit to figure out if this should be read or not.
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(*)
+ FROM {db_prefix}topics AS t
+ LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = {int:current_board} AND lb.id_member = {int:current_member})
+ LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
+ WHERE t.id_board = {int:current_board}
+ AND t.id_last_msg > IFNULL(lb.id_msg, 0)
+ AND t.id_last_msg > IFNULL(lt.id_msg, 0)' . (empty($_SESSION['id_msg_last_visit']) ? '' : '
+ AND t.id_last_msg > {int:id_msg_last_visit}'),
+ array(
+ 'current_board' => $board,
+ 'current_member' => $user_info['id'],
+ 'id_msg_last_visit' => (int) $_SESSION['id_msg_last_visit'],
+ )
+ );
+ list ($numNewTopics) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ // If there're no real new topics in this board, mark the board as seen.
+ if (empty($numNewTopics))
+ $_REQUEST['boardseen'] = true;
+ else
+ $_SESSION['topicseen_cache'][$board] = $numNewTopics;
+ }
+ // Probably one less topic - maybe not, but even if we decrease this too fast it will only make us look more often.
+ elseif (isset($_SESSION['topicseen_cache'][$board]))
+ $_SESSION['topicseen_cache'][$board]--;
+
+ // Mark board as seen if we came using last post link from BoardIndex. (or other places...)
+ if (isset($_REQUEST['boardseen']))
+ {
+ $smcFunc['db_insert']('replace',
+ '{db_prefix}log_boards',
+ array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
+ array($modSettings['maxMsgID'], $user_info['id'], $board),
+ array('id_member', 'id_board')
+ );
+ }
+ }
+
+ $attachments = array();
+
+ // If there _are_ messages here... (probably an error otherwise :!)
+ if (!empty($messages))
+ {
+ // Fetch attachments.
+ if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments'))
+ {
+ $request = $smcFunc['db_query']('', '
+ SELECT
+ a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, IFNULL(a.size, 0) AS filesize, a.downloads, a.approved,
+ a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ',
+ IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
+ FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
+ LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . '
+ WHERE a.id_msg IN ({array_int:message_list})
+ AND a.attachment_type = {int:attachment_type}',
+ array(
+ 'message_list' => $messages,
+ 'attachment_type' => 0,
+ 'is_approved' => 1,
+ )
+ );
+ $temp = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ if (!$row['approved'] && $modSettings['postmod_active'] && !allowedTo('approve_posts') && (!isset($all_posters[$row['id_msg']]) || $all_posters[$row['id_msg']] != $user_info['id']))
+ continue;
+
+ $temp[$row['id_attach']] = $row;
+
+ if (!isset($attachments[$row['id_msg']]))
+ $attachments[$row['id_msg']] = array();
+ }
+ $smcFunc['db_free_result']($request);
+
+ // This is better than sorting it with the query...
+ ksort($temp);
+
+ foreach ($temp as $row)
+ $attachments[$row['id_msg']][] = $row;
+ }
+
+ // What? It's not like it *couldn't* be only guests in this topic...
+ if (!empty($posters))
+ loadMemberData($posters);
+ $messages_request = $smcFunc['db_query']('', '
+ SELECT
+ id_msg, icon, subject, poster_time, poster_ip, id_member, modified_time, modified_name, body,
+ smileys_enabled, poster_name, poster_email, approved,
+ id_msg_modified < {int:new_from} AS is_read
+ FROM {db_prefix}messages
+ WHERE id_msg IN ({array_int:message_list})
+ ORDER BY id_msg' . (empty($options['view_newest_first']) ? '' : ' DESC'),
+ array(
+ 'message_list' => $messages,
+ 'new_from' => $topicinfo['new_from'],
+ )
+ );
+
+ // Go to the last message if the given time is beyond the time of the last message.
+ if (isset($context['start_from']) && $context['start_from'] >= $topicinfo['num_replies'])
+ $context['start_from'] = $topicinfo['num_replies'];
+
+ // Since the anchor information is needed on the top of the page we load these variables beforehand.
+ $context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
+ if (empty($options['view_newest_first']))
+ $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from'];
+ else
+ $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $topicinfo['num_replies'] - $context['start_from'];
+ }
+ else
+ {
+ $messages_request = false;
+ $context['first_message'] = 0;
+ $context['first_new_message'] = false;
+ }
+
+ $context['jump_to'] = array(
+ 'label' => addslashes(un_htmlspecialchars($txt['jump_to'])),
+ 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&' => '&'))),
+ 'child_level' => $board_info['child_level'],
+ );
+
+ // Set the callback. (do you REALIZE how much memory all the messages would take?!?)
+ $context['get_message'] = 'prepareDisplayContext';
+
+ // Now set all the wonderful, wonderful permissions... like moderation ones...
+ $common_permissions = array(
+ 'can_approve' => 'approve_posts',
+ 'can_ban' => 'manage_bans',
+ 'can_sticky' => 'make_sticky',
+ 'can_merge' => 'merge_any',
+ 'can_split' => 'split_any',
+ 'calendar_post' => 'calendar_post',
+ 'can_mark_notify' => 'mark_any_notify',
+ 'can_send_topic' => 'send_topic',
+ 'can_send_pm' => 'pm_send',
+ 'can_report_moderator' => 'report_any',
+ 'can_moderate_forum' => 'moderate_forum',
+ 'can_issue_warning' => 'issue_warning',
+ 'can_restore_topic' => 'move_any',
+ 'can_restore_msg' => 'move_any',
+ );
+ foreach ($common_permissions as $contextual => $perm)
+ $context[$contextual] = allowedTo($perm);
+
+ // Permissions with _any/_own versions. $context[YYY] => ZZZ_any/_own.
+ $anyown_permissions = array(
+ 'can_move' => 'move',
+ 'can_lock' => 'lock',
+ 'can_delete' => 'remove',
+ 'can_add_poll' => 'poll_add',
+ 'can_remove_poll' => 'poll_remove',
+ 'can_reply' => 'post_reply',
+ 'can_reply_unapproved' => 'post_unapproved_replies',
+ );
+ foreach ($anyown_permissions as $contextual => $perm)
+ $context[$contextual] = allowedTo($perm . '_any') || ($context['user']['started'] && allowedTo($perm . '_own'));
+
+ // Cleanup all the permissions with extra stuff...
+ $context['can_mark_notify'] &= !$context['user']['is_guest'];
+ $context['can_sticky'] &= !empty($modSettings['enableStickyTopics']);
+ $context['calendar_post'] &= !empty($modSettings['cal_enabled']);
+ $context['can_add_poll'] &= $modSettings['pollMode'] == '1' && $topicinfo['id_poll'] <= 0;
+ $context['can_remove_poll'] &= $modSettings['pollMode'] == '1' && $topicinfo['id_poll'] > 0;
+ $context['can_reply'] &= empty($topicinfo['locked']) || allowedTo('moderate_board');
+ $context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($topicinfo['locked']) || allowedTo('moderate_board'));
+ $context['can_issue_warning'] &= in_array('w', $context['admin_features']) && $modSettings['warning_settings'][0] == 1;
+ // Handle approval flags...
+ $context['can_reply_approved'] = $context['can_reply'];
+ $context['can_reply'] |= $context['can_reply_unapproved'];
+ $context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])));
+ $context['can_mark_unread'] = !$user_info['is_guest'] && $settings['show_mark_read'];
+
+ $context['can_send_topic'] = (!$modSettings['postmod_active'] || $topicinfo['approved']) && allowedTo('send_topic');
+
+ // Start this off for quick moderation - it will be or'd for each post.
+ $context['can_remove_post'] = allowedTo('delete_any') || (allowedTo('delete_replies') && $context['user']['started']);
+
+ // Can restore topic? That's if the topic is in the recycle board and has a previous restore state.
+ $context['can_restore_topic'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_board']);
+ $context['can_restore_msg'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_topic']);
+
+ // Wireless shows a "more" if you can do anything special.
+ if (WIRELESS && WIRELESS_PROTOCOL != 'wap')
+ {
+ $context['wireless_more'] = $context['can_sticky'] || $context['can_lock'] || allowedTo('modify_any');
+ $context['wireless_moderate'] = isset($_GET['moderate']) ? ';moderate' : '';
+ }
+
+ // Load up the "double post" sequencing magic.
+ if (!empty($options['display_quick_reply']))
+ {
+ checkSubmitOnce('register');
+ $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
+ $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
+ }
+}
+
+// Callback for the message display.
+function prepareDisplayContext($reset = false)
+{
+ global $settings, $txt, $modSettings, $scripturl, $options, $user_info, $smcFunc;
+ global $memberContext, $context, $messages_request, $topic, $attachments, $topicinfo;
+
+ static $counter = null;
+
+ // If the query returned false, bail.
+ if ($messages_request == false)
+ return false;
+
+ // Remember which message this is. (ie. reply #83)
+ if ($counter === null || $reset)
+ $counter = empty($options['view_newest_first']) ? $context['start'] : $context['total_visible_posts'] - $context['start'];
+
+ // Start from the beginning...
+ if ($reset)
+ return @$smcFunc['db_data_seek']($messages_request, 0);
+
+ // Attempt to get the next message.
+ $message = $smcFunc['db_fetch_assoc']($messages_request);
+ if (!$message)
+ {
+ $smcFunc['db_free_result']($messages_request);
+ return false;
+ }
+
+ // $context['icon_sources'] says where each icon should come from - here we set up the ones which will always exist!
+ if (empty($context['icon_sources']))
+ {
+ $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless', 'clip');
+ $context['icon_sources'] = array();
+ foreach ($stable_icons as $icon)
+ $context['icon_sources'][$icon] = 'images_url';
+ }
+
+ // Message Icon Management... check the images exist.
+ if (empty($modSettings['messageIconChecks_disable']))
+ {
+ // If the current icon isn't known, then we need to do something...
+ if (!isset($context['icon_sources'][$message['icon']]))
+ $context['icon_sources'][$message['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $message['icon'] . '.gif') ? 'images_url' : 'default_images_url';
+ }
+ elseif (!isset($context['icon_sources'][$message['icon']]))
+ $context['icon_sources'][$message['icon']] = 'images_url';
+
+ // If you're a lazy bum, you probably didn't give a subject...
+ $message['subject'] = $message['subject'] != '' ? $message['subject'] : $txt['no_subject'];
+
+ // Are you allowed to remove at least a single reply?
+ $context['can_remove_post'] |= allowedTo('delete_own') && (empty($modSettings['edit_disable_time']) || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()) && $message['id_member'] == $user_info['id'];
+
+ // If it couldn't load, or the user was a guest.... someday may be done with a guest table.
+ if (!loadMemberContext($message['id_member'], true))
+ {
+ // Notice this information isn't used anywhere else....
+ $memberContext[$message['id_member']]['name'] = $message['poster_name'];
+ $memberContext[$message['id_member']]['id'] = 0;
+ $memberContext[$message['id_member']]['group'] = $txt['guest_title'];
+ $memberContext[$message['id_member']]['link'] = $message['poster_name'];
+ $memberContext[$message['id_member']]['email'] = $message['poster_email'];
+ $memberContext[$message['id_member']]['show_email'] = showEmailAddress(true, 0);
+ $memberContext[$message['id_member']]['is_guest'] = true;
+ }
+ else
+ {
+ $memberContext[$message['id_member']]['can_view_profile'] = allowedTo('profile_view_any') || ($message['id_member'] == $user_info['id'] && allowedTo('profile_view_own'));
+ $memberContext[$message['id_member']]['is_topic_starter'] = $message['id_member'] == $context['topic_starter_id'];
+ $memberContext[$message['id_member']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member']]['warning_status'] && ($context['user']['can_mod'] || (!$user_info['is_guest'] && !empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $message['id_member'] == $user_info['id'])));
+ }
+
+ $memberContext[$message['id_member']]['ip'] = $message['poster_ip'];
+
+ // Do the censor thang.
+ censorText($message['body']);
+ censorText($message['subject']);
+
+ // Run BBC interpreter on the message.
+ $message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']);
+
+ // Compose the memory eat- I mean message array.
+ $output = array(
+ 'attachment' => loadAttachmentContext($message['id_msg']),
+ 'alternate' => $counter % 2,
+ 'id' => $message['id_msg'],
+ 'href' => $scripturl . '?topic=' . $topic . '.msg' . $message['id_msg'] . '#msg' . $message['id_msg'],
+ 'link' => '' . $message['subject'] . '',
+ 'member' => &$memberContext[$message['id_member']],
+ 'icon' => $message['icon'],
+ 'icon_url' => $settings[$context['icon_sources'][$message['icon']]] . '/post/' . $message['icon'] . '.gif',
+ 'subject' => $message['subject'],
+ 'time' => timeformat($message['poster_time']),
+ 'timestamp' => forum_time(true, $message['poster_time']),
+ 'counter' => $counter,
+ 'modified' => array(
+ 'time' => timeformat($message['modified_time']),
+ 'timestamp' => forum_time(true, $message['modified_time']),
+ 'name' => $message['modified_name']
+ ),
+ 'body' => $message['body'],
+ 'new' => empty($message['is_read']),
+ 'approved' => $message['approved'],
+ 'first_new' => isset($context['start_from']) && $context['start_from'] == $counter,
+ 'is_ignored' => !empty($modSettings['enable_buddylist']) && !empty($options['posts_apply_ignore_list']) && in_array($message['id_member'], $context['user']['ignoreusers']),
+ 'can_approve' => !$message['approved'] && $context['can_approve'],
+ 'can_unapprove' => $message['approved'] && $context['can_approve'],
+ 'can_modify' => (!$context['is_locked'] || allowedTo('moderate_board')) && (allowedTo('modify_any') || (allowedTo('modify_replies') && $context['user']['started']) || (allowedTo('modify_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || !$message['approved'] || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time()))),
+ 'can_remove' => allowedTo('delete_any') || (allowedTo('delete_replies') && $context['user']['started']) || (allowedTo('delete_own') && $message['id_member'] == $user_info['id'] && (empty($modSettings['edit_disable_time']) || $message['poster_time'] + $modSettings['edit_disable_time'] * 60 > time())),
+ 'can_see_ip' => allowedTo('moderate_forum') || ($message['id_member'] == $user_info['id'] && !empty($user_info['id'])),
+ );
+
+ // Is this user the message author?
+ $output['is_message_author'] = $message['id_member'] == $user_info['id'];
+
+ if (empty($options['view_newest_first']))
+ $counter++;
+ else
+ $counter--;
+
+ return $output;
+}
+
+// Download an attachment.
+function Download()
+{
+ global $txt, $modSettings, $user_info, $scripturl, $context, $sourcedir, $topic, $smcFunc;
+
+ // Some defaults that we need.
+ $context['character_set'] = empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'ISO-8859-1' : $txt['lang_character_set']) : $modSettings['global_character_set'];
+ $context['utf8'] = $context['character_set'] === 'UTF-8' && (strpos(strtolower(PHP_OS), 'win') === false || @version_compare(PHP_VERSION, '4.2.3') != -1);
+ $context['no_last_modified'] = true;
+
+ // Make sure some attachment was requested!
+ if (!isset($_REQUEST['attach']) && !isset($_REQUEST['id']))
+ fatal_lang_error('no_access', false);
+
+ $_REQUEST['attach'] = isset($_REQUEST['attach']) ? (int) $_REQUEST['attach'] : (int) $_REQUEST['id'];
+
+ if (isset($_REQUEST['type']) && $_REQUEST['type'] == 'avatar')
+ {
+ $request = $smcFunc['db_query']('', '
+ SELECT id_folder, filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member
+ FROM {db_prefix}attachments
+ WHERE id_attach = {int:id_attach}
+ AND id_member > {int:blank_id_member}
+ LIMIT 1',
+ array(
+ 'id_attach' => $_REQUEST['attach'],
+ 'blank_id_member' => 0,
+ )
+ );
+ $_REQUEST['image'] = true;
+ }
+ // This is just a regular attachment...
+ else
+ {
+ // This checks only the current board for $board/$topic's permissions.
+ isAllowedTo('view_attachments');
+
+ // Make sure this attachment is on this board.
+ // NOTE: We must verify that $topic is the attachment's topic, or else the permission check above is broken.
+ $request = $smcFunc['db_query']('', '
+ SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member
+ 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})
+ INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
+ WHERE a.id_attach = {int:attach}
+ LIMIT 1',
+ array(
+ 'attach' => $_REQUEST['attach'],
+ 'current_topic' => $topic,
+ )
+ );
+ }
+ if ($smcFunc['db_num_rows']($request) == 0)
+ fatal_lang_error('no_access', false);
+ list ($id_folder, $real_filename, $file_hash, $file_ext, $id_attach, $attachment_type, $mime_type, $is_approved, $id_member) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ // If it isn't yet approved, do they have permission to view it?
+ if (!$is_approved && ($id_member == 0 || $user_info['id'] != $id_member) && ($attachment_type == 0 || $attachment_type == 3))
+ isAllowedTo('approve_posts');
+
+ // Update the download counter (unless it's a thumbnail).
+ if ($attachment_type != 3)
+ $smcFunc['db_query']('attach_download_increase', '
+ UPDATE LOW_PRIORITY {db_prefix}attachments
+ SET downloads = downloads + 1
+ WHERE id_attach = {int:id_attach}',
+ array(
+ 'id_attach' => $id_attach,
+ )
+ );
+
+ $filename = getAttachmentFilename($real_filename, $_REQUEST['attach'], $id_folder, false, $file_hash);
+
+ // This is done to clear any output that was made before now. (would use ob_clean(), but that's PHP 4.2.0+...)
+ ob_end_clean();
+ if (!empty($modSettings['enableCompressedOutput']) && @version_compare(PHP_VERSION, '4.2.0') >= 0 && @filesize($filename) <= 4194304 && in_array($file_ext, array('txt', 'html', 'htm', 'js', 'doc', 'pdf', 'docx', 'rtf', 'css', 'php', 'log', 'xml', 'sql', 'c', 'java')))
+ @ob_start('ob_gzhandler');
+ else
+ {
+ ob_start();
+ header('Content-Encoding: none');
+ }
+
+ // No point in a nicer message, because this is supposed to be an attachment anyway...
+ if (!file_exists($filename))
+ {
+ loadLanguage('Errors');
+
+ header('HTTP/1.0 404 ' . $txt['attachment_not_found']);
+ header('Content-Type: text/plain; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
+
+ // We need to die like this *before* we send any anti-caching headers as below.
+ die('404 - ' . $txt['attachment_not_found']);
+ }
+
+ // If it hasn't been modified since the last time this attachement was retrieved, there's no need to display it again.
+ if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))
+ {
+ list($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
+ if (strtotime($modified_since) >= filemtime($filename))
+ {
+ ob_end_clean();
+
+ // Answer the question - no, it hasn't been modified ;).
+ header('HTTP/1.1 304 Not Modified');
+ exit;
+ }
+ }
+
+ // Check whether the ETag was sent back, and cache based on that...
+ $eTag = '"' . substr($_REQUEST['attach'] . $real_filename . filemtime($filename), 0, 64) . '"';
+ if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'], $eTag) !== false)
+ {
+ ob_end_clean();
+
+ header('HTTP/1.1 304 Not Modified');
+ exit;
+ }
+
+ // Send the attachment headers.
+ header('Pragma: ');
+ if (!$context['browser']['is_gecko'])
+ header('Content-Transfer-Encoding: binary');
+ 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($filename)) . ' GMT');
+ header('Accept-Ranges: bytes');
+ header('Connection: close');
+ header('ETag: ' . $eTag);
+
+ // IE 6 just doesn't play nice. As dirty as this seems, it works.
+ if ($context['browser']['is_ie6'] && isset($_REQUEST['image']))
+ unset($_REQUEST['image']);
+
+ // Make sure the mime type warrants an inline display.
+ elseif (isset($_REQUEST['image']) && !empty($mime_type) && strpos($mime_type, 'image/') !== 0)
+ unset($_REQUEST['image']);
+
+ // Does this have a mime type?
+ elseif (!empty($mime_type) && (isset($_REQUEST['image']) || !in_array($file_ext, array('jpg', 'gif', 'jpeg', 'x-ms-bmp', 'png', 'psd', 'tiff', 'iff'))))
+ header('Content-Type: ' . strtr($mime_type, array('image/bmp' => 'image/x-ms-bmp')));
+
+ else
+ {
+ header('Content-Type: ' . ($context['browser']['is_ie'] || $context['browser']['is_opera'] ? 'application/octetstream' : 'application/octet-stream'));
+ if (isset($_REQUEST['image']))
+ unset($_REQUEST['image']);
+ }
+
+ // Convert the file to UTF-8, cuz most browsers dig that.
+ $utf8name = !$context['utf8'] && function_exists('iconv') ? iconv($context['character_set'], 'UTF-8', $real_filename) : (!$context['utf8'] && function_exists('mb_convert_encoding') ? mb_convert_encoding($real_filename, 'UTF-8', $context['character_set']) : $real_filename);
+ $fixchar = create_function('$n', '
+ if ($n < 32)
+ return \'\';
+ elseif ($n < 128)
+ return chr($n);
+ elseif ($n < 2048)
+ return chr(192 | $n >> 6) . chr(128 | $n & 63);
+ elseif ($n < 65536)
+ return chr(224 | $n >> 12) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63);
+ else
+ return chr(240 | $n >> 18) . chr(128 | $n >> 12 & 63) . chr(128 | $n >> 6 & 63) . chr(128 | $n & 63);');
+
+ $disposition = !isset($_REQUEST['image']) ? 'attachment' : 'inline';
+
+ // Different browsers like different standards...
+ if ($context['browser']['is_firefox'])
+ header('Content-Disposition: ' . $disposition . '; filename*="UTF-8\'\'' . preg_replace('~(\d{3,8});~e', '$fixchar(\'$1\')', $utf8name) . '"');
+
+ elseif ($context['browser']['is_opera'])
+ header('Content-Disposition: ' . $disposition . '; filename="' . preg_replace('~(\d{3,8});~e', '$fixchar(\'$1\')', $utf8name) . '"');
+
+ elseif ($context['browser']['is_ie'])
+ header('Content-Disposition: ' . $disposition . '; filename="' . urlencode(preg_replace('~(\d{3,8});~e', '$fixchar(\'$1\')', $utf8name)) . '"');
+
+ else
+ header('Content-Disposition: ' . $disposition . '; filename="' . $utf8name . '"');
+
+ // If this has an "image extension" - but isn't actually an image - then ensure it isn't cached cause of silly IE.
+ if (!isset($_REQUEST['image']) && in_array($file_ext, array('gif', 'jpg', 'bmp', 'png', 'jpeg', 'tiff')))
+ header('Cache-Control: no-cache');
+ else
+ header('Cache-Control: max-age=' . (525600 * 60) . ', private');
+
+ if (empty($modSettings['enableCompressedOutput']) || filesize($filename) > 4194304)
+ header('Content-Length: ' . filesize($filename));
+
+ // Try to buy some time...
+ @set_time_limit(600);
+
+ // Recode line endings for text files, if enabled.
+ if (!empty($modSettings['attachmentRecodeLineEndings']) && !isset($_REQUEST['image']) && in_array($file_ext, array('txt', 'css', 'htm', 'html', 'php', 'xml')))
+ {
+ if (strpos($_SERVER['HTTP_USER_AGENT'], 'Windows') !== false)
+ $callback = create_function('$buffer', 'return preg_replace(\'~[\r]?\n~\', "\r\n", $buffer);');
+ elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Mac') !== false)
+ $callback = create_function('$buffer', 'return preg_replace(\'~[\r]?\n~\', "\r", $buffer);');
+ else
+ $callback = create_function('$buffer', 'return preg_replace(\'~[\r]?\n~\', "\n", $buffer);');
+ }
+
+ // Since we don't do output compression for files this large...
+ if (filesize($filename) > 4194304)
+ {
+ // Forcibly end any output buffering going on.
+ if (function_exists('ob_get_level'))
+ {
+ while (@ob_get_level() > 0)
+ @ob_end_clean();
+ }
+ else
+ {
+ @ob_end_clean();
+ @ob_end_clean();
+ @ob_end_clean();
+ }
+
+ $fp = fopen($filename, 'rb');
+ while (!feof($fp))
+ {
+ if (isset($callback))
+ echo $callback(fread($fp, 8192));
+ else
+ echo fread($fp, 8192);
+ flush();
+ }
+ fclose($fp);
+ }
+ // On some of the less-bright hosts, readfile() is disabled. It's just a faster, more byte safe, version of what's in the if.
+ elseif (isset($callback) || @readfile($filename) == null)
+ echo isset($callback) ? $callback(file_get_contents($filename)) : file_get_contents($filename);
+
+ obExit(false);
+}
+
+function loadAttachmentContext($id_msg)
+{
+ global $attachments, $modSettings, $txt, $scripturl, $topic, $sourcedir, $smcFunc;
+
+ // Set up the attachment info - based on code by Meriadoc.
+ $attachmentData = array();
+ $have_unapproved = false;
+ if (isset($attachments[$id_msg]) && !empty($modSettings['attachmentEnable']))
+ {
+ foreach ($attachments[$id_msg] as $i => $attachment)
+ {
+ $attachmentData[$i] = array(
+ 'id' => $attachment['id_attach'],
+ 'name' => preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '\\1;', htmlspecialchars($attachment['filename'])),
+ 'downloads' => $attachment['downloads'],
+ 'size' => round($attachment['filesize'] / 1024, 2) . ' ' . $txt['kilobyte'],
+ 'byte_size' => $attachment['filesize'],
+ 'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_attach'],
+ 'link' => '' . htmlspecialchars($attachment['filename']) . '',
+ 'is_image' => !empty($attachment['width']) && !empty($attachment['height']) && !empty($modSettings['attachmentShowImages']),
+ 'is_approved' => $attachment['approved'],
+ );
+
+ // If something is unapproved we'll note it so we can sort them.
+ if (!$attachment['approved'])
+ $have_unapproved = true;
+
+ if (!$attachmentData[$i]['is_image'])
+ continue;
+
+ $attachmentData[$i]['real_width'] = $attachment['width'];
+ $attachmentData[$i]['width'] = $attachment['width'];
+ $attachmentData[$i]['real_height'] = $attachment['height'];
+ $attachmentData[$i]['height'] = $attachment['height'];
+
+ // Let's see, do we want thumbs?
+ if (!empty($modSettings['attachmentThumbnails']) && !empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight']) && ($attachment['width'] > $modSettings['attachmentThumbWidth'] || $attachment['height'] > $modSettings['attachmentThumbHeight']) && strlen($attachment['filename']) < 249)
+ {
+ // A proper thumb doesn't exist yet? Create one!
+ if (empty($attachment['id_thumb']) || $attachment['thumb_width'] > $modSettings['attachmentThumbWidth'] || $attachment['thumb_height'] > $modSettings['attachmentThumbHeight'] || ($attachment['thumb_width'] < $modSettings['attachmentThumbWidth'] && $attachment['thumb_height'] < $modSettings['attachmentThumbHeight']))
+ {
+ $filename = getAttachmentFilename($attachment['filename'], $attachment['id_attach'], $attachment['id_folder']);
+
+ require_once($sourcedir . '/Subs-Graphics.php');
+ if (createThumbnail($filename, $modSettings['attachmentThumbWidth'], $modSettings['attachmentThumbHeight']))
+ {
+ // So what folder are we putting this image in?
+ if (!empty($modSettings['currentAttachmentUploadDir']))
+ {
+ if (!is_array($modSettings['attachmentUploadDir']))
+ $modSettings['attachmentUploadDir'] = @unserialize($modSettings['attachmentUploadDir']);
+ $path = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
+ $id_folder_thumb = $modSettings['currentAttachmentUploadDir'];
+ }
+ else
+ {
+ $path = $modSettings['attachmentUploadDir'];
+ $id_folder_thumb = 1;
+ }
+
+ // Calculate the size of the created thumbnail.
+ $size = @getimagesize($filename . '_thumb');
+ list ($attachment['thumb_width'], $attachment['thumb_height']) = $size;
+ $thumb_size = filesize($filename . '_thumb');
+
+ // These are the only valid image types for SMF.
+ $validImageTypes = array(1 => 'gif', 2 => 'jpeg', 3 => 'png', 5 => 'psd', 6 => 'bmp', 7 => 'tiff', 8 => 'tiff', 9 => 'jpeg', 14 => 'iff');
+
+ // What about the extension?
+ $thumb_ext = isset($validImageTypes[$size[2]]) ? $validImageTypes[$size[2]] : '';
+
+ // Figure out the mime type.
+ if (!empty($size['mime']))
+ $thumb_mime = $size['mime'];
+ else
+ $thumb_mime = 'image/' . $thumb_ext;
+
+ $thumb_filename = $attachment['filename'] . '_thumb';
+ $thumb_hash = getAttachmentFilename($thumb_filename, false, null, true);
+
+ // Add this beauty to the database.
+ $smcFunc['db_insert']('',
+ '{db_prefix}attachments',
+ array('id_folder' => 'int', 'id_msg' => 'int', 'attachment_type' => 'int', 'filename' => 'string', 'file_hash' => 'string', 'size' => 'int', 'width' => 'int', 'height' => 'int', 'fileext' => 'string', 'mime_type' => 'string'),
+ array($id_folder_thumb, $id_msg, 3, $thumb_filename, $thumb_hash, (int) $thumb_size, (int) $attachment['thumb_width'], (int) $attachment['thumb_height'], $thumb_ext, $thumb_mime),
+ array('id_attach')
+ );
+ $old_id_thumb = $attachment['id_thumb'];
+ $attachment['id_thumb'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
+ if (!empty($attachment['id_thumb']))
+ {
+ $smcFunc['db_query']('', '
+ UPDATE {db_prefix}attachments
+ SET id_thumb = {int:id_thumb}
+ WHERE id_attach = {int:id_attach}',
+ array(
+ 'id_thumb' => $attachment['id_thumb'],
+ 'id_attach' => $attachment['id_attach'],
+ )
+ );
+
+ $thumb_realname = getAttachmentFilename($thumb_filename, $attachment['id_thumb'], $id_folder_thumb, false, $thumb_hash);
+ rename($filename . '_thumb', $thumb_realname);
+
+ // Do we need to remove an old thumbnail?
+ if (!empty($old_id_thumb))
+ {
+ require_once($sourcedir . '/ManageAttachments.php');
+ removeAttachments(array('id_attach' => $old_id_thumb), '', false, false);
+ }
+ }
+ }
+ }
+
+ // Only adjust dimensions on successful thumbnail creation.
+ if (!empty($attachment['thumb_width']) && !empty($attachment['thumb_height']))
+ {
+ $attachmentData[$i]['width'] = $attachment['thumb_width'];
+ $attachmentData[$i]['height'] = $attachment['thumb_height'];
+ }
+ }
+
+ if (!empty($attachment['id_thumb']))
+ $attachmentData[$i]['thumbnail'] = array(
+ 'id' => $attachment['id_thumb'],
+ 'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_thumb'] . ';image',
+ );
+ $attachmentData[$i]['thumbnail']['has_thumb'] = !empty($attachment['id_thumb']);
+
+ // If thumbnails are disabled, check the maximum size of the image.
+ if (!$attachmentData[$i]['thumbnail']['has_thumb'] && ((!empty($modSettings['max_image_width']) && $attachment['width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachment['height'] > $modSettings['max_image_height'])))
+ {
+ if (!empty($modSettings['max_image_width']) && (empty($modSettings['max_image_height']) || $attachment['height'] * $modSettings['max_image_width'] / $attachment['width'] <= $modSettings['max_image_height']))
+ {
+ $attachmentData[$i]['width'] = $modSettings['max_image_width'];
+ $attachmentData[$i]['height'] = floor($attachment['height'] * $modSettings['max_image_width'] / $attachment['width']);
+ }
+ elseif (!empty($modSettings['max_image_width']))
+ {
+ $attachmentData[$i]['width'] = floor($attachment['width'] * $modSettings['max_image_height'] / $attachment['height']);
+ $attachmentData[$i]['height'] = $modSettings['max_image_height'];
+ }
+ }
+ elseif ($attachmentData[$i]['thumbnail']['has_thumb'])
+ {
+ // If the image is too large to show inline, make it a popup.
+ if (((!empty($modSettings['max_image_width']) && $attachmentData[$i]['real_width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachmentData[$i]['real_height'] > $modSettings['max_image_height'])))
+ $attachmentData[$i]['thumbnail']['javascript'] = 'return reqWin(\'' . $attachmentData[$i]['href'] . ';image\', ' . ($attachment['width'] + 20) . ', ' . ($attachment['height'] + 20) . ', true);';
+ else
+ $attachmentData[$i]['thumbnail']['javascript'] = 'return expandThumb(' . $attachment['id_attach'] . ');';
+ }
+
+ if (!$attachmentData[$i]['thumbnail']['has_thumb'])
+ $attachmentData[$i]['downloads']++;
+ }
+ }
+
+ // Do we need to instigate a sort?
+ if ($have_unapproved)
+ usort($attachmentData, 'approved_attach_sort');
+
+ return $attachmentData;
+}
+
+// A sort function for putting unapproved attachments first.
+function approved_attach_sort($a, $b)
+{
+ if ($a['is_approved'] == $b['is_approved'])
+ return 0;
+
+ return $a['is_approved'] > $b['is_approved'] ? -1 : 1;
+}
+
+// In-topic quick moderation.
+function QuickInTopicModeration()
+{
+ global $sourcedir, $topic, $board, $user_info, $smcFunc, $modSettings, $context;
+
+ // Check the session = get or post.
+ checkSession('request');
+
+ require_once($sourcedir . '/RemoveTopic.php');
+
+ if (empty($_REQUEST['msgs']))
+ redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
+
+ $messages = array();
+ foreach ($_REQUEST['msgs'] as $dummy)
+ $messages[] = (int) $dummy;
+
+ // We are restoring messages. We handle this in another place.
+ if (isset($_REQUEST['restore_selected']))
+ redirectexit('action=restoretopic;msgs=' . implode(',', $messages) . ';' . $context['session_var'] . '=' . $context['session_id']);
+
+ // Allowed to delete any message?
+ if (allowedTo('delete_any'))
+ $allowed_all = true;
+ // Allowed to delete replies to their messages?
+ elseif (allowedTo('delete_replies'))
+ {
+ $request = $smcFunc['db_query']('', '
+ SELECT id_member_started
+ FROM {db_prefix}topics
+ WHERE id_topic = {int:current_topic}
+ LIMIT 1',
+ array(
+ 'current_topic' => $topic,
+ )
+ );
+ list ($starter) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ $allowed_all = $starter == $user_info['id'];
+ }
+ else
+ $allowed_all = false;
+
+ // Make sure they're allowed to delete their own messages, if not any.
+ if (!$allowed_all)
+ isAllowedTo('delete_own');
+
+ // Allowed to remove which messages?
+ $request = $smcFunc['db_query']('', '
+ SELECT id_msg, subject, id_member, poster_time
+ FROM {db_prefix}messages
+ WHERE id_msg IN ({array_int:message_list})
+ AND id_topic = {int:current_topic}' . (!$allowed_all ? '
+ AND id_member = {int:current_member}' : '') . '
+ LIMIT ' . count($messages),
+ array(
+ 'current_member' => $user_info['id'],
+ 'current_topic' => $topic,
+ 'message_list' => $messages,
+ )
+ );
+ $messages = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ if (!$allowed_all && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time())
+ continue;
+
+ $messages[$row['id_msg']] = array($row['subject'], $row['id_member']);
+ }
+ $smcFunc['db_free_result']($request);
+
+ // Get the first message in the topic - because you can't delete that!
+ $request = $smcFunc['db_query']('', '
+ SELECT id_first_msg, id_last_msg
+ FROM {db_prefix}topics
+ WHERE id_topic = {int:current_topic}
+ LIMIT 1',
+ array(
+ 'current_topic' => $topic,
+ )
+ );
+ list ($first_message, $last_message) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ // Delete all the messages we know they can delete. ($messages)
+ foreach ($messages as $message => $info)
+ {
+ // Just skip the first message - if it's not the last.
+ if ($message == $first_message && $message != $last_message)
+ continue;
+ // If the first message is going then don't bother going back to the topic as we're effectively deleting it.
+ elseif ($message == $first_message)
+ $topicGone = true;
+
+ removeMessage($message);
+
+ // Log this moderation action ;).
+ if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id']))
+ logAction('delete', array('topic' => $topic, 'subject' => $info[0], 'member' => $info[1], 'board' => $board));
+ }
+
+ redirectexit(!empty($topicGone) ? 'board=' . $board : 'topic=' . $topic . '.' . $_REQUEST['start']);
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/DumpDatabase.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/DumpDatabase.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,182 @@
+ ''));
+ $dbp = str_replace('_', '\_', $match[2]);
+ }
+ else
+ {
+ $db = false;
+ $dbp = $db_prefix;
+ }
+
+ // Dump each table.
+ $tables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
+ foreach ($tables as $tableName)
+ {
+ if (function_exists('apache_reset_timeout'))
+ @apache_reset_timeout();
+
+ // Are we dumping the structures?
+ if (isset($_REQUEST['struct']))
+ {
+ echo
+ $crlf,
+ '--', $crlf,
+ '-- Table structure for table `', $tableName, '`', $crlf,
+ '--', $crlf,
+ $crlf,
+ $smcFunc['db_table_sql']($tableName), ';', $crlf;
+ }
+
+ // How about the data?
+ if (!isset($_REQUEST['data']) || substr($tableName, -10) == 'log_errors')
+ continue;
+
+ // Are there any rows in this table?
+ $get_rows = $smcFunc['db_insert_sql']($tableName);
+
+ // No rows to get - skip it.
+ if (empty($get_rows))
+ continue;
+
+ echo
+ $crlf,
+ '--', $crlf,
+ '-- Dumping data in `', $tableName, '`', $crlf,
+ '--', $crlf,
+ $crlf,
+ $get_rows,
+ '-- --------------------------------------------------------', $crlf;
+ }
+
+ echo
+ $crlf,
+ '-- Done', $crlf;
+
+ exit;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Errors.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Errors.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,420 @@
+ '<', '>' => '>', '"' => '"'));
+ $error_message = strtr($error_message, array('<br />' => ' ', '<b>' => '', '</b>' => '', "\n" => ' '));
+
+ // Add a file and line to the error message?
+ // Don't use the actual txt entries for file and line but instead use %1$s for file and %2$s for line
+ if ($file == null)
+ $file = '';
+ else
+ // Window style slashes don't play well, lets convert them to the unix style.
+ $file = str_replace('\\', '/', $file);
+
+ if ($line == null)
+ $line = 0;
+ else
+ $line = (int) $line;
+
+ // Just in case there's no id_member or IP set yet.
+ if (empty($user_info['id']))
+ $user_info['id'] = 0;
+ if (empty($user_info['ip']))
+ $user_info['ip'] = '';
+
+ // Find the best query string we can...
+ $query_string = empty($_SERVER['QUERY_STRING']) ? (empty($_SERVER['REQUEST_URL']) ? '' : str_replace($scripturl, '', $_SERVER['REQUEST_URL'])) : $_SERVER['QUERY_STRING'];
+
+ // Don't log the session hash in the url twice, it's a waste.
+ $query_string = htmlspecialchars((SMF == 'SSI' ? '' : '?') . preg_replace(array('~;sesc=[^&;]+~', '~' . session_name() . '=' . session_id() . '[&;]~'), array(';sesc', ''), $query_string));
+
+ // Just so we know what board error messages are from.
+ if (isset($_POST['board']) && !isset($_GET['board']))
+ $query_string .= ($query_string == '' ? 'board=' : ';board=') . $_POST['board'];
+
+ // What types of categories do we have?
+ $known_error_types = array(
+ 'general',
+ 'critical',
+ 'database',
+ 'undefined_vars',
+ 'user',
+ 'template',
+ 'debug',
+ );
+
+ // Make sure the category that was specified is a valid one
+ $error_type = in_array($error_type, $known_error_types) && $error_type !== true ? $error_type : 'general';
+
+ // Don't log the same error countless times, as we can get in a cycle of depression...
+ $error_info = array($user_info['id'], time(), $user_info['ip'], $query_string, $error_message, (string) $sc, $error_type, $file, $line);
+ if (empty($last_error) || $last_error != $error_info)
+ {
+ // Insert the error into the database.
+ $smcFunc['db_insert']('',
+ '{db_prefix}log_errors',
+ array('id_member' => 'int', 'log_time' => 'int', 'ip' => 'string-16', 'url' => 'string-65534', 'message' => 'string-65534', 'session' => 'string', 'error_type' => 'string', 'file' => 'string-255', 'line' => 'int'),
+ $error_info,
+ array('id_error')
+ );
+ $last_error = $error_info;
+ }
+
+ // Return the message to make things simpler.
+ return $error_message;
+}
+
+// An irrecoverable error.
+function fatal_error($error, $log = 'general')
+{
+ global $txt, $context, $modSettings;
+
+ // We don't have $txt yet, but that's okay...
+ if (empty($txt))
+ die($error);
+
+ setup_fatal_error_context($log || (!empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2) ? log_error($error, $log) : $error);
+}
+
+// A fatal error with a message stored in the language file.
+function fatal_lang_error($error, $log = 'general', $sprintf = array())
+{
+ global $txt, $language, $modSettings, $user_info, $context;
+ static $fatal_error_called = false;
+
+ // Try to load a theme if we don't have one.
+ if (empty($context['theme_loaded']) && empty($fatal_error_called))
+ {
+ $fatal_error_called = true;
+ loadTheme();
+ }
+
+ // If we have no theme stuff we can't have the language file...
+ if (empty($context['theme_loaded']))
+ die($error);
+
+ $reload_lang_file = true;
+ // Log the error in the forum's language, but don't waste the time if we aren't logging
+ if ($log || (!empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2))
+ {
+ loadLanguage('Errors', $language);
+ $reload_lang_file = $language != $user_info['language'];
+ $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
+ log_error($error_message, $log);
+ }
+
+ // Load the language file, only if it needs to be reloaded
+ if ($reload_lang_file)
+ {
+ loadLanguage('Errors');
+ $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
+ }
+
+ setup_fatal_error_context($error_message);
+}
+
+// Handler for standard error messages.
+function error_handler($error_level, $error_string, $file, $line)
+{
+ global $settings, $modSettings, $db_show_debug;
+
+ // 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;
+
+ if (strpos($file, 'eval()') !== false && !empty($settings['current_include_filename']))
+ {
+ if (function_exists('debug_backtrace'))
+ {
+ $array = debug_backtrace();
+ for ($i = 0; $i < count($array); $i++)
+ {
+ if ($array[$i]['function'] != 'loadSubTemplate')
+ continue;
+
+ // This is a bug in PHP, with eval, it seems!
+ if (empty($array[$i]['args']))
+ $i++;
+ break;
+ }
+
+ if (isset($array[$i]) && !empty($array[$i]['args']))
+ $file = realpath($settings['current_include_filename']) . ' (' . $array[$i]['args'][0] . ' sub template - eval?)';
+ else
+ $file = realpath($settings['current_include_filename']) . ' (eval?)';
+ }
+ else
+ $file = realpath($settings['current_include_filename']) . ' (eval?)';
+ }
+
+ if (isset($db_show_debug) && $db_show_debug === true)
+ {
+ // Commonly, undefined indexes will occur inside attributes; try to show them anyway!
+ if ($error_level % 255 != E_ERROR)
+ {
+ $temporary = ob_get_contents();
+ if (substr($temporary, -2) == '="')
+ echo '"';
+ }
+
+ // Debugging! This should look like a PHP error message.
+ echo '
+', $error_level % 255 == E_ERROR ? 'Error' : ($error_level % 255 == E_WARNING ? 'Warning' : 'Notice'), ': ', $error_string, ' in ', $file, ' on line ', $line, ' ';
+ }
+
+ $error_type = strpos(strtolower($error_string), 'undefined') !== false ? 'undefined_vars' : 'general';
+
+ $message = log_error($error_level . ': ' . $error_string, $error_type, $file, $line);
+
+ // Let's give integrations a chance to ouput a bit differently
+ call_integration_hook('integrate_output_error', array($message, $error_type, $error_level, $file, $line));
+
+ // Dying on these errors only causes MORE problems (blank pages!)
+ if ($file == 'Unknown')
+ return;
+
+ // If this is an E_ERROR or E_USER_ERROR.... die. Violently so.
+ if ($error_level % 255 == E_ERROR)
+ obExit(false);
+ else
+ return;
+
+ // If this is an E_ERROR, E_USER_ERROR, E_WARNING, or E_USER_WARNING.... die. Violently so.
+ if ($error_level % 255 == E_ERROR || $error_level % 255 == E_WARNING)
+ fatal_error(allowedTo('admin_forum') ? $message : $error_string, false);
+
+ // We should NEVER get to this point. Any fatal error MUST quit, or very bad things can happen.
+ if ($error_level % 255 == E_ERROR)
+ die('Hacking attempt...');
+}
+
+function setup_fatal_error_context($error_message)
+{
+ global $context, $txt, $ssi_on_error_method;
+ static $level = 0;
+
+ // Attempt to prevent a recursive loop.
+ ++$level;
+ if ($level > 1)
+ return false;
+
+ // Maybe they came from dlattach or similar?
+ if (SMF != 'SSI' && empty($context['theme_loaded']))
+ loadTheme();
+
+ // Don't bother indexing errors mate...
+ $context['robot_no_index'] = true;
+
+ if (!isset($context['error_title']))
+ $context['error_title'] = $txt['error_occured'];
+ $context['error_message'] = isset($context['error_message']) ? $context['error_message'] : $error_message;
+
+ if (empty($context['page_title']))
+ $context['page_title'] = $context['error_title'];
+
+ // Display the error message - wireless?
+ if (defined('WIRELESS') && WIRELESS)
+ $context['sub_template'] = WIRELESS_PROTOCOL . '_error';
+ // Load the template and set the sub template.
+ else
+ {
+ loadTemplate('Errors');
+ $context['sub_template'] = 'fatal_error';
+ }
+
+ // If this is SSI, what do they want us to do?
+ if (SMF == 'SSI')
+ {
+ if (!empty($ssi_on_error_method) && $ssi_on_error_method !== true && is_callable($ssi_on_error_method))
+ $ssi_on_error_method();
+ elseif (empty($ssi_on_error_method) || $ssi_on_error_method !== true)
+ loadSubTemplate('fatal_error');
+
+ // No layers?
+ if (empty($ssi_on_error_method) || $ssi_on_error_method !== true)
+ exit;
+ }
+
+ // We want whatever for the header, and a footer. (footer includes sub template!)
+ obExit(null, true, false, true);
+
+ /* DO NOT IGNORE:
+ If you are creating a bridge to SMF or modifying this function, you MUST
+ make ABSOLUTELY SURE that this function quits and DOES NOT RETURN TO NORMAL
+ PROGRAM FLOW. Otherwise, security error messages will not be shown, and
+ your forum will be in a very easily hackable state.
+ */
+ trigger_error('Hacking attempt...', E_USER_ERROR);
+}
+
+// Show an error message for the connection problems.
+function show_db_error($loadavg = false)
+{
+ global $sourcedir, $mbname, $maintenance, $mtitle, $mmessage, $modSettings;
+ global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc;
+
+ // Just check we're not in any buffers, just in case.
+ for ($i = ob_get_level(); $i > 0; $i--)
+ @ob_end_clean();
+
+ // Don't cache this page!
+ header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+ header('Cache-Control: no-cache');
+
+ // Send the right error codes.
+ header('HTTP/1.1 503 Service Temporarily Unavailable');
+ header('Status: 503 Service Temporarily Unavailable');
+ header('Retry-After: 3600');
+
+ if ($loadavg == false)
+ {
+ // For our purposes, we're gonna want this on if at all possible.
+ $modSettings['cache_enable'] = '1';
+
+ if (($temp = cache_get_data('db_last_error', 600)) !== null)
+ $db_last_error = max($db_last_error, $temp);
+
+ if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send))
+ {
+ require_once($sourcedir . '/Subs-Admin.php');
+
+ // Avoid writing to the Settings.php file if at all possible; use shared memory instead.
+ cache_put_data('db_last_error', time(), 600);
+ if (($temp = cache_get_data('db_last_error', 600)) == null)
+ updateLastDatabaseError();
+
+ // Language files aren't loaded yet :(.
+ $db_error = @$smcFunc['db_error']($db_connection);
+ @mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . $smcFunc['db_title'] . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.');
+ }
+ }
+
+ if (!empty($maintenance))
+ echo '
+
+
+
+ ', $mtitle, '
+
+
+
', $mtitle, '
+ ', $mmessage, '
+
+';
+ // If this is a load average problem, display an appropriate message (but we still don't have language files!)
+ elseif ($loadavg)
+ echo '
+
+
+
+ Temporarily Unavailable
+
+
+
Temporarily Unavailable
+ Due to high stress on the server the forum is temporarily unavailable. Please try again later.
+
+';
+ // What to do? Language files haven't and can't be loaded yet...
+ else
+ echo '
+
+
+
+ Connection Problems
+
+
+
Connection Problems
+ Sorry, SMF was unable to connect to the database. This may be caused by the server being busy. Please try again later.
+
+';
+
+ die;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Groups.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Groups.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,967 @@
+ array('GroupList', 'view_groups'),
+ 'members' => array('MembergroupMembers', 'view_groups'),
+ 'requests' => array('GroupRequests', 'group_requests'),
+ );
+
+ // Default to sub action 'index' or 'settings' depending on permissions.
+ $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'index';
+
+ // Get the template stuff up and running.
+ loadLanguage('ManageMembers');
+ loadLanguage('ModerationCenter');
+ loadTemplate('ManageMembergroups');
+
+ // If we can see the moderation center, and this has a mod bar entry, add the mod center bar.
+ if (allowedTo('access_mod_center') || $user_info['mod_cache']['bq'] != '0=1' || $user_info['mod_cache']['gq'] != '0=1' || allowedTo('manage_membergroups'))
+ {
+ require_once($sourcedir . '/ModerationCenter.php');
+ $_GET['area'] = $_REQUEST['sa'] == 'requests' ? 'groups' : 'viewgroups';
+ ModerationMain(true);
+ }
+ // Otherwise add something to the link tree, for normal people.
+ else
+ {
+ isAllowedTo('view_mlist');
+
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=groups',
+ 'name' => $txt['groups'],
+ );
+ }
+
+ // Call the actual function.
+ $subActions[$_REQUEST['sa']][0]();
+}
+
+// This very simply lists the groups, nothing snazy.
+function GroupList()
+{
+ global $txt, $scripturl, $user_profile, $user_info, $context, $settings, $modSettings, $smcFunc, $sourcedir;
+
+ // Yep, find the groups...
+ $request = $smcFunc['db_query']('', '
+ SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden,
+ mg.stars, IFNULL(gm.id_member, 0) AS can_moderate
+ FROM {db_prefix}membergroups AS mg
+ LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
+ WHERE mg.min_posts = {int:min_posts}
+ AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : '
+ AND mg.group_type != {int:is_protected}') . '
+ ORDER BY group_name',
+ array(
+ 'current_member' => $user_info['id'],
+ 'min_posts' => -1,
+ 'mod_group' => 3,
+ 'is_protected' => 1,
+ )
+ );
+ // This is where we store our groups.
+ $context['groups'] = array();
+ $group_ids = array();
+ $context['can_moderate'] = allowedTo('manage_membergroups');
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ // We only list the groups they can see.
+ if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
+ continue;
+
+ $row['stars'] = explode('#', $row['stars']);
+
+ $context['groups'][$row['id_group']] = array(
+ 'id' => $row['id_group'],
+ 'name' => $row['group_name'],
+ 'desc' => $row['description'],
+ 'color' => $row['online_color'],
+ 'type' => $row['group_type'],
+ 'num_members' => 0,
+ 'stars' => !empty($row['stars'][0]) && !empty($row['stars'][1]) ? str_repeat('', $row['stars'][0]) : '',
+ );
+
+ $context['can_moderate'] |= $row['can_moderate'];
+ $group_ids[] = $row['id_group'];
+ }
+ $smcFunc['db_free_result']($request);
+
+ // Count up the members separately...
+ if (!empty($group_ids))
+ {
+ $query = $smcFunc['db_query']('', '
+ SELECT id_group, COUNT(*) AS num_members
+ FROM {db_prefix}members
+ WHERE id_group IN ({array_int:group_list})
+ GROUP BY id_group',
+ array(
+ 'group_list' => $group_ids,
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($query))
+ $context['groups'][$row['id_group']]['num_members'] += $row['num_members'];
+ $smcFunc['db_free_result']($query);
+
+ // Only do additional groups if we can moderate...
+ if ($context['can_moderate'])
+ {
+ $query = $smcFunc['db_query']('', '
+ SELECT mg.id_group, COUNT(*) AS num_members
+ FROM {db_prefix}membergroups AS mg
+ INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_screen}
+ AND mem.id_group != mg.id_group
+ AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
+ WHERE mg.id_group IN ({array_int:group_list})
+ GROUP BY mg.id_group',
+ array(
+ 'group_list' => $group_ids,
+ 'blank_screen' => '',
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($query))
+ $context['groups'][$row['id_group']]['num_members'] += $row['num_members'];
+ $smcFunc['db_free_result']($query);
+ }
+ }
+
+ $context['sub_template'] = 'group_index';
+ $context['page_title'] = $txt['viewing_groups'];
+
+ // Making a list is not hard with this beauty.
+ require_once($sourcedir . '/Subs-List.php');
+
+ // Use the standard templates for showing this.
+ $listOptions = array(
+ 'id' => 'group_lists',
+ 'title' => $context['page_title'],
+ 'get_items' => array(
+ 'function' => 'list_getGroups',
+ ),
+ 'columns' => array(
+ 'group' => array(
+ 'header' => array(
+ 'value' => $txt['name'],
+ ),
+ 'data' => array(
+ 'function' => create_function('$group', '
+ global $scripturl, $context;
+
+ $output = \'\' . $group[\'name\'] . \'\';
+
+ if ($group[\'desc\'])
+ $output .= \'
\' . $group[\'desc\'] . \'
\';
+
+ return $output;
+ '),
+ 'style' => 'width: 50%;',
+ ),
+ ),
+ 'stars' => array(
+ 'header' => array(
+ 'value' => $txt['membergroups_stars'],
+ ),
+ 'data' => array(
+ 'db' => 'stars',
+ ),
+ ),
+ 'moderators' => array(
+ 'header' => array(
+ 'value' => $txt['moderators'],
+ ),
+ 'data' => array(
+ 'function' => create_function('$group', '
+ global $txt;
+
+ return empty($group[\'moderators\']) ? \'\' . $txt[\'membergroups_new_copy_none\'] . \'\' : implode(\', \', $group[\'moderators\']);
+ '),
+ ),
+ ),
+ 'members' => array(
+ 'header' => array(
+ 'value' => $txt['membergroups_members_top'],
+ ),
+ 'data' => array(
+ 'comma_format' => true,
+ 'db' => 'num_members',
+ ),
+ ),
+ ),
+ );
+
+ // Create the request list.
+ createList($listOptions);
+
+ $context['sub_template'] = 'show_list';
+ $context['default_list'] = 'group_lists';
+}
+
+// Get the group information for the list.
+function list_getGroups($start, $items_per_page, $sort)
+{
+ global $smcFunc, $txt, $scripturl, $user_info, $settings;
+
+ // Yep, find the groups...
+ $request = $smcFunc['db_query']('', '
+ SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden,
+ mg.stars, IFNULL(gm.id_member, 0) AS can_moderate
+ FROM {db_prefix}membergroups AS mg
+ LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
+ WHERE mg.min_posts = {int:min_posts}
+ AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : '
+ AND mg.group_type != {int:is_protected}') . '
+ ORDER BY group_name',
+ array(
+ 'current_member' => $user_info['id'],
+ 'min_posts' => -1,
+ 'mod_group' => 3,
+ 'is_protected' => 1,
+ )
+ );
+ // Start collecting the data.
+ $groups = array();
+ $group_ids = array();
+ $context['can_moderate'] = allowedTo('manage_membergroups');
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ // We only list the groups they can see.
+ if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
+ continue;
+
+ $row['stars'] = explode('#', $row['stars']);
+
+ $groups[$row['id_group']] = array(
+ 'id' => $row['id_group'],
+ 'name' => $row['group_name'],
+ 'desc' => $row['description'],
+ 'color' => $row['online_color'],
+ 'type' => $row['group_type'],
+ 'num_members' => 0,
+ 'moderators' => array(),
+ 'stars' => !empty($row['stars'][0]) && !empty($row['stars'][1]) ? str_repeat('', $row['stars'][0]) : '',
+ );
+
+ $context['can_moderate'] |= $row['can_moderate'];
+ $group_ids[] = $row['id_group'];
+ }
+ $smcFunc['db_free_result']($request);
+
+ // Count up the members separately...
+ if (!empty($group_ids))
+ {
+ $query = $smcFunc['db_query']('', '
+ SELECT id_group, COUNT(*) AS num_members
+ FROM {db_prefix}members
+ WHERE id_group IN ({array_int:group_list})
+ GROUP BY id_group',
+ array(
+ 'group_list' => $group_ids,
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($query))
+ $groups[$row['id_group']]['num_members'] += $row['num_members'];
+ $smcFunc['db_free_result']($query);
+
+ // Only do additional groups if we can moderate...
+ if ($context['can_moderate'])
+ {
+ $query = $smcFunc['db_query']('', '
+ SELECT mg.id_group, COUNT(*) AS num_members
+ FROM {db_prefix}membergroups AS mg
+ INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_screen}
+ AND mem.id_group != mg.id_group
+ AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
+ WHERE mg.id_group IN ({array_int:group_list})
+ GROUP BY mg.id_group',
+ array(
+ 'group_list' => $group_ids,
+ 'blank_screen' => '',
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($query))
+ $groups[$row['id_group']]['num_members'] += $row['num_members'];
+ $smcFunc['db_free_result']($query);
+ }
+ }
+
+ // Get any group moderators.
+ // Count up the members separately...
+ if (!empty($group_ids))
+ {
+ $query = $smcFunc['db_query']('', '
+ SELECT mods.id_group, mods.id_member, mem.member_name, mem.real_name
+ FROM {db_prefix}group_moderators AS mods
+ INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
+ WHERE mods.id_group IN ({array_int:group_list})',
+ array(
+ 'group_list' => $group_ids,
+ )
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($query))
+ $groups[$row['id_group']]['moderators'][] = '' . $row['real_name'] . '';
+ $smcFunc['db_free_result']($query);
+ }
+
+ return $groups;
+}
+
+// How many groups are there that are visible?
+function list_getGroupCount()
+{
+ global $smcFunc;
+
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(id_group) AS group_count
+ FROM {db_prefix}membergroups
+ WHERE mg.min_posts = {int:min_posts}
+ AND mg.id_group != {int:mod_group}' . (allowedTo('admin_forum') ? '' : '
+ AND mg.group_type != {int:is_protected}'),
+ array(
+ 'min_posts' => -1,
+ 'mod_group' => 3,
+ 'is_protected' => 1,
+ )
+ );
+ list ($group_count) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ return $group_count;
+}
+
+// Display members of a group, and allow adding of members to a group. Silly function name though ;)
+function MembergroupMembers()
+{
+ global $txt, $scripturl, $context, $modSettings, $sourcedir, $user_info, $settings, $smcFunc;
+
+ $_REQUEST['group'] = isset($_REQUEST['group']) ? (int) $_REQUEST['group'] : 0;
+
+ // No browsing of guests, membergroup 0 or moderators.
+ if (in_array($_REQUEST['group'], array(-1, 0, 3)))
+ fatal_lang_error('membergroup_does_not_exist', false);
+
+ // Load up the group details.
+ $request = $smcFunc['db_query']('', '
+ SELECT id_group AS id, group_name AS name, CASE WHEN min_posts = {int:min_posts} THEN 1 ELSE 0 END AS assignable, hidden, online_color,
+ stars, description, CASE WHEN min_posts != {int:min_posts} THEN 1 ELSE 0 END AS is_post_group, group_type
+ FROM {db_prefix}membergroups
+ WHERE id_group = {int:id_group}
+ LIMIT 1',
+ array(
+ 'min_posts' => -1,
+ 'id_group' => $_REQUEST['group'],
+ )
+ );
+ // Doesn't exist?
+ if ($smcFunc['db_num_rows']($request) == 0)
+ fatal_lang_error('membergroup_does_not_exist', false);
+ $context['group'] = $smcFunc['db_fetch_assoc']($request);
+ $smcFunc['db_free_result']($request);
+
+ // Fix the stars.
+ $context['group']['stars'] = explode('#', $context['group']['stars']);
+ $context['group']['stars'] = !empty($context['group']['stars'][0]) && !empty($context['group']['stars'][1]) ? str_repeat('', $context['group']['stars'][0]) : '';
+ $context['group']['can_moderate'] = allowedTo('manage_membergroups') && (allowedTo('admin_forum') || $context['group']['group_type'] != 1);
+
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=groups;sa=members;group=' . $context['group']['id'],
+ 'name' => $context['group']['name'],
+ );
+
+ // Load all the group moderators, for fun.
+ $request = $smcFunc['db_query']('', '
+ SELECT mem.id_member, mem.real_name
+ FROM {db_prefix}group_moderators AS mods
+ INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
+ WHERE mods.id_group = {int:id_group}',
+ array(
+ 'id_group' => $_REQUEST['group'],
+ )
+ );
+ $context['group']['moderators'] = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ $context['group']['moderators'][] = array(
+ 'id' => $row['id_member'],
+ 'name' => $row['real_name']
+ );
+
+ if ($user_info['id'] == $row['id_member'] && $context['group']['group_type'] != 1)
+ $context['group']['can_moderate'] = true;
+ }
+ $smcFunc['db_free_result']($request);
+
+ // If this group is hidden then it can only "exists" if the user can moderate it!
+ if ($context['group']['hidden'] && !$context['group']['can_moderate'])
+ fatal_lang_error('membergroup_does_not_exist', false);
+
+ // You can only assign membership if you are the moderator and/or can manage groups!
+ if (!$context['group']['can_moderate'])
+ $context['group']['assignable'] = 0;
+ // Non-admins cannot assign admins.
+ elseif ($context['group']['id'] == 1 && !allowedTo('admin_forum'))
+ $context['group']['assignable'] = 0;
+
+ // Removing member from group?
+ if (isset($_POST['remove']) && !empty($_REQUEST['rem']) && is_array($_REQUEST['rem']) && $context['group']['assignable'])
+ {
+ checkSession();
+
+ // Make sure we're dealing with integers only.
+ foreach ($_REQUEST['rem'] as $key => $group)
+ $_REQUEST['rem'][$key] = (int) $group;
+
+ require_once($sourcedir . '/Subs-Membergroups.php');
+ removeMembersFromGroups($_REQUEST['rem'], $_REQUEST['group'], true);
+ }
+ // Must be adding new members to the group...
+ elseif (isset($_REQUEST['add']) && (!empty($_REQUEST['toAdd']) || !empty($_REQUEST['member_add'])) && $context['group']['assignable'])
+ {
+ checkSession();
+
+ $member_query = array();
+ $member_parameters = array();
+
+ // Get all the members to be added... taking into account names can be quoted ;)
+ $_REQUEST['toAdd'] = strtr($smcFunc['htmlspecialchars']($_REQUEST['toAdd'], ENT_QUOTES), array('"' => '"'));
+ preg_match_all('~"([^"]+)"~', $_REQUEST['toAdd'], $matches);
+ $member_names = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_REQUEST['toAdd']))));
+
+ foreach ($member_names as $index => $member_name)
+ {
+ $member_names[$index] = trim($smcFunc['strtolower']($member_names[$index]));
+
+ if (strlen($member_names[$index]) == 0)
+ unset($member_names[$index]);
+ }
+
+ // Any passed by ID?
+ $member_ids = array();
+ if (!empty($_REQUEST['member_add']))
+ foreach ($_REQUEST['member_add'] as $id)
+ if ($id > 0)
+ $member_ids[] = (int) $id;
+
+ // Construct the query pelements.
+ if (!empty($member_ids))
+ {
+ $member_query[] = 'id_member IN ({array_int:member_ids})';
+ $member_parameters['member_ids'] = $member_ids;
+ }
+ if (!empty($member_names))
+ {
+ $member_query[] = 'LOWER(member_name) IN ({array_string:member_names})';
+ $member_query[] = 'LOWER(real_name) IN ({array_string:member_names})';
+ $member_parameters['member_names'] = $member_names;
+ }
+
+ $members = array();
+ if (!empty($member_query))
+ {
+ $request = $smcFunc['db_query']('', '
+ SELECT id_member
+ FROM {db_prefix}members
+ WHERE (' . implode(' OR ', $member_query) . ')
+ AND id_group != {int:id_group}
+ AND FIND_IN_SET({int:id_group}, additional_groups) = 0',
+ array_merge($member_parameters, array(
+ 'id_group' => $_REQUEST['group'],
+ ))
+ );
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ $members[] = $row['id_member'];
+ $smcFunc['db_free_result']($request);
+ }
+
+ // !!! Add $_POST['additional'] to templates!
+
+ // Do the updates...
+ if (!empty($members))
+ {
+ require_once($sourcedir . '/Subs-Membergroups.php');
+ addMembersToGroup($members, $_REQUEST['group'], isset($_POST['additional']) || $context['group']['hidden'] ? 'only_additional' : 'auto', true);
+ }
+ }
+
+ // Sort out the sorting!
+ $sort_methods = array(
+ 'name' => 'real_name',
+ 'email' => allowedTo('moderate_forum') ? 'email_address' : 'hide_email ' . (isset($_REQUEST['desc']) ? 'DESC' : 'ASC') . ', email_address',
+ 'active' => 'last_login',
+ 'registered' => 'date_registered',
+ 'posts' => 'posts',
+ );
+
+ // They didn't pick one, default to by name..
+ if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']]))
+ {
+ $context['sort_by'] = 'name';
+ $querySort = 'real_name';
+ }
+ // Otherwise default to ascending.
+ else
+ {
+ $context['sort_by'] = $_REQUEST['sort'];
+ $querySort = $sort_methods[$_REQUEST['sort']];
+ }
+
+ $context['sort_direction'] = isset($_REQUEST['desc']) ? 'down' : 'up';
+
+ // The where on the query is interesting. Non-moderators should only see people who are in this group as primary.
+ if ($context['group']['can_moderate'])
+ $where = $context['group']['is_post_group'] ? 'id_post_group = {int:group}' : 'id_group = {int:group} OR FIND_IN_SET({int:group}, additional_groups) != 0';
+ else
+ $where = $context['group']['is_post_group'] ? 'id_post_group = {int:group}' : 'id_group = {int:group}';
+
+ // Count members of the group.
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(*)
+ FROM {db_prefix}members
+ WHERE ' . $where,
+ array(
+ 'group' => $_REQUEST['group'],
+ )
+ );
+ list ($context['total_members']) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+ $context['total_members'] = comma_format($context['total_members']);
+
+ // Create the page index.
+ $context['page_index'] = constructPageIndex($scripturl . '?action=' . ($context['group']['can_moderate'] ? 'moderate;area=viewgroups' : 'groups') . ';sa=members;group=' . $_REQUEST['group'] . ';sort=' . $context['sort_by'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $context['total_members'], $modSettings['defaultMaxMembers']);
+ $context['start'] = $_REQUEST['start'];
+ $context['can_moderate_forum'] = allowedTo('moderate_forum');
+
+ // Load up all members of this group.
+ $request = $smcFunc['db_query']('', '
+ SELECT id_member, member_name, real_name, email_address, member_ip, date_registered, last_login,
+ hide_email, posts, is_activated, real_name
+ FROM {db_prefix}members
+ WHERE ' . $where . '
+ ORDER BY ' . $querySort . ' ' . ($context['sort_direction'] == 'down' ? 'DESC' : 'ASC') . '
+ LIMIT ' . $context['start'] . ', ' . $modSettings['defaultMaxMembers'],
+ array(
+ 'group' => $_REQUEST['group'],
+ )
+ );
+ $context['members'] = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ $last_online = empty($row['last_login']) ? $txt['never'] : timeformat($row['last_login']);
+
+ // Italicize the online note if they aren't activated.
+ if ($row['is_activated'] % 10 != 1)
+ $last_online = '' . $last_online . '';
+
+ $context['members'][] = array(
+ 'id' => $row['id_member'],
+ 'name' => '' . $row['real_name'] . '',
+ 'email' => $row['email_address'],
+ 'show_email' => showEmailAddress(!empty($row['hide_email']), $row['id_member']),
+ 'ip' => '' . $row['member_ip'] . '',
+ 'registered' => timeformat($row['date_registered']),
+ 'last_online' => $last_online,
+ 'posts' => comma_format($row['posts']),
+ 'is_activated' => $row['is_activated'] % 10 == 1,
+ );
+ }
+ $smcFunc['db_free_result']($request);
+
+ // Select the template.
+ $context['sub_template'] = 'group_members';
+ $context['page_title'] = $txt['membergroups_members_title'] . ': ' . $context['group']['name'];
+}
+
+// Show and manage all group requests.
+function GroupRequests()
+{
+ global $txt, $context, $scripturl, $user_info, $sourcedir, $smcFunc, $modSettings, $language;
+
+ // Set up the template stuff...
+ $context['page_title'] = $txt['mc_group_requests'];
+ $context['sub_template'] = 'show_list';
+
+ // Verify we can be here.
+ if ($user_info['mod_cache']['gq'] == '0=1')
+ isAllowedTo('manage_membergroups');
+
+ // Normally, we act normally...
+ $where = $user_info['mod_cache']['gq'] == '1=1' || $user_info['mod_cache']['gq'] == '0=1' ? $user_info['mod_cache']['gq'] : 'lgr.' . $user_info['mod_cache']['gq'];
+ $where_parameters = array();
+
+ // We've submitted?
+ if (isset($_POST[$context['session_var']]) && !empty($_POST['groupr']) && !empty($_POST['req_action']))
+ {
+ checkSession('post');
+
+ // Clean the values.
+ foreach ($_POST['groupr'] as $k => $request)
+ $_POST['groupr'][$k] = (int) $request;
+
+ // If we are giving a reason (And why shouldn't we?), then we don't actually do much.
+ if ($_POST['req_action'] == 'reason')
+ {
+ // Different sub template...
+ $context['sub_template'] = 'group_request_reason';
+ // And a limitation. We don't care that the page number bit makes no sense, as we don't need it!
+ $where .= ' AND lgr.id_request IN ({array_int:request_ids})';
+ $where_parameters['request_ids'] = $_POST['groupr'];
+
+ $context['group_requests'] = list_getGroupRequests(0, $modSettings['defaultMaxMessages'], 'lgr.id_request', $where, $where_parameters);
+
+ // Let obExit etc sort things out.
+ obExit();
+ }
+ // Otherwise we do something!
+ else
+ {
+ // Get the details of all the members concerned...
+ $request = $smcFunc['db_query']('', '
+ SELECT lgr.id_request, lgr.id_member, lgr.id_group, mem.email_address, mem.id_group AS primary_group,
+ mem.additional_groups AS additional_groups, mem.lngfile, mem.member_name, mem.notify_types,
+ mg.hidden, mg.group_name
+ FROM {db_prefix}log_group_requests AS lgr
+ INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
+ INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
+ WHERE ' . $where . '
+ AND lgr.id_request IN ({array_int:request_list})
+ ORDER BY mem.lngfile',
+ array(
+ 'request_list' => $_POST['groupr'],
+ )
+ );
+ $email_details = array();
+ $group_changes = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ $row['lngfile'] = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
+
+ // If we are approving work out what their new group is.
+ if ($_POST['req_action'] == 'approve')
+ {
+ // For people with more than one request at once.
+ if (isset($group_changes[$row['id_member']]))
+ {
+ $row['additional_groups'] = $group_changes[$row['id_member']]['add'];
+ $row['primary_group'] = $group_changes[$row['id_member']]['primary'];
+ }
+ else
+ $row['additional_groups'] = explode(',', $row['additional_groups']);
+
+ // Don't have it already?
+ if ($row['primary_group'] == $row['id_group'] || in_array($row['id_group'], $row['additional_groups']))
+ continue;
+
+ // Should it become their primary?
+ if ($row['primary_group'] == 0 && $row['hidden'] == 0)
+ $row['primary_group'] = $row['id_group'];
+ else
+ $row['additional_groups'][] = $row['id_group'];
+
+ // Add them to the group master list.
+ $group_changes[$row['id_member']] = array(
+ 'primary' => $row['primary_group'],
+ 'add' => $row['additional_groups'],
+ );
+ }
+
+ // Add required information to email them.
+ if ($row['notify_types'] != 4)
+ $email_details[] = array(
+ 'rid' => $row['id_request'],
+ 'member_id' => $row['id_member'],
+ 'member_name' => $row['member_name'],
+ 'group_id' => $row['id_group'],
+ 'group_name' => $row['group_name'],
+ 'email' => $row['email_address'],
+ 'language' => $row['lngfile'],
+ );
+ }
+ $smcFunc['db_free_result']($request);
+
+ // Remove the evidence...
+ $smcFunc['db_query']('', '
+ DELETE FROM {db_prefix}log_group_requests
+ WHERE id_request IN ({array_int:request_list})',
+ array(
+ 'request_list' => $_POST['groupr'],
+ )
+ );
+
+ // Ensure everyone who is online gets their changes right away.
+ updateSettings(array('settings_updated' => time()));
+
+ if (!empty($email_details))
+ {
+ require_once($sourcedir . '/Subs-Post.php');
+
+ // They are being approved?
+ if ($_POST['req_action'] == 'approve')
+ {
+ // Make the group changes.
+ foreach ($group_changes as $id => $groups)
+ {
+ // Sanity check!
+ foreach ($groups['add'] as $key => $value)
+ if ($value == 0 || trim($value) == '')
+ unset($groups['add'][$key]);
+
+ $smcFunc['db_query']('', '
+ UPDATE {db_prefix}members
+ SET id_group = {int:primary_group}, additional_groups = {string:additional_groups}
+ WHERE id_member = {int:selected_member}',
+ array(
+ 'primary_group' => $groups['primary'],
+ 'selected_member' => $id,
+ 'additional_groups' => implode(',', $groups['add']),
+ )
+ );
+ }
+
+ $lastLng = $user_info['language'];
+ foreach ($email_details as $email)
+ {
+ $replacements = array(
+ 'USERNAME' => $email['member_name'],
+ 'GROUPNAME' => $email['group_name'],
+ );
+
+ $emaildata = loadEmailTemplate('mc_group_approve', $replacements, $email['language']);
+
+ sendmail($email['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
+ }
+ }
+ // Otherwise, they are getting rejected (With or without a reason).
+ else
+ {
+ // Same as for approving, kind of.
+ $lastLng = $user_info['language'];
+ foreach ($email_details as $email)
+ {
+ $custom_reason = isset($_POST['groupreason']) && isset($_POST['groupreason'][$email['rid']]) ? $_POST['groupreason'][$email['rid']] : '';
+
+ $replacements = array(
+ 'USERNAME' => $email['member_name'],
+ 'GROUPNAME' => $email['group_name'],
+ );
+
+ if (!empty($custom_reason))
+ $replacements['REASON'] = $custom_reason;
+
+ $emaildata = loadEmailTemplate(empty($custom_reason) ? 'mc_group_reject' : 'mc_group_reject_reason', $replacements, $email['language']);
+
+ sendmail($email['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
+ }
+ }
+ }
+
+ // Restore the current language.
+ loadLanguage('ModerationCenter');
+ }
+ }
+
+ // We're going to want this for making our list.
+ require_once($sourcedir . '/Subs-List.php');
+
+ // This is all the information required for a group listing.
+ $listOptions = array(
+ 'id' => 'group_request_list',
+ 'title' => $txt['mc_group_requests'],
+ 'width' => '100%',
+ 'items_per_page' => $modSettings['defaultMaxMessages'],
+ 'no_items_label' => $txt['mc_groupr_none_found'],
+ 'base_href' => $scripturl . '?action=groups;sa=requests',
+ 'default_sort_col' => 'member',
+ 'get_items' => array(
+ 'function' => 'list_getGroupRequests',
+ 'params' => array(
+ $where,
+ $where_parameters,
+ ),
+ ),
+ 'get_count' => array(
+ 'function' => 'list_getGroupRequestCount',
+ 'params' => array(
+ $where,
+ $where_parameters,
+ ),
+ ),
+ 'columns' => array(
+ 'member' => array(
+ 'header' => array(
+ 'value' => $txt['mc_groupr_member'],
+ ),
+ 'data' => array(
+ 'db' => 'member_link',
+ ),
+ 'sort' => array(
+ 'default' => 'mem.member_name',
+ 'reverse' => 'mem.member_name DESC',
+ ),
+ ),
+ 'group' => array(
+ 'header' => array(
+ 'value' => $txt['mc_groupr_group'],
+ ),
+ 'data' => array(
+ 'db' => 'group_link',
+ ),
+ 'sort' => array(
+ 'default' => 'mg.group_name',
+ 'reverse' => 'mg.group_name DESC',
+ ),
+ ),
+ 'reason' => array(
+ 'header' => array(
+ 'value' => $txt['mc_groupr_reason'],
+ ),
+ 'data' => array(
+ 'db' => 'reason',
+ ),
+ ),
+ 'action' => array(
+ 'header' => array(
+ 'value' => '',
+ 'style' => 'width: 4%;',
+ ),
+ 'data' => array(
+ 'sprintf' => array(
+ 'format' => '',
+ 'params' => array(
+ 'id' => false,
+ ),
+ ),
+ 'style' => 'text-align: center;',
+ ),
+ ),
+ ),
+ 'form' => array(
+ 'href' => $scripturl . '?action=groups;sa=requests',
+ 'include_sort' => true,
+ 'include_start' => true,
+ 'hidden_fields' => array(
+ $context['session_var'] => $context['session_id'],
+ ),
+ ),
+ 'additional_rows' => array(
+ array(
+ 'position' => 'bottom_of_list',
+ 'value' => '
+
+ ',
+ 'align' => 'right',
+ ),
+ ),
+ );
+
+ // Create the request list.
+ createList($listOptions);
+
+ $context['default_list'] = 'group_request_list';
+}
+
+function list_getGroupRequestCount($where, $where_parameters)
+{
+ global $smcFunc;
+
+ $request = $smcFunc['db_query']('', '
+ SELECT COUNT(*)
+ FROM {db_prefix}log_group_requests AS lgr
+ WHERE ' . $where,
+ array_merge($where_parameters, array(
+ ))
+ );
+ list ($totalRequests) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+
+ return $totalRequests;
+}
+
+function list_getGroupRequests($start, $items_per_page, $sort, $where, $where_parameters)
+{
+ global $smcFunc, $txt, $scripturl;
+
+ $request = $smcFunc['db_query']('', '
+ SELECT lgr.id_request, lgr.id_member, lgr.id_group, lgr.time_applied, lgr.reason,
+ mem.member_name, mg.group_name, mg.online_color, mem.real_name
+ FROM {db_prefix}log_group_requests AS lgr
+ INNER JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
+ INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
+ WHERE ' . $where . '
+ ORDER BY {raw:sort}
+ LIMIT ' . $start . ', ' . $items_per_page,
+ array_merge($where_parameters, array(
+ 'sort' => $sort,
+ ))
+ );
+ $group_requests = array();
+ while ($row = $smcFunc['db_fetch_assoc']($request))
+ {
+ $group_requests[] = array(
+ 'id' => $row['id_request'],
+ 'member_link' => '' . $row['real_name'] . '',
+ 'group_link' => '' . $row['group_name'] . '',
+ 'reason' => censorText($row['reason']),
+ 'time_submitted' => timeformat($row['time_applied']),
+ );
+ }
+ $smcFunc['db_free_result']($request);
+
+ return $group_requests;
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Help.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Help.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,111 @@
+ 'Registering',
+ 'logging_in' => 'Logging_In',
+ 'profile' => 'Profile',
+ 'search' => 'Search',
+ 'posting' => 'Posting',
+ 'bbc' => 'Bulletin_board_code',
+ 'personal_messages' => 'Personal_messages',
+ 'memberlist' => 'Memberlist',
+ 'calendar' => 'Calendar',
+ 'features' => 'Features',
+ );
+
+ // Build the link tree.
+ $context['linktree'][] = array(
+ 'url' => $scripturl . '?action=help',
+ 'name' => $txt['help'],
+ );
+
+ // Lastly, some minor template stuff.
+ $context['page_title'] = $txt['manual_smf_user_help'];
+ $context['sub_template'] = 'manual';
+}
+
+// Show some of the more detailed help to give the admin an idea...
+function ShowAdminHelp()
+{
+ global $txt, $helptxt, $context, $scripturl;
+
+ if (!isset($_GET['help']) || !is_string($_GET['help']))
+ fatal_lang_error('no_access', false);
+
+ if (!isset($helptxt))
+ $helptxt = array();
+
+ // Load the admin help language file and template.
+ loadLanguage('Help');
+
+ // Permission specific help?
+ if (isset($_GET['help']) && substr($_GET['help'], 0, 14) == 'permissionhelp')
+ loadLanguage('ManagePermissions');
+
+ loadTemplate('Help');
+
+ // Set the page title to something relevant.
+ $context['page_title'] = $context['forum_name'] . ' - ' . $txt['help'];
+
+ // Don't show any template layers, just the popup sub template.
+ $context['template_layers'] = array();
+ $context['sub_template'] = 'popup';
+
+ // What help string should be used?
+ if (isset($helptxt[$_GET['help']]))
+ $context['help_text'] = $helptxt[$_GET['help']];
+ elseif (isset($txt[$_GET['help']]))
+ $context['help_text'] = $txt[$_GET['help']];
+ else
+ $context['help_text'] = $_GET['help'];
+
+ // Does this text contain a link that we should fill in?
+ if (preg_match('~%([0-9]+\$)?s\?~', $context['help_text'], $match))
+ $context['help_text'] = sprintf($context['help_text'], $scripturl, $context['session_id'], $context['session_var']);
+}
+
+?>
\ No newline at end of file
diff -r 72f59aa7e503 -r e3e11437ecea forum/Sources/Karma.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/forum/Sources/Karma.php Sun Jul 07 11:25:48 2013 +0200
@@ -0,0 +1,204 @@
+ {int:wait_time}',
+ array(
+ 'wait_time' => (int) ($modSettings['karmaWaitTime'] * 3600),
+ 'current_time' => time(),
+ )
+ );
+
+ // Start off with no change in karma.
+ $action = 0;
+
+ // Not an administrator... or one who is restricted as well.
+ if (!empty($modSettings['karmaTimeRestrictAdmins']) || !allowedTo('moderate_forum'))
+ {
+ // Find out if this user has done this recently...
+ $request = $smcFunc['db_query']('', '
+ SELECT action
+ FROM {db_prefix}log_karma
+ WHERE id_target = {int:id_target}
+ AND id_executor = {int:current_member}
+ LIMIT 1',
+ array(
+ 'current_member' => $user_info['id'],
+ 'id_target' => $_REQUEST['uid'],
+ )
+ );
+ if ($smcFunc['db_num_rows']($request) > 0)
+ list ($action) = $smcFunc['db_fetch_row']($request);
+ $smcFunc['db_free_result']($request);
+ }
+
+ // They haven't, not before now, anyhow.
+ if (empty($action) || empty($modSettings['karmaWaitTime']))
+ {
+ // Put it in the log.
+ $smcFunc['db_insert']('replace',
+ '{db_prefix}log_karma',
+ array('action' => 'int', 'id_target' => 'int', 'id_executor' => 'int', 'log_time' => 'int'),
+ array($dir, $_REQUEST['uid'], $user_info['id'], time()),
+ array('id_target', 'id_executor')
+ );
+
+ // Change by one.
+ updateMemberData($_REQUEST['uid'], array($dir == 1 ? 'karma_good' : 'karma_bad' => '+'));
+ }
+ else
+ {
+ // If you are gonna try to repeat.... don't allow it.
+ if ($action == $dir)
+ fatal_lang_error('karma_wait_time', false, array($modSettings['karmaWaitTime'], $txt['hours']));
+
+ // You decided to go back on your previous choice?
+ $smcFunc['db_query']('', '
+ UPDATE {db_prefix}log_karma
+ SET action = {int:action}, log_time = {int:current_time}
+ WHERE id_target = {int:id_target}
+ AND id_executor = {int:current_member}',
+ array(
+ 'current_member' => $user_info['id'],
+ 'action' => $dir,
+ 'current_time' => time(),
+ 'id_target' => $_REQUEST['uid'],
+ )
+ );
+
+ // It was recently changed the OTHER way... so... reverse it!
+ if ($dir == 1)
+ updateMemberData($_REQUEST['uid'], array('karma_good' => '+', 'karma_bad' => '-'));
+ else
+ updateMemberData($_REQUEST['uid'], array('karma_bad' => '+', 'karma_good' => '-'));
+ }
+
+ // Figure out where to go back to.... the topic?
+ if (!empty($topic))
+ redirectexit('topic=' . $topic . '.' . $_REQUEST['start'] . '#msg' . (int) $_REQUEST['m']);
+ // Hrm... maybe a personal message?
+ elseif (isset($_REQUEST['f']))
+ redirectexit('action=pm;f=' . $_REQUEST['f'] . ';start=' . $_REQUEST['start'] . (isset($_REQUEST['l']) ? ';l=' . (int) $_REQUEST['l'] : '') . (isset($_REQUEST['pm']) ? '#' . (int) $_REQUEST['pm'] : ''));
+ // JavaScript as a last resort.
+ else
+ {
+ echo '
+
+
+ ...
+
+
+ «
+';
+
+ obExit(false);
+ }
+}
+
+// What's this? I dunno, what are you talking about? Never seen this before, nope. No siree.
+function BookOfUnknown()
+{
+ global $context;
+
+ if (strpos($_GET['action'], 'mozilla') !== false && !$context['browser']['is_gecko'])
+ redirectexit('http://www.getfirefox.com/');
+ elseif (strpos($_GET['action'], 'mozilla') !== false)
+ redirectexit('about:mozilla');
+
+ echo '
+
+
+ The Book of Unknown, ', @$_GET['verse'] == '2:18' ? '2:18' : '4:16', '
+
+
+
+
';
+ if (@$_GET['verse'] == '2:18')
+ echo '
+ Woe, it was that his name wasn\'t known, that he came in mystery, and was recognized by none. And it became to be in those days something. Something not yet unknown to mankind. And thus what was to be known the secret project began into its existence. Henceforth the opposition was only weary and fearful, for now their match was at arms against them.';
+ else
+ echo '
+ And it came to pass that the unbelievers dwindled in number and saw rise of many proselytizers, and the opposition found fear in the face of the x and the j while those who stood with the something grew stronger and came together. Still, this was only the beginning, and what lay in the future was unknown to all, even those on the right side.';
+ echo '
+
';
+ }
+
+ loadTemplate($template_name);
+ }
+ // Cause an error otherwise.
+ elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal)
+ fatal_lang_error('theme_template_error', 'template', array((string) $template_name));
+ elseif ($fatal)
+ die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load Themes/default/%s.template.php!', (string) $template_name), 'template'));
+ else
+ return false;
+}
+
+// Load a sub template... fatal is for templates that shouldn't get a 'pretty' error screen.
+function loadSubTemplate($sub_template_name, $fatal = false)
+{
+ global $context, $settings, $options, $txt, $db_show_debug;
+
+ if ($db_show_debug === true)
+ $context['debug']['sub_templates'][] = $sub_template_name;
+
+ // Figure out what the template function is named.
+ $theme_function = 'template_' . $sub_template_name;
+ if (function_exists($theme_function))
+ $theme_function();
+ elseif ($fatal === false)
+ fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name));
+ elseif ($fatal !== 'ignore')
+ die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load the %s sub template!', (string) $sub_template_name), 'template'));
+
+ // Are we showing debugging for templates? Just make sure not to do it before the doctype...
+ if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml']))
+ {
+ echo '
+
---- ', $sub_template_name, ' ends ----
';
+ }
+}
+
+// Load a language file. Tries the current and default themes as well as the user and global languages.
+function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
+{
+ global $user_info, $language, $settings, $context, $modSettings;
+ global $cachedir, $db_show_debug, $sourcedir, $txt;
+ static $already_loaded = array();
+
+ // Default to the user's language.
+ if ($lang == '')
+ $lang = isset($user_info['language']) ? $user_info['language'] : $language;
+
+ // Do we want the English version of language file as fallback?
+ if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
+ loadLanguage($template_name, 'english', false);
+
+ if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
+ return $lang;
+
+ // Make sure we have $settings - if not we're in trouble and need to find it!
+ if (empty($settings['default_theme_dir']))
+ {
+ require_once($sourcedir . '/ScheduledTasks.php');
+ loadEssentialThemeData();
+ }
+
+ // What theme are we in?
+ $theme_name = basename($settings['theme_url']);
+ if (empty($theme_name))
+ $theme_name = 'unknown';
+
+ // For each file open it up and write it out!
+ foreach (explode('+', $template_name) as $template)
+ {
+ // Obviously, the current theme is most important to check.
+ $attempts = array(
+ array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
+ array($settings['theme_dir'], $template, $language, $settings['theme_url']),
+ );
+
+ // Do we have a base theme to worry about?
+ if (isset($settings['base_theme_dir']))
+ {
+ $attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
+ $attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
+ }
+
+ // Fall back on the default theme if necessary.
+ $attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
+ $attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
+
+ // Fall back on the English language if none of the preferred languages can be found.
+ if (!in_array('english', array($lang, $language)))
+ {
+ $attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
+ $attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
+ }
+
+ // Try to find the language file.
+ $found = false;
+ foreach ($attempts as $k => $file)
+ {
+ if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
+ {
+ // Include it!
+ template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
+
+ // Note that we found it.
+ $found = true;
+
+ break;
+ }
+ }
+
+ // That couldn't be found! Log the error, but *try* to continue normally.
+ if (!$found && $fatal)
+ {
+ log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
+ break;
+ }
+ }
+
+ // Keep track of what we're up to soldier.
+ if ($db_show_debug === true)
+ $context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')';
+
+ // Remember what we have loaded, and in which language.
+ $already_loaded[$template_name] = $lang;
+
+ // Return the language actually loaded.
+ return $lang;
+}
+
+// Get all parent boards (requires first parent as parameter)
+function getBoardParents($id_parent)
+{
+ global $scripturl, $smcFunc;
+
+ // First check if we have this cached already.
+ if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null)
+ {
+ $boards = array();
+ $original_parent = $id_parent;
+
+ // Loop while the parent is non-zero.
+ while ($id_parent != 0)
+ {
+ $result = $smcFunc['db_query']('', '
+ SELECT
+ b.id_parent, b.name, {int:board_parent} AS id_board, IFNULL(mem.id_member, 0) AS id_moderator,
+ mem.real_name, b.child_level
+ FROM {db_prefix}boards AS b
+ LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
+ LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
+ WHERE b.id_board = {int:board_parent}',
+ array(
+ 'board_parent' => $id_parent,
+ )
+ );
+ // In the EXTREMELY unlikely event this happens, give an error message.
+ if ($smcFunc['db_num_rows']($result) == 0)
+ fatal_lang_error('parent_not_found', 'critical');
+ while ($row = $smcFunc['db_fetch_assoc']($result))
+ {
+ if (!isset($boards[$row['id_board']]))
+ {
+ $id_parent = $row['id_parent'];
+ $boards[$row['id_board']] = array(
+ 'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
+ 'name' => $row['name'],
+ 'level' => $row['child_level'],
+ 'moderators' => array()
+ );
+ }
+ // If a moderator exists for this board, add that moderator for all children too.
+ if (!empty($row['id_moderator']))
+ foreach ($boards as $id => $dummy)
+ {
+ $boards[$id]['moderators'][$row['id_moderator']] = array(
+ 'id' => $row['id_moderator'],
+ 'name' => $row['real_name'],
+ 'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
+ 'link' => '' . $row['real_name'] . ''
+ );
+ }
+ }
+ $smcFunc['db_free_result']($result);
+ }
+
+ cache_put_data('board_parents-' . $original_parent, $boards, 480);
+ }
+
+ return $boards;
+}
+
+// Attempt to reload our languages.
+function getLanguages($use_cache = true, $favor_utf8 = true)
+{
+ global $context, $smcFunc, $settings, $modSettings;
+
+ // Either we don't use the cache, or its expired.
+ if (!$use_cache || ($context['languages'] = cache_get_data('known_languages' . ($favor_utf8 ? '' : '_all'), !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null)
+ {
+ // If we don't have our theme information yet, lets get it.
+ if (empty($settings['default_theme_dir']))
+ loadTheme(0, false);
+
+ // Default language directories to try.
+ $language_directories = array(
+ $settings['default_theme_dir'] . '/languages',
+ $settings['actual_theme_dir'] . '/languages',
+ );
+
+ // We possibly have a base theme directory.
+ if (!empty($settings['base_theme_dir']))
+ $language_directories[] = $settings['base_theme_dir'] . '/languages';
+
+ // Remove any duplicates.
+ $language_directories = array_unique($language_directories);
+
+ foreach ($language_directories as $language_dir)
+ {
+ // Can't look in here... doesn't exist!
+ if (!file_exists($language_dir))
+ continue;
+
+ $dir = dir($language_dir);
+ while ($entry = $dir->read())
+ {
+ // Look for the index language file....
+ if (!preg_match('~^index\.(.+)\.php$~', $entry, $matches))
+ continue;
+
+ $context['languages'][$matches[1]] = array(
+ 'name' => $smcFunc['ucwords'](strtr($matches[1], array('_' => ' '))),
+ 'selected' => false,
+ 'filename' => $matches[1],
+ 'location' => $language_dir . '/index.' . $matches[1] . '.php',
+ );
+
+ }
+ $dir->close();
+ }
+
+ // Favoring UTF8? Then prevent us from selecting non-UTF8 versions.
+ if ($favor_utf8)
+ {
+ foreach ($context['languages'] as $lang)
+ if (substr($lang['filename'], strlen($lang['filename']) - 5, 5) != '-utf8' && isset($context['languages'][$lang['filename'] . '-utf8']))
+ unset($context['languages'][$lang['filename']]);
+ }
+
+ // Lets cash in on this deal.
+ if (!empty($modSettings['cache_enable']))
+ cache_put_data('known_languages' . ($favor_utf8 ? '' : '_all'), $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600);
+ }
+
+ return $context['languages'];
+}
+
+// Replace all vulgar words with respective proper words. (substring or whole words..)
+function &censorText(&$text, $force = false)
+{
+ global $modSettings, $options, $settings, $txt;
+ static $censor_vulgar = null, $censor_proper;
+
+ if ((!empty($options['show_no_censored']) && $settings['allow_no_censored'] && !$force) || empty($modSettings['censor_vulgar']))
+ return $text;
+
+ // If they haven't yet been loaded, load them.
+ if ($censor_vulgar == null)
+ {
+ $censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
+ $censor_proper = explode("\n", $modSettings['censor_proper']);
+
+ // Quote them for use in regular expressions.
+ for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
+ {
+ $censor_vulgar[$i] = strtr(preg_quote($censor_vulgar[$i], '/'), array('\\\\\\*' => '[*]', '\\*' => '[^\s]*?', '&' => '&'));
+ $censor_vulgar[$i] = (empty($modSettings['censorWholeWord']) ? '/' . $censor_vulgar[$i] . '/' : '/(?<=^|\W)' . $censor_vulgar[$i] . '(?=$|\W)/') . (empty($modSettings['censorIgnoreCase']) ? '' : 'i') . ((empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' ? 'u' : '');
+
+ if (strpos($censor_vulgar[$i], '\'') !== false)
+ {
+ $censor_proper[count($censor_vulgar)] = $censor_proper[$i];
+ $censor_vulgar[count($censor_vulgar)] = strtr($censor_vulgar[$i], array('\'' => '''));
+ }
+ }
+ }
+
+ // Censoring isn't so very complicated :P.
+ $text = preg_replace($censor_vulgar, $censor_proper, $text);
+ return $text;
+}
+
+// Load the template/language file using eval or require? (with eval we can show an error message!)
+function template_include($filename, $once = false)
+{
+ global $context, $settings, $options, $txt, $scripturl, $modSettings;
+ global $user_info, $boardurl, $boarddir, $sourcedir;
+ global $maintenance, $mtitle, $mmessage;
+ static $templates = array();
+
+ // We want to be able to figure out any errors...
+ @ini_set('track_errors', '1');
+
+ // Don't include the file more than once, if $once is true.
+ if ($once && in_array($filename, $templates))
+ return;
+ // Add this file to the include list, whether $once is true or not.
+ else
+ $templates[] = $filename;
+
+ // Are we going to use eval?
+ if (empty($modSettings['disableTemplateEval']))
+ {
+ $file_found = file_exists($filename) && eval('?' . '>' . rtrim(file_get_contents($filename))) !== false;
+ $settings['current_include_filename'] = $filename;
+ }
+ else
+ {
+ $file_found = file_exists($filename);
+
+ if ($once && $file_found)
+ require_once($filename);
+ elseif ($file_found)
+ require($filename);
+ }
+
+ if ($file_found !== true)
+ {
+ ob_end_clean();
+ if (!empty($modSettings['enableCompressedOutput']))
+ @ob_start('ob_gzhandler');
+ else
+ ob_start();
+
+ if (isset($_GET['debug']) && !WIRELESS)
+ header('Content-Type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
+
+ // Don't cache error pages!!
+ header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+ header('Cache-Control: no-cache');
+
+ if (!isset($txt['template_parse_error']))
+ {
+ $txt['template_parse_error'] = 'Template Parse Error!';
+ $txt['template_parse_error_message'] = 'It seems something has gone sour on the forum with the template system. This problem should only be temporary, so please come back later and try again. If you continue to see this message, please contact the administrator.
You can also try refreshing this page.';
+ $txt['template_parse_error_details'] = 'There was a problem loading the %1$s template or language file. Please check the syntax and try again - remember, single quotes (\') often have to be escaped with a slash (\\). To see more specific error information from PHP, try accessing the file directly.
You may want to try to refresh this page or use the default theme.';
+ }
+
+ // First, let's get the doctype and language information out of the way.
+ echo '
+
+ ';
+ if (isset($context['character_set']))
+ echo '
+ ';
+
+ if (!empty($maintenance) && !allowedTo('admin_forum'))
+ echo '
+ ', $mtitle, '
+
+
+