diff --git a/build/build_diff.php b/build/build_diff.php new file mode 100755 index 0000000000..c8a40f4f5e --- /dev/null +++ b/build/build_diff.php @@ -0,0 +1,408 @@ +#!/usr/bin/env php + 'tar -czf', + 'tar.bz2' => 'tar -cjf', + 'zip' => 'zip -r' + ); + + chdir($location . '/save/' . $s_name); + foreach ($compress_programs as $extension => $compress_command) + { + echo "Packaging code changes for $extension\n"; + run_command("rm ./../../release_files/{$code_changes_filename}.{$extension}"); + flush(); + + // Build Package + run_command("$compress_command ./../../release_files/{$code_changes_filename}.{$extension} *"); + + // Build MD5 Sum + run_command("md5sum ./../../release_files/{$code_changes_filename}.{$extension} > ./../../release_files/{$code_changes_filename}.{$extension}.md5"); + flush(); + } +} + +/** +* $output_format can be: language, prosilver and subsilver2 +*/ +function build_code_changes($output_format) +{ + global $substitute_new, $substitute_old, $simple_name_old, $simple_name_new, $echo_changes, $package_changed_files, $location, $debug_file, $s_name; + + // Global array holding the data entries + $data = array( + 'header' => array(), + 'diff' => array(), + ); + + // Read diff file and prepare the output filedata... + //$patch_filename = '../new_version/patches/phpBB-' . $substitute_old . '_to_' . $substitute_new . '.patch'; + $release_filename = 'phpbb-' . $substitute_old . '_to_' . $substitute_new . '_' . $output_format . '.txt'; + + if (!$package_changed_files) + { + if (!$echo_changes) + { + $fp = fopen('save/' . $s_name . '/' . $output_format . '/' . $release_filename, 'wb'); + + if (!$fp) + { + die('Unable to create ' . $release_filename); + } + } + } + + include_once($location . '/build_helper.php'); + $package = new build_package(array($substitute_old, $substitute_new), false); + + $titles = array( + 'language' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' Language Pack Changes', + 'prosilver' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' prosilver Changes', + 'subsilver2' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' subsilver2 Changes', + ); + + $data['header'] = array( + 'title' => $titles[$output_format], + 'intro' => ' + +These are the ' . $titles[$output_format] . ' summed up into a little Mod. These changes are only partial and do not include any code changes, therefore not meant for updating phpBB. + + ', + 'included_files' => array(), + ); + + // We collect the files we want to diff first (ironically we grab this from a diff file) + if (!$echo_changes) + { + echo "\n\nCollecting Filenames:"; + } + + // We re-create the patch file + foreach ($package->old_packages as $_package_name => $dest_package_filename) + { + chdir($package->locations['old_versions']); + + if (!$echo_changes) + { + echo "\n\n" . 'Creating patch/diff files for phpBB-' . $dest_package_filename . $package->get('new_version_number'); + } + + $dest_package_filename = $location . '/save/' . $s_name . '/phpBB-' . $dest_package_filename . $package->get('new_version_number') . '.patch'; + $package->run_command('diff ' . $package->diff_options . ' ' . $_package_name . ' ' . $package->get('simple_name') . ' > ' . $dest_package_filename); + + // Parse this diff to determine file changes from the checked versions and save them + $result = $package->collect_diff_files($dest_package_filename, $_package_name); + $package->run_command('rm ' . $dest_package_filename); + } + + chdir($location); + + $filenames = array(); + foreach ($result['files'] as $filename) + { + if ($debug_file !== false && $filename != $debug_file) + { + continue; + } + + // Decide which files to compare... + switch ($output_format) + { + case 'language': + if (strpos($filename, 'language/en/') !== 0) + { + continue 2; + } + break; + + case 'prosilver': + if (strpos($filename, 'styles/prosilver/') !== 0) + { + continue 2; + } + break; + + case 'subsilver2': + if (strpos($filename, 'styles/subsilver2/') !== 0) + { + continue 2; + } + break; + } + + if (!file_exists($location . '/old_versions/' . $simple_name_old . '/' . $filename)) + { + // New file... include it + $data['header']['included_files'][] = array( + 'old' => $location . '/old_versions/' . $simple_name_old . '/' . $filename, + 'new' => $location . '/old_versions/' . $simple_name_new . '/' . $filename, + 'phpbb_filename' => $filename, + ); + continue; + } + + $filenames[] = array( + 'old' => $location . '/old_versions/' . $simple_name_old . '/' . $filename, + 'new' => $location . '/old_versions/' . $simple_name_new . '/' . $filename, + 'phpbb_filename' => $filename, + ); + } + + // Now let us go through the filenames list and create a more comprehensive diff + if (!$echo_changes) + { + fwrite($fp, build_header($output_format, $filenames, $data['header'])); + } + else + { + //echo build_header('text', $filenames, $data['header']); + } + + // Copy files... + $files_to_copy = array(); + + foreach ($data['header']['included_files'] as $filename) + { + $files_to_copy[] = $filename['phpbb_filename']; + } + + // First step is to copy the new version over (clean structure) + if (!$echo_changes && sizeof($files_to_copy)) + { + foreach ($files_to_copy as $file) + { + // Create directory? + $dirname = dirname($file); + + if ($dirname) + { + $dirname = explode('/', $dirname); + $__dir = array(); + + foreach ($dirname as $i => $dir) + { + $__dir[] = $dir; + run_command("mkdir -p $location/save/" . $s_name . '/' . $output_format . '/' . implode('/', $__dir)); + } + } + + $source_file = $location . '/new_version/phpBB3/' . $file; + $dest_file = $location . '/save/' . $s_name . '/' . $output_format . '/'; + $dest_file .= $file; + + $command = "cp -p $source_file $dest_file"; + $result = trim(`$command`); + echo "- Copied File: " . $source_file . " -> " . $dest_file . "\n"; + } + } + + include_once('diff_class.php'); + + if (!$echo_changes) + { + echo "\n\nDiffing Codebases:"; + } + + foreach ($filenames as $file_ary) + { + if (!file_exists($file_ary['old'])) + { + $lines1 = array(); + } + else + { + $lines1 = file($file_ary['old']); + } + $lines2 = file($file_ary['new']); + + if (!sizeof($lines1)) + { + // New File + } + else + { + $diff = new Diff($lines1, $lines2); + $fmt = new BBCodeDiffFormatter(false, 5, $debug_file); + + if (!$echo_changes) + { + fwrite($fp, $fmt->format_open($file_ary['phpbb_filename'])); + fwrite($fp, $fmt->format($diff, $lines1)); + fwrite($fp, $fmt->format_close($file_ary['phpbb_filename'])); + } + else + { + echo $fmt->format_open($file_ary['phpbb_filename']); + echo $fmt->format($diff, $lines1); + echo $fmt->format_close($file_ary['phpbb_filename']); + } + + if ($debug_file !== false) + { + echo $fmt->format_open($file_ary['phpbb_filename']); + echo $fmt->format($diff, $lines1); + echo $fmt->format_close($file_ary['phpbb_filename']); + exit; + } + } + } + + if (!$echo_changes) + { + fwrite($fp, build_footer($output_format)); + + // Close file + fclose($fp); + + chmod('save/' . $s_name . '/' . $output_format . '/' . $release_filename, 0666); + } + else + { + echo build_footer($output_format); + } +} + +/** +* Build Footer +*/ +function build_footer($mode) +{ + $html = ''; + + $html .= "# \n"; + $html .= "#-----[ SAVE/CLOSE ALL FILES ]------------------------------------------ \n"; + $html .= "# \n"; + $html .= "# EoM"; + + return $html; +} + +/** +* Build Header +*/ +function build_header($mode, $filenames, $header) +{ + global $substitute_old; + + $html = ''; + + $html .= "############################################################## \n"; + $html .= "## Title: " . $header['title'] . "\n"; + $html .= "## Author: naderman < naderman@phpbb.com > (Nils Adermann) http://www.phpbb.com \n"; + $html .= "## Description: \n"; + + $intr = explode("\n", $header['intro']); + $introduction = ''; + foreach ($intr as $_line) + { + $introduction .= wordwrap($_line, 80) . "\n"; + } + $intr = explode("\n", $introduction); + + foreach ($intr as $_line) + { + $html .= "## " . $_line . "\n"; + } + $html .= "## \n"; + $html .= "## Files To Edit: \n"; + + foreach ($filenames as $file_ary) + { + $html .= "## " . $file_ary['phpbb_filename'] . "\n"; + } + $html .= "##\n"; + if (sizeof($header['included_files'])) + { + $html .= "## Included Files: \n"; + foreach ($header['included_files'] as $filename) + { + $html .= "## {$filename['phpbb_filename']}\n"; + } + } + $html .= "## License: http://opensource.org/licenses/gpl-license.php GNU General Public License v2 \n"; + $html .= "############################################################## \n"; + $html .= "\n"; + + // COPY Statement? + if (sizeof($header['included_files'])) + { + $html .= "#\n#-----[ COPY ]------------------------------------------\n#\n"; + foreach ($header['included_files'] as $filename) + { + $html .= "copy {$filename['phpbb_filename']} to {$filename['phpbb_filename']}\n"; + } + $html .= "\n"; + } + + return $html; +} + +function run_command($command) +{ + $result = trim(`$command`); + echo "\n- Command Run: " . $command . "\n"; +} + +?> diff --git a/build/build_helper.php b/build/build_helper.php new file mode 100644 index 0000000000..2bae32218b --- /dev/null +++ b/build/build_helper.php @@ -0,0 +1,485 @@ +versions = $versions; + $this->verbose = $verbose; + + // Get last two entries + $_latest = $this->versions[sizeof($this->versions) - 1]; + $_before = $this->versions[sizeof($this->versions) - 2]; + + $this->locations = array( + 'new_version' => dirname(dirname(__FILE__)) . '/phpBB/', + 'old_versions' => dirname(__FILE__) . '/old_versions/', + 'root' => dirname(__FILE__) . '/', + 'package_dir' => dirname(__FILE__) . '/new_version/' + ); + + $this->package_infos = array( + 'package_name' => 'phpBB3', + 'name_prefix' => 'phpbb', + 'simple_name' => 'phpbb' . str_replace('.', '', $_latest), + 'new_version_number' => $_latest, + 'short_version_number' => str_replace('.', '', $_latest), + 'release_filename' => 'phpBB-' . $_latest, + 'last_version' => 'phpbb' . str_replace('.', '', $_before), + 'last_version_number' => $_before, + ); + + $this->package_infos['dest_dir'] = $this->locations['package_dir'] . $this->package_infos['package_name']; + $this->package_infos['diff_dir'] = $this->locations['old_versions'] . $this->package_infos['simple_name']; + $this->package_infos['patch_directory'] = $this->locations['package_dir'] . 'patches'; + $this->package_infos['files_directory'] = $this->locations['package_dir'] . 'files'; + $this->package_infos['update_directory'] = $this->locations['package_dir'] . 'update'; + $this->package_infos['release_directory'] = $this->locations['package_dir'] . 'release_files'; + + // Old packages always exclude the latest version. ;) + $this->old_packages = array(); + + foreach ($this->versions as $package_version) + { + if ($package_version == $_latest) + { + continue; + } + + $this->old_packages['phpbb' . str_replace('.', '', $package_version)] = $package_version . '_to_'; + } + + // We need to make sure this is up to date with the latest version + $this->clean_directory_structure = array( + 'adm' => array( + 'images' => '', + 'style' => '', + ), + 'cache' => '', + 'docs' => '', + 'download' => '', + 'files' => '', + 'images' => array( + 'avatars' => array( + 'gallery' => '', + 'upload' => '', + ), + 'icons' => array( + 'misc' => '', + 'smile' => '', + ), + 'ranks' => '', + 'smilies' => '', + 'upload_icons' => '', + ), + 'includes' => array( + 'acm' => '', + 'acp' => array( + 'info' => '', + ), + 'auth' => '', + 'captcha' => array( + 'plugins' => '', + ), + 'diff' => '', + 'db' => '', + 'hooks' => '', + 'mcp' => array( + 'info' => '', + ), + 'questionnaire' => '', + 'search' => '', + 'ucp' => array( + 'info' => '', + ), + 'utf' => array( + 'data' => '', + ), + ), + 'install' => array( + 'convertors'=> '', + 'schemas' => '', +// 'data' => '', + ), + 'language' => array( + 'en' => array( + 'acp' => '', + 'email' => '', + 'mods' => '', + ), + ), + 'store' => '', + 'styles' => array( + 'subsilver2' => array( + 'imageset' => array( + 'en' => '', + ), + 'template' => '', + 'theme' => array( + 'images' => '', + ), + ), + 'prosilver' => array( + 'imageset' => array( + 'en' => '', + ), + 'template' => '', + 'theme' => array( + 'images' => '', + ), + ), + ), + ); + + // Files to remove (not include within package) + $this->files_to_remove = array(); //array('includes/utf/data/recode_cjk.php'); + + // Files within the main directory to copy - do not include config.php + $this->files_to_copy = array( + '.htaccess', 'common.php', 'cron.php', 'faq.php', 'feed.php', 'index.php', 'mcp.php', 'memberlist.php', 'posting.php', 'report.php', + 'search.php', 'style.php', 'ucp.php', 'viewforum.php', 'viewonline.php', 'viewtopic.php' + ); + + // These files/directories will be removed and not used for creating the patch files + $this->remove_from_diff_structure = array( + 'config.php', 'cache', 'docs', 'files', 'install', 'store', 'develop' + ); + + // Writeable directories + $this->writeable = array('cache', 'store', 'images/avatars/upload', 'files'); + + // Fill the rest of the files_to_copy array + foreach ($this->clean_directory_structure as $cur_dir => $dir_struct) + { + $this->_fill_files_to_copy($this->locations['new_version'] . $cur_dir, $cur_dir, $dir_struct); + } + } + + function get($var) + { + return $this->package_infos[$var]; + } + + function _fill_files_to_copy($directory, $cur_dir, $dir_struct) + { + $dh = opendir($directory); + + while ($file = readdir($dh)) + { + if (is_file($directory . '/' . $file) && $file != '.' && $file != '..') + { + $_loc = str_replace($this->locations['new_version'], '', $directory . '/' . $file); + + if (in_array($_loc, $this->files_to_remove)) + { + continue; + } + + $this->files_to_copy[] = $cur_dir . '/' . $file; + } + } + closedir($dh); + + if (is_array($dir_struct)) + { + foreach ($dir_struct as $_cur_dir => $_dir_struct) + { + $this->_fill_files_to_copy($directory . '/' . $_cur_dir, $cur_dir . '/' . $_cur_dir, $_dir_struct); + } + } + } + + function adjust_permissions($directory) + { + $dh = opendir($directory); + + while ($file = readdir($dh)) + { + if ($file == '.' || $file == '..' || $file == '.svn') + { + continue; + } + + // If file, then 644 + if (is_file($directory . '/' . $file)) + { + chmod($directory . '/' . $file, 0644); + } + else if (is_dir($directory . '/' . $file)) + { + $_loc = str_replace($this->package_infos['dest_dir'] . '/', '', $directory . '/' . $file); + + // If directory is within the writeable chmod to 777, else 755 + $mode = (in_array($_loc, $this->writeable)) ? 0777 : 0755; + chmod($directory . '/' . $file, $mode); + + // Now traverse to the directory + $this->adjust_permissions($directory . '/' . $file); + } + } + closedir($dh); + } + + function begin_status($headline) + { + if ($this->status_begun) + { + echo "\nDone.\n\n"; + } + + $this->num_dots = 0; + + echo $headline . "\n "; + + $this->status_begun = true; + } + + function run_command($command) + { + $result = trim(`$command`); + + if ($this->verbose) + { + echo " command : " . getcwd() . '$ ' . $command . "\n"; + echo " result : " . $result . "\n"; + } + else + { + if ($this->num_dots > 70) + { + echo "\n"; + $this->num_dots = 0; + } + echo '.'; + $this->num_dots++; + } + + flush(); + } + + function create_directory($directory, $dir_struct) + { + if (!file_exists($directory)) + { + $this->run_command("mkdir $directory"); + } + + if (is_array($dir_struct)) + { + foreach ($dir_struct as $_dir => $_dir_struct) + { + $this->create_directory($directory . '/' . $_dir, $_dir_struct); + } + } + } + + function collect_diff_files($diff_filename, $package_name) + { + $diff_result = $binary = array(); + $diff_contents = file($diff_filename); + + $special_diff_contents = array(); + + foreach ($diff_contents as $num => $line) + { + $line = trim($line); + + if (!$line) + { + continue; + } + + // Special diff content? + if (strpos($line, 'diff ' . $this->diff_options . ' ') === 0 || strpos($line, '*** ') === 0 || strpos($line, '--- ') === 0 || (strpos($line, ' Exp $') !== false && strpos($line, '$Id:') !== false)) + { + $special_diff_contents[] = $line; + } + else if (strpos($line, 'diff ' . $this->diff_options . ' ') === 0 || strpos($line, '*** ') === 0 || strpos($line, '--- ') === 0 || (strpos($line, ' Exp $') !== false && strpos($line, '$Id:') !== false) || (strpos($line, ' $') !== false && strpos($line, '$Id:') !== false)) + { + $special_diff_contents[] = $line; + } + + // Is diffing line? + if (strstr($line, 'diff ' . $this->diff_options . ' ')) + { + $next_line = $diff_contents[$num+1]; + if (strpos($next_line, '***') === 0) + { + // *** phpbb208/admin/admin_board.php Sat Jul 10 20:16:26 2004 + $next_line = explode("\t", $next_line); + $next_line = trim($next_line[0]); + $next_line = str_replace('*** ' . $package_name . '/', '', $next_line); + $diff_result[] = $next_line; + } + } + + // Is binary? + if (preg_match('/^Binary files ' . $package_name . '\/(.*) and [a-z0-9_-]+\/\1 differ/i', $line, $match)) + { + $binary[] = trim($match[1]); + } + } + + // Now go through the list again and find out which files have how many changes... + $num_changes = array(); + + /* [1070] => diff -crN phpbb200/includes/usercp_avatar.php phpbb2023/includes/usercp_avatar.php + [1071] => *** phpbb200/includes/usercp_avatar.php Sat Jul 10 20:16:13 2004 + [1072] => --- phpbb2023/includes/usercp_avatar.php Wed Feb 6 22:28:04 2008 + [1073] => *** 6,12 **** + [1074] => ! * $Id$ + [1075] => --- 6,12 ---- + [1076] => *** 51,59 **** + [1077] => --- 51,60 ---- + [1078] => *** 62,80 **** + [1079] => --- 63,108 ---- + [1080] => *** 87,97 **** + */ + while (($line = array_shift($special_diff_contents)) !== NULL) + { + $line = trim($line); + + if (!$line) + { + continue; + } + + // Is diffing line? + if (strstr($line, 'diff ' . $this->diff_options . ' ')) + { + $next_line = array_shift($special_diff_contents); + if (strpos($next_line, '*** ') === 0) + { + // *** phpbb208/admin/admin_board.php Sat Jul 10 20:16:26 2004 + $next_line = explode("\t", $next_line); + $next_line = trim($next_line[0]); + $next_line = str_replace('*** ' . $package_name . '/', '', $next_line); + + $is_reached = false; + $prev_line = ''; + + while (!$is_reached) + { + $line = array_shift($special_diff_contents); + + if (strpos($line, 'diff ' . $this->diff_options) === 0 || empty($special_diff_contents)) + { + $is_reached = true; + array_unshift($special_diff_contents, $line); + continue; + } + + if (strpos($line, '*** ') === 0 && strpos($line, ' ****') !== false) + { + $is_comment = false; + while (!(strpos($line, '--- ') === 0 && strpos($line, ' ----') !== false)) + { + $line = array_shift($special_diff_contents); + if (strpos($line, ' Exp $') !== false || strpos($line, '$Id:') !== false) + { + $is_comment = true; + } + } + + if (!$is_comment) + { + if (!isset($num_changes[$next_line])) + { + $num_changes[$next_line] = 1; + } + else + { + $num_changes[$next_line]++; + } + } + } + } + } + } + } + + // Now remove those results not having changes + $return = array(); + + foreach ($diff_result as $key => $value) + { + if (isset($num_changes[$value])) + { + $return[] = $value; + } + } + + foreach ($binary as $value) + { + $return[] = $value; + } + + $diff_result = $return; + unset($return); + unset($special_diff_contents); + + $result = array( + 'files' => array(), + 'binary' => array(), + 'all' => $diff_result, + ); + + $binary_extensions = array('gif', 'jpg', 'jpeg', 'png', 'ttf'); + + // Split into file and binary + foreach ($diff_result as $filename) + { + if (strpos($filename, '.') === false) + { + $result['files'][] = $filename; + continue; + } + + $extension = explode('.', $filename); + $extension = array_pop($extension); + + if (in_array($extension, $binary_extensions)) + { + $result['binary'][] = $filename; + } + else + { + $result['files'][] = $filename; + } + } + + return $result; + } +} diff --git a/build/compare.sh b/build/compare.sh new file mode 100755 index 0000000000..df442fd4c7 --- /dev/null +++ b/build/compare.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +orig_dir="../../phpBB" + + +rm -rf test_release_files +mkdir test_release_files +cd test_release_files + +for ext in "tar.bz2" "zip" +do + cp "../new_version/release_files/$1.$ext" ./ + + if [ "$ext" = "tar.bz2" ] + then + command="tar -xjf" + else + command="unzip -q" + fi + + $command "$1.$ext" + + for file in `find phpBB3 -name '.svn' -prune -o -type f -print` + do + orig_file="${file/#phpBB3/$orig_dir}" + diff_result=`diff $orig_file $file` + + if [ -n "$diff_result" ] + then + echo "Difference in package $1.$ext" + echo $diff_result + fi + done + + rm -rf phpBB3 +done + +cd .. +rm -rf test_release_files + diff --git a/build/diff_class.php b/build/diff_class.php new file mode 100644 index 0000000000..4625ffde24 --- /dev/null +++ b/build/diff_class.php @@ -0,0 +1,1711 @@ + +* @copyright (c) 2010 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* +*/ + +/** +* Class used internally by WikiDiff to actually compute the diffs. +* +* The algorithm used here is mostly lifted from the perl module +* Algorithm::Diff (version 1.06) by Ned Konz, which is available at: +* http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip +* +* More ideas are taken from: +* http://www.ics.uci.edu/~eppstein/161/960229.html +* +* Some ideas are (and a bit of code) are from from analyze.c, from GNU +* diffutils-2.7, which can be found at: +* ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz +* +* Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations) +* are my own. +*/ +class _WikiDiffEngine +{ + var $edits; // List of editing operation to convert XV to YV. + var $xv = array(), $yv = array(); + + function _WikiDiffEngine($from_lines, $to_lines) + { + $n_from = sizeof($from_lines); + $n_to = sizeof($to_lines); + + $endskip = 0; + // Ignore trailing and leading matching lines. + while ($n_from > 0 && $n_to > 0) + { + if ($from_lines[$n_from - 1] != $to_lines[$n_to - 1]) + { + break; + } + + $n_from--; + $n_to--; + $endskip++; + } + + for ($skip = 0; $skip < min($n_from, $n_to); $skip++) + { + if ($from_lines[$skip] != $to_lines[$skip]) + { + break; + } + } + $n_from -= $skip; + $n_to -= $skip; + + $xlines = array(); + $ylines = array(); + + // Ignore lines which do not exist in both files. + for ($x = 0; $x < $n_from; $x++) + { + $xhash[$from_lines[$x + $skip]] = 1; + } + + for ($y = 0; $y < $n_to; $y++) + { + $line = $to_lines[$y + $skip]; + $ylines[] = $line; + + if (($this->ychanged[$y] = empty($xhash[$line]))) + { + continue; + } + $yhash[$line] = 1; + $this->yv[] = $line; + $this->yind[] = $y; + } + + for ($x = 0; $x < $n_from; $x++) + { + $line = $from_lines[$x + $skip]; + $xlines[] = $line; + + if (($this->xchanged[$x] = empty($yhash[$line]))) + { + continue; // fixme? what happens to yhash/xhash when + // there are two identical lines?? + } + $this->xv[] = $line; + $this->xind[] = $x; + } + + // Find the LCS. + $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv)); + + // Merge edits when possible + $this->_shift_boundaries($xlines, $this->xchanged, $this->ychanged); + $this->_shift_boundaries($ylines, $this->ychanged, $this->xchanged); + + // Compute the edit operations. + $this->edits = array(); + + if ($skip) + { + $this->edits[] = $skip; + } + + $x = 0; + $y = 0; + + while ($x < $n_from || $y < $n_to) + { + // Skip matching "snake". + $x0 = $x; + $ncopy = 0; + while ($x < $n_from && $y < $n_to && !$this->xchanged[$x] && !$this->ychanged[$y]) + { + ++$x; + ++$y; + ++$ncopy; + } + + if ($x > $x0) + { + $this->edits[] = $x - $x0; + } + + // Find deletes. + $x0 = $x; + $ndelete = 0; + + while ($x < $n_from && $this->xchanged[$x]) + { + ++$x; + ++$ndelete; + } + + if ($x > $x0) + { + $this->edits[] = -($x - $x0); + } + + // Find adds. + if (isset($this->ychanged[$y]) && $this->ychanged[$y]) + { + $adds = array(); + while ($y < $n_to && $this->ychanged[$y]) + { + $adds[] = $ylines[$y++]; + } + $this->edits[] = $adds; + } + } + + if (!empty($endskip)) + { + $this->edits[] = $endskip; + } + } + + /* Divide the Largest Common Subsequence (LCS) of the sequences + * [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally + * sized segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an + * array of NCHUNKS+1 (X, Y) indexes giving the diving points between + * sub sequences. The first sub-sequence is contained in [X0, X1), + * [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note + * that (X0, Y0) == (XOFF, YOFF) and + * (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This function assumes that the first lines of the specified portions + * of the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) + { + $flip = false; + + if ($xlim - $xoff > $ylim - $yoff) + { + // Things seems faster (I'm not sure I understand why) + // when the shortest sequence in X. + $flip = true; + list ($xoff, $xlim, $yoff, $ylim) = array( $yoff, $ylim, $xoff, $xlim); + } + + if ($flip) + { + for ($i = $ylim - 1; $i >= $yoff; $i--) + { + $ymatches[$this->xv[$i]][] = $i; + } + } + else + { + for ($i = $ylim - 1; $i >= $yoff; $i--) + { + $ymatches[$this->yv[$i]][] = $i; + } + } + + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + + for ($chunk = 0; $chunk < $nchunks; $chunk++) + { + if ($chunk > 0) + { + for ($i = 0; $i <= $this->lcs; $i++) + { + $ymids[$i][$chunk-1] = $this->seq[$i]; + } + } + + $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks); + + for ( ; $x < $x1; $x++) + { + $_index = $flip ? $this->yv[$x] : $this->xv[$x]; + $matches = (isset($ymatches[$_index])) ? $ymatches[$_index] : array(); + + if (!$matches) + { + continue; + } + reset($matches); + + while (list ($junk, $y) = each($matches)) + { + if (!isset($this->in_seq[$y]) || !$this->in_seq[$y]) + { + $k = $this->_lcs_pos($y); + //if (!$k) die('assertion "!$k" failed'); + $ymids[$k] = $ymids[$k-1]; + break; + } + } + + while (list ($junk, $y) = each($matches)) + { + if ($y > $this->seq[$k-1]) + { + //if ($y >= $this->seq[$k]) die('assertion failed'); + // Optimization: this is a common case: + // next match is just replacing previous match. + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } + else if (!isset($this->in_seq[$y]) || !$this->in_seq[$y]) + { + $k = $this->_lcs_pos($y); + //if (!$k) die('assertion "!$k" failed'); + $ymids[$k] = $ymids[$k-1]; + } + } + } + } + + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + + for ($n = 0; $n < $nchunks - 1; $n++) + { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + + return array($this->lcs, $seps); + } + + function _lcs_pos ($ypos) + { + $end = $this->lcs; + if ($end == 0 || $ypos > $this->seq[$end]) + { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + return $this->lcs; + } + + $beg = 1; + while ($beg < $end) + { + $mid = (int)(($beg + $end) / 2); + + if ($ypos > $this->seq[$mid]) + { + $beg = $mid + 1; + } + else + { + $end = $mid; + } + } + + //if ($ypos == $this->seq[$end]) die("assertion failure"); + + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + return $end; + } + + /* Find LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion + * or deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. + * All line numbers are origin-0 and discarded lines are not counted. + */ + function _compareseq ($xoff, $xlim, $yoff, $ylim) + { + // Slide down the bottom initial diagonal. + while ($xoff < $xlim && $yoff < $ylim && $this->xv[$xoff] == $this->yv[$yoff]) + { + ++$xoff; + ++$yoff; + } + + // Slide up the top initial diagonal. + while ($xlim > $xoff && $ylim > $yoff && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) + { + --$xlim; + --$ylim; + } + + if ($xoff == $xlim || $yoff == $ylim) + { + $lcs = 0; + } + else + { + // This is ad hoc but seems to work well. + //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); + //$nchunks = max(2,min(8,(int)$nchunks)); + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list ($lcs, $seps) = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); + } + + if ($lcs == 0) + { + // X and Y sequences have no common subsequence: + // mark all changed. + while ($yoff < $ylim) + { + $this->ychanged[$this->yind[$yoff++]] = 1; + } + + while ($xoff < $xlim) + { + $this->xchanged[$this->xind[$xoff++]] = 1; + } + } + else + { + // Use the partitions to split this problem into subproblems. + reset($seps); + + $pt1 = $seps[0]; + + while ($pt2 = next($seps)) + { + $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + } + + /* Adjust inserts/deletes of identical lines to join changes + * as much as possible. + * + * We do something when a run of changed lines include a + * line at one end and have an excluded, identical line at the other. + * We are free to choose which identical line is included. + * `compareseq' usually chooses the one at the beginning, + * but usually it is cleaner to consider the following identical line + * to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + function _shift_boundaries ($lines, &$changed, $other_changed) + { + $i = 0; + $j = 0; + $len = sizeof($lines); + + while (1) + { + /* + * Scan forwards to find beginning of another run of changes. + * Also keep track of the corresponding point in the other file. + */ + + while ($i < $len && $changed[$i] == 0) + { + while ($other_changed[$j++]) + { + continue; + } + $i++; + } + + if ($i == $len) + { + break; + } + + $start = $i; + + // Find the end of this run of changes. + while (isset($changed[++$i])) + { + continue; + } + + while (isset($other_changed[$j]) && $other_changed[$j]) + { + $j++; + } + + do + { + /* + * Record the length of this run of changes, so that + * we can later determine whether the run has grown. + */ + $runlength = $i - $start; + + /* + * Move the changed region back, so long as the + * previous unchanged line matches the last changed one. + * This merges with previous changed regions. + */ + while ($start && $lines[$start - 1] == $lines[$i - 1]) + { + $changed[--$start] = 1; + $changed[--$i] = false; + + while ($changed[$start - 1]) + { + $start--; + } + + while ($other_changed[--$j]) + { + continue; + } + } + + /* + * Set CORRESPONDING to the end of the changed run, at the last + * point where it corresponds to a changed run in the other file. + * CORRESPONDING == LEN means no such point has been found. + */ + $corresponding = empty($other_changed[$j - 1]) ? $len : $i; + + /* + * Move the changed region forward, so long as the + * first changed line matches the following unchanged one. + * This merges with following changed regions. + * Do this second, so that if there are no merges, + * the changed region is moved forward as far as possible. + */ + while ($i != $len && $lines[$start] == $lines[$i]) + { + $changed[$start++] = false; + $changed[$i++] = 1; + + while ($changed[$i]) + { + $i++; + } + + while ($other_changed[++$j]) + { + $corresponding = $i; + } + } + } while ($runlength != $i - $start); + + /* + * If possible, move the fully-merged run of changes + * back to a corresponding run in the other file. + */ + while ($corresponding < $i) + { + $changed[--$start] = 1; + $changed[--$i] = 0; + + while ($other_changed[--$j]) + { + continue; + } + } + } + } +} + +/** +* Class representing a diff between two files. +*/ +class Diff +{ + var $edits; + + /** + * Compute diff between files (or deserialize serialized WikiDiff.) + */ + function Diff($from_lines = false, $to_lines = false) + { + if ($from_lines && $to_lines) + { + $compute = new _WikiDiffEngine($from_lines, $to_lines); + $this->edits = $compute->edits; + } + else if ($from_lines) + { + // $from_lines is not really from_lines, but rather + // a serialized Diff. + $this->edits = unserialize($from_lines); + } + else + { + $this->edits = array(); + } + } + + /** + * Compute reversed Diff. + * + * SYNOPSIS: + * + * $diff = new Diff($lines1, $lines2); + * $rev = $diff->reverse($lines1); + * + * // reconstruct $lines1 from $lines2: + * $out = $rev->apply($lines2); + */ + function reverse ($from_lines) + { + $x = 0; + $rev = new Diff; + + for (reset($this->edits); $edit = current($this->edits); next($this->edits)) + { + if (is_array($edit)) + { // Was an add, turn it into a delete. + $nadd = sizeof($edit); + if ($nadd == 0) + { + die("assertion error"); + } + $edit = -$nadd; + } + else if ($edit > 0) + { + // Was a copy --- just pass it through. } + $x += $edit; + } + else if ($edit < 0) + { // Was a delete, turn it into an add. + $ndelete = -$edit; + $edit = array(); + + while ($ndelete-- > 0) + { + $edit[] = $from_lines[$x++]; + } + } + else + { + die("assertion error"); + } + + $rev->edits[] = $edit; + } + + return $rev; + } + + /** + * Compose (concatenate) Diffs. + * + * SYNOPSIS: + * + * $diff1 = new Diff($lines1, $lines2); + * $diff2 = new Diff($lines2, $lines3); + * $comp = $diff1->compose($diff2); + * + * // reconstruct $lines3 from $lines1: + * $out = $comp->apply($lines1); + */ + function compose ($that) + { + reset($this->edits); + reset($that->edits); + + $comp = new Diff; + $left = current($this->edits); + $right = current($that->edits); + + while ($left || $right) + { + if (!is_array($left) && $left < 0) + { // Left op is a delete. + $newop = $left; + $left = next($this->edits); + } + else if (is_array($right)) + { // Right op is an add. + $newop = $right; + $right = next($that->edits); + } + else if (!$left || !$right) + { + die ("assertion error"); + } + else if (!is_array($left) && $left > 0) + { // Left op is a copy. + if ($left <= abs($right)) + { + $newop = $right > 0 ? $left : -$left; + $right -= $newop; + + if ($right == 0) + { + $right = next($that->edits); + } + $left = next($this->edits); + } + else + { + $newop = $right; + $left -= abs($right); + $right = next($that->edits); + } + } + else + { // Left op is an add. + if (!is_array($left)) + { + die('assertion error'); + } + $nleft = sizeof($left); + + if ($nleft <= abs($right)) + { + if ($right > 0) + { // Right op is copy + $newop = $left; + $right -= $nleft; + } + else // Right op is delete + { + $newop = false; + $right += $nleft; + } + + if ($right == 0) + { + $right = next($that->edits); + } + $left = next($this->edits); + } + else + { + unset($newop); + + if ($right > 0) + { + for ($i = 0; $i < $right; $i++) + { + $newop[] = $left[$i]; + } + } + + $tmp = array(); + for ($i = abs($right); $i < $nleft; $i++) + { + $tmp[] = $left[$i]; + } + $left = $tmp; + $right = next($that->edits); + } + } + + if (!$op) + { + $op = $newop; + continue; + } + + if (!$newop) + { + continue; + } + + if (is_array($op) && is_array($newop)) + { + // Both $op and $newop are adds. + for ($i = 0; $i < sizeof($newop); $i++) + { + $op[] = $newop[$i]; + } + } + else if (($op > 0 && $newop > 0) || ($op < 0 && $newop < 0)) + { // $op and $newop are both either deletes or copies. + $op += $newop; + } + else + { + $comp->edits[] = $op; + $op = $newop; + } + } + + if ($op) + { + $comp->edits[] = $op; + } + + return $comp; + } + + /* Debugging only: + function _dump () + { + echo "
    "; + for (reset($this->edits); + { + $edit = current($this->edits); + } + next($this->edits)) + { + echo "
  1. "; + if ($edit > 0) + echo "Copy $edit"; + else if ($edit < 0) + echo "Delete " . -$edit; + else if (is_array($edit)) + { + echo "Add " . sizeof($edit) . ""; + } + else + die("assertion error"); + } + echo "
"; + } + */ + + /** + * Apply a Diff to a set of lines. + * + * SYNOPSIS: + * + * $diff = new Diff($lines1, $lines2); + * + * // reconstruct $lines2 from $lines1: + * $out = $diff->apply($lines1); + */ + function apply ($from_lines) + { + $x = 0; + $xlim = sizeof($from_lines); + + for (reset($this->edits); $edit = current($this->edits); next($this->edits)) + { + if (is_array($edit)) + { + reset($edit); + while (list ($junk, $line) = each($edit)) + { + $output[] = $line; + } + } + else if ($edit > 0) + { + while ($edit--) + { + $output[] = $from_lines[$x++]; + } + } + else + { + $x += -$edit; + } + } + + if ($x != $xlim) + { + die(sprintf("Diff::apply: line count mismatch: %s != %s", $x, $xlim)); + } + + return $output; + } + + /** + * Serialize a Diff. + * + * SYNOPSIS: + * + * $diff = new Diff($lines1, $lines2); + * $string = $diff->serialize; + * + * // recover Diff from serialized version: + * $diff2 = new Diff($string); + */ + function serialize () + { + return serialize($this->edits); + } + + /** + * Return true if two files were equal. + */ + function isEmpty() + { + if (sizeof($this->edits) > 1) + { + return false; + } + + if (sizeof($this->edits) == 0) + { + return true; + } + + // Test for: only edit is a copy. + return !is_array($this->edits[0]) && $this->edits[0] > 0; + } + + /** + * Compute the length of the Longest Common Subsequence (LCS). + * + * This is mostly for diagnostic purposed. + */ + function lcs() + { + $lcs = 0; + for (reset($this->edits); $edit = current($this->edits); next($this->edits)) + { + if (!is_array($edit) && $edit > 0) + { + $lcs += $edit; + } + } + + return $lcs; + } + + /** + * Check a Diff for validity. + * + * This is here only for debugging purposes. + */ + function _check ($from_lines, $to_lines) + { + $test = $this->apply($from_lines); + if (serialize($test) != serialize($to_lines)) + { + die("Diff::_check: failed"); + } + + reset($this->edits); + $prev = current($this->edits); + $prevtype = is_array($prev) ? 'a' : ($prev > 0 ? 'c' : 'd'); + + while ($edit = next($this->edits)) + { + $type = is_array($edit) ? 'a' : ($edit > 0 ? 'c' : 'd'); + if ($prevtype == $type) + { + die("Diff::_check: edit sequence is non-optimal"); + } + $prevtype = $type; + } + $lcs = $this->lcs(); + echo "Diff Okay: LCS = $lcs\n"; + } +} + +/** +* A class to format a Diff as HTML. +* +* Usage: +* +* $diff = new Diff($lines1, $lines2); // compute diff. +* +* $fmt = new DiffFormatter; +* echo $fmt->format($diff, $lines1); // Output HTMLified standard diff. +* +* or to output reverse diff (diff's that would take $lines2 to $lines1): +* +* $fmt = new DiffFormatter(true); +* echo $fmt->format($diff, $lines1); +*/ +class DiffFormatter +{ + var $context_lines; + var $do_reverse_diff; + var $context_prefix, $deletes_prefix, $adds_prefix; + + function DiffFormatter ($reverse = false) + { + $this->do_reverse_diff = $reverse; + $this->context_lines = 0; + $this->context_prefix = '  '; + $this->deletes_prefix = '< '; + $this->adds_prefix = '> '; + } + + function format ($diff, $from_lines) + { + $html = '' . "\n"; + $html .= $this->_format($diff->edits, $from_lines); + $html .= "
\n"; + + return $html; + } + + function _format ($edits, $from_lines) + { + $html = ''; + $x = 0; $y = 0; + $xlim = sizeof($from_lines); + + reset($edits); + while ($edit = current($edits)) + { + if (!is_array($edit) && $edit >= 0) + { // Edit op is a copy. + $ncopy = $edit; + } + else + { + $ncopy = 0; + + if (empty($hunk)) + { + // Start of an output hunk. + $xoff = max(0, $x - $this->context_lines); + $yoff = $xoff + $y - $x; + + if ($xoff < $x) + { + // Get leading context. + $context = array(); + + for ($i = $xoff; $i < $x; $i++) + { + $context[] = $from_lines[$i]; + } + $hunk['c'] = $context; + } + } + + if (is_array($edit)) + { + // Edit op is an add. + $y += sizeof($edit); + $hunk[$this->do_reverse_diff ? 'd' : 'a'] = $edit; + } + else + { + // Edit op is a delete + $deletes = array(); + + while ($edit++ < 0) + { + $deletes[] = $from_lines[$x++]; + } + + $hunk[$this->do_reverse_diff ? 'a' : 'd'] = $deletes; + } + } + + $next = next($edits); + + if (!empty($hunk)) + { + // Originally $ncopy > 2 * $this->context_lines, but we need to split as early as we can for creating MOD Text Templates. ;) + if (!$next || $ncopy > $this->context_lines) + { + // End of an output hunk. + $hunks[] = $hunk; + unset($hunk); + + $xend = min($x + $this->context_lines, $xlim); + + if ($x < $xend) + { + // Get trailing context. + $context = array(); + for ($i = $x; $i < $xend; $i++) + { + $context[] = $from_lines[$i]; + } + $hunks[] = array('c' => $context); + } + + $xlen = $xend - $xoff; + $ylen = $xend + $y - $x - $yoff; + $xbeg = $xlen ? $xoff + 1 : $xoff; + $ybeg = $ylen ? $yoff + 1 : $yoff; + + if ($this->do_reverse_diff) + { + list ($xbeg, $xlen, $ybeg, $ylen) = array($ybeg, $ylen, $xbeg, $xlen); + } + + $html .= $this->_emit_diff($xbeg, $xlen, $ybeg, $ylen, $hunks); + unset($hunks); + } + else if ($ncopy) + { + $hunks[] = $hunk; + + // Copy context. + $context = array(); + for ($i = $x; $i < $x + $ncopy; $i++) + { + $context[] = $from_lines[$i]; + } + $hunk = array('c' => $context); + } + } + + $x += $ncopy; + $y += $ncopy; + } + + return $html; + } + + function _emit_lines($lines, $prefix, $color) + { + $html = ''; + reset($lines); + while (list ($junk, $line) = each($lines)) + { + $html .= "$prefix"; + $html .= "" . htmlspecialchars($line) . "\n"; + } + return $html; + } + + function _emit_diff ($xbeg,$xlen,$ybeg,$ylen,$hunks) + { + // Save hunk... + $this->diff_hunks[] = $hunks; + + $html = ' + \n
' . + $this->_diff_header($xbeg, $xlen, $ybeg, $ylen) . ' +
+ + '; + + $prefix = array('c' => $this->context_prefix, 'a' => $this->adds_prefix, 'd' => $this->deletes_prefix); + $color = array('c' => '#ffffff', 'a' => '#ffcccc', 'd' => '#ccffcc'); + + for (reset($hunks); $hunk = current($hunks); next($hunks)) + { + if (!empty($hunk['c'])) + { + $html .= $this->_emit_lines($hunk['c'], $this->context_prefix, '#ffffff'); + } + + if (!empty($hunk['d'])) + { + $html .= $this->_emit_lines($hunk['d'], $this->deletes_prefix, '#ccffcc'); + } + + if (!empty($hunk['a'])) + { + $html .= $this->_emit_lines($hunk['a'], $this->adds_prefix, '#ffcccc'); + } + } + + $html .= "
\n"; + return $html; + } + + function _diff_header ($xbeg,$xlen,$ybeg,$ylen) + { + $what = $xlen ? ($ylen ? 'c' : 'd') : 'a'; + $xlen = $xlen > 1 ? "," . ($xbeg + $xlen - 1) : ''; + $ylen = $ylen > 1 ? "," . ($ybeg + $ylen - 1) : ''; + + return "$xbeg$xlen$what$ybeg$ylen"; + } +} + +/** +* A class to format a Diff as a pretty HTML unified diff. +* +* Usage: +* +* $diff = new Diff($lines1, $lines2); // compute diff. +* +* $fmt = new UnifiedDiffFormatter; +* echo $fmt->format($diff, $lines1); // Output HTMLified unified diff. +*/ +class UnifiedDiffFormatter extends DiffFormatter +{ + function UnifiedDiffFormatter ($reverse = false, $context_lines = 3) + { + $this->do_reverse_diff = $reverse; + $this->context_lines = $context_lines; + $this->context_prefix = ' '; + $this->deletes_prefix = '-'; + $this->adds_prefix = '+'; + } + + function _diff_header ($xbeg,$xlen,$ybeg,$ylen) + { + $xlen = $xlen == 1 ? '' : ",$xlen"; + $ylen = $ylen == 1 ? '' : ",$ylen"; + + return "@@ -$xbeg$xlen +$ybeg$ylen @@"; + } +} + +/** +* A class to format a Diff as MOD Template instuctions. +* +* Usage: +* +* $diff = new Diff($lines1, $lines2); // compute diff. +* +* $fmt = new BBCodeDiffFormatter; +* echo $fmt->format($diff, $lines1); // Output MOD Actions. +*/ +class BBCodeDiffFormatter extends DiffFormatter +{ + function BBCodeDiffFormatter ($reverse = false, $context_lines = 3, $debug = false) + { + $this->do_reverse_diff = $reverse; + $this->context_lines = $context_lines; + $this->context_prefix = ' '; + $this->deletes_prefix = '-'; + $this->adds_prefix = '+'; + $this->debug = $debug; + } + + function format ($diff, $from_lines) + { + $html = $this->_format($diff->edits, $from_lines); + + return $html; + } + + function skip_lines(&$order_array, &$ordering) + { + if (sizeof($order_array['find_c'])) + { + $text = implode('', $order_array['find_c']); + if ($text === "\n" || $text === "\t" || $text === '') + { + if (isset($order_array['first_find_c'][0]) && + is_array($order_array['first_find_c'][0]) && + trim(implode('', $order_array['first_find_c'][0])) != '' && + isset($order_array['replace'])) + { + $order_array['add'] = $order_array['replace']; + unset($order_array['replace']); + // this is actually an after add + } + else + { + return true; + } + } + } + + if (isset($order_array['add']) && sizeof($order_array['add'])) + { + $text = implode('', $order_array['add']); + if ($text === "\n" || $text === "\t" || $text === '') + { + return true; + } + } + + if (isset($order_array['replace']) && sizeof($order_array['replace'])) + { + $text = implode('', $order_array['replace']); + if ($text === "\n" || $text === "\t" || $text === '') + { + return true; + } + } + } + + function _emit_lines_bb($ybeg, &$ordering) + { + $html = ''; + + // Now adjust for bbcode display... + foreach ($ordering as $order_array) + { + // Skip useless empty lines... + if ($this->skip_lines($order_array, $ordering)) + { + continue; + } + + // Only include double-finds if the last find has very few code location indications... + if (isset($order_array['first_find_c']) && sizeof($order_array['first_find_c'])) + { + $text = implode('', $order_array['find_c']); + if ($text === "\n" || $text === "\t" || $text === '') + { + // no real find, use first_find_c if possible! + //var_dump($order_array); + if (is_array($order_array['first_find_c'][0])) + { + $order_array['find_c'] = $order_array['first_find_c'][0]; + } + else + { + if (isset($order_array['replace']) || isset($order_array['add']) || isset($order_array['delete'])) + { + echo "skipped an edit!\n"; + var_dump($order_array); + } + continue; + } + } + else + { + if (strlen(implode('', $order_array['find_c'])) < 50 && is_array($order_array['first_find_c'][0])) + { + $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n"; + $html .= implode("", $order_array['first_find_c'][0]); + $html .= "\n"; + $ybeg += sizeof($order_array['first_find_c'][0]); + } + } + } + + // still here but nothing to do? what the heck? + if (!isset($order_array['replace']) && !isset($order_array['add']) && !isset($order_array['delete'])) + { + echo "skipped an edit!\n"; + var_dump($order_array); + continue; + } + + if (sizeof($order_array['find_c'])) + { + $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n"; + $html .= implode("", $order_array['find_c']); + $html .= "\n"; + } + + if (isset($order_array['replace'])) + { + $html .= "#\n#-----[ REPLACE WITH ]---------------------------------------------\n#\n"; + $html .= implode("", $order_array['replace']); + $html .= "\n"; + } + + if (isset($order_array['add'])) + { + $html .= "#\n#-----[ AFTER, ADD ]---------------------------------------------\n#\n"; + $html .= implode("", $order_array['add']); + $html .= "\n"; + } + + // There is no DELETE. :o + // Let's try to adjust it then... + if (isset($order_array['delete'])) + { + $text = implode('', $order_array['delete']); + if ($text === "\n" || $text === "\t" || $text === '') + { + continue; + } + + $ybeg += sizeof($order_array['find_c']); + + $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n"; + $html .= implode("", $order_array['delete']); + $html .= "\n"; + + $html .= "#\n#-----[ REPLACE WITH ]---------------------------------------------\n# Just remove/delete the lines (replacing with an empty line)\n"; + $html .= "\n"; + $html .= "\n"; + } + } + + return $html; + } + + function format_open($filename) + { + $html = ''; + $html .= "#\n#-----[ OPEN ]--------------------------------------------- \n#\n{$filename}\n\n"; + + return $html; + } + + function format_close($filename) + { + return ''; + } + + function _emit_diff ($xbeg, $xlen, $ybeg, $ylen, $hunks) + { + + // Go through the hunks to determine which method we are using (AFTER, ADD; REPLACE WITH or DELETE) + + // Remove the header... + if (sizeof($hunks) <= 2 && !isset($hunks[1]['a']) && !isset($hunks[1]['d'])) + { + $reorder = false; + $orig_hunks = $hunks; + + foreach ($hunks as $key => $hunk) + { + if (isset($hunk['a']) && isset($hunk['d'])) + { + /**/ if (sizeof($hunk['a']) == 1 && sizeof($hunk['d']) == 1) + { + if (preg_match('/\* @version \$Id:.+\$$/', $hunk['a'][0]) && preg_match('/\* @version \$Id:.+\$$/', $hunk['d'][0])) + { + // Only remove this sole hunk... + unset($hunks[$key]); + $reorder = true; + continue; + } + }/**/ + + // Compare the add and replace one... + $string_1 = rtrim(trim(implode('', $hunk['a']))); + $string_2 = rtrim(trim(implode('', $hunk['d']))); + + if (strcmp($string_1, $string_2) === 0) + { + // Only remove this sole hunk... + unset($hunks[$key]); + $reorder = true; + continue; + } + } + } + + if ($reorder) + { + // Now check if we have no more 'a' and 'd's + $hunks = array_merge($hunks, array()); + } + } + else + { + $reorder = false; + $orig_hunks = $hunks; + + foreach ($hunks as $key => $hunk) + { + if (isset($hunk['a']) && isset($hunk['d'])) + { + /**/ if (sizeof($hunk['a']) == 1 && sizeof($hunk['d']) == 1) + { + if (preg_match('/\* @version \$Id:.+\$$/', $hunk['a'][0]) && preg_match('/\* @version \$Id:.+\$$/', $hunk['d'][0])) + { + // Only remove this sole hunk... + unset($hunks[$key]); + $reorder = true; + continue; + } + }/**/ + + // Compare the add and replace one... + $string_1 = rtrim(trim(implode('', $hunk['a']))); + $string_2 = rtrim(trim(implode('', $hunk['d']))); + + if (strcmp($string_1, $string_2) === 0) + { + // Only remove this sole hunk... + unset($hunks[$key]); + $reorder = true; + continue; + } + } + } + + if ($reorder) + { + $hunks = array_merge($hunks, array()); + + if (sizeof($hunks) == 1 && sizeof($hunks[0]) == 1 && isset($hunks[0]['c'])) + { + return; + } + else + { + $hunks = $orig_hunks; + } + } + } + + if (sizeof($hunks) == 1 && sizeof($hunks[0]) == 1 && isset($hunks[0]['c'])) + { + return; + } + + $replace = false; + foreach ($hunks as $key => $hunk) + { + if (isset($hunk['d']) && isset($hunk['a'])) + { + $replace = true; + break; + } + } + + $ordering = array(); + $cur_pos = 0; + + // Replace-block + if ($replace) + { + foreach ($hunks as $hunk) + { + if (!isset($hunk['a']) && !isset($hunk['d'])) + { + continue; + } + + if (!empty($hunk['c'])) + { + if (!isset($ordering[$cur_pos]['find_c'])) + { + $ordering[$cur_pos]['find_c'] = $hunk['c']; + } + else + { + $ordering[$cur_pos]['end_c'] = $hunk['c']; + } + } + + // Make sure we begin fresh... + if (!isset($ordering[$cur_pos]['replace'])) + { + $ordering[$cur_pos]['first_find_c'][] = $ordering[$cur_pos]['find_c']; + $ordering[$cur_pos]['find_c'] = array(); + $ordering[$cur_pos]['replace'] = array(); + } + + // Add the middle part if one exist... + if (isset($ordering[$cur_pos]['end_c'])) + { + $ordering[$cur_pos]['find_c'] = array_merge($ordering[$cur_pos]['find_c'], $ordering[$cur_pos]['end_c']); + $ordering[$cur_pos]['replace'] = array_merge($ordering[$cur_pos]['replace'], $ordering[$cur_pos]['end_c']); + unset($ordering[$cur_pos]['end_c']); + } + + if (isset($hunk['d'])) + { + $ordering[$cur_pos]['find_c'] = array_merge($ordering[$cur_pos]['find_c'], $hunk['d']); + } + + if (isset($hunk['a'])) + { + $ordering[$cur_pos]['replace'] = array_merge($ordering[$cur_pos]['replace'], $hunk['a']); + } + } + } + else + { + foreach ($hunks as $hunk) + { + if (!empty($hunk['c'])) + { + if (!isset($ordering[$cur_pos]['find_c'])) + { + $ordering[$cur_pos]['find_c'] = $hunk['c']; + } + else + { + $ordering[$cur_pos]['end_c'] = $hunk['c']; + } + } + + if (!empty($hunk['a'])) + { + if (isset($ordering[$cur_pos]['delete'])) + { + // If ordering is set with an delete entry, we will actually begin a new ordering array (to seperate delete from add) + $cur_pos++; + $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c']; + $ordering[$cur_pos]['add'] = $hunk['a']; + } + else + { + if (isset($ordering[$cur_pos]['add'])) + { + // Now, we really need to be quite careful here... + if (isset($ordering[$cur_pos]['end_c']) && isset($hunk['c']) && isset($hunk['a']) && sizeof($hunk) == 2) + { + // There is a new find/add entry we did not catch... let's try to add a new entry then... but first check the hunk[a] contents... + $text = trim(implode("\n", $hunk['c'])); + if ($text == "\n" || !$text) + { + $ordering[$cur_pos]['add'] = array_merge($ordering[$cur_pos]['add'], array("\n"), $hunk['a']); + } + else if (sizeof($hunk['c']) > 2 || strlen(implode('', $hunk['c'])) > 20) + { + $cur_pos++; + $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c']; + $ordering[$cur_pos]['add'] = $hunk['a']; + } + else + { + $cur_pos++; + $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c']; + $ordering[$cur_pos]['add'] = $hunk['a']; +/* echo 'FIND STATEMENT TOO TINY'; + echo ";".rawurlencode($text).";"; + var_dump($hunk); + exit;*/ + } + } + else + { + echo 'UNCATCHED ENTRY'; + var_dump($hunks); + exit; + } + } + else + { + $ordering[$cur_pos]['add'] = $hunk['a']; + } + } + } + else if (!empty($hunk['d'])) + { + if (isset($ordering[$cur_pos]['add'])) + { + // If ordering is set with an add entry, we will actually begin a new ordering array (to seperate delete from add) + $cur_pos++; + $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c']; + $ordering[$cur_pos]['delete'] = $hunk['d']; + } + else + { + $ordering[$cur_pos]['delete'] = $hunk['d']; + } + } + } + } + + $html = ''; + + return $this->_emit_lines_bb($ybeg, $ordering); + } + + function _diff_header($xbeg, $xlen, $ybeg, $ylen) + { + } +} + + +/** +* A class to format a Diff as MOD Template instuctions. +* +* Usage: +* +* $diff = new Diff($lines1, $lines2); // compute diff. +* +* $fmt = new BBCodeDiffFormatter; +* echo $fmt->format($diff, $lines1); // Output MOD Actions. +*/ +class MODXDiffFormatter extends BBCodeDiffFormatter +{ + function MODXDiffFormatter ($reverse = false, $context_lines = 3, $debug = false) + { + $this->do_reverse_diff = $reverse; + $this->context_lines = $context_lines; + $this->context_prefix = ' '; + $this->deletes_prefix = '-'; + $this->adds_prefix = '+'; + $this->debug = $debug; + } + + function _emit_lines_bb($ybeg, &$ordering) + { + $html = ''; + + // Now adjust for bbcode display... + foreach ($ordering as $order_array) + { + // Skip useless empty lines... + if ($this->skip_lines($order_array, $ordering)) + { + continue; + } + + // Only include double-finds if the last find has very few code location indications... + if (sizeof($order_array['first_find_c'])) + { + $text = implode('', $order_array['find_c']); + if ($text === "\n" || $text === "\t" || $text === '') + { + continue; + } + + if (strlen(implode('', $order_array['find_c'])) < 50) + { + if (substr($html, -8) !== '' . "\n") + { + $html .= ' ' . "\n"; + } + + $html .= ' Around Line ' . $ybeg . '' . "\n"; + $html .= ' ' . htmlspecialchars(implode('', $order_array['first_find_c'][0])) . '' . "\n"; + $ybeg += sizeof($order_array['first_find_c'][0]); + } + } + + if (sizeof($order_array['find_c'])) + { + if (substr($html, -8) !== '' . "\n") + { + $html .= ' ' . "\n"; + } + +// $html .= ' ' . "\n"; + $html .= ' Around Line ' . $ybeg . '' . "\n"; + $html .= ' ' . htmlspecialchars(implode('', $order_array['find_c'])) . '' . "\n"; + } + + if (isset($order_array['replace'])) + { + $html .= ' ' . htmlspecialchars(implode('', $order_array['replace'])) . '' . "\n"; + $html .= ' ' . "\n"; + } + + if (isset($order_array['add'])) + { + $html .= ' ' . htmlspecialchars(implode('', $order_array['add'])) . '' . "\n"; + $html .= ' ' . "\n"; + } + + // There is no DELETE. :o + // Let's try to adjust it then... + if (isset($order_array['delete'])) + { + $text = implode('', $order_array['delete']); + if ($text === "\n" || $text === "\t" || $text === '') + { + continue; + } + + $ybeg += sizeof($order_array['find_c']); + + if (substr($html, -8) !== '' . "\n") + { + $html .= ' ' . "\n"; + } + $html .= ' Around Line ' . $ybeg . ' / Just remove/delete the lines (replacing with an empty line)' . "\n"; + $html .= ' ' . htmlspecialchars(implode('', $order_array['delete'])) . '' . "\n"; + $html .= ' ' . "\n"; + $html .= ' '; + } + } + + return $html; + } + + function format_open($filename) + { + return '' . "\n"; + } + + function format_close($filename) + { + return '' . "\n"; + } + + function _diff_header($xbeg, $xlen, $ybeg, $ylen) + { + } +} diff --git a/build/package.php b/build/package.php new file mode 100755 index 0000000000..e04750d4be --- /dev/null +++ b/build/package.php @@ -0,0 +1,526 @@ +#!/usr/bin/env php +begin_status('Remove temporary files'); + +// Cleanup... +$package->run_command('rm -Rv ' . $package->get('dest_dir')); +$package->run_command('rm -Rv ' . $package->get('diff_dir')); +$package->run_command('rm -Rv ' . $package->get('patch_directory')); +$package->run_command('rm -Rv ' . $package->get('files_directory')); +$package->run_command('rm -Rv ' . $package->get('update_directory')); +$package->run_command('rm -Rv ' . $package->get('release_directory')); + +$package->begin_status('Create new directories'); + +// Make sure the directories got removed +while (file_exists($package->get('update_directory'))) +{ + sleep(1); +} + +if (!file_exists($package->get('dest_dir'))) +{ + $package->run_command('mkdir ' . $package->get('dest_dir')); +} + +if (!file_exists($package->get('diff_dir'))) +{ + $package->run_command('mkdir ' . $package->get('diff_dir')); +} + +if (!file_exists($package->get('patch_directory'))) +{ + $package->run_command('mkdir ' . $package->get('patch_directory')); +} + +if (!file_exists($package->get('files_directory'))) +{ + $package->run_command('mkdir ' . $package->get('files_directory')); +} + +if (!file_exists($package->get('update_directory'))) +{ + $package->run_command('mkdir ' . $package->get('update_directory')); +} + +if (!file_exists($package->get('release_directory'))) +{ + $package->run_command('mkdir ' . $package->get('release_directory')); +} + +$package->begin_status('Copy release files to clean release directory'); + +// Create config.php file +$package->run_command('touch ' . $package->get('dest_dir') . '/config.php'); +//$package->run_command('sudo chown www-data:www-data ' . $package->get('dest_dir') . '/config.php'); + +// Create new directory structure +foreach ($package->clean_directory_structure as $dir => $dir_struct) +{ + $package->create_directory($package->get('dest_dir') . '/' . $dir, $dir_struct); +} + +// First step is to copy the new version over (clean structure) +foreach ($package->files_to_copy as $file) +{ + $source_file = $package->locations['new_version'] . $file; + $dest_file = $package->get('dest_dir') . '/' . $file; + + $package->run_command("cp -p $source_file $dest_file"); +} + +// fix line endings +chdir($package->get('dest_dir')); +$package->run_command($package->locations['new_version'] . 'develop/fix_files.sh'); + +// Now clean up the permissions +$package->begin_status('Adjust permissions'); + +$package->adjust_permissions($package->get('dest_dir')); + +// Now create a version for diffing the version - copy the tree over to old_versions... +$package->begin_status('Create diff directory for obtaining file differences'); + +$package->run_command('cp -Rp ' . $package->get('dest_dir') . '/* ' . $package->get('diff_dir')); +$package->run_command('cp -Rp ' . $package->get('dest_dir') . '/.htaccess ' . $package->get('diff_dir')); + +// Cleanup diff directory (only contents to diff) +foreach ($package->remove_from_diff_structure as $remove_dir) +{ + $package->run_command('rm -Rv ' . $package->get('diff_dir') . '/' . $remove_dir); +} + +// Now, first of all we need to rebuild all old packages we want to support +foreach ($package->old_packages as $package_name => $tag_name) +{ + $package->begin_status('Create old packages directory for diffing to ' . $package_name); + + chdir($package->locations['old_versions']); + + if (is_dir($package->locations['old_versions'] . $package_name)) + { + $package->run_command('rm -Rv ' . $package->locations['old_versions'] . $package_name); + } + + // Now, create a new one... + $tag_name = 'release_' . str_replace(array('.', '_to_'), array('_', ''), $tag_name); + + $package->run_command('svn export --non-interactive http://code.phpbb.com/svn/phpbb/tags/' . $tag_name . '/phpBB/ ' . $package_name); + + $location = $package->locations['old_versions'] . $package_name; + chdir($location . '/'); + + $package->run_command($package->locations['new_version'] . 'develop/fix_files.sh'); + + // Now clean up the permissions + $package->begin_status('Adjust permissions for package ' . $package_name); + + $package->adjust_permissions($location); + + // Cleanup diff directory (only contents to diff) + foreach ($package->remove_from_diff_structure as $remove_dir) + { + $package->run_command('rm -Rv ' . $location . '/' . $remove_dir); + } +} + +// Go trough all versions making a diff if we even have old versions +// For phpBB 3.0.x we might choose a different update method, rendering the things below useless... +if (sizeof($package->old_packages)) +{ + chdir($package->locations['old_versions']); + + // This array is for holding the filenames change + $diff_file_changes = array(); + + foreach ($package->old_packages as $_package_name => $dest_package_filename) + { + $package->begin_status('Creating patch/diff files for phpBB-' . $dest_package_filename . $package->get('new_version_number')); + + $dest_package_filename = $package->get('patch_directory') . '/phpBB-' . $dest_package_filename . $package->get('new_version_number') . '.patch'; + $package->run_command('diff ' . $package->diff_options . ' ' . $_package_name . ' ' . $package->get('simple_name') . ' > ' . $dest_package_filename); + + // Parse this diff to determine file changes from the checked versions and save them + $diff_file_changes[$_package_name] = $package->collect_diff_files($dest_package_filename, $_package_name); + } + + // Now put those files determined within the correct directories + foreach ($diff_file_changes as $_package_name => $file_contents) + { + $package->begin_status('Creating files-only informations for ' . $package->old_packages[$_package_name] . $package->get('new_version_number')); + + $dest_filename_dir = $package->get('files_directory') . '/' . $package->old_packages[$_package_name] . $package->get('new_version_number'); + + if (!file_exists($dest_filename_dir)) + { + $package->run_command('mkdir ' . $dest_filename_dir); + } + + // Now copy the file contents + foreach ($file_contents['all'] as $file) + { + $source_filename = $package->get('dest_dir') . '/' . $file; + $dest_filename = $dest_filename_dir . '/' . $file; + + // Create Directories along the way? + $file = explode('/', $file); + // Remove filename portion + $file[sizeof($file)-1] = ''; + + chdir($dest_filename_dir); + foreach ($file as $entry) + { + $entry = trim($entry); + if ($entry) + { + if (!file_exists('./' . $entry)) + { + $package->run_command('mkdir ' . $entry); + } + chdir('./' . $entry); + } + } + + $package->run_command('cp ' . $source_filename . ' ' . $dest_filename); + } + } + + // Because there might be binary changes, we re-create the patch files... without parsing file differences. + $package->run_command('rm -Rv ' . $package->get('patch_directory')); + + if (!file_exists($package->get('patch_directory'))) + { + $package->run_command('mkdir ' . $package->get('patch_directory')); + } + + chdir($package->locations['old_versions']); + + foreach ($package->old_packages as $_package_name => $dest_package_filename) + { + $package->begin_status('Creating patch/diff files for phpBB-' . $dest_package_filename . $package->get('new_version_number')); + + $dest_package_filename = $package->get('patch_directory') . '/phpBB-' . $dest_package_filename . $package->get('new_version_number') . '.patch'; + $package->run_command('diff ' . $package->diff_options_long . ' ' . $_package_name . ' ' . $package->get('simple_name') . ' > ' . $dest_package_filename); + } + + $packages = $diff_file_changes; + + foreach ($packages as $_package_name => $file_contents) + { + $package->begin_status('Building specific update files for ' . $package->old_packages[$_package_name] . $package->get('new_version_number')); + + $dest_filename_dir = $package->get('update_directory') . '/' . $package->old_packages[$_package_name] . $package->get('new_version_number'); + + if (!file_exists($dest_filename_dir)) + { + $package->run_command('mkdir ' . $dest_filename_dir); + } + + $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/docs ' . $dest_filename_dir); + $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/install ' . $dest_filename_dir); + + $package->run_command('mkdir ' . $dest_filename_dir . '/install/update'); + $package->run_command('mkdir ' . $dest_filename_dir . '/install/update/old'); + $package->run_command('mkdir ' . $dest_filename_dir . '/install/update/new'); + + // Remove some files + $package->run_command('rm -v ' . $dest_filename_dir . '/install/install_install.php'); + $package->run_command('rm -v ' . $dest_filename_dir . '/install/install_convert.php'); + $package->run_command('rm -Rv ' . $dest_filename_dir . '/install/schemas'); + $package->run_command('rm -Rv ' . $dest_filename_dir . '/install/convertors'); + + foreach ($file_contents['all'] as $index => $file) + { + if (strpos($file, 'recode_cjk') !== false) + { + unset($file_contents['all'][$index]); + } + } + + // First of all, fill the 'old' directory + foreach ($file_contents['all'] as $file) + { + $source_filename = $package->locations['old_versions'] . $_package_name . '/' . $file; + $dest_filename = $dest_filename_dir . '/install/update/old/' . $file; + + if (!file_exists($source_filename)) + { + continue; + } + + // Create Directories along the way? + $file = explode('/', $file); + // Remove filename portion + $file[sizeof($file)-1] = ''; + + chdir($dest_filename_dir . '/install/update/old'); + foreach ($file as $entry) + { + $entry = trim($entry); + if ($entry) + { + if (!file_exists('./' . $entry)) + { + $package->run_command('mkdir ' . $entry); + } + chdir('./' . $entry); + } + } + + $package->run_command('cp ' . $source_filename . ' ' . $dest_filename); + } + + // Then fill the 'new' directory + foreach ($file_contents['all'] as $file) + { + $source_filename = $package->locations['old_versions'] . $package->get('simple_name') . '/' . $file; + $dest_filename = $dest_filename_dir . '/install/update/new/' . $file; + + if (!file_exists($source_filename)) + { + continue; + } + + // Create Directories along the way? + $file = explode('/', $file); + // Remove filename portion + $file[sizeof($file)-1] = ''; + + chdir($dest_filename_dir . '/install/update/new'); + foreach ($file as $entry) + { + $entry = trim($entry); + if ($entry) + { + if (!file_exists('./' . $entry)) + { + $package->run_command('mkdir ' . $entry); + } + chdir('./' . $entry); + } + } + + $package->run_command('cp ' . $source_filename . ' ' . $dest_filename); + } + + // Build index.php file for holding the file structure + $index_contents = ' array(\'from\' => \'' . str_replace('_to_', '', $package->old_packages[$_package_name]) . '\', \'to\' => \'' . $package->get('new_version_number') . '\'), +'; + + if (sizeof($file_contents['all'])) + { + $index_contents .= '\'files\' => array(\'' . implode("',\n\t'", $file_contents['all']) . '\'), +'; + } + else + { + $index_contents .= '\'files\' => array(), +'; + } + + if (sizeof($file_contents['binary'])) + { + $index_contents .= '\'binary\' => array(\'' . implode("',\n\t'", $file_contents['binary']) . '\'), +'; + } + else + { + $index_contents .= '\'binary\' => array(), +'; + } + + $index_contents .= '); + +?' . '>'; + + $fp = fopen($dest_filename_dir . '/install/update/index.php', 'wt'); + fwrite($fp, $index_contents); + fclose($fp); + } + unset($diff_file_changes); + + $package->begin_status('Clean up all install files'); + + // Copy the install files to their respective locations + $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/docs ' . $package->get('patch_directory')); + $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/install ' . $package->get('patch_directory')); + + // Remove some files + chdir($package->get('patch_directory') . '/install'); + + $package->run_command('rm -v install_install.php'); + $package->run_command('rm -v install_update.php'); + $package->run_command('rm -v install_convert.php'); + $package->run_command('rm -Rv schemas'); + $package->run_command('rm -Rv convertors'); +} + +// Build Main phpBB Release +$compress_programs = array( +// 'tar.gz' => 'tar -czf', + 'tar.bz2' => 'tar -cjf', + 'zip' => 'zip -r' +); + +if (sizeof($package->old_packages)) +{ + // Build Patch Files + chdir($package->get('patch_directory')); + + foreach ($compress_programs as $extension => $compress_command) + { + $package->begin_status('Packaging phpBB Patch Files for ' . $extension); + $package->run_command('rm -v ../release_files/' . $package->get('release_filename') . '-patch.' . $extension); + + // Build Package + $package->run_command($compress_command . ' ../release_files/' . $package->get('release_filename') . '-patch.' . $extension . ' *'); + + // Build MD5 Sum + $package->run_command('md5sum ../release_files/' . $package->get('release_filename') . '-patch.' . $extension . ' > ../release_files/' . $package->get('release_filename') . '-patch.' . $extension . '.md5'); + } + + // Build Files Package + chdir($package->get('files_directory')); + + foreach ($compress_programs as $extension => $compress_command) + { + $package->begin_status('Packaging phpBB Files for ' . $extension); + + $package->run_command('rm -v ../release_files/' . $package->get('release_filename') . '-files.' . $extension); + $package->run_command('mkdir ' . $package->get('files_directory') . '/release'); + $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/docs ' . $package->get('files_directory') . '/release'); + $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/install ' . $package->get('files_directory') . '/release'); + + $package->run_command('rm -v ' . $package->get('files_directory') . '/release/install/install_install.php'); + $package->run_command('rm -v ' . $package->get('files_directory') . '/release/install/install_update.php'); + $package->run_command('rm -v ' . $package->get('files_directory') . '/release/install/install_convert.php'); + $package->run_command('rm -Rv ' . $package->get('files_directory') . '/release/install/schemas'); + $package->run_command('rm -Rv ' . $package->get('files_directory') . '/release/install/convertors'); + + // Pack files + foreach ($package->old_packages as $_package_name => $package_path) + { + chdir($package_path . $package->get('new_version_number')); + $command = ($extension == 'zip') ? 'zip -r' : 'tar cf'; + $_ext = ($extension == 'zip') ? 'zip' : 'tar'; + $package->run_command("$command ../release/phpBB-$package_path" . $package->get('new_version_number') . ".$_ext *"); + chdir('..'); + } + + chdir('./release'); + $package->run_command("$compress_command ../../release_files/" . $package->get('release_filename') . '-files.' . $extension . ' *'); + // Build MD5 Sum + $package->run_command('md5sum ../../release_files/' . $package->get('release_filename') . '-files.' . $extension . ' > ../../release_files/' . $package->get('release_filename') . '-files.' . $extension . '.md5'); + chdir('..'); + + $package->run_command('rm -Rv ' . $package->get('files_directory') . '/release'); + } + + // Build Update Package + foreach ($compress_programs as $extension => $compress_command) + { + chdir($package->get('update_directory')); + + $package->begin_status('Packaging phpBB Update for ' . $extension); + + $package->run_command('rm -v ../release_files/' . $package->get('release_filename') . '-update.' . $extension); + $package->run_command('mkdir ' . $package->get('update_directory') . '/release'); + + // Pack update files + $packages = $package->old_packages; + + foreach ($packages as $_package_name => $package_path) + { + chdir($package_path . $package->get('new_version_number')); + + $package->run_command('rm -v install/install_install.php'); + $package->run_command('rm -v install/install_convert.php'); + $package->run_command('rm -v includes/utf/data/recode_cjk.php'); + $package->run_command('rm -Rv install/schemas'); + $package->run_command('rm -Rv install/convertors'); + + $command = ($extension == 'zip') ? 'zip -r' : 'tar cf'; + $_ext = ($extension == 'zip') ? 'zip' : 'tar'; + $package->run_command("$command ../release/$package_path" . $package->get('new_version_number') . ".$_ext *"); + chdir('..'); + + $last_version = $package_path . $package->get('new_version_number'); + +// chdir('./release'); +// $package->run_command("$compress_command ../../release_files/" . $package->get('release_filename') . '-update.' . $extension . ' *'); +// chdir('..'); + + chdir('./' . $last_version); + // Copy last package over... + $package->run_command('rm -v ../release_files/phpBB-' . $last_version . ".$extension"); + $package->run_command("$compress_command ../../release_files/phpBB-$last_version.$extension *"); + + // Build MD5 Sum + $package->run_command("md5sum ../../release_files/phpBB-$last_version.$extension > ../../release_files/phpBB-$last_version.$extension.md5"); + chdir('..'); + } + + $package->run_command('rm -Rv ' . $package->get('update_directory') . '/release'); + } + +} + +// Delete updater and convertor from main archive +chdir($package->get('dest_dir') . '/install'); + +// $package->run_command('rm -v database_update.php'); +$package->run_command('rm -v install_update.php'); + +chdir($package->locations['package_dir']); +foreach ($compress_programs as $extension => $compress_command) +{ + $package->begin_status('Packaging phpBB for ' . $extension); + $package->run_command('rm -v ./release_files/' . $package->get('release_filename') . ".{$extension}"); + + // Build Package + $package->run_command("$compress_command ./release_files/" . $package->get('release_filename') . '.' . $extension . ' ' . $package->get('package_name')); + + // Build MD5 Sum + $package->run_command('md5sum ./release_files/' . $package->get('release_filename') . '.' . $extension . ' > ./release_files/' . $package->get('release_filename') . '.' . $extension . '.md5'); +} + +// verify results +chdir($package->locations['root']); +$package->begin_status('********** Verifying packages **********'); +$package->run_command('./compare.sh ' . $package->package_infos['release_filename']); + +echo "Done.\n"; diff --git a/phpBB/.htaccess b/phpBB/.htaccess index 41fb129175..474f9774c2 100644 --- a/phpBB/.htaccess +++ b/phpBB/.htaccess @@ -1,3 +1,13 @@ +# +# Uncomment the statement below if you want to make use of +# HTTP authentication and it does not already work. +# This could be required if you are for example using PHP via Apache CGI. +# +# +#RewriteEngine on +#RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] +# + Order Allow,Deny Deny from All diff --git a/phpBB/adm/style/acp_captcha.html b/phpBB/adm/style/acp_captcha.html index d31527b64d..8eee370284 100644 --- a/phpBB/adm/style/acp_captcha.html +++ b/phpBB/adm/style/acp_captcha.html @@ -21,6 +21,10 @@

{L_REG_LIMIT_EXPLAIN}
+
+

{L_MAX_LOGIN_ATTEMPTS_EXPLAIN}
+
+

{L_VISUAL_CONFIRM_POST_EXPLAIN}
diff --git a/phpBB/adm/style/acp_groups.html b/phpBB/adm/style/acp_groups.html index a513e69a8a..07f7d072e8 100644 --- a/phpBB/adm/style/acp_groups.html +++ b/phpBB/adm/style/acp_groups.html @@ -120,7 +120,7 @@

{L_LINK_REMOTE_SIZE_EXPLAIN}
-
px X px
+
{L_PIXEL} × {L_PIXEL}
diff --git a/phpBB/adm/style/acp_icons.html b/phpBB/adm/style/acp_icons.html index 10166fec35..eedf39b440 100644 --- a/phpBB/adm/style/acp_icons.html +++ b/phpBB/adm/style/acp_icons.html @@ -139,9 +139,9 @@ - + {S_ADD_ORDER_LIST_DISPLAY} + {S_ADD_ORDER_LIST_UNDISPLAY} diff --git a/phpBB/adm/style/acp_jabber.html b/phpBB/adm/style/acp_jabber.html index 2cc715493a..0c4512ba98 100644 --- a/phpBB/adm/style/acp_jabber.html +++ b/phpBB/adm/style/acp_jabber.html @@ -38,7 +38,7 @@
-
+

{L_JAB_PASSWORD_EXPLAIN}
diff --git a/phpBB/adm/style/acp_main.html b/phpBB/adm/style/acp_main.html index 46d65df7a2..7d3b6945ac 100644 --- a/phpBB/adm/style/acp_main.html +++ b/phpBB/adm/style/acp_main.html @@ -40,6 +40,12 @@ + +
+

{L_PHP_VERSION_OLD}

+
+ + diff --git a/phpBB/adm/style/acp_users_avatar.html b/phpBB/adm/style/acp_users_avatar.html index deed41e16e..2b2676a19a 100644 --- a/phpBB/adm/style/acp_users_avatar.html +++ b/phpBB/adm/style/acp_users_avatar.html @@ -27,7 +27,7 @@

{L_LINK_REMOTE_SIZE_EXPLAIN}
-
px X px
+
{L_PIXEL} × {L_PIXEL}
diff --git a/phpBB/adm/style/captcha_qa_acp_demo.html b/phpBB/adm/style/captcha_qa_acp_demo.html index 57bed8af53..0ea2d6a024 100644 --- a/phpBB/adm/style/captcha_qa_acp_demo.html +++ b/phpBB/adm/style/captcha_qa_acp_demo.html @@ -1,5 +1,5 @@
-

{L_CONFIRM_QUESTION_EXPLAIN}
+

{L_CONFIRM_QUESTION_EXPLAIN}
diff --git a/phpBB/adm/style/confirm_bbcode.html b/phpBB/adm/style/confirm_bbcode.html new file mode 100644 index 0000000000..52a6523984 --- /dev/null +++ b/phpBB/adm/style/confirm_bbcode.html @@ -0,0 +1,22 @@ + + +
+
+

{L_WARNING}

+

{MESSAGE_TEXT}

+
+
+ + + {S_HIDDEN_FIELDS} + +
+   + +
+ +
+ + + + diff --git a/phpBB/adm/style/editor.js b/phpBB/adm/style/editor.js index cd4e75f51f..7e3ce1c708 100644 --- a/phpBB/adm/style/editor.js +++ b/phpBB/adm/style/editor.js @@ -152,7 +152,7 @@ function insert_text(text, spaces, popup) var sel_start = textarea.selectionStart; var sel_end = textarea.selectionEnd; - mozWrap(textarea, text, '') + mozWrap(textarea, text, ''); textarea.selectionStart = sel_start + text.length; textarea.selectionEnd = sel_end + text.length; } @@ -269,7 +269,7 @@ function mozWrap(txtarea, open, close) } var s1 = (txtarea.value).substring(0,selStart); - var s2 = (txtarea.value).substring(selStart, selEnd) + var s2 = (txtarea.value).substring(selStart, selEnd); var s3 = (txtarea.value).substring(selEnd, selLength); txtarea.value = s1 + open + s2 + close + s3; diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php index b9017937c8..4fb7b0d8f7 100644 --- a/phpBB/develop/create_schema_files.php +++ b/phpBB/develop/create_schema_files.php @@ -266,7 +266,8 @@ foreach ($supported_dbms as $dbms) case 'mssql': $line = "/*\n\n \$I" . "d: $\n\n*/\n\n"; - $line .= "BEGIN TRANSACTION\nGO\n\n"; + // no need to do this, no transaction support for schema changes + //$line .= "BEGIN TRANSACTION\nGO\n\n"; break; case 'oracle': @@ -749,7 +750,8 @@ foreach ($supported_dbms as $dbms) switch ($dbms) { case 'mssql': - $line = "\nCOMMIT\nGO\n\n"; + // No need to do this, no transaction support for schema changes + //$line = "\nCOMMIT\nGO\n\n"; break; case 'sqlite': @@ -1747,6 +1749,7 @@ function get_schema_struct() ), 'PRIMARY_KEY' => array('user_id', 'topic_id'), 'KEYS' => array( + 'topic_id' => array('INDEX', 'topic_id'), 'forum_id' => array('INDEX', 'forum_id'), ), ); diff --git a/phpBB/develop/fix_files.sh b/phpBB/develop/fix_files.sh index 782418f504..d2207289dc 100755 --- a/phpBB/develop/fix_files.sh +++ b/phpBB/develop/fix_files.sh @@ -21,9 +21,9 @@ rm FILELIST.$$ for i in $(cat FILELIST); do if [ -f $i ]; then - sed -e s/ -//g $i > $i.tmp - mv $i.tmp $i + cat $i | tr -d '\r' > $i.tmp + mv $i.tmp $i fi done rm FILELIST + diff --git a/phpBB/develop/generate_utf_confusables.php b/phpBB/develop/generate_utf_confusables.php index f79471afc6..0ffa9720c9 100644 --- a/phpBB/develop/generate_utf_confusables.php +++ b/phpBB/develop/generate_utf_confusables.php @@ -114,10 +114,14 @@ $uniarray = array( $copy = $uniarray; +/** +* @todo we need to check that the $uniarray does not reverse any of the mappings defined in the unicode definition +*/ + foreach ($array as $value) { $temp_hold = implode(array_map('utf8_chr', array_map('hexdec', explode(' ', trim($value[2]))))); - + if (isset($copy[utf8_chr(hexdec((string)$value[1]))])) { $num = ''; diff --git a/phpBB/develop/mysql_upgrader.php b/phpBB/develop/mysql_upgrader.php index a677f4464b..85da1dfa47 100644 --- a/phpBB/develop/mysql_upgrader.php +++ b/phpBB/develop/mysql_upgrader.php @@ -1237,6 +1237,7 @@ function get_schema_struct() ), 'PRIMARY_KEY' => array('user_id', 'topic_id'), 'KEYS' => array( + 'topic_id' => array('INDEX', 'topic_id'), 'forum_id' => array('INDEX', 'forum_id'), ), ); diff --git a/phpBB/docs/AUTHORS b/phpBB/docs/AUTHORS index 63540ea8a0..e886cd5a89 100644 --- a/phpBB/docs/AUTHORS +++ b/phpBB/docs/AUTHORS @@ -1,6 +1,6 @@ /** * -* phpBB3 Copyright 2000, 2002, 2005, 2007 phpBB Group +* phpBB3 © Copyright 2000, 2002, 2005, 2007 phpBB Group * http://www.phpbb.com * * This program is free software: you can redistribute it and/or modify @@ -20,39 +20,41 @@ Please see: http://www.phpbb.com/about/team/ for a list of all the people currently involved in phpBB. -phpBB Lead Developer : Acyd Burn (Meik Sievertsen) +phpBB Lead Developer: naderman (Nils Adermann) -phpBB Developers : APTX (Marek A. R.) - bantu (Andreas Fischer) - DavidMJ (David M.) - dhn (Dominik Drscher) - kellanved (Henry Sudhof) - naderman (Nils Adermann) - Terrafrost (Jim Wigginton) - ToonArmy (Chris Smith) +phpBB Developers: A_Jelly_Doughnut (Josh Woody) + Acyd Burn (Meik Sievertsen) [Lead 09/2005 - 01/2010] + APTX (Marek A. R.) + bantu (Andreas Fischer) + DavidMJ (David M.) + dhn (Dominik Dröscher) + kellanved (Henry Sudhof) + Terrafrost (Jim Wigginton) + ToonArmy (Chris Smith) -Contributions by : leviatan21 (Gabriel Vazquez) - nickvergessen (Joas Schilling) - Raimon (Raimon Meuldijk) - rxu (Ruslan Uzdenov) - Xore (Robert Hetzler) +Contributions by: Brainy (Cullen Walsh) + leviatan21 (Gabriel Vazquez) + nickvergessen (Joas Schilling) + Raimon (Raimon Meuldijk) + rxu (Ruslan Uzdenov) + Xore (Robert Hetzler) --- Previous Contributors -- +-- Former Contributors -- -phpBB Project Manager : theFinn (James Atkinson) [Founder - 04/2007] - SHS` (Jonathan Stanley) +phpBB Project Manager: theFinn (James Atkinson) [Founder - 04/2007] + SHS` (Jonathan Stanley) -phpBB Lead Developer : psoTFX (Paul S. Owen) [2001 - 09/2005] +phpBB Lead Developer: psoTFX (Paul S. Owen) [2001 - 09/2005] -phpBB Developers : Ashe (Ludovic Arnaud) [10/2002 - 11/2003, 06/2006 - 10/2006] - BartVB (Bart van Bragt) [11/2000 - 03/2006] - GrahamJE (Graham Eames) [09/2005 - 11/2006] - Vic D'Elfant (Vic D'Elfant) [04/2007 - 04/2009] +phpBB Developers: Ashe (Ludovic Arnaud) [10/2002 - 11/2003, 06/2006 - 10/2006] + BartVB (Bart van Bragt) [11/2000 - 03/2006] + GrahamJE (Graham Eames) [09/2005 - 11/2006] + Vic D'Elfant (Vic D'Elfant) [04/2007 - 04/2009] -- Copyrights -- -Visual Confirmation : Xore (Robert Hetzler) +Visual Confirmation: Xore (Robert Hetzler) Original subSilver by subBlue Design, Tom Beddard, (c) 2001 phpBB Group prosilver by subBlue Design, Tom Beddard, (c) 2004 phpBB Group diff --git a/phpBB/docs/CHANGELOG.html b/phpBB/docs/CHANGELOG.html index 3e4bb9c6c7..3042027e83 100644 --- a/phpBB/docs/CHANGELOG.html +++ b/phpBB/docs/CHANGELOG.html @@ -53,6 +53,7 @@
  1. Changelog
      +
    1. Changes since 3.0.7
    2. Changes since 3.0.6
    3. Changes since 3.0.5
    4. Changes since 3.0.4
    5. @@ -86,7 +87,22 @@
      -

      1.i. Changes since 3.0.6

      + +

      1.i. Changes since 3.0.7

      + +
        +
      • [Fix] Correctly sort database backup file list by date on database restore page. (Bug #57385)
      • +
      • [Fix] Take admin's time zone settings into account when listing database backup files. (Bug #57385)
      • +
      • [Fix] Honor minimum and maximum password length in generated passwords as much as we can. (Bug #13181)
      • +
      • [Fix] No longer return the character O in generated random strings and passwords. (Bug #57345)
      • +
      • [Fix] Allow redirect() function to redirect across directories. (Bug #56965)
      • +
      • [Fix] Add terminating semicolons to JavaScript code. (Bug #58085 - Patch by nn-)
      • +
      • [Fix] Minor language fixes. (Bug #54855)
      • +
      • [Fix] Parsing urls in signatures properly uses config settings. (Bug #57105)
      • +
      • [Feature] Support for Microsoft's Native SQL Server Driver for PHP (Bug #57055 - Patch by Chris Pucci at Microsoft)
      • +
      + +

      1.ii. Changes since 3.0.6

      • [Fix] Allow ban reason and length to be selected and copied in ACP and subsilver2 MCP. (Bug #51095)
      • @@ -98,7 +114,7 @@
      • [Fix] Do not link to user profile in ATOM feed entry if post has been made by the guest user. (Bug #54275)
      • [Fix] Make word censoring case insensitive. (Bug #54265)
      • [Fix] Fulltext-MySQL search for keywords and username at the same time. (Bug #54325)
      • -
      • [Fix] Various XHTML mistakes in prosilver. (Bug #54705)
      • +
      • [Fix] Various XHTML and CSS mistakes in prosilver and subsilver2. (Bugs #54705, #55895, #57505, #57875 - Patch by HardStyle)
      • [Fix] Correctly show topic ATOM feed link when only post id is specified. (Bug #53025)
      • [Fix] Cleanly handle forum/topic not found in ATOM Feeds. (Bug #54295)
      • [Fix] PHP 5.3 compatibility: Check if function dl() exists before calling it. (Bug #54665)
      • @@ -119,15 +135,78 @@
      • [Fix] Correct wording for "How do I show an image below my username" question answer in FAQ. (Bug #23935)
      • [Fix] Handle export of private messages where all recipients were deleted. (Bug #50985)
      • [Fix] Correctly get unread status information for global announcements in search results.
      • +
      • [Fix] Correctly handle global announcements in ATOM feeds.
      • +
      • [Fix] Use correct limit config parameter in the News feed.
      • +
      • [Fix] Restrict search for styles/../style.cfg to folders. (Bug #55665)
      • +
      • [Fix] Add ability to disable overall (aka board-wide) feed.
      • +
      • [Fix] Do not pass new_link parameter when creating a persistent connection with mysql. (Bug #55785)
      • +
      • [Fix] Improved search query performance through sorting words by their occurance. (Bug #21555)
      • +
      • [Fix] Correctly move sql_row_pointer forward when calling sql_fetchfield() on cached queries. (Bug #55865)
      • +
      • [Fix] Remove item limit from "All forums" feed.
      • +
      • [Fix] Do not use group colours for usernames on print view. (Bug #30315 - Patch by Pasqualle)
      • +
      • [Fix] Pagination of User Notes in MCP uses two different config values. (Bug #56025)
      • +
      • [Fix] List hidden groups on viewprofile where the viewing user is also a member. (Bug #31845)
      • +
      • [Fix] Sort viewprofile group list by group name.
      • +
      • [Fix] Strictly check whether a moderator can post in the destination forum when moving topic. (Bug #56255)
      • +
      • [Fix] Added some error handling to the compress class.
      • +
      • [Fix] Correctly determine permissions to show quick reply button. (Bug #56555)
      • +
      • [Fix] Do not unsubscribe users from topics replying with quickreply. (Bug #56235)
      • +
      • [Fix] Don't submit when pressing enter on preview button. (Bug #54395)
      • +
      • [Fix] Load reCAPTCHA over https when using a secure connection to the board. (Bug #55755)
      • +
      • [Fix] Clarify explanation of bump feature setting. (Bug #56075)
      • +
      • [Fix] Properly paginate unapproved posts in the MCP. (Bug #56285)
      • +
      • [Fix] Do not duplicate previous/next links in pagination text of moderator logs and user notes in MCP for subsilver2. (Bug #55045)
      • +
      • [Fix] Do not automatically unsubscribe users from topics, when email and jabber is disabled.
      • +
      • [Fix] Don't send activation email when user tries to change email without permission. (Bug #56335 - Fix by nrohler)
      • +
      • [Fix] Replace hard coded "px" with translated language-string. (Bug #52495)
      • +
      • [Fix] Correctly hover list menu in UCP and MCP for RTL languages. (Bug #49945)
      • +
      • [Fix] Correctly orientate quoted text image on RTL languages. (Bug #33745)
      • +
      • [Fix] Deprecate $allow_reply parameter to truncate_string() (Bug #56675)
      • +
      • [Fix] Fall back to default language email template if specified file does not exist. (Bug #35595)
      • +
      • [Fix] Update users last visit field correctly when changing activation status. (Bug #56185)
      • +
      • [Fix] Database updater now separates ADD COLUMN from SET NOT NULL and SET DEFAULT, when using PostgreSQL <= 7.4 (Bug #54435)
      • +
      • [Fix] Styles adjustment to correctly display an order of rtl/ltr mixed content. (Bugs #55485, #55545)
      • +
      • [Fix] Fix language string for PM-Reports refering to post-data. (Bug #54745)
      • +
      • [Fix] Do not store email templates in database. (Bug #54505)
      • +
      • [Fix] Fix javascript bug in the smilies ACP. (Bug #55725)
      • +
      • [Fix] Unify BBCode Selection across browsers. (Bug #38765)
      • +
      • [Fix] Allow convertors to read in configuration from files. (Bug #57265 - Patch by Dicky)
      • +
      • [Fix] Fix problems with firebird by no longer using 'count' as a column alias. (Bug #57455)
      • +
      • [Fix] Small language correction for the FAQ page. (Bug #57825)
      • +
      • [Fix] Restrict search for language/../iso.txt to folders. (Bug #57795)
      • +
      • [Fix] Make user_email_hash() function independent from system's architecture. (Bug #57755)
      • +
      • [Fix] Correct behavior of "force_approved_state" when value is false. (Bug #57715)
      • +
      • [Fix] Global announcements could not be accessed on a board using Firebird as the database server. (Bug #57525)
      • +
      • [Fix] BBCode parser now uses the user object for all settings rather than taking some from the template object (Bug #57365)
      • +
      • [Fix] Ensure a database connection is available before logging general errors. (Bug #57975)
      • +
      • [Fix] Do not delete unrelated attachments when deleting empty forums. (Bug #57375)
      • +
      • [Fix] Update: Store expected resulting file contents in cache and do not suggest further merges if the contents match, also fixes infinite merge loop (Bug #54075)
      • [Change] Move redirect into a hidden field to avoid issues with mod_security. (Bug #54145)
      • [Change] Log activation through inactive users ACP. (Bug #30145)
      • [Change] Send time of last item instead of current time in ATOM Feeds. (Bug #53305)
      • [Change] Use em dash instead of hyphen/minus as separator in ATOM Feeds item statistics. (Bug #53565)
      • -
      • [Change] Cache overall, forums, topics and news feeds for anonymous users and bots.
      • [Change] Alter ACP user quick tools interface to reduce confusion with the delete operation.
      • +
      • [Change] Send statistics now check for IPv6 and send private network status as a boolean.
      • +
      • [Change] Split "All topics" feed into "New Topics" and "Active Topics" feeds.
      • +
      • [Change] Forum feed no longer includes posts of subforums.
      • +
      • [Change] Show login attempt CAPTCHA option in the captcha plugin module.
      • +
      • [Change] It is no longer possible to persist a solution for the login CAPTCHA.
      • +
      • [Change] SQLite is no longer autoloaded by the installer. (Bug #56105)
      • +
      • [Change] Friends and foes will not show up as private message rule options if their respective UCP modules are disabled. (Bug #51155)
      • +
      • [Change] Offer for guests to log in for egosearch and unreadposts search before the search permissions check. (Bug #51585)
      • +
      • [Change] Show warning box for users of PHP < 5.2.0 about phpBB ending support.
      • +
      • [Change] Disallow deleting the last question of the Q&A CAPTCHA.
      • +
      • [Change] Tweak Q&A CAPTCHA garbage collection.
      • +
      • [Change] Show a proper preview for the Q&A CAPTCHA. (Bug #56365)
      • +
      • [Change] Speed up topic move operation by adding an index for topic_id on the topics track table. (Bug #56545)
      • +
      • [Change] Warn users about potentially dangerous BBcodes.
      • +
      • [Feature] Ability to use HTTP authentication in ATOM feeds by passing the GET parameter "auth=http".
      • +
      • [Feature] Add INTTEXT token type to custom bbcodes to allow non-ASCII letters in html attributes.
      • +
      • [Feature] Add ability to enable quick reply in all forums.
      • +
      -

      1.ii. Changes since 3.0.5

      +

      1.iii. Changes since 3.0.5

      • [Fix] Allow whitespaces in avatar gallery names. (Bug #44955)
      • @@ -349,7 +428,7 @@
      • [Feature] Send anonymous statistical information to phpBB on installation and update (optional).
      -

      1.iii. Changes since 3.0.4

      +

      1.iv. Changes since 3.0.4

      • [Fix] Delete user entry from ban list table upon user deletion (Bug #40015 - Patch by TerraFrost)
      • @@ -438,7 +517,7 @@
      • [Sec] Only use forum id supplied for posting if global announcement detected. (Reported by nickvergessen)
      -

      1.iv. Changes since 3.0.3

      +

      1.v. Changes since 3.0.3

      • [Fix] Allow mixed-case template directories to be inherited (Bug #36725)
      • @@ -470,7 +549,7 @@
      • [Sec] Ask for forum password if post within passworded forum quoted in private message. (Reported by nickvergessen)
      -

      1.v. Changes since 3.0.2

      +

      1.vi. Changes since 3.0.2

      • [Fix] Correctly set topic starter if first post in topic removed (Bug #30575 - Patch by blueray2048)
      • @@ -569,7 +648,7 @@
      • [Sec Precaution] Stricter validation of the HTTP_HOST header (Thanks to Techie-Micheal et al for pointing out possible issues in derived code)
      -

      1.vi. Changes since 3.0.1

      +

      1.vii. Changes since 3.0.1

      • [Fix] Ability to set permissions on non-mysql dbms (Bug #24955)
      • @@ -617,7 +696,7 @@
      • [Sec] Only allow urls gone through redirect() being used within login_box(). (thanks nookieman)
      -

      1.vii Changes since 3.0.0

      +

      1.viii Changes since 3.0.0

      • [Change] Validate birthdays (Bug #15004)
      • @@ -688,7 +767,7 @@
      • [Fix] Find and display colliding usernames correctly when converting from one database to another (Bug #23925)
      -

      1.viii. Changes since 3.0.RC8

      +

      1.ix. Changes since 3.0.RC8

      • [Fix] Cleaned usernames contain only single spaces, so "a_name" and "a__name" are treated as the same name (Bug #15634)
      • @@ -697,7 +776,7 @@
      • [Fix] Call garbage_collection() within database updater to correctly close connections (affects Oracle for example)
      -

      1.ix. Changes since 3.0.RC7

      +

      1.x. Changes since 3.0.RC7

      • [Fix] Fixed MSSQL related bug in the update system
      • @@ -732,7 +811,7 @@
      • [Fix] No duplication of active topics (Bug #15474)
      -

      1.x. Changes since 3.0.RC6

      +

      1.xi. Changes since 3.0.RC6

      • [Fix] Submitting language changes using acp_language (Bug #14736)
      • @@ -742,7 +821,7 @@
      • [Fix] Able to request new password (Bug #14743)
      -

      1.xi. Changes since 3.0.RC5

      +

      1.xii. Changes since 3.0.RC5

      • [Feature] Removing constant PHPBB_EMBEDDED in favor of using an exit_handler(); the constant was meant to achive this more or less.
      • @@ -805,7 +884,7 @@
      • [Sec] New password hashing mechanism for storing passwords (#i42)
      -

      1.xii. Changes since 3.0.RC4

      +

      1.xiii. Changes since 3.0.RC4

      • [Fix] MySQL, PostgreSQL and SQLite related database fixes (Bug #13862)
      • @@ -856,7 +935,7 @@
      • [Fix] odbc_autocommit causing existing result sets to be dropped (Bug #14182)
      -

      1.xiii. Changes since 3.0.RC3

      +

      1.xiv. Changes since 3.0.RC3

      • [Fix] Fixing some subsilver2 and prosilver style issues
      • @@ -965,7 +1044,7 @@
      -

      1.xiv. Changes since 3.0.RC2

      +

      1.xv. Changes since 3.0.RC2

      • [Fix] Re-allow searching within the memberlist
      • @@ -1011,7 +1090,7 @@
      -

      1.xv. Changes since 3.0.RC1

      +

      1.xvi. Changes since 3.0.RC1

      • [Fix] (X)HTML issues within the templates (Bug #11255, #11255)
      • diff --git a/phpBB/docs/INSTALL.html b/phpBB/docs/INSTALL.html index c297abe267..c434686357 100644 --- a/phpBB/docs/INSTALL.html +++ b/phpBB/docs/INSTALL.html @@ -273,7 +273,7 @@

        This package is meant for those wanting to only replace changed files from a previous version to the latest version. This package normally contains the changed files from up to five previous versions.

        -

        This package contains a number of archives, each contains the files changed from a given release to the latest version. You should select the appropriate archive for your current version, e.g. if you currently have 3.0.5 you should select the phpBB-3.0.5_to_3.0.6.zip/tar.gz file.

        +

        This package contains a number of archives, each contains the files changed from a given release to the latest version. You should select the appropriate archive for your current version, e.g. if you currently have 3.0.6 you should select the phpBB-3.0.6_to_3.0.7.zip/tar.gz file.

        The directory structure has been preserved enabling you (if you wish) to simply upload the contents of the archive to the appropriate location on your server, i.e. simply overwrite the existing files with the new versions. Do not forget that if you have installed any MODs these files will overwrite the originals possibly destroying them in the process. You will need to re-add MODs to any affected file before uploading.

        @@ -285,7 +285,7 @@

        The patch file is one solution for those with many Modifications (MODs) or other changes who do not want to re-add them back to all the changed files if they use the method explained above. To use this you will need command line access to a standard UNIX type patch application. If you do not have access to such an application but still want to use this update approach, we strongly recommend the Automatic update package explained below. It is also the recommended update method.

        -

        A number of patch files are provided to allow you to update from previous stable releases. Select the correct patch, e.g. if your current version is 3.0.5 you need the phpBB-3.0.5_to_3.0.6.patch file. Place the correct patch in the parent directory containing the phpBB3 core files (i.e. index.php, viewforum.php, etc.). With this done you should run the following command: patch -cl -d [PHPBB DIRECTORY] -p1 < [PATCH NAME] (where PHPBB DIRECTORY is the directory name your phpBB Installation resides in, for example phpBB3, and where PATCH NAME is the relevant filename of the selected patch file). This should complete quickly, hopefully without any HUNK FAILED comments.

        +

        A number of patch files are provided to allow you to update from previous stable releases. Select the correct patch, e.g. if your current version is 3.0.6 you need the phpBB-3.0.6_to_3.0.7.patch file. Place the correct patch in the parent directory containing the phpBB3 core files (i.e. index.php, viewforum.php, etc.). With this done you should run the following command: patch -cl -d [PHPBB DIRECTORY] -p1 < [PATCH NAME] (where PHPBB DIRECTORY is the directory name your phpBB Installation resides in, for example phpBB3, and where PATCH NAME is the relevant filename of the selected patch file). This should complete quickly, hopefully without any HUNK FAILED comments.

        If you do get failures you should look at using the Changed files only package to replace the files which failed to patch, please note that you will need to manually re-add any Modifications (MODs) to these particular files. Alternatively if you know how you can examine the .rej files to determine what failed where and make manual adjustments to the relevant source.

        diff --git a/phpBB/docs/coding-guidelines.html b/phpBB/docs/coding-guidelines.html index 8ac2e4e89d..7f747e09e2 100644 --- a/phpBB/docs/coding-guidelines.html +++ b/phpBB/docs/coding-guidelines.html @@ -69,7 +69,7 @@
      • General Guidelines
  2. -
  3. Styling
  4. +
  5. Styling
    1. Style Config Files
    2. General Styling Rules
    3. @@ -125,7 +125,7 @@

      If entered with tabs (replace the {TAB}) both equal signs need to be on the same column.

      Linefeeds:

      -

      Ensure that your editor is saving files in the UNIX (LF) line ending format. This means that lines are terminated with a newline, not with Windows Line endings (CR/LF combo) as they are on Win32 or Classic Mac (CR) Line endings. Any decent editor should be able to do this, but it might not always be the default setting. Know your editor. If you want advice for an editor for your Operating System, just ask one of the developers. Some of them do their editing on Win32. +

      Ensure that your editor is saving files in the UNIX (LF) line ending format. This means that lines are terminated with a newline, not with Windows Line endings (CR/LF combo) as they are on Win32 or Classic Mac (CR) Line endings. Any decent editor should be able to do this, but it might not always be the default setting. Know your editor. If you want advice for an editor for your Operating System, just ask one of the developers. Some of them do their editing on Win32.

      1.ii. File Header

      @@ -203,7 +203,7 @@ class ...
    4. /includes/db/firebird.php
      Firebird/Interbase Database Abstraction Layer
    5. /includes/db/msssql.php
      MSSQL Database Abstraction Layer
    6. /includes/db/mssql_odbc.php
      MSSQL ODBC Database Abstraction Layer for MSSQL
    7. -
    8. /includes/db/mysql.php
      MySQL Database Abstraction Layer for MySQL 3.x/4.0.x/4.1.x/5.x +
    9. /includes/db/mysql.php
      MySQL Database Abstraction Layer for MySQL 3.x/4.0.x/4.1.x/5.x
    10. /includes/db/mysqli.php
      MySQLi Database Abstraction Layer
    11. /includes/db/oracle.php
      Oracle Database Abstraction Layer
    12. /includes/db/postgres.php
      PostgreSQL Database Abstraction Layer
    13. @@ -224,7 +224,7 @@ class ...
    14. styles
      /styles, style.php
      phpBB Styles/Templates/Themes/Imagesets
    15. - 1.iv. Special Constants +

      1.iv. Special Constants

      There are some special constants application developers are able to utilize to bend some of phpBB's internal functionality to suit their needs.

      @@ -1170,24 +1170,13 @@ append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp; <!-- INCLUDE {FILE_VAR} --> -

      Template defined variables can also be utilised. +

      Template defined variables can also be utilised.

       <!-- DEFINE $SOME_VAR = 'my_file.html' -->
       <!-- INCLUDE {$SOME_VAR} -->
       
      -

      PHP

      A contentious decision has seen the ability to include PHP within the template introduced. This is achieved by enclosing the PHP within relevant tags:

      @@ -1541,11 +1530,11 @@ div <form method="post" id="mcp" action="{U_POST_ACTION}"> <fieldset class="submit-buttons"> - <input type="reset" value="{L_RESET}" name="reset" class="button2" />  - <input type="submit" name="action[add_warning]" value="{L_SUBMIT}" class="button1" /> + <input type="reset" value="{L_RESET}" name="reset" class="button2" />  + <input type="submit" name="action[add_warning]" value="{L_SUBMIT}" class="button1" /> {S_FORM_TOKEN} - </fieldset> -</form> + </fieldset> +</form>

      4.ii. Template Inheritance

      @@ -2337,7 +2326,7 @@ if (utf8_case_fold_nfc($string1) == utf8_case_fold_nfc($string2))
      -

      The version control system for phpBB3 is subversion. The repository is available at http://code.phpbb.com/svn/phpbb. +

      The version control system for phpBB3 is subversion. The repository is available at http://code.phpbb.com/svn/phpbb.

      7.i. Repository Structure

      diff --git a/phpBB/feed.php b/phpBB/feed.php index a7c6f831a3..1832efbc61 100644 --- a/phpBB/feed.php +++ b/phpBB/feed.php @@ -27,6 +27,15 @@ if (!$config['feed_enable']) // Start session $user->session_begin(); + +if (!empty($config['feed_http_auth']) && request_var('auth', '') == 'http') +{ + phpbb_http_login(array( + 'auth_message' => 'Feed', + 'viewonline' => request_var('viewonline', true), + )); +} + $auth->acl($user->data); $user->setup(); @@ -93,8 +102,8 @@ while ($row = $feed->get_item()) 'pubdate' => feed_format_date($item_time), 'link' => '', 'title' => censor_text($title), - 'category' => ($config['feed_item_statistics']) ? $board_url . '/viewforum.' . $phpEx . '?f=' . $row['forum_id'] : '', - 'category_name' => ($config['feed_item_statistics']) ? $row['forum_name'] : '', + 'category' => ($config['feed_item_statistics'] && !empty($row['forum_id'])) ? $board_url . '/viewforum.' . $phpEx . '?f=' . $row['forum_id'] : '', + 'category_name' => ($config['feed_item_statistics'] && isset($row['forum_name'])) ? $row['forum_name'] : '', 'description' => censor_text(feed_generate_content($row[$feed->get('text')], $row[$feed->get('bbcode_uid')], $row[$feed->get('bitfield')], $options)), 'statistics' => '', ); @@ -188,7 +197,7 @@ foreach ($item_vars as $row) echo '' . "\n"; echo '<![CDATA[' . $row['title'] . ']]>' . "\n\n"; - if (!empty($row['category'])) + if (!empty($row['category']) && isset($row['category_name']) && $row['category_name'] !== '') { echo '' . "\n"; } @@ -316,7 +325,7 @@ function feed_generate_content($content, $uid, $bitfield, $options) $content = preg_replace('#\<\!\[CDATA\[(.*?)\]\]\>#s', '', $content); // Other control characters - // $content = preg_replace('#(?:[\x00-\x1F\x7F]+|(?:\xC2[\x80-\x9F])+)#', '', $content); + $content = preg_replace('#(?:[\x00-\x1F\x7F]+|(?:\xC2[\x80-\x9F])+)#', '', $content); return $content; } @@ -352,7 +361,8 @@ class phpbb_feed_factory break; case 'topics': - if (!$config['feed_overall_topics']) + case 'topics_new': + if (!$config['feed_topics_new']) { return false; } @@ -360,6 +370,15 @@ class phpbb_feed_factory return new phpbb_feed_topics(); break; + case 'topics_active': + if (!$config['feed_topics_active']) + { + return false; + } + + return new phpbb_feed_topics_active(); + break; + case 'news': global $db; @@ -380,18 +399,20 @@ class phpbb_feed_factory break; default: - // Forum and/or topic specified? - if ($topic_id && !$config['feed_topic']) + if ($topic_id && $config['feed_topic']) { - return false; + return new phpbb_feed_topic($topic_id); + } + else if ($forum_id && $config['feed_forum']) + { + return new phpbb_feed_forum($forum_id); + } + else if ($config['feed_overall']) + { + return new phpbb_feed_overall(); } - if ($forum_id && !$topic_id && !$config['feed_forum']) - { - return false; - } - - return new phpbb_feed($forum_id, $topic_id); + return false; break; } } @@ -414,36 +435,11 @@ class phpbb_feed_base */ var $keys = array(); - /** - * An array of excluded forum ids. - */ - var $excluded_forums_ary = NULL; - /** * Number of items to fetch. Usually overwritten by $config['feed_something'] */ var $num_items = 15; - /** - * boolean to determine if items array is filled or not - */ - var $items_filled = false; - - /** - * array holding items - */ - var $items = array(); - - /** - * Default setting for last x days - */ - var $sort_days = 30; - - /** - * Default cache time of entries in seconds - */ - var $cache_time = 90; - /** * Separator for title elements to separate items (for example forum / topic) */ @@ -459,15 +455,21 @@ class phpbb_feed_base */ function phpbb_feed_base() { - global $user; - - // Disable cache if it is not a guest or a bot but a registered user - if ($this->cache_time && !empty($user) && $user->data['is_registered']) - { - $this->cache_time = 0; - } + global $config; $this->set_keys(); + + // Allow num_items to be string + if (is_string($this->num_items)) + { + $this->num_items = (int) $config[$this->num_items]; + + // A precaution + if (!$this->num_items) + { + $this->num_items = 10; + } + } } /** @@ -513,41 +515,65 @@ class phpbb_feed_base return (isset($this->keys[$key])) ? $this->keys[$key] : NULL; } - /** - * Return array of excluded forums - */ - function excluded_forums() + function get_readable_forums() { - if ($this->excluded_forums_ary !== NULL) + global $auth; + static $forum_ids; + + if (!isset($forum_ids)) { - return $this->excluded_forums_ary; + $forum_ids = array_keys($auth->acl_getf('f_read')); } - global $auth, $db, $config, $phpbb_root_path, $phpEx, $user; + return $forum_ids; + } - // Which forums should not be searched ? - $this->excluded_forums_ary = array(); + function get_moderator_approve_forums() + { + global $auth; + static $forum_ids; - // Exclude excluded forums and forums we cannot read - $forum_ids_read = array_keys($auth->acl_getf('f_read', true)); - $sql_or = (!empty($forum_ids_read)) ? 'OR ' . $db->sql_in_set('forum_id', $forum_ids_read, true) : ''; - - $sql = 'SELECT forum_id - FROM ' . FORUMS_TABLE . ' - WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '<> 0') . " - $sql_or"; - $result = $db->sql_query($sql); - - while ($row = $db->sql_fetchrow($result)) + if (!isset($forum_ids)) { - $this->excluded_forums_ary[(int) $row['forum_id']] = (int) $row['forum_id']; + $forum_ids = array_keys($auth->acl_getf('m_approve')); } - $db->sql_freeresult($result); - // Include passworded forums - $this->excluded_forums_ary = array_unique(array_merge($this->excluded_forums_ary, $this->get_passworded_forums())); + return $forum_ids; + } - return $this->excluded_forums_ary; + function get_excluded_forums() + { + global $db, $cache; + static $forum_ids; + + // Matches acp/acp_board.php + $cache_name = 'feed_excluded_forum_ids'; + + if (!isset($forum_ids) && ($forum_ids = $cache->get('_' . $cache_name)) === false) + { + $sql = 'SELECT forum_id + FROM ' . FORUMS_TABLE . ' + WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '<> 0'); + $result = $db->sql_query($sql); + + $forum_ids = array(); + while ($forum_id = (int) $db->sql_fetchfield('forum_id')) + { + $forum_ids[$forum_id] = $forum_id; + } + $db->sql_freeresult($result); + + $cache->put('_' . $cache_name, $forum_ids); + } + + return $forum_ids; + } + + function is_excluded_forum($forum_id) + { + $forum_ids = $this->get_excluded_forums(); + + return isset($forum_ids[$forum_id]) ? true : false; } function get_passworded_forums() @@ -581,156 +607,446 @@ class phpbb_feed_base function get_item() { global $db, $cache; + static $result; - if (!$this->cache_time) + if (!isset($result)) { - if (empty($this->result)) + if (!$this->get_sql()) { - if (!$this->get_sql()) - { - return false; - } - - // Query database - $sql = $db->sql_build_query('SELECT', $this->sql); - $this->result = $db->sql_query_limit($sql, $this->num_items); + return false; } - return $db->sql_fetchrow($this->result); + // Query database + $sql = $db->sql_build_query('SELECT', $this->sql); + $result = $db->sql_query_limit($sql, $this->num_items); } - else + + return $db->sql_fetchrow($result); + } + + function user_viewprofile($row) + { + global $phpEx, $user; + + $author_id = (int) $row[$this->get('author_id')]; + + if ($author_id == ANONYMOUS) { - if (empty($this->items_filled)) - { - // Try to load result set... - $cache_filename = substr(get_class($this), strlen('phpbb_')); + // Since we cannot link to a profile, we just return GUEST + // instead of $row['username'] + return $user->lang['GUEST']; + } - if (($this->items = $cache->get('_' . $cache_filename)) === false) - { - $this->items = array(); + return '' . $row[$this->get('creator')] . ''; + } +} - if ($this->get_sql()) - { - // Query database - $sql = $db->sql_build_query('SELECT', $this->sql); - $result = $db->sql_query_limit($sql, $this->num_items); +/** +* Abstract class for post based feeds +* +* @package phpBB3 +*/ +class phpbb_feed_post_base extends phpbb_feed_base +{ + var $num_items = 'feed_limit_post'; - while ($row = $db->sql_fetchrow($result)) - { - $this->items[] = $row; - } - $db->sql_freeresult($result); - } + function set_keys() + { + $this->set('title', 'post_subject'); + $this->set('title2', 'topic_title'); - $cache->put('_' . $cache_filename, $this->items, $this->cache_time); - } + $this->set('author_id', 'user_id'); + $this->set('creator', 'username'); + $this->set('date', 'post_time'); + $this->set('text', 'post_text'); - $this->items_filled = true; - } + $this->set('bitfield', 'bbcode_bitfield'); + $this->set('bbcode_uid','bbcode_uid'); - $row = array_shift($this->items); - return (!$row) ? false : $row; + $this->set('enable_bbcode', 'enable_bbcode'); + $this->set('enable_smilies', 'enable_smilies'); + $this->set('enable_magic_url', 'enable_magic_url'); + } + + function adjust_item(&$item_row, &$row) + { + global $phpEx, $config, $user; + + $item_row['link'] = feed_append_sid('/viewtopic.' . $phpEx, "t={$row['topic_id']}&p={$row['post_id']}#p{$row['post_id']}"); + + if ($config['feed_item_statistics']) + { + $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $this->user_viewprofile($row) + . ' ' . $this->separator_stats . ' ' . $user->format_date($row['post_time']); } } } /** -* Default feed class if no mode is specified. -* This can be the overall site feed or a forum/topic feed. +* Abstract class for topic based feeds * * @package phpBB3 */ -class phpbb_feed extends phpbb_feed_base +class phpbb_feed_topic_base extends phpbb_feed_base { - /** - * Forum id specified for forum feed. - */ - var $forum_id = 0; - - /** - * Topic id specified for topic feed. - */ - var $topic_id = 0; - - /** - * Constructor. - */ - function phpbb_feed($forum_id = 0, $topic_id = 0) - { - global $config; - - // Call parent constructor. - parent::phpbb_feed_base(); - - $this->forum_id = $forum_id; - $this->topic_id = $topic_id; - - $this->sql = array(); - } + var $num_items = 'feed_limit_topic'; function set_keys() { - global $config; + $this->set('title', 'topic_title'); + $this->set('title2', 'forum_name'); - $this->set('title', 'post_subject'); - $this->set('title2', 'topic_title'); - $this->set('author_id', 'user_id'); - $this->set('creator', 'username'); + $this->set('author_id', 'topic_poster'); + $this->set('creator', 'topic_first_poster_name'); + $this->set('date', 'topic_time'); $this->set('text', 'post_text'); + $this->set('bitfield', 'bbcode_bitfield'); $this->set('bbcode_uid','bbcode_uid'); - $this->set('date', 'post_time'); $this->set('enable_bbcode', 'enable_bbcode'); $this->set('enable_smilies', 'enable_smilies'); $this->set('enable_magic_url', 'enable_magic_url'); + } - $this->num_items = (int) $config['feed_limit']; + function adjust_item(&$item_row, &$row) + { + global $phpEx, $config, $user; + + $item_row['link'] = feed_append_sid('/viewtopic.' . $phpEx, 't=' . $row['topic_id'] . '&p=' . $row['post_id'] . '#p' . $row['post_id']); + + if ($config['feed_item_statistics']) + { + $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $this->user_viewprofile($row) + . ' ' . $this->separator_stats . ' ' . $user->format_date($row[$this->get('date')]) + . ' ' . $this->separator_stats . ' ' . $user->lang['REPLIES'] . ' ' . $row['topic_replies'] + . ' ' . $this->separator_stats . ' ' . $user->lang['VIEWS'] . ' ' . $row['topic_views']; + } + } +} + +/** +* Board wide feed (aka overall feed) +* +* This will give you the newest {$this->num_items} posts +* from the whole board. +* +* @package phpBB3 +*/ +class phpbb_feed_overall extends phpbb_feed_post_base +{ + function get_sql() + { + global $auth, $db; + + $forum_ids = array_diff($this->get_readable_forums(), $this->get_excluded_forums(), $this->get_passworded_forums()); + if (empty($forum_ids)) + { + return false; + } + + // Add global forum id + $forum_ids[] = 0; + + // m_approve forums + $fid_m_approve = $this->get_moderator_approve_forums(); + $sql_m_approve = (!empty($fid_m_approve)) ? 'OR ' . $db->sql_in_set('forum_id', $fid_m_approve) : ''; + + // Determine topics with recent activity + $sql = 'SELECT topic_id, topic_last_post_time + FROM ' . TOPICS_TABLE . ' + WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . ' + AND topic_moved_id = 0 + AND (topic_approved = 1 + ' . $sql_m_approve . ') + ORDER BY topic_last_post_time DESC'; + $result = $db->sql_query_limit($sql, $this->num_items); + + $topic_ids = array(); + $min_post_time = 0; + while ($row = $db->sql_fetchrow()) + { + $topic_ids[] = (int) $row['topic_id']; + + $min_post_time = (int) $row['topic_last_post_time']; + } + $db->sql_freeresult($result); + + if (empty($topic_ids)) + { + return false; + } + + // Get the actual data + $this->sql = array( + 'SELECT' => 'f.forum_id, f.forum_name, ' . + 'p.post_id, p.topic_id, p.post_time, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' . + 'u.username, u.user_id', + 'FROM' => array( + POSTS_TABLE => 'p', + USERS_TABLE => 'u', + ), + 'LEFT_JOIN' => array( + array( + 'FROM' => array(FORUMS_TABLE => 'f'), + 'ON' => 'f.forum_id = p.forum_id', + ), + ), + 'WHERE' => $db->sql_in_set('p.topic_id', $topic_ids) . ' + AND (p.post_approved = 1 + ' . str_replace('forum_id', 'p.forum_id', $sql_m_approve) . ') + AND p.post_time >= ' . $min_post_time . ' + AND u.user_id = p.poster_id', + 'ORDER_BY' => 'p.post_time DESC', + ); + + return true; + } + + function adjust_item(&$item_row, &$row) + { + parent::adjust_item($item_row, $row); + + $item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; + } +} + +/** +* Forum feed +* +* This will give you the last {$this->num_items} posts made +* within a specific forum. +* +* @package phpBB3 +*/ +class phpbb_feed_forum extends phpbb_feed_post_base +{ + var $forum_id = 0; + var $forum_data = array(); + + function phpbb_feed_forum($forum_id) + { + parent::phpbb_feed_base(); + + $this->forum_id = (int) $forum_id; + } + + function open() + { + global $db, $auth; + + // Check if forum exists + $sql = 'SELECT forum_id, forum_name, forum_password, forum_type, forum_options + FROM ' . FORUMS_TABLE . ' + WHERE forum_id = ' . $this->forum_id; + $result = $db->sql_query($sql); + $this->forum_data = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + if (empty($this->forum_data)) + { + trigger_error('NO_FORUM'); + } + + // Forum needs to be postable + if ($this->forum_data['forum_type'] != FORUM_POST) + { + trigger_error('NO_FEED'); + } + + // Make sure forum is not excluded from feed + if (phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $this->forum_data['forum_options'])) + { + trigger_error('NO_FEED'); + } + + // Make sure we can read this forum + if (!$auth->acl_get('f_read', $this->forum_id)) + { + trigger_error('SORRY_AUTH_READ'); + } + + // Make sure forum is not passworded or user is authed + if ($this->forum_data['forum_password']) + { + $forum_ids_passworded = $this->get_passworded_forums(); + + if (isset($forum_ids_passworded[$this->forum_id])) + { + trigger_error('SORRY_AUTH_READ'); + } + + unset($forum_ids_passworded); + } + } + + function get_sql() + { + global $auth, $db; + + $m_approve = ($auth->acl_get('m_approve', $this->forum_id)) ? true : false; + $forum_ids = array(0, $this->forum_id); + + // Determine topics with recent activity + $sql = 'SELECT topic_id, topic_last_post_time + FROM ' . TOPICS_TABLE . ' + WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . ' + AND topic_moved_id = 0 + ' . ((!$m_approve) ? 'AND topic_approved = 1' : '') . ' + ORDER BY topic_last_post_time DESC'; + $result = $db->sql_query_limit($sql, $this->num_items); + + $topic_ids = array(); + $min_post_time = 0; + while ($row = $db->sql_fetchrow()) + { + $topic_ids[] = (int) $row['topic_id']; + + $min_post_time = (int) $row['topic_last_post_time']; + } + $db->sql_freeresult($result); + + if (empty($topic_ids)) + { + return false; + } + + $this->sql = array( + 'SELECT' => 'p.post_id, p.topic_id, p.post_time, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' . + 'u.username, u.user_id', + 'FROM' => array( + POSTS_TABLE => 'p', + USERS_TABLE => 'u', + ), + 'WHERE' => $db->sql_in_set('p.topic_id', $topic_ids) . ' + ' . ((!$m_approve) ? 'AND p.post_approved = 1' : '') . ' + AND p.post_time >= ' . $min_post_time . ' + AND p.poster_id = u.user_id', + 'ORDER_BY' => 'p.post_time DESC', + ); + + return true; + } + + function adjust_item(&$item_row, &$row) + { + parent::adjust_item($item_row, $row); + + $item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; + } + + function get_item() + { + return ($row = parent::get_item()) ? array_merge($this->forum_data, $row) : $row; + } +} + +/** +* Topic feed for a specific topic +* +* This will give you the last {$this->num_items} posts made within this topic. +* +* @package phpBB3 +*/ +class phpbb_feed_topic extends phpbb_feed_post_base +{ + var $topic_id = 0; + var $forum_id = 0; + var $topic_data = array(); + + function phpbb_feed_topic($topic_id) + { + parent::phpbb_feed_base(); + + $this->topic_id = (int) $topic_id; } function open() { global $auth, $db, $user; - // Topic/Forum feed - if ($this->topic_id || $this->forum_id) + $sql = 'SELECT f.forum_options, f.forum_password, t.topic_id, t.forum_id, t.topic_approved, t.topic_title, t.topic_time, t.topic_views, t.topic_replies, t.topic_type + FROM ' . TOPICS_TABLE . ' t + LEFT JOIN ' . FORUMS_TABLE . ' f + ON (f.forum_id = t.forum_id) + WHERE t.topic_id = ' . $this->topic_id; + $result = $db->sql_query($sql); + $this->topic_data = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + if (empty($this->topic_data)) { - if ($this->topic_id) + trigger_error('NO_TOPIC'); + } + + if ($this->topic_data['topic_type'] == POST_GLOBAL) + { + // We need to find at least one postable forum where feeds are enabled, + // that the user can read and maybe also has approve permissions. + $in_fid_ary = $this->get_readable_forums(); + + if (empty($in_fid_ary)) { - // Topic feed - $sql = 'SELECT t.forum_id, f.forum_options - FROM ' . TOPICS_TABLE . ' t, ' . FORUMS_TABLE . ' f - WHERE t.topic_id = ' . $this->topic_id . ' - AND t.forum_id = f.forum_id'; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if (empty($row)) - { - trigger_error('NO_TOPIC'); - } - - $this->forum_id = (int) $row['forum_id']; + // User cannot read any forums + trigger_error('SORRY_AUTH_READ'); } - else - { - // Forum feed - $sql = 'SELECT forum_options - FROM ' . FORUMS_TABLE . ' - WHERE forum_id = ' . $this->forum_id; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - if (empty($row)) + if (!$this->topic_data['topic_approved']) + { + // Also require m_approve + $in_fid_ary = array_intersect($in_fid_ary, array_keys($auth->acl_getf('m_approve'))); + + if (empty($in_fid_ary)) { - trigger_error('NO_FORUM'); + trigger_error('SORRY_AUTH_READ'); } } + // Diff excluded forums + $in_fid_ary = array_diff($in_fid_ary, $this->get_excluded_forums()); + + if (empty($in_fid_ary)) + { + trigger_error('SORRY_AUTH_READ'); + } + + // Also exclude passworded forums + $in_fid_ary = array_diff($in_fid_ary, $this->get_passworded_forums()); + + if (empty($in_fid_ary)) + { + trigger_error('SORRY_AUTH_READ'); + } + + $sql = 'SELECT forum_id, left_id + FROM ' . FORUMS_TABLE . ' + WHERE forum_type = ' . FORUM_POST . ' + AND ' . $db->sql_in_set('forum_id', $in_fid_ary) . ' + ORDER BY left_id ASC'; + $result = $db->sql_query_limit($sql, 1); + $this->forum_data = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + if (empty($this->forum_data)) + { + // No forum found. + trigger_error('SORRY_AUTH_READ'); + } + + unset($in_fid_ary); + } + else + { + $this->forum_id = (int) $this->topic_data['forum_id']; + + // Make sure topic is either approved or user authed + if (!$this->topic_data['topic_approved'] && !$auth->acl_get('m_approve', $this->forum_id)) + { + trigger_error('SORRY_AUTH_READ'); + } + // Make sure forum is not excluded from feed - if (phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $row['forum_options'])) + if (phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $this->topic_data['forum_options'])) { trigger_error('NO_FEED'); } @@ -741,186 +1057,88 @@ class phpbb_feed extends phpbb_feed_base trigger_error('SORRY_AUTH_READ'); } - // Disable caching - $this->cache_time = 0; + // Make sure forum is not passworded or user is authed + if ($this->topic_data['forum_password']) + { + $forum_ids_passworded = $this->get_passworded_forums(); + + if (isset($forum_ids_passworded[$this->forum_id])) + { + trigger_error('SORRY_AUTH_READ'); + } + + unset($forum_ids_passworded); + } } } - /** - * Get SQL query for fetching items - */ function get_sql() { - global $db; + global $auth, $db; - $post_ids = array(); - - // Search for topics in last X days - $last_post_time_sql = ($this->sort_days) ? ' AND t.topic_last_post_time > ' . (time() - ($this->sort_days * 24 * 3600)) : ''; - - // Fetch latest post, grouped by topic... - if (!$this->forum_id && !$this->topic_id) - { - // First of all, the post ids... - $not_in_fid = (sizeof($this->excluded_forums())) ? ' AND ' . $db->sql_in_set('t.forum_id', $this->excluded_forums(), true) : ''; - - $sql = 'SELECT t.topic_last_post_id - FROM ' . TOPICS_TABLE . ' t - WHERE t.topic_approved = 1 - AND t.topic_moved_id = 0' . - $not_in_fid . - $last_post_time_sql . ' - ORDER BY t.topic_last_post_time DESC'; - $result = $db->sql_query_limit($sql, $this->num_items); - - while ($row = $db->sql_fetchrow($result)) - { - $post_ids[] = (int) $row['topic_last_post_id']; - } - $db->sql_freeresult($result); - } - // Fetch latest posts from forum - else if (!$this->topic_id && $this->forum_id) - { - // Make sure the forum is not listed within the forbidden ones. ;) - if (in_array($this->forum_id, $this->excluded_forums())) - { - return false; - } - - // Determine which forums to fetch - $not_in_fid = (sizeof($this->excluded_forums())) ? ' AND ' . $db->sql_in_set('f2.forum_id', $this->excluded_forums(), true) : ''; - - // Determine forum childs... - $sql = 'SELECT f2.forum_id - FROM ' . FORUMS_TABLE . ' f1, ' . FORUMS_TABLE . ' f2 - WHERE f1.forum_id = ' . $this->forum_id . ' - AND (f2.left_id BETWEEN f1.left_id AND f1.right_id' . $not_in_fid . ')'; - $result = $db->sql_query($sql); - - $forum_ids = array(); - while ($row = $db->sql_fetchrow($result)) - { - $forum_ids[] = (int) $row['forum_id']; - } - $db->sql_freeresult($result); - - // Now select from forums... - $sql = 'SELECT t.topic_last_post_id - FROM ' . TOPICS_TABLE . ' t - WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' - AND t.topic_approved = 1 - AND t.topic_moved_id = 0' . - $last_post_time_sql . ' - ORDER BY t.topic_last_post_time DESC'; - $result = $db->sql_query_limit($sql, $this->num_items); - - while ($row = $db->sql_fetchrow($result)) - { - $post_ids[] = (int) $row['topic_last_post_id']; - } - $db->sql_freeresult($result); - } - // Fetch last posts from specified topic... - else if ($this->topic_id) - { - // non-global announcement - if ($this->forum_id && in_array($this->forum_id, $this->excluded_forums())) - { - return false; - } - - $sql = 'SELECT post_id - FROM ' . POSTS_TABLE . ' - WHERE topic_id = ' . $this->topic_id . ' - AND post_approved = 1 - ORDER BY post_time DESC'; - $result = $db->sql_query_limit($sql, $this->num_items); - - while ($row = $db->sql_fetchrow($result)) - { - $post_ids[] = (int) $row['post_id']; - } - $db->sql_freeresult($result); - } - - if (!sizeof($post_ids)) - { - return false; - } - - // Now build sql query for obtaining items $this->sql = array( - 'SELECT' => 'f.forum_id, f.forum_name, f.forum_desc_options, ' . - 't.topic_last_post_time, t.topic_id, t.topic_title, t.topic_time, t.topic_replies, t.topic_views, ' . - 'p.post_id, p.post_time, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' . + 'SELECT' => 'p.post_id, p.post_time, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' . 'u.username, u.user_id', 'FROM' => array( POSTS_TABLE => 'p', - TOPICS_TABLE => 't', - FORUMS_TABLE => 'f', USERS_TABLE => 'u', ), - 'WHERE' => $db->sql_in_set('p.post_id', $post_ids) . ' - AND f.forum_id = p.forum_id - AND t.topic_id = p.topic_id - AND u.user_id = p.poster_id', + 'WHERE' => 'p.topic_id = ' . $this->topic_id . ' + ' . ($this->forum_id && !$auth->acl_get('m_approve', $this->forum_id) ? 'AND p.post_approved = 1' : '') . ' + AND p.poster_id = u.user_id', 'ORDER_BY' => 'p.post_time DESC', ); return true; } - function adjust_item(&$item_row, &$row) + function get_item() { - global $phpEx, $config; - - $item_row['title'] = (!$this->topic_id) ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; - $item_row['link'] = feed_append_sid('/viewtopic.' . $phpEx, "t={$row['topic_id']}&p={$row['post_id']}#p{$row['post_id']}"); - - if ($config['feed_item_statistics']) - { - global $user; - - $user_link = ($row['user_id'] != ANONYMOUS) ? '' . $row['username'] . '' : $row['username']; - - $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $user_link - . ' ' . $this->separator_stats . ' ' . $user->format_date($row['post_time']) - . ' ' . $this->separator_stats . ' ' . $user->lang['REPLIES'] . ' ' . $row['topic_replies'] - . ' ' . $this->separator_stats . ' ' . $user->lang['VIEWS'] . ' ' . $row['topic_views']; - } + return ($row = parent::get_item()) ? array_merge($this->topic_data, $row) : $row; } } +/** +* 'All Forums' feed +* +* This will give you a list of all postable forums where feeds are enabled +* including forum description, topic stats and post stats +* +* @package phpBB3 +*/ class phpbb_feed_forums extends phpbb_feed_base { + var $num_items = 0; + function set_keys() { - global $config; - $this->set('title', 'forum_name'); $this->set('text', 'forum_desc'); $this->set('bitfield', 'forum_desc_bitfield'); $this->set('bbcode_uid','forum_desc_uid'); $this->set('date', 'forum_last_post_time'); $this->set('options', 'forum_desc_options'); - - $this->num_items = (int) $config['feed_overall_forums_limit']; } function get_sql() { - global $db; + global $auth, $db; - $not_in_fid = (sizeof($this->excluded_forums())) ? ' AND ' . $db->sql_in_set('f.forum_id', $this->excluded_forums(), true) : ''; + $in_fid_ary = array_diff($this->get_readable_forums(), $this->get_excluded_forums()); + if (empty($in_fid_ary)) + { + return false; + } // Build SQL Query $this->sql = array( - 'SELECT' => 'f.*', + 'SELECT' => 'f.forum_id, f.left_id, f.forum_name, f.forum_last_post_time, + f.forum_desc, f.forum_desc_bitfield, f.forum_desc_uid, f.forum_desc_options, + f.forum_topics, f.forum_posts', 'FROM' => array(FORUMS_TABLE => 'f'), 'WHERE' => 'f.forum_type = ' . FORUM_POST . ' - AND (f.forum_last_post_id > 0' . $not_in_fid . ')', - 'ORDER_BY' => 'f.left_id', + AND ' . $db->sql_in_set('f.forum_id', $in_fid_ary), + 'ORDER_BY' => 'f.left_id ASC', ); return true; @@ -942,178 +1160,175 @@ class phpbb_feed_forums extends phpbb_feed_base } } -class phpbb_feed_news extends phpbb_feed_base +/** +* News feed +* +* This will give you {$this->num_items} first posts +* of all topics in the selected news forums. +* +* @package phpBB3 +*/ +class phpbb_feed_news extends phpbb_feed_topic_base { - /** - * Longer cache time for the news feed - */ - var $cache_time = 180; - - function set_keys() + function get_news_forums() { - global $config; + global $db, $cache; + static $forum_ids; - $this->set('title', 'topic_title'); - $this->set('title2', 'forum_name'); - $this->set('author_id', 'topic_poster'); - $this->set('creator', 'topic_first_poster_name'); - $this->set('text', 'post_text'); - $this->set('bitfield', 'bbcode_bitfield'); - $this->set('bbcode_uid','bbcode_uid'); - $this->set('date', 'topic_time'); + // Matches acp/acp_board.php + $cache_name = 'feed_news_forum_ids'; - $this->set('enable_bbcode', 'enable_bbcode'); - $this->set('enable_smilies', 'enable_smilies'); - $this->set('enable_magic_url', 'enable_magic_url'); + if (!isset($forum_ids) && ($forum_ids = $cache->get('_' . $cache_name)) === false) + { + $sql = 'SELECT forum_id + FROM ' . FORUMS_TABLE . ' + WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0'); + $result = $db->sql_query($sql); - $this->num_items = (int) $config['feed_overall_forums_limit']; + $forum_ids = array(); + while ($forum_id = (int) $db->sql_fetchfield('forum_id')) + { + $forum_ids[$forum_id] = $forum_id; + } + $db->sql_freeresult($result); + + $cache->put('_' . $cache_name, $forum_ids); + } + + return $forum_ids; } function get_sql() { global $auth, $config, $db; - // Get passworded forums - $forum_ids_passworded = $this->get_passworded_forums(); - - // Get news forums... - $sql = 'SELECT forum_id - FROM ' . FORUMS_TABLE . ' - WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0'); - $result = $db->sql_query($sql); - - $in_fid_ary = array(); - while ($row = $db->sql_fetchrow($result)) - { - $forum_id = (int) $row['forum_id']; - - // Passworded forum - if (isset($forum_ids_passworded[$forum_id])) - { - continue; - } - - // Make sure we can read this forum - if (!$auth->acl_get('f_read', $forum_id)) - { - continue; - } - - $in_fid_ary[] = (int) $row['forum_id']; - } - $db->sql_freeresult($result); - - if (!sizeof($in_fid_ary)) + // Determine forum ids + $in_fid_ary = array_intersect($this->get_news_forums(), $this->get_readable_forums()); + if (empty($in_fid_ary)) { return false; } - // Build SQL Query - $this->sql = array( - 'SELECT' => 'f.forum_id, f.forum_name, f.forum_topics, f.forum_posts, - t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_replies, t.topic_views, t.topic_time, - p.post_id, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', - 'FROM' => array( - TOPICS_TABLE => 't', - FORUMS_TABLE => 'f', - POSTS_TABLE => 'p', - ), - 'WHERE' => $db->sql_in_set('t.forum_id', $in_fid_ary) . ' - AND f.forum_id = t.forum_id - AND p.post_id = t.topic_first_post_id - AND t.topic_moved_id = 0', - 'ORDER_BY' => 't.topic_time DESC', - ); - - return true; - } - - function adjust_item(&$item_row, &$row) - { - global $phpEx, $config; - - $item_row['link'] = feed_append_sid('/viewtopic.' . $phpEx, 't=' . $row['topic_id'] . '&p=' . $row['post_id'] . '#p' . $row['post_id']); - - if ($config['feed_item_statistics']) + $in_fid_ary = array_diff($in_fid_ary, $this->get_passworded_forums()); + if (empty($in_fid_ary)) { - global $user; - - $user_link = ($row[$this->get('author_id')] != ANONYMOUS) ? '' . $row[$this->get('creator')] . '' : $row[$this->get('creator')]; - - $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $user_link - . ' ' . $this->separator_stats . ' ' . $user->format_date($row['topic_time']) - . ' ' . $this->separator_stats . ' ' . $user->lang['REPLIES'] . ' ' . $row['topic_replies'] - . ' ' . $this->separator_stats . ' ' . $user->lang['VIEWS'] . ' ' . $row['topic_views']; + return false; } - } -} -class phpbb_feed_topics extends phpbb_feed_base -{ - function set_keys() - { - global $config; + // Add global forum + $in_fid_ary[] = 0; - $this->set('title', 'topic_title'); - $this->set('title2', 'forum_name'); - $this->set('author_id', 'topic_poster'); - $this->set('creator', 'topic_first_poster_name'); - $this->set('text', 'post_text'); - $this->set('bitfield', 'bbcode_bitfield'); - $this->set('bbcode_uid','bbcode_uid'); - $this->set('date', 'topic_time'); - - $this->set('enable_bbcode', 'enable_bbcode'); - $this->set('enable_smilies', 'enable_smilies'); - $this->set('enable_magic_url', 'enable_magic_url'); - - $this->num_items = (int) $config['feed_overall_topics_limit']; - } - - function get_sql() - { - global $db, $config; - - $post_ids = array(); - $not_in_fid = (sizeof($this->excluded_forums())) ? ' AND ' . $db->sql_in_set('t.forum_id', $this->excluded_forums(), true) : ''; - - // Search for topics in last x days - $last_post_time_sql = ($this->sort_days) ? ' AND t.topic_last_post_time > ' . (time() - ($this->sort_days * 24 * 3600)) : ''; - - // Last x topics from all forums, with first post from topic... - $sql = 'SELECT t.topic_first_post_id - FROM ' . TOPICS_TABLE . ' t - WHERE t.topic_approved = 1 - AND t.topic_moved_id = 0' . - $not_in_fid . - $last_post_time_sql . ' - ORDER BY t.topic_last_post_time DESC'; + // We really have to get the post ids first! + $sql = 'SELECT topic_first_post_id, topic_time + FROM ' . TOPICS_TABLE . ' + WHERE ' . $db->sql_in_set('forum_id', $in_fid_ary) . ' + AND topic_moved_id = 0 + AND topic_approved = 1 + ORDER BY topic_time DESC'; $result = $db->sql_query_limit($sql, $this->num_items); + $post_ids = array(); while ($row = $db->sql_fetchrow($result)) { $post_ids[] = (int) $row['topic_first_post_id']; } $db->sql_freeresult($result); - if (!sizeof($post_ids)) + if (empty($post_ids)) { return false; } $this->sql = array( - 'SELECT' => 'f.forum_id, f.forum_name, f.forum_topics, f.forum_posts, + 'SELECT' => 'f.forum_id, f.forum_name, t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_replies, t.topic_views, t.topic_time, - p.post_id, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', + p.post_id, p.post_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', 'FROM' => array( TOPICS_TABLE => 't', - FORUMS_TABLE => 'f', POSTS_TABLE => 'p', ), - 'WHERE' => $db->sql_in_set('p.post_id', $post_ids) . ' - AND f.forum_id = p.forum_id - AND t.topic_id = p.topic_id', - 'ORDER_BY' => 't.topic_last_post_time DESC', + 'LEFT_JOIN' => array( + array( + 'FROM' => array(FORUMS_TABLE => 'f'), + 'ON' => 'p.forum_id = f.forum_id', + ), + ), + 'WHERE' => 'p.topic_id = t.topic_id + AND ' . $db->sql_in_set('p.post_id', $post_ids), + 'ORDER_BY' => 'p.post_time DESC', + ); + + return true; + } +} + +/** +* New Topics feed +* +* This will give you the last {$this->num_items} created topics +* including the first post. +* +* @package phpBB3 +*/ +class phpbb_feed_topics extends phpbb_feed_topic_base +{ + function get_sql() + { + global $db, $config; + + $forum_ids_read = $this->get_readable_forums(); + if (empty($forum_ids_read)) + { + return false; + } + + $in_fid_ary = array_diff($forum_ids_read, $this->get_excluded_forums(), $this->get_passworded_forums()); + if (empty($in_fid_ary)) + { + return false; + } + + // Add global forum + $in_fid_ary[] = 0; + + // We really have to get the post ids first! + $sql = 'SELECT topic_first_post_id, topic_time + FROM ' . TOPICS_TABLE . ' + WHERE ' . $db->sql_in_set('forum_id', $in_fid_ary) . ' + AND topic_moved_id = 0 + AND topic_approved = 1 + ORDER BY topic_time DESC'; + $result = $db->sql_query_limit($sql, $this->num_items); + + $post_ids = array(); + while ($row = $db->sql_fetchrow($result)) + { + $post_ids[] = (int) $row['topic_first_post_id']; + } + $db->sql_freeresult($result); + + if (empty($post_ids)) + { + return false; + } + + $this->sql = array( + 'SELECT' => 'f.forum_id, f.forum_name, + t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_replies, t.topic_views, t.topic_time, + p.post_id, p.post_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', + 'FROM' => array( + TOPICS_TABLE => 't', + POSTS_TABLE => 'p', + ), + 'LEFT_JOIN' => array( + array( + 'FROM' => array(FORUMS_TABLE => 'f'), + 'ON' => 'p.forum_id = f.forum_id', + ), + ), + 'WHERE' => 'p.topic_id = t.topic_id + AND ' . $db->sql_in_set('p.post_id', $post_ids), + 'ORDER_BY' => 'p.post_time DESC', ); return true; @@ -1121,21 +1336,137 @@ class phpbb_feed_topics extends phpbb_feed_base function adjust_item(&$item_row, &$row) { - global $phpEx, $config; + parent::adjust_item($item_row, $row); - $item_row['link'] = feed_append_sid('/viewtopic.' . $phpEx, 't=' . $row['topic_id'] . '&p=' . $row['post_id'] . '#p' . $row['post_id']); + $item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; + } +} - if ($config['feed_item_statistics']) +/** +* Active Topics feed +* +* This will give you the last {$this->num_items} topics +* with replies made withing the last {$this->sort_days} days +* including the last post. +* +* @package phpBB3 +*/ +class phpbb_feed_topics_active extends phpbb_feed_topic_base +{ + var $sort_days = 7; + + function set_keys() + { + parent::set_keys(); + + $this->set('author_id', 'topic_last_poster_id'); + $this->set('creator', 'topic_last_poster_name'); + $this->set('date', 'topic_last_post_time'); + $this->set('text', 'post_text'); + } + + function get_sql() + { + global $db, $config; + + $forum_ids_read = $this->get_readable_forums(); + if (empty($forum_ids_read)) { - global $user; - - $user_link = ($row[$this->get('author_id')] != ANONYMOUS) ? '' . $row[$this->get('creator')] . '' : $row[$this->get('creator')]; - - $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $user_link - . ' ' . $this->separator_stats . ' ' . $user->format_date($row['topic_time']) - . ' ' . $this->separator_stats . ' ' . $user->lang['REPLIES'] . ' ' . $row['topic_replies'] - . ' ' . $this->separator_stats . ' ' . $user->lang['VIEWS'] . ' ' . $row['topic_views']; + return false; } + + $in_fid_ary = array_intersect($forum_ids_read, $this->get_forum_ids()); + $in_fid_ary = array_diff($in_fid_ary, $this->get_passworded_forums()); + if (empty($in_fid_ary)) + { + return false; + } + + // Add global forum + $in_fid_ary[] = 0; + + // Search for topics in last X days + $last_post_time_sql = ($this->sort_days) ? ' AND topic_last_post_time > ' . (time() - ($this->sort_days * 24 * 3600)) : ''; + + // We really have to get the post ids first! + $sql = 'SELECT topic_last_post_id, topic_last_post_time + FROM ' . TOPICS_TABLE . ' + WHERE ' . $db->sql_in_set('forum_id', $in_fid_ary) . ' + AND topic_moved_id = 0 + AND topic_approved = 1 + ' . $last_post_time_sql . ' + ORDER BY topic_last_post_time DESC'; + $result = $db->sql_query_limit($sql, $this->num_items); + + $post_ids = array(); + while ($row = $db->sql_fetchrow($result)) + { + $post_ids[] = (int) $row['topic_last_post_id']; + } + $db->sql_freeresult($result); + + if (empty($post_ids)) + { + return false; + } + + $this->sql = array( + 'SELECT' => 'f.forum_id, f.forum_name, + t.topic_id, t.topic_title, t.topic_replies, t.topic_views, + t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_post_time, + p.post_id, p.post_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url', + 'FROM' => array( + TOPICS_TABLE => 't', + POSTS_TABLE => 'p', + ), + 'LEFT_JOIN' => array( + array( + 'FROM' => array(FORUMS_TABLE => 'f'), + 'ON' => 'p.forum_id = f.forum_id', + ), + ), + 'WHERE' => 'p.topic_id = t.topic_id + AND ' . $db->sql_in_set('p.post_id', $post_ids), + 'ORDER_BY' => 'p.post_time DESC', + ); + + return true; + } + + function get_forum_ids() + { + global $db, $cache; + static $forum_ids; + + $cache_name = 'feed_topic_active_forum_ids'; + + if (!isset($forum_ids) && ($forum_ids = $cache->get('_' . $cache_name)) === false) + { + $sql = 'SELECT forum_id + FROM ' . FORUMS_TABLE . ' + WHERE forum_type = ' . FORUM_POST . ' + AND ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '= 0') . ' + AND ' . $db->sql_bit_and('forum_flags', log(FORUM_FLAG_ACTIVE_TOPICS, 2), '<> 0'); + $result = $db->sql_query($sql); + + $forum_ids = array(); + while ($forum_id = (int) $db->sql_fetchfield('forum_id')) + { + $forum_ids[$forum_id] = $forum_id; + } + $db->sql_freeresult($result); + + $cache->put('_' . $cache_name, $forum_ids, 180); + } + + return $forum_ids; + } + + function adjust_item(&$item_row, &$row) + { + parent::adjust_item($item_row, $row); + + $item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title']; } } diff --git a/phpBB/includes/acm/acm_file.php b/phpBB/includes/acm/acm_file.php index 234be5c5d1..5a758aa2bb 100644 --- a/phpBB/includes/acm/acm_file.php +++ b/phpBB/includes/acm/acm_file.php @@ -410,7 +410,7 @@ class acm { if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id])) { - return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field] : false; + return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++][$field] : false; } return false; diff --git a/phpBB/includes/acm/acm_memory.php b/phpBB/includes/acm/acm_memory.php index e315e979e5..efbfd4dd62 100644 --- a/phpBB/includes/acm/acm_memory.php +++ b/phpBB/includes/acm/acm_memory.php @@ -50,6 +50,8 @@ class acm_memory if (isset($this->function) && !function_exists($this->function)) { + global $acm_type; + trigger_error("The required function [{$this->function}] is not available for the ACM module $acm_type.", E_USER_ERROR); } } @@ -364,7 +366,7 @@ class acm_memory { if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id])) { - return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field] : false; + return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++][$field] : false; } return false; diff --git a/phpBB/includes/acp/acp_attachments.php b/phpBB/includes/acp/acp_attachments.php index 849c076f0e..25e51814c4 100644 --- a/phpBB/includes/acp/acp_attachments.php +++ b/phpBB/includes/acp/acp_attachments.php @@ -124,11 +124,11 @@ class acp_attachments 'legend2' => $l_legend_cat_images, 'img_display_inlined' => array('lang' => 'DISPLAY_INLINED', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), 'img_create_thumbnail' => array('lang' => 'CREATE_THUMBNAIL', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), - 'img_max_thumb_width' => array('lang' => 'MAX_THUMB_WIDTH', 'validate' => 'int', 'type' => 'text:7:15', 'explain' => true, 'append' => ' px'), + 'img_max_thumb_width' => array('lang' => 'MAX_THUMB_WIDTH', 'validate' => 'int', 'type' => 'text:7:15', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), 'img_min_thumb_filesize' => array('lang' => 'MIN_THUMB_FILESIZE', 'validate' => 'int', 'type' => 'text:7:15', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']), 'img_imagick' => array('lang' => 'IMAGICK_PATH', 'validate' => 'string', 'type' => 'text:20:200', 'explain' => true, 'append' => '  [ ' . $user->lang['SEARCH_IMAGICK'] . ' ]'), - 'img_max' => array('lang' => 'MAX_IMAGE_SIZE', 'validate' => 'int', 'type' => 'dimension:3:4', 'explain' => true, 'append' => ' px'), - 'img_link' => array('lang' => 'IMAGE_LINK_SIZE', 'validate' => 'int', 'type' => 'dimension:3:4', 'explain' => true, 'append' => ' px'), + 'img_max' => array('lang' => 'MAX_IMAGE_SIZE', 'validate' => 'int', 'type' => 'dimension:3:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), + 'img_link' => array('lang' => 'IMAGE_LINK_SIZE', 'validate' => 'int', 'type' => 'dimension:3:4', 'explain' => true, 'append' => ' ' . $user->lang['PIXEL']), ) ); diff --git a/phpBB/includes/acp/acp_bbcodes.php b/phpBB/includes/acp/acp_bbcodes.php index b827d1107e..2b706394c4 100644 --- a/phpBB/includes/acp/acp_bbcodes.php +++ b/phpBB/includes/acp/acp_bbcodes.php @@ -124,121 +124,137 @@ class acp_bbcodes case 'modify': case 'create': - $data = $this->build_regexp($bbcode_match, $bbcode_tpl); - - // Make sure the user didn't pick a "bad" name for the BBCode tag. - $hard_coded = array('code', 'quote', 'quote=', 'attachment', 'attachment=', 'b', 'i', 'url', 'url=', 'img', 'size', 'size=', 'color', 'color=', 'u', 'list', 'list=', 'email', 'email=', 'flash', 'flash='); - - if (($action == 'modify' && strtolower($data['bbcode_tag']) !== strtolower($row['bbcode_tag'])) || ($action == 'create')) + $warn_text = preg_match('%<[^>]*\{text[\d]*\}[^>]*>%i', $bbcode_tpl); + if (!$warn_text || confirm_box(true)) { - $sql = 'SELECT 1 as test - FROM ' . BBCODES_TABLE . " - WHERE LOWER(bbcode_tag) = '" . $db->sql_escape(strtolower($data['bbcode_tag'])) . "'"; - $result = $db->sql_query($sql); - $info = $db->sql_fetchrow($result); - $db->sql_freeresult($result); + $data = $this->build_regexp($bbcode_match, $bbcode_tpl); - // Grab the end, interrogate the last closing tag - if ($info['test'] === '1' || in_array(strtolower($data['bbcode_tag']), $hard_coded) || (preg_match('#\[/([^[]*)]$#', $bbcode_match, $regs) && in_array(strtolower($regs[1]), $hard_coded))) + // Make sure the user didn't pick a "bad" name for the BBCode tag. + $hard_coded = array('code', 'quote', 'quote=', 'attachment', 'attachment=', 'b', 'i', 'url', 'url=', 'img', 'size', 'size=', 'color', 'color=', 'u', 'list', 'list=', 'email', 'email=', 'flash', 'flash='); + + if (($action == 'modify' && strtolower($data['bbcode_tag']) !== strtolower($row['bbcode_tag'])) || ($action == 'create')) { - trigger_error($user->lang['BBCODE_INVALID_TAG_NAME'] . adm_back_link($this->u_action), E_USER_WARNING); - } - } + $sql = 'SELECT 1 as test + FROM ' . BBCODES_TABLE . " + WHERE LOWER(bbcode_tag) = '" . $db->sql_escape(strtolower($data['bbcode_tag'])) . "'"; + $result = $db->sql_query($sql); + $info = $db->sql_fetchrow($result); + $db->sql_freeresult($result); - if (substr($data['bbcode_tag'], -1) === '=') - { - $test = substr($data['bbcode_tag'], 0, -1); - } - else - { - $test = $data['bbcode_tag']; - } - - if (!preg_match('%\\[' . $test . '[^]]*].*?\\[/' . $test . ']%s', $bbcode_match)) - { - trigger_error($user->lang['BBCODE_OPEN_ENDED_TAG'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - if (strlen($data['bbcode_tag']) > 16) - { - trigger_error($user->lang['BBCODE_TAG_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - if (strlen($bbcode_match) > 4000) - { - trigger_error($user->lang['BBCODE_TAG_DEF_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - - if (strlen($bbcode_helpline) > 255) - { - trigger_error($user->lang['BBCODE_HELPLINE_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - $sql_ary = array( - 'bbcode_tag' => $data['bbcode_tag'], - 'bbcode_match' => $bbcode_match, - 'bbcode_tpl' => $bbcode_tpl, - 'display_on_posting' => $display_on_posting, - 'bbcode_helpline' => $bbcode_helpline, - 'first_pass_match' => $data['first_pass_match'], - 'first_pass_replace' => $data['first_pass_replace'], - 'second_pass_match' => $data['second_pass_match'], - 'second_pass_replace' => $data['second_pass_replace'] - ); - - if ($action == 'create') - { - $sql = 'SELECT MAX(bbcode_id) as max_bbcode_id - FROM ' . BBCODES_TABLE; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - - if ($row) - { - $bbcode_id = $row['max_bbcode_id'] + 1; - - // Make sure it is greater than the core bbcode ids... - if ($bbcode_id <= NUM_CORE_BBCODES) + // Grab the end, interrogate the last closing tag + if ($info['test'] === '1' || in_array(strtolower($data['bbcode_tag']), $hard_coded) || (preg_match('#\[/([^[]*)]$#', $bbcode_match, $regs) && in_array(strtolower($regs[1]), $hard_coded))) { - $bbcode_id = NUM_CORE_BBCODES + 1; + trigger_error($user->lang['BBCODE_INVALID_TAG_NAME'] . adm_back_link($this->u_action), E_USER_WARNING); } } + + if (substr($data['bbcode_tag'], -1) === '=') + { + $test = substr($data['bbcode_tag'], 0, -1); + } else { - $bbcode_id = NUM_CORE_BBCODES + 1; + $test = $data['bbcode_tag']; } - if ($bbcode_id > 1511) + if (!preg_match('%\\[' . $test . '[^]]*].*?\\[/' . $test . ']%s', $bbcode_match)) { - trigger_error($user->lang['TOO_MANY_BBCODES'] . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($user->lang['BBCODE_OPEN_ENDED_TAG'] . adm_back_link($this->u_action), E_USER_WARNING); } - $sql_ary['bbcode_id'] = (int) $bbcode_id; + if (strlen($data['bbcode_tag']) > 16) + { + trigger_error($user->lang['BBCODE_TAG_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); + } - $db->sql_query('INSERT INTO ' . BBCODES_TABLE . $db->sql_build_array('INSERT', $sql_ary)); - $cache->destroy('sql', BBCODES_TABLE); + if (strlen($bbcode_match) > 4000) + { + trigger_error($user->lang['BBCODE_TAG_DEF_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); + } - $lang = 'BBCODE_ADDED'; - $log_action = 'LOG_BBCODE_ADD'; + + if (strlen($bbcode_helpline) > 255) + { + trigger_error($user->lang['BBCODE_HELPLINE_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING); + } + + $sql_ary = array( + 'bbcode_tag' => $data['bbcode_tag'], + 'bbcode_match' => $bbcode_match, + 'bbcode_tpl' => $bbcode_tpl, + 'display_on_posting' => $display_on_posting, + 'bbcode_helpline' => $bbcode_helpline, + 'first_pass_match' => $data['first_pass_match'], + 'first_pass_replace' => $data['first_pass_replace'], + 'second_pass_match' => $data['second_pass_match'], + 'second_pass_replace' => $data['second_pass_replace'] + ); + + if ($action == 'create') + { + $sql = 'SELECT MAX(bbcode_id) as max_bbcode_id + FROM ' . BBCODES_TABLE; + $result = $db->sql_query($sql); + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + if ($row) + { + $bbcode_id = $row['max_bbcode_id'] + 1; + + // Make sure it is greater than the core bbcode ids... + if ($bbcode_id <= NUM_CORE_BBCODES) + { + $bbcode_id = NUM_CORE_BBCODES + 1; + } + } + else + { + $bbcode_id = NUM_CORE_BBCODES + 1; + } + + if ($bbcode_id > 1511) + { + trigger_error($user->lang['TOO_MANY_BBCODES'] . adm_back_link($this->u_action), E_USER_WARNING); + } + + $sql_ary['bbcode_id'] = (int) $bbcode_id; + + $db->sql_query('INSERT INTO ' . BBCODES_TABLE . $db->sql_build_array('INSERT', $sql_ary)); + $cache->destroy('sql', BBCODES_TABLE); + + $lang = 'BBCODE_ADDED'; + $log_action = 'LOG_BBCODE_ADD'; + } + else + { + $sql = 'UPDATE ' . BBCODES_TABLE . ' + SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' + WHERE bbcode_id = ' . $bbcode_id; + $db->sql_query($sql); + $cache->destroy('sql', BBCODES_TABLE); + + $lang = 'BBCODE_EDITED'; + $log_action = 'LOG_BBCODE_EDIT'; + } + + add_log('admin', $log_action, $data['bbcode_tag']); + + trigger_error($user->lang[$lang] . adm_back_link($this->u_action)); } else { - $sql = 'UPDATE ' . BBCODES_TABLE . ' - SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' - WHERE bbcode_id = ' . $bbcode_id; - $db->sql_query($sql); - $cache->destroy('sql', BBCODES_TABLE); - - $lang = 'BBCODE_EDITED'; - $log_action = 'LOG_BBCODE_EDIT'; + confirm_box(false, $user->lang['BBCODE_DANGER'], build_hidden_fields(array( + 'action' => $action, + 'bbcode' => $bbcode_id, + 'bbcode_match' => $bbcode_match, + 'bbcode_tpl' => htmlspecialchars($bbcode_tpl), + 'bbcode_helpline' => $bbcode_helpline, + 'display_on_posting' => $display_on_posting, + )) + , 'confirm_bbcode.html'); } - add_log('admin', $log_action, $data['bbcode_tag']); - - trigger_error($user->lang[$lang] . adm_back_link($this->u_action)); - break; case 'delete': @@ -299,6 +315,18 @@ class acp_bbcodes { $bbcode_match = trim($bbcode_match); $bbcode_tpl = trim($bbcode_tpl); + $utf8 = strpos($bbcode_match, 'INTTEXT') !== false; + + // make sure we have utf8 support + $utf8_pcre_properties = false; + if (version_compare(PHP_VERSION, '5.1.0', '>=') || (version_compare(PHP_VERSION, '5.0.0-dev', '<=') && version_compare(PHP_VERSION, '4.4.0', '>='))) + { + // While this is the proper range of PHP versions, PHP may not be linked with the bundled PCRE lib and instead with an older version + if (@preg_match('/\p{L}/u', 'a') !== false) + { + $utf8_pcre_properties = true; + } + } $fp_match = preg_quote($bbcode_match, '!'); $fp_replace = preg_replace('#^\[(.*?)\]#', '[$1:$uid]', $bbcode_match); @@ -326,6 +354,9 @@ class acp_bbcodes 'SIMPLETEXT' => array( '!([a-zA-Z0-9-+.,_ ]+)!' => "$1" ), + 'INTTEXT' => array( + ($utf8_pcre_properties) ? '!([\p{L}\p{N}\-+,_. ]+)!u' : '!([a-zA-Z0-9\-+,_. ]+)!u' => "$1" + ), 'IDENTIFIER' => array( '!([a-zA-Z0-9-_]+)!' => "$1" ), @@ -343,6 +374,7 @@ class acp_bbcodes 'EMAIL' => '(' . get_preg_expression('email') . ')', 'TEXT' => '(.*?)', 'SIMPLETEXT' => '([a-zA-Z0-9-+.,_ ]+)', + 'INTTEXT' => ($utf8_pcre_properties) ? '([\p{L}\p{N}\-+,_. ]+)' : '([a-zA-Z0-9\-+,_. ]+)', 'IDENTIFIER' => '([a-zA-Z0-9-_]+)', 'COLOR' => '([a-zA-Z]+|#[0-9abcdefABCDEF]+)', 'NUMBER' => '([0-9]+)', @@ -350,6 +382,7 @@ class acp_bbcodes $pad = 0; $modifiers = 'i'; + $modifiers .= ($utf8 && $utf8_pcre_properties) ? 'u' : ''; if (preg_match_all('/\{(' . implode('|', array_keys($tokens)) . ')[0-9]*\}/i', $bbcode_match, $m)) { @@ -398,7 +431,7 @@ class acp_bbcodes } $fp_match = '!' . $fp_match . '!' . $modifiers; - $sp_match = '!' . $sp_match . '!s'; + $sp_match = '!' . $sp_match . '!s' . (($utf8) ? 'u' : ''); if (strpos($fp_match, 'e') !== false) { diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index 9f0bcf210f..a5feac1902 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -29,11 +29,12 @@ class acp_board { global $db, $user, $auth, $template; global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; + global $cache; $user->add_lang('acp/board'); $action = request_var('action', ''); - $submit = (isset($_POST['submit'])) ? true : false; + $submit = (isset($_POST['submit']) || isset($_POST['allow_quick_reply_enable'])) ? true : false; $form_key = 'acp_board'; add_form_key($form_key); @@ -88,7 +89,7 @@ class acp_board 'allow_nocensors' => array('lang' => 'ALLOW_NO_CENSORS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), 'allow_bookmarks' => array('lang' => 'ALLOW_BOOKMARKS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), 'allow_birthdays' => array('lang' => 'ALLOW_BIRTHDAYS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), - 'allow_quick_reply' => array('lang' => 'ALLOW_QUICK_REPLY', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), + 'allow_quick_reply' => array('lang' => 'ALLOW_QUICK_REPLY', 'validate' => 'bool', 'type' => 'custom', 'method' => 'quick_reply', 'explain' => true), 'legend2' => 'ACP_LOAD_SETTINGS', 'load_birthdays' => array('lang' => 'YES_BIRTHDAYS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), @@ -172,7 +173,7 @@ class acp_board 'allow_nocensors' => array('lang' => 'ALLOW_NO_CENSORS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), 'allow_bookmarks' => array('lang' => 'ALLOW_BOOKMARKS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), 'enable_post_confirm' => array('lang' => 'VISUAL_CONFIRM_POST', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), - 'allow_quick_reply' => array('lang' => 'ALLOW_QUICK_REPLY', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true), + 'allow_quick_reply' => array('lang' => 'ALLOW_QUICK_REPLY', 'validate' => 'bool', 'type' => 'custom', 'method' => 'quick_reply', 'explain' => true), 'legend2' => 'POSTING', 'bump_type' => false, @@ -266,14 +267,22 @@ class acp_board 'legend1' => 'ACP_FEED_GENERAL', 'feed_enable' => array('lang' => 'ACP_FEED_ENABLE', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), 'feed_item_statistics' => array('lang' => 'ACP_FEED_ITEM_STATISTICS', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true), - 'feed_limit' => array('lang' => 'ACP_FEED_LIMIT', 'validate' => 'int:5', 'type' => 'text:3:4', 'explain' => true), - 'feed_overall_forums' => array('lang' => 'ACP_FEED_OVERALL_FORUMS', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), - 'feed_overall_forums_limit' => array('lang' => 'ACP_FEED_OVERALL_FORUMS_LIMIT', 'validate' => 'int:5', 'type' => 'text:3:4', 'explain' => false), - 'feed_overall_topics' => array('lang' => 'ACP_FEED_OVERALL_TOPIC', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), - 'feed_overall_topics_limit' => array('lang' => 'ACP_FEED_OVERALL_TOPIC_LIMIT', 'validate' => 'int:5', 'type' => 'text:3:4', 'explain' => false), + 'feed_http_auth' => array('lang' => 'ACP_FEED_HTTP_AUTH', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true), + + 'legend2' => 'ACP_FEED_POST_BASED', + 'feed_limit_post' => array('lang' => 'ACP_FEED_LIMIT', 'validate' => 'int:5', 'type' => 'text:3:4', 'explain' => true), + 'feed_overall' => array('lang' => 'ACP_FEED_OVERALL', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), 'feed_forum' => array('lang' => 'ACP_FEED_FORUM', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), 'feed_topic' => array('lang' => 'ACP_FEED_TOPIC', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), + + 'legend3' => 'ACP_FEED_TOPIC_BASED', + 'feed_limit_topic' => array('lang' => 'ACP_FEED_LIMIT', 'validate' => 'int:5', 'type' => 'text:3:4', 'explain' => true), + 'feed_topics_new' => array('lang' => 'ACP_FEED_TOPICS_NEW', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), + 'feed_topics_active' => array('lang' => 'ACP_FEED_TOPICS_ACTIVE', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), 'feed_news_id' => array('lang' => 'ACP_FEED_NEWS', 'validate' => 'string', 'type' => 'custom', 'method' => 'select_news_forums', 'explain' => true), + + 'legend4' => 'ACP_FEED_SETTINGS_OTHER', + 'feed_overall_forums' => array('lang' => 'ACP_FEED_OVERALL_FORUMS', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true ), 'feed_exclude_id' => array('lang' => 'ACP_FEED_EXCLUDE_ID', 'validate' => 'string', 'type' => 'custom', 'method' => 'select_exclude_forums', 'explain' => true), ) ); @@ -463,12 +472,20 @@ class acp_board if ($submit) { set_config($config_name, $config_value); + + if ($config_name == 'allow_quick_reply' && isset($_POST['allow_quick_reply_enable'])) + { + enable_bitfield_column_flag(FORUMS_TABLE, 'forum_flags', log(FORUM_FLAG_QUICK_REPLY, 2)); + } } } // Store news and exclude ids if ($mode == 'feed' && $submit) { + $cache->destroy('_feed_news_forum_ids'); + $cache->destroy('_feed_excluded_forum_ids'); + $this->store_feed_forums(FORUM_OPTION_FEED_NEWS, 'feed_news_id'); $this->store_feed_forums(FORUM_OPTION_FEED_EXCLUDE, 'feed_exclude_id'); } @@ -846,6 +863,20 @@ class acp_board return h_radio('config[board_disable]', $radio_ary, $value) . '
      '; } + /** + * Global quick reply enable/disable setting and button to enable in all forums + */ + function quick_reply($value, $key) + { + global $user; + + $radio_ary = array(1 => 'YES', 0 => 'NO'); + + return h_radio('config[allow_quick_reply]', $radio_ary, $value) . + '

      '; + } + + /** * Select default dateformat */ @@ -910,7 +941,7 @@ class acp_board { global $user, $config; - $forum_list = make_forum_select(false, false, true, false, false, false, true); + $forum_list = make_forum_select(false, false, true, true, true, false, true); // Build forum options $s_forum_options = '
{L_FORUM_STATS}
'; + } + break; + + case 'fromcache': + $endtime = explode(' ', microtime()); + $endtime = $endtime[0] + $endtime[1]; + + $result = @sqlsrv_query($this->db_connect_id, $query); + while ($void = @sqlsrv_fetch_array($result)) + { + // Take the time spent on parsing rows into account + } + @sqlsrv_free_stmt($result); + + $splittime = explode(' ', microtime()); + $splittime = $splittime[0] + $splittime[1]; + + $this->sql_report('record_fromcache', $query, $endtime, $splittime); + + break; + } + } + + /** + * Utility method used to retrieve number of rows + * Emulates mysql_num_rows + * Used in acp_database.php -> write_data_mssqlnative() + */ + function mssqlnative_num_rows($res) + { + if ($res !== false) + { + $row = new result_mssqlnative($res); + $num_rows = $row->num_rows(); + return $num_rows; + } + else + { + return false; + } + } +} + +?> \ No newline at end of file diff --git a/phpBB/includes/db/mysql.php b/phpBB/includes/db/mysql.php index c03b38708c..0487dfa6d2 100644 --- a/phpBB/includes/db/mysql.php +++ b/phpBB/includes/db/mysql.php @@ -44,7 +44,7 @@ class dbal_mysql extends dbal $this->sql_layer = 'mysql4'; - $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword, $new_link) : @mysql_connect($this->server, $this->user, $sqlpassword, $new_link); + $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword) : @mysql_connect($this->server, $this->user, $sqlpassword, $new_link); if ($this->db_connect_id && $this->dbname != '') { diff --git a/phpBB/includes/db/oracle.php b/phpBB/includes/db/oracle.php index 63cdb7126d..55b3599800 100644 --- a/phpBB/includes/db/oracle.php +++ b/phpBB/includes/db/oracle.php @@ -622,6 +622,11 @@ class dbal_oracle extends dbal return 'BITAND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : ''); } + function _sql_bit_or($column_name, $bit, $compare = '') + { + return 'BITOR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : ''); + } + /** * return sql error array * @access private diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index 396267432a..38f910974a 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -202,7 +202,7 @@ function set_config_count($config_name, $increment, $is_dynamic = false) function gen_rand_string($num_chars = 8) { $rand_str = unique_id(); - $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35))); + $rand_str = str_replace(array('0', 'O'), array('Z', 'Y'), strtoupper(base_convert($rand_str, 16, 34))); return substr($rand_str, 0, $num_chars); } @@ -556,11 +556,11 @@ function _hash_crypt_private($password, $setting, &$itoa64) * * @param string $email Email address * -* @return string Big Integer +* @return string Unsigned Big Integer */ function phpbb_email_hash($email) { - return crc32(strtolower($email)) . strlen($email); + return sprintf('%u', crc32(strtolower($email))) . strlen($email); } /** @@ -2336,6 +2336,19 @@ function redirect($url, $return = false, $disable_cd_check = false) // Relative uri $pathinfo = pathinfo($url); + if (!$disable_cd_check && !file_exists($pathinfo['dirname'])) + { + $url = str_replace('../', '', $url); + $pathinfo = pathinfo($url); + + if (!file_exists($pathinfo['dirname'])) + { + // fallback to "last known user page" + $url = generate_board_url() . '/' . $user->page['page']; + break; + } + } + // Is the uri pointing to the current directory? if ($pathinfo['dirname'] == '.') { @@ -3531,7 +3544,7 @@ function msg_handler($errno, $msg_text, $errfile, $errline) } } - if (defined('DEBUG') || defined('IN_CRON') || defined('IMAGE_OUTPUT')) + if ((defined('DEBUG') || defined('IN_CRON') || defined('IMAGE_OUTPUT')) && isset($db)) { // let's avoid loops $db->sql_return_on_error(true); @@ -3920,6 +3933,108 @@ function phpbb_optionset($bit, $set, $data) return $data; } +/** +* Login using http authenticate. +* +* @param array $param Parameter array, see $param_defaults array. +* +* @return void +*/ +function phpbb_http_login($param) +{ + global $auth, $user; + global $config; + + $param_defaults = array( + 'auth_message' => '', + + 'autologin' => false, + 'viewonline' => true, + 'admin' => false, + ); + + // Overwrite default values with passed values + $param = array_merge($param_defaults, $param); + + // User is already logged in + // We will not overwrite his session + if (!empty($user->data['is_registered'])) + { + return; + } + + // $_SERVER keys to check + $username_keys = array( + 'PHP_AUTH_USER', + 'Authorization', + 'REMOTE_USER', 'REDIRECT_REMOTE_USER', + 'HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION', + 'REMOTE_AUTHORIZATION', 'REDIRECT_REMOTE_AUTHORIZATION', + 'AUTH_USER', + ); + + $password_keys = array( + 'PHP_AUTH_PW', + 'REMOTE_PASSWORD', + 'AUTH_PASSWORD', + ); + + $username = null; + foreach ($username_keys as $k) + { + if (isset($_SERVER[$k])) + { + $username = $_SERVER[$k]; + break; + } + } + + $password = null; + foreach ($password_keys as $k) + { + if (isset($_SERVER[$k])) + { + $password = $_SERVER[$k]; + break; + } + } + + // Decode encoded information (IIS, CGI, FastCGI etc.) + if (!is_null($username) && is_null($password) && strpos($username, 'Basic ') === 0) + { + list($username, $password) = explode(':', base64_decode(substr($username, 6)), 2); + } + + if (!is_null($username) && !is_null($password)) + { + set_var($username, $username, 'string', true); + set_var($password, $password, 'string', true); + + $auth_result = $auth->login($username, $password, $param['autologin'], $param['viewonline'], $param['admin']); + + if ($auth_result['status'] == LOGIN_SUCCESS) + { + return; + } + else if ($auth_result['status'] == LOGIN_ERROR_ATTEMPTS) + { + header('HTTP/1.0 401 Unauthorized'); + trigger_error('NOT_AUTHORISED'); + } + } + + // Prepend sitename to auth_message + $param['auth_message'] = ($param['auth_message'] === '') ? $config['sitename'] : $config['sitename'] . ' - ' . $param['auth_message']; + + // We should probably filter out non-ASCII characters - RFC2616 + $param['auth_message'] = preg_replace('/[\x80-\xFF]/', '?', $param['auth_message']); + + header('WWW-Authenticate: Basic realm="' . $param['auth_message'] . '"'); + header('HTTP/1.0 401 Unauthorized'); + + trigger_error('NOT_AUTHORISED'); +} + /** * Generate page header */ @@ -4139,8 +4254,10 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0 'S_LOGIN_REDIRECT' => build_hidden_fields(array('redirect' => str_replace('&', '&', build_url()))), 'S_ENABLE_FEEDS' => ($config['feed_enable']) ? true : false, + 'S_ENABLE_FEEDS_OVERALL' => ($config['feed_overall']) ? true : false, 'S_ENABLE_FEEDS_FORUMS' => ($config['feed_overall_forums']) ? true : false, - 'S_ENABLE_FEEDS_TOPICS' => ($config['feed_overall_topics']) ? true : false, + 'S_ENABLE_FEEDS_TOPICS' => ($config['feed_topics_new']) ? true : false, + 'S_ENABLE_FEEDS_TOPICS_ACTIVE' => ($config['feed_topics_active']) ? true : false, 'S_ENABLE_FEEDS_NEWS' => ($s_feed_news) ? true : false, 'T_THEME_PATH' => "{$web_path}styles/" . $user->theme['theme_path'] . '/theme', diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index ddadda8ed2..93244be55c 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -66,8 +66,6 @@ function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = { global $db, $user, $auth; - $acl = ($ignore_acl) ? '' : (($only_acl_post) ? 'f_post' : array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel')); - // This query is identical to the jumpbox one $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id FROM ' . FORUMS_TABLE . ' @@ -98,18 +96,21 @@ function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = $right = $row['right_id']; $disabled = false; - if ($acl && !$auth->acl_gets($acl, $row['forum_id'])) + if (!$ignore_acl && $auth->acl_get('f_list', $row['forum_id'])) { - // List permission? - if ($auth->acl_get('f_list', $row['forum_id'])) + if ($only_acl_post && !$auth->acl_get('f_post', $row['forum_id']) || (!$auth->acl_get('m_approve', $row['forum_id']) && !$auth->acl_get('f_noapprove', $row['forum_id']))) { $disabled = true; } - else + else if (!$only_acl_post && !$auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id'])) { - continue; + $disabled = true; } } + else if (!$ignore_acl) + { + continue; + } if ( ((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id) @@ -912,7 +913,13 @@ function delete_attachments($mode, $ids, $resync = true) { global $db, $config; - if (is_array($ids) && sizeof($ids)) + // 0 is as bad as an empty array + if (empty($ids)) + { + return false; + } + + if (is_array($ids)) { $ids = array_unique($ids); $ids = array_map('intval', $ids); @@ -922,11 +929,6 @@ function delete_attachments($mode, $ids, $resync = true) $ids = array((int) $ids); } - if (!sizeof($ids)) - { - return false; - } - $sql_where = ''; switch ($mode) @@ -3040,6 +3042,7 @@ function get_database_size() case 'mssql': case 'mssql_odbc': + case 'mssqlnative': $sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize FROM sysfiles'; $result = $db->sql_query($sql, 7200); @@ -3307,4 +3310,24 @@ function obtain_latest_version_info($force_update = false, $warn_fail = false, $ return $info; } +/** + * Enables a particular flag in a bitfield column of a given table. + * + * @param string $table_name The table to update + * @param string $column_name The column containing a bitfield to update + * @param int $flag The binary flag which is OR-ed with the current column value + * @param string $sql_more This string is attached to the sql query generated to update the table. + * + * @return void + */ +function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '') +{ + global $db; + + $sql = 'UPDATE ' . $table_name . ' + SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . ' + ' . $sql_more; + $db->sql_query($sql); +} + ?> \ No newline at end of file diff --git a/phpBB/includes/functions_compress.php b/phpBB/includes/functions_compress.php index 590daabf1d..f17c780a65 100644 --- a/phpBB/includes/functions_compress.php +++ b/phpBB/includes/functions_compress.php @@ -80,6 +80,11 @@ class compress } } } + else + { + // $src does not exist + return false; + } return true; } @@ -89,6 +94,11 @@ class compress */ function add_custom_file($src, $filename) { + if (!file_exists($src)) + { + return false; + } + $this->data($filename, file_get_contents($src), false, stat($src)); return true; } diff --git a/phpBB/includes/functions_content.php b/phpBB/includes/functions_content.php index c265d0ae41..faff9dd0de 100644 --- a/phpBB/includes/functions_content.php +++ b/phpBB/includes/functions_content.php @@ -1106,10 +1106,11 @@ function extension_allowed($forum_id, $extension, &$extensions) * @param string $string The text to truncate to the given length. String is specialchared. * @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char) * @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars). -* @param bool $allow_reply Allow Re: in front of string +* @param bool $allow_reply Allow Re: in front of string +* NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_legnth) and is deprecated. * @param string $append String to be appended */ -function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = true, $append = '') +function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '') { $chars = array(); diff --git a/phpBB/includes/functions_convert.php b/phpBB/includes/functions_convert.php index 82ec114c09..0fdae9b274 100644 --- a/phpBB/includes/functions_convert.php +++ b/phpBB/includes/functions_convert.php @@ -205,10 +205,12 @@ function get_group_id($group_name) /** * Generate the email hash stored in the users table +* +* Note: Deprecated, calls should directly go to phpbb_email_hash() */ function gen_email_hash($email) { - return (crc32(strtolower($email)) . strlen($email)); + return phpbb_email_hash($email); } /** @@ -1232,6 +1234,11 @@ function get_config() $convert->p_master->error($user->lang['FILE_NOT_FOUND'] . ': ' . $filename, __LINE__, __FILE__); } + if (isset($convert->config_schema['array_name'])) + { + unset($convert->config_schema['array_name']); + } + $convert_config = extract_variables_from_file($filename); if (!empty($convert->config_schema['array_name'])) { @@ -1264,6 +1271,7 @@ function restore_config($schema) global $db, $config; $convert_config = get_config(); + foreach ($schema['settings'] as $config_name => $src) { if (preg_match('/(.*)\((.*)\)/', $src, $m)) @@ -1274,8 +1282,16 @@ function restore_config($schema) } else { - $config_value = (isset($convert_config[$src])) ? $convert_config[$src] : ''; - } + if ($schema['table_format'] != 'file' || empty($schema['array_name'])) + { + $config_value = (isset($convert_config[$src])) ? $convert_config[$src] : ''; + } + else if (!empty($schema['array_name'])) + { + $src_ary = $schema['array_name']; + $config_value = (isset($convert_config[$src_ary][$src])) ? $convert_config[$src_ary][$src] : ''; + } + } if ($config_value !== '') { @@ -1629,6 +1645,7 @@ function mass_auth($ug_type, $forum_id, $ug_id, $acl_list, $setting = ACL_NO) case 'mssql': case 'sqlite': + case 'mssqlnative': $sql = implode(' UNION ALL ', preg_replace('#^(.*?)$#', 'SELECT \1', $sql_subary)); break; diff --git a/phpBB/includes/functions_display.php b/phpBB/includes/functions_display.php index 58cbdaef5b..f49aa42324 100644 --- a/phpBB/includes/functions_display.php +++ b/phpBB/includes/functions_display.php @@ -594,7 +594,7 @@ function generate_forum_nav(&$forum_data) 'FORUM_NAME' => $forum_data['forum_name'], 'FORUM_DESC' => generate_text_for_display($forum_data['forum_desc'], $forum_data['forum_desc_uid'], $forum_data['forum_desc_bitfield'], $forum_data['forum_desc_options']), - 'S_ENABLE_FEEDS_FORUM' => ($config['feed_forum'] && !phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $forum_data['forum_options'])) ? true : false, + 'S_ENABLE_FEEDS_FORUM' => ($config['feed_forum'] && $forum_data['forum_type'] == FORUM_POST && !phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $forum_data['forum_options'])) ? true : false, )); return; diff --git a/phpBB/includes/functions_install.php b/phpBB/includes/functions_install.php index a5889224a1..992e8d6bb0 100644 --- a/phpBB/includes/functions_install.php +++ b/phpBB/includes/functions_install.php @@ -23,7 +23,14 @@ function can_load_dll($dll) { // SQLite2 is a tricky thing, from 5.0.0 it requires PDO; if PDO is not loaded we must state that SQLite is unavailable // as the installer doesn't understand that the extension has a prerequisite. - if ($dll == 'sqlite' && version_compare(PHP_VERSION, '5.0.0', '>=') && !extension_loaded('pdo')) + // + // On top of this sometimes the SQLite extension is compiled for a different version of PDO + // by some Linux distributions which causes phpBB to bomb out with a blank page. + // + // Net result we'll disable automatic inclusion of SQLite support + // + // See: r9618 and #56105 + if ($dll == 'sqlite') { return false; } @@ -88,6 +95,16 @@ function get_available_dbms($dbms = false, $return_unavailable = false, $only_20 'AVAILABLE' => true, '2.0.x' => true, ), + 'mssqlnative' => array( + 'LABEL' => 'MS SQL Server 2005+ [ Native ]', + 'SCHEMA' => 'mssql', + 'MODULE' => 'sqlsrv', + 'DELIM' => 'GO', + 'COMMENTS' => 'remove_comments', + 'DRIVER' => 'mssqlnative', + 'AVAILABLE' => true, + '2.0.x' => false, + ), 'oracle' => array( 'LABEL' => 'Oracle', 'SCHEMA' => 'oracle', @@ -213,6 +230,7 @@ function get_tables($db) case 'mssql': case 'mssql_odbc': + case 'mssqlnative': $sql = "SELECT name FROM sysobjects WHERE type='U'"; @@ -306,6 +324,7 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix, case 'mssql': case 'mssql_odbc': + case 'mssqlnative': $prefix_length = 90; break; diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index 8f4e582b3c..99883cd9ca 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -184,6 +184,9 @@ class messenger if (!trim($template_lang)) { + // fall back to board default language if the user's language is + // missing $template_file. If this does not exist either, + // $tpl->set_custom_template will do a trigger_error $template_lang = basename($config['default_lang']); } @@ -193,13 +196,23 @@ class messenger $this->tpl_msg[$template_lang . $template_file] = new template(); $tpl = &$this->tpl_msg[$template_lang . $template_file]; + $fallback_template_path = false; + if (!$template_path) { $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; $template_path .= $template_lang . '/email'; + + // we can only specify default language fallback when the path is not a custom one for which we + // do not know the default language alternative + if ($template_lang !== basename($config['default_lang'])) + { + $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; + $fallback_template_path .= basename($config['default_lang']) . '/email'; + } } - $tpl->set_custom_template($template_path, $template_lang . '_email', 'email'); + $tpl->set_custom_template($template_path, $template_lang . '_email', $fallback_template_path); $tpl->set_filenames(array( 'body' => $template_file . '.txt', diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php index 069740ebda..21c5fe7aca 100644 --- a/phpBB/includes/functions_posting.php +++ b/phpBB/includes/functions_posting.php @@ -46,7 +46,7 @@ function generate_smilies($mode, $forum_id) page_header($user->lang['SMILIES']); - $sql = 'SELECT COUNT(smiley_id) AS count + $sql = 'SELECT COUNT(smiley_id) AS item_count FROM ' . SMILIES_TABLE . ' GROUP BY smiley_url'; $result = $db->sql_query($sql, 3600); @@ -85,10 +85,10 @@ function generate_smilies($mode, $forum_id) if ($mode == 'window') { - $sql = 'SELECT smiley_url, MIN(emotion) as emotion, MIN(code) AS code, smiley_width, smiley_height + $sql = 'SELECT smiley_url, MIN(emotion) as emotion, MIN(code) AS code, smiley_width, smiley_height, MIN(smiley_order) AS min_smiley_order FROM ' . SMILIES_TABLE . ' GROUP BY smiley_url, smiley_width, smiley_height - ORDER BY smiley_order'; + ORDER BY min_smiley_order'; $result = $db->sql_query_limit($sql, $config['smilies_per_page'], $start, 3600); } else @@ -2525,7 +2525,7 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u VALUES (' . $user->data['user_id'] . ', ' . $data['topic_id'] . ')'; $db->sql_query($sql); } - else if ($data['notify_set'] && !$data['notify']) + else if (($config['email_enable'] || $config['jab_enable']) && $data['notify_set'] && !$data['notify']) { $sql = 'DELETE FROM ' . TOPICS_WATCH_TABLE . ' WHERE user_id = ' . $user->data['user_id'] . ' diff --git a/phpBB/includes/mcp/mcp_logs.php b/phpBB/includes/mcp/mcp_logs.php index 429e19b668..6da810a489 100644 --- a/phpBB/includes/mcp/mcp_logs.php +++ b/phpBB/includes/mcp/mcp_logs.php @@ -175,7 +175,7 @@ class mcp_logs $template->assign_vars(array( 'PAGE_NUMBER' => on_page($log_count, $config['topics_per_page'], $start), 'TOTAL' => ($log_count == 1) ? $user->lang['TOTAL_LOG'] : sprintf($user->lang['TOTAL_LOGS'], $log_count), - 'PAGINATION' => generate_pagination($this->u_action . "&$u_sort_param$keywords_param", $log_count, $config['topics_per_page'], $start, true), + 'PAGINATION' => generate_pagination($this->u_action . "&$u_sort_param$keywords_param", $log_count, $config['topics_per_page'], $start), 'L_TITLE' => $user->lang['MCP_LOGS'], diff --git a/phpBB/includes/mcp/mcp_main.php b/phpBB/includes/mcp/mcp_main.php index 50b05e989f..80c3559649 100644 --- a/phpBB/includes/mcp/mcp_main.php +++ b/phpBB/includes/mcp/mcp_main.php @@ -568,7 +568,7 @@ function mcp_move_topic($topic_ids) { $additional_msg = $user->lang['FORUM_NOT_POSTABLE']; } - else if (!$auth->acl_get('f_post', $to_forum_id)) + else if (!$auth->acl_get('f_post', $to_forum_id) || (!$auth->acl_get('m_approve', $to_forum_id) && !$auth->acl_get('f_noapprove', $to_forum_id))) { $additional_msg = $user->lang['USER_CANNOT_POST']; } diff --git a/phpBB/includes/mcp/mcp_notes.php b/phpBB/includes/mcp/mcp_notes.php index 757860e1af..c684eb6f52 100644 --- a/phpBB/includes/mcp/mcp_notes.php +++ b/phpBB/includes/mcp/mcp_notes.php @@ -198,7 +198,7 @@ class mcp_notes $log_data = array(); $log_count = 0; - view_log('user', $log_data, $log_count, $config['posts_per_page'], $start, 0, 0, $user_id, $sql_where, $sql_sort, $keywords); + view_log('user', $log_data, $log_count, $config['topics_per_page'], $start, 0, 0, $user_id, $sql_where, $sql_sort, $keywords); if ($log_count) { @@ -226,8 +226,8 @@ class mcp_notes 'L_TITLE' => $user->lang['MCP_NOTES_USER'], - 'PAGE_NUMBER' => on_page($log_count, $config['posts_per_page'], $start), - 'PAGINATION' => generate_pagination($this->u_action . "&$u_sort_param$keywords_param", $log_count, $config['topics_per_page'], $start, true), + 'PAGE_NUMBER' => on_page($log_count, $config['topics_per_page'], $start), + 'PAGINATION' => generate_pagination($this->u_action . "&$u_sort_param$keywords_param", $log_count, $config['topics_per_page'], $start), 'TOTAL_REPORTS' => ($log_count == 1) ? $user->lang['LIST_REPORT'] : sprintf($user->lang['LIST_REPORTS'], $log_count), 'RANK_TITLE' => $rank_title, diff --git a/phpBB/includes/mcp/mcp_topic.php b/phpBB/includes/mcp/mcp_topic.php index abdb839e7b..9779478330 100644 --- a/phpBB/includes/mcp/mcp_topic.php +++ b/phpBB/includes/mcp/mcp_topic.php @@ -106,7 +106,14 @@ function mcp_topic_view($id, $mode, $action) if ($total == -1) { - $total = $topic_info['topic_replies'] + 1; + if ($auth->acl_get('m_approve', $topic_info['forum_id'])) + { + $total = $topic_info['topic_replies_real'] + 1; + } + else + { + $total = $topic_info['topic_replies'] + 1; + } } $posts_per_page = max(0, request_var('posts_per_page', intval($config['posts_per_page']))); diff --git a/phpBB/includes/message_parser.php b/phpBB/includes/message_parser.php index 8979511d9a..50aad8588a 100644 --- a/phpBB/includes/message_parser.php +++ b/phpBB/includes/message_parser.php @@ -1284,6 +1284,7 @@ class parse_message extends bbcode_firstpass { case 'mssql': case 'mssql_odbc': + case 'mssqlnative': $sql = 'SELECT * FROM ' . SMILIES_TABLE . ' ORDER BY LEN(code) DESC'; diff --git a/phpBB/includes/questionnaire/questionnaire.php b/phpBB/includes/questionnaire/questionnaire.php index 659c088763..cbd7638809 100644 --- a/phpBB/includes/questionnaire/questionnaire.php +++ b/phpBB/includes/questionnaire/questionnaire.php @@ -162,25 +162,59 @@ class phpbb_questionnaire_system_data_provider $server_address = $_SERVER['LOCAL_ADDR']; } - $ip_address_ary = explode('.', $server_address); - - // build ip - if (!isset($ip_address_ary[0]) || !isset($ip_address_ary[1])) - { - $ip_address_ary = explode('.', '0.0.0.0'); - } - return array( 'os' => PHP_OS, 'httpd' => $_SERVER['SERVER_SOFTWARE'], // we don't want the real IP address (for privacy policy reasons) but only // a network address to see whether your installation is running on a private or public network. + 'private_ip' => $this->is_private_ip($server_address), + 'ipv6' => strpos($server_address, ':') !== false, + ); + } + + /** + * Checks whether the given IP is in a private network. + * + * @param string $ip IP in v4 dot-decimal or v6 hex format + * @return bool true if the IP is from a private network, else false + */ + function is_private_ip($ip) + { + // IPv4 + if (strpos($ip, ':') === false) + { + $ip_address_ary = explode('.', $ip); + + // build ip + if (!isset($ip_address_ary[0]) || !isset($ip_address_ary[1])) + { + $ip_address_ary = explode('.', '0.0.0.0'); + } + // IANA reserved addresses for private networks (RFC 1918) are: // - 10.0.0.0/8 // - 172.16.0.0/12 // - 192.168.0.0/16 - 'ip' => $ip_address_ary[0] . '.' . $ip_address_ary[1] . '.XXX.YYY', - ); + if ($ip_address_ary[0] == '10' || + ($ip_address_ary[0] == '172' && intval($ip_address_ary[1]) > 15 && intval($ip_address_ary[1]) < 32) || + ($ip_address_ary[0] == '192' && $ip_address_ary[1] == '168') || + ($ip_address_ary[0] == '192' && $ip_address_ary[1] == '168')) + { + return true; + } + } + // IPv6 + else + { + // unique local unicast + $prefix = substr($ip, 0, 2); + if ($prefix == 'fc' || $prefix == 'fd') + { + return true; + } + } + + return false; } } @@ -233,6 +267,7 @@ class phpbb_questionnaire_phpbb_data_provider { global $phpbb_root_path, $phpEx; include("{$phpbb_root_path}config.$phpEx"); + unset($dbhost, $dbport, $dbname, $dbuser, $dbpasswd); // Just a precaution // Only send certain config vars $config_vars = array( @@ -315,13 +350,15 @@ class phpbb_questionnaire_phpbb_data_provider 'enable_pm_icons' => true, 'enable_post_confirm' => true, 'feed_enable' => true, - 'feed_limit' => true, + 'feed_http_auth' => true, + 'feed_limit_post' => true, + 'feed_limit_topic' => true, + 'feed_overall' => true, 'feed_overall_forums' => true, - 'feed_overall_forums_limit' => true, - 'feed_overall_topics' => true, - 'feed_overall_topics_limit' => true, 'feed_forum' => true, 'feed_topic' => true, + 'feed_topics_new' => true, + 'feed_topics_active' => true, 'feed_item_statistics' => true, 'flood_interval' => true, 'force_server_vars' => true, @@ -445,10 +482,13 @@ class phpbb_questionnaire_phpbb_data_provider } } + global $db; + $result['dbms'] = $dbms; $result['acm_type'] = $acm_type; $result['load_extensions'] = $load_extensions; $result['user_agent'] = 'Unknown'; + $result['dbms_version'] = $db->sql_server_info(true); // Try to get user agent vendor and version $match = array(); diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php index c72e674011..da3833754e 100644 --- a/phpBB/includes/search/fulltext_mysql.php +++ b/phpBB/includes/search/fulltext_mysql.php @@ -473,7 +473,7 @@ class fulltext_mysql extends search_backend while ($row = $db->sql_fetchrow($result)) { - $id_ary[] = $row[$field]; + $id_ary[] = (int) $row[$field]; } $db->sql_freeresult($result); @@ -650,7 +650,7 @@ class fulltext_mysql extends search_backend while ($row = $db->sql_fetchrow($result)) { - $id_ary[] = $row[$field]; + $id_ary[] = (int) $row[$field]; } $db->sql_freeresult($result); diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php index 5af3929ccd..c89e92711e 100644 --- a/phpBB/includes/search/fulltext_native.php +++ b/phpBB/includes/search/fulltext_native.php @@ -202,7 +202,8 @@ class fulltext_native extends search_backend { $sql = 'SELECT word_id, word_text, word_common FROM ' . SEARCH_WORDLIST_TABLE . ' - WHERE ' . $db->sql_in_set('word_text', $exact_words); + WHERE ' . $db->sql_in_set('word_text', $exact_words) . ' + ORDER BY word_count ASC'; $result = $db->sql_query($sql); // store an array of words and ids, remove common words @@ -377,10 +378,6 @@ class fulltext_native extends search_backend return false; } - sort($this->must_contain_ids); - sort($this->must_not_contain_ids); - sort($this->must_exclude_one_ids); - if (!empty($this->search_query)) { return true; @@ -420,11 +417,19 @@ class fulltext_native extends search_backend return false; } + $must_contain_ids = $this->must_contain_ids; + $must_not_contain_ids = $this->must_not_contain_ids; + $must_exclude_one_ids = $this->must_exclude_one_ids; + + sort($must_contain_ids); + sort($must_not_contain_ids); + sort($must_exclude_one_ids); + // generate a search_key from all the options to identify the results $search_key = md5(implode('#', array( - serialize($this->must_contain_ids), - serialize($this->must_not_contain_ids), - serialize($this->must_exclude_one_ids), + serialize($must_contain_ids), + serialize($must_not_contain_ids), + serialize($must_exclude_one_ids), $type, $fields, $terms, @@ -739,7 +744,7 @@ class fulltext_native extends search_backend while ($row = $db->sql_fetchrow($result)) { - $id_ary[] = $row[(($type == 'posts') ? 'post_id' : 'topic_id')]; + $id_ary[] = (int) $row[(($type == 'posts') ? 'post_id' : 'topic_id')]; } $db->sql_freeresult($result); @@ -981,7 +986,7 @@ class fulltext_native extends search_backend while ($row = $db->sql_fetchrow($result)) { - $id_ary[] = $row[$field]; + $id_ary[] = (int) $row[$field]; } $db->sql_freeresult($result); diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php index 933bd47347..11f1896332 100644 --- a/phpBB/includes/session.php +++ b/phpBB/includes/session.php @@ -1361,7 +1361,7 @@ class session WHERE user_id = ' . (int) $user_id; $db->sql_query($sql); - // Update last visit info first before deleting sessions + // If the user is logged in, update last visit info first before deleting sessions $sql = 'SELECT session_time, session_page FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . (int) $user_id . ' @@ -1370,10 +1370,13 @@ class session $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); - $sql = 'UPDATE ' . USERS_TABLE . ' - SET user_lastvisit = ' . (int) $row['session_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "' - WHERE user_id = " . (int) $user_id; - $db->sql_query($sql); + if ($row) + { + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_lastvisit = ' . (int) $row['session_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "' + WHERE user_id = " . (int) $user_id; + $db->sql_query($sql); + } // Let's also clear any current sessions for the specified user_id // If it's the current user then we'll leave this session intact diff --git a/phpBB/includes/template.php b/phpBB/includes/template.php index af5c9d3a47..f1c8094a9b 100644 --- a/phpBB/includes/template.php +++ b/phpBB/includes/template.php @@ -90,7 +90,7 @@ class template * Set custom template location (able to use directory outside of phpBB) * @access public */ - function set_custom_template($template_path, $template_name, $template_mode = 'template') + function set_custom_template($template_path, $template_name, $fallback_template_path = false) { global $phpbb_root_path, $user; @@ -103,12 +103,24 @@ class template $this->root = $template_path; $this->cachepath = $phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_'; - // As the template-engine is used for more than the template (emails, etc.), we should not set $user->theme in all cases, but only on the real template. - if ($template_mode == 'template') + if ($fallback_template_path !== false) { - $user->theme['template_storedb'] = false; - $user->theme['template_inherits_id'] = false; + if (substr($fallback_template_path, -1) == '/') + { + $fallback_template_path = substr($fallback_template_path, 0, -1); + } + + $this->inherit_root = $fallback_template_path; + $this->orig_tpl_inherits_id = true; } + else + { + $this->orig_tpl_inherits_id = false; + } + + // the database does not store the path or name of a custom template + // so there is no way we can properly store custom templates there + $this->orig_tpl_storedb = false; $this->_rootref = &$this->_tpldata['.'][0]; @@ -254,6 +266,12 @@ class template trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR); } + // reload these settings to have the values they had when this object was initialised + // using set_template or set_custom_template, they might otherwise have been overwritten + // by other template class instances in between. + $user->theme['template_storedb'] = $this->orig_tpl_storedb; + $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id; + $filename = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx; $this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0; diff --git a/phpBB/includes/ucp/ucp_pm_options.php b/phpBB/includes/ucp/ucp_pm_options.php index e80c0672cf..58c2d087c8 100644 --- a/phpBB/includes/ucp/ucp_pm_options.php +++ b/phpBB/includes/ucp/ucp_pm_options.php @@ -637,12 +637,29 @@ function define_action_option($hardcoded, $action_option, $action_lang, $folder) function define_rule_option($hardcoded, $rule_option, $rule_lang, $check_ary) { global $template; + global $module; + + $exclude = array(); + + if (!$module->loaded('zebra', 'friends')) + { + $exclude[RULE_IS_FRIEND] = true; + } + + if (!$module->loaded('zebra', 'foes')) + { + $exclude[RULE_IS_FOE] = true; + } $s_rule_options = ''; if (!$hardcoded) { foreach ($check_ary as $value => $_check) { + if (isset($exclude[$value])) + { + continue; + } $s_rule_options .= ''; } } diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php index e24acd89fc..f4f4abad4a 100644 --- a/phpBB/includes/ucp/ucp_profile.php +++ b/phpBB/includes/ucp/ucp_profile.php @@ -133,7 +133,7 @@ class ucp_profile $message = 'PROFILE_UPDATED'; - if ($config['email_enable'] && $data['email'] != $user->data['user_email'] && $user->data['user_type'] != USER_FOUNDER && ($config['require_activation'] == USER_ACTIVATION_SELF || $config['require_activation'] == USER_ACTIVATION_ADMIN)) + if ($auth->acl_get('u_chgemail') && $config['email_enable'] && $data['email'] != $user->data['user_email'] && $user->data['user_type'] != USER_FOUNDER && ($config['require_activation'] == USER_ACTIVATION_SELF || $config['require_activation'] == USER_ACTIVATION_ADMIN)) { $message = ($config['require_activation'] == USER_ACTIVATION_SELF) ? 'ACCOUNT_EMAIL_CHANGED' : 'ACCOUNT_EMAIL_CHANGED_ADMIN'; diff --git a/phpBB/includes/ucp/ucp_register.php b/phpBB/includes/ucp/ucp_register.php index 8359c223e0..9656a4a3af 100644 --- a/phpBB/includes/ucp/ucp_register.php +++ b/phpBB/includes/ucp/ucp_register.php @@ -333,6 +333,12 @@ class ucp_register trigger_error('NO_USER', E_USER_ERROR); } + // Okay, captcha, your job is done. + if ($config['enable_confirm'] && isset($captcha)) + { + $captcha->reset(); + } + if ($coppa && $config['email_enable']) { $message = $user->lang['ACCOUNT_COPPA']; diff --git a/phpBB/includes/ucp/ucp_remind.php b/phpBB/includes/ucp/ucp_remind.php index df6733d038..f9b792de20 100644 --- a/phpBB/includes/ucp/ucp_remind.php +++ b/phpBB/includes/ucp/ucp_remind.php @@ -77,11 +77,12 @@ class ucp_remind $server_url = generate_board_url(); - $key_len = 54 - strlen($server_url); - $key_len = max(6, $key_len); // we want at least 6 - $key_len = ($config['max_pass_chars']) ? min($key_len, $config['max_pass_chars']) : $key_len; // we want at most $config['max_pass_chars'] - $user_actkey = substr(gen_rand_string(10), 0, $key_len); - $user_password = gen_rand_string(8); + // Make password at least 8 characters long, make it longer if admin wants to. + // gen_rand_string() however has a limit of 12 or 13. + $user_password = gen_rand_string(max(8, rand((int) $config['min_pass_chars'], (int) $config['max_pass_chars']))); + + // For the activation key a random length between 6 and 10 will do. + $user_actkey = gen_rand_string(rand(6, 10)); $sql = 'UPDATE ' . USERS_TABLE . " SET user_newpasswd = '" . $db->sql_escape(phpbb_hash($user_password)) . "', user_actkey = '" . $db->sql_escape($user_actkey) . "' diff --git a/phpBB/install/convertors/convert_phpbb20.php b/phpBB/install/convertors/convert_phpbb20.php index 5a6603883b..1d6b79bbec 100644 --- a/phpBB/install/convertors/convert_phpbb20.php +++ b/phpBB/install/convertors/convert_phpbb20.php @@ -32,7 +32,7 @@ unset($dbpasswd); $convertor_data = array( 'forum_name' => 'phpBB 2.0.x', 'version' => '1.0.3', - 'phpbb_version' => '3.0.6', + 'phpbb_version' => '3.0.7', 'author' => 'phpBB Group', 'dbms' => $dbms, 'dbhost' => $dbhost, @@ -78,6 +78,15 @@ $tables = array( * * 'table_format' can take the value 'file' to indicate a config file. In this case array_name * is set to indicate the name of the array the config values are stored in +* Example of using a file: +* $config_schema = array( +* 'table_format' => 'file', +* 'filename' => 'NAME OF FILE', // If the file is not in the root directory, the path needs to be added with no leading slash +* 'array_name' => 'NAME OF ARRAY', // Only used if the configuration file stores the setting in an array. +* 'settings' => array( +* 'board_email' => 'SUPPORT_EMAIL', // target config name => source target name +* ) +* ); * 'table_format' can be an array if the values are stored in a table which is an assosciative array * (as per phpBB 2.0.x) * If left empty, values are assumed to be stored in a table where each config setting is diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index bf9c183e0c..df8aadfdb2 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -8,7 +8,7 @@ * */ -$updates_to_version = '3.0.6'; +$updates_to_version = '3.0.8-dev'; // Enter any version to update from to test updates. The version within the db will not be updated. $debug_from_version = false; @@ -885,18 +885,32 @@ function database_update_info() ), ), ), - // Changes from 3.0.6-RC1 to 3.0.6-RC2 - '3.0.6-RC1' => array( - 'drop_keys' => array( - LOG_TABLE => array('log_time'), - ), - ), + + // No changes from 3.0.6-RC1 to 3.0.6-RC2 + '3.0.6-RC1' => array(), // No changes from 3.0.6-RC2 to 3.0.6-RC3 '3.0.6-RC2' => array(), // No changes from 3.0.6-RC3 to 3.0.6-RC4 '3.0.6-RC3' => array(), // No changes from 3.0.6-RC4 to 3.0.6 '3.0.6-RC4' => array(), + + // Changes from 3.0.6 to 3.0.7-RC1 + '3.0.6' => array( + 'drop_keys' => array( + LOG_TABLE => array('log_time'), + ), + 'add_index' => array( + TOPICS_TRACK_TABLE => array( + 'topic_id' => array('topic_id'), + ), + ), + ), + + // No changes from 3.0.7-RC1 to 3.0.7-RC2 + '3.0.7-RC1' => array(), + // No changes from 3.0.7-RC2 to 3.0.7 + '3.0.7-RC2' => array(), ); } @@ -1569,6 +1583,65 @@ function change_database_data(&$no_updates, $version) // No changes from 3.0.6-RC4 to 3.0.6 case '3.0.6-RC4': break; + + // Changes from 3.0.6 to 3.0.7-RC1 + case '3.0.6': + + // ATOM Feeds + set_config('feed_overall', '1'); + set_config('feed_http_auth', '0'); + set_config('feed_limit_post', (string) (isset($config['feed_limit']) ? (int) $config['feed_limit'] : 15)); + set_config('feed_limit_topic', (string) (isset($config['feed_overall_topics_limit']) ? (int) $config['feed_overall_topics_limit'] : 10)); + set_config('feed_topics_new', (!empty($config['feed_overall_topics']) ? '1' : '0')); + set_config('feed_topics_active', (!empty($config['feed_overall_topics']) ? '1' : '0')); + + // Delete all text-templates from the template_data + $sql = 'DELETE FROM ' . STYLES_TEMPLATE_DATA_TABLE . ' + WHERE template_filename ' . $db->sql_like_expression($db->any_char . '.txt'); + _sql($sql, $errored, $error_ary); + + $no_updates = false; + break; + + // Changes from 3.0.7-RC1 to 3.0.7-RC2 + case '3.0.7-RC1': + + $sql = 'SELECT user_id, user_email, user_email_hash + FROM ' . USERS_TABLE . ' + WHERE user_type <> ' . USER_IGNORE . " + AND user_email <> ''"; + $result = $db->sql_query($sql); + + $i = 0; + while ($row = $db->sql_fetchrow($result)) + { + // Snapshot of the phpbb_email_hash() function + // We cannot call it directly because the auto updater updates the DB first. :/ + $user_email_hash = sprintf('%u', crc32(strtolower($row['user_email']))) . strlen($row['user_email']); + + if ($user_email_hash != $row['user_email_hash']) + { + $sql_ary = array( + 'user_email_hash' => $user_email_hash, + ); + + $sql = 'UPDATE ' . USERS_TABLE . ' + SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' + WHERE user_id = ' . (int) $row['user_id']; + _sql($sql, $errored, $error_ary, ($i % 100 == 0)); + + ++$i; + } + } + $db->sql_freeresult($result); + + $no_updates = false; + + break; + + // No changes from 3.0.7-RC2 to 3.0.7 + case '3.0.7-RC2': + break; } } @@ -1715,7 +1788,37 @@ class updater_db_tools 'VCHAR_CI' => '[varchar] (255)', 'VARBINARY' => '[varchar] (255)', ), - + + 'mssqlnative' => array( + 'INT:' => '[int]', + 'BINT' => '[float]', + 'UINT' => '[int]', + 'UINT:' => '[int]', + 'TINT:' => '[int]', + 'USINT' => '[int]', + 'BOOL' => '[int]', + 'VCHAR' => '[varchar] (255)', + 'VCHAR:' => '[varchar] (%d)', + 'CHAR:' => '[char] (%d)', + 'XSTEXT' => '[varchar] (1000)', + 'STEXT' => '[varchar] (3000)', + 'TEXT' => '[varchar] (8000)', + 'MTEXT' => '[text]', + 'XSTEXT_UNI'=> '[varchar] (100)', + 'STEXT_UNI' => '[varchar] (255)', + 'TEXT_UNI' => '[varchar] (4000)', + 'MTEXT_UNI' => '[text]', + 'TIMESTAMP' => '[int]', + 'DECIMAL' => '[float]', + 'DECIMAL:' => '[float]', + 'PDECIMAL' => '[float]', + 'PDECIMAL:' => '[float]', + 'VCHAR_UNI' => '[varchar] (255)', + 'VCHAR_UNI:'=> '[varchar] (%d)', + 'VCHAR_CI' => '[varchar] (255)', + 'VARBINARY' => '[varchar] (255)', + ), + 'oracle' => array( 'INT:' => 'number(%d)', 'BINT' => 'number(20)', @@ -1817,7 +1920,7 @@ class updater_db_tools * A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules. * @var array */ - var $supported_dbms = array('firebird', 'mssql', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite'); + var $supported_dbms = array('firebird', 'mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite'); /** * This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array). @@ -1862,6 +1965,10 @@ class updater_db_tools case 'mssql_odbc': $this->sql_layer = 'mssql'; break; + + case 'mssqlnative': + $this->sql_layer = 'mssqlnative'; + break; default: $this->sql_layer = $this->db->sql_layer; @@ -2294,6 +2401,7 @@ class updater_db_tools // same deal with PostgreSQL, we must perform more complex operations than // we technically could case 'mssql': + case 'mssqlnative': $sql = "SELECT c.name FROM syscolumns c LEFT JOIN sysobjects o ON c.id = o.id @@ -2397,7 +2505,7 @@ class updater_db_tools */ function sql_index_exists($table_name, $index_name) { - if ($this->sql_layer == 'mssql') + if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') { $sql = "EXEC sp_statistics '$table_name'"; $result = $this->db->sql_query($sql); @@ -2502,7 +2610,7 @@ class updater_db_tools */ function sql_unique_index_exists($table_name, $index_name) { - if ($this->sql_layer == 'mssql') + if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') { $sql = "EXEC sp_statistics '$table_name'"; $result = $this->db->sql_query($sql); @@ -2741,6 +2849,7 @@ class updater_db_tools break; case 'mssql': + case 'mssqlnative': $sql .= " {$column_type} "; $sql_default = " {$column_type} "; @@ -2890,6 +2999,7 @@ class updater_db_tools break; case 'mssql': + case 'mssqlnative': // Does not support AFTER, only through temporary table $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default']; break; @@ -2907,7 +3017,29 @@ class updater_db_tools case 'postgres': // Does not support AFTER, only through temporary table - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql']; + + if (version_compare($this->db->sql_server_info(true), '8.0', '>=')) + { + $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql']; + } + else + { + // old versions cannot add columns with default and null information + $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint']; + + if (isset($column_data['null'])) + { + if ($column_data['null'] == 'NOT NULL') + { + $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL'; + } + } + + if (isset($column_data['default'])) + { + $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default']; + } + } break; case 'sqlite': @@ -2992,6 +3124,7 @@ class updater_db_tools break; case 'mssql': + case 'mssqlnative': $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']'; break; @@ -3086,6 +3219,7 @@ class updater_db_tools switch ($this->sql_layer) { case 'mssql': + case 'mssqlnative': $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name; break; @@ -3122,6 +3256,7 @@ class updater_db_tools break; case 'mssql': + case 'mssqlnative': $sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD "; $sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED ("; $sql .= '[' . implode("],\n\t\t[", $column) . ']'; @@ -3215,6 +3350,7 @@ class updater_db_tools break; case 'mssql': + case 'mssqlnative': $statements[] = 'CREATE UNIQUE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]'; break; } @@ -3244,6 +3380,7 @@ class updater_db_tools break; case 'mssql': + case 'mssqlnative': $statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]'; break; } @@ -3276,6 +3413,7 @@ class updater_db_tools break; case 'mssql': + case 'mssqlnative': $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql']; if (!empty($column_data['default'])) diff --git a/phpBB/install/install_convert.php b/phpBB/install/install_convert.php index 7f1b9de5b6..06c3a8b4a6 100644 --- a/phpBB/install/install_convert.php +++ b/phpBB/install/install_convert.php @@ -1248,6 +1248,7 @@ class install_convert extends module { case 'mssql': case 'mssql_odbc': + case 'mssqlnative': $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' ON'); break; } @@ -1375,6 +1376,7 @@ class install_convert extends module { case 'mssql': case 'mssql_odbc': + case 'mssqlnative': $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' OFF'); break; diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index 1cc1365752..f4989b5bd7 100644 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -1203,6 +1203,7 @@ class install_install extends module { case 'mssql': case 'mssql_odbc': + case 'mssqlnative': $sql_query = preg_replace('#\# MSSQL IDENTITY (phpbb_[a-z_]+) (ON|OFF) \##s', 'SET IDENTITY_INSERT \1 \2;', $sql_query); break; @@ -1375,7 +1376,7 @@ class install_install extends module $sql_ary[] = 'UPDATE ' . $data['table_prefix'] . "config SET config_value = 'phpbb_captcha_gd' WHERE config_name = 'captcha_plugin'"; - + $sql_ary[] = 'UPDATE ' . $data['table_prefix'] . "config SET config_value = '1' WHERE config_name = 'captcha_gd'"; diff --git a/phpBB/install/install_update.php b/phpBB/install/install_update.php index 9f5a428029..a5e54a354a 100644 --- a/phpBB/install/install_update.php +++ b/phpBB/install/install_update.php @@ -240,6 +240,7 @@ class install_update extends module // Make sure the update list is destroyed. $cache->destroy('_update_list'); $cache->destroy('_diff_files'); + $cache->destroy('_expected_files'); break; case 'version_check': @@ -312,7 +313,14 @@ class install_update extends module case 'file_check': - // Make sure the previous file collection is no longer valid... + // retrieve info on what changes should have already been made to the files. + $expected_files = $cache->get('_expected_files'); + if (!$expected_files) + { + $expected_files = array(); + } + + // Now make sure the previous file collection is no longer valid... $cache->destroy('_diff_files'); $this->page_title = 'STAGE_FILE_CHECK'; @@ -349,7 +357,7 @@ class install_update extends module if ($get_new_list) { - $this->get_update_structure($update_list); + $this->get_update_structure($update_list, $expected_files); $cache->put('_update_list', $update_list); // Refresh the page if we are still not finished... @@ -384,6 +392,8 @@ class install_update extends module ); } + $new_expected_files = array(); + // Now assign the list to the template foreach ($update_list as $status => $filelist) { @@ -419,29 +429,38 @@ class install_update extends module $diff_url = append_sid($this->p_master->module_url, "mode=$mode&sub=file_check&action=diff&status=$status&file=" . urlencode($file_struct['filename'])); - $template->assign_block_vars($status, array( - 'STATUS' => $status, + if (isset($file_struct['as_expected']) && $file_struct['as_expected']) + { + $new_expected_files[$file_struct['filename']] = $expected_files[$file_struct['filename']]; + } + else + { + $template->assign_block_vars($status, array( + 'STATUS' => $status, - 'FILENAME' => $filename, - 'DIR_PART' => $dir_part, - 'FILE_PART' => $file_part, - 'NUM_CONFLICTS' => (isset($file_struct['conflicts'])) ? $file_struct['conflicts'] : 0, + 'FILENAME' => $filename, + 'DIR_PART' => $dir_part, + 'FILE_PART' => $file_part, + 'NUM_CONFLICTS' => (isset($file_struct['conflicts'])) ? $file_struct['conflicts'] : 0, - 'S_CUSTOM' => ($file_struct['custom']) ? true : false, - 'S_BINARY' => $s_binary, - 'CUSTOM_ORIGINAL' => ($file_struct['custom']) ? $file_struct['original'] : '', + 'S_CUSTOM' => ($file_struct['custom']) ? true : false, + 'S_BINARY' => $s_binary, + 'CUSTOM_ORIGINAL' => ($file_struct['custom']) ? $file_struct['original'] : '', - 'U_SHOW_DIFF' => $diff_url, - 'L_SHOW_DIFF' => ($status != 'up_to_date') ? $user->lang['SHOW_DIFF_' . strtoupper($status)] : '', + 'U_SHOW_DIFF' => $diff_url, + 'L_SHOW_DIFF' => ($status != 'up_to_date') ? $user->lang['SHOW_DIFF_' . strtoupper($status)] : '', - 'U_VIEW_MOD_FILE' => $diff_url . '&op=' . MERGE_MOD_FILE, - 'U_VIEW_NEW_FILE' => $diff_url . '&op=' . MERGE_NEW_FILE, - 'U_VIEW_NO_MERGE_MOD' => $diff_url . '&op=' . MERGE_NO_MERGE_MOD, - 'U_VIEW_NO_MERGE_NEW' => $diff_url . '&op=' . MERGE_NO_MERGE_NEW, - )); + 'U_VIEW_MOD_FILE' => $diff_url . '&op=' . MERGE_MOD_FILE, + 'U_VIEW_NEW_FILE' => $diff_url . '&op=' . MERGE_NEW_FILE, + 'U_VIEW_NO_MERGE_MOD' => $diff_url . '&op=' . MERGE_NO_MERGE_MOD, + 'U_VIEW_NO_MERGE_NEW' => $diff_url . '&op=' . MERGE_NO_MERGE_NEW, + )); + } } } + $cache->put('_expected_files', $new_expected_files); + $all_up_to_date = true; foreach ($update_list as $status => $filelist) { @@ -617,6 +636,7 @@ class install_update extends module // Before we do anything, let us diff the files and store the raw file information "somewhere" $get_files = false; $file_list = $cache->get('_diff_files'); + $expected_files = $cache->get('_expected_files'); if ($file_list === false || $file_list['status'] != -1) { @@ -632,6 +652,11 @@ class install_update extends module ); } + if (!isset($expected_files) || $expected_files === false) + { + $expected_files = array(); + } + $processed = 0; foreach ($update_list as $status => $files) { @@ -645,6 +670,7 @@ class install_update extends module // Skip this file if the user selected to not update it if (in_array($file_struct['filename'], $no_update)) { + $expected_files[$file_struct['filename']] = false; continue; } @@ -676,6 +702,15 @@ class install_update extends module return; } + if (file_exists($phpbb_root_path . $file_struct['filename'])) + { + $contents = file_get_contents($phpbb_root_path . $file_struct['filename']); + if (isset($expected_files[$file_struct['filename']]) && md5($contents) == $expected_files[$file_struct['filename']]) + { + continue; + } + } + $original_filename = ($file_struct['custom']) ? $file_struct['original'] : $file_struct['filename']; switch ($status) @@ -702,6 +737,7 @@ class install_update extends module break; } + $expected_files[$file_struct['filename']] = md5($contents); $file_list[$file_struct['filename']] = '_file_' . md5($file_struct['filename']); $cache->put($file_list[$file_struct['filename']], base64_encode($contents)); @@ -747,6 +783,7 @@ class install_update extends module break; } + $expected_files[$file_struct['filename']] = md5($contents); $file_list[$file_struct['filename']] = '_file_' . md5($file_struct['filename']); $cache->put($file_list[$file_struct['filename']], base64_encode($contents)); @@ -757,6 +794,7 @@ class install_update extends module } } } + $cache->put('_expected_files', $expected_files); } $file_list['status'] = -1; @@ -1217,7 +1255,7 @@ class install_update extends module /** * Collect all file status infos we need for the update by diffing all files */ - function get_update_structure(&$update_list) + function get_update_structure(&$update_list, $expected_files) { global $phpbb_root_path, $phpEx, $user; @@ -1303,7 +1341,7 @@ class install_update extends module else { // not modified? - $this->make_update_diff($update_list, $file, $file); + $this->make_update_diff($update_list, $file, $file, $expected_files); } $num_bytes_processed += (file_exists($this->new_location . $file)) ? filesize($this->new_location . $file) : 100 * 1024; @@ -1344,17 +1382,34 @@ class install_update extends module /** * Compare files for storage in update_list */ - function make_update_diff(&$update_list, $original_file, $file, $custom = false) + function make_update_diff(&$update_list, $original_file, $file, $expected_files, $custom = false) { global $phpbb_root_path, $user; - $update_ary = array('filename' => $file, 'custom' => $custom); + $update_ary = array('filename' => $file, 'custom' => $custom, 'as_expected' => false); if ($custom) { $update_ary['original'] = $original_file; } + if (file_exists($phpbb_root_path . $file)) + { + $content = file_get_contents($phpbb_root_path . $file); + + if (isset($expected_files[$file]) && // the user already selected what to do with this file + ($expected_files[$file] === false || // the user wanted this file to stay the same, so just assume it's alright + $expected_files[$file] === md5($content))) + { + // the file contains what it was supposed to contain after the merge + $update_ary['as_expected'] = true; + $update_ary['was_ignored'] = ($expected_files[$file] === false); + $update_list['up_to_date'][] = $update_ary; + + return; + } + } + // we only want to know if the files are successfully merged and newlines could result in errors (duplicate addition of lines and such things) // Therefore we check for empty diffs with two methods, preserving newlines and not preserving them (which mostly works best, therefore the first option) @@ -1364,7 +1419,7 @@ class install_update extends module { $tmp = array( 'file1' => file_get_contents($this->new_location . $original_file), - 'file2' => file_get_contents($phpbb_root_path . $file), + 'file2' => $content, ); // We need to diff the contents here to make sure the file is really the one we expect @@ -1403,7 +1458,7 @@ class install_update extends module { $tmp = array( 'file1' => file_get_contents($this->old_location . $original_file), - 'file2' => file_get_contents($phpbb_root_path . $file), + 'file2' => $content, ); // We need to diff the contents here to make sure the file is really the one we expect @@ -1414,7 +1469,7 @@ class install_update extends module $tmp = array( 'file1' => file_get_contents($this->new_location . $original_file), - 'file2' => file_get_contents($phpbb_root_path . $file), + 'file2' => $content, ); $diff = new diff($tmp['file1'], $tmp['file2'], $preserve_cr); diff --git a/phpBB/install/schemas/firebird_schema.sql b/phpBB/install/schemas/firebird_schema.sql index f3defe9a54..ab622e8fde 100644 --- a/phpBB/install/schemas/firebird_schema.sql +++ b/phpBB/install/schemas/firebird_schema.sql @@ -1263,6 +1263,7 @@ CREATE TABLE phpbb_topics_track ( ALTER TABLE phpbb_topics_track ADD PRIMARY KEY (user_id, topic_id);; +CREATE INDEX phpbb_topics_track_topic_id ON phpbb_topics_track(topic_id);; CREATE INDEX phpbb_topics_track_forum_id ON phpbb_topics_track(forum_id);; # Table: 'phpbb_topics_posted' diff --git a/phpBB/install/schemas/mssql_schema.sql b/phpBB/install/schemas/mssql_schema.sql index ac7c0928cd..068373c9a1 100644 --- a/phpBB/install/schemas/mssql_schema.sql +++ b/phpBB/install/schemas/mssql_schema.sql @@ -4,9 +4,6 @@ */ -BEGIN TRANSACTION -GO - /* Table: 'phpbb_attachments' */ @@ -1509,6 +1506,9 @@ ALTER TABLE [phpbb_topics_track] WITH NOCHECK ADD ) ON [PRIMARY] GO +CREATE INDEX [topic_id] ON [phpbb_topics_track]([topic_id]) ON [PRIMARY] +GO + CREATE INDEX [forum_id] ON [phpbb_topics_track]([forum_id]) ON [PRIMARY] GO @@ -1733,8 +1733,3 @@ ALTER TABLE [phpbb_zebra] WITH NOCHECK ADD ) ON [PRIMARY] GO - - -COMMIT -GO - diff --git a/phpBB/install/schemas/mysql_40_schema.sql b/phpBB/install/schemas/mysql_40_schema.sql index 4fcead787b..813cf8613f 100644 --- a/phpBB/install/schemas/mysql_40_schema.sql +++ b/phpBB/install/schemas/mysql_40_schema.sql @@ -862,6 +862,7 @@ CREATE TABLE phpbb_topics_track ( forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL, mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL, PRIMARY KEY (user_id, topic_id), + KEY topic_id (topic_id), KEY forum_id (forum_id) ); diff --git a/phpBB/install/schemas/mysql_41_schema.sql b/phpBB/install/schemas/mysql_41_schema.sql index 34adf5674e..97369d2bf7 100644 --- a/phpBB/install/schemas/mysql_41_schema.sql +++ b/phpBB/install/schemas/mysql_41_schema.sql @@ -862,6 +862,7 @@ CREATE TABLE phpbb_topics_track ( forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL, mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL, PRIMARY KEY (user_id, topic_id), + KEY topic_id (topic_id), KEY forum_id (forum_id) ) CHARACTER SET `utf8` COLLATE `utf8_bin`; diff --git a/phpBB/install/schemas/oracle_schema.sql b/phpBB/install/schemas/oracle_schema.sql index 15692ea984..7be7cd0756 100644 --- a/phpBB/install/schemas/oracle_schema.sql +++ b/phpBB/install/schemas/oracle_schema.sql @@ -1667,6 +1667,8 @@ CREATE TABLE phpbb_topics_track ( ) / +CREATE INDEX phpbb_topics_track_topic_id ON phpbb_topics_track (topic_id) +/ CREATE INDEX phpbb_topics_track_forum_id ON phpbb_topics_track (forum_id) / diff --git a/phpBB/install/schemas/postgres_schema.sql b/phpBB/install/schemas/postgres_schema.sql index 46e77e64b4..9cdf35024b 100644 --- a/phpBB/install/schemas/postgres_schema.sql +++ b/phpBB/install/schemas/postgres_schema.sql @@ -1117,6 +1117,7 @@ CREATE TABLE phpbb_topics_track ( PRIMARY KEY (user_id, topic_id) ); +CREATE INDEX phpbb_topics_track_topic_id ON phpbb_topics_track (topic_id); CREATE INDEX phpbb_topics_track_forum_id ON phpbb_topics_track (forum_id); /* diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 1c75ee4886..38088b291d 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -99,13 +99,15 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('enable_confirm', ' INSERT INTO phpbb_config (config_name, config_value) VALUES ('enable_pm_icons', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('enable_post_confirm', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_enable', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_limit', '10'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_overall_forums', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_overall_forums_limit', '15'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_overall_topics', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_overall_topics_limit', '15'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_http_auth', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_limit_post', '15'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_limit_topic', '10'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_overall_forums', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_overall', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_forum', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_topic', '1'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_topics_new', '1'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_topics_active', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('feed_item_statistics', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('flood_interval', '15'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('force_server_vars', '0'); @@ -239,7 +241,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('topics_per_page', INSERT INTO phpbb_config (config_name, config_value) VALUES ('tpl_allow_php', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_icons_path', 'images/upload_icons'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_path', 'files'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.0.6'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.0.8-dev'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_expire_days', '90'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_gc', '14400'); diff --git a/phpBB/install/schemas/sqlite_schema.sql b/phpBB/install/schemas/sqlite_schema.sql index d754219cdf..34b4b05478 100644 --- a/phpBB/install/schemas/sqlite_schema.sql +++ b/phpBB/install/schemas/sqlite_schema.sql @@ -834,6 +834,7 @@ CREATE TABLE phpbb_topics_track ( PRIMARY KEY (user_id, topic_id) ); +CREATE INDEX phpbb_topics_track_topic_id ON phpbb_topics_track (topic_id); CREATE INDEX phpbb_topics_track_forum_id ON phpbb_topics_track (forum_id); # Table: 'phpbb_topics_posted' diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index 8797a1210a..ce57b07eae 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -73,7 +73,8 @@ $lang = array_merge($lang, array( 'ALLOW_PM_REPORT' => 'Allow users to report private messages', 'ALLOW_PM_REPORT_EXPLAIN' => 'If this setting is enabled, users have the option of reporting a private message they have received or sent to the board’s moderators. These private messages will then be visible in the Moderator Control Panel.', 'ALLOW_QUICK_REPLY' => 'Allow quick reply', - 'ALLOW_QUICK_REPLY_EXPLAIN' => 'This setting defines if quick reply is enabled or not. If this setting is enabled, forums need to have their quick reply option enabled too.', + 'ALLOW_QUICK_REPLY_EXPLAIN' => 'This switch allows for the quick reply to be disabled board-wide. When enabled, forum specific settings will be used to determine whether the quick reply is displayed in individual forums.', + 'ALLOW_QUICK_REPLY_BUTTON' => 'Submit and enable quick reply in all forums', 'ALLOW_SIG' => 'Allow signatures', 'ALLOW_SIG_BBCODE' => 'Allow BBCode in user signatures', 'ALLOW_SIG_FLASH' => 'Allow use of [FLASH] BBCode tag in user signatures', @@ -84,7 +85,7 @@ $lang = array_merge($lang, array( 'ALLOW_SMILIES' => 'Allow smilies', 'ALLOW_TOPIC_NOTIFY' => 'Allow subscribing to topics', 'BOARD_PM' => 'Private messaging', - 'BOARD_PM_EXPLAIN' => 'Enable or disable private messaging for all users.', + 'BOARD_PM_EXPLAIN' => 'Enable private messaging for all users.', )); // Avatar Settings @@ -149,7 +150,7 @@ $lang = array_merge($lang, array( 'ALLOW_POST_FLASH_EXPLAIN' => 'If disallowed the [FLASH] BBCode tag is disabled in posts. Otherwise the permission system controls which users can use the [FLASH] BBCode tag.', 'BUMP_INTERVAL' => 'Bump interval', - 'BUMP_INTERVAL_EXPLAIN' => 'Number of minutes, hours or days between the last post to a topic and the ability to bump this topic.', + 'BUMP_INTERVAL_EXPLAIN' => 'Number of minutes, hours or days between the last post to a topic and the ability to bump this topic. Setting the value to 0 disables this feature.', 'CHAR_LIMIT' => 'Maximum characters per post/message', 'CHAR_LIMIT_EXPLAIN' => 'The number of characters allowed within a post/private message. Set to 0 for unlimited characters.', 'DELETE_TIME' => 'Limit deleting time', @@ -246,32 +247,40 @@ $lang = array_merge($lang, array( // Feeds $lang = array_merge($lang, array( - 'ACP_FEED_MANAGEMENT' => 'General Syndication Feeds settings', - 'ACP_FEED_MANAGEMENT_EXPLAIN' => 'This Module makes available various ATOM Feeds, parsing any BBCode in posts to make them readable in external feeds.', + 'ACP_FEED_MANAGEMENT' => 'General syndication feeds settings', + 'ACP_FEED_MANAGEMENT_EXPLAIN' => 'This module makes available various ATOM feeds, parsing any BBCode in posts to make them readable in external feeds.', - 'ACP_FEED_ENABLE' => 'Enable Feeds', - 'ACP_FEED_ENABLE_EXPLAIN' => 'Turns on or off ATOM Feeds for the entire board.
Disabling this switches off all Feeds, no matter how the options below are set.', + 'ACP_FEED_GENERAL' => 'General feed settings', + 'ACP_FEED_POST_BASED' => 'Post-based feed settings', + 'ACP_FEED_TOPIC_BASED' => 'Topic-based feed settings', + 'ACP_FEED_SETTINGS_OTHER' => 'Other feeds and settings', + + 'ACP_FEED_ENABLE' => 'Enable feeds', + 'ACP_FEED_ENABLE_EXPLAIN' => 'Turns on or off ATOM feeds for the entire board.
Disabling this switches off all feeds, no matter how the options below are set.', 'ACP_FEED_LIMIT' => 'Number of items', 'ACP_FEED_LIMIT_EXPLAIN' => 'The maximum number of feed items to display.', - 'ACP_FEED_OVERALL_FORUMS' => 'Enable overall forums feed', - 'ACP_FEED_OVERALL_FORUMS_EXPLAIN' => 'This feed displays the latest posts from all forums topics.', - 'ACP_FEED_OVERALL_FORUMS_LIMIT' => 'Number of items per page to display in the forums feed', - - 'ACP_FEED_OVERALL_TOPIC' => 'Enable overall topics feed', - 'ACP_FEED_OVERALL_TOPIC_EXPLAIN' => 'Enables the “All Topics” feed', - 'ACP_FEED_OVERALL_TOPIC_LIMIT' => 'Number of items per page to display in the topics feed', - 'ACP_FEED_FORUM' => 'Enable Per-Forum Feeds', - 'ACP_FEED_FORUM_EXPLAIN' => 'Single forum new posts.', - 'ACP_FEED_TOPIC' => 'Enable Per-Topic Feeds', + 'ACP_FEED_OVERALL' => 'Enable board-wide feed', + 'ACP_FEED_OVERALL_EXPLAIN' => 'Board-wide new posts.', + 'ACP_FEED_FORUM' => 'Enable per-forum feeds', + 'ACP_FEED_FORUM_EXPLAIN' => 'Single forum and subforums new posts.', + 'ACP_FEED_TOPIC' => 'Enable per-topic feeds', 'ACP_FEED_TOPIC_EXPLAIN' => 'Single topics new posts.', - 'ACP_FEED_NEWS' => 'News Feeds', + + 'ACP_FEED_TOPICS_NEW' => 'Enable new topics feed', + 'ACP_FEED_TOPICS_NEW_EXPLAIN' => 'Enables the “New Topics” feed, which displays the last created topics including the first post.', + 'ACP_FEED_TOPICS_ACTIVE' => 'Enable active topics feed', + 'ACP_FEED_TOPICS_ACTIVE_EXPLAIN' => 'Enables the “Active Topics” feed, which displays the last active topics including the last post.', + 'ACP_FEED_NEWS' => 'News feed', 'ACP_FEED_NEWS_EXPLAIN' => 'Pull the first post from these forums. Select no forums to disable news feed.
Select multiple forums by holding CTRL and clicking.', - 'ACP_FEED_GENERAL' => 'General Feed Settings', + 'ACP_FEED_OVERALL_FORUMS' => 'Enable forums feed', + 'ACP_FEED_OVERALL_FORUMS_EXPLAIN' => 'Enables the “All forums” feed, which displays a list of forums.', + 'ACP_FEED_HTTP_AUTH' => 'Allow HTTP Authentication', + 'ACP_FEED_HTTP_AUTH_EXPLAIN' => 'Enables HTTP authentication, which allows users to receive content that is hidden to guest users by adding the auth=http parameter to the feed URL. Please note that some PHP setups require additional changes to the .htaccess file. Instructions can be found in that file.', 'ACP_FEED_ITEM_STATISTICS' => 'Item statistics', - 'ACP_FEED_ITEM_STATISTICS_EXPLAIN' => 'Display individual statistics underneath feed items
(Posted by, date and time, Replies, Views)', + 'ACP_FEED_ITEM_STATISTICS_EXPLAIN' => 'Display individual statistics underneath feed items
(e.g. posted by, date and time, replies, views)', 'ACP_FEED_EXCLUDE_ID' => 'Exclude these forums', 'ACP_FEED_EXCLUDE_ID_EXPLAIN' => 'Content from these will be not included in feeds. Select no forum to pull data from all forums.
Select/Deselect multiple forums by holding CTRL and clicking.', )); @@ -383,7 +392,7 @@ $lang = array_merge($lang, array( 'LDAP_NO_EMAIL' => 'The specified e-mail attribute does not exist.', 'LDAP_NO_IDENTITY' => 'Could not find a login identity for %s.', 'LDAP_PASSWORD' => 'LDAP password', - 'LDAP_PASSWORD_EXPLAIN' => 'Leave blank to use anonymous binding. Else fill in the password for the above user. Required for Active Directory Servers. WARNING: This password will be stored as plain text in the database visible to everybody who can access your database or who can view this configuration page.', + 'LDAP_PASSWORD_EXPLAIN' => 'Leave blank to use anonymous binding. Else fill in the password for the above user. Required for Active Directory Servers.
Warning: This password will be stored as plain text in the database visible to everybody who can access your database or who can view this configuration page.', 'LDAP_PORT' => 'LDAP server port', 'LDAP_PORT_EXPLAIN' => 'Optionally you can specify a port which should be used to connect to the LDAP server instead of the default port 389.', 'LDAP_SERVER' => 'LDAP server name', @@ -495,7 +504,7 @@ $lang = array_merge($lang, array( 'SMTP_DIGEST_MD5' => 'DIGEST-MD5', 'SMTP_LOGIN' => 'LOGIN', 'SMTP_PASSWORD' => 'SMTP password', - 'SMTP_PASSWORD_EXPLAIN' => 'Only enter a password if your SMTP server requires it.', + 'SMTP_PASSWORD_EXPLAIN' => 'Only enter a password if your SMTP server requires it.
Warning: This password will be stored as plain text in the database visible to everybody who can access your database or who can view this configuration page.', 'SMTP_PLAIN' => 'PLAIN', 'SMTP_POP_BEFORE_SMTP' => 'POP-BEFORE-SMTP', 'SMTP_PORT' => 'SMTP server port', @@ -518,6 +527,7 @@ $lang = array_merge($lang, array( 'JAB_PACKAGE_SIZE' => 'Jabber package size', 'JAB_PACKAGE_SIZE_EXPLAIN' => 'This is the number of messages sent in one package. If set to 0 the message is sent immediately and will not be queued for later sending.', 'JAB_PASSWORD' => 'Jabber password', + 'JAB_PASSWORD_EXPLAIN' => 'Warning: This password will be stored as plain text in the database visible to everybody who can access your database or who can view this configuration page.', 'JAB_PORT' => 'Jabber port', 'JAB_PORT_EXPLAIN' => 'Leave blank unless you know it is not port 5222.', 'JAB_SERVER' => 'Jabber server', diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index 94188f4b1b..bca19c7f12 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -267,7 +267,6 @@ $lang = array_merge($lang, array( 'PARSE_URLS' => 'Parse links', 'PERMISSIONS_TRANSFERRED' => 'Permissions transferred', 'PERMISSIONS_TRANSFERRED_EXPLAIN' => 'You currently have the permissions from %1$s. You are able to browse the board with this user’s permissions, but not access the administration control panel since admin permissions were not transferred. You can revert to your permission set at any time.', - 'PIXEL' => 'px', 'PROCEED_TO_ACP' => '%sProceed to the ACP%s', 'REMIND' => 'Remind', @@ -340,6 +339,8 @@ $lang = array_merge($lang, array( 'NUMBER_USERS' => 'Number of users', 'NUMBER_ORPHAN' => 'Orphan attachments', + 'PHP_VERSION_OLD' => 'The version of PHP on this server will no longer be supported by future versions of phpBB. %sDetails%s', + 'POSTS_PER_DAY' => 'Posts per day', 'PURGE_CACHE' => 'Purge the cache', diff --git a/phpBB/language/en/acp/forums.php b/phpBB/language/en/acp/forums.php index 371a184fb7..eab027f295 100644 --- a/phpBB/language/en/acp/forums.php +++ b/phpBB/language/en/acp/forums.php @@ -66,7 +66,7 @@ $lang = array_merge($lang, array( 'ENABLE_POST_REVIEW' => 'Enable post review', 'ENABLE_POST_REVIEW_EXPLAIN' => 'If set to yes users are able to review their post if new posts were made to the topic while users wrote theirs. This should be disabled for chat forums.', 'ENABLE_QUICK_REPLY' => 'Enable quick reply', - 'ENABLE_QUICK_REPLY_EXPLAIN' => 'If set to yes users get a quick reply box for this forum. If the global option for quick reply is disabled or the forum not postable to the quick reply box will not be displayed, even if set to yes here.', + 'ENABLE_QUICK_REPLY_EXPLAIN' => 'Enables the quick reply in this forum. This setting is not considered if the quick reply is disabled board wide. The quick reply will only be displayed for users who have permission to post in this forum.', 'ENABLE_RECENT' => 'Display active topics', 'ENABLE_RECENT_EXPLAIN' => 'If set to yes topics made to this forum will be shown in the active topics list.', 'ENABLE_TOPIC_ICONS' => 'Enable topic icons', @@ -99,7 +99,7 @@ $lang = array_merge($lang, array( 'FORUM_PASSWORD_EXPLAIN' => 'Defines a password for this forum, use the permission system in preference.', 'FORUM_PASSWORD_UNSET' => 'Remove forum password', 'FORUM_PASSWORD_UNSET_EXPLAIN' => 'Check here if you want to remove the forum password.', - 'FORUM_PASSWORD_OLD' => 'The forum password is using an old encryption and should be changed.', + 'FORUM_PASSWORD_OLD' => 'The forum password is using an old hashing method and should be changed.', 'FORUM_PASSWORD_MISMATCH' => 'The passwords you entered did not match.', 'FORUM_PRUNE_SETTINGS' => 'Forum prune settings', 'FORUM_RESYNCED' => 'Forum “%s” successfully resynced', diff --git a/phpBB/language/en/acp/posting.php b/phpBB/language/en/acp/posting.php index 531e07f7ef..443f4a3ea2 100644 --- a/phpBB/language/en/acp/posting.php +++ b/phpBB/language/en/acp/posting.php @@ -41,6 +41,9 @@ $lang = array_merge($lang, array( 'ACP_BBCODES_EXPLAIN' => 'BBCode is a special implementation of HTML offering greater control over what and how something is displayed. From this page you can add, remove and edit custom BBCodes.', 'ADD_BBCODE' => 'Add a new BBCode', + 'BBCODE_DANGER' => 'The BBCode you are trying to add seems to use a {TEXT} token inside a HTML attribute. This is a possible XSS security issue. Try using the more restrictive {SIMPLETEXT} or {INTTEXT} types instead. Only proceed if you understand the risks involved and you consider the use of {TEXT} absolutely unavoidable.', + 'BBCODE_DANGER_PROCEED' => 'Proceed', //'I understand the risk', + 'BBCODE_ADDED' => 'BBCode added successfully.', 'BBCODE_EDITED' => 'BBCode edited successfully.', 'BBCODE_NOT_EXIST' => 'The BBCode you selected does not exist.', @@ -73,8 +76,9 @@ $lang = array_merge($lang, array( 'TOO_MANY_BBCODES' => 'You cannot create any more BBCodes. Please remove one or more BBCodes then try again.', 'tokens' => array( - 'TEXT' => 'Any text, including foreign characters, numbers, etc… You should not use this token in HTML tags. Instead try to use IDENTIFIER or SIMPLETEXT.', + 'TEXT' => 'Any text, including foreign characters, numbers, etc… You should not use this token in HTML tags. Instead try to use IDENTIFIER, INTTEXT or SIMPLETEXT.', 'SIMPLETEXT' => 'Characters from the latin alphabet (A-Z), numbers, spaces, commas, dots, minus, plus, hyphen and underscore', + 'INTTEXT' => 'Unicode letter characters, numbers, spaces, commas, dots, minus, plus, hyphen, underscore and whitespaces.', 'IDENTIFIER' => 'Characters from the latin alphabet (A-Z), numbers, hyphen and underscore', 'NUMBER' => 'Any series of digits', 'EMAIL' => 'A valid e-mail address', diff --git a/phpBB/language/en/captcha_qa.php b/phpBB/language/en/captcha_qa.php index 1d443e995a..5cd822b3c4 100644 --- a/phpBB/language/en/captcha_qa.php +++ b/phpBB/language/en/captcha_qa.php @@ -58,6 +58,8 @@ $lang = array_merge($lang, array( 'QUESTION_TEXT_EXPLAIN' => 'The question that will be asked on registration.', 'QA_ERROR_MSG' => 'Please fill in all fields and enter at least one answer.', + 'QA_LAST_QUESTION' => 'You cannot delete all questions while the plugin is active.', + )); ?> \ No newline at end of file diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php index b97445f186..a3d47b5b59 100644 --- a/phpBB/language/en/common.php +++ b/phpBB/language/en/common.php @@ -124,7 +124,7 @@ $lang = array_merge($lang, array( 'COMMA_SEPARATOR' => ', ', // Used in pagination of ACP & prosilver, use localised comma if appropriate, eg: Ideographic or Arabic 'CONFIRM' => 'Confirm', 'CONFIRM_CODE' => 'Confirmation code', - 'CONFIRM_CODE_EXPLAIN' => 'Enter the code exactly as it appears. All letters are case insensitive, there is no zero.', + 'CONFIRM_CODE_EXPLAIN' => 'Enter the code exactly as it appears. All letters are case insensitive.', 'CONFIRM_CODE_WRONG' => 'The confirmation code you entered was incorrect.', 'CONFIRM_OPERATION' => 'Are you sure you wish to carry out this operation?', 'CONGRATULATIONS' => 'Congratulations to', @@ -417,6 +417,7 @@ $lang = array_merge($lang, array( 'PAGE_OF' => 'Page %1$d of %2$d', 'PASSWORD' => 'Password', + 'PIXEL' => 'px', 'PLAY_QUICKTIME_FILE' => 'Play Quicktime file', 'PM' => 'PM', 'POSTING_MESSAGE' => 'Posting message in %s', @@ -491,6 +492,8 @@ $lang = array_merge($lang, array( 'RETURN_TO' => 'Return to', 'FEED' => 'Feed', 'FEED_NEWS' => 'News', + 'FEED_TOPICS_ACTIVE' => 'Active Topics', + 'FEED_TOPICS_NEW' => 'New Topics', 'RULES_ATTACH_CAN' => 'You can post attachments in this forum', 'RULES_ATTACH_CANNOT' => 'You cannot post attachments in this forum', 'RULES_DELETE_CAN' => 'You can delete your posts in this forum', diff --git a/phpBB/language/en/email/admin_welcome_inactive.txt b/phpBB/language/en/email/admin_welcome_inactive.txt index e0970d3ff3..30b3aae852 100644 --- a/phpBB/language/en/email/admin_welcome_inactive.txt +++ b/phpBB/language/en/email/admin_welcome_inactive.txt @@ -10,7 +10,7 @@ Username: {USERNAME} Board URL: {U_BOARD} ---------------------------- -Your account is currently inactive and will need to be approved by an administrator before you can log in. Another email will be sent when this has occured. +Your account is currently inactive and will need to be approved by an administrator before you can log in. Another email will be sent when this has occurred. Your password has been securely stored in our database and cannot be retrieved. In the event that it is forgotten, you will be able to reset it using the email address associated with your account. diff --git a/phpBB/language/en/help_faq.php b/phpBB/language/en/help_faq.php index 596e0531df..c76c281df5 100644 --- a/phpBB/language/en/help_faq.php +++ b/phpBB/language/en/help_faq.php @@ -95,8 +95,8 @@ $help = array( 1 => 'Either the administrator has not installed your language or nobody has translated this board into your language. Try asking the board administrator if they can install the language pack you need. If the language pack does not exist, feel free to create a new translation. More information can be found at the phpBB website (see link at the bottom of board pages).' ), array( - 0 => 'How do I show an image below my username?', - 1 => 'There are two images which may appear below a username when viewing posts. One of them may be an image associated with your rank, generally in the form of stars, blocks or dots, indicating how many posts you have made or your status on the board. Another, usually a larger image, is known as an avatar and is generally unique or personal to each user. It is up to the board administrators to enable avatars and to choose the way in which avatars can be made available. If you are unable to use avatars, contact a board administrators and ask them for their reasons.' + 0 => 'How do I show an image along with my username?', + 1 => 'There are two images which may appear along with a username when viewing posts. One of them may be an image associated with your rank, generally in the form of stars, blocks or dots, indicating how many posts you have made or your status on the board. Another, usually a larger image, is known as an avatar and is generally unique or personal to each user. It is up to the board administrator to enable avatars and to choose the way in which avatars can be made available. If you are unable to use avatars, contact a board administrator and ask them for their reasons.' ), array( 0 => 'What is my rank and how do I change it?', @@ -116,7 +116,7 @@ $help = array( ), array( 0 => 'How do I edit or delete a post?', - 1 => 'Unless you are a board administrator or moderator, you can only edit or delete your own posts. You can edit a post by clicking the edit button for the relevant post, sometimes for only a limited time after the post was made. If someone has already replied to the post, you will find a small piece of text output below the post when you return to the topic which lists the number of times you edited it along with the date and time. This will only appear if someone has made a reply; it will not appear if a moderator or administrator edited the post, though they may leave a note as to why they’ve edited the post at their own digression. Please note that normal users cannot delete a post once someone has replied.' + 1 => 'Unless you are a board administrator or moderator, you can only edit or delete your own posts. You can edit a post by clicking the edit button for the relevant post, sometimes for only a limited time after the post was made. If someone has already replied to the post, you will find a small piece of text output below the post when you return to the topic which lists the number of times you edited it along with the date and time. This will only appear if someone has made a reply; it will not appear if a moderator or administrator edited the post, though they may leave a note as to why they’ve edited the post at their own discretion. Please note that normal users cannot delete a post once someone has replied.' ), array( 0 => 'How do I add a signature to my post?', diff --git a/phpBB/language/en/install.php b/phpBB/language/en/install.php index 9543c712da..1c27e2f40d 100644 --- a/phpBB/language/en/install.php +++ b/phpBB/language/en/install.php @@ -147,6 +147,7 @@ $lang = array_merge($lang, array( 'DLL_MBSTRING' => 'Multi-byte character support', 'DLL_MSSQL' => 'MSSQL Server 2000+', 'DLL_MSSQL_ODBC' => 'MSSQL Server 2000+ via ODBC', + 'DLL_MSSQLNATIVE' => 'MSSQL Server 2005+ [ Native ]', 'DLL_MYSQL' => 'MySQL', 'DLL_MYSQLI' => 'MySQL with MySQLi Extension', 'DLL_ORACLE' => 'Oracle', @@ -214,6 +215,7 @@ $lang = array_merge($lang, array(
  • SQLite 2.8.2+
  • Firebird 2.1+
  • MS SQL Server 2000 or above (directly or via ODBC)
  • +
  • MS SQL Server 2005 or above (native)
  • Oracle
  • diff --git a/phpBB/language/en/mcp.php b/phpBB/language/en/mcp.php index 2e988e2d42..fc1b8c8f62 100644 --- a/phpBB/language/en/mcp.php +++ b/phpBB/language/en/mcp.php @@ -329,6 +329,7 @@ $lang = array_merge($lang, array( 'SPLIT_TOPIC_BEYOND_CONFIRM' => 'Are you sure you want to split this topic at the selected post?', 'SPLIT_TOPIC_EXPLAIN' => 'Using the form below you can split a topic in two, either by selecting the posts individually or by splitting at a selected post.', + 'THIS_PM_IP' => 'IP for this private message', 'THIS_POST_IP' => 'IP for this post', 'TOPICS_APPROVED_SUCCESS' => 'The selected topics have been approved.', 'TOPICS_DELETED_SUCCESS' => 'The selected topics have been successfully removed from the database.', diff --git a/phpBB/memberlist.php b/phpBB/memberlist.php index 7cffbfd854..b46230b10a 100644 --- a/phpBB/memberlist.php +++ b/phpBB/memberlist.php @@ -430,22 +430,72 @@ switch ($mode) $user_id = (int) $member['user_id']; + // Get group memberships + // Also get visiting user's groups to determine hidden group memberships if necessary. + $auth_hidden_groups = ($user_id === (int) $user->data['user_id'] || $auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? true : false; + $sql_uid_ary = ($auth_hidden_groups) ? array($user_id) : array($user_id, (int) $user->data['user_id']); + // Do the SQL thang - $sql = 'SELECT g.group_id, g.group_name, g.group_type - FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . " ug - WHERE ug.user_id = $user_id - AND g.group_id = ug.group_id" . ((!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? ' AND g.group_type <> ' . GROUP_HIDDEN : '') . ' - AND ug.user_pending = 0 - ORDER BY g.group_type, g.group_name'; + $sql = 'SELECT g.group_id, g.group_name, g.group_type, ug.user_id + FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug + WHERE ' . $db->sql_in_set('ug.user_id', $sql_uid_ary) . ' + AND g.group_id = ug.group_id + AND ug.user_pending = 0'; $result = $db->sql_query($sql); - $group_options = ''; + // Divide data into profile data and current user data + $profile_groups = $user_groups = array(); while ($row = $db->sql_fetchrow($result)) { - $group_options .= ''; + $row['user_id'] = (int) $row['user_id']; + $row['group_id'] = (int) $row['group_id']; + + if ($row['user_id'] == $user_id) + { + $profile_groups[] = $row; + } + else + { + $user_groups[$row['group_id']] = $row['group_id']; + } } $db->sql_freeresult($result); + // Filter out hidden groups and sort groups by name + $group_data = $group_sort = array(); + foreach ($profile_groups as $row) + { + if ($row['group_type'] == GROUP_SPECIAL) + { + // Lookup group name in language dictionary + if (isset($user->lang['G_' . $row['group_name']])) + { + $row['group_name'] = $user->lang['G_' . $row['group_name']]; + } + } + else if (!$auth_hidden_groups && $row['group_type'] == GROUP_HIDDEN && !isset($user_groups[$row['group_id']])) + { + // Skip over hidden groups the user cannot see + continue; + } + + $group_sort[$row['group_id']] = utf8_clean_string($row['group_name']); + $group_data[$row['group_id']] = $row; + } + unset($profile_groups); + unset($user_groups); + asort($group_sort); + + $group_options = ''; + foreach ($group_sort as $group_id => $null) + { + $row = $group_data[$group_id]; + + $group_options .= ''; + } + unset($group_data); + unset($group_sort); + // What colour is the zebra $sql = 'SELECT friend, foe FROM ' . ZEBRA_TABLE . " diff --git a/phpBB/posting.php b/phpBB/posting.php index 660ca8ef23..5f27b61aae 100644 --- a/phpBB/posting.php +++ b/phpBB/posting.php @@ -992,7 +992,7 @@ if ($submit || $preview || $refresh) $forum_type = (int) $db->sql_fetchfield('forum_type'); $db->sql_freeresult($result); - if ($forum_type != FORUM_POST || !$auth->acl_get('f_post', $to_forum_id)) + if ($forum_type != FORUM_POST || !$auth->acl_get('f_post', $to_forum_id) || (!$auth->acl_get('m_approve', $to_forum_id) && !$auth->acl_get('f_noapprove', $to_forum_id))) { $to_forum_id = 0; } @@ -1113,7 +1113,7 @@ if ($submit || $preview || $refresh) } // Check the permissions for post approval. Moderators are not affected. - if ((!$auth->acl_get('f_noapprove', $data['forum_id']) && !$auth->acl_get('m_approve', $data['forum_id'])) || !empty($post_data['force_approved_state'])) + if ((!$auth->acl_get('f_noapprove', $data['forum_id']) && !$auth->acl_get('m_approve', $data['forum_id']) && empty($data['force_approved_state'])) || (isset($data['force_approved_state']) && !$data['force_approved_state'])) { meta_refresh(10, $redirect_url); $message = ($mode == 'edit') ? $user->lang['POST_EDITED_MOD'] : $user->lang['POST_STORED_MOD']; @@ -1152,7 +1152,7 @@ if (!sizeof($error) && $preview) $parse_sig->bbcode_bitfield = $preview_signature_bitfield; // Not sure about parameters for bbcode/smilies/urls... in signatures - $parse_sig->format_display($config['allow_sig_bbcode'], true, $config['allow_sig_smilies']); + $parse_sig->format_display($config['allow_sig_bbcode'], $config['allow_sig_links'], $config['allow_sig_smilies']); $preview_signature = $parse_sig->message; unset($parse_sig); } diff --git a/phpBB/search.php b/phpBB/search.php index c70f389b94..ab2221a96e 100644 --- a/phpBB/search.php +++ b/phpBB/search.php @@ -47,6 +47,26 @@ $sort_dir = request_var('sd', 'd'); $return_chars = request_var('ch', ($topic_id) ? -1 : 300); $search_forum = request_var('fid', array(0)); +// We put login boxes for the case if search_id is egosearch or unreadposts +// because a guest should be able to log in even if guests search is not permitted + +// Egosearch is an author search +if ($search_id == 'egosearch') +{ + $author_id = $user->data['user_id']; + + if ($user->data['user_id'] == ANONYMOUS) + { + login_box('', $user->lang['LOGIN_EXPLAIN_EGOSEARCH']); + } +} + +// Search for unread posts needs user to be logged in if topics tracking for guests is disabled +if ($search_id == 'unreadposts' && !$config['load_anon_lastread'] && !$user->data['is_registered']) +{ + login_box('', $user->lang['LOGIN_EXPLAIN_UNREADSEARCH']); +} + // Is user able to search? Has search been disabled? if (!$auth->acl_get('u_search') || !$auth->acl_getf_global('f_search') || !$config['load_search']) { @@ -84,24 +104,6 @@ if ($keywords || $author || $author_id || $search_id || $submit) // clear arrays $id_ary = array(); - // egosearch is an author search - if ($search_id == 'egosearch') - { - $author_id = $user->data['user_id']; - - if ($user->data['user_id'] == ANONYMOUS) - { - login_box('', $user->lang['LOGIN_EXPLAIN_EGOSEARCH']); - } - } - - // search for unread posts needs user to be logged in - // if topics tracking for guests is disabled - if ($search_id == 'unreadposts' && !$config['load_anon_lastread'] && !$user->data['is_registered']) - { - login_box('', $user->lang['LOGIN_EXPLAIN_UNREADSEARCH']); - } - // If we are looking for authors get their ids $author_id_ary = array(); $sql_author_match = ''; diff --git a/phpBB/styles/prosilver/imageset/imageset.cfg b/phpBB/styles/prosilver/imageset/imageset.cfg index c838dffd03..7fc8274ad2 100644 --- a/phpBB/styles/prosilver/imageset/imageset.cfg +++ b/phpBB/styles/prosilver/imageset/imageset.cfg @@ -2,8 +2,8 @@ # phpBB Imageset Configuration File # # @package phpBB3 -# @copyright (c) 2006 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2006 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,23 +11,23 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # -# +# # General Information about this style name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.6 +version = 3.0.7 # Images img_site_logo = site_logo.gif*52*139 -img_poll_left = -img_poll_center = -img_poll_right = -img_icon_friend = -img_icon_foe = +img_poll_left = +img_poll_center = +img_poll_right = +img_icon_friend = +img_icon_foe = img_forum_link = forum_link.gif*27*27 img_forum_read = forum_read.gif*27*27 @@ -111,6 +111,6 @@ img_icon_topic_newest = icon_topic_newest.gif*9*11 img_icon_topic_reported = icon_topic_reported.gif*14*16 img_icon_topic_unapproved = icon_topic_unapproved.gif*14*16 -img_icon_user_profile = +img_icon_user_profile = img_icon_user_warn = icon_user_warn.gif*20*20 diff --git a/phpBB/styles/prosilver/style.cfg b/phpBB/styles/prosilver/style.cfg index 219106e9b6..8b660c969e 100644 --- a/phpBB/styles/prosilver/style.cfg +++ b/phpBB/styles/prosilver/style.cfg @@ -2,8 +2,8 @@ # phpBB Style Configuration File # # @package phpBB3 -# @copyright (c) 2005 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2005 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,12 +11,12 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # -# +# # General Information about this style name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.6 \ No newline at end of file +version = 3.0.7 \ No newline at end of file diff --git a/phpBB/styles/prosilver/template/editor.js b/phpBB/styles/prosilver/template/editor.js index 1699f783d5..9c2691db8d 100644 --- a/phpBB/styles/prosilver/template/editor.js +++ b/phpBB/styles/prosilver/template/editor.js @@ -157,7 +157,7 @@ function insert_text(text, spaces, popup) var sel_start = textarea.selectionStart; var sel_end = textarea.selectionEnd; - mozWrap(textarea, text, '') + mozWrap(textarea, text, ''); textarea.selectionStart = sel_start + text.length; textarea.selectionEnd = sel_end + text.length; } @@ -259,7 +259,7 @@ function addquote(post_id, username) var lines = split_lines(theSelection); for (i = 0; i < lines.length; i++) { - insert_text('> ' + lines[i] + '\n') + insert_text('> ' + lines[i] + '\n'); } } } @@ -289,7 +289,7 @@ function split_lines(text) if (splitAt == -1) { splitLines[j] = line; - j++ + j++; } else { @@ -298,7 +298,7 @@ function split_lines(text) j++; } } - while(splitAt != -1) + while(splitAt != -1); } } return splitLines; @@ -319,12 +319,12 @@ function mozWrap(txtarea, open, close) } var s1 = (txtarea.value).substring(0,selStart); - var s2 = (txtarea.value).substring(selStart, selEnd) + var s2 = (txtarea.value).substring(selStart, selEnd); var s3 = (txtarea.value).substring(selEnd, selLength); txtarea.value = s1 + open + s2 + close + s3; - txtarea.selectionStart = selEnd + open.length + close.length; - txtarea.selectionEnd = txtarea.selectionStart; + txtarea.selectionStart = selStart + open.length; + txtarea.selectionEnd = selEnd + open.length; txtarea.focus(); txtarea.scrollTop = scrollTop; diff --git a/phpBB/styles/prosilver/template/forum_fn.js b/phpBB/styles/prosilver/template/forum_fn.js index 074c250b18..6fb3778952 100644 --- a/phpBB/styles/prosilver/template/forum_fn.js +++ b/phpBB/styles/prosilver/template/forum_fn.js @@ -398,9 +398,9 @@ function apply_onkeypress_event() // jQuery code in case jQuery is used if (jquery_present) { - $('form input').live('keypress', function (e) + jQuery('form input[type=text], form input[type=password]').live('keypress', function (e) { - var default_button = $(this).parents('form').find('input[type=submit].default-submit-action'); + var default_button = jQuery(this).parents('form').find('input[type=submit].default-submit-action'); if (!default_button || default_button.length <= 0) return true; @@ -424,11 +424,11 @@ function apply_onkeypress_event() for (var i = 0, element = input_tags[0]; i < input_tags.length ; element = input_tags[++i]) { - if (element.type == 'hidden') - continue; - - // onkeydown is possible too - element.onkeypress = function (evt) { submit_default_button((evt || window.event), this, 'default-submit-action'); }; + if (element.type == 'text' || element.type == 'password') + { + // onkeydown is possible too + element.onkeypress = function (evt) { submit_default_button((evt || window.event), this, 'default-submit-action'); }; + } } } diff --git a/phpBB/styles/prosilver/template/mcp_post.html b/phpBB/styles/prosilver/template/mcp_post.html index 8a3c8e7309..0265d7ce12 100644 --- a/phpBB/styles/prosilver/template/mcp_post.html +++ b/phpBB/styles/prosilver/template/mcp_post.html @@ -103,7 +103,7 @@
    -
    {L_THIS_POST_IP}: +
    {L_THIS_PM_IP}{L_THIS_POST_IP}: {POST_IPADDR}{POST_IP} ({POST_IP}{L_LOOKUP_IP}) {POST_IPADDR} ({POST_IP}){POST_IP} ({L_LOOKUP_IP}) diff --git a/phpBB/styles/prosilver/template/overall_header.html b/phpBB/styles/prosilver/template/overall_header.html index 7c8903b474..8f4ac19fb8 100644 --- a/phpBB/styles/prosilver/template/overall_header.html +++ b/phpBB/styles/prosilver/template/overall_header.html @@ -16,10 +16,11 @@ {SITENAME} • <!-- IF S_IN_MCP -->{L_MCP} • <!-- ELSEIF S_IN_UCP -->{L_UCP} • <!-- ENDIF -->{PAGE_TITLE} - + - + + diff --git a/phpBB/styles/prosilver/template/template.cfg b/phpBB/styles/prosilver/template/template.cfg index 22328dd50b..03b61e1003 100644 --- a/phpBB/styles/prosilver/template/template.cfg +++ b/phpBB/styles/prosilver/template/template.cfg @@ -2,8 +2,8 @@ # phpBB Template Configuration File # # @package phpBB3 -# @copyright (c) 2006 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2006 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,15 +11,15 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # -# +# # General Information about this template name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.6 +version = 3.0.7 # Defining a different template bitfield template_bitfield = lNg= diff --git a/phpBB/styles/prosilver/template/ucp_avatar_options.html b/phpBB/styles/prosilver/template/ucp_avatar_options.html index 57aa82ca7b..7012c42f3b 100644 --- a/phpBB/styles/prosilver/template/ucp_avatar_options.html +++ b/phpBB/styles/prosilver/template/ucp_avatar_options.html @@ -35,8 +35,8 @@

    {L_LINK_REMOTE_SIZE_EXPLAIN}
    - ×  - + ×  +
    diff --git a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html index 2f2778f496..45ee6d1fae 100644 --- a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html +++ b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html @@ -83,7 +83,7 @@ hr.sep { {L_TO}: - class="sep">{to_recipient.NAME}  + class="sep">{to_recipient.NAME}  diff --git a/phpBB/styles/prosilver/template/viewforum_body.html b/phpBB/styles/prosilver/template/viewforum_body.html index 3f8921c6d3..cc38ed9d2b 100644 --- a/phpBB/styles/prosilver/template/viewforum_body.html +++ b/phpBB/styles/prosilver/template/viewforum_body.html @@ -6,7 +6,7 @@
    {FORUM_DESC}
    - {L_MODERATOR}{L_MODERATORS}: {MODERATORS} +

    {L_MODERATOR}{L_MODERATORS}: {MODERATORS}

    diff --git a/phpBB/styles/prosilver/template/viewtopic_print.html b/phpBB/styles/prosilver/template/viewtopic_print.html index be1feb9208..03102dd351 100644 --- a/phpBB/styles/prosilver/template/viewtopic_print.html +++ b/phpBB/styles/prosilver/template/viewtopic_print.html @@ -36,7 +36,7 @@

    {postrow.POST_SUBJECT}

    {postrow.MINI_POST_IMG}{L_POSTED}: {postrow.POST_DATE}
    -
    {L_POST_BY_AUTHOR} style="color: {postrow.POST_AUTHOR_COLOUR}">{postrow.POST_AUTHOR}
    +
    {L_POST_BY_AUTHOR} {postrow.POST_AUTHOR}
    {postrow.MESSAGE}

    diff --git a/phpBB/styles/prosilver/theme/bidi.css b/phpBB/styles/prosilver/theme/bidi.css index be7cdab2b9..109312ac1b 100644 --- a/phpBB/styles/prosilver/theme/bidi.css +++ b/phpBB/styles/prosilver/theme/bidi.css @@ -225,6 +225,12 @@ text-align: left; } +/* Links adjustment to correctly display an order of rtl/ltr mixed content */ +.rtl a { + direction: rtl; + unicode-bidi: embed; +} + .rtl a.top { float: left; } @@ -371,7 +377,7 @@ /* Quote block */ .rtl blockquote { margin: 0.5em 25px 0 1px; - background-position: 99% 5px; + background-position: 99% 8px; } .rtl blockquote blockquote { diff --git a/phpBB/styles/prosilver/theme/colours.css b/phpBB/styles/prosilver/theme/colours.css index 2c0606001c..5d74ff9d8f 100644 --- a/phpBB/styles/prosilver/theme/colours.css +++ b/phpBB/styles/prosilver/theme/colours.css @@ -796,6 +796,7 @@ ul.cplist { } #navigation a:hover { + background-image: none; background-color: #aabac6; color: #BC2A4D; } diff --git a/phpBB/styles/prosilver/theme/forms.css b/phpBB/styles/prosilver/theme/forms.css index a280218e75..4db342661c 100644 --- a/phpBB/styles/prosilver/theme/forms.css +++ b/phpBB/styles/prosilver/theme/forms.css @@ -316,7 +316,7 @@ a.button1, input.button1, input.button3, a.button2, input.button2 { padding-bottom: 1px; font-family: "Lucida Grande", Verdana, Helvetica, Arial, sans-serif; color: #000; - background: #FAFAFA none repeat-x top; + background: #FAFAFA none repeat-x top left; } a.button1, input.button1 { diff --git a/phpBB/styles/prosilver/theme/links.css b/phpBB/styles/prosilver/theme/links.css index 4284157326..ea9ca8f4b1 100644 --- a/phpBB/styles/prosilver/theme/links.css +++ b/phpBB/styles/prosilver/theme/links.css @@ -1,6 +1,12 @@ /* proSilver Link Styles ---------------------------------------- */ +/* Links adjustment to correctly display an order of rtl/ltr mixed content */ +a { + direction: ltr; + unicode-bidi: embed; +} + a:link { color: #898989; text-decoration: none; } a:visited { color: #898989; text-decoration: none; } a:hover { color: #d3d3d3; text-decoration: underline; } diff --git a/phpBB/styles/prosilver/theme/theme.cfg b/phpBB/styles/prosilver/theme/theme.cfg index 91a35bbcc8..8b7916a144 100644 --- a/phpBB/styles/prosilver/theme/theme.cfg +++ b/phpBB/styles/prosilver/theme/theme.cfg @@ -2,8 +2,8 @@ # phpBB Theme Configuration File # # @package phpBB3 -# @copyright (c) 2006 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2006 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,22 +11,22 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # # Available and used values: # parse_css_file -# +# # General Information about this theme name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.6 +version = 3.0.7 # Some configuration options # -# You have to turn this option on if you want to use the +# You have to turn this option on if you want to use the # path template variables ({T_IMAGESET_PATH} for example) within # your css file. # This is mostly the case if you want to use language specific diff --git a/phpBB/styles/subsilver2/imageset/imageset.cfg b/phpBB/styles/subsilver2/imageset/imageset.cfg index 97fd0d8949..7709070734 100644 --- a/phpBB/styles/subsilver2/imageset/imageset.cfg +++ b/phpBB/styles/subsilver2/imageset/imageset.cfg @@ -2,8 +2,8 @@ # phpBB Imageset Configuration File # # @package phpBB3 -# @copyright (c) 2005 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2005 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,7 +11,7 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # # @@ -19,9 +19,9 @@ # General Information about this style name = subsilver2 copyright = © phpBB Group, 2003 -version = 3.0.6 +version = 3.0.7 -# Images +# Images img_site_logo = site_logo.gif*94*170 img_upload_bar = upload_bar.gif*16*280 img_poll_left = poll_left.gif*12*4 @@ -81,8 +81,8 @@ img_global_unread_mine = announce_unread_mine.gif*18*19 img_global_unread_locked = announce_unread_locked.gif*18*19 img_global_unread_locked_mine = announce_unread_locked_mine.gif*18*19 -img_subforum_read = -img_subforum_unread = +img_subforum_read = +img_subforum_unread = img_pm_read = topic_read.gif*18*19 img_pm_unread = topic_unread.gif*18*19 diff --git a/phpBB/styles/subsilver2/style.cfg b/phpBB/styles/subsilver2/style.cfg index 5eaaac782d..86b5b8a4e8 100644 --- a/phpBB/styles/subsilver2/style.cfg +++ b/phpBB/styles/subsilver2/style.cfg @@ -2,8 +2,8 @@ # phpBB Style Configuration File # # @package phpBB3 -# @copyright (c) 2005 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2005 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,12 +11,12 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # -# +# # General Information about this style name = subsilver2 copyright = © 2005 phpBB Group -version = 3.0.6 +version = 3.0.7 diff --git a/phpBB/styles/subsilver2/template/editor.js b/phpBB/styles/subsilver2/template/editor.js index b4a426df4e..2d157caada 100644 --- a/phpBB/styles/subsilver2/template/editor.js +++ b/phpBB/styles/subsilver2/template/editor.js @@ -156,7 +156,7 @@ function insert_text(text, spaces, popup) var sel_start = textarea.selectionStart; var sel_end = textarea.selectionEnd; - mozWrap(textarea, text, '') + mozWrap(textarea, text, ''); textarea.selectionStart = sel_start + text.length; textarea.selectionEnd = sel_end + text.length; } @@ -272,12 +272,12 @@ function mozWrap(txtarea, open, close) } var s1 = (txtarea.value).substring(0,selStart); - var s2 = (txtarea.value).substring(selStart, selEnd) + var s2 = (txtarea.value).substring(selStart, selEnd); var s3 = (txtarea.value).substring(selEnd, selLength); txtarea.value = s1 + open + s2 + close + s3; - txtarea.selectionStart = selEnd + open.length + close.length; - txtarea.selectionEnd = txtarea.selectionStart; + txtarea.selectionStart = selStart + open.length; + txtarea.selectionEnd = selEnd + open.length; txtarea.focus(); txtarea.scrollTop = scrollTop; diff --git a/phpBB/styles/subsilver2/template/mcp_logs.html b/phpBB/styles/subsilver2/template/mcp_logs.html index 6b78df3e5e..8b69197b0f 100644 --- a/phpBB/styles/subsilver2/template/mcp_logs.html +++ b/phpBB/styles/subsilver2/template/mcp_logs.html @@ -22,7 +22,7 @@ - {L_SEARCH_KEYWORDS}:   + {L_SEARCH_KEYWORDS}:   {L_DISPLAY_LOG}: {S_SELECT_SORT_DAYS} {L_SORT_BY} {S_SELECT_SORT_KEY} {S_SELECT_SORT_DIR}  diff --git a/phpBB/styles/subsilver2/template/mcp_notes_user.html b/phpBB/styles/subsilver2/template/mcp_notes_user.html index 522c19fa05..4ee3f9e04e 100644 --- a/phpBB/styles/subsilver2/template/mcp_notes_user.html +++ b/phpBB/styles/subsilver2/template/mcp_notes_user.html @@ -55,7 +55,7 @@ - {L_SEARCH_KEYWORDS}:   + {L_SEARCH_KEYWORDS}:   {L_DISPLAY_LOG}: {S_SELECT_SORT_DAYS} {L_SORT_BY}: {S_SELECT_SORT_KEY} {S_SELECT_SORT_DIR}  diff --git a/phpBB/styles/subsilver2/template/mcp_post.html b/phpBB/styles/subsilver2/template/mcp_post.html index ecf4558b45..6fb68ca680 100644 --- a/phpBB/styles/subsilver2/template/mcp_post.html +++ b/phpBB/styles/subsilver2/template/mcp_post.html @@ -58,7 +58,7 @@ - {L_THIS_POST_IP}: + {L_THIS_PM_IP}{L_THIS_POST_IP}: {POST_IPADDR}{POST_IP} ({POST_IP}{L_LOOKUP_IP}) diff --git a/phpBB/styles/subsilver2/template/overall_header.html b/phpBB/styles/subsilver2/template/overall_header.html index 5972073011..963f5160dd 100644 --- a/phpBB/styles/subsilver2/template/overall_header.html +++ b/phpBB/styles/subsilver2/template/overall_header.html @@ -16,10 +16,11 @@ {SITENAME} • <!-- IF S_IN_MCP -->{L_MCP} • <!-- ELSEIF S_IN_UCP -->{L_UCP} • <!-- ENDIF -->{PAGE_TITLE} - + - + + diff --git a/phpBB/styles/subsilver2/template/template.cfg b/phpBB/styles/subsilver2/template/template.cfg index 106eae28e6..b6765268bc 100644 --- a/phpBB/styles/subsilver2/template/template.cfg +++ b/phpBB/styles/subsilver2/template/template.cfg @@ -2,8 +2,8 @@ # phpBB Template Configuration File # # @package phpBB3 -# @copyright (c) 2005 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2005 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,13 +11,13 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # -# +# # General Information about this template name = subsilver2 copyright = © phpBB Group, 2003 -version = 3.0.6 +version = 3.0.7 diff --git a/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html b/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html index 7963d76765..f1c59df1c8 100644 --- a/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html +++ b/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html @@ -85,7 +85,7 @@ hr.sep { {L_TO}: - class="sep">{to_recipient.NAME}  + class="sep">{to_recipient.NAME}  diff --git a/phpBB/styles/subsilver2/template/ucp_profile_avatar.html b/phpBB/styles/subsilver2/template/ucp_profile_avatar.html index c981b8df75..4188d0e5f2 100644 --- a/phpBB/styles/subsilver2/template/ucp_profile_avatar.html +++ b/phpBB/styles/subsilver2/template/ucp_profile_avatar.html @@ -40,7 +40,7 @@ {L_LINK_REMOTE_SIZE}:
    {L_LINK_REMOTE_SIZE_EXPLAIN} - px X px + {L_PIXEL} × {L_PIXEL} diff --git a/phpBB/styles/subsilver2/template/viewtopic_print.html b/phpBB/styles/subsilver2/template/viewtopic_print.html index e1521c00a7..cfd78b6fc7 100644 --- a/phpBB/styles/subsilver2/template/viewtopic_print.html +++ b/phpBB/styles/subsilver2/template/viewtopic_print.html @@ -91,7 +91,7 @@ hr.sep { - + diff --git a/phpBB/styles/subsilver2/theme/stylesheet.css b/phpBB/styles/subsilver2/theme/stylesheet.css index ab19231595..726efdca0a 100644 --- a/phpBB/styles/subsilver2/theme/stylesheet.css +++ b/phpBB/styles/subsilver2/theme/stylesheet.css @@ -379,6 +379,13 @@ hr { /* Links ------------ */ + +/* Links adjustment to correctly display an order of rtl/ltr mixed content */ +.rtl a { + direction: rtl; + unicode-bidi: embed; +} + a:link { color: #006597; text-decoration: none; diff --git a/phpBB/styles/subsilver2/theme/theme.cfg b/phpBB/styles/subsilver2/theme/theme.cfg index 57b7fa8c63..7f6bfd9a12 100644 --- a/phpBB/styles/subsilver2/theme/theme.cfg +++ b/phpBB/styles/subsilver2/theme/theme.cfg @@ -2,8 +2,8 @@ # phpBB Theme Configuration File # # @package phpBB3 -# @copyright (c) 2005 phpBB Group -# @license http://opensource.org/licenses/gpl-license.php GNU Public License +# @copyright (c) 2005 phpBB Group +# @license http://opensource.org/licenses/gpl-license.php GNU Public License # # # At the left is the name, please do not change this @@ -11,22 +11,22 @@ # For on/off options the valid values are on, off, 1, 0, true and false # # Values get trimmed, if you want to add a space in front or at the end of -# the value, then enclose the value with single or double quotes. +# the value, then enclose the value with single or double quotes. # Single and double quotes do not need to be escaped. # # Available and used values: # parse_css_file -# +# # General Information about this theme name = subsilver2 copyright = © phpBB Group, 2003 -version = 3.0.6 +version = 3.0.7 # Some configuration options # -# You have to turn this option on if you want to use the +# You have to turn this option on if you want to use the # path template variables ({T_IMAGESET_PATH} for example) within # your css file. # This is mostly the case if you want to use language specific diff --git a/phpBB/viewtopic.php b/phpBB/viewtopic.php index a114940449..29c4e5cdc1 100644 --- a/phpBB/viewtopic.php +++ b/phpBB/viewtopic.php @@ -186,6 +186,13 @@ $sql_array = array( 'FROM' => array(FORUMS_TABLE => 'f'), ); +// Firebird handles two columns of the same name a little differently, this +// addresses that by forcing the forum_id to come from the forums table. +if ($db->sql_layer === 'firebird') +{ + $sql_array['SELECT'] = 'f.forum_id AS forum_id, ' . $sql_array['SELECT']; +} + // The FROM-Order is quite important here, else t.* columns can not be correctly bound. if ($post_id) { @@ -1678,8 +1685,12 @@ else if (!$all_marked_read) } // let's set up quick_reply -$s_allowed_reply = ((!$auth->acl_get('f_reply', $forum_id) || ($topic_data['forum_status'] == ITEM_LOCKED) || ($topic_data['topic_status'] == ITEM_LOCKED)) && !$auth->acl_get('m_edit', $forum_id)) ? false : true; -$s_quick_reply = $s_allowed_reply && $user->data['is_registered'] && $config['allow_quick_reply'] && ($topic_data['forum_flags'] & FORUM_FLAG_QUICK_REPLY); +$s_quick_reply = false; +if ($user->data['is_registered'] && $config['allow_quick_reply'] && ($topic_data['forum_flags'] & FORUM_FLAG_QUICK_REPLY) && $auth->acl_get('f_reply', $forum_id)) +{ + // Quick reply enabled forum + $s_quick_reply = (($topic_data['forum_status'] == ITEM_UNLOCKED && $topic_data['topic_status'] == ITEM_UNLOCKED) || $auth->acl_get('m_edit', $forum_id)) ? true : false; +} if ($s_can_vote || $s_quick_reply) { @@ -1690,7 +1701,7 @@ if ($s_can_vote || $s_quick_reply) $s_attach_sig = $config['allow_sig'] && $user->optionget('attachsig') && $auth->acl_get('f_sigs', $forum_id) && $auth->acl_get('u_sig'); $s_smilies = $config['allow_smilies'] && $user->optionget('smilies') && $auth->acl_get('f_smilies', $forum_id); $s_bbcode = $config['allow_bbcode'] && $user->optionget('bbcode') && $auth->acl_get('f_bbcode', $forum_id); - $s_notify = $config['allow_topic_notify'] && $user->data['user_notify']; + $s_notify = $config['allow_topic_notify'] && ($user->data['user_notify'] || $s_watching_topic['is_watching']); $qr_hidden_fields = array( 'topic_cur_post_id' => (int) $topic_data['topic_last_post_id'],
    {L_AUTHOR}:  style="color: {postrow.POST_AUTHOR_COLOUR}">{postrow.POST_AUTHOR} [ {postrow.POST_DATE} ]{postrow.POST_AUTHOR} [ {postrow.POST_DATE} ]
    {L_POST_SUBJECT}: