diff --git a/.gitignore b/.gitignore index 8298f5a894..7d789c59a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,12 @@ *~ -phpunit.xml -phpBB/cache/*.php -phpBB/cache/queue.php.lock -phpBB/config.php -phpBB/files/* -phpBB/images/avatars/gallery/* -phpBB/images/avatars/upload/* -phpBB/store/* -tests/phpbb_unit_tests.sqlite2 -tests/test_config.php -tests/utf/data/*.txt +/phpunit.xml +/phpBB/cache/*.php +/phpBB/cache/queue.php.lock +/phpBB/config.php +/phpBB/files/* +/phpBB/images/avatars/gallery/* +/phpBB/images/avatars/upload/* +/phpBB/store/* +/tests/phpbb_unit_tests.sqlite2 +/tests/test_config.php +/tests/tmp/* diff --git a/build/build.xml b/build/build.xml index 78dbdd379c..3d8d3de640 100644 --- a/build/build.xml +++ b/build/build.xml @@ -2,9 +2,9 @@ - - - + + + @@ -49,19 +49,16 @@ --coverage-clover build/logs/clover.xml --coverage-html build/coverage" passthru="true" /> + - - + + @@ -122,6 +119,29 @@ + + + + + + + + + + + - {L_ADD} + {L_ADD} ({L_MARK_ALL}) diff --git a/phpBB/adm/style/acp_main.html b/phpBB/adm/style/acp_main.html index 7d3b6945ac..c665c9d893 100644 --- a/phpBB/adm/style/acp_main.html +++ b/phpBB/adm/style/acp_main.html @@ -34,6 +34,36 @@ + + +
+

{L_ERROR_MBSTRING_FUNC_OVERLOAD}

+

{L_ERROR_MBSTRING_FUNC_OVERLOAD_EXPLAIN}

+
+ + + +
+

{L_ERROR_MBSTRING_ENCODING_TRANSLATION}

+

{L_ERROR_MBSTRING_ENCODING_TRANSLATION_EXPLAIN}

+
+ + + +
+

{L_ERROR_MBSTRING_HTTP_INPUT}

+

{L_ERROR_MBSTRING_HTTP_INPUT_EXPLAIN}

+
+ + + +
+

{L_ERROR_MBSTRING_HTTP_OUTPUT}

+

{L_ERROR_MBSTRING_HTTP_OUTPUT_EXPLAIN}

+
+ + +

{L_WRITABLE_CONFIG}

diff --git a/phpBB/adm/style/acp_ranks.html b/phpBB/adm/style/acp_ranks.html index 2ad8b3e8aa..1f45109517 100644 --- a/phpBB/adm/style/acp_ranks.html +++ b/phpBB/adm/style/acp_ranks.html @@ -35,7 +35,7 @@
-
+

SQL Quotes:

-

Double quotes where applicable (The variables in these examples are typecasted to integers before) ... examples:

+

Use double quotes where applicable. (The variables in these examples are typecasted to integers beforehand.) Examples:

// These are wrong.

@@ -832,7 +826,7 @@ SELECT FROM phpbb_forums WHERE forum_id <> 1
 
 	

sql_build_query():

-

The $db->sql_build_query() function is responsible for building sql statements for select and select distinct queries if you need to JOIN on more than one table or retrieving data from more than one table while doing a JOIN. This needs to be used to make sure the resulting statement is working on all supported db's. Instead of explaining every possible combination, i will give a short example:

+

The $db->sql_build_query() function is responsible for building sql statements for SELECT and SELECT DISTINCT queries if you need to JOIN on more than one table or retrieve data from more than one table while doing a JOIN. This needs to be used to make sure the resulting statement is working on all supported db's. Instead of explaining every possible combination, I will give a short example:

 $sql_array = array(
@@ -917,7 +911,7 @@ for ($i = 0, $size = sizeof($post_data); $i < $size; $i++)
 	

Use of in_array():

-

Try to avoid using in_array() on huge arrays, and try to not place them into loops if the array to check consist of more than 20 entries. in_array() can be very time consuming and uses a lot of cpu processing time. For little checks it is not noticable, but if checked against a huge array within a loop those checks alone can be a bunch of seconds. If you need this functionality, try using isset() on the arrays keys instead, actually shifting the values into keys and vice versa. A call to isset($array[$var]) is a lot faster than in_array($var, array_keys($array)) for example.

+

Try to avoid using in_array() on huge arrays, and try to not place them into loops if the array to check consist of more than 20 entries. in_array() can be very time consuming and uses a lot of cpu processing time. For little checks it is not noticeable, but if checked against a huge array within a loop those checks alone can take several seconds. If you need this functionality, try using isset() on the arrays keys instead, actually shifting the values into keys and vice versa. A call to isset($array[$var]) is a lot faster than in_array($var, array_keys($array)) for example.

2.v. General Guidelines

@@ -930,7 +924,7 @@ for ($i = 0, $size = sizeof($post_data); $i < $size; $i++)

No attempt should be made to remove any copyright information (either contained within the source or displayed interactively when the source is run/compiled), neither should the copyright information be altered in any way (it may be added to).

Variables:

-

Make use of the request_var() function for anything except for submit or single checking params.

+

Make use of the request_var() function for anything except for submit or single checking params.

The request_var function determines the type to set from the second parameter (which determines the default value too). If you need to get a scalar variable type, you need to tell this the request_var function explicitly. Examples:

// Old method, do not use it

@@ -997,7 +991,7 @@ $user->setup();

The $user->setup() call can be used to pass on additional language definition and a custom style (used in viewforum).

Errors and messages:

-

All messages/errors should be outputed by calling trigger_error() using the appropriate message type and language string. Example:

+

All messages/errors should be outputted by calling trigger_error() using the appropriate message type and language string. Example:

 trigger_error('NO_FORUM');
@@ -1015,7 +1009,7 @@ trigger_error('NO_MODE', E_USER_ERROR);
 
 	

All urls pointing to internal files need to be prepended by the $phpbb_root_path variable. Within the administration control panel all urls pointing to internal files need to be prepended by the $phpbb_admin_path variable. This makes sure the path is always correct and users being able to just rename the admin folder and the acp still working as intended (though some links will fail and the code need to be slightly adjusted).

-

The append_sid() function from 2.0.x is available too, though does not handle url alterations automatically. Please have a look at the code documentation if you want to get more details on how to use append_sid(). A sample call to append_sid() can look like this:

+

The append_sid() function from 2.0.x is available too, though it does not handle url alterations automatically. Please have a look at the code documentation if you want to get more details on how to use append_sid(). A sample call to append_sid() can look like this:

 append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $row['group_id'])
@@ -1023,7 +1017,7 @@ append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;
 
 	

General function usage:

-

Some of these functions are only chosen over others because of personal preference and having no other benefit than to be consistent over the code.

+

Some of these functions are only chosen over others because of personal preference and have no benefit other than maintaining consistency throughout the code.

  • @@ -1073,7 +1067,7 @@ append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp; required_imageset = prosilver

3.2. General Styling Rules

-

Templates should be produced in a consistent manner. Where appropriate they should be based off an existing copy, e.g. index, viewforum or viewtopic (the combination of which implement a range of conditional and variable forms). Please also note that the intendation and coding guidelines also apply to templates where possible.

+

Templates should be produced in a consistent manner. Where appropriate they should be based off an existing copy, e.g. index, viewforum or viewtopic (the combination of which implement a range of conditional and variable forms). Please also note that the indentation and coding guidelines also apply to templates where possible.

The outer table class forumline has gone and is replaced with tablebg.

When writing <table> the order <table class="" cellspacing="" cellpadding="" border="" align=""> creates consistency and allows everyone to easily see which table produces which "look". The same applies to most other tags for which additional parameters can be set, consistency is the major aim here.

@@ -1093,7 +1087,7 @@ append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;

Row colours/classes are now defined by the template, use an IF S_ROW_COUNT switch, see viewtopic or viewforum for an example.

-

Remember block level ordering is important ... while not all pages validate as XHTML 1.0 Strict compliant it is something we're trying to work too.

+

Remember block level ordering is important ... while not all pages validate as XHTML 1.0 Strict compliant it is something we're trying to work on.

Use a standard cellpadding of 2 and cellspacing of 0 on outer tables. Inner tables can vary from 0 to 3 or even 4 depending on the need.

@@ -1142,12 +1136,12 @@ append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;

4.i. General Templating

File naming

-

Firstly templates now take the suffix ".html" rather than ".tpl". This was done simply to make the lifes of some people easier wrt syntax highlighting, etc.

+

Firstly templates now take the suffix ".html" rather than ".tpl". This was done simply to make the lives of some people easier wrt syntax highlighting, etc.

Variables

All template variables should be named appropriately (using underscores for spaces), language entries should be prefixed with L_, system data with S_, urls with U_, javascript urls with UA_, language to be put in javascript statements with LA_, all other variables should be presented 'as is'.

-

L_* template variables are automatically tried to be mapped to the corresponding language entry if the code does not set (and therefore overwrite) this variable specifically. For example {L_USERNAME} maps to $user->lang['USERNAME']. The LA_* template variables are handled within the same way, but properly escaped to be put in javascript code. This should reduce the need to assign loads of new lang vars in Modifications. +

L_* template variables are automatically mapped to the corresponding language entry if the code does not set (and therefore overwrite) this variable specifically and if the language entry exists. For example {L_USERNAME} maps to $user->lang['USERNAME']. The LA_* template variables are handled within the same way, but properly escaped so they can be put in javascript code. This should reduce the need to assign loads of new language variables in MODifications.

Blocks/Loops

@@ -1430,9 +1424,9 @@ div <!-- END l_block1 -->
-

Here we open the loop l_block1 and doing some things if the value S_SELECTED within the current loop iteration is true, else we write the blocks link and title. Here, you see {l_block1.L_TITLE} referenced - you remember that L_* variables get automatically assigned the corresponding language entry? This is true, but not within loops. The L_TITLE variable within the loop l_block1 is assigned within the code itself.

+

Here we open the loop l_block1 and do some things if the value S_SELECTED within the current loop iteration is true, else we write the blocks link and title. Here, you see {l_block1.L_TITLE} referenced - you remember that L_* variables get automatically assigned the corresponding language entry? This is true, but not within loops. The L_TITLE variable within the loop l_block1 is assigned within the code itself.

-

Let's have a closer look to the markup:

+

Let's have a closer look at the markup:

 <!-- BEGIN l_block1 -->
@@ -1526,7 +1520,7 @@ div
 </ul> <!-- written on third iteration -->
 
-

Just always remember that processing is taking place from up to down.

+

Just always remember that processing is taking place from top to bottom.

Forms

If a form is used for a non-trivial operation (i.e. more than a jumpbox), then it should include the {S_FORM_TOKEN} template variable.

@@ -1542,11 +1536,9 @@ div

4.ii. Template Inheritance

-

When basing a new template on an existing one, it is not necessary to provide all template files. By declaring the template to be "inheriting" in the template configuration file.

+

When basing a new style on an existing one, it is not necessary to provide all the template files. By declaring the base style name in the inherit_from field in the template configuration file, the style can be set to inherit template files from the base style. The limitation on this is that the base style has to be installed and complete, meaning that it is not itself inheriting.

-

The limitation on this is that the base style has to be installed and complete, meaning that it is not itself inheriting.

- -

The effect of doing so is that the template engine will use the files in the new template where they exist, but fall back to files in the base template otherwise. Declaring a style to be inheriting also causes it to use some of the configuration settings of the base style, notably database storage.

+

The effect of doing so is that the template engine will use the template files in the new style where they exist, but fall back to files in the base style otherwise. Declaring a style to inherit from another also causes it to use some of the configuration settings of the base style, notably database storage.

We strongly encourage the use of inheritance for styles based on the bundled styles, as it will ease the update procedure.

@@ -1673,7 +1665,7 @@ if (utf8_case_fold_nfc($string1) == utf8_case_fold_nfc($string2))

Encoding:

-

With phpBB3, the output encoding for the forum in now UTF-8, a Universal Character Encoding by the Unicode Consortium that is by design a superset to US-ASCII and ISO-8859-1. By using one character set which simultaenously supports all scripts which previously would have required different encodings (eg: ISO-8859-1 to ISO-8859-15 (Latin, Greek, Cyrillic, Thai, Hebrew, Arabic); GB2312 (Simplified Chinese); Big5 (Traditional Chinese), EUC-JP (Japanese), EUC-KR (Korean), VISCII (Vietnamese); et cetera), this removes the need to convert between encodings and improves the accessibility of multilingual forums.

+

With phpBB3, the output encoding for the forum in now UTF-8, a Universal Character Encoding by the Unicode Consortium that is by design a superset to US-ASCII and ISO-8859-1. By using one character set which simultaenously supports all scripts which previously would have required different encodings (eg: ISO-8859-1 to ISO-8859-15 (Latin, Greek, Cyrillic, Thai, Hebrew, Arabic); GB2312 (Simplified Chinese); Big5 (Traditional Chinese), EUC-JP (Japanese), EUC-KR (Korean), VISCII (Vietnamese); et cetera), we remove the need to convert between encodings and improves the accessibility of multilingual forums.

The impact is that the language files for phpBB must now also be encoded as UTF-8, with a caveat that the files must not contain a BOM for compatibility reasons with non-Unicode aware versions of PHP. For those with forums using the Latin character set (ie: most European languages), this change is transparent since UTF-8 is superset to US-ASCII and ISO-8859-1.

@@ -2323,51 +2315,7 @@ if (utf8_case_fold_nfc($string1) == utf8_case_fold_nfc($string2))
-

7. VCS Guidelines

- -
-
- -
- -

The version control system for phpBB3 is git. The repository is available at http://github.com/phpbb/phpbb3.

- -

7.i. Repository Structure

- -
    -
  • develop
    The latest unstable development version with new features etc.
  • -
  • develop-*
    Development branches of stable phpBB releases. Branched off of develop at the time of feature freeze. -
      -
    • phpBB3.0develop-olympus
      Development branch of the stable 3.0 line. Bug fixes are applied here.
    • -
    • phpBB3.1develop-ascraeus
      Development branch of the stable 3.1 line. Bug fixes are applied here.
    • -
    -
  • -
  • master
    A branch containing all stable phpBB3 release points
  • -
  • tags
    Released versions. Stable ones get merged into the master branch. -
      -
    • release-3.Y-BX
      Beta release X of the 3.Y line.
    • -
    • release-3.Y-RCX
      Release candidate X of the 3.Y line.
    • -
    • release-3.Y.Z-RCX
      Release candidate X of the stable 3.Y.Z release.
    • -
    • release-3.0.X
      Stable 3.0.X release.
    • -
    • release-2.0.X
      Old stable 2.0.X release.
    • -
    -
  • -
- -

7.ii. Commit Messages and Repository Rules

- -

Information on repository rules, such as commit messages can be found at http://wiki.phpbb.com/display/DEV/Git.

- -
- - - -
-
- -
- -

9. Copyright and disclaimer

+

8. Copyright and disclaimer

diff --git a/phpBB/feed.php b/phpBB/feed.php index d737b8e10c..9816f0f303 100644 --- a/phpBB/feed.php +++ b/phpBB/feed.php @@ -173,6 +173,12 @@ if (defined('DEBUG_EXTRA') && request_var('explain', 0) && $auth->acl_get('a_')) header("Content-Type: application/atom+xml; charset=UTF-8"); header("Last-Modified: " . gmdate('D, d M Y H:i:s', $feed_updated_time) . ' GMT'); +if (!empty($user->data['is_bot'])) +{ + // Let reverse proxies know we detected a bot. + header('X-PHPBB-IS-BOT: yes'); +} + echo '' . "\n"; echo '' . "\n"; echo '' . "\n\n"; @@ -604,30 +610,9 @@ class phpbb_feed_base function get_passworded_forums() { - global $db, $user; + global $user; - // Exclude passworded forums - $sql = 'SELECT f.forum_id, fa.user_id - FROM ' . FORUMS_TABLE . ' f - LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa - ON (fa.forum_id = f.forum_id - AND fa.session_id = '" . $db->sql_escape($user->session_id) . "') - WHERE f.forum_password <> ''"; - $result = $db->sql_query($sql); - - $forum_ids = array(); - while ($row = $db->sql_fetchrow($result)) - { - $forum_id = (int) $row['forum_id']; - - if ($row['user_id'] != $user->data['user_id']) - { - $forum_ids[$forum_id] = $forum_id; - } - } - $db->sql_freeresult($result); - - return $forum_ids; + return $user->get_passworded_forums(); } function get_item() diff --git a/phpBB/includes/acp/acp_ban.php b/phpBB/includes/acp/acp_ban.php index a7ea57b753..f8af1b86e1 100644 --- a/phpBB/includes/acp/acp_ban.php +++ b/phpBB/includes/acp/acp_ban.php @@ -175,12 +175,21 @@ class acp_ban } $result = $db->sql_query($sql); - $banned_options = ''; + $banned_options = $excluded_options = array(); $ban_length = $ban_reasons = $ban_give_reasons = array(); while ($row = $db->sql_fetchrow($result)) { - $banned_options .= '' . $row[$field] . ''; + $option = ''; + + if ($row['ban_exclude']) + { + $excluded_options[] = $option; + } + else + { + $banned_options[] = $option; + } $time_length = ($row['ban_end']) ? ($row['ban_end'] - $row['ban_start']) / 60 : 0; @@ -241,11 +250,26 @@ class acp_ban } } + $options = ''; + if ($excluded_options) + { + $options .= ''; + $options .= implode('', $excluded_options); + $options .= ''; + } + + if ($banned_options) + { + $options .= ''; + $options .= implode('', $banned_options); + $options .= ''; + } + $template->assign_vars(array( 'S_BAN_END_OPTIONS' => $ban_end_options, - 'S_BANNED_OPTIONS' => ($banned_options) ? true : false, - 'BANNED_OPTIONS' => $banned_options) - ); + 'S_BANNED_OPTIONS' => ($banned_options || $excluded_options) ? true : false, + 'BANNED_OPTIONS' => $options, + )); } } diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index d8ab42ed2d..3ed5f40368 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -188,7 +188,7 @@ class acp_board 'hot_threshold' => array('lang' => 'HOT_THRESHOLD', 'validate' => 'int:0', 'type' => 'text:3:4', 'explain' => true), 'max_poll_options' => array('lang' => 'MAX_POLL_OPTIONS', 'validate' => 'int:2:127', 'type' => 'text:4:4', 'explain' => false), 'max_post_chars' => array('lang' => 'CHAR_LIMIT', 'validate' => 'int:0', 'type' => 'text:4:6', 'explain' => true), - 'min_post_chars' => array('lang' => 'MIN_CHAR_LIMIT', 'validate' => 'int:0', 'type' => 'text:4:6', 'explain' => true), + 'min_post_chars' => array('lang' => 'MIN_CHAR_LIMIT', 'validate' => 'int:1', 'type' => 'text:4:6', 'explain' => true), 'max_post_smilies' => array('lang' => 'SMILIES_LIMIT', 'validate' => 'int:0', 'type' => 'text:4:4', 'explain' => true), 'max_post_urls' => array('lang' => 'MAX_POST_URLS', 'validate' => 'int:0', 'type' => 'text:5:4', 'explain' => true), 'max_post_font_size' => array('lang' => 'MAX_POST_FONT_SIZE', 'validate' => 'int:0', 'type' => 'text:5:4', 'explain' => true, 'append' => ' %'), diff --git a/phpBB/includes/acp/acp_database.php b/phpBB/includes/acp/acp_database.php index 193dd001c0..62bcd43a47 100644 --- a/phpBB/includes/acp/acp_database.php +++ b/phpBB/includes/acp/acp_database.php @@ -221,6 +221,7 @@ class acp_database case 'submit': $delete = request_var('delete', ''); $file = request_var('file', ''); + $download = request_var('download', ''); if (!preg_match('#^backup_\d{10,}_[a-z\d]{16}\.(sql(?:\.(?:gz|bz2))?)$#', $file, $matches)) { @@ -247,10 +248,8 @@ class acp_database confirm_box(false, $user->lang['DELETE_SELECTED_BACKUP'], build_hidden_fields(array('delete' => $delete, 'file' => $file))); } } - else + else if ($download || confirm_box(true)) { - $download = request_var('download', ''); - if ($download) { $name = $matches[0]; @@ -411,6 +410,10 @@ class acp_database trigger_error($user->lang['RESTORE_SUCCESS'] . adm_back_link($this->u_action)); break; } + else if (!$download) + { + confirm_box(false, $user->lang['RESTORE_SELECTED_BACKUP'], build_hidden_fields(array('file' => $file))); + } default: $methods = array('sql'); diff --git a/phpBB/includes/acp/acp_email.php b/phpBB/includes/acp/acp_email.php index 133fe47e09..df0d44c0c5 100644 --- a/phpBB/includes/acp/acp_email.php +++ b/phpBB/includes/acp/acp_email.php @@ -136,8 +136,9 @@ class acp_email $i = $j = 0; - // Send with BCC, no more than 50 recipients for one mail (to not exceed the limit) - $max_chunk_size = 50; + // Send with BCC + // Maximum number of bcc recipients + $max_chunk_size = (int) $config['email_max_chunk_size']; $email_list = array(); $old_lang = $row['user_lang']; $old_notify_type = $row['user_notify_type']; @@ -194,10 +195,7 @@ class acp_email $messenger->template('admin_send_email', $used_lang); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->subject(htmlspecialchars_decode($subject)); $messenger->set_mail_priority($priority); diff --git a/phpBB/includes/acp/acp_forums.php b/phpBB/includes/acp/acp_forums.php index 4d9b9f01e0..50e12a0f15 100644 --- a/phpBB/includes/acp/acp_forums.php +++ b/phpBB/includes/acp/acp_forums.php @@ -212,15 +212,11 @@ class acp_forums $message = ($action == 'add') ? $user->lang['FORUM_CREATED'] : $user->lang['FORUM_UPDATED']; - // Redirect to permissions - if ($auth->acl_get('a_fauth') && !$copied_permissions) - { - $message .= '

' . sprintf($user->lang['REDIRECT_ACL'], '', ''); - } - // redirect directly to permission settings screen if authed if ($action == 'add' && !$copied_permissions && $auth->acl_get('a_fauth')) { + $message .= '

' . sprintf($user->lang['REDIRECT_ACL'], '', ''); + meta_refresh(4, append_sid("{$phpbb_admin_path}index.$phpEx", 'i=permissions' . $acl_url)); } @@ -875,7 +871,7 @@ class acp_forums $errors = array(); - if (!$forum_data['forum_name']) + if ($forum_data['forum_name'] == '') { $errors[] = $user->lang['FORUM_NAME_EMPTY']; } diff --git a/phpBB/includes/acp/acp_inactive.php b/phpBB/includes/acp/acp_inactive.php index 1a12c4967c..f3f332d707 100644 --- a/phpBB/includes/acp/acp_inactive.php +++ b/phpBB/includes/acp/acp_inactive.php @@ -118,10 +118,7 @@ class acp_inactive $messenger->to($row['user_email'], $row['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'USERNAME' => htmlspecialchars_decode($row['username'])) @@ -209,10 +206,7 @@ class acp_inactive $messenger->to($row['user_email'], $row['username']); $messenger->im($row['user_jabber'], $row['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'USERNAME' => htmlspecialchars_decode($row['username']), @@ -301,7 +295,7 @@ class acp_inactive 'PAGINATION' => generate_pagination($this->u_action . "&$u_sort_param&users_per_page=$per_page", $inactive_count, $per_page, $start, true), 'USERS_PER_PAGE' => $per_page, - 'U_ACTION' => $this->u_action . '&start=' . $start, + 'U_ACTION' => $this->u_action . "&$u_sort_param&users_per_page=$per_page&start=$start", )); $this->tpl_name = 'acp_inactive'; diff --git a/phpBB/includes/acp/acp_language.php b/phpBB/includes/acp/acp_language.php index 598b390302..d560cdd0c5 100644 --- a/phpBB/includes/acp/acp_language.php +++ b/phpBB/includes/acp/acp_language.php @@ -919,6 +919,9 @@ class acp_language $default_lang_id = (int) $db->sql_fetchfield('lang_id'); $db->sql_freeresult($result); + // We want to notify the admin that custom profile fields need to be updated for the new language. + $notify_cpf_update = false; + // From the mysql documentation: // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14. // Due to this we stay on the safe side if we do the insertion "the manual way" @@ -932,6 +935,7 @@ class acp_language { $row['lang_id'] = $lang_id; $db->sql_query('INSERT INTO ' . PROFILE_LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $row)); + $notify_cpf_update = true; } $db->sql_freeresult($result); @@ -944,12 +948,15 @@ class acp_language { $row['lang_id'] = $lang_id; $db->sql_query('INSERT INTO ' . PROFILE_FIELDS_LANG_TABLE . ' ' . $db->sql_build_array('INSERT', $row)); + $notify_cpf_update = true; } $db->sql_freeresult($result); add_log('admin', 'LOG_LANGUAGE_PACK_INSTALLED', $lang_pack['name']); - trigger_error(sprintf($user->lang['LANGUAGE_PACK_INSTALLED'], $lang_pack['name']) . adm_back_link($this->u_action)); + $message = sprintf($user->lang['LANGUAGE_PACK_INSTALLED'], $lang_pack['name']); + $message .= ($notify_cpf_update) ? '

' . $user->lang['LANGUAGE_PACK_CPF_UPDATE'] : ''; + trigger_error($message . adm_back_link($this->u_action)); break; diff --git a/phpBB/includes/acp/acp_logs.php b/phpBB/includes/acp/acp_logs.php index 0f4f78fcdd..2fc86e325f 100644 --- a/phpBB/includes/acp/acp_logs.php +++ b/phpBB/includes/acp/acp_logs.php @@ -127,12 +127,12 @@ class acp_logs // Grab log data $log_data = array(); $log_count = 0; - view_log($mode, $log_data, $log_count, $config['topics_per_page'], $start, $forum_id, 0, 0, $sql_where, $sql_sort, $keywords); + $start = view_log($mode, $log_data, $log_count, $config['topics_per_page'], $start, $forum_id, 0, 0, $sql_where, $sql_sort, $keywords); $template->assign_vars(array( 'L_TITLE' => $l_title, 'L_EXPLAIN' => $l_title_explain, - 'U_ACTION' => $this->u_action, + 'U_ACTION' => $this->u_action . "&$u_sort_param$keywords_param&start=$start", 'S_ON_PAGE' => on_page($log_count, $config['topics_per_page'], $start), 'PAGINATION' => generate_pagination($this->u_action . "&$u_sort_param$keywords_param", $log_count, $config['topics_per_page'], $start, true), diff --git a/phpBB/includes/acp/acp_main.php b/phpBB/includes/acp/acp_main.php index 60cebe3c08..c8df21f5a9 100644 --- a/phpBB/includes/acp/acp_main.php +++ b/phpBB/includes/acp/acp_main.php @@ -415,11 +415,8 @@ class acp_main { $latest_version_info = explode("\n", $latest_version_info); - $latest_version = str_replace('rc', 'RC', strtolower(trim($latest_version_info[0]))); - $current_version = str_replace('rc', 'RC', strtolower($config['version'])); - $template->assign_vars(array( - 'S_VERSION_UP_TO_DATE' => version_compare($current_version, $latest_version, '<') ? false : true, + 'S_VERSION_UP_TO_DATE' => phpbb_version_compare(trim($latest_version_info[0]), $config['version'], '<='), )); } @@ -521,7 +518,7 @@ class acp_main 'U_ADMIN_LOG' => append_sid("{$phpbb_admin_path}index.$phpEx", 'i=logs&mode=admin'), 'U_INACTIVE_USERS' => append_sid("{$phpbb_admin_path}index.$phpEx", 'i=inactive&mode=list'), 'U_VERSIONCHECK' => append_sid("{$phpbb_admin_path}index.$phpEx", 'i=update&mode=version_check'), - 'U_VERSIONCHECK_FORCE' => append_sid("{$phpbb_admin_path}index.$phpEx", 'i=1&versioncheck_force=1'), + 'U_VERSIONCHECK_FORCE' => append_sid("{$phpbb_admin_path}index.$phpEx", 'versioncheck_force=1'), 'S_ACTION_OPTIONS' => ($auth->acl_get('a_board')) ? true : false, 'S_FOUNDER' => ($user->data['user_type'] == USER_FOUNDER) ? true : false, @@ -603,6 +600,17 @@ class acp_main $template->assign_var('S_WRITABLE_CONFIG', (bool) (@fileperms($phpbb_root_path . 'config.' . $phpEx) & 0x0002)); } + if (extension_loaded('mbstring')) + { + $template->assign_vars(array( + 'S_MBSTRING_LOADED' => true, + 'S_MBSTRING_FUNC_OVERLOAD_FAIL' => (intval(@ini_get('mbstring.func_overload')) & (MB_OVERLOAD_MAIL | MB_OVERLOAD_STRING)), + 'S_MBSTRING_ENCODING_TRANSLATION_FAIL' => (@ini_get('mbstring.encoding_translation') != 0), + 'S_MBSTRING_HTTP_INPUT_FAIL' => (@ini_get('mbstring.http_input') != 'pass'), + 'S_MBSTRING_HTTP_OUTPUT_FAIL' => (@ini_get('mbstring.http_output') != 'pass'), + )); + } + // Fill dbms version if not yet filled if (empty($config['dbms_version'])) { diff --git a/phpBB/includes/acp/acp_php_info.php b/phpBB/includes/acp/acp_php_info.php index 0499095004..7dd345971a 100644 --- a/phpBB/includes/acp/acp_php_info.php +++ b/phpBB/includes/acp/acp_php_info.php @@ -67,6 +67,9 @@ class acp_php_info $output = preg_replace('#', ''), array('class="row1"', 'class="row2"', '', '', ''), $output); + // Fix invalid anchor names (eg "module_Zend Optimizer") + $output = preg_replace_callback('##', array($this, 'remove_spaces'), $output); + if (empty($output)) { trigger_error('NO_PHPINFO_AVAILABLE', E_USER_WARNING); @@ -79,6 +82,11 @@ class acp_php_info $template->assign_var('PHPINFO', $output); } + + function remove_spaces($matches) + { + return ''; + } } ?> \ No newline at end of file diff --git a/phpBB/includes/acp/acp_ranks.php b/phpBB/includes/acp/acp_ranks.php index fcfef2a61e..dfd7511427 100644 --- a/phpBB/includes/acp/acp_ranks.php +++ b/phpBB/includes/acp/acp_ranks.php @@ -199,7 +199,7 @@ class acp_ranks 'RANK_TITLE' => (isset($ranks['rank_title'])) ? $ranks['rank_title'] : '', 'S_FILENAME_LIST' => $filename_list, 'RANK_IMAGE' => ($edit_img) ? $phpbb_root_path . $config['ranks_path'] . '/' . $edit_img : $phpbb_admin_path . 'images/spacer.gif', - 'S_SPECIAL_RANK' => (!isset($ranks['rank_special']) || $ranks['rank_special']) ? true : false, + 'S_SPECIAL_RANK' => (isset($ranks['rank_special']) && $ranks['rank_special']) ? true : false, 'MIN_POSTS' => (isset($ranks['rank_min']) && !$ranks['rank_special']) ? $ranks['rank_min'] : 0) ); diff --git a/phpBB/includes/acp/acp_styles.php b/phpBB/includes/acp/acp_styles.php index 3bc8c86500..5300265686 100644 --- a/phpBB/includes/acp/acp_styles.php +++ b/phpBB/includes/acp/acp_styles.php @@ -1634,6 +1634,13 @@ parse_css_file = {PARSE_CSS_FILE} trigger_error($user->lang['NO_' . $l_prefix] . adm_back_link($this->u_action), E_USER_WARNING); } + $s_only_component = $this->display_component_options($mode, $style_row[$mode . '_id'], $style_row); + + if ($s_only_component) + { + trigger_error($user->lang['ONLY_' . $l_prefix] . adm_back_link($this->u_action), E_USER_WARNING); + } + if ($update) { if ($mode == 'style') @@ -1678,8 +1685,6 @@ parse_css_file = {PARSE_CSS_FILE} trigger_error($user->lang[$message] . adm_back_link($this->u_action)); } - $this->display_component_options($mode, $style_row[$mode . '_id'], $style_row); - $this->page_title = 'DELETE_' . $l_prefix; $template->assign_vars(array( @@ -1765,11 +1770,14 @@ parse_css_file = {PARSE_CSS_FILE} /** * Display the options which can be used to replace a style/template/theme/imageset + * + * @return boolean Returns true if the component is the only component and can not be deleted. */ function display_component_options($component, $component_id, $style_row = false, $style_id = false) { global $db, $template, $user; + $is_only_component = true; $component_in_use = array(); if ($component != 'style') { @@ -1801,6 +1809,9 @@ parse_css_file = {PARSE_CSS_FILE} $s_options = ''; if (($component != 'style') && empty($component_in_use)) { + // If it is not in use, there must be another component + $is_only_component = false; + $sql = "SELECT {$component}_id, {$component}_name FROM $sql_from WHERE {$component}_id = {$component_id}"; @@ -1824,6 +1835,7 @@ parse_css_file = {PARSE_CSS_FILE} { if ($row[$component . '_id'] != $component_id) { + $is_only_component = false; $s_options .= ''; } else if ($component != 'style') @@ -1851,6 +1863,8 @@ parse_css_file = {PARSE_CSS_FILE} } } } + + return $is_only_component; } /** diff --git a/phpBB/includes/acp/acp_update.php b/phpBB/includes/acp/acp_update.php index b0ce8f1084..7e3d1a1024 100644 --- a/phpBB/includes/acp/acp_update.php +++ b/phpBB/includes/acp/acp_update.php @@ -37,7 +37,7 @@ class acp_update $errstr = ''; $errno = 0; - $info = obtain_latest_version_info(request_var('versioncheck_force', false), true); + $info = obtain_latest_version_info(request_var('versioncheck_force', false)); if ($info === false) { @@ -69,12 +69,9 @@ class acp_update $current_version = (!empty($version_update_from)) ? $version_update_from : $config['version']; - $up_to_date_automatic = (version_compare(str_replace('rc', 'RC', strtolower($current_version)), str_replace('rc', 'RC', strtolower($latest_version)), '<')) ? false : true; - $up_to_date = (version_compare(str_replace('rc', 'RC', strtolower($config['version'])), str_replace('rc', 'RC', strtolower($latest_version)), '<')) ? false : true; - $template->assign_vars(array( - 'S_UP_TO_DATE' => $up_to_date, - 'S_UP_TO_DATE_AUTO' => $up_to_date_automatic, + 'S_UP_TO_DATE' => phpbb_version_compare($latest_version, $config['version'], '<='), + 'S_UP_TO_DATE_AUTO' => phpbb_version_compare($latest_version, $current_version, '<='), 'S_VERSION_CHECK' => true, 'U_ACTION' => $this->u_action, 'U_VERSIONCHECK_FORCE' => append_sid($this->u_action . '&versioncheck_force=1'), diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php index 6be0760be0..4f58434a43 100644 --- a/phpBB/includes/acp/acp_users.php +++ b/phpBB/includes/acp/acp_users.php @@ -348,10 +348,7 @@ class acp_users $messenger->to($user_row['user_email'], $user_row['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'WELCOME_MSG' => htmlspecialchars_decode(sprintf($user->lang['WELCOME_SUBJECT'], $config['sitename'])), @@ -406,10 +403,7 @@ class acp_users $messenger->to($user_row['user_email'], $user_row['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'USERNAME' => htmlspecialchars_decode($user_row['username'])) @@ -818,7 +812,7 @@ class acp_users // Which updates do we need to do? $update_username = ($user_row['username'] != $data['username']) ? $data['username'] : false; - $update_password = ($data['new_password'] && !phpbb_check_hash($user_row['user_password'], $data['new_password'])) ? true : false; + $update_password = ($data['new_password'] && !phpbb_check_hash($data['new_password'], $user_row['user_password'])) ? true : false; $update_email = ($data['email'] != $user_row['user_email']) ? $data['email'] : false; if (!sizeof($error)) @@ -1124,7 +1118,7 @@ class acp_users // Grab log data $log_data = array(); $log_count = 0; - view_log('user', $log_data, $log_count, $config['topics_per_page'], $start, 0, 0, $user_id, $sql_where, $sql_sort); + $start = view_log('user', $log_data, $log_count, $config['topics_per_page'], $start, 0, 0, $user_id, $sql_where, $sql_sort); $template->assign_vars(array( 'S_FEEDBACK' => true, diff --git a/phpBB/includes/auth.php b/phpBB/includes/auth.php index 5564de2943..0585921426 100644 --- a/phpBB/includes/auth.php +++ b/phpBB/includes/auth.php @@ -349,6 +349,14 @@ class auth /** * Get permission listing based on user_id/options/forum_ids + * + * Be careful when using this function with permissions a_, m_, u_ and f_ ! + * It may not work correctly. When a user group grants an a_* permission, + * e.g. a_foo, but the user's a_foo permission is set to "Never", then + * the user does not in fact have the a_ permission. + * But the user will still be listed as having the a_ permission. + * + * For more information see: http://tracker.phpbb.com/browse/PHPBB3-10252 */ function acl_get_list($user_id = false, $opts = false, $forum_id = false) { diff --git a/phpBB/includes/auth/auth_db.php b/phpBB/includes/auth/auth_db.php index 6ca69d9174..c20196d019 100644 --- a/phpBB/includes/auth/auth_db.php +++ b/phpBB/includes/auth/auth_db.php @@ -107,6 +107,15 @@ function login_db($username, $password, $ip = '', $browser = '', $forwarded_for if (!$row) { + if ($config['ip_login_limit_max'] && $attempts >= $config['ip_login_limit_max']) + { + return array( + 'status' => LOGIN_ERROR_ATTEMPTS, + 'error_msg' => 'LOGIN_ERROR_ATTEMPTS', + 'user_row' => array('user_id' => ANONYMOUS), + ); + } + return array( 'status' => LOGIN_ERROR_USERNAME, 'error_msg' => 'LOGIN_ERROR_USERNAME', @@ -264,4 +273,4 @@ function login_db($username, $password, $ip = '', $browser = '', $forwarded_for ); } -?> \ No newline at end of file +?> diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index d77bb3c4a7..9356e3e9b4 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -584,6 +584,13 @@ class bbcode $code = str_replace("\t", '   ', $code); $code = str_replace(' ', '  ', $code); $code = str_replace(' ', '  ', $code); + $code = str_replace("\n ", "\n ", $code); + + // keep space at the beginning + if (!empty($code) && $code[0] == ' ') + { + $code = ' ' . substr($code, 1); + } // remove newline at the beginning if (!empty($code) && $code[0] == "\n") diff --git a/phpBB/includes/constants.php b/phpBB/includes/constants.php index 3940888216..b70cf5bc59 100644 --- a/phpBB/includes/constants.php +++ b/phpBB/includes/constants.php @@ -25,7 +25,7 @@ if (!defined('IN_PHPBB')) */ // phpBB Version -define('PHPBB_VERSION', '3.0.9'); +define('PHPBB_VERSION', '3.0.10'); // QA-related // define('PHPBB_QA', 1); diff --git a/phpBB/includes/db/db_tools.php b/phpBB/includes/db/db_tools.php index 50e308dea2..2cba11133a 100644 --- a/phpBB/includes/db/db_tools.php +++ b/phpBB/includes/db/db_tools.php @@ -347,6 +347,66 @@ class phpbb_db_tools } } + /** + * Gets a list of tables in the database. + * + * @return array Array of table names (all lower case) + */ + function sql_list_tables() + { + switch ($this->db->sql_layer) + { + case 'mysql': + case 'mysql4': + case 'mysqli': + $sql = 'SHOW TABLES'; + break; + + case 'sqlite': + $sql = 'SELECT name + FROM sqlite_master + WHERE type = "table"'; + break; + + case 'mssql': + case 'mssql_odbc': + case 'mssqlnative': + $sql = "SELECT name + FROM sysobjects + WHERE type='U'"; + break; + + case 'postgres': + $sql = 'SELECT relname + FROM pg_stat_user_tables'; + break; + + case 'firebird': + $sql = 'SELECT rdb$relation_name + FROM rdb$relations + WHERE rdb$view_source is null + AND rdb$system_flag = 0'; + break; + + case 'oracle': + $sql = 'SELECT table_name + FROM USER_TABLES'; + break; + } + + $result = $this->db->sql_query($sql); + + $tables = array(); + while ($row = $this->db->sql_fetchrow($result)) + { + $name = current($row); + $tables[$name] = $name; + } + $this->db->sql_freeresult($result); + + return $tables; + } + /** * Check if table exists * @@ -649,6 +709,23 @@ class phpbb_db_tools $sqlite = true; } + // Drop tables? + if (!empty($schema_changes['drop_tables'])) + { + foreach ($schema_changes['drop_tables'] as $table) + { + // only drop table if it exists + if ($this->sql_table_exists($table)) + { + $result = $this->sql_table_drop($table); + if ($this->return_statements) + { + $statements = array_merge($statements, $result); + } + } + } + } + // Add tables? if (!empty($schema_changes['add_tables'])) { @@ -994,34 +1071,21 @@ class phpbb_db_tools } /** - * Check if a specified column exist + * Gets a list of columns of a table. * - * @param string $table Table to check the column at - * @param string $column_name The column to check + * @param string $table Table name * - * @return bool True if column exists, else false + * @return array Array of column names (all lower case) */ - function sql_column_exists($table, $column_name) + function sql_list_columns($table) { + $columns = array(); + switch ($this->sql_layer) { case 'mysql_40': case 'mysql_41': - $sql = "SHOW COLUMNS FROM $table"; - $result = $this->db->sql_query($sql); - - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['Field']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; break; // PostgreSQL has a way of doing this in a much simpler way but would @@ -1032,19 +1096,6 @@ class phpbb_db_tools WHERE c.relname = '{$table}' AND a.attnum > 0 AND a.attrelid = c.oid"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['attname']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - - return false; break; // same deal with PostgreSQL, we must perform more complex operations than @@ -1055,62 +1106,26 @@ class phpbb_db_tools FROM syscolumns c LEFT JOIN sysobjects o ON c.id = o.id WHERE o.name = '{$table}'"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['name']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; break; case 'oracle': $sql = "SELECT column_name FROM user_tab_columns WHERE LOWER(table_name) = '" . strtolower($table) . "'"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['column_name']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; break; case 'firebird': $sql = "SELECT RDB\$FIELD_NAME as FNAME FROM RDB\$RELATION_FIELDS WHERE RDB\$RELATION_NAME = '" . strtoupper($table) . "'"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['fname']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; break; - // ugh, SQLite case 'sqlite': $sql = "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = '{$table}'"; + $result = $this->db->sql_query($sql); if (!$result) @@ -1134,14 +1149,39 @@ class phpbb_db_tools continue; } - if (strtolower($entities[0]) == $column_name) - { - return true; - } + $column = strtolower($entities[0]); + $columns[$column] = $column; } - return false; + + return $columns; break; } + + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + $column = strtolower(current($row)); + $columns[$column] = $column; + } + $this->db->sql_freeresult($result); + + return $columns; + } + + /** + * Check whether a specified column exist in a table + * + * @param string $table Table to check + * @param string $column_name Column to check + * + * @return bool True if column exists, false otherwise + */ + function sql_column_exists($table, $column_name) + { + $columns = $this->sql_list_columns($table); + + return isset($columns[$column_name]); } /** @@ -1788,7 +1828,7 @@ class phpbb_db_tools break; case 'oracle': - $statements[] = 'ALTER TABLE ' . $table_name . ' DROP ' . $column_name; + $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN ' . $column_name; break; case 'postgres': @@ -1939,6 +1979,7 @@ class phpbb_db_tools $statements[] = "DROP SEQUENCE {$row['referenced_name']}"; } $this->db->sql_freeresult($result); + break; case 'postgres': // PGSQL does not "tightly" bind sequences and tables, we must guess... @@ -2059,7 +2100,7 @@ class phpbb_db_tools $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) { - $max_length = $table_prefix + 24; + $max_length = strlen($table_prefix) + 24; trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); } @@ -2074,7 +2115,7 @@ class phpbb_db_tools case 'mysql_40': case 'mysql_41': - $statements[] = 'CREATE UNIQUE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; + $statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX (' . implode(', ', $column) . ')'; break; case 'mssql': @@ -2096,7 +2137,7 @@ class phpbb_db_tools $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) { - $max_length = $table_prefix + 24; + $max_length = strlen($table_prefix) + 24; trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); } @@ -2127,7 +2168,7 @@ class phpbb_db_tools } // no break case 'mysql_41': - $statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; + $statements[] = 'ALTER TABLE ' . $table_name . ' ADD INDEX ' . $index_name . '(' . implode(', ', $column) . ')'; break; case 'mssql': diff --git a/phpBB/includes/db/dbal.php b/phpBB/includes/db/dbal.php index 9b45c085a2..b4c1a72e1c 100644 --- a/phpBB/includes/db/dbal.php +++ b/phpBB/includes/db/dbal.php @@ -609,7 +609,7 @@ class dbal } } - $sql .= $this->_sql_custom_build('FROM', implode(', ', $table_array)); + $sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array)); if (!empty($array['LEFT_JOIN'])) { diff --git a/phpBB/includes/db/mssqlnative.php b/phpBB/includes/db/mssqlnative.php index 6810562d17..7fbc374e77 100644 --- a/phpBB/includes/db/mssqlnative.php +++ b/phpBB/includes/db/mssqlnative.php @@ -396,7 +396,7 @@ class dbal_mssqlnative extends dbal */ function sql_affectedrows() { - return ($this->db_connect_id) ? @sqlsrv_rows_affected($this->db_connect_id) : false; + return (!empty($this->query_result)) ? @sqlsrv_rows_affected($this->query_result) : false; } /** diff --git a/phpBB/includes/db/mysqli.php b/phpBB/includes/db/mysqli.php index 862d62f4ba..456ce906d0 100644 --- a/phpBB/includes/db/mysqli.php +++ b/phpBB/includes/db/mysqli.php @@ -33,14 +33,33 @@ class dbal_mysqli extends dbal */ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false , $new_link = false) { - $this->persistency = $persistency; + // Mysqli extension supports persistent connection since PHP 5.3.0 + $this->persistency = (version_compare(PHP_VERSION, '5.3.0', '>=')) ? $persistency : false; $this->user = $sqluser; - $this->server = $sqlserver; + + // If persistent connection, set dbhost to localhost when empty and prepend it with 'p:' prefix + $this->server = ($this->persistency) ? 'p:' . (($sqlserver) ? $sqlserver : 'localhost') : $sqlserver; + $this->dbname = $database; $port = (!$port) ? NULL : $port; - // Persistant connections not supported by the mysqli extension? - $this->db_connect_id = @mysqli_connect($this->server, $this->user, $sqlpassword, $this->dbname, $port); + // If port is set and it is not numeric, most likely mysqli socket is set. + // Try to map it to the $socket parameter. + $socket = NULL; + if ($port) + { + if (is_numeric($port)) + { + $port = (int) $port; + } + else + { + $socket = $port; + $port = NULL; + } + } + + $this->db_connect_id = @mysqli_connect($this->server, $this->user, $sqlpassword, $this->dbname, $port, $socket); if ($this->db_connect_id && $this->dbname != '') { @@ -230,7 +249,13 @@ class dbal_mysqli extends dbal return $cache->sql_fetchrow($query_id); } - return ($query_id !== false) ? @mysqli_fetch_assoc($query_id) : false; + if ($query_id !== false) + { + $result = @mysqli_fetch_assoc($query_id); + return $result !== null ? $result : false; + } + + return false; } /** diff --git a/phpBB/includes/error_collector.php b/phpBB/includes/error_collector.php index 55834f354c..3c0a89a1f3 100644 --- a/phpBB/includes/error_collector.php +++ b/phpBB/includes/error_collector.php @@ -49,13 +49,15 @@ class phpbb_error_collector { $text .= "
\n"; } + list($errno, $msg_text, $errfile, $errline) = $error; - $text .= "Errno $errno: $msg_text"; - if (defined('DEBUG_EXTRA') || defined('IN_INSTALL')) - { - $text .= " at $errfile line $errline"; - } + + // Prevent leakage of local path to phpBB install + $errfile = phpbb_filter_root_path($errfile); + + $text .= "Errno $errno: $msg_text at $errfile line $errline"; } + return $text; } } diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index b1c1c14d0c..01b3ca92a9 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -265,6 +265,27 @@ function phpbb_mt_rand($min, $max) return ($min > $max) ? mt_rand($max, $min) : mt_rand($min, $max); } +/** +* Wrapper for getdate() which returns the equivalent array for UTC timestamps. +* +* @param int $time Unix timestamp (optional) +* +* @return array Returns an associative array of information related to the timestamp. +* See http://www.php.net/manual/en/function.getdate.php +*/ +function phpbb_gmgetdate($time = false) +{ + if ($time === false) + { + $time = time(); + } + + // getdate() interprets timestamps in local time. + // What follows uses the fact that getdate() and + // date('Z') balance each other out. + return getdate($time - date('Z')); +} + /** * Return formatted string for filesizes * @@ -598,6 +619,34 @@ function phpbb_email_hash($email) return sprintf('%u', crc32(strtolower($email))) . strlen($email); } +/** +* Wrapper for version_compare() that allows using uppercase A and B +* for alpha and beta releases. +* +* See http://www.php.net/manual/en/function.version-compare.php +* +* @param string $version1 First version number +* @param string $version2 Second version number +* @param string $operator Comparison operator (optional) +* +* @return mixed Boolean (true, false) if comparison operator is specified. +* Integer (-1, 0, 1) otherwise. +*/ +function phpbb_version_compare($version1, $version2, $operator = null) +{ + $version1 = strtolower($version1); + $version2 = strtolower($version2); + + if (is_null($operator)) + { + return version_compare($version1, $version2); + } + else + { + return version_compare($version1, $version2, $operator); + } +} + /** * Global function for chmodding directories and files for internal use * @@ -1831,7 +1880,7 @@ function get_unread_topics($user_id = false, $sql_extra = '', $sql_sort = '', $s */ function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false) { - global $db, $tracking_topics, $user, $config; + global $db, $tracking_topics, $user, $config, $auth; // Determine the users last forum mark time if not given. if ($mark_time_forum === false) @@ -1854,6 +1903,10 @@ function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_ti } } + // Handle update of unapproved topics info. + // Only update for moderators having m_approve permission for the forum. + $sql_update_unapproved = ($auth->acl_get('m_approve', $forum_id)) ? '': 'AND t.topic_approved = 1'; + // Check the forum for any left unread topics. // If there are none, we mark the forum as read. if ($config['load_db_lastread'] && $user->data['is_registered']) @@ -1869,7 +1922,8 @@ function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_ti LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ') WHERE t.forum_id = ' . $forum_id . ' AND t.topic_last_post_time > ' . $mark_time_forum . ' - AND t.topic_moved_id = 0 + AND t.topic_moved_id = 0 ' . + $sql_update_unapproved . ' AND (tt.topic_id IS NULL OR tt.mark_time < t.topic_last_post_time) GROUP BY t.forum_id'; $result = $db->sql_query_limit($sql, 1); @@ -1889,11 +1943,12 @@ function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_ti } else { - $sql = 'SELECT topic_id - FROM ' . TOPICS_TABLE . ' - WHERE forum_id = ' . $forum_id . ' - AND topic_last_post_time > ' . $mark_time_forum . ' - AND topic_moved_id = 0'; + $sql = 'SELECT t.topic_id + FROM ' . TOPICS_TABLE . ' t + WHERE t.forum_id = ' . $forum_id . ' + AND t.topic_last_post_time > ' . $mark_time_forum . ' + AND t.topic_moved_id = 0 ' . + $sql_update_unapproved; $result = $db->sql_query($sql); $check_forum = $tracking_topics['tf'][$forum_id]; @@ -2078,7 +2133,7 @@ function generate_pagination($base_url, $num_items, $per_page, $start_item, $add $start_cnt = min(max(1, $on_page - 4), $total_pages - 5); $end_cnt = max(min($total_pages, $on_page + 4), 6); - $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator; + $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator; for ($i = $start_cnt + 1; $i < $end_cnt; $i++) { @@ -2089,7 +2144,7 @@ function generate_pagination($base_url, $num_items, $per_page, $start_item, $add } } - $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator; + $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator; } else { @@ -2176,6 +2231,12 @@ function append_sid($url, $params = false, $is_amp = true, $session_id = false) { global $_SID, $_EXTRA_URL, $phpbb_hook; + if ($params === '' || (is_array($params) && empty($params))) + { + // Do not append the ? if the param-list is empty anyway. + $params = false; + } + // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropriately. // They could mimic most of what is within this function if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id)) @@ -2659,12 +2720,6 @@ function send_status_line($code, $message) { $version = $_SERVER['SERVER_PROTOCOL']; } - else if (!empty($_SERVER['HTTP_VERSION'])) - { - // I cannot remember where I got this from. - // This code path may never be reachable in reality. - $version = $_SERVER['HTTP_VERSION']; - } else { $version = 'HTTP/1.0'; @@ -3338,61 +3393,44 @@ function add_log() } /** -* Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com) +* Return a nicely formatted backtrace. +* +* Turns the array returned by debug_backtrace() into HTML markup. +* Also filters out absolute paths to phpBB root. +* +* @return string HTML markup */ function get_backtrace() { - global $phpbb_root_path; - $output = '
'; $backtrace = debug_backtrace(); - $path = phpbb_realpath($phpbb_root_path); - foreach ($backtrace as $number => $trace) + // We skip the first one, because it only shows this file/function + unset($backtrace[0]); + + foreach ($backtrace as $trace) { - // We skip the first one, because it only shows this file/function - if ($number == 0) - { - continue; - } - // Strip the current directory from path - if (empty($trace['file'])) - { - $trace['file'] = ''; - } - else - { - $trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']); - $trace['file'] = substr($trace['file'], 1); - } - $args = array(); + $trace['file'] = (empty($trace['file'])) ? '(not given by php)' : htmlspecialchars(phpbb_filter_root_path($trace['file'])); + $trace['line'] = (empty($trace['line'])) ? '(not given by php)' : $trace['line']; - // If include/require/include_once is not called, do not show arguments - they may contain sensible information - if (!in_array($trace['function'], array('include', 'require', 'include_once'))) + // Only show function arguments for include etc. + // Other parameters may contain sensible information + $argument = ''; + if (!empty($trace['args'][0]) && in_array($trace['function'], array('include', 'require', 'include_once', 'require_once'))) { - unset($trace['args']); - } - else - { - // Path... - if (!empty($trace['args'][0])) - { - $argument = htmlspecialchars($trace['args'][0]); - $argument = str_replace(array($path, '\\'), array('', '/'), $argument); - $argument = substr($argument, 1); - $args[] = "'{$argument}'"; - } + $argument = htmlspecialchars(phpbb_filter_root_path($trace['args'][0])); } $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class']; $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type']; $output .= '
'; - $output .= 'FILE: ' . htmlspecialchars($trace['file']) . '
'; + $output .= 'FILE: ' . $trace['file'] . '
'; $output .= 'LINE: ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '
'; - $output .= 'CALL: ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')
'; + $output .= 'CALL: ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']); + $output .= '(' . (($argument !== '') ? "'$argument'" : '') . ')
'; } $output .= '
'; return $output; @@ -3454,6 +3492,10 @@ function get_preg_expression($mode) $inline = ($mode == 'relative_url') ? ')' : ''; return "(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?"; break; + + case 'table_prefix': + return '#^[a-zA-Z][a-zA-Z0-9_]*$#'; + break; } return ''; @@ -3686,10 +3728,19 @@ function phpbb_checkdnsrr($host, $type = 'MX') { return true; } + break; default: - case 'A': case 'AAAA': + // AAAA records returned by nslookup on Windows XP/2003 have this format. + // Later Windows versions use the A record format below for AAAA records. + if (stripos($line, "$host AAAA IPv6 address") === 0) + { + return true; + } + // No break + + case 'A': if (!empty($host_matches)) { // Second line @@ -3758,9 +3809,8 @@ function msg_handler($errno, $msg_text, $errfile, $errline) if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false) { - // remove complete path to installation, with the risk of changing backslashes meant to be there - $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile); - $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text); + $errfile = phpbb_filter_root_path($errfile); + $msg_text = phpbb_filter_root_path($msg_text); $error_name = ($errno === E_WARNING) ? 'PHP Warning' : 'PHP Notice'; echo '[phpBB Debug] ' . $error_name . ': in file ' . $errfile . ' on line ' . $errline . ': ' . $msg_text . '
' . "\n"; @@ -3938,6 +3988,29 @@ function msg_handler($errno, $msg_text, $errfile, $errline) return false; } +/** +* Removes absolute path to phpBB root directory from error messages +* and converts backslashes to forward slashes. +* +* @param string $errfile Absolute file path +* (e.g. /var/www/phpbb3/phpBB/includes/functions.php) +* Please note that if $errfile is outside of the phpBB root, +* the root path will not be found and can not be filtered. +* @return string Relative file path +* (e.g. /includes/functions.php) +*/ +function phpbb_filter_root_path($errfile) +{ + static $root_path; + + if (empty($root_path)) + { + $root_path = phpbb_realpath(dirname(__FILE__) . '/../'); + } + + return str_replace(array($root_path, '\\'), array('[ROOT]', '/'), $errfile); +} + /** * Queries the session table to get information about online guests * @param int $item_id Limits the search to the item with this id @@ -4305,7 +4378,7 @@ function phpbb_http_login($param) */ function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum') { - global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path; + global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path; if (defined('HEADER_INC')) { @@ -4458,6 +4531,15 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0 $s_search_hidden_fields['sid'] = $_SID; } + if (!empty($_EXTRA_URL)) + { + foreach ($_EXTRA_URL as $url_param) + { + $url_param = explode('=', $url_param, 2); + $s_hidden_fields[$url_param[0]] = $url_param[1]; + } + } + // The following assigns all _common_ variables that may be used at any point in a template. $template->assign_vars(array( 'SITENAME' => $config['sitename'], @@ -4589,6 +4671,12 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0 header('Expires: 0'); header('Pragma: no-cache'); + if (!empty($user->data['is_bot'])) + { + // Let reverse proxies know we detected a bot. + header('X-PHPBB-IS-BOT: yes'); + } + return; } diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index cb0cf34e69..526bc16ff0 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -2595,6 +2595,35 @@ function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id $sql_keywords .= 'LOWER(l.log_data) ' . implode(' OR LOWER(l.log_data) ', $keywords) . ')'; } + if ($log_count !== false) + { + $sql = 'SELECT COUNT(l.log_id) AS total_entries + FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . " u + WHERE l.log_type = $log_type + AND l.user_id = u.user_id + AND l.log_time >= $limit_days + $sql_keywords + $sql_forum"; + $result = $db->sql_query($sql); + $log_count = (int) $db->sql_fetchfield('total_entries'); + $db->sql_freeresult($result); + } + + // $log_count may be false here if false was passed in for it, + // because in this case we did not run the COUNT() query above. + // If we ran the COUNT() query and it returned zero rows, return; + // otherwise query for logs below. + if ($log_count === 0) + { + // Save the queries, because there are no logs to display + return 0; + } + + if ($offset >= $log_count) + { + $offset = ($offset - $limit < 0) ? 0 : $offset - $limit; + } + $sql = "SELECT l.*, u.username, u.username_clean, u.user_colour FROM " . LOG_TABLE . " l, " . USERS_TABLE . " u WHERE l.log_type = $log_type @@ -2762,21 +2791,7 @@ function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id } } - if ($log_count !== false) - { - $sql = 'SELECT COUNT(l.log_id) AS total_entries - FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . " u - WHERE l.log_type = $log_type - AND l.user_id = u.user_id - AND l.log_time >= $limit_days - $sql_keywords - $sql_forum"; - $result = $db->sql_query($sql); - $log_count = (int) $db->sql_fetchfield('total_entries'); - $db->sql_freeresult($result); - } - - return; + return $offset; } /** @@ -2908,6 +2923,12 @@ function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $li $user_count = (int) $db->sql_fetchfield('user_count'); $db->sql_freeresult($result); + if ($user_count == 0) + { + // Save the queries, because there are no users to display + return 0; + } + if ($offset >= $user_count) { $offset = ($offset - $limit < 0) ? 0 : $offset - $limit; @@ -3113,7 +3134,7 @@ function get_database_size() /** * Retrieve contents from remotely stored file */ -function get_remote_file($host, $directory, $filename, &$errstr, &$errno, $port = 80, $timeout = 10) +function get_remote_file($host, $directory, $filename, &$errstr, &$errno, $port = 80, $timeout = 6) { global $user; @@ -3123,6 +3144,9 @@ function get_remote_file($host, $directory, $filename, &$errstr, &$errno, $port @fputs($fsock, "HOST: $host\r\n"); @fputs($fsock, "Connection: close\r\n\r\n"); + $timer_stop = time() + $timeout; + stream_set_timeout($fsock, $timeout); + $file_info = ''; $get_info = false; @@ -3145,6 +3169,14 @@ function get_remote_file($host, $directory, $filename, &$errstr, &$errno, $port return false; } } + + $stream_meta_data = stream_get_meta_data($fsock); + + if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop) + { + $errstr = $user->lang['FSOCK_TIMEOUT']; + return false; + } } @fclose($fsock); } diff --git a/phpBB/includes/functions_content.php b/phpBB/includes/functions_content.php index faff9dd0de..b7650ecd6a 100644 --- a/phpBB/includes/functions_content.php +++ b/phpBB/includes/functions_content.php @@ -1107,7 +1107,7 @@ function extension_allowed($forum_id, $extension, &$extensions) * @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char) * @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars). * @param bool $allow_reply Allow Re: in front of string -* NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_legnth) and is deprecated. +* NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated. * @param string $append String to be appended */ function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '') diff --git a/phpBB/includes/functions_display.php b/phpBB/includes/functions_display.php index d7422aa2c9..ee7048638d 100644 --- a/phpBB/includes/functions_display.php +++ b/phpBB/includes/functions_display.php @@ -51,6 +51,27 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod $sql_where = 'left_id > ' . $root_data['left_id'] . ' AND left_id < ' . $root_data['right_id']; } + // Handle marking everything read + if ($mark_read == 'all') + { + $redirect = build_url(array('mark', 'hash')); + meta_refresh(3, $redirect); + + if (check_link_hash(request_var('hash', ''), 'global')) + { + markread('all'); + + trigger_error( + $user->lang['FORUMS_MARKED'] . '

' . + sprintf($user->lang['RETURN_INDEX'], '
', '') + ); + } + else + { + trigger_error(sprintf($user->lang['RETURN_PAGE'], '', '')); + } + } + // Display list of active topics for this category? $show_active = (isset($root_data['forum_flags']) && ($root_data['forum_flags'] & FORUM_FLAG_ACTIVE_TOPICS)) ? true : false; @@ -120,13 +141,14 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod $forum_id = $row['forum_id']; // Mark forums read? - if ($mark_read == 'forums' || $mark_read == 'all') + if ($mark_read == 'forums') { if ($auth->acl_get('f_list', $forum_id)) { $forum_ids[] = $forum_id; - continue; } + + continue; } // Category with no members @@ -152,8 +174,6 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod continue; } - $forum_ids[] = $forum_id; - if ($config['load_db_lastread'] && $user->data['is_registered']) { $forum_tracking_info[$forum_id] = (!empty($row['mark_time'])) ? $row['mark_time'] : $user->data['user_lastmark']; @@ -255,24 +275,16 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod $db->sql_freeresult($result); // Handle marking posts - if ($mark_read == 'forums' || $mark_read == 'all') + if ($mark_read == 'forums') { $redirect = build_url(array('mark', 'hash')); $token = request_var('hash', ''); if (check_link_hash($token, 'global')) { - if ($mark_read == 'all') - { - markread('all'); - $message = sprintf($user->lang['RETURN_INDEX'], '', ''); - } - else - { - // Add 0 to forums array to mark global announcements correctly - $forum_ids[] = 0; - markread('topics', $forum_ids); - $message = sprintf($user->lang['RETURN_FORUM'], '', ''); - } + // Add 0 to forums array to mark global announcements correctly + $forum_ids[] = 0; + markread('topics', $forum_ids); + $message = sprintf($user->lang['RETURN_FORUM'], '', ''); meta_refresh(3, $redirect); trigger_error($user->lang['FORUMS_MARKED'] . '

' . $message); } @@ -453,6 +465,7 @@ function display_forums($root_data = '', $display_moderators = true, $return_mod 'S_NO_CAT' => $catless && !$last_catless, 'S_IS_LINK' => ($row['forum_type'] == FORUM_LINK) ? true : false, 'S_UNREAD_FORUM' => $forum_unread, + 'S_AUTH_READ' => $auth->acl_get('f_read', $row['forum_id']), 'S_LOCKED_FORUM' => ($row['forum_status'] == ITEM_LOCKED) ? true : false, 'S_LIST_SUBFORUMS' => ($row['display_subforum_list']) ? true : false, 'S_SUBFORUMS' => (sizeof($subforums_list)) ? true : false, @@ -662,7 +675,7 @@ function topic_generate_pagination($replies, $url) $pagination .= '' . $times . ''; if ($times == 1 && $total_pages > 5) { - $pagination .= ' ... '; + $pagination .= ' ... '; // Display the last three pages $times = $total_pages - 3; @@ -996,13 +1009,17 @@ function display_user_activity(&$userdata) } // Obtain active topic + // We need to exclude passworded forums here so we do not leak the topic title + $forum_ary_topic = array_unique(array_merge($forum_ary, $user->get_passworded_forums())); + $forum_sql_topic = (!empty($forum_ary_topic)) ? 'AND ' . $db->sql_in_set('forum_id', $forum_ary_topic, true) : ''; + $sql = 'SELECT topic_id, COUNT(post_id) AS num_posts FROM ' . POSTS_TABLE . ' WHERE poster_id = ' . $userdata['user_id'] . " AND post_postcount = 1 AND (post_approved = 1 $sql_m_approve) - $forum_sql + $forum_sql_topic GROUP BY topic_id ORDER BY num_posts DESC"; $result = $db->sql_query_limit($sql, 1); @@ -1058,7 +1075,7 @@ function display_user_activity(&$userdata) /** * Topic and forum watching common code */ -function watch_topic_forum($mode, &$s_watching, $user_id, $forum_id, $topic_id, $notify_status = 'unset', $start = 0) +function watch_topic_forum($mode, &$s_watching, $user_id, $forum_id, $topic_id, $notify_status = 'unset', $start = 0, $item_title = '') { global $template, $db, $user, $phpEx, $start, $phpbb_root_path; @@ -1067,6 +1084,7 @@ function watch_topic_forum($mode, &$s_watching, $user_id, $forum_id, $topic_id, $match_id = ($mode == 'forum') ? $forum_id : $topic_id; $u_url = "uid={$user->data['user_id']}"; $u_url .= ($mode == 'forum') ? '&f' : '&f=' . $forum_id . '&t'; + $is_watching = 0; // Is user watching this thread? if ($user_id != ANONYMOUS) @@ -1091,28 +1109,51 @@ function watch_topic_forum($mode, &$s_watching, $user_id, $forum_id, $topic_id, if (isset($_GET['unwatch'])) { $uid = request_var('uid', 0); - if ($uid != $user_id) + $token = request_var('hash', ''); + + if ($token && check_link_hash($token, "{$mode}_$match_id") || confirm_box(true)) { - $redirect_url = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&start=$start"); - $message = $user->lang['ERR_UNWATCHING'] . '

' . sprintf($user->lang['RETURN_' . strtoupper($mode)], '', ''); - trigger_error($message); - } - if ($_GET['unwatch'] == $mode) - { - $is_watching = 0; + if ($uid != $user_id || $_GET['unwatch'] != $mode) + { + $redirect_url = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&start=$start"); + $message = $user->lang['ERR_UNWATCHING'] . '

' . sprintf($user->lang['RETURN_' . strtoupper($mode)], '', ''); + trigger_error($message); + } $sql = 'DELETE FROM ' . $table_sql . " WHERE $where_sql = $match_id AND user_id = $user_id"; $db->sql_query($sql); + + $redirect_url = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&start=$start"); + $message = $user->lang['NOT_WATCHING_' . strtoupper($mode)] . '

'; + $message .= sprintf($user->lang['RETURN_' . strtoupper($mode)], '', ''); + meta_refresh(3, $redirect_url); + trigger_error($message); } + else + { + $s_hidden_fields = array( + 'uid' => $user->data['user_id'], + 'unwatch' => $mode, + 'start' => $start, + 'f' => $forum_id, + ); + if ($mode != 'forum') + { + $s_hidden_fields['t'] = $topic_id; + } - $redirect_url = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&start=$start"); - - meta_refresh(3, $redirect_url); - - $message = $user->lang['NOT_WATCHING_' . strtoupper($mode)] . '

' . sprintf($user->lang['RETURN_' . strtoupper($mode)], '', ''); - trigger_error($message); + if ($item_title == '') + { + $confirm_box_message = 'UNWATCH_' . strtoupper($mode); + } + else + { + $confirm_box_message = $user->lang('UNWATCH_' . strtoupper($mode) . '_DETAILED', $item_title); + } + confirm_box(false, $confirm_box_message, build_hidden_fields($s_hidden_fields)); + } } else { @@ -1132,26 +1173,45 @@ function watch_topic_forum($mode, &$s_watching, $user_id, $forum_id, $topic_id, { if (isset($_GET['watch'])) { + $uid = request_var('uid', 0); $token = request_var('hash', ''); - $redirect_url = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&start=$start"); - if ($_GET['watch'] == $mode && check_link_hash($token, "{$mode}_$match_id")) + if ($token && check_link_hash($token, "{$mode}_$match_id") || confirm_box(true)) { + if ($uid != $user_id || $_GET['watch'] != $mode) + { + $redirect_url = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&start=$start"); + $message = $user->lang['ERR_WATCHING'] . '

' . sprintf($user->lang['RETURN_' . strtoupper($mode)], '', ''); + trigger_error($message); + } + $is_watching = true; $sql = 'INSERT INTO ' . $table_sql . " (user_id, $where_sql, notify_status) VALUES ($user_id, $match_id, " . NOTIFY_YES . ')'; $db->sql_query($sql); + + $redirect_url = append_sid("{$phpbb_root_path}view$mode.$phpEx", "$u_url=$match_id&start=$start"); $message = $user->lang['ARE_WATCHING_' . strtoupper($mode)] . '

' . sprintf($user->lang['RETURN_' . strtoupper($mode)], '', ''); + meta_refresh(3, $redirect_url); + trigger_error($message); } else { - $message = $user->lang['ERR_WATCHING'] . '

' . sprintf($user->lang['RETURN_' . strtoupper($mode)], '', ''); + $s_hidden_fields = array( + 'uid' => $user->data['user_id'], + 'watch' => $mode, + 'start' => $start, + 'f' => $forum_id, + ); + if ($mode != 'forum') + { + $s_hidden_fields['t'] = $topic_id; + } + + $confirm_box_message = (($item_title == '') ? 'WATCH_' . strtoupper($mode) : $user->lang('WATCH_' . strtoupper($mode) . '_DETAILED', $item_title)); + confirm_box(false, $confirm_box_message, build_hidden_fields($s_hidden_fields)); } - - meta_refresh(3, $redirect_url); - - trigger_error($message); } else { @@ -1161,7 +1221,7 @@ function watch_topic_forum($mode, &$s_watching, $user_id, $forum_id, $topic_id, } else { - if (isset($_GET['unwatch']) && $_GET['unwatch'] == $mode) + if ((isset($_GET['unwatch']) && $_GET['unwatch'] == $mode) || (isset($_GET['watch']) && $_GET['watch'] == $mode)) { login_box(); } diff --git a/phpBB/includes/functions_install.php b/phpBB/includes/functions_install.php index 992e8d6bb0..2c640e0999 100644 --- a/phpBB/includes/functions_install.php +++ b/phpBB/includes/functions_install.php @@ -211,61 +211,20 @@ function dbms_select($default = '', $only_20x_options = false) /** * Get tables of a database +* +* @deprecated */ -function get_tables($db) +function get_tables(&$db) { - switch ($db->sql_layer) + if (!class_exists('phpbb_db_tools')) { - case 'mysql': - case 'mysql4': - case 'mysqli': - $sql = 'SHOW TABLES'; - break; - - case 'sqlite': - $sql = 'SELECT name - FROM sqlite_master - WHERE type = "table"'; - break; - - case 'mssql': - case 'mssql_odbc': - case 'mssqlnative': - $sql = "SELECT name - FROM sysobjects - WHERE type='U'"; - break; - - case 'postgres': - $sql = 'SELECT relname - FROM pg_stat_user_tables'; - break; - - case 'firebird': - $sql = 'SELECT rdb$relation_name - FROM rdb$relations - WHERE rdb$view_source is null - AND rdb$system_flag = 0'; - break; - - case 'oracle': - $sql = 'SELECT table_name - FROM USER_TABLES'; - break; + global $phpbb_root_path, $phpEx; + require($phpbb_root_path . 'includes/db/db_tools.' . $phpEx); } - $result = $db->sql_query($sql); + $db_tools = new phpbb_db_tools($db); - $tables = array(); - - while ($row = $db->sql_fetchrow($result)) - { - $tables[] = current($row); - } - - $db->sql_freeresult($result); - - return $tables; + return $db_tools->sql_list_tables(); } /** diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index b5c87094c0..91b361183c 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -162,6 +162,22 @@ class messenger $this->extra_headers[] = trim($headers); } + /** + * Adds X-AntiAbuse headers + * + * @param array $config Configuration array + * @param user $user A user object + * + * @return null + */ + function anti_abuse_headers($config, $user) + { + $this->headers('X-AntiAbuse: Board servername - ' . mail_encode($config['server_name'])); + $this->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); + $this->headers('X-AntiAbuse: Username - ' . mail_encode($user->data['username'])); + $this->headers('X-AntiAbuse: User IP - ' . $user->ip); + } + /** * Set the email priority */ @@ -975,9 +991,16 @@ function smtpmail($addresses, $subject, $message, &$err_msg, $headers = false) $smtp->add_backtrace('Connecting to ' . $config['smtp_host'] . ':' . $config['smtp_port']); // Ok we have error checked as much as we can to this point let's get on it already. - ob_start(); + if (!class_exists('phpbb_error_collector')) + { + global $phpbb_root_path, $phpEx; + include($phpbb_root_path . 'includes/error_collector.' . $phpEx); + } + $collector = new phpbb_error_collector; + $collector->install(); $smtp->socket = fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 20); - $error_contents = ob_get_clean(); + $collector->uninstall(); + $error_contents = $collector->format_errors(); if (!$smtp->socket) { @@ -1608,18 +1631,27 @@ function mail_encode($str, $eol = "\r\n") */ function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg) { - global $config; + global $config, $phpbb_root_path, $phpEx; // We use the EOL character for the OS here because the PHP mail function does not correctly transform line endings. On Windows SMTP is used (SMTP is \r\n), on UNIX a command is used... // Reference: http://bugs.php.net/bug.php?id=15841 $headers = implode($eol, $headers); - ob_start(); + if (!class_exists('phpbb_error_collector')) + { + include($phpbb_root_path . 'includes/error_collector.' . $phpEx); + } + + $collector = new phpbb_error_collector; + $collector->install(); + // On some PHP Versions mail() *may* fail if there are newlines within the subject. // Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8. // Because PHP can't decide what is wanted we revert back to the non-RFC-compliant way of separating by one space (Use '' as parameter to mail_encode() results in SPACE used) $result = $config['email_function_name']($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers); - $err_msg = ob_get_clean(); + + $collector->uninstall(); + $err_msg = $collector->format_errors(); return $result; } diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php index a641afbaed..77d92e26e2 100644 --- a/phpBB/includes/functions_posting.php +++ b/phpBB/includes/functions_posting.php @@ -1870,9 +1870,9 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u case 'edit_topic': case 'edit_first_post': - if (isset($poll['poll_options']) && !empty($poll['poll_options'])) + if (isset($poll['poll_options'])) { - $poll_start = ($poll['poll_start']) ? $poll['poll_start'] : $current_time; + $poll_start = ($poll['poll_start'] || empty($poll['poll_options'])) ? $poll['poll_start'] : $current_time; $poll_length = $poll['poll_length'] * 86400; if ($poll_length < 0) { @@ -2075,11 +2075,11 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u } // Update Poll Tables - if (isset($poll['poll_options']) && !empty($poll['poll_options'])) + if (isset($poll['poll_options'])) { $cur_poll_options = array(); - if ($poll['poll_start'] && $mode == 'edit') + if ($mode == 'edit') { $sql = 'SELECT * FROM ' . POLL_OPTIONS_TABLE . ' diff --git a/phpBB/includes/functions_privmsgs.php b/phpBB/includes/functions_privmsgs.php index 4c34bc92ca..c40ceb088f 100644 --- a/phpBB/includes/functions_privmsgs.php +++ b/phpBB/includes/functions_privmsgs.php @@ -1607,7 +1607,7 @@ function submit_pm($mode, $subject, &$data, $put_in_outbox = true) // Send Notifications if ($mode != 'edit') { - pm_notification($mode, $data['from_username'], $recipients, $subject, $data['message']); + pm_notification($mode, $data['from_username'], $recipients, $subject, $data['message'], $data['msg_id']); } return $data['msg_id']; @@ -1616,7 +1616,7 @@ function submit_pm($mode, $subject, &$data, $put_in_outbox = true) /** * PM Notification */ -function pm_notification($mode, $author, $recipients, $subject, $message) +function pm_notification($mode, $author, $recipients, $subject, $message, $msg_id) { global $db, $user, $config, $phpbb_root_path, $phpEx, $auth; @@ -1688,8 +1688,9 @@ function pm_notification($mode, $author, $recipients, $subject, $message) 'AUTHOR_NAME' => htmlspecialchars_decode($author), 'USERNAME' => htmlspecialchars_decode($addr['name']), - 'U_INBOX' => generate_board_url() . "/ucp.$phpEx?i=pm&folder=inbox") - ); + 'U_INBOX' => generate_board_url() . "/ucp.$phpEx?i=pm&folder=inbox", + 'U_VIEW_MESSAGE' => generate_board_url() . "/ucp.$phpEx?i=pm&mode=view&p=$msg_id", + )); $messenger->send($addr['method']); } diff --git a/phpBB/includes/functions_transfer.php b/phpBB/includes/functions_transfer.php index 046abede8e..5ab7a87efd 100644 --- a/phpBB/includes/functions_transfer.php +++ b/phpBB/includes/functions_transfer.php @@ -808,23 +808,56 @@ class ftp_fsock extends transfer */ function _open_data_connection() { - $this->_send_command('PASV', '', false); - - if (!$ip_port = $this->_check_command(true)) + // Try to find out whether we have a IPv4 or IPv6 (control) connection + if (function_exists('stream_socket_get_name')) { - return false; + $socket_name = stream_socket_get_name($this->connection, true); + $server_ip = substr($socket_name, 0, strrpos($socket_name, ':')); } - // open the connection to start sending the file - if (!preg_match('#[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+#', $ip_port, $temp)) + if (!isset($server_ip) || preg_match(get_preg_expression('ipv4'), $server_ip)) { - // bad ip and port - return false; + // Passive mode + $this->_send_command('PASV', '', false); + + if (!$ip_port = $this->_check_command(true)) + { + return false; + } + + // open the connection to start sending the file + if (!preg_match('#[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+#', $ip_port, $temp)) + { + // bad ip and port + return false; + } + + $temp = explode(',', $temp[0]); + $server_ip = $temp[0] . '.' . $temp[1] . '.' . $temp[2] . '.' . $temp[3]; + $server_port = $temp[4] * 256 + $temp[5]; + } + else + { + // Extended Passive Mode - RFC2428 + $this->_send_command('EPSV', '', false); + + if (!$epsv_response = $this->_check_command(true)) + { + return false; + } + + // Response looks like "229 Entering Extended Passive Mode (|||12345|)" + // where 12345 is the tcp port for the data connection + if (!preg_match('#\(\|\|\|([0-9]+)\|\)#', $epsv_response, $match)) + { + return false; + } + $server_port = (int) $match[1]; + + // fsockopen expects IPv6 address in square brackets + $server_ip = "[$server_ip]"; } - $temp = explode(',', $temp[0]); - $server_ip = $temp[0] . '.' . $temp[1] . '.' . $temp[2] . '.' . $temp[3]; - $server_port = $temp[4] * 256 + $temp[5]; $errno = 0; $errstr = ''; diff --git a/phpBB/includes/mcp/mcp_logs.php b/phpBB/includes/mcp/mcp_logs.php index 6da810a489..73ff72c177 100644 --- a/phpBB/includes/mcp/mcp_logs.php +++ b/phpBB/includes/mcp/mcp_logs.php @@ -170,7 +170,7 @@ class mcp_logs // Grab log data $log_data = array(); $log_count = 0; - view_log('mod', $log_data, $log_count, $config['topics_per_page'], $start, $forum_list, $topic_id, 0, $sql_where, $sql_sort, $keywords); + $start = view_log('mod', $log_data, $log_count, $config['topics_per_page'], $start, $forum_list, $topic_id, 0, $sql_where, $sql_sort, $keywords); $template->assign_vars(array( 'PAGE_NUMBER' => on_page($log_count, $config['topics_per_page'], $start), @@ -179,7 +179,7 @@ class mcp_logs 'L_TITLE' => $user->lang['MCP_LOGS'], - 'U_POST_ACTION' => $this->u_action, + 'U_POST_ACTION' => $this->u_action . "&$u_sort_param$keywords_param&start=$start", 'S_CLEAR_ALLOWED' => ($auth->acl_get('a_clearlogs')) ? true : false, 'S_SELECT_SORT_DIR' => $s_sort_dir, 'S_SELECT_SORT_KEY' => $s_sort_key, diff --git a/phpBB/includes/mcp/mcp_main.php b/phpBB/includes/mcp/mcp_main.php index ad10a52705..ffede11d37 100644 --- a/phpBB/includes/mcp/mcp_main.php +++ b/phpBB/includes/mcp/mcp_main.php @@ -286,14 +286,6 @@ function change_topic_type($action, $topic_ids) { global $auth, $user, $db, $phpEx, $phpbb_root_path; - // For changing topic types, we only allow operations in one forum. - $forum_id = check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('f_announce', 'f_sticky', 'm_'), true); - - if ($forum_id === false) - { - return; - } - switch ($action) { case 'make_announce': @@ -316,11 +308,18 @@ function change_topic_type($action, $topic_ids) default: $new_topic_type = POST_NORMAL; - $check_acl = ''; + $check_acl = false; $l_new_type = (sizeof($topic_ids) == 1) ? 'MCP_MAKE_NORMAL' : 'MCP_MAKE_NORMALS'; break; } + $forum_id = check_ids($topic_ids, TOPICS_TABLE, 'topic_id', $check_acl, true); + + if ($forum_id === false) + { + return; + } + $redirect = request_var('redirect', build_url(array('action', 'quickmod'))); $s_hidden_fields = array( diff --git a/phpBB/includes/mcp/mcp_notes.php b/phpBB/includes/mcp/mcp_notes.php index c684eb6f52..02a89c0251 100644 --- a/phpBB/includes/mcp/mcp_notes.php +++ b/phpBB/includes/mcp/mcp_notes.php @@ -198,7 +198,7 @@ class mcp_notes $log_data = array(); $log_count = 0; - view_log('user', $log_data, $log_count, $config['topics_per_page'], $start, 0, 0, $user_id, $sql_where, $sql_sort, $keywords); + $start = view_log('user', $log_data, $log_count, $config['topics_per_page'], $start, 0, 0, $user_id, $sql_where, $sql_sort, $keywords); if ($log_count) { diff --git a/phpBB/includes/mcp/mcp_post.php b/phpBB/includes/mcp/mcp_post.php index de7f3e63ee..ba45037a18 100644 --- a/phpBB/includes/mcp/mcp_post.php +++ b/phpBB/includes/mcp/mcp_post.php @@ -246,7 +246,7 @@ function mcp_post_details($id, $mode, $action) } // Get Reports - if ($auth->acl_get('m_', $post_info['forum_id'])) + if ($auth->acl_get('m_report', $post_info['forum_id'])) { $sql = 'SELECT r.*, re.*, u.user_id, u.username FROM ' . REPORTS_TABLE . ' r, ' . USERS_TABLE . ' u, ' . REPORTS_REASONS_TABLE . " re diff --git a/phpBB/includes/mcp/mcp_queue.php b/phpBB/includes/mcp/mcp_queue.php index c419da5574..764461fa53 100644 --- a/phpBB/includes/mcp/mcp_queue.php +++ b/phpBB/includes/mcp/mcp_queue.php @@ -216,6 +216,7 @@ class mcp_queue 'POST_IP' => $post_info['poster_ip'], 'POST_IPADDR' => ($auth->acl_get('m_info', $post_info['forum_id']) && request_var('lookup', '')) ? @gethostbyaddr($post_info['poster_ip']) : '', 'POST_ID' => $post_info['post_id'], + 'S_FIRST_POST' => ($post_info['topic_first_post_id'] == $post_id), 'U_LOOKUP_IP' => ($auth->acl_get('m_info', $post_info['forum_id'])) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=queue&mode=approve_details&f=' . $post_info['forum_id'] . '&p=' . $post_id . '&lookup=' . $post_info['poster_ip']) . '#ip' : '', )); @@ -778,6 +779,8 @@ function disapprove_post($post_id_list, $id, $mode) if (!$row || (!$reason && strtolower($row['reason_title']) == 'other')) { $additional_msg = $user->lang['NO_REASON_DISAPPROVAL']; + unset($_REQUEST['confirm_key']); + unset($_POST['confirm_key']); unset($_POST['confirm']); } else diff --git a/phpBB/includes/mcp/mcp_reports.php b/phpBB/includes/mcp/mcp_reports.php index 39d9fbd4af..def5422be2 100644 --- a/phpBB/includes/mcp/mcp_reports.php +++ b/phpBB/includes/mcp/mcp_reports.php @@ -148,6 +148,7 @@ class mcp_reports $message = bbcode_nl2br($message); $message = smiley_text($message); + $report['report_text'] = make_clickable(bbcode_nl2br($report['report_text'])); if ($post_info['post_attachment'] && $auth->acl_get('u_download') && $auth->acl_get('f_download', $post_info['forum_id'])) { diff --git a/phpBB/includes/mcp/mcp_topic.php b/phpBB/includes/mcp/mcp_topic.php index 76cd9beb92..d7cc1e795a 100644 --- a/phpBB/includes/mcp/mcp_topic.php +++ b/phpBB/includes/mcp/mcp_topic.php @@ -239,8 +239,8 @@ function mcp_topic_view($id, $mode, $action) 'MINI_POST_IMG' => ($post_unread) ? $user->img('icon_post_target_unread', 'UNREAD_POST') : $user->img('icon_post_target', 'POST'), - 'S_POST_REPORTED' => ($row['post_reported']) ? true : false, - 'S_POST_UNAPPROVED' => ($row['post_approved']) ? false : true, + 'S_POST_REPORTED' => ($row['post_reported'] && $auth->acl_get('m_report', $topic_info['forum_id'])), + 'S_POST_UNAPPROVED' => (!$row['post_approved'] && $auth->acl_get('m_approve', $topic_info['forum_id'])), 'S_CHECKED' => (($submitted_id_list && !in_array(intval($row['post_id']), $submitted_id_list)) || in_array(intval($row['post_id']), $checked_ids)) ? true : false, 'S_HAS_ATTACHMENTS' => (!empty($attachments[$row['post_id']])) ? true : false, diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php index e9e706e2b8..caadcbafaa 100644 --- a/phpBB/includes/session.php +++ b/phpBB/includes/session.php @@ -2410,6 +2410,39 @@ class user extends session return true; } + + /** + * Returns all password protected forum ids the user is currently NOT authenticated for. + * + * @return array Array of forum ids + * @access public + */ + function get_passworded_forums() + { + global $db; + + $sql = 'SELECT f.forum_id, fa.user_id + FROM ' . FORUMS_TABLE . ' f + LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa + ON (fa.forum_id = f.forum_id + AND fa.session_id = '" . $db->sql_escape($this->session_id) . "') + WHERE f.forum_password <> ''"; + $result = $db->sql_query($sql); + + $forum_ids = array(); + while ($row = $db->sql_fetchrow($result)) + { + $forum_id = (int) $row['forum_id']; + + if ($row['user_id'] != $this->data['user_id']) + { + $forum_ids[$forum_id] = $forum_id; + } + } + $db->sql_freeresult($result); + + return $forum_ids; + } } ?> \ No newline at end of file diff --git a/phpBB/includes/startup.php b/phpBB/includes/startup.php index ca9665da29..bbe2f127f1 100644 --- a/phpBB/includes/startup.php +++ b/phpBB/includes/startup.php @@ -97,8 +97,8 @@ function deregister_globals() unset($input); } -// If we are on PHP >= 6.0.0 we do not need some code -if (version_compare(PHP_VERSION, '6.0.0-dev', '>=')) +// Register globals and magic quotes have been dropped in PHP 5.4 +if (version_compare(PHP_VERSION, '5.4.0-dev', '>=')) { /** * @ignore diff --git a/phpBB/includes/ucp/ucp_activate.php b/phpBB/includes/ucp/ucp_activate.php index b00c1b9f52..82c1937919 100644 --- a/phpBB/includes/ucp/ucp_activate.php +++ b/phpBB/includes/ucp/ucp_activate.php @@ -117,10 +117,7 @@ class ucp_activate $messenger->to($user_row['user_email'], $user_row['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'USERNAME' => htmlspecialchars_decode($user_row['username'])) diff --git a/phpBB/includes/ucp/ucp_groups.php b/phpBB/includes/ucp/ucp_groups.php index 1c055a4823..d62dbb1866 100644 --- a/phpBB/includes/ucp/ucp_groups.php +++ b/phpBB/includes/ucp/ucp_groups.php @@ -193,48 +193,44 @@ class ucp_groups if ($group_row[$group_id]['group_type'] == GROUP_FREE) { group_user_add($group_id, $user->data['user_id']); - - $email_template = 'group_added'; } else { group_user_add($group_id, $user->data['user_id'], false, false, false, 0, 1); - $email_template = 'group_request'; + include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx); + $messenger = new messenger(); + + $sql = 'SELECT u.username, u.username_clean, u.user_email, u.user_notify_type, u.user_jabber, u.user_lang + FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u + WHERE ug.user_id = u.user_id + AND ug.group_leader = 1 + AND ug.group_id = $group_id"; + $result = $db->sql_query($sql); + + while ($row = $db->sql_fetchrow($result)) + { + $messenger->template('group_request', $row['user_lang']); + + $messenger->to($row['user_email'], $row['username']); + $messenger->im($row['user_jabber'], $row['username']); + + $messenger->assign_vars(array( + 'USERNAME' => htmlspecialchars_decode($row['username']), + 'GROUP_NAME' => htmlspecialchars_decode($group_row[$group_id]['group_name']), + 'REQUEST_USERNAME' => $user->data['username'], + + 'U_PENDING' => generate_board_url() . "/ucp.$phpEx?i=groups&mode=manage&action=list&g=$group_id", + 'U_GROUP' => generate_board_url() . "/memberlist.$phpEx?mode=group&g=$group_id") + ); + + $messenger->send($row['user_notify_type']); + } + $db->sql_freeresult($result); + + $messenger->save_queue(); } - include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx); - $messenger = new messenger(); - - $sql = 'SELECT u.username, u.username_clean, u.user_email, u.user_notify_type, u.user_jabber, u.user_lang - FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u - WHERE ug.user_id = u.user_id - AND ' . (($group_row[$group_id]['group_type'] == GROUP_FREE) ? "ug.user_id = {$user->data['user_id']}" : 'ug.group_leader = 1') . " - AND ug.group_id = $group_id"; - $result = $db->sql_query($sql); - - while ($row = $db->sql_fetchrow($result)) - { - $messenger->template($email_template, $row['user_lang']); - - $messenger->to($row['user_email'], $row['username']); - $messenger->im($row['user_jabber'], $row['username']); - - $messenger->assign_vars(array( - 'USERNAME' => htmlspecialchars_decode($row['username']), - 'GROUP_NAME' => htmlspecialchars_decode($group_row[$group_id]['group_name']), - 'REQUEST_USERNAME' => $user->data['username'], - - 'U_PENDING' => generate_board_url() . "/ucp.$phpEx?i=groups&mode=manage&action=list&g=$group_id", - 'U_GROUP' => generate_board_url() . "/memberlist.$phpEx?mode=group&g=$group_id") - ); - - $messenger->send($row['user_notify_type']); - } - $db->sql_freeresult($result); - - $messenger->save_queue(); - add_log('user', $user->data['user_id'], 'LOG_USER_GROUP_JOIN' . (($group_row[$group_id]['group_type'] == GROUP_FREE) ? '' : '_PENDING'), $group_row[$group_id]['group_name']); meta_refresh(3, $this->u_action); diff --git a/phpBB/includes/ucp/ucp_pm.php b/phpBB/includes/ucp/ucp_pm.php index c675928a5b..447b6ebe87 100644 --- a/phpBB/includes/ucp/ucp_pm.php +++ b/phpBB/includes/ucp/ucp_pm.php @@ -243,7 +243,7 @@ class ucp_pm $num_not_moved = $num_removed = 0; $release = request_var('release', 0); - if ($user->data['user_new_privmsg'] && $action == 'view_folder') + if ($user->data['user_new_privmsg'] && ($action == 'view_folder' || $action == 'view_message')) { $return = place_pm_into_folder($global_privmsgs_rules, $release); $num_not_moved = $return['not_moved']; diff --git a/phpBB/includes/ucp/ucp_pm_viewmessage.php b/phpBB/includes/ucp/ucp_pm_viewmessage.php index d0cfa1ffd2..82a095dd9c 100644 --- a/phpBB/includes/ucp/ucp_pm_viewmessage.php +++ b/phpBB/includes/ucp/ucp_pm_viewmessage.php @@ -208,7 +208,7 @@ function view_message($id, $mode, $folder_id, $msg_id, $folder, $message_row) 'U_PM' => ($config['allow_privmsg'] && $auth->acl_get('u_sendpm') && ($user_info['user_allow_pm'] || $auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_'))) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=compose&u=' . $author_id) : '', 'U_WWW' => (!empty($user_info['user_website'])) ? $user_info['user_website'] : '', - 'U_ICQ' => ($user_info['user_icq']) ? 'http://www.icq.com/people' . urlencode($user_info['user_icq']) . '/' : '', + 'U_ICQ' => ($user_info['user_icq']) ? 'http://www.icq.com/people/' . urlencode($user_info['user_icq']) . '/' : '', 'U_AIM' => ($user_info['user_aim'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&action=aim&u=' . $author_id) : '', 'U_YIM' => ($user_info['user_yim']) ? 'http://edit.yahoo.com/config/send_webmesg?.target=' . urlencode($user_info['user_yim']) . '&.src=pg' : '', 'U_MSN' => ($user_info['user_msnm'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contact&action=msnm&u=' . $author_id) : '', diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php index c099e3b3fa..d35d13b6c1 100644 --- a/phpBB/includes/ucp/ucp_profile.php +++ b/phpBB/includes/ucp/ucp_profile.php @@ -78,14 +78,14 @@ class ucp_profile $error = validate_data($data, $check_ary); - if ($auth->acl_get('u_chgpasswd') && $data['new_password'] && $data['password_confirm'] != $data['new_password']) + if ($auth->acl_get('u_chgemail') && $data['email'] != $user->data['user_email'] && $data['email_confirm'] != $data['email']) { - $error[] = 'NEW_PASSWORD_ERROR'; + $error[] = ($data['email_confirm']) ? 'NEW_EMAIL_ERROR' : 'NEW_EMAIL_CONFIRM_EMPTY'; } - if (($data['new_password'] || ($auth->acl_get('u_chgemail') && $data['email'] != $user->data['user_email']) || ($data['username'] != $user->data['username'] && $auth->acl_get('u_chgname') && $config['allow_namechange'])) && !phpbb_check_hash($data['cur_password'], $user->data['user_password'])) + if ($auth->acl_get('u_chgpasswd') && $data['new_password'] && $data['password_confirm'] != $data['new_password']) { - $error[] = 'CUR_PASSWORD_ERROR'; + $error[] = ($data['password_confirm']) ? 'NEW_PASSWORD_ERROR' : 'NEW_PASSWORD_CONFIRM_EMPTY'; } // Only check the new password against the previous password if there have been no errors @@ -94,9 +94,9 @@ class ucp_profile $error[] = 'SAME_PASSWORD_ERROR'; } - if ($auth->acl_get('u_chgemail') && $data['email'] != $user->data['user_email'] && $data['email_confirm'] != $data['email']) + if (!phpbb_check_hash($data['cur_password'], $user->data['user_password'])) { - $error[] = 'NEW_EMAIL_ERROR'; + $error[] = ($data['cur_password']) ? 'CUR_PASSWORD_ERROR' : 'CUR_PASSWORD_EMPTY'; } if (!check_form_key('ucp_reg_details')) @@ -150,10 +150,7 @@ class ucp_profile $messenger->to($data['email'], $data['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'USERNAME' => htmlspecialchars_decode($data['username']), diff --git a/phpBB/includes/ucp/ucp_register.php b/phpBB/includes/ucp/ucp_register.php index 13b9945851..4e8729db56 100644 --- a/phpBB/includes/ucp/ucp_register.php +++ b/phpBB/includes/ucp/ucp_register.php @@ -155,8 +155,8 @@ class ucp_register $this->tpl_name = 'ucp_agreement'; return; } - - + + // The CAPTCHA kicks in here. We can't help that the information gets lost on language change. if ($config['enable_confirm']) { @@ -366,10 +366,7 @@ class ucp_register $messenger->to($data['email'], $data['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'WELCOME_MSG' => htmlspecialchars_decode(sprintf($user->lang['WELCOME_SUBJECT'], $config['sitename'])), diff --git a/phpBB/includes/ucp/ucp_resend.php b/phpBB/includes/ucp/ucp_resend.php index 39e9be24a1..4d181dba49 100644 --- a/phpBB/includes/ucp/ucp_resend.php +++ b/phpBB/includes/ucp/ucp_resend.php @@ -94,10 +94,7 @@ class ucp_resend $messenger->template(($coppa) ? 'coppa_resend_inactive' : 'user_resend_inactive', $user_row['user_lang']); $messenger->to($user_row['user_email'], $user_row['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'WELCOME_MSG' => htmlspecialchars_decode(sprintf($user->lang['WELCOME_SUBJECT'], $config['sitename'])), @@ -133,10 +130,7 @@ class ucp_resend $messenger->to($row['user_email'], $row['username']); $messenger->im($row['user_jabber'], $row['username']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'USERNAME' => htmlspecialchars_decode($user_row['username']), diff --git a/phpBB/index.php b/phpBB/index.php index cc83641acd..0105a0a1bd 100644 --- a/phpBB/index.php +++ b/phpBB/index.php @@ -81,15 +81,23 @@ $legend = implode(', ', $legend); // Generate birthday list if required ... $birthday_list = ''; -if ($config['load_birthdays'] && $config['allow_birthdays']) +if ($config['load_birthdays'] && $config['allow_birthdays'] && $auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) { - $now = getdate(time() + $user->timezone + $user->dst - date('Z')); + $now = phpbb_gmgetdate(time() + $user->timezone + $user->dst); + + // Display birthdays of 29th february on 28th february in non-leap-years + $leap_year_birthdays = ''; + if ($now['mday'] == 28 && $now['mon'] == 2 && !$user->format_date(time(), 'L')) + { + $leap_year_birthdays = " OR user_birthday LIKE '" . $db->sql_escape(sprintf('%2d-%2d-', 29, 2)) . "%'"; + } + $sql = 'SELECT u.user_id, u.username, u.user_colour, u.user_birthday FROM ' . USERS_TABLE . ' u LEFT JOIN ' . BANLIST_TABLE . " b ON (u.user_id = b.ban_userid) WHERE (b.ban_id IS NULL OR b.ban_exclude = 1) - AND u.user_birthday LIKE '" . $db->sql_escape(sprintf('%2d-%2d-', $now['mday'], $now['mon'])) . "%' + AND (u.user_birthday LIKE '" . $db->sql_escape(sprintf('%2d-%2d-', $now['mday'], $now['mon'])) . "%' $leap_year_birthdays) AND u.user_type IN (" . USER_NORMAL . ', ' . USER_FOUNDER . ')'; $result = $db->sql_query($sql); @@ -99,7 +107,7 @@ if ($config['load_birthdays'] && $config['allow_birthdays']) if ($age = (int) substr($row['user_birthday'], -4)) { - $birthday_list .= ' (' . ($now['year'] - $age) . ')'; + $birthday_list .= ' (' . max(0, $now['year'] - $age) . ')'; } } $db->sql_freeresult($result); diff --git a/phpBB/install/convertors/convert_phpbb20.php b/phpBB/install/convertors/convert_phpbb20.php index 7f6e3c0250..81cc2f68f3 100644 --- a/phpBB/install/convertors/convert_phpbb20.php +++ b/phpBB/install/convertors/convert_phpbb20.php @@ -32,7 +32,7 @@ unset($dbpasswd); $convertor_data = array( 'forum_name' => 'phpBB 2.0.x', 'version' => '1.0.3', - 'phpbb_version' => '3.0.9', + 'phpbb_version' => '3.0.10', 'author' => 'phpBB Group', 'dbms' => $dbms, 'dbhost' => $dbhost, @@ -504,7 +504,7 @@ if (!$get_info) array('topic_type', 'topics.topic_type', 'phpbb_convert_topic_type'), array('topic_first_post_id', 'topics.topic_first_post_id', ''), array('topic_last_view_time', 'posts.post_time', 'intval'), - array('poll_title', 'vote_desc.vote_text', array('function1' => 'null_to_str', 'function2' => 'phpbb_set_encoding', 'function3' => 'utf8_htmlspecialchars')), + array('poll_title', 'vote_desc.vote_text', array('function1' => 'null_to_str', 'function2' => 'phpbb_set_encoding', 'function3' => 'htmlspecialchars_decode', 'function4' => 'utf8_htmlspecialchars')), array('poll_start', 'vote_desc.vote_start', 'null_to_zero'), array('poll_length', 'vote_desc.vote_length', 'null_to_zero'), array('poll_max_options', 1, ''), @@ -537,7 +537,7 @@ if (!$get_info) array('topic_type', 'topics.topic_type', 'phpbb_convert_topic_type'), array('topic_first_post_id', 'topics.topic_first_post_id', ''), - array('poll_title', 'vote_desc.vote_text', array('function1' => 'null_to_str', 'function2' => 'phpbb_set_encoding', 'function3' => 'utf8_htmlspecialchars')), + array('poll_title', 'vote_desc.vote_text', array('function1' => 'null_to_str', 'function2' => 'phpbb_set_encoding', 'function3' => 'htmlspecialchars_decode', 'function4' => 'utf8_htmlspecialchars')), array('poll_start', 'vote_desc.vote_start', 'null_to_zero'), array('poll_length', 'vote_desc.vote_length', 'null_to_zero'), array('poll_max_options', 1, ''), @@ -582,7 +582,7 @@ if (!$get_info) array('poll_option_id', 'vote_results.vote_option_id', ''), array('topic_id', 'vote_desc.topic_id', ''), array('', 'topics.topic_poster AS poster_id', 'phpbb_user_id'), - array('poll_option_text', 'vote_results.vote_option_text', array('function1' => 'phpbb_set_encoding', 'function2' => 'utf8_htmlspecialchars')), + array('poll_option_text', 'vote_results.vote_option_text', array('function1' => 'phpbb_set_encoding', 'function2' => 'htmlspecialchars_decode', 'function3' => 'utf8_htmlspecialchars')), array('poll_option_total', 'vote_results.vote_result', ''), 'where' => 'vote_results.vote_id = vote_desc.vote_id', diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index e73f859758..5694c6e29f 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -8,7 +8,7 @@ * */ -define('UPDATES_TO_VERSION', '3.0.9'); +define('UPDATES_TO_VERSION', '3.0.10'); // Enter any version to update from to test updates. The version within the db will not be updated. define('DEBUG_FROM_VERSION', false); @@ -99,6 +99,8 @@ require($phpbb_root_path . 'includes/constants.' . $phpEx); require($phpbb_root_path . 'includes/db/' . $dbms . '.' . $phpEx); require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); +phpbb_require_updated('includes/db/db_tools.' . $phpEx); + // new table constants are separately defined here in case the updater is run // before the files are updated if (!defined('LOGIN_ATTEMPT_TABLE')) @@ -178,9 +180,9 @@ while ($row = $db->sql_fetchrow($result)) } $db->sql_freeresult($result); -// We do not include DB Tools here, because we can not be sure the file is up-to-date ;) -// Instead, this file defines a clean db_tools version (we are also not able to provide a different file, else the database update will not work standalone) -$db_tools = new updater_db_tools($db, true); +// phpbb_db_tools will be taken from new files (under install/update/new) +// if possible, falling back to the board's copy. +$db_tools = new phpbb_db_tools($db, true); $database_update_info = database_update_info(); @@ -949,7 +951,7 @@ function database_update_info() // this column was removed from the database updater // after 3.0.9-RC3 was released. It might still exist // in 3.0.9-RCX installations and has to be dropped in - // 3.0.10 after the db_tools class is capable of properly + // 3.0.11 after the db_tools class is capable of properly // removing a primary key. // 'attempt_id' => array('UINT', NULL, 'auto_increment'), 'attempt_ip' => array('VCHAR:40', ''), @@ -983,8 +985,16 @@ function database_update_info() '3.0.9-RC3' => array(), // No changes from 3.0.9-RC4 to 3.0.9 '3.0.9-RC4' => array(), + // No changes from 3.0.9 to 3.0.10-RC1 + '3.0.9' => array(), + // No changes from 3.0.10-RC1 to 3.0.10-RC2 + '3.0.10-RC1' => array(), + // No changes from 3.0.10-RC2 to 3.0.10-RC3 + '3.0.10-RC2' => array(), + // No changes from 3.0.10-RC3 to 3.0.10 + '3.0.10-RC3' => array(), - /** @todo DROP LOGIN_ATTEMPT_TABLE.attempt_id in 3.0.10-RC1 */ + /** @todo DROP LOGIN_ATTEMPT_TABLE.attempt_id in 3.0.11-RC1 */ ); } @@ -1992,2245 +2002,28 @@ function change_database_data(&$no_updates, $version) // No changes from 3.0.9-RC4 to 3.0.9 case '3.0.9-RC4': break; - } -} - -/** -* Database Tools for handling cross-db actions such as altering columns, etc. -* Currently not supported is returning SQL for creating tables. -* -* @package dbal -*/ -class updater_db_tools -{ - /** - * Current sql layer - */ - var $sql_layer = ''; - - /** - * @var object DB object - */ - var $db = NULL; - - /** - * The Column types for every database we support - * @var array - */ - var $dbms_type_map = array( - 'mysql_41' => array( - 'INT:' => 'int(%d)', - 'BINT' => 'bigint(20)', - 'UINT' => 'mediumint(8) UNSIGNED', - 'UINT:' => 'int(%d) UNSIGNED', - 'TINT:' => 'tinyint(%d)', - 'USINT' => 'smallint(4) UNSIGNED', - 'BOOL' => 'tinyint(1) UNSIGNED', - 'VCHAR' => 'varchar(255)', - 'VCHAR:' => 'varchar(%d)', - 'CHAR:' => 'char(%d)', - 'XSTEXT' => 'text', - 'XSTEXT_UNI'=> 'varchar(100)', - 'STEXT' => 'text', - 'STEXT_UNI' => 'varchar(255)', - 'TEXT' => 'text', - 'TEXT_UNI' => 'text', - 'MTEXT' => 'mediumtext', - 'MTEXT_UNI' => 'mediumtext', - 'TIMESTAMP' => 'int(11) UNSIGNED', - 'DECIMAL' => 'decimal(5,2)', - 'DECIMAL:' => 'decimal(%d,2)', - 'PDECIMAL' => 'decimal(6,3)', - 'PDECIMAL:' => 'decimal(%d,3)', - 'VCHAR_UNI' => 'varchar(255)', - 'VCHAR_UNI:'=> 'varchar(%d)', - 'VCHAR_CI' => 'varchar(255)', - 'VARBINARY' => 'varbinary(255)', - ), - - 'mysql_40' => array( - 'INT:' => 'int(%d)', - 'BINT' => 'bigint(20)', - 'UINT' => 'mediumint(8) UNSIGNED', - 'UINT:' => 'int(%d) UNSIGNED', - 'TINT:' => 'tinyint(%d)', - 'USINT' => 'smallint(4) UNSIGNED', - 'BOOL' => 'tinyint(1) UNSIGNED', - 'VCHAR' => 'varbinary(255)', - 'VCHAR:' => 'varbinary(%d)', - 'CHAR:' => 'binary(%d)', - 'XSTEXT' => 'blob', - 'XSTEXT_UNI'=> 'blob', - 'STEXT' => 'blob', - 'STEXT_UNI' => 'blob', - 'TEXT' => 'blob', - 'TEXT_UNI' => 'blob', - 'MTEXT' => 'mediumblob', - 'MTEXT_UNI' => 'mediumblob', - 'TIMESTAMP' => 'int(11) UNSIGNED', - 'DECIMAL' => 'decimal(5,2)', - 'DECIMAL:' => 'decimal(%d,2)', - 'PDECIMAL' => 'decimal(6,3)', - 'PDECIMAL:' => 'decimal(%d,3)', - 'VCHAR_UNI' => 'blob', - 'VCHAR_UNI:'=> array('varbinary(%d)', 'limit' => array('mult', 3, 255, 'blob')), - 'VCHAR_CI' => 'blob', - 'VARBINARY' => 'varbinary(255)', - ), - - 'firebird' => array( - 'INT:' => 'INTEGER', - 'BINT' => 'DOUBLE PRECISION', - 'UINT' => 'INTEGER', - 'UINT:' => 'INTEGER', - 'TINT:' => 'INTEGER', - 'USINT' => 'INTEGER', - 'BOOL' => 'INTEGER', - 'VCHAR' => 'VARCHAR(255) CHARACTER SET NONE', - 'VCHAR:' => 'VARCHAR(%d) CHARACTER SET NONE', - 'CHAR:' => 'CHAR(%d) CHARACTER SET NONE', - 'XSTEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE', - 'STEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE', - 'TEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE', - 'MTEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE', - 'XSTEXT_UNI'=> 'VARCHAR(100) CHARACTER SET UTF8', - 'STEXT_UNI' => 'VARCHAR(255) CHARACTER SET UTF8', - 'TEXT_UNI' => 'BLOB SUB_TYPE TEXT CHARACTER SET UTF8', - 'MTEXT_UNI' => 'BLOB SUB_TYPE TEXT CHARACTER SET UTF8', - 'TIMESTAMP' => 'INTEGER', - 'DECIMAL' => 'DOUBLE PRECISION', - 'DECIMAL:' => 'DOUBLE PRECISION', - 'PDECIMAL' => 'DOUBLE PRECISION', - 'PDECIMAL:' => 'DOUBLE PRECISION', - 'VCHAR_UNI' => 'VARCHAR(255) CHARACTER SET UTF8', - 'VCHAR_UNI:'=> 'VARCHAR(%d) CHARACTER SET UTF8', - 'VCHAR_CI' => 'VARCHAR(255) CHARACTER SET UTF8', - 'VARBINARY' => 'CHAR(255) CHARACTER SET NONE', - ), - - 'mssql' => array( - 'INT:' => '[int]', - 'BINT' => '[float]', - 'UINT' => '[int]', - 'UINT:' => '[int]', - 'TINT:' => '[int]', - 'USINT' => '[int]', - 'BOOL' => '[int]', - 'VCHAR' => '[varchar] (255)', - 'VCHAR:' => '[varchar] (%d)', - 'CHAR:' => '[char] (%d)', - 'XSTEXT' => '[varchar] (1000)', - 'STEXT' => '[varchar] (3000)', - 'TEXT' => '[varchar] (8000)', - 'MTEXT' => '[text]', - 'XSTEXT_UNI'=> '[varchar] (100)', - 'STEXT_UNI' => '[varchar] (255)', - 'TEXT_UNI' => '[varchar] (4000)', - 'MTEXT_UNI' => '[text]', - 'TIMESTAMP' => '[int]', - 'DECIMAL' => '[float]', - 'DECIMAL:' => '[float]', - 'PDECIMAL' => '[float]', - 'PDECIMAL:' => '[float]', - 'VCHAR_UNI' => '[varchar] (255)', - 'VCHAR_UNI:'=> '[varchar] (%d)', - 'VCHAR_CI' => '[varchar] (255)', - 'VARBINARY' => '[varchar] (255)', - ), - - 'mssqlnative' => array( - 'INT:' => '[int]', - 'BINT' => '[float]', - 'UINT' => '[int]', - 'UINT:' => '[int]', - 'TINT:' => '[int]', - 'USINT' => '[int]', - 'BOOL' => '[int]', - 'VCHAR' => '[varchar] (255)', - 'VCHAR:' => '[varchar] (%d)', - 'CHAR:' => '[char] (%d)', - 'XSTEXT' => '[varchar] (1000)', - 'STEXT' => '[varchar] (3000)', - 'TEXT' => '[varchar] (8000)', - 'MTEXT' => '[text]', - 'XSTEXT_UNI'=> '[varchar] (100)', - 'STEXT_UNI' => '[varchar] (255)', - 'TEXT_UNI' => '[varchar] (4000)', - 'MTEXT_UNI' => '[text]', - 'TIMESTAMP' => '[int]', - 'DECIMAL' => '[float]', - 'DECIMAL:' => '[float]', - 'PDECIMAL' => '[float]', - 'PDECIMAL:' => '[float]', - 'VCHAR_UNI' => '[varchar] (255)', - 'VCHAR_UNI:'=> '[varchar] (%d)', - 'VCHAR_CI' => '[varchar] (255)', - 'VARBINARY' => '[varchar] (255)', - ), - - 'oracle' => array( - 'INT:' => 'number(%d)', - 'BINT' => 'number(20)', - 'UINT' => 'number(8)', - 'UINT:' => 'number(%d)', - 'TINT:' => 'number(%d)', - 'USINT' => 'number(4)', - 'BOOL' => 'number(1)', - 'VCHAR' => 'varchar2(255)', - 'VCHAR:' => 'varchar2(%d)', - 'CHAR:' => 'char(%d)', - 'XSTEXT' => 'varchar2(1000)', - 'STEXT' => 'varchar2(3000)', - 'TEXT' => 'clob', - 'MTEXT' => 'clob', - 'XSTEXT_UNI'=> 'varchar2(300)', - 'STEXT_UNI' => 'varchar2(765)', - 'TEXT_UNI' => 'clob', - 'MTEXT_UNI' => 'clob', - 'TIMESTAMP' => 'number(11)', - 'DECIMAL' => 'number(5, 2)', - 'DECIMAL:' => 'number(%d, 2)', - 'PDECIMAL' => 'number(6, 3)', - 'PDECIMAL:' => 'number(%d, 3)', - 'VCHAR_UNI' => 'varchar2(765)', - 'VCHAR_UNI:'=> array('varchar2(%d)', 'limit' => array('mult', 3, 765, 'clob')), - 'VCHAR_CI' => 'varchar2(255)', - 'VARBINARY' => 'raw(255)', - ), - - 'sqlite' => array( - 'INT:' => 'int(%d)', - 'BINT' => 'bigint(20)', - 'UINT' => 'INTEGER UNSIGNED', //'mediumint(8) UNSIGNED', - 'UINT:' => 'INTEGER UNSIGNED', // 'int(%d) UNSIGNED', - 'TINT:' => 'tinyint(%d)', - 'USINT' => 'INTEGER UNSIGNED', //'mediumint(4) UNSIGNED', - 'BOOL' => 'INTEGER UNSIGNED', //'tinyint(1) UNSIGNED', - 'VCHAR' => 'varchar(255)', - 'VCHAR:' => 'varchar(%d)', - 'CHAR:' => 'char(%d)', - 'XSTEXT' => 'text(65535)', - 'STEXT' => 'text(65535)', - 'TEXT' => 'text(65535)', - 'MTEXT' => 'mediumtext(16777215)', - 'XSTEXT_UNI'=> 'text(65535)', - 'STEXT_UNI' => 'text(65535)', - 'TEXT_UNI' => 'text(65535)', - 'MTEXT_UNI' => 'mediumtext(16777215)', - 'TIMESTAMP' => 'INTEGER UNSIGNED', //'int(11) UNSIGNED', - 'DECIMAL' => 'decimal(5,2)', - 'DECIMAL:' => 'decimal(%d,2)', - 'PDECIMAL' => 'decimal(6,3)', - 'PDECIMAL:' => 'decimal(%d,3)', - 'VCHAR_UNI' => 'varchar(255)', - 'VCHAR_UNI:'=> 'varchar(%d)', - 'VCHAR_CI' => 'varchar(255)', - 'VARBINARY' => 'blob', - ), - - 'postgres' => array( - 'INT:' => 'INT4', - 'BINT' => 'INT8', - 'UINT' => 'INT4', // unsigned - 'UINT:' => 'INT4', // unsigned - 'USINT' => 'INT2', // unsigned - 'BOOL' => 'INT2', // unsigned - 'TINT:' => 'INT2', - 'VCHAR' => 'varchar(255)', - 'VCHAR:' => 'varchar(%d)', - 'CHAR:' => 'char(%d)', - 'XSTEXT' => 'varchar(1000)', - 'STEXT' => 'varchar(3000)', - 'TEXT' => 'varchar(8000)', - 'MTEXT' => 'TEXT', - 'XSTEXT_UNI'=> 'varchar(100)', - 'STEXT_UNI' => 'varchar(255)', - 'TEXT_UNI' => 'varchar(4000)', - 'MTEXT_UNI' => 'TEXT', - 'TIMESTAMP' => 'INT4', // unsigned - 'DECIMAL' => 'decimal(5,2)', - 'DECIMAL:' => 'decimal(%d,2)', - 'PDECIMAL' => 'decimal(6,3)', - 'PDECIMAL:' => 'decimal(%d,3)', - 'VCHAR_UNI' => 'varchar(255)', - 'VCHAR_UNI:'=> 'varchar(%d)', - 'VCHAR_CI' => 'varchar_ci', - 'VARBINARY' => 'bytea', - ), - ); - - /** - * A list of types being unsigned for better reference in some db's - * @var array - */ - var $unsigned_types = array('UINT', 'UINT:', 'USINT', 'BOOL', 'TIMESTAMP'); - - /** - * A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules. - * @var array - */ - var $supported_dbms = array('firebird', 'mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite'); - - /** - * This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array). - * This mode has no effect on some methods (inserting of data for example). This is expressed within the methods command. - */ - var $return_statements = false; - - /** - * Constructor. Set DB Object and set {@link $return_statements return_statements}. - * - * @param phpbb_dbal $db DBAL object - * @param bool $return_statements True if only statements should be returned and no SQL being executed - */ - function updater_db_tools(&$db, $return_statements = false) - { - $this->db = $db; - $this->return_statements = $return_statements; - - // Determine mapping database type - switch ($this->db->sql_layer) - { - case 'mysql': - $this->sql_layer = 'mysql_40'; - break; - - case 'mysql4': - if (version_compare($this->db->sql_server_info(true), '4.1.3', '>=')) - { - $this->sql_layer = 'mysql_41'; - } - else - { - $this->sql_layer = 'mysql_40'; - } - break; - - case 'mysqli': - $this->sql_layer = 'mysql_41'; - break; - - case 'mssql': - case 'mssql_odbc': - $this->sql_layer = 'mssql'; - break; - - case 'mssqlnative': - $this->sql_layer = 'mssqlnative'; - break; - - default: - $this->sql_layer = $this->db->sql_layer; - break; - } - } - - /** - * Check if table exists - * - * - * @param string $table_name The table name to check for - * @return bool true if table exists, else false - */ - function sql_table_exists($table_name) - { - $this->db->sql_return_on_error(true); - $result = $this->db->sql_query_limit('SELECT * FROM ' . $table_name, 1); - $this->db->sql_return_on_error(false); - - if ($result) - { - $this->db->sql_freeresult($result); - return true; - } - - return false; - } - - /** - * Create SQL Table - * - * @param string $table_name The table name to create - * @param array $table_data Array containing table data. - * @return array Statements if $return_statements is true. - */ - function sql_create_table($table_name, $table_data) - { - // holds the DDL for a column - $columns = $statements = array(); - - if ($this->sql_table_exists($table_name)) - { - return $this->_sql_run_sql($statements); - } - - // Begin transaction - $statements[] = 'begin'; - - // Determine if we have created a PRIMARY KEY in the earliest - $primary_key_gen = false; - - // Determine if the table must be created with TEXTIMAGE - $create_textimage = false; - - // Determine if the table requires a sequence - $create_sequence = false; - - // Begin table sql statement - switch ($this->sql_layer) - { - case 'mssql': - case 'mssqlnative': - $table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n"; - break; - - default: - $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n"; - break; - } - - // Iterate through the columns to create a table - foreach ($table_data['COLUMNS'] as $column_name => $column_data) - { - // here lies an array, filled with information compiled on the column's data - $prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data); - - if (isset($prepared_column['auto_increment']) && strlen($column_name) > 26) // "${column_name}_gen" + // Changes from 3.0.9 to 3.0.10-RC1 + case '3.0.9': + if (!isset($config['email_max_chunk_size'])) { - trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR); + set_config('email_max_chunk_size', '50'); } - // here we add the definition of the new column to the list of columns - switch ($this->sql_layer) - { - case 'mssql': - case 'mssqlnative': - $columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default']; - break; + $no_updates = false; + break; - default: - $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql']; - break; - } + // No changes from 3.0.10-RC1 to 3.0.10-RC2 + case '3.0.10-RC1': + break; - // see if we have found a primary key set due to a column definition if we have found it, we can stop looking - if (!$primary_key_gen) - { - $primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set']; - } + // No changes from 3.0.10-RC2 to 3.0.10-RC3 + case '3.0.10-RC2': + break; - // create textimage DDL based off of the existance of certain column types - if (!$create_textimage) - { - $create_textimage = isset($prepared_column['textimage']) && $prepared_column['textimage']; - } - - // create sequence DDL based off of the existance of auto incrementing columns - if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment']) - { - $create_sequence = $column_name; - } - } - - // this makes up all the columns in the create table statement - $table_sql .= implode(",\n", $columns); - - // Close the table for two DBMS and add to the statements - switch ($this->sql_layer) - { - case 'firebird': - $table_sql .= "\n);"; - $statements[] = $table_sql; - break; - - case 'mssql': - case 'mssqlnative': - $table_sql .= "\n) ON [PRIMARY]" . (($create_textimage) ? ' TEXTIMAGE_ON [PRIMARY]' : ''); - $statements[] = $table_sql; - break; - } - - // we have yet to create a primary key for this table, - // this means that we can add the one we really wanted instead - if (!$primary_key_gen) - { - // Write primary key - if (isset($table_data['PRIMARY_KEY'])) - { - if (!is_array($table_data['PRIMARY_KEY'])) - { - $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']); - } - - switch ($this->sql_layer) - { - case 'mysql_40': - case 'mysql_41': - case 'postgres': - case 'sqlite': - $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')'; - break; - - case 'firebird': - case 'mssql': - case 'mssqlnative': - // We need the data here - $old_return_statements = $this->return_statements; - $this->return_statements = true; - - $primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']); - foreach ($primary_key_stmts as $pk_stmt) - { - $statements[] = $pk_stmt; - } - - $this->return_statements = $old_return_statements; - break; - - case 'oracle': - $table_sql .= ",\n\t CONSTRAINT pk_{$table_name} PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')'; - break; - } - } - } - - // close the table - switch ($this->sql_layer) - { - case 'mysql_41': - // make sure the table is in UTF-8 mode - $table_sql .= "\n) CHARACTER SET `utf8` COLLATE `utf8_bin`;"; - $statements[] = $table_sql; - break; - - case 'mysql_40': - case 'sqlite': - $table_sql .= "\n);"; - $statements[] = $table_sql; - break; - - case 'postgres': - // do we need to add a sequence for auto incrementing columns? - if ($create_sequence) - { - $statements[] = "CREATE SEQUENCE {$table_name}_seq;"; - } - - $table_sql .= "\n);"; - $statements[] = $table_sql; - break; - - case 'oracle': - $table_sql .= "\n)"; - $statements[] = $table_sql; - - // do we need to add a sequence and a tigger for auto incrementing columns? - if ($create_sequence) - { - // create the actual sequence - $statements[] = "CREATE SEQUENCE {$table_name}_seq"; - - // the trigger is the mechanism by which we increment the counter - $trigger = "CREATE OR REPLACE TRIGGER t_{$table_name}\n"; - $trigger .= "BEFORE INSERT ON {$table_name}\n"; - $trigger .= "FOR EACH ROW WHEN (\n"; - $trigger .= "\tnew.{$create_sequence} IS NULL OR new.{$create_sequence} = 0\n"; - $trigger .= ")\n"; - $trigger .= "BEGIN\n"; - $trigger .= "\tSELECT {$table_name}_seq.nextval\n"; - $trigger .= "\tINTO :new.{$create_sequence}\n"; - $trigger .= "\tFROM dual;\n"; - $trigger .= "END;"; - - $statements[] = $trigger; - } - break; - - case 'firebird': - if ($create_sequence) - { - $statements[] = "CREATE GENERATOR {$table_name}_gen;"; - $statements[] = "SET GENERATOR {$table_name}_gen TO 0;"; - - $trigger = "CREATE TRIGGER t_$table_name FOR $table_name\n"; - $trigger .= "BEFORE INSERT\nAS\nBEGIN\n"; - $trigger .= "\tNEW.{$create_sequence} = GEN_ID({$table_name}_gen, 1);\nEND;"; - $statements[] = $trigger; - } - break; - } - - // Write Keys - if (isset($table_data['KEYS'])) - { - foreach ($table_data['KEYS'] as $key_name => $key_data) - { - if (!is_array($key_data[1])) - { - $key_data[1] = array($key_data[1]); - } - - $old_return_statements = $this->return_statements; - $this->return_statements = true; - - $key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]); - - foreach ($key_stmts as $key_stmt) - { - $statements[] = $key_stmt; - } - - $this->return_statements = $old_return_statements; - } - } - - // Commit Transaction - $statements[] = 'commit'; - - return $this->_sql_run_sql($statements); - } - - /** - * Handle passed database update array. - * Expected structure... - * Key being one of the following - * change_columns: Column changes (only type, not name) - * add_columns: Add columns to a table - * drop_keys: Dropping keys - * drop_columns: Removing/Dropping columns - * add_primary_keys: adding primary keys - * add_unique_index: adding an unique index - * add_index: adding an index (can be column:index_size if you need to provide size) - * - * The values are in this format: - * {TABLE NAME} => array( - * {COLUMN NAME} => array({COLUMN TYPE}, {DEFAULT VALUE}, {OPTIONAL VARIABLES}), - * {KEY/INDEX NAME} => array({COLUMN NAMES}), - * ) - * - * For more information have a look at /develop/create_schema_files.php (only available through SVN) - */ - function perform_schema_changes($schema_changes) - { - if (empty($schema_changes)) - { - return; - } - - $statements = array(); - $sqlite = false; - - // For SQLite we need to perform the schema changes in a much more different way - if ($this->db->sql_layer == 'sqlite' && $this->return_statements) - { - $sqlite_data = array(); - $sqlite = true; - } - - // Add tables? - if (!empty($schema_changes['add_tables'])) - { - foreach ($schema_changes['add_tables'] as $table => $table_data) - { - $result = $this->sql_create_table($table, $table_data); - if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - - // Change columns? - if (!empty($schema_changes['change_columns'])) - { - foreach ($schema_changes['change_columns'] as $table => $columns) - { - foreach ($columns as $column_name => $column_data) - { - // If the column exists we change it, else we add it ;) - if ($column_exists = $this->sql_column_exists($table, $column_name)) - { - $result = $this->sql_column_change($table, $column_name, $column_data, true); - } - else - { - $result = $this->sql_column_add($table, $column_name, $column_data, true); - } - - if ($sqlite) - { - if ($column_exists) - { - $sqlite_data[$table]['change_columns'][] = $result; - } - else - { - $sqlite_data[$table]['add_columns'][] = $result; - } - } - else if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - } - - // Add columns? - if (!empty($schema_changes['add_columns'])) - { - foreach ($schema_changes['add_columns'] as $table => $columns) - { - foreach ($columns as $column_name => $column_data) - { - // Only add the column if it does not exist yet - if ($column_exists = $this->sql_column_exists($table, $column_name)) - { - continue; - // This is commented out here because it can take tremendous time on updates -// $result = $this->sql_column_change($table, $column_name, $column_data, true); - } - else - { - $result = $this->sql_column_add($table, $column_name, $column_data, true); - } - - if ($sqlite) - { - if ($column_exists) - { - continue; -// $sqlite_data[$table]['change_columns'][] = $result; - } - else - { - $sqlite_data[$table]['add_columns'][] = $result; - } - } - else if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - } - - // Remove keys? - if (!empty($schema_changes['drop_keys'])) - { - foreach ($schema_changes['drop_keys'] as $table => $indexes) - { - foreach ($indexes as $index_name) - { - if (!$this->sql_index_exists($table, $index_name)) - { - continue; - } - - $result = $this->sql_index_drop($table, $index_name); - - if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - } - - // Drop columns? - if (!empty($schema_changes['drop_columns'])) - { - foreach ($schema_changes['drop_columns'] as $table => $columns) - { - foreach ($columns as $column) - { - // Only remove the column if it exists... - if ($this->sql_column_exists($table, $column)) - { - $result = $this->sql_column_remove($table, $column, true); - - if ($sqlite) - { - $sqlite_data[$table]['drop_columns'][] = $result; - } - else if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - } - } - - // Add primary keys? - if (!empty($schema_changes['add_primary_keys'])) - { - foreach ($schema_changes['add_primary_keys'] as $table => $columns) - { - $result = $this->sql_create_primary_key($table, $columns, true); - - if ($sqlite) - { - $sqlite_data[$table]['primary_key'] = $result; - } - else if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - - // Add unqiue indexes? - if (!empty($schema_changes['add_unique_index'])) - { - foreach ($schema_changes['add_unique_index'] as $table => $index_array) - { - foreach ($index_array as $index_name => $column) - { - if ($this->sql_unique_index_exists($table, $index_name)) - { - continue; - } - - $result = $this->sql_create_unique_index($table, $index_name, $column); - - if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - } - - // Add indexes? - if (!empty($schema_changes['add_index'])) - { - foreach ($schema_changes['add_index'] as $table => $index_array) - { - foreach ($index_array as $index_name => $column) - { - if ($this->sql_index_exists($table, $index_name)) - { - continue; - } - - $result = $this->sql_create_index($table, $index_name, $column); - - if ($this->return_statements) - { - $statements = array_merge($statements, $result); - } - } - } - } - - if ($sqlite) - { - foreach ($sqlite_data as $table_name => $sql_schema_changes) - { - // Create temporary table with original data - $statements[] = 'begin'; - - $sql = "SELECT sql - FROM sqlite_master - WHERE type = 'table' - AND name = '{$table_name}' - ORDER BY type DESC, name;"; - $result = $this->db->sql_query($sql); - - if (!$result) - { - continue; - } - - $row = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - - // Create a backup table and populate it, destroy the existing one - $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']); - $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; - $statements[] = 'DROP TABLE ' . $table_name; - - // Get the columns... - preg_match('#\((.*)\)#s', $row['sql'], $matches); - - $plain_table_cols = trim($matches[1]); - $new_table_cols = preg_split('/,(?![\s\w]+\))/m', $plain_table_cols); - $column_list = array(); - - foreach ($new_table_cols as $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if ($entities[0] == 'PRIMARY') - { - continue; - } - $column_list[] = $entities[0]; - } - - // note down the primary key notation because sqlite only supports adding it to the end for the new table - $primary_key = false; - $_new_cols = array(); - - foreach ($new_table_cols as $key => $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if ($entities[0] == 'PRIMARY') - { - $primary_key = $declaration; - continue; - } - $_new_cols[] = $declaration; - } - - $new_table_cols = $_new_cols; - - // First of all... change columns - if (!empty($sql_schema_changes['change_columns'])) - { - foreach ($sql_schema_changes['change_columns'] as $column_sql) - { - foreach ($new_table_cols as $key => $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if (strpos($column_sql, $entities[0] . ' ') === 0) - { - $new_table_cols[$key] = $column_sql; - } - } - } - } - - if (!empty($sql_schema_changes['add_columns'])) - { - foreach ($sql_schema_changes['add_columns'] as $column_sql) - { - $new_table_cols[] = $column_sql; - } - } - - // Now drop them... - if (!empty($sql_schema_changes['drop_columns'])) - { - foreach ($sql_schema_changes['drop_columns'] as $column_name) - { - // Remove from column list... - $new_column_list = array(); - foreach ($column_list as $key => $value) - { - if ($value === $column_name) - { - continue; - } - - $new_column_list[] = $value; - } - - $column_list = $new_column_list; - - // Remove from table... - $_new_cols = array(); - foreach ($new_table_cols as $key => $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if (strpos($column_name . ' ', $entities[0] . ' ') === 0) - { - continue; - } - $_new_cols[] = $declaration; - } - $new_table_cols = $_new_cols; - } - } - - // Primary key... - if (!empty($sql_schema_changes['primary_key'])) - { - $new_table_cols[] = 'PRIMARY KEY (' . implode(', ', $sql_schema_changes['primary_key']) . ')'; - } - // Add a new one or the old primary key - else if ($primary_key !== false) - { - $new_table_cols[] = $primary_key; - } - - $columns = implode(',', $column_list); - - // create a new table and fill it up. destroy the temp one - $statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $new_table_cols) . ');'; - $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; - $statements[] = 'DROP TABLE ' . $table_name . '_temp'; - - $statements[] = 'commit'; - } - } - - if ($this->return_statements) - { - return $statements; - } - } - - /** - * Check if a specified column exist - * - * @param string $table Table to check the column at - * @param string $column_name The column to check - * - * @return bool True if column exists, else false - */ - function sql_column_exists($table, $column_name) - { - switch ($this->sql_layer) - { - case 'mysql_40': - case 'mysql_41': - - $sql = "SHOW COLUMNS FROM $table"; - $result = $this->db->sql_query($sql); - - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['Field']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; - break; - - // PostgreSQL has a way of doing this in a much simpler way but would - // not allow us to support all versions of PostgreSQL - case 'postgres': - $sql = "SELECT a.attname - FROM pg_class c, pg_attribute a - WHERE c.relname = '{$table}' - AND a.attnum > 0 - AND a.attrelid = c.oid"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['attname']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - - return false; - break; - - // same deal with PostgreSQL, we must perform more complex operations than - // we technically could - case 'mssql': - case 'mssqlnative': - $sql = "SELECT c.name - FROM syscolumns c - LEFT JOIN sysobjects o ON c.id = o.id - WHERE o.name = '{$table}'"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['name']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; - break; - - case 'oracle': - $sql = "SELECT column_name - FROM user_tab_columns - WHERE LOWER(table_name) = '" . strtolower($table) . "'"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['column_name']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; - break; - - case 'firebird': - $sql = "SELECT RDB\$FIELD_NAME as FNAME - FROM RDB\$RELATION_FIELDS - WHERE RDB\$RELATION_NAME = '" . strtoupper($table) . "'"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - // lower case just in case - if (strtolower($row['fname']) == $column_name) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - return false; - break; - - // ugh, SQLite - case 'sqlite': - $sql = "SELECT sql - FROM sqlite_master - WHERE type = 'table' - AND name = '{$table}'"; - $result = $this->db->sql_query($sql); - - if (!$result) - { - return false; - } - - $row = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - - preg_match('#\((.*)\)#s', $row['sql'], $matches); - - $cols = trim($matches[1]); - $col_array = preg_split('/,(?![\s\w]+\))/m', $cols); - - foreach ($col_array as $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if ($entities[0] == 'PRIMARY') - { - continue; - } - - if (strtolower($entities[0]) == $column_name) - { - return true; - } - } - return false; - break; - } - } - - /** - * Check if a specified index exists in table. Does not return PRIMARY KEY and UNIQUE indexes. - * - * @param string $table_name Table to check the index at - * @param string $index_name The index name to check - * - * @return bool True if index exists, else false - */ - function sql_index_exists($table_name, $index_name) - { - if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') - { - $sql = "EXEC sp_statistics '$table_name'"; - $result = $this->db->sql_query($sql); - - while ($row = $this->db->sql_fetchrow($result)) - { - if ($row['TYPE'] == 3) - { - if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) - { - $this->db->sql_freeresult($result); - return true; - } - } - } - $this->db->sql_freeresult($result); - - return false; - } - - switch ($this->sql_layer) - { - case 'firebird': - $sql = "SELECT LOWER(RDB\$INDEX_NAME) as index_name - FROM RDB\$INDICES - WHERE RDB\$RELATION_NAME = '" . strtoupper($table_name) . "' - AND RDB\$UNIQUE_FLAG IS NULL - AND RDB\$FOREIGN_KEY IS NULL"; - $col = 'index_name'; - break; - - case 'postgres': - $sql = "SELECT ic.relname as index_name - FROM pg_class bc, pg_class ic, pg_index i - WHERE (bc.oid = i.indrelid) - AND (ic.oid = i.indexrelid) - AND (bc.relname = '" . $table_name . "') - AND (i.indisunique != 't') - AND (i.indisprimary != 't')"; - $col = 'index_name'; - break; - - case 'mysql_40': - case 'mysql_41': - $sql = 'SHOW KEYS - FROM ' . $table_name; - $col = 'Key_name'; - break; - - case 'oracle': - $sql = "SELECT index_name - FROM user_indexes - WHERE table_name = '" . strtoupper($table_name) . "' - AND generated = 'N' - AND uniqueness = 'NONUNIQUE'"; - $col = 'index_name'; - break; - - case 'sqlite': - $sql = "PRAGMA index_list('" . $table_name . "');"; - $col = 'name'; - break; - } - - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique']) - { - continue; - } - - // These DBMS prefix index name with the table name - switch ($this->sql_layer) - { - case 'firebird': - case 'oracle': - case 'postgres': - case 'sqlite': - $row[$col] = substr($row[$col], strlen($table_name) + 1); - break; - } - - if (strtolower($row[$col]) == strtolower($index_name)) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - - return false; - } - - /** - * Check if a specified UNIQUE index exists in table. - * - * @param string $table_name Table to check the index at - * @param string $index_name The index name to check - * - * @return bool True if index exists, else false - */ - function sql_unique_index_exists($table_name, $index_name) - { - if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') - { - $sql = "EXEC sp_statistics '$table_name'"; - $result = $this->db->sql_query($sql); - - while ($row = $this->db->sql_fetchrow($result)) - { - // Usually NON_UNIQUE is the column we want to check, but we allow for both - if ($row['TYPE'] == 3) - { - if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) - { - $this->db->sql_freeresult($result); - return true; - } - } - } - $this->db->sql_freeresult($result); - return false; - } - - switch ($this->sql_layer) - { - case 'firebird': - $sql = "SELECT LOWER(RDB\$INDEX_NAME) as index_name - FROM RDB\$INDICES - WHERE RDB\$RELATION_NAME = '" . strtoupper($table_name) . "' - AND RDB\$UNIQUE_FLAG IS NOT NULL - AND RDB\$FOREIGN_KEY IS NULL"; - $col = 'index_name'; - break; - - case 'postgres': - $sql = "SELECT ic.relname as index_name, i.indisunique - FROM pg_class bc, pg_class ic, pg_index i - WHERE (bc.oid = i.indrelid) - AND (ic.oid = i.indexrelid) - AND (bc.relname = '" . $table_name . "') - AND (i.indisprimary != 't')"; - $col = 'index_name'; - break; - - case 'mysql_40': - case 'mysql_41': - $sql = 'SHOW KEYS - FROM ' . $table_name; - $col = 'Key_name'; - break; - - case 'oracle': - $sql = "SELECT index_name, table_owner - FROM user_indexes - WHERE table_name = '" . strtoupper($table_name) . "' - AND generated = 'N' - AND uniqueness = 'UNIQUE'"; - $col = 'index_name'; - break; - - case 'sqlite': - $sql = "PRAGMA index_list('" . $table_name . "');"; - $col = 'name'; - break; - } - - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && ($row['Non_unique'] || $row[$col] == 'PRIMARY')) - { - continue; - } - - if ($this->sql_layer == 'sqlite' && !$row['unique']) - { - continue; - } - - if ($this->sql_layer == 'postgres' && $row['indisunique'] != 't') - { - continue; - } - - // These DBMS prefix index name with the table name - switch ($this->sql_layer) - { - case 'oracle': - // Two cases here... prefixed with U_[table_owner] and not prefixed with table_name - if (strpos($row[$col], 'U_') === 0) - { - $row[$col] = substr($row[$col], strlen('U_' . $row['table_owner']) + 1); - } - else if (strpos($row[$col], strtoupper($table_name)) === 0) - { - $row[$col] = substr($row[$col], strlen($table_name) + 1); - } - break; - - case 'firebird': - case 'postgres': - case 'sqlite': - $row[$col] = substr($row[$col], strlen($table_name) + 1); - break; - } - - if (strtolower($row[$col]) == strtolower($index_name)) - { - $this->db->sql_freeresult($result); - return true; - } - } - $this->db->sql_freeresult($result); - - return false; - } - - /** - * Private method for performing sql statements (either execute them or return them) - * @access private - */ - function _sql_run_sql($statements) - { - if ($this->return_statements) - { - return $statements; - } - - // We could add error handling here... - foreach ($statements as $sql) - { - if ($sql === 'begin') - { - $this->db->sql_transaction('begin'); - } - else if ($sql === 'commit') - { - $this->db->sql_transaction('commit'); - } - else - { - $this->db->sql_query($sql); - } - } - - return true; - } - - /** - * Function to prepare some column information for better usage - * @access private - */ - function sql_prepare_column_data($table_name, $column_name, $column_data) - { - if (strlen($column_name) > 30) - { - trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR); - } - - // Get type - if (strpos($column_data[0], ':') !== false) - { - list($orig_column_type, $column_length) = explode(':', $column_data[0]); - if (!is_array($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'])) - { - $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'], $column_length); - } - else - { - if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'])) - { - switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][0]) - { - case 'div': - $column_length /= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][1]; - $column_length = ceil($column_length); - $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length); - break; - } - } - - if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'])) - { - switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][0]) - { - case 'mult': - $column_length *= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][1]; - if ($column_length > $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][2]) - { - $column_type = $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][3]; - } - else - { - $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length); - } - break; - } - } - } - $orig_column_type .= ':'; - } - else - { - $orig_column_type = $column_data[0]; - $column_type = $this->dbms_type_map[$this->sql_layer][$column_data[0]]; - } - - // Adjust default value if db-dependant specified - if (is_array($column_data[1])) - { - $column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default']; - } - - $sql = ''; - - $return_array = array(); - - switch ($this->sql_layer) - { - case 'firebird': - $sql .= " {$column_type} "; - $return_array['column_type_sql_type'] = " {$column_type} "; - - if (!is_null($column_data[1])) - { - $sql .= 'DEFAULT ' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ' '; - $return_array['column_type_sql_default'] = ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ' '; - } - - $sql .= 'NOT NULL'; - - // This is a UNICODE column and thus should be given it's fair share - if (preg_match('/^X?STEXT_UNI|VCHAR_(CI|UNI:?)/', $column_data[0])) - { - $sql .= ' COLLATE UNICODE'; - } - - $return_array['auto_increment'] = false; - if (isset($column_data[2]) && $column_data[2] == 'auto_increment') - { - $return_array['auto_increment'] = true; - } - - break; - - case 'mssql': - case 'mssqlnative': - $sql .= " {$column_type} "; - $sql_default = " {$column_type} "; - - // For adding columns we need the default definition - if (!is_null($column_data[1])) - { - // For hexadecimal values do not use single quotes - if (strpos($column_data[1], '0x') === 0) - { - $return_array['default'] = 'DEFAULT (' . $column_data[1] . ') '; - $sql_default .= $return_array['default']; - } - else - { - $return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') '; - $sql_default .= $return_array['default']; - } - } - - if (isset($column_data[2]) && $column_data[2] == 'auto_increment') - { -// $sql .= 'IDENTITY (1, 1) '; - $sql_default .= 'IDENTITY (1, 1) '; - } - - $return_array['textimage'] = $column_type === '[text]'; - - $sql .= 'NOT NULL'; - $sql_default .= 'NOT NULL'; - - $return_array['column_type_sql_default'] = $sql_default; - - break; - - case 'mysql_40': - case 'mysql_41': - $sql .= " {$column_type} "; - - // For hexadecimal values do not use single quotes - if (!is_null($column_data[1]) && substr($column_type, -4) !== 'text' && substr($column_type, -4) !== 'blob') - { - $sql .= (strpos($column_data[1], '0x') === 0) ? "DEFAULT {$column_data[1]} " : "DEFAULT '{$column_data[1]}' "; - } - $sql .= 'NOT NULL'; - - if (isset($column_data[2])) - { - if ($column_data[2] == 'auto_increment') - { - $sql .= ' auto_increment'; - } - else if ($this->sql_layer === 'mysql_41' && $column_data[2] == 'true_sort') - { - $sql .= ' COLLATE utf8_unicode_ci'; - } - } - - break; - - case 'oracle': - $sql .= " {$column_type} "; - $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}' " : ''; - - // In Oracle empty strings ('') are treated as NULL. - // Therefore in oracle we allow NULL's for all DEFAULT '' entries - // Oracle does not like setting NOT NULL on a column that is already NOT NULL (this happens only on number fields) - if (!preg_match('/number/i', $column_type)) - { - $sql .= ($column_data[1] === '') ? '' : 'NOT NULL'; - } - - $return_array['auto_increment'] = false; - if (isset($column_data[2]) && $column_data[2] == 'auto_increment') - { - $return_array['auto_increment'] = true; - } - - break; - - case 'postgres': - $return_array['column_type'] = $column_type; - - $sql .= " {$column_type} "; - - $return_array['auto_increment'] = false; - if (isset($column_data[2]) && $column_data[2] == 'auto_increment') - { - $default_val = "nextval('{$table_name}_seq')"; - $return_array['auto_increment'] = true; - } - else if (!is_null($column_data[1])) - { - $default_val = "'" . $column_data[1] . "'"; - $return_array['null'] = 'NOT NULL'; - $sql .= 'NOT NULL '; - } - - $return_array['default'] = $default_val; - - $sql .= "DEFAULT {$default_val}"; - - // Unsigned? Then add a CHECK contraint - if (in_array($orig_column_type, $this->unsigned_types)) - { - $return_array['constraint'] = "CHECK ({$column_name} >= 0)"; - $sql .= " CHECK ({$column_name} >= 0)"; - } - - break; - - case 'sqlite': - $return_array['primary_key_set'] = false; - if (isset($column_data[2]) && $column_data[2] == 'auto_increment') - { - $sql .= ' INTEGER PRIMARY KEY'; - $return_array['primary_key_set'] = true; - } - else - { - $sql .= ' ' . $column_type; - } - - $sql .= ' NOT NULL '; - $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}'" : ''; - - break; - } - - $return_array['column_type_sql'] = $sql; - - return $return_array; - } - - /** - * Add new column - */ - function sql_column_add($table_name, $column_name, $column_data, $inline = false) - { - $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data); - $statements = array(); - - switch ($this->sql_layer) - { - case 'firebird': - // Does not support AFTER statement, only POSITION (and there you need the column position) - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD "' . strtoupper($column_name) . '" ' . $column_data['column_type_sql']; - break; - - case 'mssql': - case 'mssqlnative': - // Does not support AFTER, only through temporary table - $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default']; - break; - - case 'mysql_40': - case 'mysql_41': - $after = (!empty($column_data['after'])) ? ' AFTER ' . $column_data['after'] : ''; - $statements[] = 'ALTER TABLE `' . $table_name . '` ADD COLUMN `' . $column_name . '` ' . $column_data['column_type_sql'] . $after; - break; - - case 'oracle': - // Does not support AFTER, only through temporary table - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql']; - break; - - case 'postgres': - // Does not support AFTER, only through temporary table - - if (version_compare($this->db->sql_server_info(true), '8.0', '>=')) - { - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql']; - } - else - { - // old versions cannot add columns with default and null information - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint']; - - if (isset($column_data['null'])) - { - if ($column_data['null'] == 'NOT NULL') - { - $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL'; - } - } - - if (isset($column_data['default'])) - { - $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default']; - } - } - break; - - case 'sqlite': - - if ($inline && $this->return_statements) - { - return $column_name . ' ' . $column_data['column_type_sql']; - } - - if (version_compare(sqlite_libversion(), '3.0') == -1) - { - $sql = "SELECT sql - FROM sqlite_master - WHERE type = 'table' - AND name = '{$table_name}' - ORDER BY type DESC, name;"; - $result = $this->db->sql_query($sql); - - if (!$result) - { - break; - } - - $row = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - - $statements[] = 'begin'; - - // Create a backup table and populate it, destroy the existing one - $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']); - $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; - $statements[] = 'DROP TABLE ' . $table_name; - - preg_match('#\((.*)\)#s', $row['sql'], $matches); - - $new_table_cols = trim($matches[1]); - $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); - $column_list = array(); - - foreach ($old_table_cols as $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if ($entities[0] == 'PRIMARY') - { - continue; - } - $column_list[] = $entities[0]; - } - - $columns = implode(',', $column_list); - - $new_table_cols = $column_name . ' ' . $column_data['column_type_sql'] . ',' . $new_table_cols; - - // create a new table and fill it up. destroy the temp one - $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');'; - $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; - $statements[] = 'DROP TABLE ' . $table_name . '_temp'; - - $statements[] = 'commit'; - } - else - { - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' [' . $column_data['column_type_sql'] . ']'; - } - break; - } - - return $this->_sql_run_sql($statements); - } - - /** - * Drop column - */ - function sql_column_remove($table_name, $column_name, $inline = false) - { - $statements = array(); - - switch ($this->sql_layer) - { - case 'firebird': - $statements[] = 'ALTER TABLE ' . $table_name . ' DROP "' . strtoupper($column_name) . '"'; - break; - - case 'mssql': - case 'mssqlnative': - $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']'; - break; - - case 'mysql_40': - case 'mysql_41': - $statements[] = 'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column_name . '`'; - break; - - case 'oracle': - $statements[] = 'ALTER TABLE ' . $table_name . ' DROP ' . $column_name; - break; - - case 'postgres': - $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"'; - break; - - case 'sqlite': - - if ($inline && $this->return_statements) - { - return $column_name; - } - - if (version_compare(sqlite_libversion(), '3.0') == -1) - { - $sql = "SELECT sql - FROM sqlite_master - WHERE type = 'table' - AND name = '{$table_name}' - ORDER BY type DESC, name;"; - $result = $this->db->sql_query($sql); - - if (!$result) - { - break; - } - - $row = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - - $statements[] = 'begin'; - - // Create a backup table and populate it, destroy the existing one - $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']); - $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; - $statements[] = 'DROP TABLE ' . $table_name; - - preg_match('#\((.*)\)#s', $row['sql'], $matches); - - $new_table_cols = trim($matches[1]); - $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); - $column_list = array(); - - foreach ($old_table_cols as $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if ($entities[0] == 'PRIMARY' || $entities[0] === $column_name) - { - continue; - } - $column_list[] = $entities[0]; - } - - $columns = implode(',', $column_list); - - $new_table_cols = $new_table_cols = preg_replace('/' . $column_name . '[^,]+(?:,|$)/m', '', $new_table_cols); - - // create a new table and fill it up. destroy the temp one - $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');'; - $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; - $statements[] = 'DROP TABLE ' . $table_name . '_temp'; - - $statements[] = 'commit'; - } - else - { - $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN ' . $column_name; - } - break; - } - - return $this->_sql_run_sql($statements); - } - - /** - * Drop Index - */ - function sql_index_drop($table_name, $index_name) - { - $statements = array(); - - switch ($this->sql_layer) - { - case 'mssql': - case 'mssqlnative': - $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name; - break; - - case 'mysql_40': - case 'mysql_41': - $statements[] = 'DROP INDEX ' . $index_name . ' ON ' . $table_name; - break; - - case 'firebird': - case 'oracle': - case 'postgres': - case 'sqlite': - $statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name; - break; - } - - return $this->_sql_run_sql($statements); - } - - /** - * Add primary key - */ - function sql_create_primary_key($table_name, $column, $inline = false) - { - $statements = array(); - - switch ($this->sql_layer) - { - case 'firebird': - case 'postgres': - case 'mysql_40': - case 'mysql_41': - $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')'; - break; - - case 'mssql': - case 'mssqlnative': - $sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD "; - $sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED ("; - $sql .= '[' . implode("],\n\t\t[", $column) . ']'; - $sql .= ') ON [PRIMARY]'; - - $statements[] = $sql; - break; - - case 'oracle': - $statements[] = 'ALTER TABLE ' . $table_name . 'add CONSTRAINT pk_' . $table_name . ' PRIMARY KEY (' . implode(', ', $column) . ')'; - break; - - case 'sqlite': - - if ($inline && $this->return_statements) - { - return $column; - } - - $sql = "SELECT sql - FROM sqlite_master - WHERE type = 'table' - AND name = '{$table_name}' - ORDER BY type DESC, name;"; - $result = $this->db->sql_query($sql); - - if (!$result) - { - break; - } - - $row = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - - $statements[] = 'begin'; - - // Create a backup table and populate it, destroy the existing one - $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']); - $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; - $statements[] = 'DROP TABLE ' . $table_name; - - preg_match('#\((.*)\)#s', $row['sql'], $matches); - - $new_table_cols = trim($matches[1]); - $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); - $column_list = array(); - - foreach ($old_table_cols as $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - if ($entities[0] == 'PRIMARY') - { - continue; - } - $column_list[] = $entities[0]; - } - - $columns = implode(',', $column_list); - - // create a new table and fill it up. destroy the temp one - $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ', PRIMARY KEY (' . implode(', ', $column) . '));'; - $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; - $statements[] = 'DROP TABLE ' . $table_name . '_temp'; - - $statements[] = 'commit'; - break; - } - - return $this->_sql_run_sql($statements); - } - - /** - * Add unique index - */ - function sql_create_unique_index($table_name, $index_name, $column) - { - $statements = array(); - - $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) - if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) - { - $max_length = $table_prefix + 24; - trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); - } - - switch ($this->sql_layer) - { - case 'firebird': - case 'postgres': - case 'oracle': - case 'sqlite': - $statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; - break; - - case 'mysql_40': - case 'mysql_41': - $statements[] = 'CREATE UNIQUE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; - break; - - case 'mssql': - case 'mssqlnative': - $statements[] = 'CREATE UNIQUE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]'; - break; - } - - return $this->_sql_run_sql($statements); - } - - /** - * Add index - */ - function sql_create_index($table_name, $index_name, $column) - { - $statements = array(); - - $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) - if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) - { - $max_length = $table_prefix + 24; - trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); - } - - // remove index length unless MySQL4 - if ('mysql_40' != $this->sql_layer) - { - $column = preg_replace('#:.*$#', '', $column); - } - - switch ($this->sql_layer) - { - case 'firebird': - case 'postgres': - case 'oracle': - case 'sqlite': - $statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; - break; - - case 'mysql_40': - // add index size to definition as required by MySQL4 - foreach ($column as $i => $col) - { - if (false !== strpos($col, ':')) - { - list($col, $index_size) = explode(':', $col); - $column[$i] = "$col($index_size)"; - } - } - // no break - case 'mysql_41': - $statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; - break; - - case 'mssql': - case 'mssqlnative': - $statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ') ON [PRIMARY]'; - break; - } - - return $this->_sql_run_sql($statements); - } - - /** - * Change column type (not name!) - */ - function sql_column_change($table_name, $column_name, $column_data, $inline = false) - { - $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data); - $statements = array(); - - switch ($this->sql_layer) - { - case 'firebird': - // Change type... - if (!empty($column_data['column_type_sql_default'])) - { - $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN "' . strtoupper($column_name) . '" TYPE ' . ' ' . $column_data['column_type_sql_type']; - $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN "' . strtoupper($column_name) . '" SET DEFAULT ' . ' ' . $column_data['column_type_sql_default']; - } - else - { - // TODO: try to change pkey without removing trigger, generator or constraints. ATM this query may fail. - $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN "' . strtoupper($column_name) . '" TYPE ' . ' ' . $column_data['column_type_sql_type']; - } - break; - - case 'mssql': - case 'mssqlnative': - $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql']; - - if (!empty($column_data['default'])) - { - // Using TRANSACT-SQL for this statement because we do not want to have colliding data if statements are executed at a later stage - $statements[] = "DECLARE @drop_default_name VARCHAR(100), @cmd VARCHAR(1000) - SET @drop_default_name = - (SELECT so.name FROM sysobjects so - JOIN sysconstraints sc ON so.id = sc.constid - WHERE object_name(so.parent_obj) = '{$table_name}' - AND so.xtype = 'D' - AND sc.colid = (SELECT colid FROM syscolumns - WHERE id = object_id('{$table_name}') - AND name = '{$column_name}')) - IF @drop_default_name <> '' - BEGIN - SET @cmd = 'ALTER TABLE [{$table_name}] DROP CONSTRAINT [' + @drop_default_name + ']' - EXEC(@cmd) - END - SET @cmd = 'ALTER TABLE [{$table_name}] ADD CONSTRAINT [DF_{$table_name}_{$column_name}_1] {$column_data['default']} FOR [{$column_name}]' - EXEC(@cmd)"; - } - break; - - case 'mysql_40': - case 'mysql_41': - $statements[] = 'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_name . '` `' . $column_name . '` ' . $column_data['column_type_sql']; - break; - - case 'oracle': - $statements[] = 'ALTER TABLE ' . $table_name . ' MODIFY ' . $column_name . ' ' . $column_data['column_type_sql']; - break; - - case 'postgres': - $sql = 'ALTER TABLE ' . $table_name . ' '; - - $sql_array = array(); - $sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type']; - - if (isset($column_data['null'])) - { - if ($column_data['null'] == 'NOT NULL') - { - $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL'; - } - else if ($column_data['null'] == 'NULL') - { - $sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL'; - } - } - - if (isset($column_data['default'])) - { - $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default']; - } - - // we don't want to double up on constraints if we change different number data types - if (isset($column_data['constraint'])) - { - $constraint_sql = "SELECT consrc as constraint_data - FROM pg_constraint, pg_class bc - WHERE conrelid = bc.oid - AND bc.relname = '{$table_name}' - AND NOT EXISTS ( - SELECT * - FROM pg_constraint as c, pg_inherits as i - WHERE i.inhrelid = pg_constraint.conrelid - AND c.conname = pg_constraint.conname - AND c.consrc = pg_constraint.consrc - AND c.conrelid = i.inhparent - )"; - - $constraint_exists = false; - - $result = $this->db->sql_query($constraint_sql); - while ($row = $this->db->sql_fetchrow($result)) - { - if (trim($row['constraint_data']) == trim($column_data['constraint'])) - { - $constraint_exists = true; - break; - } - } - $this->db->sql_freeresult($result); - - if (!$constraint_exists) - { - $sql_array[] = 'ADD ' . $column_data['constraint']; - } - } - - $sql .= implode(', ', $sql_array); - - $statements[] = $sql; - break; - - case 'sqlite': - - if ($inline && $this->return_statements) - { - return $column_name . ' ' . $column_data['column_type_sql']; - } - - $sql = "SELECT sql - FROM sqlite_master - WHERE type = 'table' - AND name = '{$table_name}' - ORDER BY type DESC, name;"; - $result = $this->db->sql_query($sql); - - if (!$result) - { - break; - } - - $row = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - - $statements[] = 'begin'; - - // Create a temp table and populate it, destroy the existing one - $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']); - $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; - $statements[] = 'DROP TABLE ' . $table_name; - - preg_match('#\((.*)\)#s', $row['sql'], $matches); - - $new_table_cols = trim($matches[1]); - $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); - $column_list = array(); - - foreach ($old_table_cols as $key => $declaration) - { - $entities = preg_split('#\s+#', trim($declaration)); - $column_list[] = $entities[0]; - if ($entities[0] == $column_name) - { - $old_table_cols[$key] = $column_name . ' ' . $column_data['column_type_sql']; - } - } - - $columns = implode(',', $column_list); - - // create a new table and fill it up. destroy the temp one - $statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $old_table_cols) . ');'; - $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; - $statements[] = 'DROP TABLE ' . $table_name . '_temp'; - - $statements[] = 'commit'; - - break; - } - - return $this->_sql_run_sql($statements); + // No changes from 3.0.10-RC3 to 3.0.10 + case '3.0.10-RC3': + break; } } diff --git a/phpBB/install/install_convert.php b/phpBB/install/install_convert.php index 814b50cf68..62efc3e46b 100644 --- a/phpBB/install/install_convert.php +++ b/phpBB/install/install_convert.php @@ -1716,19 +1716,16 @@ class install_convert extends module fix_empty_primary_groups(); - if (!isset($config['board_startdate'])) - { - $sql = 'SELECT MIN(user_regdate) AS board_startdate - FROM ' . USERS_TABLE; - $result = $db->sql_query($sql); - $row = $db->sql_fetchrow($result); - $db->sql_freeresult($result); + $sql = 'SELECT MIN(user_regdate) AS board_startdate + FROM ' . USERS_TABLE; + $result = $db->sql_query($sql); + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); - if (($row['board_startdate'] < $config['board_startdate'] && $row['board_startdate'] > 0) || !isset($config['board_startdate'])) - { - set_config('board_startdate', $row['board_startdate']); - $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_regdate = ' . $row['board_startdate'] . ' WHERE user_id = ' . ANONYMOUS); - } + if (!isset($config['board_startdate']) || ($row['board_startdate'] < $config['board_startdate'] && $row['board_startdate'] > 0)) + { + set_config('board_startdate', $row['board_startdate']); + $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_regdate = ' . $row['board_startdate'] . ' WHERE user_id = ' . ANONYMOUS); } update_dynamic_config(); diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index 9fe0c8aed5..8b073df44c 100644 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -546,6 +546,11 @@ class install_install extends module $error[] = $lang['INST_ERR_NO_DB']; $connect_test = false; } + else if (!preg_match(get_preg_expression('table_prefix'), $data['table_prefix'])) + { + $error[] = $lang['INST_ERR_DB_INVALID_PREFIX']; + $connect_test = false; + } else { $connect_test = connect_check_db(true, $error, $available_dbms[$data['dbms']], $data['table_prefix'], $data['dbhost'], $data['dbuser'], htmlspecialchars_decode($data['dbpasswd']), $data['dbname'], $data['dbport']); @@ -1940,10 +1945,7 @@ class install_install extends module $messenger->to($data['board_email1'], $data['admin_name']); - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'USERNAME' => htmlspecialchars_decode($data['admin_name']), @@ -2032,7 +2034,7 @@ class install_install extends module 'dbname' => array('lang' => 'DB_NAME', 'type' => 'text:25:100', 'explain' => false), 'dbuser' => array('lang' => 'DB_USERNAME', 'type' => 'text:25:100', 'explain' => false), 'dbpasswd' => array('lang' => 'DB_PASSWORD', 'type' => 'password:25:100', 'explain' => false), - 'table_prefix' => array('lang' => 'TABLE_PREFIX', 'type' => 'text:25:100', 'explain' => false), + 'table_prefix' => array('lang' => 'TABLE_PREFIX', 'type' => 'text:25:100', 'explain' => true), ); var $admin_config_options = array( 'legend1' => 'ADMIN_CONFIG', diff --git a/phpBB/install/schemas/firebird_schema.sql b/phpBB/install/schemas/firebird_schema.sql index 40041b13cb..73052f0a22 100644 --- a/phpBB/install/schemas/firebird_schema.sql +++ b/phpBB/install/schemas/firebird_schema.sql @@ -1,7 +1,8 @@ +# DO NOT EDIT THIS FILE, IT IS GENERATED # -# $Id: $ -# - +# To change the contents of this file, edit +# phpBB/develop/create_schema_files.php and +# run it. # Table: 'phpbb_attachments' CREATE TABLE phpbb_attachments ( diff --git a/phpBB/install/schemas/mssql_schema.sql b/phpBB/install/schemas/mssql_schema.sql index c4fc2d4eec..8ed3ba7e12 100644 --- a/phpBB/install/schemas/mssql_schema.sql +++ b/phpBB/install/schemas/mssql_schema.sql @@ -1,8 +1,10 @@ /* - - $Id: $ - -*/ + * DO NOT EDIT THIS FILE, IT IS GENERATED + * + * To change the contents of this file, edit + * phpBB/develop/create_schema_files.php and + * run it. + */ /* Table: 'phpbb_attachments' diff --git a/phpBB/install/schemas/mysql_40_schema.sql b/phpBB/install/schemas/mysql_40_schema.sql index 06d32166f3..42b7291d9d 100644 --- a/phpBB/install/schemas/mysql_40_schema.sql +++ b/phpBB/install/schemas/mysql_40_schema.sql @@ -1,7 +1,8 @@ +# DO NOT EDIT THIS FILE, IT IS GENERATED # -# $Id: $ -# - +# To change the contents of this file, edit +# phpBB/develop/create_schema_files.php and +# run it. # Table: 'phpbb_attachments' CREATE TABLE phpbb_attachments ( attach_id mediumint(8) UNSIGNED NOT NULL auto_increment, diff --git a/phpBB/install/schemas/mysql_41_schema.sql b/phpBB/install/schemas/mysql_41_schema.sql index 1db2790ec7..7a6d0ae188 100644 --- a/phpBB/install/schemas/mysql_41_schema.sql +++ b/phpBB/install/schemas/mysql_41_schema.sql @@ -1,7 +1,8 @@ +# DO NOT EDIT THIS FILE, IT IS GENERATED # -# $Id: $ -# - +# To change the contents of this file, edit +# phpBB/develop/create_schema_files.php and +# run it. # Table: 'phpbb_attachments' CREATE TABLE phpbb_attachments ( attach_id mediumint(8) UNSIGNED NOT NULL auto_increment, diff --git a/phpBB/install/schemas/oracle_schema.sql b/phpBB/install/schemas/oracle_schema.sql index 783261e365..6e7ec31efc 100644 --- a/phpBB/install/schemas/oracle_schema.sql +++ b/phpBB/install/schemas/oracle_schema.sql @@ -1,8 +1,10 @@ /* - - $Id: $ - -*/ + * DO NOT EDIT THIS FILE, IT IS GENERATED + * + * To change the contents of this file, edit + * phpBB/develop/create_schema_files.php and + * run it. + */ /* This first section is optional, however its probably the best method diff --git a/phpBB/install/schemas/postgres_schema.sql b/phpBB/install/schemas/postgres_schema.sql index cf655cb1c7..38f167bc7b 100644 --- a/phpBB/install/schemas/postgres_schema.sql +++ b/phpBB/install/schemas/postgres_schema.sql @@ -1,8 +1,10 @@ /* - - $Id: $ - -*/ + * DO NOT EDIT THIS FILE, IT IS GENERATED + * + * To change the contents of this file, edit + * phpBB/develop/create_schema_files.php and + * run it. + */ BEGIN; diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 0153bd8ec2..d7433d52fd 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -94,6 +94,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('delete_time', '0') INSERT INTO phpbb_config (config_name, config_value) VALUES ('email_check_mx', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('email_enable', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('email_function_name', 'mail'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('email_max_chunk_size', '50'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('email_package_size', '20'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('enable_confirm', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('enable_pm_icons', '1'); @@ -245,7 +246,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('topics_per_page', INSERT INTO phpbb_config (config_name, config_value) VALUES ('tpl_allow_php', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_icons_path', 'images/upload_icons'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('upload_path', 'files'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.0.9'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('version', '3.0.10'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_expire_days', '90'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('warnings_gc', '14400'); diff --git a/phpBB/install/schemas/sqlite_schema.sql b/phpBB/install/schemas/sqlite_schema.sql index dae1eb839c..c0574244ca 100644 --- a/phpBB/install/schemas/sqlite_schema.sql +++ b/phpBB/install/schemas/sqlite_schema.sql @@ -1,7 +1,8 @@ +# DO NOT EDIT THIS FILE, IT IS GENERATED # -# $Id: $ -# - +# To change the contents of this file, edit +# phpBB/develop/create_schema_files.php and +# run it. BEGIN TRANSACTION; # Table: 'phpbb_attachments' diff --git a/phpBB/language/en/acp/ban.php b/phpBB/language/en/acp/ban.php index 099834d1e4..7b2ef59a7a 100644 --- a/phpBB/language/en/acp/ban.php +++ b/phpBB/language/en/acp/ban.php @@ -68,6 +68,9 @@ $lang = array_merge($lang, array( 'LENGTH_BAN_INVALID' => 'The date has to be formatted YYYY-MM-DD.', + 'OPTIONS_BANNED' => 'Banned', + 'OPTIONS_EXCLUDED' => 'Excluded', + 'PERMANENT' => 'Permanent', 'UNTIL' => 'Until', diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index 0254bff992..6e6d4302cd 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -53,7 +53,7 @@ $lang = array_merge($lang, array( 'SYSTEM_TIMEZONE' => 'Guest timezone', 'SYSTEM_TIMEZONE_EXPLAIN' => 'Timezone to use for displaying times to users who are not logged in (guests, bots). Logged in users set their timezone during registration and can change it in their user control panel.', 'WARNINGS_EXPIRE' => 'Warning duration', - 'WARNINGS_EXPIRE_EXPLAIN' => 'Number of days that will elapse before the warning will automatically expire from a user’s record.', + 'WARNINGS_EXPIRE_EXPLAIN' => 'Number of days that will elapse before the warning will automatically expire from a user’s record. Set this value to 0 to make warnings permanent.', )); // Board Features @@ -174,7 +174,7 @@ $lang = array_merge($lang, array( 'MAX_POST_URLS' => 'Maximum links per post', 'MAX_POST_URLS_EXPLAIN' => 'Maximum number of URLs in a post. Set to 0 for unlimited links.', 'MIN_CHAR_LIMIT' => 'Minimum characters per post/message', - 'MIN_CHAR_LIMIT_EXPLAIN' => 'The minimum number of characters the user need to enter within a post/private message.', + 'MIN_CHAR_LIMIT_EXPLAIN' => 'The minimum number of characters the user need to enter within a post/private message. The minimum for this setting is 1.', 'POSTING' => 'Posting', 'POSTS_PER_PAGE' => 'Posts per page', 'QUOTE_DEPTH_LIMIT' => 'Maximum nesting depth for quotes', diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index 8c71e936b3..ef8d6f1cb3 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -327,6 +327,16 @@ $lang = array_merge($lang, array( 'DATABASE_SERVER_INFO' => 'Database server', 'DATABASE_SIZE' => 'Database size', + // Enviroment configuration checks, mbstring related + 'ERROR_MBSTRING_FUNC_OVERLOAD' => 'Function overloading is improperly configured', + 'ERROR_MBSTRING_FUNC_OVERLOAD_EXPLAIN' => 'mbstring.func_overload must be set to either 0 or 4. You can check the current value on the PHP information page.', + 'ERROR_MBSTRING_ENCODING_TRANSLATION' => 'Transparent character encoding is improperly configured', + 'ERROR_MBSTRING_ENCODING_TRANSLATION_EXPLAIN' => 'mbstring.encoding_translation must be set to 0. You can check the current value on the PHP information page.', + 'ERROR_MBSTRING_HTTP_INPUT' => 'HTTP input character conversion is improperly configured', + 'ERROR_MBSTRING_HTTP_INPUT_EXPLAIN' => 'mbstring.http_input must be set to pass. You can check the current value on the PHP information page.', + 'ERROR_MBSTRING_HTTP_OUTPUT' => 'HTTP output character conversion is improperly configured', + 'ERROR_MBSTRING_HTTP_OUTPUT_EXPLAIN' => 'mbstring.http_output must be set to pass. You can check the current value on the PHP information page.', + 'FILES_PER_DAY' => 'Attachments per day', 'FORUM_STATS' => 'Board statistics', diff --git a/phpBB/language/en/acp/database.php b/phpBB/language/en/acp/database.php index ae8f76d6b7..9c8ecbf13a 100644 --- a/phpBB/language/en/acp/database.php +++ b/phpBB/language/en/acp/database.php @@ -59,6 +59,7 @@ $lang = array_merge($lang, array( 'RESTORE_FAILURE' => 'The backup file may be corrupt.', 'RESTORE_OPTIONS' => 'Restore options', + 'RESTORE_SELECTED_BACKUP' => 'Are you sure you want to restore the selected backup?', 'RESTORE_SUCCESS' => 'The database has been successfully restored.

Your board should be back to the state it was when the backup was made.', 'SELECT_ALL' => 'Select all', diff --git a/phpBB/language/en/acp/forums.php b/phpBB/language/en/acp/forums.php index eab027f295..b99a0724be 100644 --- a/phpBB/language/en/acp/forums.php +++ b/phpBB/language/en/acp/forums.php @@ -72,7 +72,7 @@ $lang = array_merge($lang, array( 'ENABLE_TOPIC_ICONS' => 'Enable topic icons', 'FORUM_ADMIN' => 'Forum administration', - 'FORUM_ADMIN_EXPLAIN' => 'In phpBB3 there are no categories, everything is forum based. Each forum can have an unlimited number of sub-forums and you can determine whether each may be posted to or not (i.e. whether it acts like an old category). Here you can add, edit, delete, lock, unlock individual forums as well as set certain additional controls. If your posts and topics have got out of sync you can also resynchronise a forum. You need to copy or set appropriate permissions for newly created forums to have them displayed.', + 'FORUM_ADMIN_EXPLAIN' => 'In phpBB3 everything is forum based. A category is just a special type of forum. Each forum can have an unlimited number of sub-forums and you can determine whether each may be posted to or not (i.e. whether it acts like an old category). Here you can add, edit, delete, lock, unlock individual forums as well as set certain additional controls. If your posts and topics have got out of sync you can also resynchronise a forum. You need to copy or set appropriate permissions for newly created forums to have them displayed.', 'FORUM_AUTO_PRUNE' => 'Enable auto-pruning', 'FORUM_AUTO_PRUNE_EXPLAIN' => 'Prunes the forum of topics, set the frequency/age parameters below.', 'FORUM_CREATED' => 'Forum created successfully.', diff --git a/phpBB/language/en/acp/language.php b/phpBB/language/en/acp/language.php index 4d11309ebb..dde4e3d722 100644 --- a/phpBB/language/en/acp/language.php +++ b/phpBB/language/en/acp/language.php @@ -59,6 +59,7 @@ $lang = array_merge($lang, array( 'LANGUAGE_PACK_DELETED' => 'The language pack %s has been removed successfully. All users using this language have been reset to the boards default language.', 'LANGUAGE_PACK_DETAILS' => 'Language pack details', 'LANGUAGE_PACK_INSTALLED' => 'The language pack %s has been successfully installed.', + 'LANGUAGE_PACK_CPF_UPDATE' => 'The custom profile fields’ language strings were copied from the default language. Please change them if necessary.', 'LANGUAGE_PACK_ISO' => 'ISO', 'LANGUAGE_PACK_LOCALNAME' => 'Local name', 'LANGUAGE_PACK_NAME' => 'Name', diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php index 078a280223..005640b7dd 100644 --- a/phpBB/language/en/common.php +++ b/phpBB/language/en/common.php @@ -165,6 +165,7 @@ $lang = array_merge($lang, array( 'EMPTY_MESSAGE_SUBJECT' => 'You must specify a subject when composing a new message.', 'ENABLED' => 'Enabled', 'ENCLOSURE' => 'Enclosure', + 'ENTER_USERNAME' => 'Enter username', 'ERR_CHANGING_DIRECTORY' => 'Unable to change directory.', 'ERR_CONNECTING_SERVER' => 'Error connecting to the server.', 'ERR_JAB_AUTH' => 'Could not authorise on Jabber server.', @@ -200,6 +201,7 @@ $lang = array_merge($lang, array( 'FORUM_RULES_LINK' => 'Please click here to view the forum rules', 'FROM' => 'from', 'FSOCK_DISABLED' => 'The operation could not be completed because the fsockopen function has been disabled or the server being queried could not be found.', + 'FSOCK_TIMEOUT' => 'A timeout occurred while reading from the network stream.', 'FTP_FSOCK_HOST' => 'FTP host', 'FTP_FSOCK_HOST_EXPLAIN' => 'FTP server used to connect your site.', @@ -652,6 +654,10 @@ $lang = array_merge($lang, array( 'UNREAD_PMS' => '%d unread messages', 'UNREAD_POST' => 'Unread post', 'UNREAD_POSTS' => 'Unread posts', + 'UNWATCH_FORUM_CONFIRM' => 'Are you sure you wish to unsubscribe from this forum?', + 'UNWATCH_FORUM_DETAILED' => 'Are you sure you wish to unsubscribe from the forum “%s”?', + 'UNWATCH_TOPIC_CONFIRM' => 'Are you sure you wish to unsubscribe from this topic?', + 'UNWATCH_TOPIC_DETAILED' => 'Are you sure you wish to unsubscribe from the topic “%s”?', 'UNWATCHED_FORUMS' => 'You are no longer subscribed to the selected forums.', 'UNWATCHED_TOPICS' => 'You are no longer subscribed to the selected topics.', 'UNWATCHED_FORUMS_TOPICS' => 'You are no longer subscribed to the selected entries.', @@ -700,6 +706,10 @@ $lang = array_merge($lang, array( 'WARNINGS' => 'Warnings', 'WARN_USER' => 'Warn user', + 'WATCH_FORUM_CONFIRM' => 'Are you sure you wish to subscribe to this forum?', + 'WATCH_FORUM_DETAILED' => 'Are you sure you wish to subscribe to the forum “%s”?', + 'WATCH_TOPIC_CONFIRM' => 'Are you sure you wish to subscribe to this topic?', + 'WATCH_TOPIC_DETAILED' => 'Are you sure you wish to subscribe to the topic “%s”?', 'WELCOME_SUBJECT' => 'Welcome to %s forums', 'WEBSITE' => 'Website', 'WHOIS' => 'Whois', diff --git a/phpBB/language/en/email/admin_welcome_activated.txt b/phpBB/language/en/email/admin_welcome_activated.txt index 2397868323..cfdb69bdcb 100644 --- a/phpBB/language/en/email/admin_welcome_activated.txt +++ b/phpBB/language/en/email/admin_welcome_activated.txt @@ -2,7 +2,7 @@ Subject: Account activated Hello {USERNAME}, -Your account on "{SITENAME}" has now been activated, you may login using the username you received in a previous e-mail. +Your account on "{SITENAME}" has been activated by an administrator, you may login now. Your password has been securely stored in our database and cannot be retrieved. In the event that it is forgotten, you will be able to reset it using the email address associated with your account. diff --git a/phpBB/language/en/email/privmsg_notify.txt b/phpBB/language/en/email/privmsg_notify.txt index d363df7973..d3a86cc73c 100644 --- a/phpBB/language/en/email/privmsg_notify.txt +++ b/phpBB/language/en/email/privmsg_notify.txt @@ -8,7 +8,7 @@ You have received a new private message from "{AUTHOR_NAME}" to your account on You can view your new message by clicking on the following link: -{U_INBOX} +{U_VIEW_MESSAGE} You have requested that you be notified on this event, remember that you can always choose not to be notified of new messages by changing the appropriate setting in your profile. diff --git a/phpBB/language/en/help_faq.php b/phpBB/language/en/help_faq.php index 3b7dc02d3f..b915a3da19 100644 --- a/phpBB/language/en/help_faq.php +++ b/phpBB/language/en/help_faq.php @@ -60,7 +60,7 @@ $help = array( ), array( 0 => 'I registered in the past but cannot login any more?!', - 1 => 'Attempt to locate the e-mail sent to you when you first registered, check your username and password and try again. It is possible an administrator has deactivated or deleted your account for some reason. Also, many boards periodically remove users who have not posted for a long time to reduce the size of the database. If this has happened, try registering again and being more involved in discussions.' + 1 => 'It is possible an administrator has deactivated or deleted your account for some reason. Also, many boards periodically remove users who have not posted for a long time to reduce the size of the database. If this has happened, try registering again and being more involved in discussions.' ), array( 0 => 'What is COPPA?', diff --git a/phpBB/language/en/install.php b/phpBB/language/en/install.php index 37147cc6a0..f69ca40613 100644 --- a/phpBB/language/en/install.php +++ b/phpBB/language/en/install.php @@ -151,7 +151,7 @@ $lang = array_merge($lang, array( 'DLL_MYSQL' => 'MySQL', 'DLL_MYSQLI' => 'MySQL with MySQLi Extension', 'DLL_ORACLE' => 'Oracle', - 'DLL_POSTGRES' => 'PostgreSQL 7.x/8.x', + 'DLL_POSTGRES' => 'PostgreSQL', 'DLL_SQLITE' => 'SQLite', 'DLL_XML' => 'XML support [ Jabber ]', 'DLL_ZLIB' => 'zlib compression support [ gz, .tar.gz, .zip ]', @@ -232,6 +232,7 @@ $lang = array_merge($lang, array( 'INST_ERR' => 'Installation error', 'INST_ERR_DB_CONNECT' => 'Could not connect to the database, see error message below.', 'INST_ERR_DB_FORUM_PATH' => 'The database file specified is within your board directory tree. You should put this file in a non web-accessible location.', + 'INST_ERR_DB_INVALID_PREFIX'=> 'The prefix you entered is invalid. It must start with a letter and must only contain letters, numbers and underscores.', 'INST_ERR_DB_NO_ERROR' => 'No error message given.', 'INST_ERR_DB_NO_MYSQLI' => 'The version of MySQL installed on this machine is incompatible with the “MySQL with MySQLi Extension” option you have selected. Please try the “MySQL” option instead.', 'INST_ERR_DB_NO_SQLITE' => 'The version of the SQLite extension you have installed is too old, it must be upgraded to at least 2.8.2.', @@ -352,6 +353,7 @@ $lang = array_merge($lang, array( 'TABLES_MISSING' => 'Could not find these tables
» %s.', 'TABLE_PREFIX' => 'Prefix for tables in database', + 'TABLE_PREFIX_EXPLAIN' => 'The prefix must start with a letter and must only contain letters, numbers and underscores.', 'TABLE_PREFIX_SAME' => 'The table prefix needs to be the one used by the software you are converting from.
» Specified table prefix was %s.', 'TESTS_PASSED' => 'Tests passed', 'TESTS_FAILED' => 'Tests failed', @@ -494,7 +496,7 @@ $lang = array_merge($lang, array( 'SHOW_DIFF_NEW' => 'Show file contents', 'SHOW_DIFF_NEW_CONFLICT' => 'Show differences', 'SHOW_DIFF_NOT_MODIFIED' => 'Show differences', - 'SOME_QUERIES_FAILED' => 'Some queries failed, the statements and errors are listing below.', + 'SOME_QUERIES_FAILED' => 'Some queries failed, the statements and errors are listed below.', 'SQL' => 'SQL', 'SQL_FAILURE_EXPLAIN' => 'This is probably nothing to worry about, update will continue. Should this fail to complete you may need to seek help at our support forums. See README for details on how to obtain advice.', 'STAGE_FILE_CHECK' => 'Check files', diff --git a/phpBB/language/en/posting.php b/phpBB/language/en/posting.php index 8016a233fd..f8d265dddd 100644 --- a/phpBB/language/en/posting.php +++ b/phpBB/language/en/posting.php @@ -48,7 +48,7 @@ $lang = array_merge($lang, array( 'BBCODE_A_HELP' => 'Inline uploaded attachment: [attachment=]filename.ext[/attachment]', 'BBCODE_B_HELP' => 'Bold text: [b]text[/b]', 'BBCODE_C_HELP' => 'Code display: [code]code[/code]', - 'BBCODE_E_HELP' => 'List: Add list element', + 'BBCODE_D_HELP' => 'Flash: [flash=width,height]http://url[/flash]', 'BBCODE_F_HELP' => 'Font size: [size=85]small text[/size]', 'BBCODE_IS_OFF' => '%sBBCode%s is OFF', 'BBCODE_IS_ON' => '%sBBCode%s is ON', @@ -61,7 +61,7 @@ $lang = array_merge($lang, array( 'BBCODE_S_HELP' => 'Font colour: [color=red]text[/color] Tip: you can also use color=#FF0000', 'BBCODE_U_HELP' => 'Underline text: [u]text[/u]', 'BBCODE_W_HELP' => 'Insert URL: [url]http://url[/url] or [url=http://url]URL text[/url]', - 'BBCODE_D_HELP' => 'Flash: [flash=width,height]http://url[/flash]', + 'BBCODE_Y_HELP' => 'List: Add list element', 'BUMP_ERROR' => 'You cannot bump this topic so soon after the last post.', 'CANNOT_DELETE_REPLIED' => 'Sorry but you may only delete posts which have not been replied to.', diff --git a/phpBB/language/en/ucp.php b/phpBB/language/en/ucp.php index c1e3c06c43..3ebc863447 100644 --- a/phpBB/language/en/ucp.php +++ b/phpBB/language/en/ucp.php @@ -138,6 +138,7 @@ $lang = array_merge($lang, array( 'CURRENT_IMAGE' => 'Current image', 'CURRENT_PASSWORD' => 'Current password', 'CURRENT_PASSWORD_EXPLAIN' => 'You must confirm your current password if you wish to change it, alter your e-mail address or username.', + 'CUR_PASSWORD_EMPTY' => 'You did not enter your current password.', 'CUR_PASSWORD_ERROR' => 'The current password you entered is incorrect.', 'CUSTOM_DATEFORMAT' => 'Custom…', @@ -268,9 +269,11 @@ $lang = array_merge($lang, array( 'MOVE_TO_FOLDER' => 'Move to folder', 'MOVE_UP' => 'Move up', + 'NEW_EMAIL_CONFIRM_EMPTY' => 'You did not enter a confirm e-mail address.', 'NEW_EMAIL_ERROR' => 'The e-mail addresses you entered do not match.', 'NEW_FOLDER_NAME' => 'New folder name', 'NEW_PASSWORD' => 'New password', + 'NEW_PASSWORD_CONFIRM_EMPTY' => 'You did not enter a confirm password.', 'NEW_PASSWORD_ERROR' => 'The passwords you entered do not match.', 'NOTIFY_METHOD' => 'Notification method', 'NOTIFY_METHOD_BOTH' => 'Both', diff --git a/phpBB/memberlist.php b/phpBB/memberlist.php index 589877305f..b3c0bae16a 100644 --- a/phpBB/memberlist.php +++ b/phpBB/memberlist.php @@ -899,10 +899,7 @@ switch ($mode) $notify_type = NOTIFY_EMAIL; } - $messenger->headers('X-AntiAbuse: Board servername - ' . $config['server_name']); - $messenger->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); - $messenger->headers('X-AntiAbuse: Username - ' . $user->data['username']); - $messenger->headers('X-AntiAbuse: User IP - ' . $user->ip); + $messenger->anti_abuse_headers($config, $user); $messenger->assign_vars(array( 'BOARD_CONTACT' => $config['board_contact'], @@ -1293,13 +1290,6 @@ switch ($mode) $total_users = $config['num_users']; } - $s_char_options = ''; - for ($i = 97; $i < 123; $i++) - { - $s_char_options .= ''; - } - $s_char_options .= ''; - // Build a relevant pagination_url $params = $sort_params = array(); @@ -1329,6 +1319,7 @@ switch ($mode) 'first_char' => array('first_char', ''), ); + $u_first_char_params = array(); foreach ($check_params as $key => $call) { if (!isset($_REQUEST[$key])) @@ -1340,6 +1331,10 @@ switch ($mode) $param = urlencode($key) . '=' . ((is_string($param)) ? urlencode($param) : $param); $params[] = $param; + if ($key != 'first_char') + { + $u_first_char_params[] = $param; + } if ($key != 'sk' && $key != 'sd') { $sort_params[] = $param; @@ -1359,6 +1354,27 @@ switch ($mode) unset($search_params, $sort_params); + $u_first_char_params = implode('&', $u_first_char_params); + $u_first_char_params .= ($u_first_char_params) ? '&' : ''; + + $first_characters = array(); + $first_characters[''] = $user->lang['ALL']; + for ($i = 97; $i < 123; $i++) + { + $first_characters[chr($i)] = chr($i - 32); + } + $first_characters['other'] = $user->lang['OTHER']; + + foreach ($first_characters as $char => $desc) + { + $template->assign_block_vars('first_char', array( + 'DESC' => $desc, + 'VALUE' => $char, + 'S_SELECTED' => ($first_char == $char) ? true : false, + 'U_SORT' => append_sid("{$phpbb_root_path}memberlist.$phpEx", $u_first_char_params . 'first_char=' . $char) . '#memberlist', + )); + } + // Some search user specific data if ($mode == 'searchuser' && ($config['load_search'] || $auth->acl_get('a_'))) { @@ -1603,7 +1619,6 @@ switch ($mode) 'S_LEADERS_SET' => $leaders_set, 'S_MODE_SELECT' => $s_sort_key, 'S_ORDER_SELECT' => $s_sort_dir, - 'S_CHAR_OPTIONS' => $s_char_options, 'S_MODE_ACTION' => $pagination_url) ); } @@ -1667,7 +1682,7 @@ function show_profile($data, $user_notes_enabled = false, $warn_user_enabled = f if ($bday_year) { - $now = getdate(time() + $user->timezone + $user->dst - date('Z')); + $now = phpbb_gmgetdate(time() + $user->timezone + $user->dst); $diff = $now['mon'] - $bday_month; if ($diff == 0) @@ -1679,7 +1694,7 @@ function show_profile($data, $user_notes_enabled = false, $warn_user_enabled = f $diff = ($diff < 0) ? 1 : 0; } - $age = (int) ($now['year'] - $bday_year - $diff); + $age = max(0, (int) ($now['year'] - $bday_year - $diff)); } } diff --git a/phpBB/posting.php b/phpBB/posting.php index 1bc498efe7..76c8100c78 100644 --- a/phpBB/posting.php +++ b/phpBB/posting.php @@ -895,7 +895,7 @@ if ($submit || $preview || $refresh) $message_parser->parse_poll($poll); - $post_data['poll_options'] = (isset($poll['poll_options'])) ? $poll['poll_options'] : ''; + $post_data['poll_options'] = (isset($poll['poll_options'])) ? $poll['poll_options'] : array(); $post_data['poll_title'] = (isset($poll['poll_title'])) ? $poll['poll_title'] : ''; /* We reset votes, therefore also allow removing options @@ -904,6 +904,24 @@ if ($submit || $preview || $refresh) $message_parser->warn_msg[] = $user->lang['NO_DELETE_POLL_OPTIONS']; }*/ } + else if ($mode == 'edit' && $post_id == $post_data['topic_first_post_id'] && $auth->acl_get('f_poll', $forum_id)) + { + // The user removed all poll options, this is equal to deleting the poll. + $poll = array( + 'poll_title' => '', + 'poll_length' => 0, + 'poll_max_options' => 0, + 'poll_option_text' => '', + 'poll_start' => 0, + 'poll_last_vote' => 0, + 'poll_vote_change' => 0, + 'poll_options' => array(), + ); + + $post_data['poll_options'] = array(); + $post_data['poll_title'] = ''; + $post_data['poll_start'] = $post_data['poll_length'] = $post_data['poll_max_options'] = $post_data['poll_last_vote'] = $post_data['poll_vote_change'] = 0; + } else if (!$auth->acl_get('f_poll', $forum_id) && ($mode == 'edit') && ($post_id == $post_data['topic_first_post_id']) && ($original_poll_data['poll_title'] != '')) { // We have a poll but the editing user is not permitted to create/edit it. @@ -917,7 +935,7 @@ if ($submit || $preview || $refresh) $message_parser->parse_poll($poll); - $post_data['poll_options'] = (isset($poll['poll_options'])) ? $poll['poll_options'] : ''; + $post_data['poll_options'] = (isset($poll['poll_options'])) ? $poll['poll_options'] : array(); $post_data['poll_title'] = (isset($poll['poll_title'])) ? $poll['poll_title'] : ''; } else diff --git a/phpBB/report.php b/phpBB/report.php index ca8c7bbd0e..c1172ec1d5 100644 --- a/phpBB/report.php +++ b/phpBB/report.php @@ -39,11 +39,13 @@ if (!$post_id && (!$pm_id || !$config['allow_pm_report'])) if ($post_id) { $redirect_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&p=$post_id") . "#p$post_id"; + $return_forum_url = append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$forum_id"); $pm_id = 0; } else { $redirect_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=pm&mode=view&p=$pm_id"); + $return_forum_url = ''; $post_id = 0; $forum_id = 0; } @@ -101,6 +103,7 @@ if ($post_id) { $message = $user->lang['ALREADY_REPORTED']; $message .= '

' . sprintf($user->lang['RETURN_TOPIC'], '', ''); + $message .= '

' . sprintf($user->lang['RETURN_FORUM'], '', ''); trigger_error($message); } } @@ -209,6 +212,10 @@ if ($submit && $reason_id) meta_refresh(3, $redirect_url); $message = $lang_success . '

' . sprintf($lang_return, '', ''); + if ($return_forum_url) + { + $message .= '

' . sprintf($user->lang['RETURN_FORUM'], '', ''); + } trigger_error($message); } diff --git a/phpBB/styles/prosilver/imageset/imageset.cfg b/phpBB/styles/prosilver/imageset/imageset.cfg index 72252079d6..5a703d9e47 100644 --- a/phpBB/styles/prosilver/imageset/imageset.cfg +++ b/phpBB/styles/prosilver/imageset/imageset.cfg @@ -19,7 +19,7 @@ # General Information about this style name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.9 +version = 3.0.10 # Images img_site_logo = site_logo.gif*52*139 diff --git a/phpBB/styles/prosilver/style.cfg b/phpBB/styles/prosilver/style.cfg index 83e762f65b..95d8d287e4 100644 --- a/phpBB/styles/prosilver/style.cfg +++ b/phpBB/styles/prosilver/style.cfg @@ -19,4 +19,4 @@ # General Information about this style name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.9 \ No newline at end of file +version = 3.0.10 \ No newline at end of file diff --git a/phpBB/styles/prosilver/template/editor.js b/phpBB/styles/prosilver/template/editor.js index ddc862bb8c..cfdb54f54b 100644 --- a/phpBB/styles/prosilver/template/editor.js +++ b/phpBB/styles/prosilver/template/editor.js @@ -151,8 +151,10 @@ function insert_text(text, spaces, popup) { text = ' ' + text + ' '; } - - if (!isNaN(textarea.selectionStart)) + + // Since IE9, IE also has textarea.selectionStart, but it still needs to be treated the old way. + // Therefore we simply add a !is_ie here until IE fixes the text-selection completely. + if (!isNaN(textarea.selectionStart) && !is_ie) { var sel_start = textarea.selectionStart; var sel_end = textarea.selectionEnd; @@ -216,11 +218,12 @@ function addquote(post_id, username, l_wrote) } // Get text selection - not only the post content :( - if (window.getSelection) + // IE9 must use the document.selection method but has the *.getSelection so we just force no IE + if (window.getSelection && !is_ie) { theSelection = window.getSelection().toString(); } - else if (document.getSelection) + else if (document.getSelection && !is_ie) { theSelection = document.getSelection(); } diff --git a/phpBB/styles/prosilver/template/forum_fn.js b/phpBB/styles/prosilver/template/forum_fn.js index 4a85858df5..240fe7e51d 100644 --- a/phpBB/styles/prosilver/template/forum_fn.js +++ b/phpBB/styles/prosilver/template/forum_fn.js @@ -200,7 +200,7 @@ function selectCode(a) // Get ID of code block var e = a.parentNode.parentNode.getElementsByTagName('CODE')[0]; - // Not IE + // Not IE and IE9+ if (window.getSelection) { var s = window.getSelection(); diff --git a/phpBB/styles/prosilver/template/jumpbox.html b/phpBB/styles/prosilver/template/jumpbox.html index f7b4fca609..1029627561 100644 --- a/phpBB/styles/prosilver/template/jumpbox.html +++ b/phpBB/styles/prosilver/template/jumpbox.html @@ -10,7 +10,7 @@ -
+
diff --git a/phpBB/styles/prosilver/template/mcp_approve.html b/phpBB/styles/prosilver/template/mcp_approve.html index 329205e2a2..185dd49c1b 100644 --- a/phpBB/styles/prosilver/template/mcp_approve.html +++ b/phpBB/styles/prosilver/template/mcp_approve.html @@ -8,7 +8,7 @@

{MESSAGE_TITLE}

-

{ADDITIONAL_MSG}

+

{ADDITIONAL_MSG}

diff --git a/phpBB/styles/prosilver/template/mcp_front.html b/phpBB/styles/prosilver/template/mcp_front.html index 0891948fb6..b71d120ed6 100644 --- a/phpBB/styles/prosilver/template/mcp_front.html +++ b/phpBB/styles/prosilver/template/mcp_front.html @@ -28,7 +28,6 @@
{unapproved.SUBJECT} {unapproved.ATTACH_ICON_IMG}
- {report.PAGINATION} {L_POSTED} {L_POST_BY_AUTHOR} {unapproved.AUTHOR_FULL} » {unapproved.POST_TIME}
diff --git a/phpBB/styles/prosilver/template/mcp_notes_user.html b/phpBB/styles/prosilver/template/mcp_notes_user.html index c7c089ecad..7e92445476 100644 --- a/phpBB/styles/prosilver/template/mcp_notes_user.html +++ b/phpBB/styles/prosilver/template/mcp_notes_user.html @@ -78,7 +78,7 @@ {usernotes.REPORT_AT} {usernotes.ACTION} - + diff --git a/phpBB/styles/prosilver/template/mcp_post.html b/phpBB/styles/prosilver/template/mcp_post.html index 9d4997e576..f8403ffccd 100644 --- a/phpBB/styles/prosilver/template/mcp_post.html +++ b/phpBB/styles/prosilver/template/mcp_post.html @@ -74,6 +74,7 @@

  + {S_FORM_TOKEN}

diff --git a/phpBB/styles/prosilver/template/memberlist_body.html b/phpBB/styles/prosilver/template/memberlist_body.html index 197cbd4a0c..9e6f8c3aab 100644 --- a/phpBB/styles/prosilver/template/memberlist_body.html +++ b/phpBB/styles/prosilver/template/memberlist_body.html @@ -34,34 +34,11 @@
  • {L_FIND_USERNAME}{L_HIDE_MEMBER_SEARCH} - {L_ALL}  - A  - B  - C  - D  - E  - F  - G  - H  - I  - J  - K  - L  - M  - N  - O  - P  - Q  - R  - S  - T  - U  - V  - W  - X  - Y  - Z  - # + + + {first_char.DESC}  + +
  • -
    {L_ACTIVE_IN_FORUM}:
    {ACTIVE_FORUM}
    ({ACTIVE_FORUM_POSTS} / {ACTIVE_FORUM_PCT}) -
    -
    {L_ACTIVE_IN_TOPIC}:
    {ACTIVE_TOPIC}
    ({ACTIVE_TOPIC_POSTS} / {ACTIVE_TOPIC_PCT}) -
    +
    {L_ACTIVE_IN_FORUM}:
    {ACTIVE_FORUM}
    ({ACTIVE_FORUM_POSTS} / {ACTIVE_FORUM_PCT}) -
    +
    {L_ACTIVE_IN_TOPIC}:
    {ACTIVE_TOPIC}
    ({ACTIVE_TOPIC_POSTS} / {ACTIVE_TOPIC_PCT}) -
    diff --git a/phpBB/styles/prosilver/template/message_body.html b/phpBB/styles/prosilver/template/message_body.html index 896f0b826e..3a970769b7 100644 --- a/phpBB/styles/prosilver/template/message_body.html +++ b/phpBB/styles/prosilver/template/message_body.html @@ -1,4 +1,8 @@ - + + + + +
    @@ -8,4 +12,8 @@
    - \ No newline at end of file + + + + + diff --git a/phpBB/styles/prosilver/template/overall_header.html b/phpBB/styles/prosilver/template/overall_header.html index e13c49b59a..a46c161542 100644 --- a/phpBB/styles/prosilver/template/overall_header.html +++ b/phpBB/styles/prosilver/template/overall_header.html @@ -68,7 +68,7 @@ { eval(onload_functions[i]); } - } + }; window.onunload = function() { @@ -76,7 +76,7 @@ { eval(onunload_functions[i]); } - } + }; // ]]> diff --git a/phpBB/styles/prosilver/template/posting_buttons.html b/phpBB/styles/prosilver/template/posting_buttons.html index 19d55d1a4a..78c2a0d9f2 100644 --- a/phpBB/styles/prosilver/template/posting_buttons.html +++ b/phpBB/styles/prosilver/template/posting_buttons.html @@ -25,7 +25,7 @@ a: '{LA_BBCODE_A_HELP}', s: '{LA_BBCODE_S_HELP}', f: '{LA_BBCODE_F_HELP}', - e: '{LA_BBCODE_E_HELP}', + y: '{LA_BBCODE_Y_HELP}', d: '{LA_BBCODE_D_HELP}' ,cb_{custom_tags.BBCODE_ID}: '{custom_tags.A_BBCODE_HELPLINE}' @@ -79,7 +79,7 @@ - + diff --git a/phpBB/styles/prosilver/template/template.cfg b/phpBB/styles/prosilver/template/template.cfg index 42383a022f..d31dcb7356 100644 --- a/phpBB/styles/prosilver/template/template.cfg +++ b/phpBB/styles/prosilver/template/template.cfg @@ -19,7 +19,7 @@ # General Information about this template name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.9 +version = 3.0.10 # Defining a different template bitfield template_bitfield = lNg= diff --git a/phpBB/styles/prosilver/template/ucp_groups_manage.html b/phpBB/styles/prosilver/template/ucp_groups_manage.html index bce31431cb..87b548c23b 100644 --- a/phpBB/styles/prosilver/template/ucp_groups_manage.html +++ b/phpBB/styles/prosilver/template/ucp_groups_manage.html @@ -143,8 +143,15 @@ + + - + + + + + + diff --git a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html index b1a93296bd..ce2a376768 100644 --- a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html +++ b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html @@ -1,120 +1,57 @@ + -{SITENAME} :: {PAGE_TITLE} - - + + + + + + +{META} +{SITENAME} • {PAGE_TITLE} + - -
    {L_GROUPS_NO_MEMBERS}{L_MEMBERS}
    {L_GROUPS_NO_MEMBERS}
    - - - - - - - - - - -
    {SITENAME}
    {L_PRIVATE_MESSAGING}

    {SUBJECT}
    {PAGE_NUMBER}
    + +
    + -
    + \ No newline at end of file diff --git a/phpBB/styles/prosilver/template/viewtopic_body.html b/phpBB/styles/prosilver/template/viewtopic_body.html index 32292f89ef..57de96d853 100644 --- a/phpBB/styles/prosilver/template/viewtopic_body.html +++ b/phpBB/styles/prosilver/template/viewtopic_body.html @@ -4,13 +4,10 @@
    {FORUM_DESC}
    - -

    - - {L_MODERATOR}{L_MODERATORS}: {MODERATORS} - - -

    + +

    + {L_MODERATOR}{L_MODERATORS}: {MODERATORS} +

    diff --git a/phpBB/styles/prosilver/theme/bidi.css b/phpBB/styles/prosilver/theme/bidi.css index f441784d85..d1cff9c8be 100644 --- a/phpBB/styles/prosilver/theme/bidi.css +++ b/phpBB/styles/prosilver/theme/bidi.css @@ -1,4 +1,4 @@ -/* proSilver RTL definitions +/* RTL definitions ---------------------------------------- */ /** @@ -519,7 +519,7 @@ /** * cp.css */ -/* proSilver Control Panel Styles +/* Control Panel Styles ---------------------------------------- */ diff --git a/phpBB/styles/prosilver/theme/buttons.css b/phpBB/styles/prosilver/theme/buttons.css index 6cffdc5930..a9ded9cf98 100644 --- a/phpBB/styles/prosilver/theme/buttons.css +++ b/phpBB/styles/prosilver/theme/buttons.css @@ -1,4 +1,4 @@ -/* proSilver Button Styles +/* Button Styles ---------------------------------------- */ /* Rollover buttons diff --git a/phpBB/styles/prosilver/theme/common.css b/phpBB/styles/prosilver/theme/common.css index a86f723f81..8b5e09297e 100644 --- a/phpBB/styles/prosilver/theme/common.css +++ b/phpBB/styles/prosilver/theme/common.css @@ -1,4 +1,4 @@ -/* General proSilver Markup Styles +/* General Markup Styles ---------------------------------------- */ * { diff --git a/phpBB/styles/prosilver/theme/content.css b/phpBB/styles/prosilver/theme/content.css index 64beb97a37..5f627c8f77 100644 --- a/phpBB/styles/prosilver/theme/content.css +++ b/phpBB/styles/prosilver/theme/content.css @@ -1,4 +1,4 @@ -/* proSilver Content Styles +/* Content Styles ---------------------------------------- */ ul.topiclist { diff --git a/phpBB/styles/prosilver/theme/cp.css b/phpBB/styles/prosilver/theme/cp.css index b574b0ae1f..708bfbaf83 100644 --- a/phpBB/styles/prosilver/theme/cp.css +++ b/phpBB/styles/prosilver/theme/cp.css @@ -1,4 +1,4 @@ -/* proSilver Control Panel Styles +/* Control Panel Styles ---------------------------------------- */ diff --git a/phpBB/styles/prosilver/theme/forms.css b/phpBB/styles/prosilver/theme/forms.css index 803c608bcf..43888733cc 100644 --- a/phpBB/styles/prosilver/theme/forms.css +++ b/phpBB/styles/prosilver/theme/forms.css @@ -1,4 +1,4 @@ -/* proSilver Form Styles +/* Form Styles ---------------------------------------- */ /* General form styles @@ -26,6 +26,7 @@ select { border: 1px solid #666666; padding: 1px; background-color: #FAFAFA; + font-size: 1em; } option { diff --git a/phpBB/styles/prosilver/theme/links.css b/phpBB/styles/prosilver/theme/links.css index 1f6c2af550..05662b9b44 100644 --- a/phpBB/styles/prosilver/theme/links.css +++ b/phpBB/styles/prosilver/theme/links.css @@ -1,4 +1,4 @@ -/* proSilver Link Styles +/* Link Styles ---------------------------------------- */ /* Links adjustment to correctly display an order of rtl/ltr mixed content */ diff --git a/phpBB/styles/prosilver/theme/print.css b/phpBB/styles/prosilver/theme/print.css index 2cfcd4da20..bc3ca80fdc 100644 --- a/phpBB/styles/prosilver/theme/print.css +++ b/phpBB/styles/prosilver/theme/print.css @@ -1,10 +1,5 @@ -/* proSilver Print Style Sheet -------------------------------------------------- - Author: subBlue ( http://www.subBlue.com/ ) - Version: 25 August 2004 - - Copyright 2004 phpBB Group --------------------------------------------------*/ +/* Print Style Sheet +---------------------------------------- */ /* Lots still TODO here! */ diff --git a/phpBB/styles/prosilver/theme/stylesheet.css b/phpBB/styles/prosilver/theme/stylesheet.css index c7db605bbb..4a7356fbaa 100644 --- a/phpBB/styles/prosilver/theme/stylesheet.css +++ b/phpBB/styles/prosilver/theme/stylesheet.css @@ -1,11 +1,9 @@ -/* phpBB 3.0 Style Sheet +/* phpBB3 Style Sheet -------------------------------------------------------------- - Style name: proSilver - Based on style: proSilver (this is the default phpBB 3 style) - Original author: subBlue ( http://www.subBlue.com/ ) - Modified by: - - Copyright 2006 phpBB Group ( http://www.phpbb.com/ ) + Style name: prosilver (the default phpBB 3.0.x style) + Based on style: + Original author: Tom Beddard ( http://www.subblue.com/ ) + Modified by: phpBB Group ( http://www.phpbb.com/ ) -------------------------------------------------------------- */ diff --git a/phpBB/styles/prosilver/theme/theme.cfg b/phpBB/styles/prosilver/theme/theme.cfg index 4270094eab..e8698f7fe4 100644 --- a/phpBB/styles/prosilver/theme/theme.cfg +++ b/phpBB/styles/prosilver/theme/theme.cfg @@ -21,7 +21,7 @@ # General Information about this theme name = prosilver copyright = © phpBB Group, 2007 -version = 3.0.9 +version = 3.0.10 # Some configuration options diff --git a/phpBB/styles/prosilver/theme/tweaks.css b/phpBB/styles/prosilver/theme/tweaks.css index 30fe5fb986..f7322c2cce 100644 --- a/phpBB/styles/prosilver/theme/tweaks.css +++ b/phpBB/styles/prosilver/theme/tweaks.css @@ -1,4 +1,4 @@ -/* proSilver Style Sheet Tweaks +/* Style Sheet Tweaks These style definitions are mainly IE specific tweaks required due to its poor CSS support. diff --git a/phpBB/styles/subsilver2/imageset/imageset.cfg b/phpBB/styles/subsilver2/imageset/imageset.cfg index d8cfabcb02..75a4aad038 100644 --- a/phpBB/styles/subsilver2/imageset/imageset.cfg +++ b/phpBB/styles/subsilver2/imageset/imageset.cfg @@ -19,7 +19,7 @@ # General Information about this style name = subsilver2 copyright = © phpBB Group, 2003 -version = 3.0.9 +version = 3.0.10 # Images img_site_logo = site_logo.gif*94*170 diff --git a/phpBB/styles/subsilver2/style.cfg b/phpBB/styles/subsilver2/style.cfg index 0cdb7204bb..13e44435c6 100644 --- a/phpBB/styles/subsilver2/style.cfg +++ b/phpBB/styles/subsilver2/style.cfg @@ -19,4 +19,4 @@ # General Information about this style name = subsilver2 copyright = © 2005 phpBB Group -version = 3.0.9 +version = 3.0.10 diff --git a/phpBB/styles/subsilver2/template/attachment.html b/phpBB/styles/subsilver2/template/attachment.html index b5b547b2e6..fca620b481 100644 --- a/phpBB/styles/subsilver2/template/attachment.html +++ b/phpBB/styles/subsilver2/template/attachment.html @@ -72,7 +72,7 @@ - + diff --git a/phpBB/styles/subsilver2/template/editor.js b/phpBB/styles/subsilver2/template/editor.js index cd22812bab..7cc5de9034 100644 --- a/phpBB/styles/subsilver2/template/editor.js +++ b/phpBB/styles/subsilver2/template/editor.js @@ -151,8 +151,10 @@ function insert_text(text, spaces, popup) { text = ' ' + text + ' '; } - - if (!isNaN(textarea.selectionStart)) + + // Since IE9, IE also has textarea.selectionStart, but it still needs to be treated the old way. + // Therefore we simply add a !is_ie here until IE fixes the text-selection completely. + if (!isNaN(textarea.selectionStart) && !is_ie) { var sel_start = textarea.selectionStart; var sel_end = textarea.selectionEnd; @@ -218,11 +220,12 @@ function addquote(post_id, username, l_wrote) } // Get text selection - not only the post content :( - if (window.getSelection) + // IE9 must use the document.selection method but has the *.getSelection so we just force no IE + if (window.getSelection && !is_ie) { theSelection = window.getSelection().toString(); } - else if (document.getSelection) + else if (document.getSelection && !is_ie) { theSelection = document.getSelection(); } diff --git a/phpBB/styles/subsilver2/template/memberlist_body.html b/phpBB/styles/subsilver2/template/memberlist_body.html index cb7a7b20c7..800162b7b0 100644 --- a/phpBB/styles/subsilver2/template/memberlist_body.html +++ b/phpBB/styles/subsilver2/template/memberlist_body.html @@ -14,7 +14,12 @@ - + diff --git a/phpBB/styles/subsilver2/template/memberlist_view.html b/phpBB/styles/subsilver2/template/memberlist_view.html index 9ef2b85878..434d795895 100644 --- a/phpBB/styles/subsilver2/template/memberlist_view.html +++ b/phpBB/styles/subsilver2/template/memberlist_view.html @@ -89,11 +89,11 @@ - + - +
    {L_USERNAME_BEGINS_WITH}:  {L_USERNAME_BEGINS_WITH}: +   {L_FIND_USERNAME}
    {L_ACTIVE_IN_FORUM}: {ACTIVE_FORUM}
    [ {ACTIVE_FORUM_POSTS} / {ACTIVE_FORUM_PCT} ]-
    {ACTIVE_FORUM}
    [ {ACTIVE_FORUM_POSTS} / {ACTIVE_FORUM_PCT} ]-
    {L_ACTIVE_IN_TOPIC}: {ACTIVE_TOPIC}
    [ {ACTIVE_TOPIC_POSTS} / {ACTIVE_TOPIC_PCT} ]-
    {ACTIVE_TOPIC}
    [ {ACTIVE_TOPIC_POSTS} / {ACTIVE_TOPIC_PCT} ]-
    @@ -148,10 +148,10 @@ {L_LOCATION}: {LOCATION} - + {L_AGE}: - {AGE} - + {AGE} diff --git a/phpBB/styles/subsilver2/template/posting_buttons.html b/phpBB/styles/subsilver2/template/posting_buttons.html index 92b4bd3e39..a9105b5eec 100644 --- a/phpBB/styles/subsilver2/template/posting_buttons.html +++ b/phpBB/styles/subsilver2/template/posting_buttons.html @@ -22,9 +22,8 @@ a: '{LA_BBCODE_A_HELP}', s: '{LA_BBCODE_S_HELP}', f: '{LA_BBCODE_F_HELP}', - e: '{LA_BBCODE_E_HELP}', + y: '{LA_BBCODE_Y_HELP}', d: '{LA_BBCODE_D_HELP}', - t: '{LA_BBCODE_T_HELP}', tip: '{L_STYLES_TIP}' ,cb_{custom_tags.BBCODE_ID}: '{custom_tags.A_BBCODE_HELPLINE}' @@ -45,7 +44,7 @@ - + diff --git a/phpBB/styles/subsilver2/template/template.cfg b/phpBB/styles/subsilver2/template/template.cfg index 92ccfef35a..4e5c36af99 100644 --- a/phpBB/styles/subsilver2/template/template.cfg +++ b/phpBB/styles/subsilver2/template/template.cfg @@ -19,5 +19,5 @@ # General Information about this template name = subsilver2 copyright = © phpBB Group, 2003 -version = 3.0.9 +version = 3.0.10 diff --git a/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html b/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html index 6753a5bc33..24194e4c26 100644 --- a/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html +++ b/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html @@ -61,7 +61,7 @@ hr.sep { - + diff --git a/phpBB/styles/subsilver2/theme/stylesheet.css b/phpBB/styles/subsilver2/theme/stylesheet.css index c2b6718d87..444104ae3c 100644 --- a/phpBB/styles/subsilver2/theme/stylesheet.css +++ b/phpBB/styles/subsilver2/theme/stylesheet.css @@ -1,14 +1,9 @@ -/* phpBB 3.0 Style Sheet +/* phpBB3 Style Sheet -------------------------------------------------------------- - Style name: subsilver2 - Based on style: subSilver (the default phpBB 2 style) - Original author: subBlue ( http://www.subBlue.com/ ) - Modified by: psoTFX and the phpBB team ( http://www.phpbb.com ) - - This is an alternative style for phpBB3 for those wishing to stay with - the familiar subSilver style of phpBB version 2.x - - Copyright 2006 phpBB Group ( http://www.phpbb.com/ ) + Style name: subsilver2 + Based on style: subSilver (the default phpBB 2.0.x style) + Original author: Tom Beddard ( http://www.subblue.com/ ) + Modified by: phpBB Group ( http://www.phpbb.com/ ) -------------------------------------------------------------- */ diff --git a/phpBB/styles/subsilver2/theme/theme.cfg b/phpBB/styles/subsilver2/theme/theme.cfg index 94e2f6d53d..d7837a3766 100644 --- a/phpBB/styles/subsilver2/theme/theme.cfg +++ b/phpBB/styles/subsilver2/theme/theme.cfg @@ -21,7 +21,7 @@ # General Information about this theme name = subsilver2 copyright = © phpBB Group, 2003 -version = 3.0.9 +version = 3.0.10 # Some configuration options diff --git a/phpBB/ucp.php b/phpBB/ucp.php index 8fa022668b..45caeb12ea 100644 --- a/phpBB/ucp.php +++ b/phpBB/ucp.php @@ -254,6 +254,12 @@ if (!$user->data['is_registered']) redirect(append_sid("{$phpbb_root_path}index.$phpEx")); } + if ($id == 'pm' && $mode == 'view' && isset($_GET['p'])) + { + $redirect_url = append_sid("{$phpbb_root_path}ucp.$phpEx?i=pm&p=" . request_var('p', 0)); + login_box($redirect_url, $user->lang['LOGIN_EXPLAIN_UCP']); + } + login_box('', $user->lang['LOGIN_EXPLAIN_UCP']); } diff --git a/phpBB/viewforum.php b/phpBB/viewforum.php index 61ec27ed79..588f60b589 100644 --- a/phpBB/viewforum.php +++ b/phpBB/viewforum.php @@ -205,10 +205,10 @@ $s_watching_forum = array( 'is_watching' => false, ); -if (($config['email_enable'] || $config['jab_enable']) && $config['allow_forum_notify'] && $forum_data['forum_type'] == FORUM_POST && $auth->acl_get('f_subscribe', $forum_id)) +if (($config['email_enable'] || $config['jab_enable']) && $config['allow_forum_notify'] && $forum_data['forum_type'] == FORUM_POST && ($auth->acl_get('f_subscribe', $forum_id) || $user->data['user_id'] == ANONYMOUS)) { $notify_status = (isset($forum_data['notify_status'])) ? $forum_data['notify_status'] : NULL; - watch_topic_forum('forum', $s_watching_forum, $user->data['user_id'], $forum_id, 0, $notify_status); + watch_topic_forum('forum', $s_watching_forum, $user->data['user_id'], $forum_id, 0, $notify_status, $start, $forum_data['forum_name']); } $s_forum_rules = ''; @@ -273,6 +273,15 @@ if ($_SID) $s_search_hidden_fields['sid'] = $_SID; } +if (!empty($_EXTRA_URL)) +{ + foreach ($_EXTRA_URL as $url_param) + { + $url_param = explode('=', $url_param, 2); + $s_hidden_fields[$url_param[0]] = $url_param[1]; + } +} + $template->assign_vars(array( 'MODERATORS' => (!empty($moderators[$forum_id])) ? implode(', ', $moderators[$forum_id]) : '', @@ -376,6 +385,12 @@ if ($forum_data['forum_type'] == FORUM_POST) while ($row = $db->sql_fetchrow($result)) { + if (!$row['topic_approved'] && !$auth->acl_get('m_approve', $row['forum_id'])) + { + // Do not display announcements that are waiting for approval. + continue; + } + $rowset[$row['topic_id']] = $row; $announcement_list[] = $row['topic_id']; @@ -533,10 +548,13 @@ if ($s_display_active) $topics_count = 1; } +// We need to readd the local announcements to the forums total topic count, otherwise the number is different from the one on the forum list +$total_topic_count = $topics_count + sizeof($announcement_list) - sizeof($global_announce_list); + $template->assign_vars(array( 'PAGINATION' => generate_pagination(append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$forum_id" . ((strlen($u_sort_param)) ? "&$u_sort_param" : '')), $topics_count, $config['topics_per_page'], $start), 'PAGE_NUMBER' => on_page($topics_count, $config['topics_per_page'], $start), - 'TOTAL_TOPICS' => ($s_display_active) ? false : (($topics_count == 1) ? $user->lang['VIEW_FORUM_TOPIC'] : sprintf($user->lang['VIEW_FORUM_TOPICS'], $topics_count))) + 'TOTAL_TOPICS' => ($s_display_active) ? false : (($total_topic_count == 1) ? $user->lang['VIEW_FORUM_TOPIC'] : sprintf($user->lang['VIEW_FORUM_TOPICS'], $total_topic_count))) ); $topic_list = ($store_reverse) ? array_merge($announcement_list, array_reverse($topic_list)) : array_merge($announcement_list, $topic_list); diff --git a/phpBB/viewtopic.php b/phpBB/viewtopic.php index 08e82591de..01cd6a28a8 100644 --- a/phpBB/viewtopic.php +++ b/phpBB/viewtopic.php @@ -196,7 +196,7 @@ if ($db->sql_layer === 'firebird') // The FROM-Order is quite important here, else t.* columns can not be correctly bound. if ($post_id) { - $sql_array['SELECT'] .= ', p.post_approved'; + $sql_array['SELECT'] .= ', p.post_approved, p.post_time, p.post_id'; $sql_array['FROM'][POSTS_TABLE] = 'p'; } @@ -314,12 +314,19 @@ if ($post_id) } else { - $sql = 'SELECT COUNT(p1.post_id) AS prev_posts - FROM ' . POSTS_TABLE . ' p1, ' . POSTS_TABLE . " p2 - WHERE p1.topic_id = {$topic_data['topic_id']} - AND p2.post_id = {$post_id} - " . ((!$auth->acl_get('m_approve', $forum_id)) ? 'AND p1.post_approved = 1' : '') . ' - AND ' . (($sort_dir == 'd') ? 'p1.post_time >= p2.post_time' : 'p1.post_time <= p2.post_time'); + $sql = 'SELECT COUNT(p.post_id) AS prev_posts + FROM ' . POSTS_TABLE . " p + WHERE p.topic_id = {$topic_data['topic_id']} + " . ((!$auth->acl_get('m_approve', $forum_id)) ? 'AND p.post_approved = 1' : ''); + + if ($sort_dir == 'd') + { + $sql .= " AND (p.post_time > {$topic_data['post_time']} OR (p.post_time = {$topic_data['post_time']} AND p.post_id >= {$topic_data['post_id']}))"; + } + else + { + $sql .= " AND (p.post_time < {$topic_data['post_time']} OR (p.post_time = {$topic_data['post_time']} AND p.post_id <= {$topic_data['post_id']}))"; + } $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); @@ -486,9 +493,10 @@ $s_watching_topic = array( 'is_watching' => false, ); -if (($config['email_enable'] || $config['jab_enable']) && $config['allow_topic_notify'] && $user->data['is_registered']) +if (($config['email_enable'] || $config['jab_enable']) && $config['allow_topic_notify']) { - watch_topic_forum('topic', $s_watching_topic, $user->data['user_id'], $forum_id, $topic_id, $topic_data['notify_status'], $start); + $notify_status = (isset($topic_data['notify_status'])) ? $topic_data['notify_status'] : null; + watch_topic_forum('topic', $s_watching_topic, $user->data['user_id'], $forum_id, $topic_id, $notify_status, $start, $topic_data['topic_title']); // Reset forum notification if forum notify is set if ($config['allow_forum_notify'] && $auth->acl_get('f_subscribe', $forum_id)) @@ -594,6 +602,15 @@ if ($_SID) $s_search_hidden_fields['sid'] = $_SID; } +if (!empty($_EXTRA_URL)) +{ + foreach ($_EXTRA_URL as $url_param) + { + $url_param = explode('=', $url_param, 2); + $s_hidden_fields[$url_param[0]] = $url_param[1]; + } +} + // Send vars to template $template->assign_vars(array( 'FORUM_ID' => $forum_id, @@ -995,7 +1012,7 @@ $sql = $db->sql_build_query('SELECT', array( $result = $db->sql_query($sql); -$now = getdate(time() + $user->timezone + $user->dst - date('Z')); +$now = phpbb_gmgetdate(time() + $user->timezone + $user->dst); // Posts are stored in the $rowset array while $attach_list, $user_cache // and the global bbcode_bitfield are built diff --git a/tests/dbal/cross_join_test.php b/tests/dbal/cross_join_test.php new file mode 100644 index 0000000000..7110c7a2ea --- /dev/null +++ b/tests/dbal/cross_join_test.php @@ -0,0 +1,55 @@ +createXMLDataSet(dirname(__FILE__).'/fixtures/massmail_crossjoin.xml'); + } + + public function test_cross_join() + { + $db = $this->new_dbal(); + + // http://tracker.phpbb.com/browse/PHPBB3-10296 + // Test CROSS JOIN with INNER JOIN + // Failed on Postgres, MSSQL and Oracle + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.username', + 'FROM' => array( + 'phpbb_users' => 'u', + 'phpbb_user_group' => 'ug', + ), + 'LEFT_JOIN' => array( + array( + 'FROM' => array( + 'phpbb_banlist' => 'b', + ), + 'ON' => 'u.user_id = b.ban_userid', + ), + ), + 'WHERE' => 'ug.group_id = 1 + AND u.user_id = ug.user_id + AND b.ban_id IS NULL', + 'ORDER_BY' => 'u.username', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array(array('username' => 'mass email')), $db->sql_fetchrowset($result)); + } +} diff --git a/tests/dbal/db_tools_test.php b/tests/dbal/db_tools_test.php index ddea500f83..c0c66b5be7 100644 --- a/tests/dbal/db_tools_test.php +++ b/tests/dbal/db_tools_test.php @@ -234,6 +234,14 @@ class phpbb_dbal_db_tools_test extends phpbb_database_test_case $this->assertEquals($row2, $row_actual); } + public function test_list_columns() + { + $this->assertEquals( + array_keys($this->table_data['COLUMNS']), + array_values($this->tools->sql_list_columns('prefix_table_name')) + ); + } + public function test_column_exists() { $this->assertTrue($this->tools->sql_column_exists('prefix_table_name', 'c_id')); @@ -258,6 +266,13 @@ class phpbb_dbal_db_tools_test extends phpbb_database_test_case $this->assertFalse($this->tools->sql_column_exists('prefix_table_name', 'c_id')); } + public function test_list_tables() + { + $tables = $this->tools->sql_list_tables(); + $this->assertTrue(isset($tables['prefix_table_name'])); + $this->assertFalse(isset($tables['prefix_does_not_exist'])); + } + public function test_table_exists() { $this->assertTrue($this->tools->sql_table_exists('prefix_table_name')); @@ -271,6 +286,77 @@ class phpbb_dbal_db_tools_test extends phpbb_database_test_case 'foo' => array('UINT', 42))) ); + $this->assertTrue($this->tools->sql_table_exists('prefix_test_table')); + $this->tools->sql_table_drop('prefix_test_table'); + + $this->assertFalse($this->tools->sql_table_exists('prefix_test_table')); + } + + public function test_peform_schema_changes_drop_tables() + { + $db_tools = $this->getMock('phpbb_db_tools', array( + 'sql_table_exists', + 'sql_table_drop', + ), array(&$this->db)); + + // pretend all tables exist + $db_tools->expects($this->any())->method('sql_table_exists') + ->will($this->returnValue(true)); + + // drop tables + $db_tools->expects($this->exactly(2))->method('sql_table_drop'); + $db_tools->expects($this->at(1))->method('sql_table_drop') + ->with($this->equalTo('dropped_table_1')); + $db_tools->expects($this->at(3))->method('sql_table_drop') + ->with($this->equalTo('dropped_table_2')); + + $db_tools->perform_schema_changes(array( + 'drop_tables' => array( + 'dropped_table_1', + 'dropped_table_2', + ), + )); + } + + public function test_peform_schema_changes_drop_columns() + { + $db_tools = $this->getMock('phpbb_db_tools', array( + 'sql_column_exists', + 'sql_column_remove', + ), array(&$this->db)); + + // pretend all columns exist + $db_tools->expects($this->any())->method('sql_column_exists') + ->will($this->returnValue(true)); + $db_tools->expects($this->any())->method('sql_column_exists') + ->will($this->returnValue(true)); + + // drop columns + $db_tools->expects($this->exactly(2))->method('sql_column_remove'); + $db_tools->expects($this->at(1))->method('sql_column_remove') + ->with($this->equalTo('existing_table'), $this->equalTo('dropped_column_1')); + $db_tools->expects($this->at(3))->method('sql_column_remove') + ->with($this->equalTo('existing_table'), $this->equalTo('dropped_column_2')); + + $db_tools->perform_schema_changes(array( + 'drop_columns' => array( + 'existing_table' => array( + 'dropped_column_1', + 'dropped_column_2', + ), + ), + )); + } + + public function test_index_exists() + { + $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'i_simple')); + } + + public function test_create_index_against_index_exists() + { + $this->tools->sql_create_index('prefix_table_name', 'fookey', array('c_timestamp', 'c_decimal')); + $this->assertTrue($this->tools->sql_index_exists('prefix_table_name', 'fookey')); } } diff --git a/tests/dbal/fixtures/massmail_crossjoin.xml b/tests/dbal/fixtures/massmail_crossjoin.xml new file mode 100644 index 0000000000..ef0a2b7149 --- /dev/null +++ b/tests/dbal/fixtures/massmail_crossjoin.xml @@ -0,0 +1,59 @@ + + +
    {SITENAME}
    {L_PRIVATE_MESSAGING}
    {SITENAME}
    {L_PRIVATE_MESSAGING}

    + ban_id + ban_userid + + 1 + 2 + +
    + + user_id + username + username_clean + user_permissions + user_sig + user_occ + user_interests + + 1 + mass email + mass email + + + + + + + 2 + banned + banned + + + + + + + 3 + not in group + not in group + + + + + +
    + + user_id + group_id + + 1 + 1 + + + 2 + 1 + +
    + diff --git a/tests/dbal/fixtures/three_users.xml b/tests/dbal/fixtures/three_users.xml index a6789f4a01..a50e3e8634 100644 --- a/tests/dbal/fixtures/three_users.xml +++ b/tests/dbal/fixtures/three_users.xml @@ -3,17 +3,33 @@ user_idusername_clean + user_permissions + user_sig + user_occ + user_interests 1 barfoo + + + + 2 foobar + + + + 3 bertie + + + +
    diff --git a/tests/dbal/select_test.php b/tests/dbal/select_test.php index e0d08d9306..8ddd27465d 100644 --- a/tests/dbal/select_test.php +++ b/tests/dbal/select_test.php @@ -319,7 +319,7 @@ class phpbb_dbal_select_test extends phpbb_database_test_case $db->sql_freeresult($result); } - function test_nested_transactions() + public function test_nested_transactions() { $db = $this->new_dbal(); @@ -341,4 +341,20 @@ class phpbb_dbal_select_test extends phpbb_database_test_case $this->assertEquals('1', $row['user_id']); } + + /** + * fix for PHPBB3-10307 + */ + public function test_sql_fetchrow_returns_false_when_empty() + { + $db = $this->new_dbal(); + + $sql = 'SELECT * FROM (SELECT 1) AS TBL WHERE 1 = 0'; + $result = $db->sql_query($sql); + + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + $this->assertSame(false, $row); + } } diff --git a/tests/error_collector_test.php b/tests/error_collector_test.php new file mode 100644 index 0000000000..e1ac32f5ac --- /dev/null +++ b/tests/error_collector_test.php @@ -0,0 +1,35 @@ +install(); + + // Cause a warning + 1/0; $line = __LINE__; + + $collector->uninstall(); + + list($errno, $msg_text, $errfile, $errline) = $collector->errors[0]; + $error_contents = $collector->format_errors(); + + $this->assertEquals($errno, 2); + + // Unfortunately $error_contents will contain the full path here, + // because the tests directory is outside of phpbb root path. + $this->assertStringStartsWith('Errno 2: Division by zero at ', $error_contents); + $this->assertStringEndsWith(" line $line", $error_contents); + } +} diff --git a/tests/mock/cache.php b/tests/mock/cache.php index 11e525ff79..020574b0bb 100644 --- a/tests/mock/cache.php +++ b/tests/mock/cache.php @@ -41,6 +41,28 @@ class phpbb_mock_cache { return $this->data['_bots']; } + + /** + * Obtain list of word censors. We don't need to parse them here, + * that is tested elsewhere. + */ + public function obtain_word_list() + { + return array( + 'match' => array( + '#(? array( + 'replacement1', + 'replacement2', + 'replacement3', + 'replacement4', + ), + ); + } public function set_bots($bots) { diff --git a/tests/mock_user.php b/tests/mock_user.php index 74d31c4c4a..5b89ea3e19 100644 --- a/tests/mock_user.php +++ b/tests/mock_user.php @@ -17,4 +17,20 @@ class phpbb_mock_user { public $host = "testhost"; public $page = array('root_script_path' => '/'); + + private $options = array(); + public function optionget($item) + { + if (!isset($this->options[$item])) + { + throw new Exception(sprintf("You didn't set the option '%s' on the mock user using optionset.", $item)); + } + + return $this->options[$item]; + } + + public function optionset($item, $value) + { + $this->options[$item] = $value; + } } diff --git a/tests/network/ftp_fsock_pasv_epsv_test.php b/tests/network/ftp_fsock_pasv_epsv_test.php new file mode 100644 index 0000000000..6ad811e3ca --- /dev/null +++ b/tests/network/ftp_fsock_pasv_epsv_test.php @@ -0,0 +1,63 @@ +assert_ls_contains_debian(self::$ipv4); + } + + public function test_epsv() + { + $ipv4 = self::$ipv4; + // EPSV + $this->assert_ls_contains_debian("[::ffff:$ipv4]"); + } + + protected function assert_ls_contains_debian($hostname) + { + $o = $this->get_object($hostname); + $result = $o->_init(); + // This test may fail on IPv6 addresses if IPv6 support is + // not available. PHP must be compiled with IPv6 support enabled, + // and your operating system must be configured for IPv6 as well. + if ($result !== true) + { + $this->markTestSkipped("Failed to connect to $hostname: $result"); + } + $this->assertContains('debian', $o->_ls()); + $o->_close(); + } + + protected function get_object($hostname) + { + return new ftp_fsock($hostname, 'anonymous', 'anonymous@localhost.tld', '/'); + } +} diff --git a/tests/profile/custom_test.php b/tests/profile/custom_test.php index 0e0a851243..585182e583 100644 --- a/tests/profile/custom_test.php +++ b/tests/profile/custom_test.php @@ -48,7 +48,7 @@ class phpbb_profile_custom_test extends phpbb_database_test_case ); $cp = new custom_profile; - $result = $cp->validate_profile_field(FIELD_DROPDOWN, &$field_value, $field_data); + $result = $cp->validate_profile_field(FIELD_DROPDOWN, $field_value, $field_data); $this->assertEquals($expected, $result, $description); } diff --git a/tests/regex/table_prefix_test.php b/tests/regex/table_prefix_test.php new file mode 100644 index 0000000000..67a18b4fbc --- /dev/null +++ b/tests/regex/table_prefix_test.php @@ -0,0 +1,35 @@ +assertEquals($expected, preg_match(get_preg_expression('table_prefix'), $prefix)); + } +} diff --git a/tests/session/append_sid_test.php b/tests/session/append_sid_test.php new file mode 100644 index 0000000000..1a3ad633e3 --- /dev/null +++ b/tests/session/append_sid_test.php @@ -0,0 +1,51 @@ + 1, 'f' => 2), true, false, 'viewtopic.php?t=1&f=2', 'parameters in params-argument as array'), + + // Custom sid parameter + array('viewtopic.php', 't=1&f=2', true, 'custom-sid', 'viewtopic.php?t=1&f=2&sid=custom-sid', 'using session_id'), + + // Testing anchors + array('viewtopic.php?t=1&f=2#anchor', false, true, false, 'viewtopic.php?t=1&f=2#anchor', 'anchor in url-argument'), + array('viewtopic.php', 't=1&f=2#anchor', true, false, 'viewtopic.php?t=1&f=2#anchor', 'anchor in params-argument'), + array('viewtopic.php', array('t' => 1, 'f' => 2, '#' => 'anchor'), true, false, 'viewtopic.php?t=1&f=2#anchor', 'anchor in params-argument (array)'), + + // Anchors and custom sid + array('viewtopic.php?t=1&f=2#anchor', false, true, 'custom-sid', 'viewtopic.php?t=1&f=2&sid=custom-sid#anchor', 'anchor in url-argument using session_id'), + array('viewtopic.php', 't=1&f=2#anchor', true, 'custom-sid', 'viewtopic.php?t=1&f=2&sid=custom-sid#anchor', 'anchor in params-argument using session_id'), + array('viewtopic.php', array('t' => 1, 'f' => 2, '#' => 'anchor'), true, 'custom-sid', 'viewtopic.php?t=1&f=2&sid=custom-sid#anchor', 'anchor in params-argument (array) using session_id'), + + // Empty parameters should not append the ? + array('viewtopic.php', false, true, false, 'viewtopic.php', 'no params using bool false'), + array('viewtopic.php', '', true, false, 'viewtopic.php', 'no params using empty string'), + array('viewtopic.php', array(), true, false, 'viewtopic.php', 'no params using empty array'), + ); + } + + /** + * @dataProvider append_sid_data + */ + public function test_append_sid($url, $params, $is_amp, $session_id, $expected, $description) + { + $this->assertEquals($expected, append_sid($url, $params, $is_amp, $session_id)); + } +} + diff --git a/tests/session/fixtures/sessions_empty.xml b/tests/session/fixtures/sessions_empty.xml index f94337314e..0e6ddccd88 100644 --- a/tests/session/fixtures/sessions_empty.xml +++ b/tests/session/fixtures/sessions_empty.xml @@ -3,17 +3,33 @@ user_idusername_clean + user_permissions + user_sig + user_occ + user_interests 1 anonymous + + + + 3 foo + + + + 4 bar + + + +
    diff --git a/tests/session/fixtures/sessions_full.xml b/tests/session/fixtures/sessions_full.xml index bf6fc65997..509687f4d2 100644 --- a/tests/session/fixtures/sessions_full.xml +++ b/tests/session/fixtures/sessions_full.xml @@ -3,17 +3,33 @@
    user_idusername_clean + user_permissions + user_sig + user_occ + user_interests 1 anonymous + + + + 3 foo + + + + 4 bar + + + +
    diff --git a/tests/template/template_test.php b/tests/template/template_test.php index 33c82d53ad..5005710220 100644 --- a/tests/template/template_test.php +++ b/tests/template/template_test.php @@ -63,9 +63,10 @@ class phpbb_template_template_test extends phpbb_test_case // Test the engine can be used $this->setup_engine(); - if (!is_writable(dirname($this->template->cachepath))) + $template_cache_dir = dirname($this->template->cachepath); + if (!is_writable($template_cache_dir)) { - $this->markTestSkipped("Template cache directory is not writable."); + $this->markTestSkipped("Template cache directory ({$template_cache_dir}) is not writable."); } foreach (glob($this->template->cachepath . '*') as $file) diff --git a/tests/test_framework/phpbb_database_test_connection_manager.php b/tests/test_framework/phpbb_database_test_connection_manager.php index a7559e2183..68e09add94 100644 --- a/tests/test_framework/phpbb_database_test_connection_manager.php +++ b/tests/test_framework/phpbb_database_test_connection_manager.php @@ -69,6 +69,11 @@ class phpbb_database_test_connection_manager default: $dsn .= 'host=' . $this->config['dbhost']; + if ($this->config['dbport']) + { + $dsn .= ';port=' . $this->config['dbport']; + } + if ($use_db) { $dsn .= ';dbname=' . $this->config['dbname']; diff --git a/tests/text_processing/censor_text_test.php b/tests/text_processing/censor_text_test.php new file mode 100644 index 0000000000..2843f0b20b --- /dev/null +++ b/tests/text_processing/censor_text_test.php @@ -0,0 +1,85 @@ +optionset('viewcensors', false); + + return array( + array('', ''), + + array('badword1', 'replacement1'), + array(' badword1', ' replacement1'), + array('badword1 ', 'replacement1 '), + array(' badword1 ', ' replacement1 '), + array('abadword1', 'replacement1'), + array('badword1w', 'replacement1'), + array('abadword1w', 'replacement1'), + array('anotherbadword1test', 'replacement1'), + array('this badword1', 'this replacement1'), + array('this badword1 word', 'this replacement1 word'), + + array('badword2', 'replacement2'), + array('bbadword2', 'replacement2'), + array('bbbadword2', 'replacement2'), + array('badword2d', 'badword2d'), + array('bbadword2d', 'bbadword2d'), + array('test badword2', 'test replacement2'), + array('test badword2 word', 'test replacement2 word'), + + array('badword3', 'replacement3'), + array('bbadword3', 'bbadword3'), + array('badword3d', 'replacement3'), + array('badword3ddd', 'replacement3'), + array('bbadword3d', 'bbadword3d'), + array(' badword3 ', ' replacement3 '), + array(' badword3', ' replacement3'), + + array('badword4', 'replacement4'), + array('this badword4 word', 'this replacement4 word'), + array('abadword4', 'abadword4'), + array('badword4d', 'badword4d'), + array('abadword4d', 'abadword4d'), + + 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('öäü', 'öäü'), + array('badw' . chr(1) . 'ord1', 'badw' . chr(1) . 'ord1'), + array('badw' . chr(2) . 'ord1', 'badw' . chr(2) . 'ord1'), + array('badw' . chr(3) . 'ord1', 'badw' . chr(3) . 'ord1'), + array('badw' . chr(4) . 'ord1', 'badw' . chr(4) . 'ord1'), + array('badw' . chr(5) . 'ord1', 'badw' . chr(5) . 'ord1'), + array('badw' . chr(6) . 'ord1', 'badw' . chr(6) . 'ord1'), + ); + } + + /** + * @dataProvider censor_text_data + */ + public function test_censor_text($input, $expected) + { + $label = 'Testing word censor: ' . $input; + $this->assertEquals($expected, censor_text($input), $label); + } +} diff --git a/tests/utf/data/.gitkeep b/tests/tmp/.gitkeep similarity index 100% rename from tests/utf/data/.gitkeep rename to tests/tmp/.gitkeep diff --git a/tests/utf/normalizer_test.php b/tests/utf/normalizer_test.php index f78dba8004..1dc69e283e 100644 --- a/tests/utf/normalizer_test.php +++ b/tests/utf/normalizer_test.php @@ -14,10 +14,13 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/utf/utf_normalizer.php'; */ class phpbb_utf_normalizer_test extends phpbb_test_case { + static private $data_dir; + static public function setUpBeforeClass() { - self::download('http://www.unicode.org/Public/UNIDATA/NormalizationTest.txt', dirname(__FILE__).'/data'); - self::download('http://www.unicode.org/Public/UNIDATA/UnicodeData.txt', dirname(__FILE__).'/data'); + self::$data_dir = dirname(__file__) . '/../tmp'; + self::download('http://www.unicode.org/Public/UNIDATA/NormalizationTest.txt', self::$data_dir); + self::download('http://www.unicode.org/Public/UNIDATA/UnicodeData.txt', self::$data_dir); } public function test_normalizer() @@ -62,7 +65,7 @@ class phpbb_utf_normalizer_test extends phpbb_test_case $tested_chars = array(); - $fp = fopen(dirname(__FILE__).'/data/NormalizationTest.txt', 'rb'); + $fp = fopen(self::$data_dir . '/NormalizationTest.txt', 'rb'); while (!feof($fp)) { $line = fgets($fp); @@ -99,7 +102,7 @@ class phpbb_utf_normalizer_test extends phpbb_test_case foreach ($tests as $test) { $utf_result = $utf_expected; - call_user_func(array('utf_normalizer', $form), &$utf_result); + call_user_func_array(array('utf_normalizer', $form), array(&$utf_result)); $hex_result = $this->utf_to_hexseq($utf_result); $this->assertEquals($utf_expected, $utf_result, "$expected == $form($test) ($hex_expected != $hex_result)"); @@ -117,7 +120,7 @@ class phpbb_utf_normalizer_test extends phpbb_test_case */ public function test_invariants(array $tested_chars) { - $fp = fopen(dirname(__FILE__).'/data/UnicodeData.txt', 'rb'); + $fp = fopen(self::$data_dir . '/UnicodeData.txt', 'rb'); while (!feof($fp)) { @@ -151,7 +154,7 @@ class phpbb_utf_normalizer_test extends phpbb_test_case foreach (array('nfc', 'nfkc', 'nfd', 'nfkd') as $form) { $utf_result = $utf_expected; - call_user_func(array('utf_normalizer', $form), &$utf_result); + call_user_func_array(array('utf_normalizer', $form), array(&$utf_result)); $hex_result = $this->utf_to_hexseq($utf_result); $this->assertEquals($utf_expected, $utf_result, "$hex_expected == $form($hex_tested) ($hex_expected != $hex_result)"); diff --git a/tests/wrapper/gmgetdate_test.php b/tests/wrapper/gmgetdate_test.php new file mode 100644 index 0000000000..0b4c3378a9 --- /dev/null +++ b/tests/wrapper/gmgetdate_test.php @@ -0,0 +1,49 @@ +run_gmgetdate_assertion(); + $this->run_test_with_timezone('UTC'); + $this->run_test_with_timezone('Europe/Berlin'); + $this->run_test_with_timezone('America/Los_Angeles'); + $this->run_test_with_timezone('Antarctica/South_Pole'); + } + + protected function run_test_with_timezone($timezone_identifier) + { + $current_timezone = date_default_timezone_get(); + + date_default_timezone_set($timezone_identifier); + $this->run_gmgetdate_assertion(); + date_default_timezone_set($current_timezone); + } + + protected function run_gmgetdate_assertion() + { + $expected = time(); + + $date_array = phpbb_gmgetdate($expected); + + $actual = gmmktime( + $date_array['hours'], + $date_array['minutes'], + $date_array['seconds'], + $date_array['mon'], + $date_array['mday'], + $date_array['year'] + ); + + $this->assertEquals($expected, $actual); + } +} diff --git a/tests/random/mt_rand.php b/tests/wrapper/mt_rand_test.php similarity index 94% rename from tests/random/mt_rand.php rename to tests/wrapper/mt_rand_test.php index d6502c4e80..c8bcb3d14c 100644 --- a/tests/random/mt_rand.php +++ b/tests/wrapper/mt_rand_test.php @@ -9,7 +9,7 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; -class phpbb_random_mt_rand_test extends phpbb_test_case +class phpbb_wrapper_mt_rand_test extends phpbb_test_case { public function test_max_equals_min() { diff --git a/tests/wrapper/version_compare_test.php b/tests/wrapper/version_compare_test.php new file mode 100644 index 0000000000..f718cfd16b --- /dev/null +++ b/tests/wrapper/version_compare_test.php @@ -0,0 +1,130 @@ +assertEquals(-1, phpbb_version_compare('1.0.0', '1.0.1')); + $this->assertEquals(0, phpbb_version_compare('1.0.0', '1.0.0')); + $this->assertEquals(1, phpbb_version_compare('1.0.1', '1.0.0')); + } + + public function test_three_parameters() + { + $this->assertEquals(true, phpbb_version_compare('1.0.0', '1.0.1', '<')); + $this->assertEquals(true, phpbb_version_compare('1.0.0', '1.0.0', '<=')); + $this->assertEquals(true, phpbb_version_compare('1.0.0', '1.0.0', '=')); + $this->assertEquals(true, phpbb_version_compare('1.0.0', '1.0.0', '>=')); + $this->assertEquals(true, phpbb_version_compare('1.0.1', '1.0.0', '>')); + } + + public function test_strict_order() + { + $releases = array( + '2.0.0', + '2.0.1', + // Those are not version_compare() compatible + //'2.0.6a', + //'2.0.6b', + //'2.0.6c', + //'2.0.6d', + '2.0.7', + '2.0.23', + '3.0.A1', + '3.0.A2', + '3.0.A3', + '3.0.B1', + '3.0.B2', + '3.0.B4', + '3.0.B5', + '3.0.RC1', + '3.0.RC5', + '3.0.0', + '3.0.1', + '3.0.2', + '3.0.7', + '3.0.7-PL1', + '3.0.8-RC1', + '3.0.8', + '3.0.9-dev', + '3.0.9-RC1', + '3.0.9-RC2', + '3.0.9-RC4', + '3.0.10-RC1', + '3.1-dev', + '3.2-A1', + ); + + for ($i = 0, $size = sizeof($releases); $i < $size - 1; ++$i) + { + $version1 = $releases[$i]; + $version2 = $releases[$i + 1]; + + $this->assertEquals( + true, + phpbb_version_compare($version1, $version2, '<'), + "Result of version comparison $version1 < $version2 is incorrect." + ); + } + } + + /** + * @dataProvider equality_test_data + */ + public function test_equality($version1, $version2) + { + $this->assertEquals( + 0, + phpbb_version_compare($version1, $version2), + "Result of version comparison $version1 = $version2 is incorrect." + ); + + $this->assertEquals( + true, + phpbb_version_compare($version1, $version2, '='), + "Result of version comparison $version1 = $version2 is incorrect." + ); + } + + public function equality_test_data() + { + return array( + array('1.1.0-A2', '1.1.0-a2'), + array('1.1.0-B1', '1.1.0-b1'), + ); + } + + /** + * @dataProvider alpha_beta_test_data + */ + public function test_alpha_beta($expected, $version1, $version2) + { + $this->assertEquals( + $expected, + phpbb_version_compare($version1, $version2), + "Result of version comparison ($version1, $version2) = $expected is incorrect." + ); + + } + + public function alpha_beta_test_data() + { + return array( + array(-1, '1.1.0-A2', '1.1.0-B1'), + array(-1, '1.1.0-a2', '1.1.0-b1'), + + array(-1, '1.1.0-a2', '1.1.0-B1'), + array(-1, '1.1.0-A2', '1.1.0-b1'), + ); + } + +}