diff --git a/build/build_diff.php b/build/build_diff.php new file mode 100755 index 0000000000..ab3bb774b7 --- /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"; +} + +?> \ No newline at end of file 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/diff_class.php b/build/diff_class.php new file mode 100644 index 0000000000..0d7c2dcd3a --- /dev/null +++ b/build/diff_class.php @@ -0,0 +1,1677 @@ + +* @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 === '') + { + 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 === '') + { + continue; + } + + 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]); + } + } + + 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) + { + } +} + +?> \ No newline at end of file diff --git a/build/package.php b/build/package.php new file mode 100755 index 0000000000..50a9e76ab7 --- /dev/null +++ b/build/package.php @@ -0,0 +1,521 @@ +#!/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'); +} + +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_ban.html b/phpBB/adm/style/acp_ban.html index bc326345b0..539e8032dd 100644 --- a/phpBB/adm/style/acp_ban.html +++ b/phpBB/adm/style/acp_ban.html @@ -93,15 +93,15 @@
-
+
-
+
-
+

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/acp_users_overview.html b/phpBB/adm/style/acp_users_overview.html index e3eff369cc..d48cfa57be 100644 --- a/phpBB/adm/style/acp_users_overview.html +++ b/phpBB/adm/style/acp_users_overview.html @@ -124,13 +124,6 @@
- -
-

{L_DELETE_USER_EXPLAIN}
-
-
-
-

@@ -141,4 +134,20 @@ + +
+

+ {L_DELETE_USER} +
+

{L_DELETE_USER_EXPLAIN}
+
+
+

+ + + {S_FORM_TOKEN} +

+
+ + 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/captcha_recaptcha.html b/phpBB/adm/style/captcha_recaptcha.html index e4fa5dd426..702a4a1099 100644 --- a/phpBB/adm/style/captcha_recaptcha.html +++ b/phpBB/adm/style/captcha_recaptcha.html @@ -4,7 +4,7 @@ diff --git a/phpBB/common.php b/phpBB/common.php index c4a3160102..9b6913e95f 100644 --- a/phpBB/common.php +++ b/phpBB/common.php @@ -19,8 +19,12 @@ if (!defined('IN_PHPBB')) $starttime = explode(' ', microtime()); $starttime = $starttime[1] + $starttime[0]; -// Report all errors, except notices -error_reporting(E_ALL ^ E_NOTICE); +// Report all errors, except notices and deprecation messages +if (!defined('E_DEPRECATED')) +{ + define('E_DEPRECATED', 8192); +} +error_reporting(E_ALL ^ E_NOTICE ^ E_DEPRECATED); /* * Remove variables created by register_globals from the global scope @@ -172,7 +176,8 @@ if (defined('DEBUG_EXTRA')) } // Load Extensions -if (!empty($load_extensions)) +// dl() is deprecated and disabled by default as of PHP 5.3. +if (!empty($load_extensions) && function_exists('dl')) { $load_extensions = explode(',', $load_extensions); diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php index b9017937c8..03932bcc13 100644 --- a/phpBB/develop/create_schema_files.php +++ b/phpBB/develop/create_schema_files.php @@ -1198,7 +1198,6 @@ function get_schema_struct() 'PRIMARY_KEY' => 'log_id', 'KEYS' => array( 'log_type' => array('INDEX', 'log_type'), - 'log_time' => array('INDEX', 'log_time'), 'forum_id' => array('INDEX', 'forum_id'), 'topic_id' => array('INDEX', 'topic_id'), 'reportee_id' => array('INDEX', 'reportee_id'), @@ -1747,6 +1746,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..57230339e8 100644 --- a/phpBB/develop/mysql_upgrader.php +++ b/phpBB/develop/mysql_upgrader.php @@ -688,7 +688,6 @@ function get_schema_struct() 'PRIMARY_KEY' => 'log_id', 'KEYS' => array( 'log_type' => array('INDEX', 'log_type'), - 'log_time' => array('INDEX', 'log_time'), 'forum_id' => array('INDEX', 'forum_id'), 'topic_id' => array('INDEX', 'topic_id'), 'reportee_id' => array('INDEX', 'reportee_id'), @@ -1237,6 +1236,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 becfa021c6..9f6b886932 100644 --- a/phpBB/docs/CHANGELOG.html +++ b/phpBB/docs/CHANGELOG.html @@ -53,6 +53,7 @@
  1. Changelog
      +
    1. Changes since 3.0.6
    2. Changes since 3.0.5
    3. Changes since 3.0.4
    4. Changes since 3.0.3
    5. @@ -85,7 +86,95 @@
      -

      1.i. Changes since 3.0.5

      +

      1.i. Changes since 3.0.6

      + +
        +
      • [Fix] Allow ban reason and length to be selected and copied in ACP and subsilver2 MCP. (Bug #51095)
      • +
      • [Fix] Force full date for board online record date.
      • +
      • [Fix] Correctly reset login keys if passed value is the current user. (Bug #54125)
      • +
      • [Fix] Correctly set last modified headers. (Bug #54245, thanks Paul.J.Murphy)
      • +
      • [Fix] Show correct HTML title when reporting private messages. (Bug #54375)
      • +
      • [Fix] Correctly exclude subforums from ATOM Feeds. (Bug #54285)
      • +
      • [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 and CSS mistakes in prosilver. (Bugs #54705, #55895)
      • +
      • [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)
      • +
      • [Fix] PHP 5.3 compatibility: Disable E_DEPRECATED on startup to keep set_magic_quotes_runtime(0) quiet. (Bug #54495)
      • +
      • [Fix] Correctly replace table prefix before inserting schema data into the database. (Bug #54815)
      • +
      • [Fix] Correctly take post time instead of topic time for the overall forum feed statistics row. (Bug #55005)
      • +
      • [Fix] Posting errors with CAPTCHAs using user::add_lang(). (Bug #55245)
      • +
      • [Fix] Use memcache::replace() instead of memcache::set() for existing keys to prevent problems.
      • +
      • [Fix] Check for required functions in eAccelerator. (Bug #54465)
      • +
      • [Fix] Use correct RFC 3339 date format in ATOM feed. (Bug #55005)
      • +
      • [Fix] Do not deliver topics from unreadable or passworded forums in the news feed. (Bug #54345)
      • +
      • [Fix] Restore user language choice to compiled stylesheets. (Bug #54035)
      • +
      • [Fix] Add missing language entries. (Bug #55095)
      • +
      • [Fix] Do not permit unauthorised users to delete private messages from folder listing. (Bug #54355)
      • +
      • [Fix] Correctly check for empty strings in custom profile fields. (Bug #55335)
      • +
      • [Fix] Use correct options to parse BBCodes in signatures when previewing PMs.
      • +
      • [Fix] Correct rendering of prosilver quick reply under IE6. (Bug #54115 - Patch by Raimon)
      • +
      • [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 (fix by nrohler). (Bug #56335)
      • +
      • [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)
      • +
      • [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] 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)
      • +
      • [Feature] Ability to use HTTP authentication in ATOM feeds by passing the GET parameter "auth=http".
      • +
      + +

      1.ii. Changes since 3.0.5

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

      1.ii. Changes since 3.0.4

      +

      1.iii. Changes since 3.0.4

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

      1.iii. Changes since 3.0.3

      +

      1.iv. Changes since 3.0.3

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

      1.iv. Changes since 3.0.2

      +

      1.v. Changes since 3.0.2

      • [Fix] Correctly set topic starter if first post in topic removed (Bug #30575 - Patch by blueray2048)
      • @@ -527,7 +616,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.v. Changes since 3.0.1

      +

      1.vi. Changes since 3.0.1

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

      1.vi Changes since 3.0.0

      +

      1.vii Changes since 3.0.0

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

      1.vii. Changes since 3.0.RC8

      +

      1.viii. 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)
      • @@ -655,7 +744,7 @@
      • [Fix] Call garbage_collection() within database updater to correctly close connections (affects Oracle for example)
      -

      1.viii. Changes since 3.0.RC7

      +

      1.ix. Changes since 3.0.RC7

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

      1.ix. Changes since 3.0.RC6

      +

      1.x. Changes since 3.0.RC6

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

      1.x. Changes since 3.0.RC5

      +

      1.xi. 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.
      • @@ -763,7 +852,7 @@
      • [Sec] New password hashing mechanism for storing passwords (#i42)
      -

      1.xi. Changes since 3.0.RC4

      +

      1.xii. Changes since 3.0.RC4

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

      1.xii. Changes since 3.0.RC3

      +

      1.xiii. Changes since 3.0.RC3

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

      1.xiii. Changes since 3.0.RC2

      +

      1.xiv. Changes since 3.0.RC2

      • [Fix] Re-allow searching within the memberlist
      • @@ -969,7 +1058,7 @@
      -

      1.xiv. Changes since 3.0.RC1

      +

      1.xv. 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..f9d4e28c27 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.5 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/download/file.php b/phpBB/download/file.php index c6106fc09f..00b8e2e656 100644 --- a/phpBB/download/file.php +++ b/phpBB/download/file.php @@ -668,7 +668,7 @@ function set_modified_headers($stamp, $browser) $last_load = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime(trim($_SERVER['HTTP_IF_MODIFIED_SINCE'])) : false; if ((strpos(strtolower($browser), 'msie 6.0') === false) && (strpos(strtolower($browser), 'msie 8.0') === false)) { - if ($last_load !== false && $last_load <= $stamp) + if ($last_load !== false && $last_load >= $stamp) { if (substr(strtolower(@php_sapi_name()),0,3) === 'cgi') { diff --git a/phpBB/feed.php b/phpBB/feed.php index b9d6e1959a..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(); @@ -35,14 +44,12 @@ $forum_id = request_var('f', 0); $topic_id = request_var('t', 0); $mode = request_var('mode', ''); -// Feed date format for PHP > 5 and PHP4 -$feed_date_format = (PHP_VERSION >= 5) ? 'c' : "Y-m-d\TH:i:sO"; -$params = false; - // We do not use a template, therefore we simply define the global template variables here $global_vars = $item_vars = array(); +$feed_updated_time = 0; // Generate params array for use in append_sid() to correctly link back to this page +$params = false; if ($forum_id || $topic_id || $mode) { $params = array( @@ -67,19 +74,6 @@ if ($feed === false) // Open Feed $feed->open(); -// Some default assignments -// FEED_IMAGE is not used (atom) -$global_vars = array( - 'FEED_IMAGE' => ($user->img('site_logo', '', false, '', 'src')) ? $board_url . '/' . substr($user->img('site_logo', '', false, '', 'src'), strlen($phpbb_root_path)) : '', - 'SELF_LINK' => feed_append_sid('/feed.' . $phpEx, $params), - 'FEED_LINK' => $board_url . '/index.' . $phpEx, - 'FEED_TITLE' => $config['sitename'], - 'FEED_SUBTITLE' => $config['site_desc'], - 'FEED_UPDATED' => $user->format_date(time(), $feed_date_format, true), - 'FEED_LANG' => $user->lang['USER_LANG'], - 'FEED_AUTHOR' => $config['sitename'], -); - // Iterate through items while ($row = $feed->get_item()) { @@ -99,16 +93,17 @@ while ($row = $feed->get_item()) $options = $row[$feed->get('options')]; } - $title = ($row[$feed->get('title')]) ? $row[$feed->get('title')] : ((isset($row[$feed->get('title2')])) ? $row[$feed->get('title2')] : ''); - $title = censor_text($title); + $title = (isset($row[$feed->get('title')]) && $row[$feed->get('title')] !== '') ? $row[$feed->get('title')] : ((isset($row[$feed->get('title2')])) ? $row[$feed->get('title2')] : ''); + + $item_time = (int) $row[$feed->get('date')]; $item_row = array( 'author' => ($feed->get('creator') !== NULL) ? $row[$feed->get('creator')] : '', - 'pubdate' => $user->format_date($row[$feed->get('date')], $feed_date_format, true), + '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']) ? utf8_htmlspecialchars($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' => '', ); @@ -117,8 +112,29 @@ while ($row = $feed->get_item()) $feed->adjust_item($item_row, $row); $item_vars[] = $item_row; + + $feed_updated_time = max($feed_updated_time, $item_time); } +// If we do not have any items at all, sending the current time is better than sending no time. +if (!$feed_updated_time) +{ + $feed_updated_time = time(); +} + +// Some default assignments +// FEED_IMAGE is not used (atom) +$global_vars = array_merge($global_vars, array( + 'FEED_IMAGE' => ($user->img('site_logo', '', false, '', 'src')) ? $board_url . '/' . substr($user->img('site_logo', '', false, '', 'src'), strlen($phpbb_root_path)) : '', + 'SELF_LINK' => feed_append_sid('/feed.' . $phpEx, $params), + 'FEED_LINK' => $board_url . '/index.' . $phpEx, + 'FEED_TITLE' => $config['sitename'], + 'FEED_SUBTITLE' => $config['site_desc'], + 'FEED_UPDATED' => feed_format_date($feed_updated_time), + 'FEED_LANG' => $user->lang['USER_LANG'], + 'FEED_AUTHOR' => $config['sitename'], +)); + $feed->close(); // Output page @@ -133,12 +149,7 @@ if ($config['gzip_compress']) } // IF debug extra is enabled and admin want to "explain" the page we need to set other headers... -if (!defined('DEBUG_EXTRA') || !request_var('explain', 0) || !$auth->acl_get('a_')) -{ - header("Content-Type: application/atom+xml; charset=UTF-8"); - header("Last-Modified: " . gmdate('D, d M Y H:i:s', time()) . ' GMT'); -} -else +if (defined('DEBUG_EXTRA') && request_var('explain', 0) && $auth->acl_get('a_')) { header('Content-type: text/html; charset=UTF-8'); header('Cache-Control: private, no-cache="set-cookie"'); @@ -157,6 +168,9 @@ else exit_handler(); } +header("Content-Type: application/atom+xml; charset=UTF-8"); +header("Last-Modified: " . gmdate('D, d M Y H:i:s', $feed_updated_time) . ' GMT'); + echo '' . "\n"; echo '' . "\n"; echo '' . "\n\n"; @@ -183,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"; } @@ -215,6 +229,33 @@ function feed_append_sid($url, $params) return append_sid($board_url . $url, $params, true, ''); } +/** +* Generate ISO 8601 date string (RFC 3339) +**/ +function feed_format_date($time) +{ + static $zone_offset; + static $offset_string; + + if (empty($offset_string)) + { + global $user; + + $zone_offset = (int) $user->timezone + (int) $user->dst; + + $sign = ($zone_offset < 0) ? '-' : '+'; + $time_offset = abs($zone_offset); + + $offset_seconds = $time_offset % 3600; + $offset_minutes = $offset_seconds / 60; + $offset_hours = ($time_offset - $offset_seconds) / 3600; + + $offset_string = sprintf("%s%02d:%02d", $sign, $offset_hours, $offset_minutes); + } + + return gmdate("Y-m-d\TH:i:s", $time + $zone_offset) . $offset_string; +} + /** * Generate text content **/ @@ -284,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; } @@ -320,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; } @@ -328,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; @@ -348,44 +399,36 @@ 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; } } } /** -* Base/default Feed class if no mode is specified. -* This can be the overall site feed or a forum/topic feed. +* Base class with some generic functions and settings. +* * @package phpBB3 */ -class phpbb_feed +class phpbb_feed_base { - /** - * Forum id specified for forum feed. - */ - var $forum_id = 0; - - /** - * Topic id specified for topic feed. - */ - var $topic_id = 0; - /** * SQL Query to be executed to get feed items */ - var $sql; + var $sql = array(); /** * Keys specified for retrieval of title, content, etc. @@ -393,34 +436,9 @@ class phpbb_feed var $keys = array(); /** - * An array of excluded forum ids. + * Number of items to fetch. Usually overwritten by $config['feed_something'] */ - var $excluded_forums_ary = NULL; - - /** - * Number of items to fetch - */ - var $num_items; - - /** - * 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 = 0; + var $num_items = 15; /** * Separator for title elements to separate items (for example forum / topic) @@ -428,79 +446,55 @@ class phpbb_feed var $separator = "\xE2\x80\xA2"; // • /** - * Constructor. Set standard keys. + * Separator for the statistics row (Posted by, post date, replies, etc.) */ - function phpbb_feed($forum_id = 0, $topic_id = 0) + var $separator_stats = "\xE2\x80\x94"; // — + + /** + * Constructor + */ + function phpbb_feed_base() { global $config; - $this->forum_id = $forum_id; - $this->topic_id = $topic_id; - - $this->sql = array(); - - // Set some values for pagination - $this->num_items = (int) $config['feed_limit']; $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; + } + } } + /** + * Set keys. + */ function set_keys() { - // Set keys for items... - $this->set('title', 'post_subject'); - $this->set('title2', 'topic_title'); - $this->set('author_id', 'user_id'); - $this->set('creator', 'username'); - $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'); } + /** + * Open feed + */ function open() { - if (!$this->forum_id && !$this->topic_id) - { - return; - } - else if ($this->forum_id && !$this->topic_id) - { - global $db, $user, $global_vars; - - $sql = 'SELECT forum_name - FROM ' . FORUMS_TABLE . ' - WHERE forum_id = ' . $this->forum_id; - $result = $db->sql_query($sql); - - $global_vars['FEED_MODE'] = $user->lang['FORUM'] . ': ' . $db->sql_fetchfield('forum_name'); - - $db->sql_freeresult($result); - } - else if ($this->topic_id) - { - global $db, $user, $global_vars; - - $sql = 'SELECT topic_title - FROM ' . TOPICS_TABLE . ' - WHERE topic_id = ' . $this->topic_id; - $result = $db->sql_query($sql); - - $global_vars['FEED_MODE'] = $user->lang['TOPIC'] . ': ' . $db->sql_fetchfield('topic_title'); - - $db->sql_freeresult($result); - } } + /** + * Close feed + */ function close() { + global $db; + if (!empty($this->result)) { - global $db; - $db->sql_freeresult($this->result); } } @@ -521,197 +515,577 @@ class phpbb_feed 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; - - // Which forums should not be searched ? - $exclude_forums = array(); - - $sql = 'SELECT forum_id - FROM ' . FORUMS_TABLE . ' - WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '<> 0'); - $result = $db->sql_query($sql); - - while ($row = $db->sql_fetchrow($result)) - { - $exclude_forums[] = (int) $row['forum_id']; - } - $db->sql_freeresult($result); - - // Exclude forums the user is not able to read - $this->excluded_forums_ary = array_keys($auth->acl_getf('!f_read', true)); - $this->excluded_forums_ary = (sizeof($exclude_forums)) ? array_merge($exclude_forums, $this->excluded_forums_ary) : $this->excluded_forums_ary; - - $not_in_fid = (sizeof($this->excluded_forums_ary)) ? 'WHERE (' . $db->sql_in_set('f.forum_id', $this->excluded_forums_ary, true) . ' AND ' . $db->sql_in_set('f.parent_id', $this->excluded_forums_ary, true) . ") OR (f.forum_password <> '' AND fa.user_id <> " . (int) $user->data['user_id'] . ')' : ''; - - $sql = 'SELECT f.forum_id, f.forum_name, f.parent_id, f.forum_type, f.right_id, f.forum_password, fa.user_id - FROM ' . FORUMS_TABLE . ' f - LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa ON (fa.forum_id = f.forum_id - AND fa.session_id = '" . $db->sql_escape($user->session_id) . "') - $not_in_fid - ORDER BY f.left_id"; - $result = $db->sql_query($sql); - - $right_id = 0; - while ($row = $db->sql_fetchrow($result)) - { - // Exclude passworded forum completely - if ($row['forum_password'] && $row['user_id'] != $user->data['user_id']) - { - $this->excluded_forums_ary[] = (int) $row['forum_id']; - continue; - } - - if ($row['right_id'] > $right_id) - { - $right_id = (int) $row['right_id']; - } - else if ($row['right_id'] < $right_id) - { - continue; - } - } - $db->sql_freeresult($result); - - return $this->excluded_forums_ary; + return $forum_ids; } - /** - * Get SQL query for fetching items - */ - function get_sql() + function get_moderator_approve_forums() { - global $db; + global $auth; + static $forum_ids; - $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) + if (!isset($forum_ids)) { - // 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); + $forum_ids = array_keys($auth->acl_getf('m_approve')); } - // Fetch latest posts from forum - else if (!$this->topic_id && $this->forum_id) + + return $forum_ids; + } + + 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) { - // 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('f1.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 . ')'; + $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 ($row = $db->sql_fetchrow($result)) + while ($forum_id = (int) $db->sql_fetchfield('forum_id')) { - $forum_ids[] = (int) $row['forum_id']; + $forum_ids[$forum_id] = $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); + $cache->put('_' . $cache_name, $forum_ids); } - // Fetch last posts from specified topic... - else if ($this->topic_id) - { - // First of all, determine the forum... - $sql = 'SELECT forum_id - FROM ' . TOPICS_TABLE . ' - WHERE topic_id = ' . $this->topic_id; - $result = $db->sql_query_limit($sql, 1); - $this->forum_id = (int) $db->sql_fetchfield('forum_id'); - $db->sql_freeresult($result); - // non-global announcement - if ($this->forum_id && in_array($this->forum_id, $this->excluded_forums())) + 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() + { + global $db, $user; + + // Exclude passworded forums + $sql = 'SELECT f.forum_id, fa.user_id + FROM ' . FORUMS_TABLE . ' f + LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa + ON (fa.forum_id = f.forum_id + AND fa.session_id = '" . $db->sql_escape($user->session_id) . "') + WHERE f.forum_password <> ''"; + $result = $db->sql_query($sql); + + $forum_ids = array(); + while ($row = $db->sql_fetchrow($result)) + { + $forum_id = (int) $row['forum_id']; + + if ($row['user_id'] != $user->data['user_id']) + { + $forum_ids[$forum_id] = $forum_id; + } + } + $db->sql_freeresult($result); + + return $forum_ids; + } + + function get_item() + { + global $db, $cache; + static $result; + + if (!isset($result)) + { + if (!$this->get_sql()) { return false; } - $sql = 'SELECT post_id - FROM ' . POSTS_TABLE . ' - WHERE topic_id = ' . $this->topic_id . ' - AND post_approved = 1 - ORDER BY post_time DESC'; + // Query database + $sql = $db->sql_build_query('SELECT', $this->sql); $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 $db->sql_fetchrow($result); + } + + function user_viewprofile($row) + { + global $phpEx, $user; + + $author_id = (int) $row[$this->get('author_id')]; + + if ($author_id == ANONYMOUS) + { + // Since we cannot link to a profile, we just return GUEST + // instead of $row['username'] + return $user->lang['GUEST']; + } + + return '' . $row[$this->get('creator')] . ''; + } +} + +/** +* Abstract class for post based feeds +* +* @package phpBB3 +*/ +class phpbb_feed_post_base extends phpbb_feed_base +{ + var $num_items = 'feed_limit_post'; + + function set_keys() + { + $this->set('title', 'post_subject'); + $this->set('title2', 'topic_title'); + + $this->set('author_id', 'user_id'); + $this->set('creator', 'username'); + $this->set('date', 'post_time'); + $this->set('text', 'post_text'); + + $this->set('bitfield', 'bbcode_bitfield'); + $this->set('bbcode_uid','bbcode_uid'); + + $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']); + } + } +} + +/** +* Abstract class for topic based feeds +* +* @package phpBB3 +*/ +class phpbb_feed_topic_base extends phpbb_feed_base +{ + var $num_items = 'feed_limit_topic'; + + function set_keys() + { + $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('date', 'topic_time'); + $this->set('text', 'post_text'); + + $this->set('bitfield', 'bbcode_bitfield'); + $this->set('bbcode_uid','bbcode_uid'); + + $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[$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; } - // Now build sql query for obtaining items + // 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, 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, ' . - 'u.username, u.user_id, u.user_email, u.user_colour', + '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', - 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', + '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; + + $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)) + { + 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)) + { + // User cannot read any forums + trigger_error('SORRY_AUTH_READ'); + } + + 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('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, $this->topic_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->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); + } + } + } + + function get_sql() + { + global $auth, $db; + + $this->sql = array( + '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', + USERS_TABLE => 'u', + ), + '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', ); @@ -720,127 +1094,51 @@ class phpbb_feed function get_item() { - global $db, $cache; - - // Disable cache if it is not a guest or a bot but a registered user - if ($this->cache_time) - { - global $user; - - // We check this here because we call get_item() quite often - if (!empty($user) && $user->data['is_registered']) - { - $this->cache_time = 0; - } - } - - if (!$this->cache_time) - { - if (empty($this->result)) - { - 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 $db->sql_fetchrow($this->result); - } - else - { - if (empty($this->items_filled)) - { - // Try to load result set... - $cache_filename = substr(get_class($this), strlen('phpbb_')); - - if (($this->items = $cache->get('_' . $cache_filename)) === false) - { - $this->items = array(); - - if ($this->get_sql()) - { - // Query database - $sql = $db->sql_build_query('SELECT', $this->sql); - $result = $db->sql_query_limit($sql, $this->num_items); - - while ($row = $db->sql_fetchrow($result)) - { - $this->items[] = $row; - } - $db->sql_freeresult($result); - } - - $cache->put('_' . $cache_filename, $this->items, $this->cache_time); - } - - $this->items_filled = true; - } - - $row = array_shift($this->items); - return (!$row) ? false : $row; - } - } - - function adjust_item(&$item_row, &$row) - { - 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['username'] . ''; - - $time = ($this->topic_id) ? $row['post_time'] : $row['topic_time']; - - $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $user_link . ' - ' . $user->format_date($time). ' - ' . $user->lang['REPLIES'] . ' ' . $row['topic_replies'] . ' - ' . $user->lang['VIEWS'] . ' ' . $row['topic_views']; - } + return ($row = parent::get_item()) ? array_merge($this->topic_data, $row) : $row; } } -class phpbb_feed_forums extends phpbb_feed +/** +* '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 open() - { - global $user, $global_vars; - - $global_vars['FEED_MODE'] = $user->lang['FORUMS']; } 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; @@ -856,178 +1154,181 @@ class phpbb_feed_forums extends phpbb_feed { global $user; - $item_row['statistics'] = sprintf($user->lang['TOTAL_TOPICS_OTHER'], $row['forum_topics']) . ' - ' . sprintf($user->lang['TOTAL_POSTS_OTHER'], $row['forum_posts']); + $item_row['statistics'] = sprintf($user->lang['TOTAL_TOPICS_OTHER'], $row['forum_topics']) + . ' ' . $this->separator_stats . ' ' . sprintf($user->lang['TOTAL_POSTS_OTHER'], $row['forum_posts']); } } } -class phpbb_feed_news extends phpbb_feed +/** +* 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 { - 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); - function open() - { - global $user, $global_vars; + $cache->put('_' . $cache_name, $forum_ids); + } - $global_vars['FEED_MODE'] = $user->lang['FEED_NEWS']; + return $forum_ids; } function get_sql() { - global $db, $config; + global $auth, $config, $db; - // 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)) - { - $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_password, f.forum_name, f.forum_topics, f.forum_posts, f.parent_id, f.left_id, f.right_id, - 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, - u.username, u.user_id, u.user_email, u.user_colour', - 'FROM' => array( - TOPICS_TABLE => 't', - FORUMS_TABLE => 'f', - POSTS_TABLE => 'p', - USERS_TABLE => 'u', - ), - '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_poster = u.user_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('creator')] . ''; - - $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $user_link . ' - ' . $user->format_date($row['topic_time']). ' - ' . $user->lang['REPLIES'] . ' ' . $row['topic_replies'] . ' - ' . $user->lang['VIEWS'] . ' ' . $row['topic_views']; + return false; } - } -} -class phpbb_feed_topics extends phpbb_feed -{ - 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 open() - { - global $user, $global_vars; - - $global_vars['FEED_MODE'] = $user->lang['TOPICS']; - } - - 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_password, f.forum_name, f.forum_topics, f.forum_posts, f.parent_id, f.left_id, f.right_id, + '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, - u.username, u.user_id, u.user_email, u.user_colour', + 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', - 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', - '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; @@ -1035,18 +1336,137 @@ class phpbb_feed_topics extends phpbb_feed 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('creator')] . ''; - - $item_row['statistics'] = $user->lang['POSTED'] . ' ' . $user->lang['POST_BY_AUTHOR'] . ' ' . $user_link . ' - ' . $user->format_date($row['topic_time']). ' - ' . $user->lang['REPLIES'] . ' ' . $row['topic_replies'] . ' - ' . $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_eaccelerator.php b/phpBB/includes/acm/acm_eaccelerator.php index 1a3cf3c0f7..645067c199 100644 --- a/phpBB/includes/acm/acm_eaccelerator.php +++ b/phpBB/includes/acm/acm_eaccelerator.php @@ -30,6 +30,7 @@ if (!class_exists('acm_memory')) class acm extends acm_memory { var $extension = 'eaccelerator'; + var $function = 'eaccelerator_get'; var $serialize_header = '#phpbb-serialized#'; 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_memcache.php b/phpBB/includes/acm/acm_memcache.php index 3077ee9615..52b8832749 100644 --- a/phpBB/includes/acm/acm_memcache.php +++ b/phpBB/includes/acm/acm_memcache.php @@ -105,7 +105,11 @@ class acm extends acm_memory */ function _write($var, $data, $ttl = 2592000) { - return $this->memcache->set($this->key_prefix . $var, $data, $this->flags, $ttl); + if (!$this->memcache->replace($this->key_prefix . $var, $data, $this->flags, $ttl)) + { + return $this->memcache->set($this->key_prefix . $var, $data, $this->flags, $ttl); + } + return true; } /** diff --git a/phpBB/includes/acm/acm_memory.php b/phpBB/includes/acm/acm_memory.php index 1ed4fb0d55..efbfd4dd62 100644 --- a/phpBB/includes/acm/acm_memory.php +++ b/phpBB/includes/acm/acm_memory.php @@ -47,6 +47,13 @@ class acm_memory trigger_error("Could not find required extension [{$this->extension}] for the ACM module $acm_type.", E_USER_ERROR); } + + 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); + } } /** @@ -359,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_board.php b/phpBB/includes/acp/acp_board.php index 9f0bcf210f..20a63e646e 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -29,6 +29,7 @@ class acp_board { global $db, $user, $auth, $template; global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx; + global $cache; $user->add_lang('acp/board'); @@ -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), ) ); @@ -469,6 +478,9 @@ class acp_board // 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'); } @@ -910,7 +922,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}