aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--build/build.xml16
-rwxr-xr-xcomposer.pharbin0 -> 499053 bytes
-rw-r--r--phpBB/adm/images/phpbb_logo.gifbin3883 -> 0 bytes
-rw-r--r--phpBB/adm/images/phpbb_logo.pngbin0 -> 9313 bytes
-rw-r--r--phpBB/adm/style/admin.css6
-rw-r--r--phpBB/assets/javascript/core.js35
-rw-r--r--phpBB/develop/create_schema_files.php2
-rw-r--r--phpBB/docs/INSTALL.html2
-rw-r--r--phpBB/docs/coding-guidelines.html38
-rw-r--r--phpBB/includes/acp/acp_inactive.php7
-rw-r--r--phpBB/includes/bbcode.php2
-rw-r--r--phpBB/includes/cache/service.php55
-rw-r--r--phpBB/includes/db/dbal.php31
-rw-r--r--phpBB/includes/db/mssql.php8
-rw-r--r--phpBB/includes/db/mssql_odbc.php8
-rw-r--r--phpBB/includes/db/mssqlnative.php10
-rw-r--r--phpBB/includes/db/mysql.php8
-rw-r--r--phpBB/includes/db/mysqli.php8
-rw-r--r--phpBB/includes/functions.php30
-rw-r--r--phpBB/includes/functions_display.php2
-rw-r--r--phpBB/includes/functions_install.php14
-rw-r--r--phpBB/includes/functions_posting.php4
-rw-r--r--phpBB/includes/functions_upload.php15
-rw-r--r--phpBB/includes/php/ini.php175
-rw-r--r--phpBB/includes/search/fulltext_mysql.php103
-rw-r--r--phpBB/includes/search/fulltext_postgres.php856
-rw-r--r--phpBB/includes/style/style.php8
-rw-r--r--phpBB/includes/template/filter.php4
-rw-r--r--phpBB/includes/template/template.php7
-rw-r--r--phpBB/includes/ucp/info/ucp_profile.php1
-rw-r--r--phpBB/includes/ucp/ucp_pm_options.php17
-rw-r--r--phpBB/includes/ucp/ucp_profile.php54
-rw-r--r--phpBB/includes/user.php21
-rw-r--r--phpBB/install/database_update.php22
-rw-r--r--phpBB/install/install_install.php4
-rw-r--r--phpBB/install/schemas/schema_data.sql3
-rw-r--r--phpBB/language/en/acp/search.php18
-rw-r--r--phpBB/language/en/search.php2
-rw-r--r--phpBB/language/en/ucp.php7
-rw-r--r--phpBB/search.php13
-rw-r--r--phpBB/styles/prosilver/template/forumlist_body.html7
-rw-r--r--phpBB/styles/prosilver/template/mcp_topic.html2
-rw-r--r--phpBB/styles/prosilver/template/overall_footer.html6
-rw-r--r--phpBB/styles/prosilver/template/posting_editor.html2
-rw-r--r--phpBB/styles/prosilver/template/quickreply_editor.html2
-rw-r--r--phpBB/styles/prosilver/template/search_results.html2
-rw-r--r--phpBB/styles/prosilver/template/ucp_groups_membership.html2
-rw-r--r--phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html5
-rw-r--r--phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html53
-rw-r--r--phpBB/styles/prosilver/template/viewtopic_body.html2
-rw-r--r--phpBB/styles/subsilver2/template/forumlist_body.html8
-rw-r--r--phpBB/styles/subsilver2/template/mcp_topic.html2
-rw-r--r--phpBB/styles/subsilver2/template/posting_body.html2
-rw-r--r--phpBB/styles/subsilver2/template/quickreply_editor.html2
-rw-r--r--phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html49
-rw-r--r--phpBB/styles/subsilver2/template/viewforum_body.html2
-rw-r--r--phpBB/styles/subsilver2/template/viewtopic_body.html4
-rw-r--r--phpBB/viewforum.php6
-rw-r--r--phpBB/viewtopic.php12
-rw-r--r--tests/RUNNING_TESTS.txt21
-rw-r--r--tests/bbcode/url_bbcode_test.php2
-rw-r--r--tests/bootstrap.php2
-rw-r--r--tests/class_loader/class_loader_test.php2
-rw-r--r--tests/config/db_test.php2
-rw-r--r--tests/cron/manager_test.php1
-rw-r--r--tests/cron/task_provider_test.php2
-rw-r--r--tests/dbal/case_test.php69
-rw-r--r--tests/dbal/concatenate_test.php64
-rw-r--r--tests/dbal/order_lower_test.php2
-rw-r--r--tests/download/http_byte_range_test.php1
-rw-r--r--tests/extension/finder_test.php3
-rw-r--r--tests/extension/manager_test.php1
-rw-r--r--tests/functional/extension_controller_test.php9
-rw-r--r--tests/functional/fileupload_form_test.php62
-rw-r--r--tests/functional/fileupload_remote_test.php72
-rw-r--r--tests/functional/fixtures/files/empty.png0
-rw-r--r--tests/functional/fixtures/files/illegal-extension.bifbin0 -> 519 bytes
-rw-r--r--tests/functional/fixtures/files/too-large.pngbin0 -> 284717 bytes
-rw-r--r--tests/functional/fixtures/files/valid.jpgbin0 -> 554 bytes
-rw-r--r--tests/functions_acp/build_cfg_template_test.php1
-rw-r--r--tests/functions_acp/build_select_test.php1
-rw-r--r--tests/functions_acp/h_radio_test.php1
-rw-r--r--tests/functions_acp/validate_config_vars_test.php1
-rw-r--r--tests/functions_acp/validate_range_test.php1
-rw-r--r--tests/mock/filespec.php32
-rw-r--r--tests/mock/fileupload.php52
-rw-r--r--tests/mock/user.php (renamed from tests/mock_user.php)0
-rw-r--r--tests/security/base.php2
-rw-r--r--tests/session/continue_test.php1
-rw-r--r--tests/session/init_test.php1
-rw-r--r--tests/session/testable_factory.php3
-rw-r--r--tests/template/invalid_constructs_test.php87
-rw-r--r--tests/template/parent_templates/parent_and_child.js1
-rw-r--r--tests/template/parent_templates/parent_only.js1
-rw-r--r--tests/template/template_includejs_test.php9
-rw-r--r--tests/template/template_locate_test.php6
-rw-r--r--tests/template/template_test.php2
-rw-r--r--tests/template/template_test_case.php5
-rw-r--r--tests/template/template_test_case_with_tree.php4
-rw-r--r--tests/template/templates/child_only.js1
-rw-r--r--tests/template/templates/includejs.html4
-rw-r--r--tests/template/templates/invalid/endif_without_if.html1
-rw-r--r--tests/template/templates/invalid/include_nonexistent_file.html1
-rw-r--r--tests/template/templates/invalid/output/endif_without_if.html1
-rw-r--r--tests/template/templates/invalid/output/include_nonexistent_file.html1
-rw-r--r--tests/template/templates/invalid/output/unknown_tag.html1
-rw-r--r--tests/template/templates/invalid/unknown_tag.html1
-rw-r--r--tests/template/templates/parent_and_child.js1
-rw-r--r--tests/test_framework/phpbb_database_connection_odbc_pdo_wrapper.php37
-rw-r--r--tests/test_framework/phpbb_database_test_case.php35
-rw-r--r--tests/test_framework/phpbb_database_test_connection_manager.php127
-rw-r--r--tests/test_framework/phpbb_test_case_helpers.php4
-rw-r--r--tests/text_processing/censor_text_test.php6
-rw-r--r--tests/upload/filespec_test.php275
-rw-r--r--tests/upload/fileupload_test.php115
-rw-r--r--tests/upload/fixture/copies/.gitkeep0
-rw-r--r--tests/upload/fixture/gifbin0 -> 35 bytes
-rw-r--r--tests/upload/fixture/jpgbin0 -> 519 bytes
-rw-r--r--tests/upload/fixture/pngbin0 -> 69 bytes
-rw-r--r--tests/upload/fixture/tifbin0 -> 256 bytes
-rw-r--r--tests/upload/fixture/txt2
-rw-r--r--tests/wrapper/phpbb_php_ini_fake.php16
-rw-r--r--tests/wrapper/phpbb_php_ini_test.php86
124 files changed, 2735 insertions, 296 deletions
diff --git a/.travis.yml b/.travis.yml
index 20c0c6bf4f..e2bedef32b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,8 +9,8 @@ env:
- DB=postgres
before_script:
- - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS phpbb_tests;' -U postgres; fi"
- - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database phpbb_tests;' -U postgres; fi"
+ - sh -c "if [ '$DB' = 'postgres' ]; then psql -c 'DROP DATABASE IF EXISTS phpbb_tests;' -U postgres; fi"
+ - sh -c "if [ '$DB' = 'postgres' ]; then psql -c 'create database phpbb_tests;' -U postgres; fi"
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS phpbb_tests;'; fi"
- pyrus install --force phpunit/DbUnit
- phpenv rehash
diff --git a/build/build.xml b/build/build.xml
index bc65cd0b38..1b8d42a660 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -45,13 +45,7 @@
<target name="composer" depends="clean,prepare">
<exec dir="./phpBB/"
- command="curl -s http://getcomposer.org/installer | php"
- passthru="true" />
- <exec dir="./phpBB/"
- command="php composer.phar install"
- passthru="true" />
- <exec dir="./phpBB/"
- command="rm composer.phar"
+ command="php ../composer.phar install"
passthru="true" />
</target>
@@ -169,13 +163,7 @@
checkreturn="true" />
<exec dir="${dir}"
- command="curl -s http://getcomposer.org/installer | php"
- passthru="true" />
- <exec dir="${dir}"
- command="php composer.phar install"
- passthru="true" />
- <exec dir="${dir}"
- command="rm composer.phar"
+ command="php ../composer.phar install"
passthru="true" />
<delete file="${dir}/config.php" />
diff --git a/composer.phar b/composer.phar
new file mode 100755
index 0000000000..3572477546
--- /dev/null
+++ b/composer.phar
Binary files differ
diff --git a/phpBB/adm/images/phpbb_logo.gif b/phpBB/adm/images/phpbb_logo.gif
deleted file mode 100644
index 239993182b..0000000000
--- a/phpBB/adm/images/phpbb_logo.gif
+++ /dev/null
Binary files differ
diff --git a/phpBB/adm/images/phpbb_logo.png b/phpBB/adm/images/phpbb_logo.png
new file mode 100644
index 0000000000..c3f9248ed7
--- /dev/null
+++ b/phpBB/adm/images/phpbb_logo.png
Binary files differ
diff --git a/phpBB/adm/style/admin.css b/phpBB/adm/style/admin.css
index 94f421030d..08613de0dd 100644
--- a/phpBB/adm/style/admin.css
+++ b/phpBB/adm/style/admin.css
@@ -146,15 +146,15 @@ li {
#page-header {
clear: both;
text-align: right;
- background: url("../images/phpbb_logo.gif") top left no-repeat;
- height: 49px;
+ background: url("../images/phpbb_logo.png") top left no-repeat;
+ height: 54px;
font-size: 0.85em;
margin-bottom: 10px;
}
.rtl #page-header {
text-align: left;
- background: url("../images/phpbb_logo.gif") top right no-repeat;
+ background: url("../images/phpbb_logo.png") top right no-repeat;
}
#page-header h1 {
diff --git a/phpBB/assets/javascript/core.js b/phpBB/assets/javascript/core.js
index ab1aaa8505..f07a245307 100644
--- a/phpBB/assets/javascript/core.js
+++ b/phpBB/assets/javascript/core.js
@@ -521,14 +521,47 @@ phpbb.add_ajax_callback = function(id, callback)
* the alt-text data attribute, and replaces the text in the attribute with the
* current text so that the process can be repeated.
*/
-phpbb.add_ajax_callback('alt_text', function(data) {
+phpbb.add_ajax_callback('alt_text', function() {
var el = $(this),
alt_text;
alt_text = el.attr('data-alt-text');
+ el.attr('data-alt-text', el.text());
el.attr('title', alt_text);
el.text(alt_text);
});
+/**
+ * This callback is based on the alt_text callback.
+ *
+ * It replaces the current text with the text in the alt-text data attribute,
+ * and replaces the text in the attribute with the current text so that the
+ * process can be repeated.
+ * Additionally it replaces the class of the link's parent
+ * and changes the link itself.
+ */
+phpbb.add_ajax_callback('toggle_link', function() {
+ var el = $(this),
+ toggle_text,
+ toggle_url,
+ toggle_class;
+
+ // Toggle link text
+
+ toggle_text = el.attr('data-toggle-text');
+ el.attr('data-toggle-text', el.text());
+ el.attr('title', toggle_text);
+ el.text(toggle_text);
+
+ // Toggle link url
+ toggle_url = el.attr('data-toggle-url');
+ el.attr('data-toggle-url', el.attr('href'));
+ el.attr('href', toggle_url);
+
+ // Toggle class of link parent
+ toggle_class = el.attr('data-toggle-class');
+ el.attr('data-toggle-class', el.parent().attr('class'));
+ el.parent().attr('class', toggle_class);
+});
})(jQuery); // Avoid conflicts with other libraries
diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php
index 0d239b11f2..6825ba1bf6 100644
--- a/phpBB/develop/create_schema_files.php
+++ b/phpBB/develop/create_schema_files.php
@@ -237,7 +237,7 @@ $supported_dbms = array('firebird', 'mssql', 'mysql_40', 'mysql_41', 'oracle', '
foreach ($supported_dbms as $dbms)
{
- $fp = fopen($schema_path . $dbms . '_schema.sql', 'wt');
+ $fp = fopen($schema_path . $dbms . '_schema.sql', 'wb');
$line = '';
diff --git a/phpBB/docs/INSTALL.html b/phpBB/docs/INSTALL.html
index 47cf546ee8..cec11facb6 100644
--- a/phpBB/docs/INSTALL.html
+++ b/phpBB/docs/INSTALL.html
@@ -132,7 +132,7 @@
<ul>
<li>MySQL 3.23 or above (MySQLi supported)</li>
<li>PostgreSQL 7.3+</li>
- <li>SQLite 2.8.2+</li>
+ <li>SQLite 2.8.2+ (SQLite 3 is not supported)</li>
<li>Firebird 2.1+</li>
<li>MS SQL Server 2000 or above (directly or via ODBC or the native adapter)</li>
<li>Oracle</li>
diff --git a/phpBB/docs/coding-guidelines.html b/phpBB/docs/coding-guidelines.html
index 3f2c142ac6..ae4655e094 100644
--- a/phpBB/docs/coding-guidelines.html
+++ b/phpBB/docs/coding-guidelines.html
@@ -295,11 +295,17 @@ PHPBB_QA (Set board to QA-Mode, which means the updater also c
<p>We will not be using any form of hungarian notation in our naming conventions. Many of us believe that hungarian naming is one of the primary code obfuscation techniques currently in use.</p>
<h4>Variable Names:</h4>
- <p>Variable names should be in all lowercase, with words separated by an underscore, example:</p>
+ <p>In PHP, variable names should be in all lowercase, with words separated by an underscore, example:</p>
<div class="indent">
<p><code>$current_user</code> is right, but <code>$currentuser</code> and <code> $currentUser</code> are not.</p>
</div>
+
+ <p>In JavaScript, variable names should use camel case:</p>
+
+ <div class="indent">
+ <p><code>currentUser</code> is right, but <code>currentuser</code> and <code>current_user</code> are not.</p>
+ </div>
<p>Names should be descriptive, but concise. We don't want huge sentences as our variable names, but typing an extra couple of characters is always better than wondering what exactly a certain variable is for. </p>
@@ -317,7 +323,7 @@ for ($i = 0; $i &lt; $outer_size; $i++)
</pre></div>
<h4>Function Names:</h4>
- <p>Functions should also be named descriptively. We're not programming in C here, we don't want to write functions called things like "stristr()". Again, all lower-case names with words separated by a single underscore character. Function names should preferably have a verb in them somewhere. Good function names are <code>print_login_status()</code>, <code>get_user_data()</code>, etc. </p>
+ <p>Functions should also be named descriptively. We're not programming in C here, we don't want to write functions called things like "stristr()". Again, all lower-case names with words separated by a single underscore character in PHP, and camel caps in JavaScript. Function names should preferably have a verb in them somewhere. Good function names are <code>print_login_status()</code>, <code>get_user_data()</code>, etc. Constructor functions in JavaScript should begin with a capital letter.</p>
<h4>Function Arguments:</h4>
<p>Arguments are subject to the same guidelines as variable names. We don't want a bunch of functions like: <code>do_stuff($a, $b, $c)</code>. In most cases, we'd like to be able to tell how to use a function by just looking at its declaration. </p>
@@ -397,7 +403,7 @@ for ($i = 0; $i &lt; size; $i++)
</pre></div>
<h4>Where to put the braces:</h4>
- <p>This one is a bit of a holy war, but we're going to use a style that can be summed up in one sentence: Braces always go on their own line. The closing brace should also always be at the same column as the corresponding opening brace, examples:</p>
+ <p>In PHP code, braces always go on their own line. The closing brace should also always be at the same column as the corresponding opening brace, examples:</p>
<div class="codebox"><pre>
if (condition)
@@ -427,6 +433,30 @@ function do_stuff()
...
}
</pre></div>
+
+ <p>In JavaScript code, braces always go on the same line:</p>
+
+ <div class="codebox"><pre>
+if (condition) {
+ while (condition2) {
+ ...
+ }
+} else {
+ ...
+}
+
+for (var i = 0; i &lt; size; i++) {
+ ...
+}
+
+while (condition) {
+ ...
+}
+
+function do_stuff() {
+ ...
+}
+ </pre></div>
<h4>Use spaces between tokens:</h4>
<p>This is another simple, easy step that helps keep code readable without much effort. Whenever you write an assignment, expression, etc.. Always leave <em>one</em> space between the tokens. Basically, write code as if it was English. Put spaces between variable names and operators. Don't put spaces just after an opening bracket or before a closing bracket. Don't put spaces just before a comma or a semicolon. This is best shown with a few examples, examples:</p>
@@ -502,7 +532,7 @@ $post_url = "{$phpbb_root_path}posting.$phpEx?mode=$mode&amp;amp;start=$start";
<p>In SQL statements mixing single and double quotes is partly allowed (following the guidelines listed here about SQL formatting), else one should try to only use one method - mostly single quotes.</p>
<h4>Commas after every array element:</h4>
- <p>If an array is defined with each element on its own line, you still have to modify the previous line to add a comma when appending a new element. PHP allows for trailing (useless) commas in array definitions. These should always be used so each element including the comma can be appended with a single line</p>
+ <p>If an array is defined with each element on its own line, you still have to modify the previous line to add a comma when appending a new element. PHP allows for trailing (useless) commas in array definitions. These should always be used so each element including the comma can be appended with a single line. In JavaScript, do not use the trailing comma, as it causes browsers to throw errors.</p>
<p class="bad">// wrong</p>
<div class="codebox"><pre>
diff --git a/phpBB/includes/acp/acp_inactive.php b/phpBB/includes/acp/acp_inactive.php
index 78d6a0b2f3..f098b772ee 100644
--- a/phpBB/includes/acp/acp_inactive.php
+++ b/phpBB/includes/acp/acp_inactive.php
@@ -136,6 +136,8 @@ class acp_inactive
add_log('admin', 'LOG_USER_ACTIVE', $row['username']);
add_log('user', $row['user_id'], 'LOG_USER_ACTIVE_USER');
}
+
+ trigger_error(sprintf($user->lang['LOG_INACTIVE_ACTIVATE'], implode($user->lang['COMMA_SEPARATOR'], $user_affected) . ' ' . adm_back_link($this->u_action)));
}
// For activate we really need to redirect, else a refresh can result in users being deactivated again
@@ -159,6 +161,8 @@ class acp_inactive
}
add_log('admin', 'LOG_INACTIVE_' . strtoupper($action), implode(', ', $user_affected));
+
+ trigger_error(sprintf($user->lang['LOG_INACTIVE_DELETE'], implode($user->lang['COMMA_SEPARATOR'], $user_affected) . ' ' . adm_back_link($this->u_action)));
}
else
{
@@ -230,7 +234,8 @@ class acp_inactive
$db->sql_query($sql);
add_log('admin', 'LOG_INACTIVE_REMIND', implode(', ', $usernames));
- unset($usernames);
+
+ trigger_error(sprintf($user->lang['LOG_INACTIVE_REMIND'], implode($user->lang['COMMA_SEPARATOR'], $usernames) . ' ' . adm_back_link($this->u_action)));
}
$db->sql_freeresult($result);
diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php
index fde917e5b1..444446e9c3 100644
--- a/phpBB/includes/bbcode.php
+++ b/phpBB/includes/bbcode.php
@@ -130,7 +130,7 @@ class bbcode
if (empty($this->template_filename))
{
- $this->template_bitfield = new bitfield($user->theme['bbcode_bitfield']);
+ $this->template_bitfield = new bitfield($user->style['bbcode_bitfield']);
$style_resource_locator = new phpbb_style_resource_locator();
$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider());
diff --git a/phpBB/includes/cache/service.php b/phpBB/includes/cache/service.php
index aa225ade69..37f32aa753 100644
--- a/phpBB/includes/cache/service.php
+++ b/phpBB/includes/cache/service.php
@@ -321,50 +321,39 @@ class phpbb_cache_service
/**
* Obtain cfg file data
*/
- function obtain_cfg_items($theme)
+ function obtain_cfg_items($style)
{
global $config, $phpbb_root_path;
- $parsed_items = array(
- 'theme' => array(),
- 'template' => array(),
- 'imageset' => array()
- );
+ $parsed_array = $this->driver->get('_cfg_' . $style['style_path']);
- foreach ($parsed_items as $key => $parsed_array)
+ if ($parsed_array === false)
{
- $parsed_array = $this->driver->get('_cfg_' . $key . '_' . $theme[$key . '_path']);
-
- if ($parsed_array === false)
- {
- $parsed_array = array();
- }
+ $parsed_array = array();
+ }
- $reparse = false;
- $filename = $phpbb_root_path . 'styles/' . $theme[$key . '_path'] . '/' . $key . '/' . $key . '.cfg';
+ $reparse = false;
+ $filename = $phpbb_root_path . 'styles/' . $style['style_path'] . '/style.cfg';
- if (!file_exists($filename))
- {
- continue;
- }
+ if (!file_exists($filename))
+ {
+ continue;
+ }
- if (!isset($parsed_array['filetime']) || (($config['load_tplcompile'] && @filemtime($filename) > $parsed_array['filetime'])))
- {
- $reparse = true;
- }
+ if (!isset($parsed_array['filetime']) || (($config['load_tplcompile'] && @filemtime($filename) > $parsed_array['filetime'])))
+ {
+ $reparse = true;
+ }
- // Re-parse cfg file
- if ($reparse)
- {
- $parsed_array = parse_cfg_file($filename);
- $parsed_array['filetime'] = @filemtime($filename);
+ // Re-parse cfg file
+ if ($reparse)
+ {
+ $parsed_array = parse_cfg_file($filename);
+ $parsed_array['filetime'] = @filemtime($filename);
- $this->driver->put('_cfg_' . $key . '_' . $theme[$key . '_path'], $parsed_array);
- }
- $parsed_items[$key] = $parsed_array;
+ $this->driver->put('_cfg_' . $style['style_path'], $parsed_array);
}
-
- return $parsed_items;
+ return $parsed_array;
}
/**
diff --git a/phpBB/includes/db/dbal.php b/phpBB/includes/db/dbal.php
index cf54d455f7..159703d3be 100644
--- a/phpBB/includes/db/dbal.php
+++ b/phpBB/includes/db/dbal.php
@@ -284,6 +284,37 @@ class dbal
}
/**
+ * Build a case expression
+ *
+ * Note: The two statements action_true and action_false must have the same data type (int, vchar, ...) in the database!
+ *
+ * @param string $condition The condition which must be true, to use action_true rather then action_else
+ * @param string $action_true SQL expression that is used, if the condition is true
+ * @param string $action_else SQL expression that is used, if the condition is false, optional
+ * @return string CASE expression including the condition and statements
+ */
+ public function sql_case($condition, $action_true, $action_false = false)
+ {
+ $sql_case = 'CASE WHEN ' . $condition;
+ $sql_case .= ' THEN ' . $action_true;
+ $sql_case .= ($action_false !== false) ? ' ELSE ' . $action_false : '';
+ $sql_case .= ' END';
+ return $sql_case;
+ }
+
+ /**
+ * Build a concatenated expression
+ *
+ * @param string $expr1 Base SQL expression where we append the second one
+ * @param string $expr2 SQL expression that is appended to the first expression
+ * @return string Concatenated string
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $expr1 . ' || ' . $expr2;
+ }
+
+ /**
* Returns whether results of a query need to be buffered to run a transaction while iterating over them.
*
* @return bool Whether buffering is required.
diff --git a/phpBB/includes/db/mssql.php b/phpBB/includes/db/mssql.php
index abeabc389f..fb044b492f 100644
--- a/phpBB/includes/db/mssql.php
+++ b/phpBB/includes/db/mssql.php
@@ -92,6 +92,14 @@ class dbal_mssql extends dbal
}
/**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $expr1 . ' + ' . $expr2;
+ }
+
+ /**
* SQL Transaction
* @access private
*/
diff --git a/phpBB/includes/db/mssql_odbc.php b/phpBB/includes/db/mssql_odbc.php
index 6e24f4e9e8..64fa9634d1 100644
--- a/phpBB/includes/db/mssql_odbc.php
+++ b/phpBB/includes/db/mssql_odbc.php
@@ -110,6 +110,14 @@ class dbal_mssql_odbc extends dbal
}
/**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $expr1 . ' + ' . $expr2;
+ }
+
+ /**
* SQL Transaction
* @access private
*/
diff --git a/phpBB/includes/db/mssqlnative.php b/phpBB/includes/db/mssqlnative.php
index 8a4503f111..1f37d54ecb 100644
--- a/phpBB/includes/db/mssqlnative.php
+++ b/phpBB/includes/db/mssqlnative.php
@@ -260,6 +260,14 @@ class dbal_mssqlnative extends dbal
/**
* {@inheritDoc}
*/
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $expr1 . ' + ' . $expr2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
function sql_buffer_nested_transactions()
{
return true;
@@ -435,7 +443,7 @@ class dbal_mssqlnative extends dbal
unset($row['line2'], $row['line3']);
}
}
- return $row;
+ return (sizeof($row)) ? $row : false;
}
/**
diff --git a/phpBB/includes/db/mysql.php b/phpBB/includes/db/mysql.php
index eb38e3e913..8d1f805870 100644
--- a/phpBB/includes/db/mysql.php
+++ b/phpBB/includes/db/mysql.php
@@ -120,6 +120,14 @@ class dbal_mysql extends dbal
}
/**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')';
+ }
+
+ /**
* SQL Transaction
* @access private
*/
diff --git a/phpBB/includes/db/mysqli.php b/phpBB/includes/db/mysqli.php
index 4210a58002..e07cd35e24 100644
--- a/phpBB/includes/db/mysqli.php
+++ b/phpBB/includes/db/mysqli.php
@@ -123,6 +123,14 @@ class dbal_mysqli extends dbal
}
/**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')';
+ }
+
+ /**
* SQL Transaction
* @access private
*/
diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php
index 3533a4ca00..22e456c7de 100644
--- a/phpBB/includes/functions.php
+++ b/phpBB/includes/functions.php
@@ -4922,9 +4922,9 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
'T_ASSETS_VERSION' => $config['assets_version'],
'T_ASSETS_PATH' => "{$web_path}assets",
- 'T_THEME_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/theme',
- 'T_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/template',
- 'T_SUPER_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/template',
+ 'T_THEME_PATH' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/theme',
+ 'T_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/template',
+ 'T_SUPER_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/template',
'T_IMAGES_PATH' => "{$web_path}images/",
'T_SMILIES_PATH' => "{$web_path}{$config['smilies_path']}/",
'T_AVATAR_PATH' => "{$web_path}{$config['avatar_path']}/",
@@ -4932,16 +4932,15 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
'T_ICONS_PATH' => "{$web_path}{$config['icons_path']}/",
'T_RANKS_PATH' => "{$web_path}{$config['ranks_path']}/",
'T_UPLOAD_PATH' => "{$web_path}{$config['upload_path']}/",
- 'T_STYLESHEET_LINK' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/theme/stylesheet.css?assets_version=' . $config['assets_version'],
- 'T_STYLESHEET_LANG_LINK' => "{$web_path}styles/" . rawurlencode($user->theme['style_path']) . '/theme/' . $user->lang_name . '/stylesheet.css?assets_version=' . $config['assets_version'],
- 'T_STYLESHEET_NAME' => $user->theme['style_name'],
+ 'T_STYLESHEET_LINK' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/theme/stylesheet.css?assets_version=' . $config['assets_version'],
+ 'T_STYLESHEET_LANG_LINK' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/theme/' . $user->lang_name . '/stylesheet.css?assets_version=' . $config['assets_version'],
'T_JQUERY_LINK' => ($config['load_jquery_cdn'] && !empty($config['load_jquery_url'])) ? $config['load_jquery_url'] : "{$web_path}assets/javascript/jquery.js?assets_version=" . $config['assets_version'],
'S_JQUERY_FALLBACK' => ($config['load_jquery_cdn']) ? true : false,
- 'T_THEME_NAME' => rawurlencode($user->theme['style_path']),
+ 'T_THEME_NAME' => rawurlencode($user->style['style_path']),
'T_THEME_LANG_NAME' => $user->data['user_lang'],
- 'T_TEMPLATE_NAME' => $user->theme['style_path'],
- 'T_SUPER_TEMPLATE_NAME' => rawurlencode((isset($user->theme['style_parent_tree']) && $user->theme['style_parent_tree']) ? $user->theme['style_parent_tree'] : $user->theme['style_path']),
+ 'T_TEMPLATE_NAME' => $user->style['style_path'],
+ 'T_SUPER_TEMPLATE_NAME' => rawurlencode((isset($user->style['style_parent_tree']) && $user->style['style_parent_tree']) ? $user->style['style_parent_tree'] : $user->style['style_path']),
'T_IMAGES' => 'images',
'T_SMILIES' => $config['smilies_path'],
'T_AVATAR' => $config['avatar_path'],
@@ -5137,3 +5136,16 @@ function phpbb_pcre_utf8_support()
}
return $utf8_pcre_properties;
}
+
+/**
+* Casts a numeric string $input to an appropriate numeric type (i.e. integer or float)
+*
+* @param string $input A numeric string.
+*
+* @return int|float Integer $input if $input fits integer,
+* float $input otherwise.
+*/
+function phpbb_to_numeric($input)
+{
+ return ($input > PHP_INT_MAX) ? (float) $input : (int) $input;
+}
diff --git a/phpBB/includes/functions_display.php b/phpBB/includes/functions_display.php
index 1f45d5e8e1..545f75ad67 100644
--- a/phpBB/includes/functions_display.php
+++ b/phpBB/includes/functions_display.php
@@ -1221,7 +1221,9 @@ function watch_topic_forum($mode, &$s_watching, $user_id, $forum_id, $topic_id,
if ($can_watch)
{
$s_watching['link'] = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&amp;" . (($is_watching) ? 'unwatch' : 'watch') . "=$mode&amp;start=$start&amp;hash=" . generate_link_hash("{$mode}_$match_id"));
+ $s_watching['link_toggle'] = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&amp;" . ((!$is_watching) ? 'unwatch' : 'watch') . "=$mode&amp;start=$start&amp;hash=" . generate_link_hash("{$mode}_$match_id"));
$s_watching['title'] = $user->lang[(($is_watching) ? 'STOP' : 'START') . '_WATCHING_' . strtoupper($mode)];
+ $s_watching['title_toggle'] = $user->lang[((!$is_watching) ? 'STOP' : 'START') . '_WATCHING_' . strtoupper($mode)];
$s_watching['is_watching'] = $is_watching;
}
diff --git a/phpBB/includes/functions_install.php b/phpBB/includes/functions_install.php
index 50af8fe019..46541acd44 100644
--- a/phpBB/includes/functions_install.php
+++ b/phpBB/includes/functions_install.php
@@ -463,17 +463,21 @@ function connect_check_db($error_connect, &$error, $dbms_details, $table_prefix,
}
/**
-* Removes comments from schema files
+* Removes "/* style" as well as "# style" comments from $input.
+*
+* @param string $input Input string
+*
+* @return string Input string with comments removed
*/
-function remove_comments($sql)
+function phpbb_remove_comments($input)
{
// Remove /* */ comments (http://ostermiller.org/findcomment.html)
- $sql = preg_replace('#/\*(.|[\r\n])*?\*/#', "\n", $sql);
+ $input = preg_replace('#/\*(.|[\r\n])*?\*/#', "\n", $input);
// Remove # style comments
- $sql = preg_replace('/\n{2,}/', "\n", preg_replace('/^#.*$/m', "\n", $sql));
+ $input = preg_replace('/\n{2,}/', "\n", preg_replace('/^#.*$/m', "\n", $input));
- return $sql;
+ return $input;
}
/**
diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php
index f77f54679f..c549f99091 100644
--- a/phpBB/includes/functions_posting.php
+++ b/phpBB/includes/functions_posting.php
@@ -1657,8 +1657,8 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u
// First of all make sure the subject and topic title are having the correct length.
// To achieve this without cutting off between special chars we convert to an array and then count the elements.
- $subject = truncate_string($subject);
- $data['topic_title'] = truncate_string($data['topic_title']);
+ $subject = truncate_string($subject, 120);
+ $data['topic_title'] = truncate_string($data['topic_title'], 120);
// Collect some basic information about which tables and which rows to update/insert
$sql_data = $topic_row = array();
diff --git a/phpBB/includes/functions_upload.php b/phpBB/includes/functions_upload.php
index f70e20e616..33cb585b19 100644
--- a/phpBB/includes/functions_upload.php
+++ b/phpBB/includes/functions_upload.php
@@ -151,7 +151,8 @@ class filespec
*/
function is_image()
{
- return (strpos($this->mimetype, 'image/') !== false) ? true : false;
+ $mimetype = $this->get_mimetype($this->filename);
+ return (strpos($mimetype, 'image/') === 0);
}
/**
@@ -200,17 +201,12 @@ class filespec
}
/**
- * Get mimetype. Utilize mime_content_type if the function exist.
- * Not used at the moment...
+ * Get mimetype. Utilises the finfo class.
*/
function get_mimetype($filename)
{
- $mimetype = '';
-
- if (function_exists('mime_content_type'))
- {
- $mimetype = mime_content_type($filename);
- }
+ $finfo = new finfo(FILEINFO_MIME_TYPE);
+ $mimetype = $finfo->file($filename);
// Some browsers choke on a mimetype of application/octet-stream
if (!$mimetype || $mimetype == 'application/octet-stream')
@@ -342,6 +338,7 @@ class filespec
// Remove temporary filename
@unlink($this->filename);
+ $this->filename = $this->destination_file;
if (sizeof($this->error))
{
diff --git a/phpBB/includes/php/ini.php b/phpBB/includes/php/ini.php
new file mode 100644
index 0000000000..17e8c54a57
--- /dev/null
+++ b/phpBB/includes/php/ini.php
@@ -0,0 +1,175 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Wrapper class for ini_get function.
+*
+* Provides easier handling of the different interpretations of ini values.
+*
+* @package phpBB
+*/
+class phpbb_php_ini
+{
+ /**
+ * Simple wrapper for ini_get()
+ * See http://php.net/manual/en/function.ini-get.php
+ *
+ * @param string $varname The configuration option name.
+ * @return bool|string False if configuration option does not exist,
+ * the configuration option value (string) otherwise.
+ */
+ public function get($varname)
+ {
+ return ini_get($varname);
+ }
+
+ /**
+ * Gets the configuration option value as a trimmed string.
+ *
+ * @param string $varname The configuration option name.
+ * @return bool|string False if configuration option does not exist,
+ * the configuration option value (string) otherwise.
+ */
+ public function get_string($varname)
+ {
+ $value = $this->get($varname);
+
+ if ($value === false)
+ {
+ return false;
+ }
+
+ return trim($value);
+ }
+
+ /**
+ * Gets configuration option value as a boolean.
+ * Interprets the string value 'off' as false.
+ *
+ * @param string $varname The configuration option name.
+ * @return bool False if configuration option does not exist.
+ * False if configuration option is disabled.
+ * True otherwise.
+ */
+ public function get_bool($varname)
+ {
+ $value = $this->get_string($varname);
+
+ if (empty($value) || strtolower($value) == 'off')
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets configuration option value as an integer.
+ *
+ * @param string $varname The configuration option name.
+ * @return bool|int False if configuration option does not exist,
+ * false if configuration option value is not numeric,
+ * the configuration option value (integer) otherwise.
+ */
+ public function get_int($varname)
+ {
+ $value = $this->get_string($varname);
+
+ if (!is_numeric($value))
+ {
+ return false;
+ }
+
+ return (int) $value;
+ }
+
+ /**
+ * Gets configuration option value as a float.
+ *
+ * @param string $varname The configuration option name.
+ * @return bool|float False if configuration option does not exist,
+ * false if configuration option value is not numeric,
+ * the configuration option value (float) otherwise.
+ */
+ public function get_float($varname)
+ {
+ $value = $this->get_string($varname);
+
+ if (!is_numeric($value))
+ {
+ return false;
+ }
+
+ return (float) $value;
+ }
+
+ /**
+ * Gets configuration option value in bytes.
+ * Converts strings like '128M' to bytes (integer or float).
+ *
+ * @param string $varname The configuration option name.
+ * @return bool|int|float False if configuration option does not exist,
+ * false if configuration option value is not well-formed,
+ * the configuration option value otherwise.
+ */
+ public function get_bytes($varname)
+ {
+ $value = $this->get_string($varname);
+
+ if ($value === false)
+ {
+ return false;
+ }
+
+ if (is_numeric($value))
+ {
+ // Already in bytes.
+ return phpbb_to_numeric($value);
+ }
+ else if (strlen($value) < 2)
+ {
+ // Single character.
+ return false;
+ }
+ else if (strlen($value) < 3 && $value[0] === '-')
+ {
+ // Two characters but the first one is a minus.
+ return false;
+ }
+
+ $value_lower = strtolower($value);
+ $value_numeric = phpbb_to_numeric($value);
+
+ switch ($value_lower[strlen($value_lower) - 1])
+ {
+ case 'g':
+ $value_numeric *= 1024;
+ case 'm':
+ $value_numeric *= 1024;
+ case 'k':
+ $value_numeric *= 1024;
+ break;
+
+ default:
+ // It's not already in bytes (and thus numeric)
+ // and does not carry a unit.
+ return false;
+ }
+
+ return $value_numeric;
+ }
+}
diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php
index 7c94038cc9..20dcb74c0d 100644
--- a/phpBB/includes/search/fulltext_mysql.php
+++ b/phpBB/includes/search/fulltext_mysql.php
@@ -27,8 +27,6 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
var $split_words = array();
var $search_query;
var $common_words = array();
- var $pcre_properties = false;
- var $mbstring_regex = false;
public function __construct(&$error)
{
@@ -36,18 +34,6 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$this->word_length = array('min' => $config['fulltext_mysql_min_word_len'], 'max' => $config['fulltext_mysql_max_word_len']);
- // PHP may not be linked with the bundled PCRE lib and instead with an older version
- if (phpbb_pcre_utf8_support())
- {
- $this->pcre_properties = true;
- }
-
- if (function_exists('mb_ereg'))
- {
- $this->mbstring_regex = true;
- mb_regex_encoding('UTF-8');
- }
-
$error = false;
}
@@ -70,7 +56,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
if ($db->sql_layer != 'mysql4' && $db->sql_layer != 'mysqli')
{
- return $user->lang['FULLTEXT_MYSQL_INCOMPATIBLE_VERSION'];
+ return $user->lang['FULLTEXT_MYSQL_INCOMPATIBLE_DATABASE'];
}
$result = $db->sql_query('SHOW TABLE STATUS LIKE \'' . POSTS_TABLE . '\'');
@@ -133,40 +119,10 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$split_keywords = preg_replace("#[\n\r\t]+#", ' ', trim(htmlspecialchars_decode($keywords)));
// Split words
- if ($this->pcre_properties)
- {
- $split_keywords = preg_replace('#([^\p{L}\p{N}\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
- }
- else if ($this->mbstring_regex)
- {
- $split_keywords = mb_ereg_replace('([^\w\'*"()])', '\\1\\1', str_replace('\'\'', '\' \'', trim($split_keywords)));
- }
- else
- {
- $split_keywords = preg_replace('#([^\w\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
- }
-
- if ($this->pcre_properties)
- {
- $matches = array();
- preg_match_all('#(?:[^\p{L}\p{N}*"()]|^)([+\-|]?(?:[\p{L}\p{N}*"()]+\'?)*[\p{L}\p{N}*"()])(?:[^\p{L}\p{N}*"()]|$)#u', $split_keywords, $matches);
- $this->split_words = $matches[1];
- }
- else if ($this->mbstring_regex)
- {
- mb_ereg_search_init($split_keywords, '(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)');
-
- while (($word = mb_ereg_search_regs()))
- {
- $this->split_words[] = $word[1];
- }
- }
- else
- {
- $matches = array();
- preg_match_all('#(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)#u', $split_keywords, $matches);
- $this->split_words = $matches[1];
- }
+ $split_keywords = preg_replace('#([^\p{L}\p{N}\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
+ $matches = array();
+ preg_match_all('#(?:[^\p{L}\p{N}*"()]|^)([+\-|]?(?:[\p{L}\p{N}*"()]+\'?)*[\p{L}\p{N}*"()])(?:[^\p{L}\p{N}*"()]|$)#u', $split_keywords, $matches);
+ $this->split_words = $matches[1];
// We limit the number of allowed keywords to minimize load on the database
if ($config['max_num_search_keywords'] && sizeof($this->split_words) > $config['max_num_search_keywords'])
@@ -271,41 +227,10 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
global $config;
// Split words
- if ($this->pcre_properties)
- {
- $text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
- }
- else if ($this->mbstring_regex)
- {
- $text = mb_ereg_replace('([^\w\'*])', '\\1\\1', str_replace('\'\'', '\' \'', trim($text)));
- }
- else
- {
- $text = preg_replace('#([^\w\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
- }
-
- if ($this->pcre_properties)
- {
- $matches = array();
- preg_match_all('#(?:[^\p{L}\p{N}*]|^)([+\-|]?(?:[\p{L}\p{N}*]+\'?)*[\p{L}\p{N}*])(?:[^\p{L}\p{N}*]|$)#u', $text, $matches);
- $text = $matches[1];
- }
- else if ($this->mbstring_regex)
- {
- mb_ereg_search_init($text, '(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)');
-
- $text = array();
- while (($word = mb_ereg_search_regs()))
- {
- $text[] = $word[1];
- }
- }
- else
- {
- $matches = array();
- preg_match_all('#(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)#u', $text, $matches);
- $text = $matches[1];
- }
+ $text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
+ $matches = array();
+ preg_match_all('#(?:[^\p{L}\p{N}*]|^)([+\-|]?(?:[\p{L}\p{N}*]+\'?)*[\p{L}\p{N}*])(?:[^\p{L}\p{N}*]|$)#u', $text, $matches);
+ $text = $matches[1];
// remove too short or too long words
$text = array_values($text);
@@ -748,7 +673,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
{
if ($db->sql_layer == 'mysqli' || version_compare($db->sql_server_info(true), '4.1.3', '>='))
{
- //$alter[] = 'MODIFY post_subject varchar(100) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL';
+ $alter[] = 'MODIFY post_subject varchar(255) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL';
}
else
{
@@ -909,14 +834,6 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$tpl = '
<dl>
- <dt><label>' . $user->lang['FULLTEXT_MYSQL_PCRE'] . '</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_PCRE_EXPLAIN'] . '</span></dt>
- <dd>' . (($this->pcre_properties) ? $user->lang['YES'] : $user->lang['NO']) . ' (PHP ' . PHP_VERSION . ')</dd>
- </dl>
- <dl>
- <dt><label>' . $user->lang['FULLTEXT_MYSQL_MBSTRING'] . '</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_MBSTRING_EXPLAIN'] . '</span></dt>
- <dd>' . (($this->mbstring_regex) ? $user->lang['YES'] : $user->lang['NO']). '</dd>
- </dl>
- <dl>
<dt><label>' . $user->lang['MIN_SEARCH_CHARS'] . ':</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_MIN_SEARCH_CHARS_EXPLAIN'] . '</span></dt>
<dd>' . $config['fulltext_mysql_min_word_len'] . '</dd>
</dl>
diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php
new file mode 100644
index 0000000000..84ce674564
--- /dev/null
+++ b/phpBB/includes/search/fulltext_postgres.php
@@ -0,0 +1,856 @@
+<?php
+/**
+*
+* @package search
+* @copyright (c) 2005 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* fulltext_postgres
+* Fulltext search for PostgreSQL
+* @package search
+*/
+class phpbb_search_fulltext_postgres extends phpbb_search_base
+{
+ private $stats = array();
+ private $split_words = array();
+ private $tsearch_usable = false;
+ private $version;
+ private $tsearch_query;
+ private $phrase_search = false;
+ public $search_query;
+ public $common_words = array();
+ public $word_length = array();
+
+ /**
+ * Constructor
+ * Creates a new phpbb_search_fulltext_postgres, which is used as a search backend.
+ *
+ * @param string|bool $error Any error that occurs is passed on through this reference variable otherwise false
+ */
+ public function __construct(&$error)
+ {
+ global $db, $config;
+
+ $this->word_length = array('min' => $config['fulltext_postgres_min_word_len'], 'max' => $config['fulltext_postgres_max_word_len']);
+
+
+ if ($db->sql_layer == 'postgres')
+ {
+ $pgsql_version = explode(',', substr($db->sql_server_info(), 10));
+ $this->version = trim($pgsql_version[0]);
+ if (version_compare($this->version, '8.3', '>='))
+ {
+ $this->tsearch_usable = true;
+ }
+ }
+
+ $error = false;
+ }
+
+ /**
+ * Returns the name of this search backend to be displayed to administrators
+ *
+ * @return string Name
+ *
+ * @access public
+ */
+ public function get_name()
+ {
+ return 'PostgreSQL Fulltext';
+ }
+
+ /**
+ * Returns if phrase search is supported or not
+ *
+ * @return bool
+ *
+ * @access public
+ */
+ public function supports_phrase_search()
+ {
+ return $this->phrase_search;
+ }
+
+ /**
+ * Checks for correct PostgreSQL version and stores min/max word length in the config
+ *
+ * @return string|bool Language key of the error/incompatiblity occured
+ *
+ * @access public
+ */
+ function init()
+ {
+ global $db, $user;
+
+ if ($db->sql_layer != 'postgres')
+ {
+ return $user->lang['FULLTEXT_POSTGRES_INCOMPATIBLE_DATABASE'];
+ }
+
+ if (!$this->tsearch_usable)
+ {
+ return $user->lang['FULLTEXT_POSTGRES_TS_NOT_USABLE'];
+ }
+
+ return false;
+ }
+
+ /**
+ * Splits keywords entered by a user into an array of words stored in $this->split_words
+ * Stores the tidied search query in $this->search_query
+ *
+ * @param string &$keywords Contains the keyword as entered by the user
+ * @param string $terms is either 'all' or 'any'
+ * @return bool false if no valid keywords were found and otherwise true
+ *
+ * @access public
+ */
+ function split_keywords(&$keywords, $terms)
+ {
+ global $config;
+
+ if ($terms == 'all')
+ {
+ $match = array('#\sand\s#iu', '#\sor\s#iu', '#\snot\s#iu', '#\+#', '#-#', '#\|#');
+ $replace = array(' +', ' |', ' -', ' +', ' -', ' |');
+
+ $keywords = preg_replace($match, $replace, $keywords);
+ }
+
+ // Filter out as above
+ $split_keywords = preg_replace("#[\"\n\r\t]+#", ' ', trim(htmlspecialchars_decode($keywords)));
+
+ // Split words
+ $split_keywords = preg_replace('#([^\p{L}\p{N}\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
+ $matches = array();
+ preg_match_all('#(?:[^\p{L}\p{N}*"()]|^)([+\-|]?(?:[\p{L}\p{N}*"()]+\'?)*[\p{L}\p{N}*"()])(?:[^\p{L}\p{N}*"()]|$)#u', $split_keywords, $matches);
+ $this->split_words = $matches[1];
+
+ foreach ($this->split_words as $i => $word)
+ {
+ $clean_word = preg_replace('#^[+\-|"]#', '', $word);
+
+ // check word length
+ $clean_len = utf8_strlen(str_replace('*', '', $clean_word));
+ if (($clean_len < $config['fulltext_postgres_min_word_len']) || ($clean_len > $config['fulltext_postgres_max_word_len']))
+ {
+ $this->common_words[] = $word;
+ unset($this->split_words[$i]);
+ }
+ }
+
+ if ($terms == 'any')
+ {
+ $this->search_query = '';
+ $this->tsearch_query = '';
+ foreach ($this->split_words as $word)
+ {
+ if ((strpos($word, '+') === 0) || (strpos($word, '-') === 0) || (strpos($word, '|') === 0))
+ {
+ $word = substr($word, 1);
+ }
+ $this->search_query .= $word . ' ';
+ $this->tsearch_query .= '|' . $word . ' ';
+ }
+ }
+ else
+ {
+ $this->search_query = '';
+ $this->tsearch_query = '';
+ foreach ($this->split_words as $word)
+ {
+ if (strpos($word, '+') === 0)
+ {
+ $this->search_query .= $word . ' ';
+ $this->tsearch_query .= '&' . substr($word, 1) . ' ';
+ }
+ elseif (strpos($word, '-') === 0)
+ {
+ $this->search_query .= $word . ' ';
+ $this->tsearch_query .= '&!' . substr($word, 1) . ' ';
+ }
+ elseif (strpos($word, '|') === 0)
+ {
+ $this->search_query .= $word . ' ';
+ $this->tsearch_query .= '|' . substr($word, 1) . ' ';
+ }
+ else
+ {
+ $this->search_query .= '+' . $word . ' ';
+ $this->tsearch_query .= '&' . $word . ' ';
+ }
+ }
+ }
+
+ $this->tsearch_query = substr($this->tsearch_query, 1);
+ $this->search_query = utf8_htmlspecialchars($this->search_query);
+
+ if ($this->search_query)
+ {
+ $this->split_words = array_values($this->split_words);
+ sort($this->split_words);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Turns text into an array of words
+ * @param string $text contains post text/subject
+ *
+ * @access public
+ */
+ function split_message($text)
+ {
+ global $config;
+
+ // Split words
+ $text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
+ $matches = array();
+ preg_match_all('#(?:[^\p{L}\p{N}*]|^)([+\-|]?(?:[\p{L}\p{N}*]+\'?)*[\p{L}\p{N}*])(?:[^\p{L}\p{N}*]|$)#u', $text, $matches);
+ $text = $matches[1];
+
+ // remove too short or too long words
+ $text = array_values($text);
+ for ($i = 0, $n = sizeof($text); $i < $n; $i++)
+ {
+ $text[$i] = trim($text[$i]);
+ if (utf8_strlen($text[$i]) < $config['fulltext_postgres_min_word_len'] || utf8_strlen($text[$i]) > $config['fulltext_postgres_max_word_len'])
+ {
+ unset($text[$i]);
+ }
+ }
+
+ return array_values($text);
+ }
+
+ /**
+ * Performs a search on keywords depending on display specific params. You have to run split_keywords() first.
+ *
+ * @param string $type contains either posts or topics depending on what should be searched for
+ * @param string $fields contains either titleonly (topic titles should be searched), msgonly (only message bodies should be searched), firstpost (only subject and body of the first post should be searched) or all (all post bodies and subjects should be searched)
+ * @param string $terms is either 'all' (use query as entered, words without prefix should default to "have to be in field") or 'any' (ignore search query parts and just return all posts that contain any of the specified words)
+ * @param array $sort_by_sql contains SQL code for the ORDER BY part of a query
+ * @param string $sort_key is the key of $sort_by_sql for the selected sorting
+ * @param string $sort_dir is either a or d representing ASC and DESC
+ * @param string $sort_days specifies the maximum amount of days a post may be old
+ * @param array $ex_fid_ary specifies an array of forum ids which should not be searched
+ * @param array $m_approve_fid_ary specifies an array of forum ids in which the searcher is allowed to view unapproved posts
+ * @param int $topic_id is set to 0 or a topic id, if it is not 0 then only posts in this topic should be searched
+ * @param array $author_ary an array of author ids if the author should be ignored during the search the array is empty
+ * @param string $author_name specifies the author match, when ANONYMOUS is also a search-match
+ * @param array &$id_ary passed by reference, to be filled with ids for the page specified by $start and $per_page, should be ordered
+ * @param int $start indicates the first index of the page
+ * @param int $per_page number of ids each page is supposed to contain
+ * @return boolean|int total number of results
+ *
+ * @access public
+ */
+ function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ {
+ global $config, $db;
+
+ // No keywords? No posts.
+ if (!$this->search_query)
+ {
+ return false;
+ }
+
+ // generate a search_key from all the options to identify the results
+ $search_key = md5(implode('#', array(
+ implode(', ', $this->split_words),
+ $type,
+ $fields,
+ $terms,
+ $sort_days,
+ $sort_key,
+ $topic_id,
+ implode(',', $ex_fid_ary),
+ implode(',', $m_approve_fid_ary),
+ implode(',', $author_ary)
+ )));
+
+ // try reading the results from cache
+ $result_count = 0;
+ if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
+ {
+ return $result_count;
+ }
+
+ $id_ary = array();
+
+ $join_topic = ($type == 'posts') ? false : true;
+
+ // Build sql strings for sorting
+ $sql_sort = $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
+ $sql_sort_table = $sql_sort_join = '';
+
+ switch ($sql_sort[0])
+ {
+ case 'u':
+ $sql_sort_table = USERS_TABLE . ' u, ';
+ $sql_sort_join = ($type == 'posts') ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ';
+ break;
+
+ case 't':
+ $join_topic = true;
+ break;
+
+ case 'f':
+ $sql_sort_table = FORUMS_TABLE . ' f, ';
+ $sql_sort_join = ' AND f.forum_id = p.forum_id ';
+ break;
+ }
+
+ // Build some display specific sql strings
+ switch ($fields)
+ {
+ case 'titleonly':
+ $sql_match = 'p.post_subject';
+ $sql_match_where = ' AND p.post_id = t.topic_first_post_id';
+ $join_topic = true;
+ break;
+
+ case 'msgonly':
+ $sql_match = 'p.post_text';
+ $sql_match_where = '';
+ break;
+
+ case 'firstpost':
+ $sql_match = 'p.post_subject, p.post_text';
+ $sql_match_where = ' AND p.post_id = t.topic_first_post_id';
+ $join_topic = true;
+ break;
+
+ default:
+ $sql_match = 'p.post_subject, p.post_text';
+ $sql_match_where = '';
+ break;
+ }
+
+ if (!sizeof($m_approve_fid_ary))
+ {
+ $m_approve_fid_sql = ' AND p.post_approved = 1';
+ }
+ else if ($m_approve_fid_ary === array(-1))
+ {
+ $m_approve_fid_sql = '';
+ }
+ else
+ {
+ $m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db->sql_in_set('p.forum_id', $m_approve_fid_ary, true) . ')';
+ }
+
+ $sql_select = ($type == 'posts') ? 'p.post_id' : 'DISTINCT t.topic_id';
+ $sql_from = ($join_topic) ? TOPICS_TABLE . ' t, ' : '';
+ $field = ($type == 'posts') ? 'post_id' : 'topic_id';
+ $sql_author = (sizeof($author_ary) == 1) ? ' = ' . $author_ary[0] : 'IN (' . implode(', ', $author_ary) . ')';
+
+ if (sizeof($author_ary) && $author_name)
+ {
+ // first one matches post of registered users, second one guests and deleted users
+ $sql_author = '(' . $db->sql_in_set('p.poster_id', array_diff($author_ary, array(ANONYMOUS)), false, true) . ' OR p.post_username ' . $author_name . ')';
+ }
+ else if (sizeof($author_ary))
+ {
+ $sql_author = ' AND ' . $db->sql_in_set('p.poster_id', $author_ary);
+ }
+ else
+ {
+ $sql_author = '';
+ }
+
+ $sql_where_options = $sql_sort_join;
+ $sql_where_options .= ($topic_id) ? ' AND p.topic_id = ' . $topic_id : '';
+ $sql_where_options .= ($join_topic) ? ' AND t.topic_id = p.topic_id' : '';
+ $sql_where_options .= (sizeof($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '';
+ $sql_where_options .= $m_approve_fid_sql;
+ $sql_where_options .= $sql_author;
+ $sql_where_options .= ($sort_days) ? ' AND p.post_time >= ' . (time() - ($sort_days * 86400)) : '';
+ $sql_where_options .= $sql_match_where;
+
+ $tmp_sql_match = array();
+ foreach (explode(',', $sql_match) as $sql_match_column)
+ {
+ $tmp_sql_match[] = "to_tsvector ('" . $db->sql_escape($config['fulltext_postgres_ts_name']) . "', " . $sql_match_column . ") @@ to_tsquery ('" . $db->sql_escape($config['fulltext_postgres_ts_name']) . "', '" . $db->sql_escape($this->tsearch_query) . "')";
+ }
+
+ $sql = "SELECT $sql_select
+ FROM $sql_from$sql_sort_table" . POSTS_TABLE . " p
+ WHERE (" . implode(' OR ', $tmp_sql_match) . ")
+ $sql_where_options
+ ORDER BY $sql_sort";
+ $result = $db->sql_query_limit($sql, $config['search_block_size'], $start);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $id_ary[] = $row[$field];
+ }
+ $db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+
+ if (!sizeof($id_ary))
+ {
+ return false;
+ }
+
+ // if the total result count is not cached yet, retrieve it from the db
+ if (!$result_count)
+ {
+ $result_count = sizeof ($id_ary);
+
+ if (!$result_count)
+ {
+ return false;
+ }
+ }
+
+ // store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
+ $this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
+ $id_ary = array_slice($id_ary, 0, (int) $per_page);
+
+ return $result_count;
+ }
+
+ /**
+ * Performs a search on an author's posts without caring about message contents. Depends on display specific params
+ *
+ * @param string $type contains either posts or topics depending on what should be searched for
+ * @param boolean $firstpost_only if true, only topic starting posts will be considered
+ * @param array $sort_by_sql contains SQL code for the ORDER BY part of a query
+ * @param string $sort_key is the key of $sort_by_sql for the selected sorting
+ * @param string $sort_dir is either a or d representing ASC and DESC
+ * @param string $sort_days specifies the maximum amount of days a post may be old
+ * @param array $ex_fid_ary specifies an array of forum ids which should not be searched
+ * @param array $m_approve_fid_ary specifies an array of forum ids in which the searcher is allowed to view unapproved posts
+ * @param int $topic_id is set to 0 or a topic id, if it is not 0 then only posts in this topic should be searched
+ * @param array $author_ary an array of author ids
+ * @param string $author_name specifies the author match, when ANONYMOUS is also a search-match
+ * @param array &$id_ary passed by reference, to be filled with ids for the page specified by $start and $per_page, should be ordered
+ * @param int $start indicates the first index of the page
+ * @param int $per_page number of ids each page is supposed to contain
+ * @return boolean|int total number of results
+ *
+ * @access public
+ */
+ function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ {
+ global $config, $db;
+
+ // No author? No posts.
+ if (!sizeof($author_ary))
+ {
+ return 0;
+ }
+
+ // generate a search_key from all the options to identify the results
+ $search_key = md5(implode('#', array(
+ '',
+ $type,
+ ($firstpost_only) ? 'firstpost' : '',
+ '',
+ '',
+ $sort_days,
+ $sort_key,
+ $topic_id,
+ implode(',', $ex_fid_ary),
+ implode(',', $m_approve_fid_ary),
+ implode(',', $author_ary),
+ $author_name,
+ )));
+
+ // try reading the results from cache
+ $result_count = 0;
+ if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
+ {
+ return $result_count;
+ }
+
+ $id_ary = array();
+
+ // Create some display specific sql strings
+ if ($author_name)
+ {
+ // first one matches post of registered users, second one guests and deleted users
+ $sql_author = '(' . $db->sql_in_set('p.poster_id', array_diff($author_ary, array(ANONYMOUS)), false, true) . ' OR p.post_username ' . $author_name . ')';
+ }
+ else
+ {
+ $sql_author = $db->sql_in_set('p.poster_id', $author_ary);
+ }
+ $sql_fora = (sizeof($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '';
+ $sql_topic_id = ($topic_id) ? ' AND p.topic_id = ' . (int) $topic_id : '';
+ $sql_time = ($sort_days) ? ' AND p.post_time >= ' . (time() - ($sort_days * 86400)) : '';
+ $sql_firstpost = ($firstpost_only) ? ' AND p.post_id = t.topic_first_post_id' : '';
+
+ // Build sql strings for sorting
+ $sql_sort = $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
+ $sql_sort_table = $sql_sort_join = '';
+ switch ($sql_sort[0])
+ {
+ case 'u':
+ $sql_sort_table = USERS_TABLE . ' u, ';
+ $sql_sort_join = ($type == 'posts') ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ';
+ break;
+
+ case 't':
+ $sql_sort_table = ($type == 'posts' && !$firstpost_only) ? TOPICS_TABLE . ' t, ' : '';
+ $sql_sort_join = ($type == 'posts' && !$firstpost_only) ? ' AND t.topic_id = p.topic_id ' : '';
+ break;
+
+ case 'f':
+ $sql_sort_table = FORUMS_TABLE . ' f, ';
+ $sql_sort_join = ' AND f.forum_id = p.forum_id ';
+ break;
+ }
+
+ if (!sizeof($m_approve_fid_ary))
+ {
+ $m_approve_fid_sql = ' AND p.post_approved = 1';
+ }
+ else if ($m_approve_fid_ary == array(-1))
+ {
+ $m_approve_fid_sql = '';
+ }
+ else
+ {
+ $m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db->sql_in_set('p.forum_id', $m_approve_fid_ary, true) . ')';
+ }
+
+ // Build the query for really selecting the post_ids
+ if ($type == 'posts')
+ {
+ $sql = "SELECT p.post_id
+ FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (($firstpost_only) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
+ WHERE $sql_author
+ $sql_topic_id
+ $sql_firstpost
+ $m_approve_fid_sql
+ $sql_fora
+ $sql_sort_join
+ $sql_time
+ ORDER BY $sql_sort";
+ $field = 'post_id';
+ }
+ else
+ {
+ $sql = "SELECT t.topic_id
+ FROM " . $sql_sort_table . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
+ WHERE $sql_author
+ $sql_topic_id
+ $sql_firstpost
+ $m_approve_fid_sql
+ $sql_fora
+ AND t.topic_id = p.topic_id
+ $sql_sort_join
+ $sql_time
+ GROUP BY t.topic_id, $sort_by_sql[$sort_key]
+ ORDER BY $sql_sort";
+ $field = 'topic_id';
+ }
+
+ // Only read one block of posts from the db and then cache it
+ $result = $db->sql_query_limit($sql, $config['search_block_size'], $start);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $id_ary[] = $row[$field];
+ }
+ $db->sql_freeresult($result);
+
+ // retrieve the total result count if needed
+ if (!$result_count)
+ {
+ $result_count = sizeof ($id_ary);
+
+ if (!$result_count)
+ {
+ return false;
+ }
+ }
+
+ if (sizeof($id_ary))
+ {
+ $this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
+ $id_ary = array_slice($id_ary, 0, $per_page);
+
+ return $result_count;
+ }
+ return false;
+ }
+
+ /**
+ * Destroys cached search results, that contained one of the new words in a post so the results won't be outdated.
+ *
+ * @param string $mode contains the post mode: edit, post, reply, quote ...
+ * @param int $post_id contains the post id of the post to index
+ * @param string $message contains the post text of the post
+ * @param string $subject contains the subject of the post to index
+ * @param int $poster_id contains the user id of the poster
+ * @param int $forum_id contains the forum id of parent forum of the post
+ *
+ * @access public
+ */
+ function index($mode, $post_id, &$message, &$subject, $poster_id, $forum_id)
+ {
+ global $db;
+
+ // Split old and new post/subject to obtain array of words
+ $split_text = $this->split_message($message);
+ $split_title = ($subject) ? $this->split_message($subject) : array();
+
+ $words = array_unique(array_merge($split_text, $split_title));
+
+ unset($split_text);
+ unset($split_title);
+
+ // destroy cached search results containing any of the words removed or added
+ $this->destroy_cache($words, array($poster_id));
+
+ unset($words);
+ }
+
+ /**
+ * Destroy cached results, that might be outdated after deleting a post
+ *
+ * @access public
+ */
+ function index_remove($post_ids, $author_ids, $forum_ids)
+ {
+ $this->destroy_cache(array(), $author_ids);
+ }
+
+ /**
+ * Destroy old cache entries
+ *
+ * @access public
+ */
+ function tidy()
+ {
+ global $db, $config;
+
+ // destroy too old cached search results
+ $this->destroy_cache(array());
+
+ set_config('search_last_gc', time(), true);
+ }
+
+ /**
+ * Create fulltext index
+ *
+ * @return string|bool error string is returned incase of errors otherwise false
+ *
+ * @access public
+ */
+ function create_index($acp_module, $u_action)
+ {
+ global $db, $config;
+
+ // Make sure we can actually use PostgreSQL with fulltext indexes
+ if ($error = $this->init())
+ {
+ return $error;
+ }
+
+ if (empty($this->stats))
+ {
+ $this->get_stats();
+ }
+
+ if (!isset($this->stats['post_subject']))
+ {
+ $db->sql_query("CREATE INDEX " . POSTS_TABLE . "_" . $config['fulltext_postgres_ts_name'] . "_post_subject ON " . POSTS_TABLE . " USING gin (to_tsvector ('" . $db->sql_escape($config['fulltext_postgres_ts_name']) . "', post_subject))");
+ }
+
+ if (!isset($this->stats['post_text']))
+ {
+ $db->sql_query("CREATE INDEX " . POSTS_TABLE . "_" . $config['fulltext_postgres_ts_name'] . "_post_text ON " . POSTS_TABLE . " USING gin (to_tsvector ('" . $db->sql_escape($config['fulltext_postgres_ts_name']) . "', post_text))");
+ }
+
+ $db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
+
+ return false;
+ }
+
+ /**
+ * Drop fulltext index
+ *
+ * @return string|bool error string is returned incase of errors otherwise false
+ *
+ * @access public
+ */
+ function delete_index($acp_module, $u_action)
+ {
+ global $db;
+
+ // Make sure we can actually use PostgreSQL with fulltext indexes
+ if ($error = $this->init())
+ {
+ return $error;
+ }
+
+ if (empty($this->stats))
+ {
+ $this->get_stats();
+ }
+
+ if (isset($this->stats['post_subject']))
+ {
+ $db->sql_query('DROP INDEX ' . $this->stats['post_subject']['relname']);
+ }
+
+ if (isset($this->stats['post_text']))
+ {
+ $db->sql_query('DROP INDEX ' . $this->stats['post_text']['relname']);
+ }
+
+ $db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
+
+ return false;
+ }
+
+ /**
+ * Returns true if both FULLTEXT indexes exist
+ *
+ * @access public
+ */
+ function index_created()
+ {
+ if (empty($this->stats))
+ {
+ $this->get_stats();
+ }
+
+ return (isset($this->stats['post_text']) && isset($this->stats['post_subject'])) ? true : false;
+ }
+
+ /**
+ * Returns an associative array containing information about the indexes
+ *
+ * @access public
+ */
+ function index_stats()
+ {
+ global $user;
+
+ if (empty($this->stats))
+ {
+ $this->get_stats();
+ }
+
+ return array(
+ $user->lang['FULLTEXT_POSTGRES_TOTAL_POSTS'] => ($this->index_created()) ? $this->stats['total_posts'] : 0,
+ );
+ }
+
+ /**
+ * Computes the stats and store them in the $this->stats associative array
+ *
+ * @access private
+ */
+ function get_stats()
+ {
+ global $db, $config;
+
+ if ($db->sql_layer != 'postgres')
+ {
+ $this->stats = array();
+ return;
+ }
+
+ $sql = "SELECT c2.relname, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS indexdef
+ FROM pg_catalog.pg_class c1, pg_catalog.pg_index i, pg_catalog.pg_class c2
+ WHERE c1.relname = '" . POSTS_TABLE . "'
+ AND pg_catalog.pg_table_is_visible(c1.oid)
+ AND c1.oid = i.indrelid
+ AND i.indexrelid = c2.oid";
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ // deal with older PostgreSQL versions which didn't use Index_type
+ if (strpos($row['indexdef'], 'to_tsvector') !== false)
+ {
+ if ($row['relname'] == POSTS_TABLE . '_' . $config['fulltext_postgres_ts_name'] . '_post_text' || $row['relname'] == POSTS_TABLE . '_post_text')
+ {
+ $this->stats['post_text'] = $row;
+ }
+ else if ($row['relname'] == POSTS_TABLE . '_' . $config['fulltext_postgres_ts_name'] . '_post_subject' || $row['relname'] == POSTS_TABLE . '_post_subject')
+ {
+ $this->stats['post_subject'] = $row;
+ }
+ }
+ }
+ $db->sql_freeresult($result);
+
+ $this->stats['total_posts'] = $config['num_posts'];
+ }
+
+ /**
+ * Display various options that can be configured for the backend from the acp
+ *
+ * @return associative array containing template and config variables
+ *
+ * @access public
+ */
+ function acp()
+ {
+ global $user, $config, $db;
+
+ $tpl = '
+ <dl>
+ <dt><label>' . $user->lang['FULLTEXT_POSTGRES_VERSION_CHECK'] . '</label><br /><span>' . $user->lang['FULLTEXT_POSTGRES_VERSION_CHECK_EXPLAIN'] . '</span></dt>
+ <dd>' . (($this->tsearch_usable) ? $user->lang['YES'] : $user->lang['NO']) . ' (PostgreSQL ' . $this->version . ')</dd>
+ </dl>
+ <dl>
+ <dt><label>' . $user->lang['FULLTEXT_POSTGRES_TS_NAME'] . '</label><br /><span>' . $user->lang['FULLTEXT_POSTGRES_TS_NAME_EXPLAIN'] . '</span></dt>
+ <dd><select name="config[fulltext_postgres_ts_name]">';
+
+ if ($db->sql_layer == 'postgres' && $this->tsearch_usable)
+ {
+ $sql = 'SELECT cfgname AS ts_name
+ FROM pg_ts_config';
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $tpl .= '<option value="' . $row['ts_name'] . '"' . ($row['ts_name'] === $config['fulltext_postgres_ts_name'] ? ' selected="selected"' : '') . '>' . $row['ts_name'] . '</option>';
+ }
+ $db->sql_freeresult($result);
+ }
+ else
+ {
+ $tpl .= '<option value="' . $config['fulltext_postgres_ts_name'] . '" selected="selected">' . $config['fulltext_postgres_ts_name'] . '</option>';
+ }
+
+ $tpl .= '</select></dd>
+ </dl>
+ <dl>
+ <dt><label for="fulltext_postgres_min_word_len">' . $user->lang['FULLTEXT_POSTGRES_MIN_WORD_LEN'] . ':</label><br /><span>' . $user->lang['FULLTEXT_POSTGRES_MIN_WORD_LEN_EXPLAIN'] . '</span></dt>
+ <dd><input id="fulltext_postgres_min_word_len" type="text" size="3" maxlength="3" name="config[fulltext_postgres_min_word_len]" value="' . (int) $config['fulltext_postgres_min_word_len'] . '" /></dd>
+ </dl>
+ <dl>
+ <dt><label for="fulltext_postgres_max_word_len">' . $user->lang['FULLTEXT_POSTGRES_MAX_WORD_LEN'] . ':</label><br /><span>' . $user->lang['FULLTEXT_POSTGRES_MAX_WORD_LEN_EXPLAIN'] . '</span></dt>
+ <dd><input id="fulltext_postgres_max_word_len" type="text" size="3" maxlength="3" name="config[fulltext_postgres_max_word_len]" value="' . (int) $config['fulltext_postgres_max_word_len'] . '" /></dd>
+ </dl>
+ ';
+
+ // These are fields required in the config table
+ return array(
+ 'tpl' => $tpl,
+ 'config' => array('fulltext_postgres_ts_name' => 'string', 'fulltext_postgres_min_word_len' => 'integer:0:255', 'fulltext_postgres_max_word_len' => 'integer:0:255')
+ );
+ }
+}
diff --git a/phpBB/includes/style/style.php b/phpBB/includes/style/style.php
index 3f470015f6..22e0f1d67a 100644
--- a/phpBB/includes/style/style.php
+++ b/phpBB/includes/style/style.php
@@ -89,9 +89,9 @@ class phpbb_style
*/
public function set_style()
{
- $style_name = $this->user->theme['style_path'];
- $style_dirs = ($this->user->theme['style_parent_id']) ? array_reverse(explode('/', $this->user->theme['style_parent_tree'])) : array();
- $paths = array($this->get_style_path($style_name));
+ $style_path = $this->user->style['style_path'];
+ $style_dirs = ($this->user->style['style_parent_id']) ? array_reverse(explode('/', $this->user->style['style_parent_tree'])) : array();
+ $paths = array($this->get_style_path($style_path));
foreach ($style_dirs as $dir)
{
$paths[] = $this->get_style_path($dir);
@@ -100,7 +100,7 @@ class phpbb_style
// Add 'all' path, used as last fallback path by hooks and extensions
$paths[] = $this->get_style_path('all');
- return $this->set_custom_style($style_name, $paths);
+ return $this->set_custom_style($style_path, $paths);
}
/**
diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php
index 4a2593b757..ad2e35de6a 100644
--- a/phpBB/includes/template/filter.php
+++ b/phpBB/includes/template/filter.php
@@ -905,12 +905,12 @@ class phpbb_template_filter extends php_user_filter
if (substr($filename, 0, strlen($this->phpbb_root_path)) != $this->phpbb_root_path)
{
// Absolute path, include as is
- return ' $_template->_js_include(\'' . addslashes($filename) . '\', false); ';
+ return ' $_template->_js_include(\'' . addslashes($filename) . '\', false, false); ';
}
// Relative path, remove root path from it
$filename = substr($filename, strlen($this->phpbb_root_path));
- return ' global $phpbb_root_path; $_template->_js_include($phpbb_root_path . \'' . addslashes($filename) . '\', false); ';
+ return ' $_template->_js_include(\'' . addslashes($filename) . '\', false, true); ';
}
/**
diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php
index e6512c8417..8ab3c44be3 100644
--- a/phpBB/includes/template/template.php
+++ b/phpBB/includes/template/template.php
@@ -496,14 +496,19 @@ class phpbb_template
*
* @param string $file file name
* @param bool $locate True if file needs to be located
+ * @param bool $relative True if path is relative to phpBB root directory. Ignored if $locate == true
*/
- public function _js_include($file, $locate = false)
+ public function _js_include($file, $locate = false, $relative = false)
{
// Locate file
if ($locate)
{
$file = $this->locator->get_first_file_location(array($file), true, true);
}
+ else if ($relative)
+ {
+ $file = $this->phpbb_root_path . $file;
+ }
$file .= (strpos($file, '?') === false) ? '?' : '&';
$file .= 'assets_version=' . $this->config['assets_version'];
diff --git a/phpBB/includes/ucp/info/ucp_profile.php b/phpBB/includes/ucp/info/ucp_profile.php
index 09c0318de9..968538a178 100644
--- a/phpBB/includes/ucp/info/ucp_profile.php
+++ b/phpBB/includes/ucp/info/ucp_profile.php
@@ -23,6 +23,7 @@ class ucp_profile_info
'signature' => array('title' => 'UCP_PROFILE_SIGNATURE', 'auth' => '', 'cat' => array('UCP_PROFILE')),
'avatar' => array('title' => 'UCP_PROFILE_AVATAR', 'auth' => 'cfg_allow_avatar && (cfg_allow_avatar_local || cfg_allow_avatar_remote || cfg_allow_avatar_upload || cfg_allow_avatar_remote_upload)', 'cat' => array('UCP_PROFILE')),
'reg_details' => array('title' => 'UCP_PROFILE_REG_DETAILS', 'auth' => '', 'cat' => array('UCP_PROFILE')),
+ 'autologin_keys'=> array('title' => 'UCP_PROFILE_AUTOLOGIN_KEYS', 'auth' => '', 'cat' => array('UCP_PROFILE')),
),
);
}
diff --git a/phpBB/includes/ucp/ucp_pm_options.php b/phpBB/includes/ucp/ucp_pm_options.php
index bde5d1dfcf..bf7334b307 100644
--- a/phpBB/includes/ucp/ucp_pm_options.php
+++ b/phpBB/includes/ucp/ucp_pm_options.php
@@ -327,10 +327,23 @@ function message_options($id, $mode, $global_privmsgs_rules, $global_rule_condit
trigger_error('RULE_ALREADY_DEFINED');
}
+ // Prevent users from flooding the rules table
+ $sql = 'SELECT COUNT(rule_id) AS num_rules
+ FROM ' . PRIVMSGS_RULES_TABLE . '
+ WHERE user_id = ' . (int) $user->data['user_id'];
+ $result = $db->sql_query($sql);
+ $num_rules = (int) $db->sql_fetchfield('num_rules');
+ $db->sql_freeresult($result);
+
+ if ($num_rules >= 5000)
+ {
+ trigger_error('RULE_LIMIT_REACHED');
+ }
+
$sql = 'INSERT INTO ' . PRIVMSGS_RULES_TABLE . ' ' . $db->sql_build_array('INSERT', $rule_ary);
$db->sql_query($sql);
- // Update users message rules
+ // Set the user_message_rules bit
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_message_rules = 1
WHERE user_id = ' . $user->data['user_id'];
@@ -377,7 +390,7 @@ function message_options($id, $mode, $global_privmsgs_rules, $global_rule_condit
$row = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
- // Update users message rules
+ // Unset the user_message_rules bit
if (!$row)
{
$sql = 'UPDATE ' . USERS_TABLE . '
diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php
index 9d81503f0a..2ac82fb52f 100644
--- a/phpBB/includes/ucp/ucp_profile.php
+++ b/phpBB/includes/ucp/ucp_profile.php
@@ -618,6 +618,60 @@ class ucp_profile
}
break;
+
+ case 'autologin_keys':
+
+ add_form_key('ucp_autologin_keys');
+
+ if ($submit)
+ {
+ $keys = request_var('keys', array(''));
+
+ if (!check_form_key('ucp_autologin_keys'))
+ {
+ $error[] = 'FORM_INVALID';
+ }
+
+ if (!sizeof($error))
+ {
+ if (!empty($keys))
+ {
+ $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
+ WHERE user_id = ' . (int) $user->data['user_id'] . '
+ AND ' . $db->sql_in_set('key_id', $keys) ;
+
+ $db->sql_query($sql);
+
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang['AUTOLOGIN_SESSION_KEYS_DELETED'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ trigger_error($message);
+ }
+ }
+
+ // Replace "error" strings with their real, localised form
+ $error = array_map(array($user, 'lang'), $error);
+ }
+
+ $sql = 'SELECT key_id, last_ip, last_login
+ FROM ' . SESSIONS_KEYS_TABLE . '
+ WHERE user_id = ' . (int) $user->data['user_id'];
+
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $template->assign_block_vars('sessions', array(
+ 'errors' => $error,
+
+ 'KEY' => $row['key_id'],
+ 'IP' => $row['last_ip'],
+ 'LOGIN_TIME' => $user->format_date($row['last_login']),
+ ));
+ }
+
+ $db->sql_freeresult($result);
+
+ break;
}
$template->assign_vars(array(
diff --git a/phpBB/includes/user.php b/phpBB/includes/user.php
index 7dfb375a9b..fb6c1552e8 100644
--- a/phpBB/includes/user.php
+++ b/phpBB/includes/user.php
@@ -27,7 +27,7 @@ class phpbb_user extends phpbb_session
{
var $lang = array();
var $help = array();
- var $theme = array();
+ var $style = array();
var $date_format;
/**
@@ -168,11 +168,11 @@ class phpbb_user extends phpbb_session
FROM ' . STYLES_TABLE . " s
WHERE s.style_id = $style_id";
$result = $db->sql_query($sql, 3600);
- $this->theme = $db->sql_fetchrow($result);
+ $this->style = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
// User has wrong style
- if (!$this->theme && $style_id == $this->data['user_style'])
+ if (!$this->style && $style_id == $this->data['user_style'])
{
$style_id = $this->data['user_style'] = $config['default_style'];
@@ -185,20 +185,17 @@ class phpbb_user extends phpbb_session
FROM ' . STYLES_TABLE . " s
WHERE s.style_id = $style_id";
$result = $db->sql_query($sql, 3600);
- $this->theme = $db->sql_fetchrow($result);
+ $this->style = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
}
- if (!$this->theme)
+ if (!$this->style)
{
trigger_error('Could not get style data', E_USER_ERROR);
}
// Now parse the cfg file and cache it
- $parsed_items = $cache->obtain_cfg_items($this->theme);
-
- // We are only interested in the theme configuration for now
- $parsed_items = $parsed_items['theme'];
+ $parsed_items = $cache->obtain_cfg_items($this->style);
$check_for = array(
'pagination_sep' => (string) ', '
@@ -206,12 +203,12 @@ class phpbb_user extends phpbb_session
foreach ($check_for as $key => $default_value)
{
- $this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value;
- settype($this->theme[$key], gettype($default_value));
+ $this->style[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value;
+ settype($this->style[$key], gettype($default_value));
if (is_string($default_value))
{
- $this->theme[$key] = htmlspecialchars($this->theme[$key]);
+ $this->style[$key] = htmlspecialchars($this->style[$key]);
}
}
diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php
index dd2d04d3a6..360e12e0b6 100644
--- a/phpBB/install/database_update.php
+++ b/phpBB/install/database_update.php
@@ -2248,6 +2248,21 @@ function change_database_data(&$no_updates, $version)
set_config('search_type', 'phpbb_search_' . $config['search_type']);
}
+ if (!isset($config['fulltext_postgres_ts_name']))
+ {
+ set_config('fulltext_postgres_ts_name', 'simple');
+ }
+
+ if (!isset($config['fulltext_postgres_min_word_len']))
+ {
+ set_config('fulltext_postgres_min_word_len', 4);
+ }
+
+ if (!isset($config['fulltext_postgres_max_word_len']))
+ {
+ set_config('fulltext_postgres_max_word_len', 254);
+ }
+
if (!isset($config['load_jquery_cdn']))
{
set_config('load_jquery_cdn', 0);
@@ -2346,6 +2361,13 @@ function change_database_data(&$no_updates, $version)
'auth' => 'acl_a_styles',
'cat' => 'ACP_STYLE_MANAGEMENT',
),
+ 'autologin_keys' => array(
+ 'base' => 'ucp_profile',
+ 'class' => 'ucp',
+ 'title' => 'UCP_PROFILE_AUTOLOGIN_KEYS',
+ 'auth' => '',
+ 'cat' => 'UCP_PROFILE',
+ ),
);
_add_modules($modules_to_install);
diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php
index bafcefe3f5..40dee7b3d7 100644
--- a/phpBB/install/install_install.php
+++ b/phpBB/install/install_install.php
@@ -1178,7 +1178,7 @@ class install_install extends module
$sql_query = preg_replace('#phpbb_#i', $data['table_prefix'], $sql_query);
- $sql_query = remove_comments($sql_query);
+ $sql_query = phpbb_remove_comments($sql_query);
$sql_query = split_sql_file($sql_query, $delimiter);
@@ -1216,7 +1216,7 @@ class install_install extends module
// Change language strings...
$sql_query = preg_replace_callback('#\{L_([A-Z0-9\-_]*)\}#s', 'adjust_language_keys_callback', $sql_query);
- $sql_query = remove_comments($sql_query);
+ $sql_query = phpbb_remove_comments($sql_query);
$sql_query = split_sql_file($sql_query, ';');
foreach ($sql_query as $sql)
diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql
index 307864825c..0bb1858c11 100644
--- a/phpBB/install/schemas/schema_data.sql
+++ b/phpBB/install/schemas/schema_data.sql
@@ -124,6 +124,9 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('fulltext_native_co
INSERT INTO phpbb_config (config_name, config_value) VALUES ('fulltext_native_load_upd', '1');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('fulltext_native_max_chars', '14');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('fulltext_native_min_chars', '3');
+INSERT INTO phpbb_config (config_name, config_value) VALUES ('fulltext_postgres_max_word_len', '254');
+INSERT INTO phpbb_config (config_name, config_value) VALUES ('fulltext_postgres_min_word_len', '4');
+INSERT INTO phpbb_config (config_name, config_value) VALUES ('fulltext_postgres_ts_name', 'simple');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('gzip_compress', '0');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('hot_threshold', '25');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('icons_path', 'images/icons');
diff --git a/phpBB/language/en/acp/search.php b/phpBB/language/en/acp/search.php
index 3dc89570bf..cd319c66a9 100644
--- a/phpBB/language/en/acp/search.php
+++ b/phpBB/language/en/acp/search.php
@@ -51,16 +51,24 @@ $lang = array_merge($lang, array(
'DELETING_INDEX_IN_PROGRESS' => 'Deleting the index in progress',
'DELETING_INDEX_IN_PROGRESS_EXPLAIN' => 'The search backend is currently cleaning its index. This can take a few minutes.',
- 'FULLTEXT_MYSQL_INCOMPATIBLE_VERSION' => 'The MySQL fulltext backend can only be used with MySQL4 and above.',
+ 'FULLTEXT_MYSQL_INCOMPATIBLE_DATABASE' => 'The MySQL fulltext backend can only be used with MySQL4 and above.',
'FULLTEXT_MYSQL_NOT_MYISAM' => 'MySQL fulltext indexes can only be used with MyISAM tables.',
'FULLTEXT_MYSQL_TOTAL_POSTS' => 'Total number of indexed posts',
- 'FULLTEXT_MYSQL_MBSTRING' => 'Support for non-latin UTF-8 characters using mbstring:',
- 'FULLTEXT_MYSQL_PCRE' => 'Support for non-latin UTF-8 characters using PCRE:',
- 'FULLTEXT_MYSQL_MBSTRING_EXPLAIN' => 'If PCRE does not have unicode character properties, the search backend will try to use mbstring’s regular expression engine.',
- 'FULLTEXT_MYSQL_PCRE_EXPLAIN' => 'This search backend requires PCRE unicode character properties, only available in PHP 4.4, 5.1 and above, if you want to search for non-latin characters.',
'FULLTEXT_MYSQL_MIN_SEARCH_CHARS_EXPLAIN' => 'Words with at least this many characters will be indexed for searching. You or your host can only change this setting by changing the mysql configuration.',
'FULLTEXT_MYSQL_MAX_SEARCH_CHARS_EXPLAIN' => 'Words with no more than this many characters will be indexed for searching. You or your host can only change this setting by changing the mysql configuration.',
+ 'FULLTEXT_POSTGRES_INCOMPATIBLE_DATABASE' => 'The PostgreSQL fulltext backend can only be used with PostgreSQL.',
+ 'FULLTEXT_POSTGRES_TS_NOT_USABLE' => 'The PostgreSQL fulltext backend can only be used with PostgreSQL 8.3 and above.',
+ 'FULLTEXT_POSTGRES_TOTAL_POSTS' => 'Total number of indexed posts',
+ 'FULLTEXT_POSTGRES_VERSION_CHECK' => 'PostgreSQL version',
+ 'FULLTEXT_POSTGRES_TS_NAME' => 'Text search Configuration Profile:',
+ 'FULLTEXT_POSTGRES_MIN_WORD_LEN' => 'Minimum word length for keywords',
+ 'FULLTEXT_POSTGRES_MAX_WORD_LEN' => 'Maximum word length for keywords',
+ 'FULLTEXT_POSTGRES_VERSION_CHECK_EXPLAIN' => 'This search backend requires PostgreSQL version 8.3 and above.',
+ 'FULLTEXT_POSTGRES_TS_NAME_EXPLAIN' => 'The Text search configuration profile used to determine the parser and dictionary.',
+ 'FULLTEXT_POSTGRES_MIN_WORD_LEN_EXPLAIN' => 'Words with at least this many characters will be included in the query to the database.',
+ 'FULLTEXT_POSTGRES_MAX_WORD_LEN_EXPLAIN' => 'Words with no more than this many characters will be included in the query to the database.',
+
'GENERAL_SEARCH_SETTINGS' => 'General search settings',
'GO_TO_SEARCH_INDEX' => 'Go to search index page',
diff --git a/phpBB/language/en/search.php b/phpBB/language/en/search.php
index 5b6fdce0e7..d09b4303cd 100644
--- a/phpBB/language/en/search.php
+++ b/phpBB/language/en/search.php
@@ -72,6 +72,7 @@ $lang = array_merge($lang, array(
'WORDS_IN_NO_POST' => 'No posts were found because the words <strong>%s</strong> are not contained in any post.',
'POST_CHARACTERS' => 'characters of posts',
+ 'PHRASE_SEARCH_DISABLED' => 'Searching by exact phrase is not supported on this board.',
'RECENT_SEARCHES' => 'Recent searches',
'RESULT_DAYS' => 'Limit results to previous',
@@ -81,6 +82,7 @@ $lang = array_merge($lang, array(
'SEARCHED_FOR' => 'Search term used',
'SEARCHED_TOPIC' => 'Searched topic',
+ 'SEARCHED_QUERY' => 'Searched query',
'SEARCH_ALL_TERMS' => 'Search for all terms or use query as entered',
'SEARCH_ANY_TERMS' => 'Search for any terms',
'SEARCH_AUTHOR' => 'Search for author',
diff --git a/phpBB/language/en/ucp.php b/phpBB/language/en/ucp.php
index 64a434d89c..14cc3fde27 100644
--- a/phpBB/language/en/ucp.php
+++ b/phpBB/language/en/ucp.php
@@ -87,6 +87,7 @@ $lang = array_merge($lang, array(
'ATTACHMENTS_EXPLAIN' => 'This is a list of attachments you have made in posts to this board.',
'ATTACHMENTS_DELETED' => 'Attachments successfully deleted.',
'ATTACHMENT_DELETED' => 'Attachment successfully deleted.',
+ 'AUTOLOGIN_SESSION_KEYS_DELETED'=> 'The selected persistent login keys were successfully deleted.',
'AVATAR_CATEGORY' => 'Category',
'AVATAR_EXPLAIN' => 'Maximum dimensions; width: %1$s, height: %2$s, file size: %3$.2f KiB.',
'AVATAR_FEATURES_DISABLED' => 'The avatar functionality is currently disabled.',
@@ -255,6 +256,8 @@ $lang = array_merge($lang, array(
'LINK_REMOTE_SIZE' => 'Avatar dimensions',
'LINK_REMOTE_SIZE_EXPLAIN' => 'Specify the width and height of the avatar, leave blank to attempt automatic verification.',
'LOGIN_EXPLAIN_UCP' => 'Please login in order to access the User Control Panel.',
+ 'LOGIN_KEY' => 'Login Key',
+ 'LOGIN_TIME' => 'Login Time',
'LOGIN_REDIRECT' => 'You have been successfully logged in.',
'LOGOUT_FAILED' => 'You were not logged out, as the request did not match your session. Please contact the board administrator if you continue to experience problems.',
'LOGOUT_REDIRECT' => 'You have been successfully logged out.',
@@ -376,6 +379,8 @@ $lang = array_merge($lang, array(
'PREFERENCES_UPDATED' => 'Your preferences have been updated.',
'PROFILE_INFO_NOTICE' => 'Please note that this information may be viewable to other members. Be careful when including any personal details. Any fields marked with a * must be completed.',
'PROFILE_UPDATED' => 'Your profile has been updated.',
+ 'PROFILE_AUTOLOGIN_KEYS' => 'The persistent login keys automatically log you in when you visit the board. If you logout, the persistent login key is deleted only on the computer you are using to logout. Here you can see persistent login keys created on other computers you used to access this site.',
+ 'PROFILE_NO_AUTOLOGIN_KEYS' => 'There are no saved persistent login keys.',
'RECIPIENT' => 'Recipient',
'RECIPIENTS' => 'Recipients',
@@ -398,6 +403,7 @@ $lang = array_merge($lang, array(
'RULE_ADDED' => 'Rule successfully added.',
'RULE_ALREADY_DEFINED' => 'This rule was defined previously.',
'RULE_DELETED' => 'Rule successfully removed.',
+ 'RULE_LIMIT_REACHED' => 'You cannot add more PM rules. You have reached the maximum number of rules.',
'RULE_NOT_DEFINED' => 'Rule not correctly specified.',
'RULE_REMOVED_MESSAGES' => array(
1 => '%d private message was removed due to private message filters.',
@@ -470,6 +476,7 @@ $lang = array_merge($lang, array(
'UCP_PROFILE_PROFILE_INFO' => 'Edit profile',
'UCP_PROFILE_REG_DETAILS' => 'Edit account settings',
'UCP_PROFILE_SIGNATURE' => 'Edit signature',
+ 'UCP_PROFILE_AUTOLOGIN_KEYS'=> 'Edit persistent login keys',
'UCP_USERGROUPS' => 'Usergroups',
'UCP_USERGROUPS_MEMBER' => 'Edit memberships',
diff --git a/phpBB/search.php b/phpBB/search.php
index 2b463aec9c..93cd0c31fd 100644
--- a/phpBB/search.php
+++ b/phpBB/search.php
@@ -596,13 +596,24 @@ if ($keywords || $author || $author_id || $search_id || $submit)
$u_search .= ($search_fields != 'all') ? '&amp;sf=' . $search_fields : '';
$u_search .= ($return_chars != 300) ? '&amp;ch=' . $return_chars : '';
+ // Check if search backend supports phrase search or not
+ $phrase_search_disabled = '';
+ if (strpos(html_entity_decode($keywords), '"') !== false && method_exists($search, 'supports_phrase_search'))
+ {
+ $phrase_search_disabled = $search->supports_phrase_search() ? false : true;
+ }
+
$template->assign_vars(array(
'SEARCH_TITLE' => $l_search_title,
'SEARCH_MATCHES' => $l_search_matches,
- 'SEARCH_WORDS' => $search->search_query,
+ 'SEARCH_WORDS' => $keywords,
+ 'SEARCHED_QUERY' => $search->search_query,
'IGNORED_WORDS' => (sizeof($search->common_words)) ? implode(' ', $search->common_words) : '',
'PAGINATION' => generate_pagination($u_search, $total_match_count, $per_page, $start),
'PAGE_NUMBER' => on_page($total_match_count, $per_page, $start),
+
+ 'PHRASE_SEARCH_DISABLED' => $phrase_search_disabled,
+
'TOTAL_MATCHES' => $total_match_count,
'SEARCH_IN_RESULTS' => ($search_id) ? false : true,
diff --git a/phpBB/styles/prosilver/template/forumlist_body.html b/phpBB/styles/prosilver/template/forumlist_body.html
index ca5e8abb1d..723c2ffeda 100644
--- a/phpBB/styles/prosilver/template/forumlist_body.html
+++ b/phpBB/styles/prosilver/template/forumlist_body.html
@@ -35,7 +35,12 @@
<!-- IF forumrow.MODERATORS -->
<br /><strong>{forumrow.L_MODERATOR_STR}:</strong> {forumrow.MODERATORS}
<!-- ENDIF -->
- <!-- IF forumrow.SUBFORUMS and forumrow.S_LIST_SUBFORUMS --><br /><strong>{forumrow.L_SUBFORUM_STR}</strong> {forumrow.SUBFORUMS}<!-- ENDIF -->
+ <!-- IF .forumrow.subforum and forumrow.S_LIST_SUBFORUMS -->
+ <br /><strong>{forumrow.L_SUBFORUM_STR}</strong>
+ <!-- BEGIN subforum -->
+ <a href="{forumrow.subforum.U_SUBFORUM}" class="subforum<!-- IF forumrow.subforum.S_UNREAD --> unread<!-- ELSE --> read<!-- ENDIF -->" title="<!-- IF forumrow.subforum.UNREAD -->{L_UNREAD_POSTS}<!-- ELSE -->{L_NO_UNREAD_POSTS}<!-- ENDIF -->">{forumrow.subforum.SUBFORUM_NAME}</a><!-- IF not forumrow.subforum.S_LAST_ROW -->,<!-- ENDIF -->
+ <!-- END subforum -->
+ <!-- ENDIF -->
</dt>
<!-- IF forumrow.CLICKS -->
<dd class="redirect"><span>{L_REDIRECTS}: {forumrow.CLICKS}</span></dd>
diff --git a/phpBB/styles/prosilver/template/mcp_topic.html b/phpBB/styles/prosilver/template/mcp_topic.html
index 2a6de3ce9d..8d0294d226 100644
--- a/phpBB/styles/prosilver/template/mcp_topic.html
+++ b/phpBB/styles/prosilver/template/mcp_topic.html
@@ -67,7 +67,7 @@ onload_functions.push('subPanels()');
<dl>
<dt><label for="subject">{L_SPLIT_SUBJECT}:</label></dt>
- <dd><input type="text" name="subject" id="subject" size="45" maxlength="64" tabindex="2" value="{SPLIT_SUBJECT}" title="{L_SPLIT_SUBJECT}" class="inputbox" /></dd>
+ <dd><input type="text" name="subject" id="subject" size="45" maxlength="124" tabindex="2" value="{SPLIT_SUBJECT}" title="{L_SPLIT_SUBJECT}" class="inputbox" /></dd>
</dl>
<dl>
<dt><label>{L_SPLIT_FORUM}:</label></dt>
diff --git a/phpBB/styles/prosilver/template/overall_footer.html b/phpBB/styles/prosilver/template/overall_footer.html
index 2cb721073f..b473e7022c 100644
--- a/phpBB/styles/prosilver/template/overall_footer.html
+++ b/phpBB/styles/prosilver/template/overall_footer.html
@@ -8,9 +8,9 @@
<ul class="linklist">
<li class="icon-home"><a href="{U_INDEX}" accesskey="h">{L_INDEX}</a></li>
<!-- IF not S_IS_BOT -->
- <!-- IF S_WATCH_FORUM_LINK --><li <!-- IF S_WATCHING_FORUM -->class="icon-unsubscribe"<!-- ELSE -->class="icon-subscribe"<!-- ENDIF -->><a href="{S_WATCH_FORUM_LINK}" title="{S_WATCH_FORUM_TITLE}" data-ajax="alt_text" data-alt-text="<!-- IF S_WATCHING_FORUM -->{L_START_WATCHING_FORUM}<!-- ELSE -->{L_STOP_WATCHING_FORUM}<!-- ENDIF -->">{S_WATCH_FORUM_TITLE}</a></li><!-- ENDIF -->
- <!-- IF U_WATCH_TOPIC --><li <!-- IF S_WATCHING_TOPIC -->class="icon-unsubscribe"<!-- ELSE -->class="icon-subscribe"<!-- ENDIF -->><a href="{U_WATCH_TOPIC}" title="{L_WATCH_TOPIC}" data-ajax="alt_text" data-alt-text="<!-- IF S_WATCHING_TOPIC -->{L_START_WATCHING_TOPIC}<!-- ELSE -->{L_STOP_WATCHING_TOPIC}<!-- ENDIF -->">{L_WATCH_TOPIC}</a></li><!-- ENDIF -->
- <!-- IF U_BOOKMARK_TOPIC --><li class="icon-bookmark"><a href="{U_BOOKMARK_TOPIC}" title="{L_BOOKMARK_TOPIC}" data-ajax="alt_text" data-alt-text="<!-- IF S_BOOKMARKED_TOPIC -->{L_BOOKMARK_TOPIC_REAL}<!-- ELSE -->{L_BOOKMARK_TOPIC_REMOVE}<!-- ENDIF -->">{L_BOOKMARK_TOPIC}</a></li><!-- ENDIF -->
+ <!-- IF U_WATCH_FORUM_LINK --><li <!-- IF S_WATCHING_FORUM -->class="icon-unsubscribe"<!-- ELSE -->class="icon-subscribe"<!-- ENDIF -->><a href="{U_WATCH_FORUM_LINK}" title="{S_WATCH_FORUM_TITLE}" data-ajax="toggle_link" data-toggle-class="icon-<!-- IF not S_WATCHING_FORUM -->unsubscribe<!-- ELSE -->subscribe<!-- ENDIF -->" data-toggle-text="{S_WATCH_FORUM_TOGGLE}" data-toggle-url="{U_WATCH_FORUM_TOGGLE}">{S_WATCH_FORUM_TITLE}</a></li><!-- ENDIF -->
+ <!-- IF U_WATCH_TOPIC --><li <!-- IF S_WATCHING_TOPIC -->class="icon-unsubscribe"<!-- ELSE -->class="icon-subscribe"<!-- ENDIF -->><a href="{U_WATCH_TOPIC}" title="{S_WATCH_TOPIC_TITLE}" data-ajax="toggle_link" data-toggle-class="<!-- IF not S_WATCHING_TOPIC -->icon-unsubscribe<!-- ELSE -->icon-subscribe<!-- ENDIF -->" data-toggle-text="{S_WATCH_TOPIC_TOGGLE}" data-toggle-url="{U_WATCH_TOPIC_TOGGLE}">{S_WATCH_TOPIC_TITLE}</a></li><!-- ENDIF -->
+ <!-- IF U_BOOKMARK_TOPIC --><li class="icon-bookmark"><a href="{U_BOOKMARK_TOPIC}" title="{L_BOOKMARK_TOPIC}" data-ajax="alt_text" data-alt-text="{S_BOOKMARK_TOGGLE}">{S_BOOKMARK_TOPIC}</a></li><!-- ENDIF -->
<!-- IF U_BUMP_TOPIC --><li class="icon-bump"><a href="{U_BUMP_TOPIC}" title="{L_BUMP_TOPIC}" data-ajax="true">{L_BUMP_TOPIC}</a></li><!-- ENDIF -->
<!-- ENDIF -->
<li class="rightside"><!-- IF U_TEAM --><a href="{U_TEAM}">{L_THE_TEAM}</a> &bull; <!-- ENDIF --><!-- IF not S_IS_BOT --><a href="{U_DELETE_COOKIES}" data-ajax="true" data-refresh="true">{L_DELETE_COOKIES}</a> &bull; <!-- ENDIF -->{S_TIMEZONE}</li>
diff --git a/phpBB/styles/prosilver/template/posting_editor.html b/phpBB/styles/prosilver/template/posting_editor.html
index 60766495c6..d1c86e7e13 100644
--- a/phpBB/styles/prosilver/template/posting_editor.html
+++ b/phpBB/styles/prosilver/template/posting_editor.html
@@ -103,7 +103,7 @@
<!-- IF S_POST_ACTION or S_PRIVMSGS or S_EDIT_DRAFT -->
<dl style="clear: left;">
<dt><label for="subject">{L_SUBJECT}:</label></dt>
- <dd><input type="text" name="subject" id="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->60<!-- ELSE -->64<!-- ENDIF -->" tabindex="2" value="{SUBJECT}{DRAFT_SUBJECT}" class="inputbox autowidth" /></dd>
+ <dd><input type="text" name="subject" id="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->120<!-- ELSE -->124<!-- ENDIF -->" tabindex="2" value="{SUBJECT}{DRAFT_SUBJECT}" class="inputbox autowidth" /></dd>
</dl>
<!-- IF CAPTCHA_TEMPLATE and S_CONFIRM_CODE -->
<!-- DEFINE $CAPTCHA_TAB_INDEX = 3 -->
diff --git a/phpBB/styles/prosilver/template/quickreply_editor.html b/phpBB/styles/prosilver/template/quickreply_editor.html
index 724fdb85b8..5fcdf0f5d4 100644
--- a/phpBB/styles/prosilver/template/quickreply_editor.html
+++ b/phpBB/styles/prosilver/template/quickreply_editor.html
@@ -5,7 +5,7 @@
<fieldset class="fields1">
<dl style="clear: left;">
<dt><label for="subject">{L_SUBJECT}:</label></dt>
- <dd><input type="text" name="subject" id="subject" size="45" maxlength="64" tabindex="2" value="{SUBJECT}" class="inputbox autowidth" /></dd>
+ <dd><input type="text" name="subject" id="subject" size="45" maxlength="124" tabindex="2" value="{SUBJECT}" class="inputbox autowidth" /></dd>
</dl>
<div id="message-box">
<textarea style="height: 9em;" name="message" rows="7" cols="76" tabindex="3" class="inputbox"></textarea>
diff --git a/phpBB/styles/prosilver/template/search_results.html b/phpBB/styles/prosilver/template/search_results.html
index 5d75bd3d56..0a58605b31 100644
--- a/phpBB/styles/prosilver/template/search_results.html
+++ b/phpBB/styles/prosilver/template/search_results.html
@@ -1,7 +1,9 @@
<!-- INCLUDE overall_header.html -->
<h2><!-- IF SEARCH_TITLE -->{SEARCH_TITLE}<!-- ELSE -->{SEARCH_MATCHES}<!-- ENDIF --><!-- IF SEARCH_WORDS -->: <a href="{U_SEARCH_WORDS}">{SEARCH_WORDS}</a><!-- ENDIF --></h2>
+<!-- IF SEARCHED_QUERY --> <p>{L_SEARCHED_QUERY}: <strong>{SEARCHED_QUERY}</strong></p><!-- ENDIF -->
<!-- IF IGNORED_WORDS --> <p>{L_IGNORED_TERMS}: <strong>{IGNORED_WORDS}</strong></p><!-- ENDIF -->
+<!-- IF PHRASE_SEARCH_DISABLED --> <p><strong>{L_PHRASE_SEARCH_DISABLED}</strong></p><!-- ENDIF -->
<!-- IF SEARCH_TOPIC -->
<p><a class="{S_CONTENT_FLOW_BEGIN}" href="{U_SEARCH_TOPIC}">{L_RETURN_TO}: {SEARCH_TOPIC}</a></p>
diff --git a/phpBB/styles/prosilver/template/ucp_groups_membership.html b/phpBB/styles/prosilver/template/ucp_groups_membership.html
index a312911ae4..5cb238082a 100644
--- a/phpBB/styles/prosilver/template/ucp_groups_membership.html
+++ b/phpBB/styles/prosilver/template/ucp_groups_membership.html
@@ -93,7 +93,7 @@
<!-- IF pending.GROUP_DESC --><br />{pending.GROUP_DESC}<!-- ENDIF -->
<!-- IF not pending.GROUP_SPECIAL --><br /><i>{pending.GROUP_STATUS}</i><!-- ENDIF -->
</dt>
- <dd class="mark"><input type="radio" name="selected" value="{pending.GROUP_ID}" <!-- IF pending.GROUP_SPECIAL -->disabled="diabled"<!-- ENDIF --> /></dd>
+ <dd class="mark"><input type="radio" name="selected" value="{pending.GROUP_ID}" <!-- IF pending.GROUP_SPECIAL -->disabled="disabled"<!-- ENDIF --> /></dd>
</dl>
</li>
<!-- END pending -->
diff --git a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html
index bafb64dbc8..43e7de2e12 100644
--- a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html
+++ b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html
@@ -17,10 +17,9 @@
<div id="page-header">
<h1>{SITENAME}</h1>
- <p>{SITE_DESCRIPTION}<br /><a href="{U_FORUM}">{U_FORUM}</a></p>
+ <p>{SITE_DESCRIPTION}</p>
- <h2>{TOPIC_TITLE}</h2>
- <p><a href="{U_TOPIC}">{U_TOPIC}</a></p>
+ <h2>{L_PRIVATE_MESSAGING}</h2>
</div>
<div id="page-body">
diff --git a/phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html b/phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html
new file mode 100644
index 0000000000..a6c19508e2
--- /dev/null
+++ b/phpBB/styles/prosilver/template/ucp_profile_autologin_keys.html
@@ -0,0 +1,53 @@
+<!-- INCLUDE ucp_header.html -->
+
+<form id="ucp" method="post" action="{S_UCP_ACTION}"{S_FORM_ENCTYPE}>
+
+<h2>{L_TITLE}</h2>
+<div class="panel">
+ <div class="inner">
+
+ <fieldset>
+ <!-- IF .errors -->
+ <p class="error">
+ <!-- BEGIN errors -->
+ {errors} <br />
+ <!-- END errors -->
+ </p>
+ <!-- ENDIF -->
+
+ <p>{L_PROFILE_AUTOLOGIN_KEYS}</p><br />
+ <table cellspacing="1" class="table1">
+ <thead>
+ <tr>
+ <th>{L_MARK}</th>
+ <th>{L_LOGIN_KEY}</th>
+ <th>{L_IP}</th>
+ <th>{L_LOGIN_TIME}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <!-- BEGIN sessions -->
+ <!-- IF sessions.S_ROW_COUNT is even --><tr class="bg1"><!-- ELSE --><tr class="bg2"><!-- ENDIF -->
+ <td style="text-align: center"><input type="checkbox" name="keys[]" value="{sessions.KEY}" id="{sessions.KEY}"/></td>
+ <td><label for="{sessions.KEY}">{sessions.KEY}</label></td>
+ <td style="text-align: center">{sessions.IP}</td>
+ <td style="text-align: center">{sessions.LOGIN_TIME}</td>
+ </tr>
+ <!-- BEGINELSE -->
+ <tr><td colspan="4" class="bg1" style="text-align: center">{L_PROFILE_NO_AUTOLOGIN_KEYS}</td></tr>
+ <!-- END sessions -->
+ </tbody>
+ </table>
+ </fieldset>
+ </div>
+</div>
+
+<!-- IF .sessions -->
+ <fieldset class="submit-buttons">
+ {S_HIDDEN_FIELDS}<input type="submit" name="submit" value="{L_DELETE}" class="button1" />
+ {S_FORM_TOKEN}
+ </fieldset>
+<!-- ENDIF -->
+</form>
+
+<!-- INCLUDE ucp_footer.html -->
diff --git a/phpBB/styles/prosilver/template/viewtopic_body.html b/phpBB/styles/prosilver/template/viewtopic_body.html
index 9110cea4e9..1af512732d 100644
--- a/phpBB/styles/prosilver/template/viewtopic_body.html
+++ b/phpBB/styles/prosilver/template/viewtopic_body.html
@@ -263,7 +263,7 @@
<!-- INCLUDE jumpbox.html -->
<!-- IF .quickmod -->
- <form method="post" action="{S_MOD_ACTION}" id="quickmodform" data-ajax="true">
+ <form method="post" action="{S_MOD_ACTION}" id="quickmodform">
<fieldset class="quickmod">
<label for="quick-mod-select">{L_QUICK_MOD}:</label>
<select name="action" id="quick-mod-select">
diff --git a/phpBB/styles/subsilver2/template/forumlist_body.html b/phpBB/styles/subsilver2/template/forumlist_body.html
index be32d1fb77..b9a1102df0 100644
--- a/phpBB/styles/subsilver2/template/forumlist_body.html
+++ b/phpBB/styles/subsilver2/template/forumlist_body.html
@@ -48,8 +48,12 @@
<!-- IF forumrow.MODERATORS -->
<p class="forumdesc"><strong>{forumrow.L_MODERATOR_STR}:</strong> {forumrow.MODERATORS}</p>
<!-- ENDIF -->
- <!-- IF forumrow.SUBFORUMS and forumrow.S_LIST_SUBFORUMS -->
- <p class="forumdesc"><strong>{forumrow.L_SUBFORUM_STR}</strong> {forumrow.SUBFORUMS}</p>
+ <!-- IF .forumrow.subforum and forumrow.S_LIST_SUBFORUMS -->
+ <p class="forumdesc"><strong>{forumrow.L_SUBFORUM_STR}</strong>
+ <!-- BEGIN subforum -->
+ <a href="{forumrow.subforum.U_SUBFORUM}" class="subforum<!-- IF forumrow.subforum.S_UNREAD --> unread<!-- ELSE --> read<!-- ENDIF -->" title="<!-- IF forumrow.subforum.UNREAD -->{L_UNREAD_POSTS}<!-- ELSE -->{L_NO_UNREAD_POSTS}<!-- ENDIF -->">{forumrow.subforum.SUBFORUM_NAME}</a><!-- IF not forumrow.subforum.S_LAST_ROW -->,<!-- ENDIF -->
+ <!-- END subforum -->
+ </p>
<!-- ENDIF -->
</td>
<td class="row2" align="center"><p class="topicdetails">{forumrow.TOPICS}</p></td>
diff --git a/phpBB/styles/subsilver2/template/mcp_topic.html b/phpBB/styles/subsilver2/template/mcp_topic.html
index 8ff648da39..6bbf13e12c 100644
--- a/phpBB/styles/subsilver2/template/mcp_topic.html
+++ b/phpBB/styles/subsilver2/template/mcp_topic.html
@@ -12,7 +12,7 @@
</tr>
<tr>
<td class="row1" nowrap="nowrap"><span class="gen">{L_SPLIT_SUBJECT}</span></td>
- <td class="row2" colspan="2"><input class="post" style="width: 350px" type="text" size="35" maxlength="64" name="subject" value="{SPLIT_SUBJECT}" /></td>
+ <td class="row2" colspan="2"><input class="post" style="width: 350px" type="text" size="35" maxlength="124" name="subject" value="{SPLIT_SUBJECT}" /></td>
</tr>
<tr>
<td class="row1" nowrap="nowrap"><span class="gen">{L_SPLIT_FORUM}</span></td>
diff --git a/phpBB/styles/subsilver2/template/posting_body.html b/phpBB/styles/subsilver2/template/posting_body.html
index 712c02b856..ae7d2f88d6 100644
--- a/phpBB/styles/subsilver2/template/posting_body.html
+++ b/phpBB/styles/subsilver2/template/posting_body.html
@@ -173,7 +173,7 @@
<tr>
<td class="row1" width="22%"><b class="genmed">{L_SUBJECT}:</b></td>
- <td class="row2" width="78%"><input class="post" style="width:450px" type="text" name="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->60<!-- ELSE -->64<!-- ENDIF -->" tabindex="2" value="{SUBJECT}" /></td>
+ <td class="row2" width="78%"><input class="post" style="width:450px" type="text" name="subject" size="45" maxlength="<!-- IF S_NEW_MESSAGE -->120<!-- ELSE -->124<!-- ENDIF -->" tabindex="2" value="{SUBJECT}" /></td>
</tr>
<tr>
<td class="row1" valign="top"><b class="genmed">{L_MESSAGE_BODY}:</b><br /><span class="gensmall">{L_MESSAGE_BODY_EXPLAIN}&nbsp;</span><br /><br />
diff --git a/phpBB/styles/subsilver2/template/quickreply_editor.html b/phpBB/styles/subsilver2/template/quickreply_editor.html
index 4c3f7a3d0b..7a68a8f605 100644
--- a/phpBB/styles/subsilver2/template/quickreply_editor.html
+++ b/phpBB/styles/subsilver2/template/quickreply_editor.html
@@ -6,7 +6,7 @@
</tr>
<tr>
<td class="row1" width="22%"><b class="genmed">{L_SUBJECT}:</b></td>
- <td class="row2" width="78%"><input class="post" style="width:450px" type="text" name="subject" size="45" maxlength="64" tabindex="2" value="{SUBJECT}" /></td>
+ <td class="row2" width="78%"><input class="post" style="width:450px" type="text" name="subject" size="45" maxlength="124" tabindex="2" value="{SUBJECT}" /></td>
</tr>
<tr>
<td class="row1" width="22%"><b class="genmed">{L_MESSAGE}:</b></td>
diff --git a/phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html b/phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html
new file mode 100644
index 0000000000..1dab9acb9c
--- /dev/null
+++ b/phpBB/styles/subsilver2/template/ucp_profile_autologin_keys.html
@@ -0,0 +1,49 @@
+<!-- INCLUDE ucp_header.html -->
+
+<table class="tablebg" width="100%" cellspacing="1">
+<tr>
+ <th colspan="4" valign="middle">{L_TITLE}</th>
+</tr>
+<!-- IF .errors -->
+ <tr>
+ <td class="row3" colspan="2" align="center"><span class="gensmall error">
+ <!-- BEGIN errors -->
+ {errors} <br />
+ <!-- END errors -->
+ </td>
+ </tr>
+<!-- ENDIF -->
+
+<tr>
+ <td colspan="4" class="row1">{L_PROFILE_AUTOLOGIN_KEYS}</td>
+</tr>
+<tr>
+ <th>{L_MARK}</th>
+ <th>{L_LOGIN_KEY}</th>
+ <th>{L_IP}</th>
+ <th>{L_LOGIN_TIME}</th>
+</tr>
+<!-- BEGIN sessions -->
+ <!-- IF sessions.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <td class="genmed" style="text-align: center"><input type="checkbox" name="keys[]" value="{sessions.KEY}" id="{sessions.KEY}"/></td>
+ <td class="genmed"><label for="{sessions.KEY}">{sessions.KEY}</label></td>
+ <td class="genmed" style="text-align: center">{sessions.IP}</td>
+ <td class="genmed" style="text-align: center">{sessions.LOGIN_TIME}</td>
+ </tr>
+<!-- BEGINELSE -->
+ <tr>
+ <td colspan="4" class="row1" style="text-align: center">{L_PROFILE_NO_AUTOLOGIN_KEYS}</td>
+ </tr>
+<!-- END sessions -->
+
+<!-- IF .sessions -->
+ <tr>
+ <td class="cat" colspan="4" align="center">
+ {S_HIDDEN_FIELDS}<input class="btnmain" type="submit" name="submit" value="{L_DELETE}" />
+ {S_FORM_TOKEN}
+ </td>
+ </tr>
+<!-- ENDIF -->
+</table>
+
+<!-- INCLUDE ucp_footer.html -->
diff --git a/phpBB/styles/subsilver2/template/viewforum_body.html b/phpBB/styles/subsilver2/template/viewforum_body.html
index f132185716..afb8426799 100644
--- a/phpBB/styles/subsilver2/template/viewforum_body.html
+++ b/phpBB/styles/subsilver2/template/viewforum_body.html
@@ -154,7 +154,7 @@
<td class="cat" colspan="<!-- IF S_TOPIC_ICONS -->7<!-- ELSE -->6<!-- ENDIF -->">
<table width="100%" cellspacing="0">
<tr class="nav">
- <td valign="middle">&nbsp;<!-- IF S_WATCH_FORUM_LINK and not S_IS_BOT --><a href="{S_WATCH_FORUM_LINK}">{S_WATCH_FORUM_TITLE}</a><!-- ENDIF --></td>
+ <td valign="middle">&nbsp;<!-- IF U_WATCH_FORUM_LINK and not S_IS_BOT --><a href="{U_WATCH_FORUM_LINK}">{S_WATCH_FORUM_TITLE}</a><!-- ENDIF --></td>
<td align="{S_CONTENT_FLOW_END}" valign="middle"><!-- IF not S_IS_BOT and U_MARK_TOPICS --><a href="{U_MARK_TOPICS}">{L_MARK_TOPICS_READ}</a><!-- ENDIF -->&nbsp;</td>
</tr>
</table>
diff --git a/phpBB/styles/subsilver2/template/viewtopic_body.html b/phpBB/styles/subsilver2/template/viewtopic_body.html
index 2c5351b926..a4e2dd886a 100644
--- a/phpBB/styles/subsilver2/template/viewtopic_body.html
+++ b/phpBB/styles/subsilver2/template/viewtopic_body.html
@@ -52,8 +52,8 @@
<tr>
<td class="nav" nowrap="nowrap">&nbsp;
<!-- IF not S_IS_BOT -->
- <!-- IF U_WATCH_TOPIC --><a href="{U_WATCH_TOPIC}" title="{L_WATCH_TOPIC}">{L_WATCH_TOPIC}</a><!-- IF U_PRINT_TOPIC or U_EMAIL_TOPIC or U_BUMP_TOPIC or U_BOOKMARK_TOPIC --> | <!-- ENDIF --><!-- ENDIF -->
- <!-- IF U_BOOKMARK_TOPIC --><a href="{U_BOOKMARK_TOPIC}" title="{L_BOOKMARK_TOPIC}">{L_BOOKMARK_TOPIC}</a><!-- IF U_PRINT_TOPIC or U_EMAIL_TOPIC or U_BUMP_TOPIC --> | <!-- ENDIF --><!-- ENDIF -->
+ <!-- IF U_WATCH_TOPIC --><a href="{U_WATCH_TOPIC}" title="{S_WATCH_TOPIC_TITLE}">{S_WATCH_TOPIC_TITLE}</a><!-- IF U_PRINT_TOPIC or U_EMAIL_TOPIC or U_BUMP_TOPIC or U_BOOKMARK_TOPIC --> | <!-- ENDIF --><!-- ENDIF -->
+ <!-- IF U_BOOKMARK_TOPIC --><a href="{U_BOOKMARK_TOPIC}" title="{S_BOOKMARK_TOPIC}">{S_BOOKMARK_TOPIC}</a><!-- IF U_PRINT_TOPIC or U_EMAIL_TOPIC or U_BUMP_TOPIC --> | <!-- ENDIF --><!-- ENDIF -->
<!-- IF U_PRINT_TOPIC --><a href="{U_PRINT_TOPIC}" title="{L_PRINT_TOPIC}">{L_PRINT_TOPIC}</a><!-- IF U_EMAIL_TOPIC or U_BUMP_TOPIC --> | <!-- ENDIF --><!-- ENDIF -->
<!-- IF U_EMAIL_TOPIC --><a href="{U_EMAIL_TOPIC}" title="{L_EMAIL_TOPIC}">{L_EMAIL_TOPIC}</a><!-- IF U_BUMP_TOPIC --> | <!-- ENDIF --><!-- ENDIF -->
<!-- IF U_BUMP_TOPIC --><a href="{U_BUMP_TOPIC}" title="{L_BUMP_TOPIC}">{L_BUMP_TOPIC}</a><!-- ENDIF -->
diff --git a/phpBB/viewforum.php b/phpBB/viewforum.php
index 2d91581cf4..6a39f10394 100644
--- a/phpBB/viewforum.php
+++ b/phpBB/viewforum.php
@@ -204,7 +204,9 @@ if (!$config['use_system_cron'])
// Forum rules and subscription info
$s_watching_forum = array(
'link' => '',
+ 'link_toggle' => '',
'title' => '',
+ 'title_toggle' => '',
'is_watching' => false,
);
@@ -318,8 +320,10 @@ $template->assign_vars(array(
'S_SELECT_SORT_KEY' => $s_sort_key,
'S_SELECT_SORT_DAYS' => $s_limit_days,
'S_TOPIC_ICONS' => ($s_display_active && sizeof($active_forum_ary)) ? max($active_forum_ary['enable_icons']) : (($forum_data['enable_icons']) ? true : false),
- 'S_WATCH_FORUM_LINK' => $s_watching_forum['link'],
+ 'U_WATCH_FORUM_LINK' => $s_watching_forum['link'],
+ 'U_WATCH_FORUM_TOGGLE' => $s_watching_forum['link_toggle'],
'S_WATCH_FORUM_TITLE' => $s_watching_forum['title'],
+ 'S_WATCH_FORUM_TOGGLE' => $s_watching_forum['title_toggle'],
'S_WATCHING_FORUM' => $s_watching_forum['is_watching'],
'S_FORUM_ACTION' => append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$forum_id" . (($start == 0) ? '' : "&amp;start=$start")),
'S_DISPLAY_SEARCHBOX' => ($auth->acl_get('u_search') && $auth->acl_get('f_search', $forum_id) && $config['load_search']) ? true : false,
diff --git a/phpBB/viewtopic.php b/phpBB/viewtopic.php
index 65237a7a02..dbebc793fd 100644
--- a/phpBB/viewtopic.php
+++ b/phpBB/viewtopic.php
@@ -449,7 +449,9 @@ $viewtopic_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&a
// Are we watching this topic?
$s_watching_topic = array(
'link' => '',
+ 'link_toggle' => '',
'title' => '',
+ 'title_toggle' => '',
'is_watching' => false,
);
@@ -649,13 +651,15 @@ $template->assign_vars(array(
'U_PRINT_TOPIC' => ($auth->acl_get('f_print', $forum_id)) ? $viewtopic_url . '&amp;view=print' : '',
'U_EMAIL_TOPIC' => ($auth->acl_get('f_email', $forum_id) && $config['email_enable']) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=email&amp;t=$topic_id") : '',
- 'U_WATCH_TOPIC' => $s_watching_topic['link'],
- 'L_WATCH_TOPIC' => $s_watching_topic['title'],
+ 'U_WATCH_TOPIC' => $s_watching_topic['link'],
+ 'U_WATCH_TOPIC_TOGGLE' => $s_watching_topic['link_toggle'],
+ 'S_WATCH_TOPIC_TITLE' => $s_watching_topic['title'],
+ 'S_WATCH_TOPIC_TOGGLE' => $s_watching_topic['title_toggle'],
'S_WATCHING_TOPIC' => $s_watching_topic['is_watching'],
'U_BOOKMARK_TOPIC' => ($user->data['is_registered'] && $config['allow_bookmarks']) ? $viewtopic_url . '&amp;bookmark=1&amp;hash=' . generate_link_hash("topic_$topic_id") : '',
- 'L_BOOKMARK_TOPIC' => ($user->data['is_registered'] && $config['allow_bookmarks'] && $topic_data['bookmarked']) ? $user->lang['BOOKMARK_TOPIC_REMOVE'] : $user->lang['BOOKMARK_TOPIC'],
- 'L_BOOKMARK_TOPIC_REAL' => $user->lang['BOOKMARK_TOPIC'],
+ 'S_BOOKMARK_TOPIC' => ($user->data['is_registered'] && $config['allow_bookmarks'] && $topic_data['bookmarked']) ? $user->lang['BOOKMARK_TOPIC_REMOVE'] : $user->lang['BOOKMARK_TOPIC'],
+ 'S_BOOKMARK_TOGGLE' => (!$user->data['is_registered'] || !$config['allow_bookmarks'] || !$topic_data['bookmarked']) ? $user->lang['BOOKMARK_TOPIC_REMOVE'] : $user->lang['BOOKMARK_TOPIC'],
'S_BOOKMARKED_TOPIC' => ($user->data['is_registered'] && $config['allow_bookmarks'] && $topic_data['bookmarked']) ? true : false,
'U_POST_NEW_TOPIC' => ($auth->acl_get('f_post', $forum_id) || $user->data['user_id'] == ANONYMOUS) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=post&amp;f=$forum_id") : '',
diff --git a/tests/RUNNING_TESTS.txt b/tests/RUNNING_TESTS.txt
index b976545c22..7c2a7c3fce 100644
--- a/tests/RUNNING_TESTS.txt
+++ b/tests/RUNNING_TESTS.txt
@@ -51,6 +51,27 @@ test_config.php file:
PHPBB_TEST_DBNAME='database' PHPBB_TEST_DBUSER='user' \
PHPBB_TEST_DBPASSWD='password' phpunit
+Special Database Cases
+----------------------
+In order to run tests on some of the databases that we support, it will be
+necessary to provide a custom DSN string in test_config.php. This is only
+needed for MSSQL 2000+ (PHP module), MSSQL via ODBC, and Firebird when
+PDO_Firebird does not work on your system
+(https://bugs.php.net/bug.php?id=61183). The variable must be named $custom_dsn.
+
+Examples:
+Firebird using http://www.firebirdsql.org/en/odbc-driver/
+$custom_dsn = "Driver={Firebird/InterBase(r) driver};dbname=$dbhost:$dbname";
+
+MSSQL
+$custom_dsn = "Driver={SQL Server Native Client 10.0};Server=$dbhost;Database=$dbname";
+
+The other fields in test_config.php should be filled out as you would normally
+to connect to that database in phpBB.
+
+Additionally, you will need to be running the DbUnit fork from
+https://github.com/phpbb/dbunit/tree/phpbb.
+
Running
=======
diff --git a/tests/bbcode/url_bbcode_test.php b/tests/bbcode/url_bbcode_test.php
index b944b6135d..d5df386714 100644
--- a/tests/bbcode/url_bbcode_test.php
+++ b/tests/bbcode/url_bbcode_test.php
@@ -11,8 +11,6 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_content.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/bbcode.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/message_parser.php';
-require_once dirname(__FILE__) . '/../mock_user.php';
-require_once dirname(__FILE__) . '/../mock/request.php';
class phpbb_url_bbcode_test extends phpbb_test_case
{
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index f103d8f15a..1017e0c72f 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -16,6 +16,8 @@ $table_prefix = 'phpbb_';
require_once $phpbb_root_path . 'includes/constants.php';
require_once $phpbb_root_path . 'includes/class_loader.' . $phpEx;
+$phpbb_class_loader_mock = new phpbb_class_loader('phpbb_mock_', $phpbb_root_path . '../tests/mock/', ".php");
+$phpbb_class_loader_mock->register();
$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".php");
$phpbb_class_loader_ext->register();
$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".php");
diff --git a/tests/class_loader/class_loader_test.php b/tests/class_loader/class_loader_test.php
index 80f0b38095..76af4dde37 100644
--- a/tests/class_loader/class_loader_test.php
+++ b/tests/class_loader/class_loader_test.php
@@ -7,8 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/cache.php';
-
class phpbb_class_loader_test extends PHPUnit_Framework_TestCase
{
public function setUp()
diff --git a/tests/config/db_test.php b/tests/config/db_test.php
index a9a53541a5..0b8f73d53a 100644
--- a/tests/config/db_test.php
+++ b/tests/config/db_test.php
@@ -7,8 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/cache.php';
-
class phpbb_config_db_test extends phpbb_database_test_case
{
private $cache;
diff --git a/tests/cron/manager_test.php b/tests/cron/manager_test.php
index f433fc9a9b..3e40a6d338 100644
--- a/tests/cron/manager_test.php
+++ b/tests/cron/manager_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/extension_manager.php';
require_once dirname(__FILE__) . '/includes/cron/task/core/dummy_task.php';
require_once dirname(__FILE__) . '/includes/cron/task/core/second_dummy_task.php';
require_once dirname(__FILE__) . '/ext/testext/cron/dummy_task.php';
diff --git a/tests/cron/task_provider_test.php b/tests/cron/task_provider_test.php
index 4547c61a55..b42f40bb4a 100644
--- a/tests/cron/task_provider_test.php
+++ b/tests/cron/task_provider_test.php
@@ -7,8 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/extension_manager.php';
-
class phpbb_cron_task_provider_test extends PHPUnit_Framework_TestCase
{
public function setUp()
diff --git a/tests/dbal/case_test.php b/tests/dbal/case_test.php
new file mode 100644
index 0000000000..57a1729a39
--- /dev/null
+++ b/tests/dbal/case_test.php
@@ -0,0 +1,69 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_dbal_case_test extends phpbb_database_test_case
+{
+ public function getDataSet()
+ {
+ return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/config.xml');
+ }
+
+ public function test_case_int()
+ {
+ $db = $this->new_dbal();
+
+ $sql = 'SELECT ' . $db->sql_case('1 = 1', '1', '2') . ' AS test_num
+ FROM phpbb_config';
+ $result = $db->sql_query_limit($sql, 1);
+
+ $this->assertEquals(1, (int) $db->sql_fetchfield('test_num'));
+
+ $sql = 'SELECT ' . $db->sql_case('1 = 0', '1', '2') . ' AS test_num
+ FROM phpbb_config';
+ $result = $db->sql_query_limit($sql, 1);
+
+ $this->assertEquals(2, (int) $db->sql_fetchfield('test_num'));
+ }
+
+ public function test_case_string()
+ {
+ $db = $this->new_dbal();
+
+ $sql = 'SELECT ' . $db->sql_case('1 = 1', "'foo'", "'bar'") . ' AS test_string
+ FROM phpbb_config';
+ $result = $db->sql_query_limit($sql, 1);
+
+ $this->assertEquals('foo', $db->sql_fetchfield('test_string'));
+
+ $sql = 'SELECT ' . $db->sql_case('1 = 0', "'foo'", "'bar'") . ' AS test_string
+ FROM phpbb_config';
+ $result = $db->sql_query_limit($sql, 1);
+
+ $this->assertEquals('bar', $db->sql_fetchfield('test_string'));
+ }
+
+ public function test_case_column()
+ {
+ $db = $this->new_dbal();
+
+ $sql = 'SELECT ' . $db->sql_case("config_name = 'config1'", 'config_name', 'config_value') . " AS test_string
+ FROM phpbb_config
+ WHERE config_name = 'config1'";
+ $result = $db->sql_query_limit($sql, 1);
+
+ $this->assertEquals('config1', $db->sql_fetchfield('test_string'));
+
+ $sql = 'SELECT ' . $db->sql_case("config_name = 'config1'", 'config_name', 'config_value') . " AS test_string
+ FROM phpbb_config
+ WHERE config_value = 'bar'";
+ $result = $db->sql_query_limit($sql, 1);
+
+ $this->assertEquals('bar', $db->sql_fetchfield('test_string'));
+ }
+}
diff --git a/tests/dbal/concatenate_test.php b/tests/dbal/concatenate_test.php
new file mode 100644
index 0000000000..0891fa58a0
--- /dev/null
+++ b/tests/dbal/concatenate_test.php
@@ -0,0 +1,64 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_dbal_concatenate_test extends phpbb_database_test_case
+{
+ public function getDataSet()
+ {
+ return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/config.xml');
+ }
+
+ public function test_concatenate_string()
+ {
+ $db = $this->new_dbal();
+
+ $sql = 'SELECT config_name, ' . $db->sql_concatenate('config_name', "'" . $db->sql_escape('append') . "'") . ' AS string
+ FROM phpbb_config';
+ $result = $db->sql_query($sql);
+
+ $db->sql_return_on_error(false);
+
+ $this->assertEquals(array(
+ array(
+ 'config_name' => 'config1',
+ 'string' => 'config1append',
+ ),
+ array(
+ 'config_name' => 'config2',
+ 'string' => 'config2append',
+ ),
+ ),
+ $db->sql_fetchrowset($result)
+ );
+ }
+
+ public function test_concatenate_statement()
+ {
+ $db = $this->new_dbal();
+
+ $sql = 'SELECT config_name, ' . $db->sql_concatenate('config_name', 'config_value') . ' AS string
+ FROM phpbb_config';
+ $result = $db->sql_query($sql);
+
+ $db->sql_return_on_error(false);
+
+ $this->assertEquals(array(
+ array(
+ 'config_name' => 'config1',
+ 'string' => 'config1foo',
+ ),
+ array(
+ 'config_name' => 'config2',
+ 'string' => 'config2bar',
+ ),
+ ),
+ $db->sql_fetchrowset($result)
+ );
+ }
+}
diff --git a/tests/dbal/order_lower_test.php b/tests/dbal/order_lower_test.php
index e16c0c20ee..84d454742f 100644
--- a/tests/dbal/order_lower_test.php
+++ b/tests/dbal/order_lower_test.php
@@ -14,7 +14,7 @@ class phpbb_dbal_order_lower_test extends phpbb_database_test_case
return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/styles.xml');
}
- public function test_cross_join()
+ public function test_order_lower()
{
$db = $this->new_dbal();
diff --git a/tests/download/http_byte_range_test.php b/tests/download/http_byte_range_test.php
index b93c1b630c..23b9169fe3 100644
--- a/tests/download/http_byte_range_test.php
+++ b/tests/download/http_byte_range_test.php
@@ -8,7 +8,6 @@
*/
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_download.php';
-require_once dirname(__FILE__) . '/../mock/request.php';
class phpbb_download_http_byte_range_test extends phpbb_test_case
{
diff --git a/tests/extension/finder_test.php b/tests/extension/finder_test.php
index f7e9bd57bb..622f404786 100644
--- a/tests/extension/finder_test.php
+++ b/tests/extension/finder_test.php
@@ -7,9 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/cache.php';
-require_once dirname(__FILE__) . '/../mock/extension_manager.php';
-
class phpbb_extension_finder_test extends phpbb_test_case
{
protected $extension_manager;
diff --git a/tests/extension/manager_test.php b/tests/extension/manager_test.php
index 0a689916c7..45bed247ae 100644
--- a/tests/extension/manager_test.php
+++ b/tests/extension/manager_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/cache.php';
require_once dirname(__FILE__) . '/ext/bar/ext.php';
require_once dirname(__FILE__) . '/ext/foo/ext.php';
require_once dirname(__FILE__) . '/ext/vendor/moo/ext.php';
diff --git a/tests/functional/extension_controller_test.php b/tests/functional/extension_controller_test.php
index 4ee0e68718..e9409d9d3f 100644
--- a/tests/functional/extension_controller_test.php
+++ b/tests/functional/extension_controller_test.php
@@ -65,15 +65,6 @@ class phpbb_functional_extension_controller_test extends phpbb_functional_test_c
}
}
- public static function tearDownAfterClass()
- {
- $phpbb_root_path = self::$config['phpbb_functional_path'];
-
- // @todo delete the fixtures from the $phpbb_root_path board
- // Note that it might be best to find a public domain function
- // and port it into here instead of writing it from scratch
- }
-
public function setUp()
{
parent::setUp();
diff --git a/tests/functional/fileupload_form_test.php b/tests/functional/fileupload_form_test.php
new file mode 100644
index 0000000000..6ba55eeba7
--- /dev/null
+++ b/tests/functional/fileupload_form_test.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ *
+ * @package testing
+ * @copyright (c) 2012 phpBB Group
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+ *
+ */
+
+/**
+ * @group functional
+ */
+class phpbb_functional_fileupload_form_test extends phpbb_functional_test_case
+{
+ private $path;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->path = __DIR__ . '/fixtures/files/';
+ $this->add_lang('posting');
+ $this->login();
+ }
+
+ public function test_empty_file()
+ {
+ $crawler = $this->request('GET', 'posting.php?mode=reply&f=2&t=1&sid=' . $this->sid);
+ $form = $crawler->selectButton('add_file')->form();
+ $form['fileupload']->upload($this->path . 'empty.png');
+ $crawler = $this->client->submit($form);
+ $this->assertEquals($this->lang('ATTACHED_IMAGE_NOT_IMAGE'), $crawler->filter('div#message p')->text());
+ }
+
+ public function test_invalid_extension()
+ {
+ $crawler = $this->request('GET', 'posting.php?mode=reply&f=2&t=1&sid=' . $this->sid);
+ $form = $crawler->selectButton('add_file')->form();
+ $form['fileupload']->upload($this->path . 'illegal-extension.bif');
+ $crawler = $this->client->submit($form);
+ $this->assertEquals($this->lang('DISALLOWED_EXTENSION', 'bif'), $crawler->filter('p.error')->text());
+ }
+
+ public function test_too_large()
+ {
+ $this->markTestIncomplete('Functional tests use an admin account which ignores maximum upload size.');
+ $crawler = $this->request('GET', 'posting.php?mode=reply&f=2&t=1&sid=' . $this->sid);
+ $form = $crawler->selectButton('add_file')->form();
+ $form['fileupload']->upload($this->path . 'too-large.png');
+ $crawler = $this->client->submit($form);
+ $this->assertEquals($this->lang('WRONG_FILESIZE', '256', 'KiB'), $crawler->filter('p.error')->text());
+ }
+
+ public function test_valid_file()
+ {
+ $crawler = $this->request('GET', 'posting.php?mode=reply&f=2&t=1&sid=' . $this->sid);
+ $form = $crawler->selectButton('add_file')->form();
+ $form['fileupload']->upload($this->path . 'valid.jpg');
+ $crawler = $this->client->submit($form);
+ $this->assertEquals(0, $crawler->filter('p.error')->count());
+ $this->assertContains($this->lang('POSTED_ATTACHMENTS'), $crawler->filter('#postform h3')->eq(1)->text());
+ }
+}
diff --git a/tests/functional/fileupload_remote_test.php b/tests/functional/fileupload_remote_test.php
new file mode 100644
index 0000000000..0deb79acf6
--- /dev/null
+++ b/tests/functional/fileupload_remote_test.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ *
+ * @package testing
+ * @copyright (c) 2012 phpBB Group
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+ *
+ */
+
+/**
+ * @group functional
+ */
+class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case
+{
+ public function setUp()
+ {
+ parent::setUp();
+ // Only doing this within the functional framework because we need a
+ // URL
+
+ // Global $config required by unique_id
+ // Global $user required by fileupload::remote_upload
+ global $config, $user;
+
+ if (!is_array($config))
+ {
+ $config = array();
+ }
+
+ $config['rand_seed'] = '';
+ $config['rand_seed_last_update'] = time() + 600;
+
+ $user = new phpbb_mock_user();
+ $user->lang = new phpbb_mock_lang();
+ }
+
+ public function tearDown()
+ {
+ global $config, $user;
+ $user = null;
+ $config = array();
+ }
+
+ public function test_invalid_extension()
+ {
+ $upload = new fileupload('', array('jpg'), 100);
+ $file = $upload->remote_upload('http://example.com/image.gif');
+ $this->assertEquals('URL_INVALID', $file->error[0]);
+ }
+
+ public function test_non_existant()
+ {
+ $upload = new fileupload('', array('jpg'), 100);
+ $file = $upload->remote_upload('http://example.com/image.jpg');
+ $this->assertEquals('EMPTY_REMOTE_DATA', $file->error[0]);
+ }
+
+ public function test_successful_upload()
+ {
+ $upload = new fileupload('', array('gif'), 1000);
+ $file = $upload->remote_upload($this->root_url . 'styles/prosilver/theme/images/forum_read.gif');
+ $this->assertEquals(0, sizeof($file->error));
+ $this->assertTrue(file_exists($file->filename));
+ }
+
+ public function test_too_large()
+ {
+ $upload = new fileupload('', array('gif'), 100);
+ $file = $upload->remote_upload($this->root_url . 'styles/prosilver/theme/images/forum_read.gif');
+ $this->assertEquals('WRONG_FILESIZE', $file->error[0]);
+ }
+}
diff --git a/tests/functional/fixtures/files/empty.png b/tests/functional/fixtures/files/empty.png
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/functional/fixtures/files/empty.png
diff --git a/tests/functional/fixtures/files/illegal-extension.bif b/tests/functional/fixtures/files/illegal-extension.bif
new file mode 100644
index 0000000000..3cd5038e38
--- /dev/null
+++ b/tests/functional/fixtures/files/illegal-extension.bif
Binary files differ
diff --git a/tests/functional/fixtures/files/too-large.png b/tests/functional/fixtures/files/too-large.png
new file mode 100644
index 0000000000..ed4b0abd80
--- /dev/null
+++ b/tests/functional/fixtures/files/too-large.png
Binary files differ
diff --git a/tests/functional/fixtures/files/valid.jpg b/tests/functional/fixtures/files/valid.jpg
new file mode 100644
index 0000000000..95a87ddbdf
--- /dev/null
+++ b/tests/functional/fixtures/files/valid.jpg
Binary files differ
diff --git a/tests/functions_acp/build_cfg_template_test.php b/tests/functions_acp/build_cfg_template_test.php
index bb479ffac9..12121f6678 100644
--- a/tests/functions_acp/build_cfg_template_test.php
+++ b/tests/functions_acp/build_cfg_template_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/lang.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_acp.php';
class phpbb_functions_acp_build_cfg_template_test extends phpbb_test_case
diff --git a/tests/functions_acp/build_select_test.php b/tests/functions_acp/build_select_test.php
index 782acae1fa..aca49b7655 100644
--- a/tests/functions_acp/build_select_test.php
+++ b/tests/functions_acp/build_select_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/lang.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_acp.php';
class phpbb_functions_acp_built_select_test extends phpbb_test_case
diff --git a/tests/functions_acp/h_radio_test.php b/tests/functions_acp/h_radio_test.php
index 02b2444a9e..a61f2e8975 100644
--- a/tests/functions_acp/h_radio_test.php
+++ b/tests/functions_acp/h_radio_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/lang.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_acp.php';
class phpbb_functions_acp_h_radio_test extends phpbb_test_case
diff --git a/tests/functions_acp/validate_config_vars_test.php b/tests/functions_acp/validate_config_vars_test.php
index 55441561a6..7cd7fa3892 100644
--- a/tests/functions_acp/validate_config_vars_test.php
+++ b/tests/functions_acp/validate_config_vars_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/lang.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_acp.php';
class phpbb_functions_acp_validate_config_vars_test extends phpbb_test_case
diff --git a/tests/functions_acp/validate_range_test.php b/tests/functions_acp/validate_range_test.php
index 34ce848e76..8606158251 100644
--- a/tests/functions_acp/validate_range_test.php
+++ b/tests/functions_acp/validate_range_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/lang.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/utf/utf_tools.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_acp.php';
diff --git a/tests/mock/filespec.php b/tests/mock/filespec.php
new file mode 100644
index 0000000000..9d2a5c84de
--- /dev/null
+++ b/tests/mock/filespec.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * @package testing
+ * @copyright (c) 2012 phpBB Group
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+ *
+ */
+
+/**
+ * Mock filespec class with some basic values to help with testing the
+ * fileupload class
+ */
+class phpbb_mock_filespec
+{
+ public $filesize;
+ public $realname;
+ public $extension;
+ public $width;
+ public $height;
+ public $error = array();
+
+ public function check_content($disallowed_content)
+ {
+ return true;
+ }
+
+ public function get($property)
+ {
+ return $this->$property;
+ }
+}
diff --git a/tests/mock/fileupload.php b/tests/mock/fileupload.php
new file mode 100644
index 0000000000..409036ba63
--- /dev/null
+++ b/tests/mock/fileupload.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ *
+ * @package testing
+ * @copyright (c) 2012 phpBB Group
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+ *
+ */
+
+/**
+ * Mock fileupload class with some basic values to help with testing the
+ * filespec class
+ */
+class phpbb_mock_fileupload
+{
+ public $max_filesize = 100;
+ public $error_prefix = '';
+
+ public function valid_dimensions($filespec)
+ {
+ return true;
+ }
+
+ /**
+ * Copied verbatim from phpBB/includes/functions_upload.php's fileupload
+ * class to ensure the correct behaviour of filespec::move_file.
+ *
+ * Maps file extensions to the constant in second index of the array
+ * returned by getimagesize()
+ */
+ public function image_types()
+ {
+ return array(
+ IMAGETYPE_GIF => array('gif'),
+ IMAGETYPE_JPEG => array('jpg', 'jpeg'),
+ IMAGETYPE_PNG => array('png'),
+ IMAGETYPE_SWF => array('swf'),
+ IMAGETYPE_PSD => array('psd'),
+ IMAGETYPE_BMP => array('bmp'),
+ IMAGETYPE_TIFF_II => array('tif', 'tiff'),
+ IMAGETYPE_TIFF_MM => array('tif', 'tiff'),
+ IMAGETYPE_JPC => array('jpg', 'jpeg'),
+ IMAGETYPE_JP2 => array('jpg', 'jpeg'),
+ IMAGETYPE_JPX => array('jpg', 'jpeg'),
+ IMAGETYPE_JB2 => array('jpg', 'jpeg'),
+ IMAGETYPE_SWC => array('swc'),
+ IMAGETYPE_IFF => array('iff'),
+ IMAGETYPE_WBMP => array('wbmp'),
+ IMAGETYPE_XBM => array('xbm'),
+ );
+ }
+}
diff --git a/tests/mock_user.php b/tests/mock/user.php
index ec14ce430e..ec14ce430e 100644
--- a/tests/mock_user.php
+++ b/tests/mock/user.php
diff --git a/tests/security/base.php b/tests/security/base.php
index 82e4dda9d0..08878ad60d 100644
--- a/tests/security/base.php
+++ b/tests/security/base.php
@@ -7,8 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/request.php';
-
abstract class phpbb_security_test_base extends phpbb_test_case
{
/**
diff --git a/tests/session/continue_test.php b/tests/session/continue_test.php
index c4f7f8d75b..ad78d92299 100644
--- a/tests/session/continue_test.php
+++ b/tests/session/continue_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/cache.php';
require_once dirname(__FILE__) . '/testable_factory.php';
class phpbb_session_continue_test extends phpbb_database_test_case
diff --git a/tests/session/init_test.php b/tests/session/init_test.php
index 2ce6c4a4ac..830de34ed0 100644
--- a/tests/session/init_test.php
+++ b/tests/session/init_test.php
@@ -7,7 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/cache.php';
require_once dirname(__FILE__) . '/testable_factory.php';
class phpbb_session_init_test extends phpbb_database_test_case
diff --git a/tests/session/testable_factory.php b/tests/session/testable_factory.php
index a042085908..cb85a01c5c 100644
--- a/tests/session/testable_factory.php
+++ b/tests/session/testable_factory.php
@@ -7,9 +7,6 @@
*
*/
-require_once dirname(__FILE__) . '/../mock/request.php';
-require_once dirname(__FILE__) . '/../mock/session_testable.php';
-
/**
* This class exists to setup an instance of phpbb's session class for testing.
*
diff --git a/tests/template/invalid_constructs_test.php b/tests/template/invalid_constructs_test.php
new file mode 100644
index 0000000000..19d192b8b6
--- /dev/null
+++ b/tests/template/invalid_constructs_test.php
@@ -0,0 +1,87 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+require_once dirname(__FILE__) . '/template_test_case.php';
+
+class phpbb_template_invalid_constructs_test extends phpbb_template_template_test_case
+{
+ public function template_data()
+ {
+ return array(
+ array(
+ 'Unknown tag',
+ 'invalid/unknown_tag.html',
+ array(),
+ array(),
+ array(),
+ 'invalid/output/unknown_tag.html',
+ ),
+ /*
+ * Produces a parse error which is fatal, therefore
+ * destroying the test suite.
+ array(
+ 'ENDIF without IF',
+ 'invalid/endif_without_if.html',
+ array(),
+ array(),
+ array(),
+ 'invalid/output/endif_without_if.html',
+ ),
+ */
+ );
+ }
+
+ public function template_data_error()
+ {
+ return array(
+ array(
+ 'Include a nonexistent file',
+ 'invalid/include_nonexistent_file.html',
+ array(),
+ array(),
+ array(),
+ E_USER_ERROR,
+ 'invalid/output/include_nonexistent_file.html',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider template_data
+ */
+ public function test_template($description, $file, $vars, $block_vars, $destroy, $expected)
+ {
+ $cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php';
+
+ $this->assertFileNotExists($cache_file);
+
+ $expected = file_get_contents(dirname(__FILE__) . '/templates/' . $expected);
+ // apparently the template engine does not put
+ // the trailing newline into compiled templates
+ $expected = trim($expected);
+ $this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file);
+ }
+
+ /**
+ * @dataProvider template_data_error
+ */
+ public function test_template_error($description, $file, $vars, $block_vars, $destroy, $error, $expected)
+ {
+ $cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php';
+
+ $this->assertFileNotExists($cache_file);
+
+ $expected = file_get_contents(dirname(__FILE__) . '/templates/' . $expected);
+ // apparently the template engine does not put
+ // the trailing newline into compiled templates
+ $expected = trim($expected);
+ $this->setExpectedTriggerError($error, $expected);
+ $this->run_template($file, $vars, $block_vars, $destroy, '', $cache_file);
+ }
+}
diff --git a/tests/template/parent_templates/parent_and_child.js b/tests/template/parent_templates/parent_and_child.js
new file mode 100644
index 0000000000..6d9bb163bf
--- /dev/null
+++ b/tests/template/parent_templates/parent_and_child.js
@@ -0,0 +1 @@
+// JavaScript file in a parent style.
diff --git a/tests/template/parent_templates/parent_only.js b/tests/template/parent_templates/parent_only.js
new file mode 100644
index 0000000000..9c3007d83f
--- /dev/null
+++ b/tests/template/parent_templates/parent_only.js
@@ -0,0 +1 @@
+// JavaScript file only in parent style.
diff --git a/tests/template/template_includejs_test.php b/tests/template/template_includejs_test.php
index 632fde61d1..a8f9a9037f 100644
--- a/tests/template/template_includejs_test.php
+++ b/tests/template/template_includejs_test.php
@@ -17,15 +17,14 @@ class phpbb_template_template_includejs_test extends phpbb_template_template_tes
$this->setup_engine(array('assets_version' => 1));
// Prepare correct result
- $dir = dirname(__FILE__);
$scripts = array(
- '<script src="' . $dir . '/templates/parent_and_child.html?assets_version=1"></script>',
- '<script src="' . $dir . '/parent_templates/parent_only.html?assets_version=1"></script>',
- '<script src="' . $dir . '/templates/child_only.html?assets_version=1"></script>'
+ '<script src="' . $this->test_path . '/templates/parent_and_child.js?assets_version=1"></script>',
+ '<script src="' . $this->test_path . '/parent_templates/parent_only.js?assets_version=1"></script>',
+ '<script src="' . $this->test_path . '/templates/child_only.js?assets_version=1"></script>'
);
// Run test
$cache_file = $this->template->cachepath . 'includejs.html.php';
- $this->run_template('includejs.html', array('PARENT' => 'parent_only.html'), array(), array(), implode('', $scripts), $cache_file);
+ $this->run_template('includejs.html', array('PARENT' => 'parent_only.js'), array(), array(), implode('', $scripts), $cache_file);
}
}
diff --git a/tests/template/template_locate_test.php b/tests/template/template_locate_test.php
index d6e2e82a47..be9ae06809 100644
--- a/tests/template/template_locate_test.php
+++ b/tests/template/template_locate_test.php
@@ -17,21 +17,21 @@ class phpbb_template_template_locate_test extends phpbb_template_template_test_c
// First element of the array is test name - keep them distinct
array(
'simple inheritance - only parent template exists',
- dirname(__FILE__) . '/parent_templates/parent_only.html',
+ $this->test_path . '/parent_templates/parent_only.html',
'parent_only.html',
false,
true,
),
array(
'simple inheritance - only child template exists',
- dirname(__FILE__) . '/templates/child_only.html',
+ $this->test_path . '/templates/child_only.html',
'child_only.html',
false,
true,
),
array(
'simple inheritance - both parent and child templates exist',
- dirname(__FILE__) . '/templates/parent_and_child.html',
+ $this->test_path . '/templates/parent_and_child.html',
'parent_and_child.html',
false,
true,
diff --git a/tests/template/template_test.php b/tests/template/template_test.php
index 739bbe9387..f8677ed913 100644
--- a/tests/template/template_test.php
+++ b/tests/template/template_test.php
@@ -277,7 +277,7 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
$this->template->set_filenames(array('test' => $filename));
$this->assertFileNotExists($this->template_path . '/' . $filename, 'Testing missing file, file cannot exist');
- $expecting = sprintf('style resource locator: File for handle test does not exist. Could not find: %s', realpath($this->template_path . '/../') . '/templates/' . $filename);
+ $expecting = sprintf('style resource locator: File for handle test does not exist. Could not find: %s', $this->test_path . '/templates/' . $filename);
$this->setExpectedTriggerError(E_USER_ERROR, $expecting);
$this->display('test');
diff --git a/tests/template/template_test_case.php b/tests/template/template_test_case.php
index d660aa3f56..6f76cb049d 100644
--- a/tests/template/template_test_case.php
+++ b/tests/template/template_test_case.php
@@ -8,7 +8,6 @@
*/
require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
-require_once dirname(__FILE__) . '/../mock/extension_manager.php';
class phpbb_template_template_test_case extends phpbb_test_case
{
@@ -18,6 +17,8 @@ class phpbb_template_template_test_case extends phpbb_test_case
protected $style_resource_locator;
protected $style_provider;
+ protected $test_path = 'tests/template';
+
// Keep the contents of the cache for debugging?
const PRESERVE_CACHE = true;
@@ -63,7 +64,7 @@ class phpbb_template_template_test_case extends phpbb_test_case
$defaults = $this->config_defaults();
$config = new phpbb_config(array_merge($defaults, $new_config));
- $this->template_path = dirname(__FILE__) . '/templates';
+ $this->template_path = $this->test_path . '/templates';
$this->style_resource_locator = new phpbb_style_resource_locator();
$this->style_provider = new phpbb_style_path_provider();
$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator);
diff --git a/tests/template/template_test_case_with_tree.php b/tests/template/template_test_case_with_tree.php
index 9522c97330..05ccb7ee55 100644
--- a/tests/template/template_test_case_with_tree.php
+++ b/tests/template/template_test_case_with_tree.php
@@ -18,8 +18,8 @@ class phpbb_template_template_test_case_with_tree extends phpbb_template_templat
$defaults = $this->config_defaults();
$config = new phpbb_config(array_merge($defaults, $new_config));
- $this->template_path = dirname(__FILE__) . '/templates';
- $this->parent_template_path = dirname(__FILE__) . '/parent_templates';
+ $this->template_path = $this->test_path . '/templates';
+ $this->parent_template_path = $this->test_path . '/parent_templates';
$this->style_resource_locator = new phpbb_style_resource_locator();
$this->style_provider = new phpbb_style_path_provider();
$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator);
diff --git a/tests/template/templates/child_only.js b/tests/template/templates/child_only.js
new file mode 100644
index 0000000000..542b26526c
--- /dev/null
+++ b/tests/template/templates/child_only.js
@@ -0,0 +1 @@
+// JavaScript file only in a child style.
diff --git a/tests/template/templates/includejs.html b/tests/template/templates/includejs.html
index 186fc30b43..8a2587d76b 100644
--- a/tests/template/templates/includejs.html
+++ b/tests/template/templates/includejs.html
@@ -1,5 +1,5 @@
-<!-- INCLUDEJS parent_and_child.html -->
+<!-- INCLUDEJS parent_and_child.js -->
<!-- INCLUDEJS {PARENT} -->
-<!-- DEFINE $TEST = 'child_only.html' -->
+<!-- DEFINE $TEST = 'child_only.js' -->
<!-- INCLUDEJS {$TEST} -->
{SCRIPTS} \ No newline at end of file
diff --git a/tests/template/templates/invalid/endif_without_if.html b/tests/template/templates/invalid/endif_without_if.html
new file mode 100644
index 0000000000..e371ffd150
--- /dev/null
+++ b/tests/template/templates/invalid/endif_without_if.html
@@ -0,0 +1 @@
+<!-- ENDIF -->
diff --git a/tests/template/templates/invalid/include_nonexistent_file.html b/tests/template/templates/invalid/include_nonexistent_file.html
new file mode 100644
index 0000000000..617d2fdaaa
--- /dev/null
+++ b/tests/template/templates/invalid/include_nonexistent_file.html
@@ -0,0 +1 @@
+<!-- INCLUDE nonexistent.html -->
diff --git a/tests/template/templates/invalid/output/endif_without_if.html b/tests/template/templates/invalid/output/endif_without_if.html
new file mode 100644
index 0000000000..5f2239c964
--- /dev/null
+++ b/tests/template/templates/invalid/output/endif_without_if.html
@@ -0,0 +1 @@
+Parse error (fatal, destroys php runtime).
diff --git a/tests/template/templates/invalid/output/include_nonexistent_file.html b/tests/template/templates/invalid/output/include_nonexistent_file.html
new file mode 100644
index 0000000000..8a118d2713
--- /dev/null
+++ b/tests/template/templates/invalid/output/include_nonexistent_file.html
@@ -0,0 +1 @@
+style resource locator: File for handle nonexistent.html does not exist. Could not find:
diff --git a/tests/template/templates/invalid/output/unknown_tag.html b/tests/template/templates/invalid/output/unknown_tag.html
new file mode 100644
index 0000000000..1489e5e31a
--- /dev/null
+++ b/tests/template/templates/invalid/output/unknown_tag.html
@@ -0,0 +1 @@
+<!-- UNKNOWNTAG variable.html -->
diff --git a/tests/template/templates/invalid/unknown_tag.html b/tests/template/templates/invalid/unknown_tag.html
new file mode 100644
index 0000000000..1489e5e31a
--- /dev/null
+++ b/tests/template/templates/invalid/unknown_tag.html
@@ -0,0 +1 @@
+<!-- UNKNOWNTAG variable.html -->
diff --git a/tests/template/templates/parent_and_child.js b/tests/template/templates/parent_and_child.js
new file mode 100644
index 0000000000..d544d94d83
--- /dev/null
+++ b/tests/template/templates/parent_and_child.js
@@ -0,0 +1 @@
+// JavaScript file in a child style.
diff --git a/tests/test_framework/phpbb_database_connection_odbc_pdo_wrapper.php b/tests/test_framework/phpbb_database_connection_odbc_pdo_wrapper.php
new file mode 100644
index 0000000000..ec59fa3886
--- /dev/null
+++ b/tests/test_framework/phpbb_database_connection_odbc_pdo_wrapper.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+if (!class_exists('PDO'))
+{
+ return;
+}
+
+/**
+* Used for passing in information about the PDO driver
+* since the PDO class reveals nothing about the DSN that
+* the user provided.
+*
+* This is used in the custom PHPUnit ODBC driver
+*/
+class phpbb_database_connection_odbc_pdo_wrapper extends PDO
+{
+ // Name of the driver being used (i.e. mssql, firebird)
+ public $driver = '';
+
+ // Version number of driver since PDO::getAttribute(PDO::ATTR_CLIENT_VERSION) is pretty useless for this
+ public $version = 0;
+
+ function __construct($dbms, $version, $dsn, $user, $pass)
+ {
+ $this->driver = $dbms;
+ $this->version = (double) $version;
+
+ parent::__construct($dsn, $user, $pass);
+ }
+}
diff --git a/tests/test_framework/phpbb_database_test_case.php b/tests/test_framework/phpbb_database_test_case.php
index e742b543b0..bb86df0ef0 100644
--- a/tests/test_framework/phpbb_database_test_case.php
+++ b/tests/test_framework/phpbb_database_test_case.php
@@ -28,6 +28,28 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test
);
}
+ public function createXMLDataSet($path)
+ {
+ $db_config = $this->get_database_config();
+
+ // Firebird requires table and column names to be uppercase
+ if ($db_config['dbms'] == 'firebird')
+ {
+ $xml_data = file_get_contents($path);
+ $xml_data = preg_replace_callback('/(?:(<table name="))([a-z_]+)(?:(">))/', 'phpbb_database_test_case::to_upper', $xml_data);
+ $xml_data = preg_replace_callback('/(?:(<column>))([a-z_]+)(?:(<\/column>))/', 'phpbb_database_test_case::to_upper', $xml_data);
+
+ $new_fixture = tmpfile();
+ fwrite($new_fixture, $xml_data);
+ fseek($new_fixture, 0);
+
+ $meta_data = stream_get_meta_data($new_fixture);
+ $path = $meta_data['uri'];
+ }
+
+ return parent::createXMLDataSet($path);
+ }
+
public function get_test_case_helpers()
{
if (!$this->test_case_helpers)
@@ -106,4 +128,17 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test
{
return new phpbb_database_test_connection_manager($config);
}
+
+ /**
+ * Converts a match in the middle of a string to uppercase.
+ * This is necessary for transforming the fixture information for Firebird tests
+ *
+ * @param $matches The array of matches from a regular expression
+ *
+ * @return string The string with the specified match converted to uppercase
+ */
+ public static function to_upper($matches)
+ {
+ return $matches[1] . strtoupper($matches[2]) . $matches[3];
+ }
}
diff --git a/tests/test_framework/phpbb_database_test_connection_manager.php b/tests/test_framework/phpbb_database_test_connection_manager.php
index c734c90a1a..25e0972f42 100644
--- a/tests/test_framework/phpbb_database_test_connection_manager.php
+++ b/tests/test_framework/phpbb_database_test_connection_manager.php
@@ -8,6 +8,7 @@
*/
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_install.php';
+require_once dirname(__FILE__) . '/phpbb_database_connection_odbc_pdo_wrapper.php';
class phpbb_database_test_connection_manager
{
@@ -62,6 +63,13 @@ class phpbb_database_test_connection_manager
// e.g. Driver={SQL Server Native Client 10.0};Server=(local)\SQLExpress;
$dsn .= $this->config['dbhost'];
+ // Primarily for MSSQL Native/Azure as ODBC needs it in $dbhost, attached to the Server param
+ if ($this->config['dbport'])
+ {
+ $port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
+ $dsn .= $port_delimiter . $this->config['dbport'];
+ }
+
if ($use_db)
{
$dsn .= ';Database=' . $this->config['dbname'];
@@ -80,12 +88,52 @@ class phpbb_database_test_connection_manager
{
$dsn .= ';dbname=' . $this->config['dbname'];
}
+ else if ($this->dbms['PDO'] == 'pgsql')
+ {
+ // Postgres always connects to a
+ // database. If the database is not
+ // specified here, but the username
+ // is specified, then connection
+ // will be to the database named
+ // as the username.
+ //
+ // For greater compatibility, connect
+ // instead to postgres database which
+ // should always exist:
+ // http://www.postgresql.org/docs/9.0/static/manage-ag-templatedbs.html
+ $dsn .= ';dbname=postgres';
+ }
break;
}
+ // These require different connection strings on the phpBB side than they do in PDO
+ // so you must provide a DSN string for ODBC separately
+ if (!empty($this->config['custom_dsn']) && ($this->config['dbms'] == 'mssql' || $this->config['dbms'] == 'firebird'))
+ {
+ $dsn = 'odbc:' . $this->config['custom_dsn'];
+ }
+
try
{
- $this->pdo = new PDO($dsn, $this->config['dbuser'], $this->config['dbpasswd']);
+ switch ($this->config['dbms'])
+ {
+ case 'mssql':
+ case 'mssql_odbc':
+ $this->pdo = new phpbb_database_connection_odbc_pdo_wrapper('mssql', 0, $dsn, $this->config['dbuser'], $this->config['dbpasswd']);
+ break;
+
+ case 'firebird':
+ if (!empty($this->config['custom_dsn']))
+ {
+ $this->pdo = new phpbb_database_connection_odbc_pdo_wrapper('firebird', 0, $dsn, $this->config['dbuser'], $this->config['dbpasswd']);
+ break;
+ }
+ // Fall through if they're using the firebird PDO driver and not the generic ODBC driver
+
+ default:
+ $this->pdo = new PDO($dsn, $this->config['dbuser'], $this->config['dbpasswd']);
+ break;
+ }
}
catch (PDOException $e)
{
@@ -93,8 +141,7 @@ class phpbb_database_test_connection_manager
throw new Exception("Unable do connect to $cleaned_dsn using PDO with error: {$e->getMessage()}");
}
- // good for debug
- // $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/**
@@ -125,12 +172,41 @@ class phpbb_database_test_connection_manager
}
break;
+ case 'firebird':
+ $this->connect();
+ // Drop all of the tables
+ foreach ($this->get_tables() as $table)
+ {
+ $this->pdo->exec('DROP TABLE ' . $table);
+ }
+ $this->purge_extras();
+ break;
+
+ case 'oracle':
+ $this->connect();
+ // Drop all of the tables
+ foreach ($this->get_tables() as $table)
+ {
+ $this->pdo->exec('DROP TABLE ' . $table . ' CASCADE CONSTRAINTS');
+ }
+ $this->purge_extras();
+ break;
+
default:
$this->connect(false);
try
{
$this->pdo->exec('DROP DATABASE ' . $this->config['dbname']);
+
+ try
+ {
+ $this->pdo->exec('CREATE DATABASE ' . $this->config['dbname']);
+ }
+ catch (PDOException $e)
+ {
+ throw new Exception("Unable to re-create database: {$e->getMessage()}");
+ }
}
catch (PDOException $e)
{
@@ -139,9 +215,8 @@ class phpbb_database_test_connection_manager
{
$this->pdo->exec('DROP TABLE ' . $table);
}
+ $this->purge_extras();
}
-
- $this->pdo->exec('CREATE DATABASE ' . $this->config['dbname']);
break;
}
}
@@ -243,7 +318,7 @@ class phpbb_database_test_connection_manager
$filename = $directory . $schema . '_schema.sql';
$queries = file_get_contents($filename);
- $sql = remove_comments($queries);
+ $sql = phpbb_remove_comments($queries);
$sql = split_sql_file($sql, $this->dbms['DELIM']);
@@ -317,4 +392,44 @@ class phpbb_database_test_connection_manager
throw new Exception($message);
}
}
+
+ /**
+ * Removes extra objects from a database. This is for cases where dropping the database fails.
+ */
+ public function purge_extras()
+ {
+ $this->ensure_connected(__METHOD__);
+ $queries = array();
+
+ switch ($this->config['dbms'])
+ {
+ case 'firebird':
+ $sql = 'SELECT RDB$GENERATOR_NAME
+ FROM RDB$GENERATORS
+ WHERE RDB$SYSTEM_FLAG = 0';
+ $result = $this->pdo->query($sql);
+
+ while ($row = $result->fetch(PDO::FETCH_NUM))
+ {
+ $queries[] = 'DROP GENERATOR ' . current($row);
+ }
+ break;
+
+ case 'oracle':
+ $sql = 'SELECT sequence_name
+ FROM USER_SEQUENCES';
+ $result = $this->pdo->query($sql);
+
+ while ($row = $result->fetch(PDO::FETCH_NUM))
+ {
+ $queries[] = 'DROP SEQUENCE ' . current($row);
+ }
+ break;
+ }
+
+ foreach ($queries as $query)
+ {
+ $this->pdo->exec($query);
+ }
+ }
}
diff --git a/tests/test_framework/phpbb_test_case_helpers.php b/tests/test_framework/phpbb_test_case_helpers.php
index 329af2c537..46feef550a 100644
--- a/tests/test_framework/phpbb_test_case_helpers.php
+++ b/tests/test_framework/phpbb_test_case_helpers.php
@@ -84,6 +84,7 @@ class phpbb_test_case_helpers
'dbname' => $dbname,
'dbuser' => $dbuser,
'dbpasswd' => $dbpasswd,
+ 'custom_dsn' => isset($custom_dsn) ? $custom_dsn : '',
));
if (isset($phpbb_functional_url))
@@ -100,7 +101,8 @@ class phpbb_test_case_helpers
'dbport' => isset($_SERVER['PHPBB_TEST_DBPORT']) ? $_SERVER['PHPBB_TEST_DBPORT'] : '',
'dbname' => isset($_SERVER['PHPBB_TEST_DBNAME']) ? $_SERVER['PHPBB_TEST_DBNAME'] : '',
'dbuser' => isset($_SERVER['PHPBB_TEST_DBUSER']) ? $_SERVER['PHPBB_TEST_DBUSER'] : '',
- 'dbpasswd' => isset($_SERVER['PHPBB_TEST_DBPASSWD']) ? $_SERVER['PHPBB_TEST_DBPASSWD'] : ''
+ 'dbpasswd' => isset($_SERVER['PHPBB_TEST_DBPASSWD']) ? $_SERVER['PHPBB_TEST_DBPASSWD'] : '',
+ 'custom_dsn' => isset($_SERVER['PHPBB_TEST_CUSTOM_DSN']) ? $_SERVER['PHPBB_TEST_CUSTOM_DSN'] : '',
));
}
diff --git a/tests/text_processing/censor_text_test.php b/tests/text_processing/censor_text_test.php
index 8fcdb7ef85..043d8eb27d 100644
--- a/tests/text_processing/censor_text_test.php
+++ b/tests/text_processing/censor_text_test.php
@@ -9,8 +9,6 @@
require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_content.php';
-require_once dirname(__FILE__) . '/../mock_user.php';
-require_once dirname(__FILE__) . '/../mock/cache.php';
class phpbb_text_processing_censor_text_test extends phpbb_test_case
{
@@ -19,7 +17,7 @@ class phpbb_text_processing_censor_text_test extends phpbb_test_case
global $cache, $user;
$cache = new phpbb_mock_cache;
$user = new phpbb_mock_user;
-
+
$user->optionset('viewcensors', false);
return array(
@@ -61,7 +59,7 @@ class phpbb_text_processing_censor_text_test extends phpbb_test_case
array('badword1 badword2 badword3 badword4', 'replacement1 replacement2 replacement3 replacement4'),
array('badword1 badword2 badword3 badword4d', 'replacement1 replacement2 replacement3 badword4d'),
array('abadword1 badword2 badword3 badword4', 'replacement1 replacement2 replacement3 replacement4'),
-
+
array("new\nline\ntest", "new\nline\ntest"),
array("tab\ttest\t", "tab\ttest\t"),
array('öäü', 'öäü'),
diff --git a/tests/upload/filespec_test.php b/tests/upload/filespec_test.php
new file mode 100644
index 0000000000..c7ff2e78e0
--- /dev/null
+++ b/tests/upload/filespec_test.php
@@ -0,0 +1,275 @@
+<?php
+/**
+ *
+ * @package testing
+ * @copyright (c) 2012 phpBB Group
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+ *
+ */
+
+require_once __DIR__ . '/../../phpBB/includes/functions.php';
+require_once __DIR__ . '/../../phpBB/includes/utf/utf_tools.php';
+require_once __DIR__ . '/../../phpBB/includes/functions_upload.php';
+
+class phpbb_filespec_test extends phpbb_test_case
+{
+ const TEST_COUNT = 100;
+ const PREFIX = 'phpbb_';
+ const MAX_STR_LEN = 50;
+ const UPLOAD_MAX_FILESIZE = 1000;
+
+ private $config;
+ public $path;
+
+ protected function setUp()
+ {
+ // Global $config required by unique_id
+ // Global $user required by filespec::additional_checks and
+ // filespec::move_file
+ global $config, $user;
+
+ if (!is_array($config))
+ {
+ $config = array();
+ }
+
+ $config['rand_seed'] = '';
+ $config['rand_seed_last_update'] = time() + 600;
+ // This config value is normally pulled from the database which is set
+ // to this value at install time.
+ // See: phpBB/install/schemas/schema_data.sql
+ $config['mime_triggers'] = 'body|head|html|img|plaintext|a href|pre|script|table|title';
+
+ $user = new phpbb_mock_user();
+ $user->lang = new phpbb_mock_lang();
+
+ $this->config = &$config;
+ $this->path = __DIR__ . '/fixture/';
+
+ // Create copies of the files for use in testing move_file
+ $iterator = new DirectoryIterator($this->path);
+ foreach ($iterator as $fileinfo)
+ {
+ if ($fileinfo->isDot() || $fileinfo->isDir())
+ {
+ continue;
+ }
+
+ copy($fileinfo->getPathname(), $this->path . 'copies/' . $fileinfo->getFilename() . '_copy');
+ if ($fileinfo->getFilename() === 'txt')
+ {
+ copy($fileinfo->getPathname(), $this->path . 'copies/' . $fileinfo->getFilename() . '_copy_2');
+ }
+ }
+ }
+
+ private function get_filespec($override = array())
+ {
+ // Initialise a blank filespec object for use with trivial methods
+ $upload_ary = array(
+ 'name' => '',
+ 'type' => '',
+ 'size' => '',
+ 'tmp_name' => '',
+ 'error' => '',
+ );
+
+ return new filespec(array_merge($upload_ary, $override), null);
+ }
+
+ protected function tearDown()
+ {
+ global $user;
+ $this->config = array();
+ $user = null;
+
+ $iterator = new DirectoryIterator($this->path . 'copies');
+ foreach ($iterator as $fileinfo)
+ {
+ $name = $fileinfo->getFilename();
+ if ($name[0] !== '.')
+ {
+ unlink($fileinfo->getPathname());
+ }
+ }
+ }
+
+ public function additional_checks_variables()
+ {
+ // False here just indicates the file is too large and fails the
+ // filespec::additional_checks method because of it. All other code
+ // paths in that method are covered elsewhere.
+ return array(
+ array('gif', true),
+ array('jpg', false),
+ array('png', true),
+ array('tif', false),
+ array('txt', false),
+ );
+ }
+
+ /**
+ * @dataProvider additional_checks_variables
+ */
+ public function test_additional_checks($filename, $expected)
+ {
+ $upload = new phpbb_mock_fileupload();
+ $filespec = $this->get_filespec();
+ $filespec->upload = $upload;
+ $filespec->file_moved = true;
+ $filespec->filesize = $filespec->get_filesize($this->path . $filename);
+
+ $this->assertEquals($expected, $filespec->additional_checks());
+ }
+
+ public function check_content_variables()
+ {
+ // False here indicates that a file is non-binary and contains
+ // disallowed content that makes IE report the mimetype incorrectly.
+ return array(
+ array('gif', true),
+ array('jpg', true),
+ array('png', true),
+ array('tif', true),
+ array('txt', false),
+ );
+ }
+
+ /**
+ * @dataProvider check_content_variables
+ */
+ public function test_check_content($filename, $expected)
+ {
+ $disallowed_content = explode('|', $this->config['mime_triggers']);
+ $filespec = $this->get_filespec(array('tmp_name' => $this->path . $filename));
+ $this->assertEquals($expected, $filespec->check_content($disallowed_content));
+ }
+
+ public function clean_filename_variables()
+ {
+ $chunks = str_split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\'\\" /:*?<>|[];(){},#+=-_`', 8);
+ return array(
+ array($chunks[0] . $chunks[7]),
+ array($chunks[1] . $chunks[8]),
+ array($chunks[2] . $chunks[9]),
+ array($chunks[3] . $chunks[4]),
+ array($chunks[5] . $chunks[6]),
+ );
+ }
+
+ /**
+ * @dataProvider clean_filename_variables
+ */
+ public function test_clean_filename_real($filename)
+ {
+ $bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|');
+ $filespec = $this->get_filespec(array('name' => $filename));
+ $filespec->clean_filename('real', self::PREFIX);
+ $name = $filespec->realname;
+
+ $this->assertEquals(0, preg_match('/%(\w{2})/', $name));
+ foreach ($bad_chars as $char)
+ {
+ $this->assertFalse(strpos($name, $char));
+ }
+ }
+
+ public function test_clean_filename_unique()
+ {
+ $filenames = array();
+ for ($tests = 0; $tests < self::TEST_COUNT; $tests++)
+ {
+ $filespec = $this->get_filespec();
+ $filespec->clean_filename('unique', self::PREFIX);
+ $name = $filespec->realname;
+
+ $this->assertEquals(strlen($name), 32 + strlen(self::PREFIX));
+ $this->assertRegExp('#^[A-Za-z0-9]+$#', substr($name, strlen(self::PREFIX)));
+ $this->assertFalse(isset($filenames[$name]));
+ $filenames[$name] = true;
+ }
+ }
+
+ public function get_extension_variables()
+ {
+ return array(
+ array('file.png', 'png'),
+ array('file.phpbb.gif', 'gif'),
+ array('file..', ''),
+ array('.file..jpg.webp', 'webp'),
+ );
+ }
+
+ /**
+ * @dataProvider get_extension_variables
+ */
+ public function test_get_extension($filename, $expected)
+ {
+ $filespec = $this->get_filespec();
+ $this->assertEquals($expected, $filespec->get_extension($filename));
+ }
+
+ public function is_image_variables()
+ {
+ return array(
+ array('gif', 'image/gif', true),
+ array('jpg', 'image/jpg', true),
+ array('png', 'image/png', true),
+ array('tif', 'image/tif', true),
+ array('txt', 'text/plain', false),
+ );
+ }
+
+ /**
+ * @dataProvider is_image_variables
+ */
+ public function test_is_image($filename, $mimetype, $expected)
+ {
+ $filespec = $this->get_filespec(array('tmp_name' => $this->path . $filename, 'type' => $mimetype));
+ $this->assertEquals($expected, $filespec->is_image());
+ }
+
+ public function move_file_variables()
+ {
+ return array(
+ array('gif_copy', 'gif_moved', 'image/gif', 'gif', false, true),
+ array('non_existant', 'still_non_existant', 'text/plain', 'txt', 'GENERAL_UPLOAD_ERROR', false),
+ array('txt_copy', 'txt_as_img', 'image/jpg', 'txt', false, true),
+ array('txt_copy_2', 'txt_moved', 'text/plain', 'txt', false, true),
+ array('jpg_copy', 'jpg_moved', 'image/png', 'jpg', false, true),
+ array('png_copy', 'png_moved', 'image/png', 'jpg', 'IMAGE_FILETYPE_MISMATCH', true),
+ );
+ }
+
+ /**
+ * @dataProvider move_file_variables
+ */
+ public function test_move_file($tmp_name, $realname, $mime_type, $extension, $error, $expected)
+ {
+ // Global $phpbb_root_path and $phpEx are required by phpbb_chmod
+ global $phpbb_root_path, $phpEx;
+ $phpbb_root_path = '';
+ $phpEx = 'php';
+
+ $upload = new phpbb_mock_fileupload();
+ $upload->max_filesize = self::UPLOAD_MAX_FILESIZE;
+
+ $filespec = $this->get_filespec(array(
+ 'tmp_name' => $this->path . 'copies/' . $tmp_name,
+ 'name' => $realname,
+ 'type' => $mime_type,
+ ));
+ $filespec->extension = $extension;
+ $filespec->upload = $upload;
+ $filespec->local = true;
+
+ $this->assertEquals($expected, $filespec->move_file($this->path . 'copies'));
+ $this->assertEquals($filespec->file_moved, file_exists($this->path . 'copies/' . $realname));
+ if ($error)
+ {
+ $this->assertEquals($error, $filespec->error[0]);
+ }
+
+ $phpEx = '';
+ }
+}
diff --git a/tests/upload/fileupload_test.php b/tests/upload/fileupload_test.php
new file mode 100644
index 0000000000..076855ab56
--- /dev/null
+++ b/tests/upload/fileupload_test.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ *
+ * @package testing
+ * @copyright (c) 2012 phpBB Group
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+ *
+ */
+
+require_once __DIR__ . '/../../phpBB/includes/functions.php';
+require_once __DIR__ . '/../../phpBB/includes/utf/utf_tools.php';
+require_once __DIR__ . '/../../phpBB/includes/functions_upload.php';
+
+class phpbb_fileupload_test extends phpbb_test_case
+{
+ private $path;
+
+ protected function setUp()
+ {
+ // Global $config required by unique_id
+ // Global $user required by several functions dealing with translations
+ global $config, $user;
+
+ if (!is_array($config))
+ {
+ $config = array();
+ }
+
+ $config['rand_seed'] = '';
+ $config['rand_seed_last_update'] = time() + 600;
+
+ $user = new phpbb_mock_user();
+ $user->lang = new phpbb_mock_lang();
+ $this->path = __DIR__ . '/fixture/';
+ }
+
+ private function gen_valid_filespec()
+ {
+ $filespec = new phpbb_mock_filespec();
+ $filespec->filesize = 1;
+ $filespec->extension = 'jpg';
+ $filespec->realname = 'valid';
+ $filespec->width = 2;
+ $filespec->height = 2;
+
+ return $filespec;
+ }
+
+ protected function tearDown()
+ {
+ // Clear globals
+ global $config, $user;
+ $config = array();
+ $user = null;
+ }
+
+ public function test_common_checks_invalid_extension()
+ {
+ $upload = new fileupload('', array('png'), 100);
+ $file = $this->gen_valid_filespec();
+ $upload->common_checks($file);
+ $this->assertEquals('DISALLOWED_EXTENSION', $file->error[0]);
+ }
+
+ public function test_common_checks_invalid_filename()
+ {
+ $upload = new fileupload('', array('jpg'), 100);
+ $file = $this->gen_valid_filespec();
+ $file->realname = 'invalid?';
+ $upload->common_checks($file);
+ $this->assertEquals('INVALID_FILENAME', $file->error[0]);
+ }
+
+ public function test_common_checks_too_large()
+ {
+ $upload = new fileupload('', array('jpg'), 100);
+ $file = $this->gen_valid_filespec();
+ $file->filesize = 1000;
+ $upload->common_checks($file);
+ $this->assertEquals('WRONG_FILESIZE', $file->error[0]);
+ }
+
+ public function test_common_checks_valid_file()
+ {
+ $upload = new fileupload('', array('jpg'), 1000);
+ $file = $this->gen_valid_filespec();
+ $upload->common_checks($file);
+ $this->assertEquals(0, sizeof($file->error));
+ }
+
+ public function test_local_upload()
+ {
+ $upload = new fileupload('', array('jpg'), 1000);
+
+ copy($this->path . 'jpg', $this->path . 'jpg.jpg');
+ $file = $upload->local_upload($this->path . 'jpg.jpg');
+ $this->assertEquals(0, sizeof($file->error));
+ unlink($this->path . 'jpg.jpg');
+ }
+
+ public function test_valid_dimensions()
+ {
+ $upload = new fileupload('', false, false, 1, 1, 100, 100);
+
+ $file1 = $this->gen_valid_filespec();
+ $file2 = $this->gen_valid_filespec();
+ $file2->height = 101;
+ $file3 = $this->gen_valid_filespec();
+ $file3->width = 0;
+
+ $this->assertTrue($upload->valid_dimensions($file1));
+ $this->assertFalse($upload->valid_dimensions($file2));
+ $this->assertFalse($upload->valid_dimensions($file3));
+ }
+}
diff --git a/tests/upload/fixture/copies/.gitkeep b/tests/upload/fixture/copies/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/upload/fixture/copies/.gitkeep
diff --git a/tests/upload/fixture/gif b/tests/upload/fixture/gif
new file mode 100644
index 0000000000..b636f4b8df
--- /dev/null
+++ b/tests/upload/fixture/gif
Binary files differ
diff --git a/tests/upload/fixture/jpg b/tests/upload/fixture/jpg
new file mode 100644
index 0000000000..3cd5038e38
--- /dev/null
+++ b/tests/upload/fixture/jpg
Binary files differ
diff --git a/tests/upload/fixture/png b/tests/upload/fixture/png
new file mode 100644
index 0000000000..5514ad40e9
--- /dev/null
+++ b/tests/upload/fixture/png
Binary files differ
diff --git a/tests/upload/fixture/tif b/tests/upload/fixture/tif
new file mode 100644
index 0000000000..248b50f9cb
--- /dev/null
+++ b/tests/upload/fixture/tif
Binary files differ
diff --git a/tests/upload/fixture/txt b/tests/upload/fixture/txt
new file mode 100644
index 0000000000..a78c858f5c
--- /dev/null
+++ b/tests/upload/fixture/txt
@@ -0,0 +1,2 @@
+<HTML>mime trigger</HTML>
+The HTML tags should remain uppercase so that case-insensitivity can be checked.
diff --git a/tests/wrapper/phpbb_php_ini_fake.php b/tests/wrapper/phpbb_php_ini_fake.php
new file mode 100644
index 0000000000..49bc5936e5
--- /dev/null
+++ b/tests/wrapper/phpbb_php_ini_fake.php
@@ -0,0 +1,16 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_php_ini_fake extends phpbb_php_ini
+{
+ function get($varname)
+ {
+ return $varname;
+ }
+}
diff --git a/tests/wrapper/phpbb_php_ini_test.php b/tests/wrapper/phpbb_php_ini_test.php
new file mode 100644
index 0000000000..8e08d5c204
--- /dev/null
+++ b/tests/wrapper/phpbb_php_ini_test.php
@@ -0,0 +1,86 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+require_once dirname(__FILE__) . '/phpbb_php_ini_fake.php';
+require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
+
+class phpbb_wrapper_phpbb_php_ini_test extends phpbb_test_case
+{
+ protected $php_ini;
+
+ public function setUp()
+ {
+ $this->php_ini = new phpbb_php_ini_fake;
+ }
+
+ public function test_get_string()
+ {
+ $this->assertSame(false, $this->php_ini->get_string(false));
+ $this->assertSame('phpbb', $this->php_ini->get_string(' phpbb '));
+ }
+
+ public function test_get_bool()
+ {
+ $this->assertSame(true, $this->php_ini->get_bool('ON'));
+ $this->assertSame(true, $this->php_ini->get_bool('on'));
+ $this->assertSame(true, $this->php_ini->get_bool('1'));
+
+ $this->assertSame(false, $this->php_ini->get_bool('OFF'));
+ $this->assertSame(false, $this->php_ini->get_bool('off'));
+ $this->assertSame(false, $this->php_ini->get_bool('0'));
+ $this->assertSame(false, $this->php_ini->get_bool(''));
+ }
+
+ public function test_get_int()
+ {
+ $this->assertSame(1234, $this->php_ini->get_int('1234'));
+ $this->assertSame(-12345, $this->php_ini->get_int('-12345'));
+ $this->assertSame(false, $this->php_ini->get_int('phpBB'));
+ }
+
+ public function test_get_float()
+ {
+ $this->assertSame(1234.0, $this->php_ini->get_float('1234'));
+ $this->assertSame(-12345.0, $this->php_ini->get_float('-12345'));
+ $this->assertSame(false, $this->php_ini->get_float('phpBB'));
+ }
+
+ public function test_get_bytes_invalid()
+ {
+ $this->assertSame(false, $this->php_ini->get_bytes(false));
+ $this->assertSame(false, $this->php_ini->get_bytes('phpBB'));
+ $this->assertSame(false, $this->php_ini->get_bytes('k'));
+ $this->assertSame(false, $this->php_ini->get_bytes('-k'));
+ $this->assertSame(false, $this->php_ini->get_bytes('M'));
+ $this->assertSame(false, $this->php_ini->get_bytes('-M'));
+ }
+
+ /**
+ * @dataProvider get_bytes_data
+ */
+ public function test_get_bytes($expected, $value)
+ {
+ $actual = $this->php_ini->get_bytes($value);
+
+ $this->assertTrue(is_float($actual) || is_int($actual));
+ $this->assertEquals($expected, $actual);
+ }
+
+ static public function get_bytes_data()
+ {
+ return array(
+ array(32 * pow(2, 20), '32m'),
+ array(- 32 * pow(2, 20), '-32m'),
+ array(8 * pow(2, 30), '8G'),
+ array(- 8 * pow(2, 30), '-8G'),
+ array(1234, '1234'),
+ array(-12345, '-12345'),
+ );
+ }
+}