aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml8
-rw-r--r--build/build.xml2
-rwxr-xr-xbuild/build_diff.php409
-rw-r--r--build/diff_class.php1710
-rwxr-xr-xbuild/package.php54
-rwxr-xr-xgit-tools/commit-msg-hook-range.sh30
-rw-r--r--phpBB/adm/style/acp_attachments.html8
-rw-r--r--phpBB/adm/style/acp_bbcodes.html4
-rw-r--r--phpBB/adm/style/acp_bots.html4
-rw-r--r--phpBB/adm/style/acp_groups.html10
-rw-r--r--phpBB/adm/style/acp_icons.html18
-rw-r--r--phpBB/adm/style/acp_inactive.html5
-rw-r--r--phpBB/adm/style/acp_language.html8
-rw-r--r--phpBB/adm/style/acp_logs.html4
-rw-r--r--phpBB/adm/style/acp_main.html10
-rw-r--r--phpBB/adm/style/acp_modules.html4
-rw-r--r--phpBB/adm/style/acp_permission_roles.html19
-rw-r--r--phpBB/adm/style/acp_profile.html21
-rw-r--r--phpBB/adm/style/acp_prune_forums.html4
-rw-r--r--phpBB/adm/style/acp_ranks.html4
-rw-r--r--phpBB/adm/style/acp_reasons.html16
-rw-r--r--phpBB/adm/style/acp_users.html8
-rw-r--r--phpBB/adm/style/acp_users_feedback.html5
-rw-r--r--phpBB/adm/style/acp_users_warnings.html4
-rw-r--r--phpBB/adm/style/acp_words.html4
-rw-r--r--phpBB/adm/style/admin.css22
-rw-r--r--phpBB/adm/style/ajax.js12
-rw-r--r--phpBB/adm/style/captcha_qa_acp.html10
-rw-r--r--phpBB/assets/javascript/editor.js9
-rw-r--r--phpBB/assets/javascript/plupload.js854
-rw-r--r--phpBB/assets/plupload/img/done.gif (renamed from phpBB/assets/plupload/jquery.plupload.queue/img/done.gif)bin1024 -> 1024 bytes
-rw-r--r--phpBB/assets/plupload/img/error.gif (renamed from phpBB/assets/plupload/jquery.plupload.queue/img/error.gif)bin994 -> 994 bytes
-rw-r--r--phpBB/assets/plupload/img/throbber.gif (renamed from phpBB/assets/plupload/jquery.plupload.queue/img/throbber.gif)bin1922 -> 1922 bytes
-rw-r--r--phpBB/assets/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css177
-rw-r--r--phpBB/assets/plupload/jquery.plupload.queue/img/backgrounds.gifbin2977 -> 0 bytes
-rw-r--r--phpBB/assets/plupload/jquery.plupload.queue/img/buttons-disabled.pngbin1292 -> 0 bytes
-rw-r--r--phpBB/assets/plupload/jquery.plupload.queue/img/buttons.pngbin1439 -> 0 bytes
-rw-r--r--phpBB/assets/plupload/jquery.plupload.queue/img/delete.gifbin180 -> 0 bytes
-rw-r--r--phpBB/assets/plupload/jquery.plupload.queue/img/transp50.pngbin399 -> 0 bytes
-rw-r--r--phpBB/assets/plupload/jquery.plupload.queue/jquery.plupload.queue.min.js1
-rw-r--r--phpBB/assets/plupload/plupload.full.min.js16
-rw-r--r--phpBB/config/auth_providers.yml4
-rw-r--r--phpBB/config/console.yml35
-rw-r--r--phpBB/config/passwords.yml62
-rw-r--r--phpBB/config/profilefields.yml82
-rw-r--r--phpBB/config/services.yml2
-rw-r--r--phpBB/config/tables.yml4
-rw-r--r--phpBB/docs/AUTHORS3
-rw-r--r--phpBB/docs/events.md2
-rw-r--r--phpBB/includes/acp/acp_forums.php7
-rw-r--r--phpBB/includes/acp/acp_icons.php11
-rw-r--r--phpBB/includes/acp/acp_modules.php8
-rw-r--r--phpBB/includes/acp/acp_permission_roles.php47
-rw-r--r--phpBB/includes/acp/acp_profile.php603
-rw-r--r--phpBB/includes/acp/acp_reasons.php25
-rw-r--r--phpBB/includes/acp/acp_users.php10
-rw-r--r--phpBB/includes/db/schema_data.php8
-rw-r--r--phpBB/includes/functions.php283
-rw-r--r--phpBB/includes/functions_module.php39
-rw-r--r--phpBB/includes/functions_posting.php1
-rw-r--r--phpBB/includes/functions_profile_fields.php1187
-rw-r--r--phpBB/includes/functions_transfer.php6
-rw-r--r--phpBB/includes/functions_user.php9
-rw-r--r--phpBB/includes/mcp/mcp_queue.php38
-rw-r--r--phpBB/includes/mcp/mcp_reports.php10
-rw-r--r--phpBB/includes/message_parser.php10
-rw-r--r--phpBB/includes/ucp/ucp_notifications.php14
-rw-r--r--phpBB/includes/ucp/ucp_pm_viewmessage.php8
-rw-r--r--phpBB/includes/ucp/ucp_profile.php18
-rw-r--r--phpBB/includes/ucp/ucp_register.php9
-rw-r--r--phpBB/includes/ucp/ucp_remind.php7
-rw-r--r--phpBB/index.php45
-rw-r--r--phpBB/install/database_update.php10
-rw-r--r--phpBB/install/schemas/firebird_schema.sql8
-rw-r--r--phpBB/install/schemas/mssql_schema.sql8
-rw-r--r--phpBB/install/schemas/mysql_40_schema.sql8
-rw-r--r--phpBB/install/schemas/mysql_41_schema.sql8
-rw-r--r--phpBB/install/schemas/oracle_schema.sql8
-rw-r--r--phpBB/install/schemas/postgres_schema.sql8
-rw-r--r--phpBB/install/schemas/sqlite_schema.sql8
-rw-r--r--phpBB/language/en/common.php2
-rw-r--r--phpBB/language/en/plupload.php2
-rw-r--r--phpBB/language/en/search.php5
-rw-r--r--phpBB/memberlist.php6
-rw-r--r--phpBB/phpbb/auth/provider/apache.php13
-rw-r--r--phpBB/phpbb/auth/provider/db.php32
-rw-r--r--phpBB/phpbb/auth/provider/ldap.php19
-rw-r--r--phpBB/phpbb/auth/provider/oauth/oauth.php13
-rw-r--r--phpBB/phpbb/console/command/config/command.php22
-rw-r--r--phpBB/phpbb/console/command/config/delete.php46
-rw-r--r--phpBB/phpbb/console/command/config/get.php54
-rw-r--r--phpBB/phpbb/console/command/config/increment.php52
-rw-r--r--phpBB/phpbb/console/command/config/set.php52
-rw-r--r--phpBB/phpbb/console/command/config/set_atomic.php65
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_types.php106
-rw-r--r--phpBB/phpbb/db/tools.php106
-rw-r--r--phpBB/phpbb/log/log.php4
-rw-r--r--phpBB/phpbb/notification/type/approve_post.php7
-rw-r--r--phpBB/phpbb/notification/type/approve_topic.php7
-rw-r--r--phpBB/phpbb/notification/type/base.php6
-rw-r--r--phpBB/phpbb/notification/type/disapprove_post.php7
-rw-r--r--phpBB/phpbb/notification/type/disapprove_topic.php7
-rw-r--r--phpBB/phpbb/notification/type/post.php11
-rw-r--r--phpBB/phpbb/notification/type/report_post.php7
-rw-r--r--phpBB/phpbb/notification/type/report_post_closed.php7
-rw-r--r--phpBB/phpbb/notification/type/topic.php11
-rw-r--r--phpBB/phpbb/passwords/driver/base.php45
-rw-r--r--phpBB/phpbb/passwords/driver/bcrypt.php104
-rw-r--r--phpBB/phpbb/passwords/driver/bcrypt_2y.php34
-rw-r--r--phpBB/phpbb/passwords/driver/driver_interface.php60
-rw-r--r--phpBB/phpbb/passwords/driver/helper.php144
-rw-r--r--phpBB/phpbb/passwords/driver/phpass.php26
-rw-r--r--phpBB/phpbb/passwords/driver/salted_md5.php160
-rw-r--r--phpBB/phpbb/passwords/helper.php103
-rw-r--r--phpBB/phpbb/passwords/manager.php341
-rw-r--r--phpBB/phpbb/path_helper.php44
-rw-r--r--phpBB/phpbb/permissions.php1
-rw-r--r--phpBB/phpbb/profilefields/lang_helper.php130
-rw-r--r--phpBB/phpbb/profilefields/manager.php375
-rw-r--r--phpBB/phpbb/profilefields/type/type_base.php175
-rw-r--r--phpBB/phpbb/profilefields/type/type_bool.php387
-rw-r--r--phpBB/phpbb/profilefields/type/type_date.php358
-rw-r--r--phpBB/phpbb/profilefields/type/type_dropdown.php298
-rw-r--r--phpBB/phpbb/profilefields/type/type_int.php234
-rw-r--r--phpBB/phpbb/profilefields/type/type_interface.php177
-rw-r--r--phpBB/phpbb/profilefields/type/type_string.php156
-rw-r--r--phpBB/phpbb/profilefields/type/type_string_common.php114
-rw-r--r--phpBB/phpbb/profilefields/type/type_text.php201
-rw-r--r--phpBB/phpbb/search/fulltext_mysql.php2
-rw-r--r--phpBB/phpbb/search/fulltext_native.php2
-rw-r--r--phpBB/phpbb/template/twig/environment.php15
-rw-r--r--phpBB/phpbb/template/twig/twig.php2
-rw-r--r--phpBB/posting.php34
-rw-r--r--phpBB/styles/prosilver/template/ajax.js41
-rw-r--r--phpBB/styles/prosilver/template/custom_profile_fields.html31
-rw-r--r--phpBB/styles/prosilver/template/forum_fn.js2
-rw-r--r--phpBB/styles/prosilver/template/index_body.html3
-rw-r--r--phpBB/styles/prosilver/template/mcp_header.html2
-rw-r--r--phpBB/styles/prosilver/template/overall_footer.html2
-rw-r--r--phpBB/styles/prosilver/template/overall_header.html19
-rw-r--r--phpBB/styles/prosilver/template/plupload.html25
-rw-r--r--phpBB/styles/prosilver/template/posting_attach_body.html74
-rw-r--r--phpBB/styles/prosilver/template/posting_editor.html38
-rw-r--r--phpBB/styles/prosilver/template/posting_smilies.html2
-rw-r--r--phpBB/styles/prosilver/template/profilefields/bool.html7
-rw-r--r--phpBB/styles/prosilver/template/profilefields/date.html5
-rw-r--r--phpBB/styles/prosilver/template/profilefields/dropdown.html5
-rw-r--r--phpBB/styles/prosilver/template/profilefields/int.html3
-rw-r--r--phpBB/styles/prosilver/template/profilefields/string.html3
-rw-r--r--phpBB/styles/prosilver/template/profilefields/text.html3
-rw-r--r--phpBB/styles/prosilver/template/ucp_groups_manage.html2
-rw-r--r--phpBB/styles/prosilver/template/ucp_header.html2
-rw-r--r--phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html2
-rw-r--r--phpBB/styles/prosilver/template/viewtopic_print.html4
-rw-r--r--phpBB/styles/prosilver/theme/bidi.css46
-rw-r--r--phpBB/styles/prosilver/theme/colours.css36
-rw-r--r--phpBB/styles/prosilver/theme/common.css1
-rw-r--r--phpBB/styles/prosilver/theme/cp.css26
-rw-r--r--phpBB/styles/prosilver/theme/images/icon_mark.gifbin0 -> 360 bytes
-rw-r--r--phpBB/styles/prosilver/theme/links.css23
-rw-r--r--phpBB/styles/prosilver/theme/plupload.css77
-rw-r--r--phpBB/styles/prosilver/theme/responsive.css9
-rw-r--r--phpBB/styles/subsilver2/template/index_body.html2
-rw-r--r--phpBB/styles/subsilver2/template/overall_header.html2
-rw-r--r--phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html1
-rw-r--r--phpBB/styles/subsilver2/template/viewtopic_print.html1
-rw-r--r--phpBB/viewforum.php16
-rw-r--r--phpBB/viewtopic.php6
-rw-r--r--tests/auth/fixtures/user.xml15
-rw-r--r--tests/auth/fixtures/user_533.xml48
-rw-r--r--tests/auth/provider_apache_test.php35
-rw-r--r--tests/auth/provider_db_test.php38
-rw-r--r--tests/extension/ext/vendor2/foo/acp/a_info.php2
-rw-r--r--tests/extension/modules_test.php44
-rw-r--r--tests/functional/extension_controller_test.php28
-rw-r--r--tests/functional/fixtures/ext/foo/bar/config/routing.yml4
-rw-r--r--tests/functional/fixtures/ext/foo/bar/config/services.yml6
-rw-r--r--tests/functional/fixtures/ext/foo/bar/controller/controller.php80
-rw-r--r--tests/functional/fixtures/ext/foo/bar/styles/prosilver/template/redirect_body.html8
-rw-r--r--tests/functional/forum_password_test.php55
-rw-r--r--tests/functional/plupload_test.php4
-rw-r--r--tests/lint_test.php22
-rw-r--r--tests/passwords/drivers_test.php81
-rw-r--r--tests/passwords/manager_test.php295
-rw-r--r--tests/path_helper/web_root_path_test.php23
-rw-r--r--tests/profile/custom_test.php44
-rw-r--r--tests/profile/fixtures/profile_fields.xml6
-rw-r--r--tests/security/hash_test.php25
-rw-r--r--tests/security/redirect_test.php101
-rw-r--r--tests/test_framework/phpbb_functional_test_case.php28
191 files changed, 7072 insertions, 5015 deletions
diff --git a/.travis.yml b/.travis.yml
index bfe58bee7d..4f4705e4f7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,13 +32,7 @@ script:
- sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.5' -a '$DB' = 'mysql' ]; then ../phpBB/vendor/bin/phing sniff; fi"
- cd ..
- phpBB/vendor/bin/phpunit --configuration travis/phpunit-$DB-travis.xml
-
-notifications:
- email:
- recipients:
- - dev-team@phpbb.com
- on_success: change
- on_failure: change
+ - sh -c "if [ '$TRAVIS_PULL_REQUEST' != 'false' ]; then git-tools/commit-msg-hook-range.sh $TRAVIS_COMMIT_RANGE; fi"
matrix:
allow_failures:
diff --git a/build/build.xml b/build/build.xml
index ebc1f0be8b..d4ba927d96 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -165,8 +165,6 @@
<target name="package" depends="clean,prepare,prepare-new-version,old-version-diffs">
<exec dir="build" command="php -f package.php '${versions}' > logs/package.log" escape="false" />
- <exec dir="build" command="php -f build_diff.php '${prevversion}' '${newversion}' > logs/build_diff.log" escape="false" />
-
<exec dir="build" escape="false"
command="diff -crNEBwd old_versions/release-${prevversion}/language new_version/phpBB3/language >
save/save_${prevversion}_to_${newversion}/language/phpbb-${prevversion}_to_${newversion}_language.patch" />
diff --git a/build/build_diff.php b/build/build_diff.php
deleted file mode 100755
index 68bac65a66..0000000000
--- a/build/build_diff.php
+++ /dev/null
@@ -1,409 +0,0 @@
-#!/usr/bin/env php
-<?php
-/**
-*
-* @package build
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-if ($_SERVER['argc'] != 3)
-{
- die("Please specify the previous and current version as arguments (e.g. build_diff.php '1.0.2' '1.0.3').");
-}
-
-$old_version = trim($_SERVER['argv'][1]);
-$new_version = trim($_SERVER['argv'][2]);
-
-$substitute_old = $old_version;
-$substitute_new = $new_version;
-$simple_name_old = 'release-' . $old_version;
-$simple_name_new = 'release-' . $new_version;
-$echo_changes = false;
-
-// DO NOT EVER USE THE FOLLOWING! Fix the script to generate proper changes,
-// do NOT manually create them.
-
-// Set this to true to just compress the changes and do not build them again
-// This should be used for building custom modified txt file. ;)
-$package_changed_files = false;
-
-//$debug_file = 'includes/functions_user.php'; //'styles/prosilver/style.cfg';
-$debug_file = false;
-
-if ($debug_file !== false)
-{
- $echo_changes = false;
-}
-
-$s_name = 'save_' . $substitute_old . '_to_' . $substitute_new;
-
-$location = dirname(__FILE__);
-
-if (!$package_changed_files)
-{
- if (!$echo_changes)
- {
- // Create directory...
- run_command("mkdir $location/save/{$s_name}");
- run_command("mkdir $location/save/{$s_name}/language");
- run_command("mkdir $location/save/{$s_name}/prosilver");
- run_command("mkdir $location/save/{$s_name}/subsilver2");
- }
-}
-
-// Build code changes and place them into 'save'
-if (!$package_changed_files)
-{
- build_code_changes('language');
- build_code_changes('prosilver');
- build_code_changes('subsilver2');
-}
-
-// Package code changes
-$code_changes_filename = 'phpBB-' . $substitute_old . '_to_' . $substitute_new . '-codechanges';
-
-if (!$echo_changes)
-{
- // Now compress the files...
- // Build Main phpBB Release
- $compress_programs = array(
-// 'tar.gz' => 'tar -czf',
- 'tar.bz2' => 'tar -cjf',
- 'zip' => 'zip -r'
- );
-
- chdir($location . '/save/' . $s_name);
- foreach ($compress_programs as $extension => $compress_command)
- {
- echo "Packaging code changes for $extension\n";
- run_command("rm ./../../new_version/release_files/{$code_changes_filename}.{$extension}");
- flush();
-
- // Build Package
- run_command("$compress_command ./../../new_version/release_files/{$code_changes_filename}.{$extension} *");
- flush();
- }
-}
-
-/**
-* $output_format can be: language, prosilver and subsilver2
-*/
-function build_code_changes($output_format)
-{
- global $substitute_new, $substitute_old, $simple_name_old, $simple_name_new, $echo_changes, $package_changed_files, $location, $debug_file, $s_name;
-
- // Global array holding the data entries
- $data = array(
- 'header' => array(),
- 'diff' => array(),
- );
-
- // Read diff file and prepare the output filedata...
- //$patch_filename = '../new_version/patches/phpBB-' . $substitute_old . '_to_' . $substitute_new . '.patch';
- $release_filename = 'phpbb-' . $substitute_old . '_to_' . $substitute_new . '_' . $output_format . '.txt';
-
- if (!$package_changed_files)
- {
- if (!$echo_changes)
- {
- $fp = fopen('save/' . $s_name . '/' . $output_format . '/' . $release_filename, 'wb');
-
- if (!$fp)
- {
- die('Unable to create ' . $release_filename);
- }
- }
- }
-
- include_once($location . '/build_helper.php');
- $package = new build_package(array($substitute_old, $substitute_new), false);
-
- $titles = array(
- 'language' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' Language Pack Changes',
- 'prosilver' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' prosilver Changes',
- 'subsilver2' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' subsilver2 Changes',
- );
-
- $data['header'] = array(
- 'title' => $titles[$output_format],
- 'intro' => '
-
-These are the ' . $titles[$output_format] . ' summed up into a little Mod. These changes are only partial and do not include any code changes, therefore not meant for updating phpBB.
-
- ',
- 'included_files' => array(),
- );
-
- // We collect the files we want to diff first (ironically we grab this from a diff file)
- if (!$echo_changes)
- {
- echo "\n\nCollecting Filenames:";
- }
-
- // We re-create the patch file
- foreach ($package->old_packages as $_package_name => $dest_package_filename)
- {
- chdir($package->locations['old_versions']);
-
- if (!$echo_changes)
- {
- echo "\n\n" . 'Creating patch/diff files for phpBB-' . $dest_package_filename . $package->get('new_version_number');
- }
-
- $dest_package_filename = $location . '/save/' . $s_name . '/phpBB-' . $dest_package_filename . $package->get('new_version_number') . '.patch';
- $package->run_command('diff ' . $package->diff_options . ' ' . $_package_name . ' ' . $package->get('simple_name') . ' > ' . $dest_package_filename);
-
- // Parse this diff to determine file changes from the checked versions and save them
- $result = $package->collect_diff_files($dest_package_filename, $_package_name);
- $package->run_command('rm ' . $dest_package_filename);
- }
-
- chdir($location);
-
- $filenames = array();
- foreach ($result['files'] as $filename)
- {
- if ($debug_file !== false && $filename != $debug_file)
- {
- continue;
- }
-
- // Decide which files to compare...
- switch ($output_format)
- {
- case 'language':
- if (strpos($filename, 'language/en/') !== 0)
- {
- continue 2;
- }
- break;
-
- case 'prosilver':
- if (strpos($filename, 'styles/prosilver/') !== 0)
- {
- continue 2;
- }
- break;
-
- case 'subsilver2':
- if (strpos($filename, 'styles/subsilver2/') !== 0)
- {
- continue 2;
- }
- break;
- }
-
- if (!file_exists($location . '/old_versions/' . $simple_name_old . '/' . $filename))
- {
- // New file... include it
- $data['header']['included_files'][] = array(
- 'old' => $location . '/old_versions/' . $simple_name_old . '/' . $filename,
- 'new' => $location . '/old_versions/' . $simple_name_new . '/' . $filename,
- 'phpbb_filename' => $filename,
- );
- continue;
- }
-
- $filenames[] = array(
- 'old' => $location . '/old_versions/' . $simple_name_old . '/' . $filename,
- 'new' => $location . '/old_versions/' . $simple_name_new . '/' . $filename,
- 'phpbb_filename' => $filename,
- );
- }
-
- // Now let us go through the filenames list and create a more comprehensive diff
- if (!$echo_changes)
- {
- fwrite($fp, build_header($output_format, $filenames, $data['header']));
- }
- else
- {
- //echo build_header('text', $filenames, $data['header']);
- }
-
- // Copy files...
- $files_to_copy = array();
-
- foreach ($data['header']['included_files'] as $filename)
- {
- $files_to_copy[] = $filename['phpbb_filename'];
- }
-
- // First step is to copy the new version over (clean structure)
- if (!$echo_changes && sizeof($files_to_copy))
- {
- foreach ($files_to_copy as $file)
- {
- // Create directory?
- $dirname = dirname($file);
-
- if ($dirname)
- {
- $dirname = explode('/', $dirname);
- $__dir = array();
-
- foreach ($dirname as $i => $dir)
- {
- $__dir[] = $dir;
- run_command("mkdir -p $location/save/" . $s_name . '/' . $output_format . '/' . implode('/', $__dir));
- }
- }
-
- $source_file = $location . '/new_version/phpBB3/' . $file;
- $dest_file = $location . '/save/' . $s_name . '/' . $output_format . '/';
- $dest_file .= $file;
-
- $command = "cp -p $source_file $dest_file";
- $result = trim(`$command`);
- echo "- Copied File: " . $source_file . " -> " . $dest_file . "\n";
- }
- }
-
- include_once('diff_class.php');
-
- if (!$echo_changes)
- {
- echo "\n\nDiffing Codebases:";
- }
-
- foreach ($filenames as $file_ary)
- {
- if (!file_exists($file_ary['old']))
- {
- $lines1 = array();
- }
- else
- {
- $lines1 = file($file_ary['old']);
- }
- $lines2 = file($file_ary['new']);
-
- if (!sizeof($lines1))
- {
- // New File
- }
- else
- {
- $diff = new Diff($lines1, $lines2);
- $fmt = new BBCodeDiffFormatter(false, 5, $debug_file);
-
- if (!$echo_changes)
- {
- fwrite($fp, $fmt->format_open($file_ary['phpbb_filename']));
- fwrite($fp, $fmt->format($diff, $lines1));
- fwrite($fp, $fmt->format_close($file_ary['phpbb_filename']));
- }
- else
- {
- echo $fmt->format_open($file_ary['phpbb_filename']);
- echo $fmt->format($diff, $lines1);
- echo $fmt->format_close($file_ary['phpbb_filename']);
- }
-
- if ($debug_file !== false)
- {
- echo $fmt->format_open($file_ary['phpbb_filename']);
- echo $fmt->format($diff, $lines1);
- echo $fmt->format_close($file_ary['phpbb_filename']);
- exit;
- }
- }
- }
-
- if (!$echo_changes)
- {
- fwrite($fp, build_footer($output_format));
-
- // Close file
- fclose($fp);
-
- chmod('save/' . $s_name . '/' . $output_format . '/' . $release_filename, 0666);
- }
- else
- {
- echo build_footer($output_format);
- }
-}
-
-/**
-* Build Footer
-*/
-function build_footer($mode)
-{
- $html = '';
-
- $html .= "# \n";
- $html .= "#-----[ SAVE/CLOSE ALL FILES ]------------------------------------------ \n";
- $html .= "# \n";
- $html .= "# EoM";
-
- return $html;
-}
-
-/**
-* Build Header
-*/
-function build_header($mode, $filenames, $header)
-{
- global $substitute_old;
-
- $html = '';
-
- $html .= "############################################################## \n";
- $html .= "## Title: " . $header['title'] . "\n";
- $html .= "## Author: naderman < naderman@phpbb.com > (Nils Adermann) http://www.phpbb.com \n";
- $html .= "## Description: \n";
-
- $intr = explode("\n", $header['intro']);
- $introduction = '';
- foreach ($intr as $_line)
- {
- $introduction .= wordwrap($_line, 80) . "\n";
- }
- $intr = explode("\n", $introduction);
-
- foreach ($intr as $_line)
- {
- $html .= "## " . $_line . "\n";
- }
- $html .= "## \n";
- $html .= "## Files To Edit: \n";
-
- foreach ($filenames as $file_ary)
- {
- $html .= "## " . $file_ary['phpbb_filename'] . "\n";
- }
- $html .= "##\n";
- if (sizeof($header['included_files']))
- {
- $html .= "## Included Files: \n";
- foreach ($header['included_files'] as $filename)
- {
- $html .= "## {$filename['phpbb_filename']}\n";
- }
- }
- $html .= "## License: http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 \n";
- $html .= "############################################################## \n";
- $html .= "\n";
-
- // COPY Statement?
- if (sizeof($header['included_files']))
- {
- $html .= "#\n#-----[ COPY ]------------------------------------------\n#\n";
- foreach ($header['included_files'] as $filename)
- {
- $html .= "copy {$filename['phpbb_filename']} to {$filename['phpbb_filename']}\n";
- }
- $html .= "\n";
- }
-
- return $html;
-}
-
-function run_command($command)
-{
- $result = trim(`$command`);
- echo "\n- Command Run: " . $command . "\n";
-}
diff --git a/build/diff_class.php b/build/diff_class.php
deleted file mode 100644
index 2d8555400d..0000000000
--- a/build/diff_class.php
+++ /dev/null
@@ -1,1710 +0,0 @@
-<?php
-/**
-*
-* @package build
-* @copyright (c) 2000 Geoffrey T. Dairiki <dairiki@dairiki.org>
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* Class used internally by WikiDiff to actually compute the diffs.
-*
-* The algorithm used here is mostly lifted from the perl module
-* Algorithm::Diff (version 1.06) by Ned Konz, which is available at:
-* http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip
-*
-* More ideas are taken from:
-* http://www.ics.uci.edu/~eppstein/161/960229.html
-*
-* Some ideas are (and a bit of code) are from from analyze.c, from GNU
-* diffutils-2.7, which can be found at:
-* ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
-*
-* Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations)
-* are my own.
-*/
-class _WikiDiffEngine
-{
- var $edits; // List of editing operation to convert XV to YV.
- var $xv = array(), $yv = array();
-
- function _WikiDiffEngine($from_lines, $to_lines)
- {
- $n_from = sizeof($from_lines);
- $n_to = sizeof($to_lines);
-
- $endskip = 0;
- // Ignore trailing and leading matching lines.
- while ($n_from > 0 && $n_to > 0)
- {
- if ($from_lines[$n_from - 1] != $to_lines[$n_to - 1])
- {
- break;
- }
-
- $n_from--;
- $n_to--;
- $endskip++;
- }
-
- for ($skip = 0; $skip < min($n_from, $n_to); $skip++)
- {
- if ($from_lines[$skip] != $to_lines[$skip])
- {
- break;
- }
- }
- $n_from -= $skip;
- $n_to -= $skip;
-
- $xlines = array();
- $ylines = array();
-
- // Ignore lines which do not exist in both files.
- for ($x = 0; $x < $n_from; $x++)
- {
- $xhash[$from_lines[$x + $skip]] = 1;
- }
-
- for ($y = 0; $y < $n_to; $y++)
- {
- $line = $to_lines[$y + $skip];
- $ylines[] = $line;
-
- if (($this->ychanged[$y] = empty($xhash[$line])))
- {
- continue;
- }
- $yhash[$line] = 1;
- $this->yv[] = $line;
- $this->yind[] = $y;
- }
-
- for ($x = 0; $x < $n_from; $x++)
- {
- $line = $from_lines[$x + $skip];
- $xlines[] = $line;
-
- if (($this->xchanged[$x] = empty($yhash[$line])))
- {
- continue; // fixme? what happens to yhash/xhash when
- // there are two identical lines??
- }
- $this->xv[] = $line;
- $this->xind[] = $x;
- }
-
- // Find the LCS.
- $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
-
- // Merge edits when possible
- $this->_shift_boundaries($xlines, $this->xchanged, $this->ychanged);
- $this->_shift_boundaries($ylines, $this->ychanged, $this->xchanged);
-
- // Compute the edit operations.
- $this->edits = array();
-
- if ($skip)
- {
- $this->edits[] = $skip;
- }
-
- $x = 0;
- $y = 0;
-
- while ($x < $n_from || $y < $n_to)
- {
- // Skip matching "snake".
- $x0 = $x;
- $ncopy = 0;
- while ($x < $n_from && $y < $n_to && !$this->xchanged[$x] && !$this->ychanged[$y])
- {
- ++$x;
- ++$y;
- ++$ncopy;
- }
-
- if ($x > $x0)
- {
- $this->edits[] = $x - $x0;
- }
-
- // Find deletes.
- $x0 = $x;
- $ndelete = 0;
-
- while ($x < $n_from && $this->xchanged[$x])
- {
- ++$x;
- ++$ndelete;
- }
-
- if ($x > $x0)
- {
- $this->edits[] = -($x - $x0);
- }
-
- // Find adds.
- if (isset($this->ychanged[$y]) && $this->ychanged[$y])
- {
- $adds = array();
- while ($y < $n_to && $this->ychanged[$y])
- {
- $adds[] = $ylines[$y++];
- }
- $this->edits[] = $adds;
- }
- }
-
- if (!empty($endskip))
- {
- $this->edits[] = $endskip;
- }
- }
-
- /* Divide the Largest Common Subsequence (LCS) of the sequences
- * [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally
- * sized segments.
- *
- * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an
- * array of NCHUNKS+1 (X, Y) indexes giving the diving points between
- * sub sequences. The first sub-sequence is contained in [X0, X1),
- * [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note
- * that (X0, Y0) == (XOFF, YOFF) and
- * (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
- *
- * This function assumes that the first lines of the specified portions
- * of the two files do not match, and likewise that the last lines do not
- * match. The caller must trim matching lines from the beginning and end
- * of the portions it is going to specify.
- */
- function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
- {
- $flip = false;
-
- if ($xlim - $xoff > $ylim - $yoff)
- {
- // Things seems faster (I'm not sure I understand why)
- // when the shortest sequence in X.
- $flip = true;
- list ($xoff, $xlim, $yoff, $ylim) = array( $yoff, $ylim, $xoff, $xlim);
- }
-
- if ($flip)
- {
- for ($i = $ylim - 1; $i >= $yoff; $i--)
- {
- $ymatches[$this->xv[$i]][] = $i;
- }
- }
- else
- {
- for ($i = $ylim - 1; $i >= $yoff; $i--)
- {
- $ymatches[$this->yv[$i]][] = $i;
- }
- }
-
- $this->lcs = 0;
- $this->seq[0]= $yoff - 1;
- $this->in_seq = array();
- $ymids[0] = array();
-
- $numer = $xlim - $xoff + $nchunks - 1;
- $x = $xoff;
-
- for ($chunk = 0; $chunk < $nchunks; $chunk++)
- {
- if ($chunk > 0)
- {
- for ($i = 0; $i <= $this->lcs; $i++)
- {
- $ymids[$i][$chunk-1] = $this->seq[$i];
- }
- }
-
- $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
-
- for ( ; $x < $x1; $x++)
- {
- $_index = $flip ? $this->yv[$x] : $this->xv[$x];
- $matches = (isset($ymatches[$_index])) ? $ymatches[$_index] : array();
-
- if (!$matches)
- {
- continue;
- }
- reset($matches);
-
- while (list ($junk, $y) = each($matches))
- {
- if (!isset($this->in_seq[$y]) || !$this->in_seq[$y])
- {
- $k = $this->_lcs_pos($y);
- //if (!$k) die('assertion "!$k" failed');
- $ymids[$k] = $ymids[$k-1];
- break;
- }
- }
-
- while (list ($junk, $y) = each($matches))
- {
- if ($y > $this->seq[$k-1])
- {
- //if ($y >= $this->seq[$k]) die('assertion failed');
- // Optimization: this is a common case:
- // next match is just replacing previous match.
- $this->in_seq[$this->seq[$k]] = false;
- $this->seq[$k] = $y;
- $this->in_seq[$y] = 1;
- }
- else if (!isset($this->in_seq[$y]) || !$this->in_seq[$y])
- {
- $k = $this->_lcs_pos($y);
- //if (!$k) die('assertion "!$k" failed');
- $ymids[$k] = $ymids[$k-1];
- }
- }
- }
- }
-
- $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
- $ymid = $ymids[$this->lcs];
-
- for ($n = 0; $n < $nchunks - 1; $n++)
- {
- $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
- $y1 = $ymid[$n] + 1;
- $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
- }
- $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
-
- return array($this->lcs, $seps);
- }
-
- function _lcs_pos ($ypos)
- {
- $end = $this->lcs;
- if ($end == 0 || $ypos > $this->seq[$end])
- {
- $this->seq[++$this->lcs] = $ypos;
- $this->in_seq[$ypos] = 1;
- return $this->lcs;
- }
-
- $beg = 1;
- while ($beg < $end)
- {
- $mid = (int)(($beg + $end) / 2);
-
- if ($ypos > $this->seq[$mid])
- {
- $beg = $mid + 1;
- }
- else
- {
- $end = $mid;
- }
- }
-
- //if ($ypos == $this->seq[$end]) die("assertion failure");
-
- $this->in_seq[$this->seq[$end]] = false;
- $this->seq[$end] = $ypos;
- $this->in_seq[$ypos] = 1;
- return $end;
- }
-
- /* Find LCS of two sequences.
- *
- * The results are recorded in the vectors $this->{x,y}changed[], by
- * storing a 1 in the element for each line that is an insertion
- * or deletion (ie. is not in the LCS).
- *
- * The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
- *
- * Note that XLIM, YLIM are exclusive bounds.
- * All line numbers are origin-0 and discarded lines are not counted.
- */
- function _compareseq ($xoff, $xlim, $yoff, $ylim)
- {
- // Slide down the bottom initial diagonal.
- while ($xoff < $xlim && $yoff < $ylim && $this->xv[$xoff] == $this->yv[$yoff])
- {
- ++$xoff;
- ++$yoff;
- }
-
- // Slide up the top initial diagonal.
- while ($xlim > $xoff && $ylim > $yoff && $this->xv[$xlim - 1] == $this->yv[$ylim - 1])
- {
- --$xlim;
- --$ylim;
- }
-
- if ($xoff == $xlim || $yoff == $ylim)
- {
- $lcs = 0;
- }
- else
- {
- // This is ad hoc but seems to work well.
- //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
- //$nchunks = max(2,min(8,(int)$nchunks));
- $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
- list ($lcs, $seps) = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
- }
-
- if ($lcs == 0)
- {
- // X and Y sequences have no common subsequence:
- // mark all changed.
- while ($yoff < $ylim)
- {
- $this->ychanged[$this->yind[$yoff++]] = 1;
- }
-
- while ($xoff < $xlim)
- {
- $this->xchanged[$this->xind[$xoff++]] = 1;
- }
- }
- else
- {
- // Use the partitions to split this problem into subproblems.
- reset($seps);
-
- $pt1 = $seps[0];
-
- while ($pt2 = next($seps))
- {
- $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
- $pt1 = $pt2;
- }
- }
- }
-
- /* Adjust inserts/deletes of identical lines to join changes
- * as much as possible.
- *
- * We do something when a run of changed lines include a
- * line at one end and have an excluded, identical line at the other.
- * We are free to choose which identical line is included.
- * `compareseq' usually chooses the one at the beginning,
- * but usually it is cleaner to consider the following identical line
- * to be the "change".
- *
- * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
- */
- function _shift_boundaries ($lines, &$changed, $other_changed)
- {
- $i = 0;
- $j = 0;
- $len = sizeof($lines);
-
- while (1)
- {
- /*
- * Scan forwards to find beginning of another run of changes.
- * Also keep track of the corresponding point in the other file.
- */
-
- while ($i < $len && $changed[$i] == 0)
- {
- while ($other_changed[$j++])
- {
- continue;
- }
- $i++;
- }
-
- if ($i == $len)
- {
- break;
- }
-
- $start = $i;
-
- // Find the end of this run of changes.
- while (isset($changed[++$i]))
- {
- continue;
- }
-
- while (isset($other_changed[$j]) && $other_changed[$j])
- {
- $j++;
- }
-
- do
- {
- /*
- * Record the length of this run of changes, so that
- * we can later determine whether the run has grown.
- */
- $runlength = $i - $start;
-
- /*
- * Move the changed region back, so long as the
- * previous unchanged line matches the last changed one.
- * This merges with previous changed regions.
- */
- while ($start && $lines[$start - 1] == $lines[$i - 1])
- {
- $changed[--$start] = 1;
- $changed[--$i] = false;
-
- while ($changed[$start - 1])
- {
- $start--;
- }
-
- while ($other_changed[--$j])
- {
- continue;
- }
- }
-
- /*
- * Set CORRESPONDING to the end of the changed run, at the last
- * point where it corresponds to a changed run in the other file.
- * CORRESPONDING == LEN means no such point has been found.
- */
- $corresponding = empty($other_changed[$j - 1]) ? $len : $i;
-
- /*
- * Move the changed region forward, so long as the
- * first changed line matches the following unchanged one.
- * This merges with following changed regions.
- * Do this second, so that if there are no merges,
- * the changed region is moved forward as far as possible.
- */
- while ($i != $len && $lines[$start] == $lines[$i])
- {
- $changed[$start++] = false;
- $changed[$i++] = 1;
-
- while ($changed[$i])
- {
- $i++;
- }
-
- while ($other_changed[++$j])
- {
- $corresponding = $i;
- }
- }
- } while ($runlength != $i - $start);
-
- /*
- * If possible, move the fully-merged run of changes
- * back to a corresponding run in the other file.
- */
- while ($corresponding < $i)
- {
- $changed[--$start] = 1;
- $changed[--$i] = 0;
-
- while ($other_changed[--$j])
- {
- continue;
- }
- }
- }
- }
-}
-
-/**
-* Class representing a diff between two files.
-*/
-class Diff
-{
- var $edits;
-
- /**
- * Compute diff between files (or deserialize serialized WikiDiff.)
- */
- function Diff($from_lines = false, $to_lines = false)
- {
- if ($from_lines && $to_lines)
- {
- $compute = new _WikiDiffEngine($from_lines, $to_lines);
- $this->edits = $compute->edits;
- }
- else if ($from_lines)
- {
- // $from_lines is not really from_lines, but rather
- // a serialized Diff.
- $this->edits = unserialize($from_lines);
- }
- else
- {
- $this->edits = array();
- }
- }
-
- /**
- * Compute reversed Diff.
- *
- * SYNOPSIS:
- *
- * $diff = new Diff($lines1, $lines2);
- * $rev = $diff->reverse($lines1);
- *
- * // reconstruct $lines1 from $lines2:
- * $out = $rev->apply($lines2);
- */
- function reverse ($from_lines)
- {
- $x = 0;
- $rev = new Diff;
-
- for (reset($this->edits); $edit = current($this->edits); next($this->edits))
- {
- if (is_array($edit))
- { // Was an add, turn it into a delete.
- $nadd = sizeof($edit);
- if ($nadd == 0)
- {
- die("assertion error");
- }
- $edit = -$nadd;
- }
- else if ($edit > 0)
- {
- // Was a copy --- just pass it through. }
- $x += $edit;
- }
- else if ($edit < 0)
- { // Was a delete, turn it into an add.
- $ndelete = -$edit;
- $edit = array();
-
- while ($ndelete-- > 0)
- {
- $edit[] = $from_lines[$x++];
- }
- }
- else
- {
- die("assertion error");
- }
-
- $rev->edits[] = $edit;
- }
-
- return $rev;
- }
-
- /**
- * Compose (concatenate) Diffs.
- *
- * SYNOPSIS:
- *
- * $diff1 = new Diff($lines1, $lines2);
- * $diff2 = new Diff($lines2, $lines3);
- * $comp = $diff1->compose($diff2);
- *
- * // reconstruct $lines3 from $lines1:
- * $out = $comp->apply($lines1);
- */
- function compose ($that)
- {
- reset($this->edits);
- reset($that->edits);
-
- $comp = new Diff;
- $left = current($this->edits);
- $right = current($that->edits);
-
- while ($left || $right)
- {
- if (!is_array($left) && $left < 0)
- { // Left op is a delete.
- $newop = $left;
- $left = next($this->edits);
- }
- else if (is_array($right))
- { // Right op is an add.
- $newop = $right;
- $right = next($that->edits);
- }
- else if (!$left || !$right)
- {
- die ("assertion error");
- }
- else if (!is_array($left) && $left > 0)
- { // Left op is a copy.
- if ($left <= abs($right))
- {
- $newop = $right > 0 ? $left : -$left;
- $right -= $newop;
-
- if ($right == 0)
- {
- $right = next($that->edits);
- }
- $left = next($this->edits);
- }
- else
- {
- $newop = $right;
- $left -= abs($right);
- $right = next($that->edits);
- }
- }
- else
- { // Left op is an add.
- if (!is_array($left))
- {
- die('assertion error');
- }
- $nleft = sizeof($left);
-
- if ($nleft <= abs($right))
- {
- if ($right > 0)
- { // Right op is copy
- $newop = $left;
- $right -= $nleft;
- }
- else // Right op is delete
- {
- $newop = false;
- $right += $nleft;
- }
-
- if ($right == 0)
- {
- $right = next($that->edits);
- }
- $left = next($this->edits);
- }
- else
- {
- unset($newop);
-
- if ($right > 0)
- {
- for ($i = 0; $i < $right; $i++)
- {
- $newop[] = $left[$i];
- }
- }
-
- $tmp = array();
- for ($i = abs($right); $i < $nleft; $i++)
- {
- $tmp[] = $left[$i];
- }
- $left = $tmp;
- $right = next($that->edits);
- }
- }
-
- if (!$op)
- {
- $op = $newop;
- continue;
- }
-
- if (!$newop)
- {
- continue;
- }
-
- if (is_array($op) && is_array($newop))
- {
- // Both $op and $newop are adds.
- for ($i = 0; $i < sizeof($newop); $i++)
- {
- $op[] = $newop[$i];
- }
- }
- else if (($op > 0 && $newop > 0) || ($op < 0 && $newop < 0))
- { // $op and $newop are both either deletes or copies.
- $op += $newop;
- }
- else
- {
- $comp->edits[] = $op;
- $op = $newop;
- }
- }
-
- if ($op)
- {
- $comp->edits[] = $op;
- }
-
- return $comp;
- }
-
- /* Debugging only:
- function _dump ()
- {
- echo "<ol>";
- for (reset($this->edits);
- {
- $edit = current($this->edits);
- }
- next($this->edits))
- {
- echo "<li>";
- if ($edit > 0)
- echo "Copy $edit";
- else if ($edit < 0)
- echo "Delete " . -$edit;
- else if (is_array($edit))
- {
- echo "Add " . sizeof($edit) . "<ul>";
- for ($i = 0; $i < sizeof($edit); $i++)
- echo "<li>" . htmlspecialchars($edit[$i]);
- echo "</ul>";
- }
- else
- die("assertion error");
- }
- echo "</ol>";
- }
- */
-
- /**
- * Apply a Diff to a set of lines.
- *
- * SYNOPSIS:
- *
- * $diff = new Diff($lines1, $lines2);
- *
- * // reconstruct $lines2 from $lines1:
- * $out = $diff->apply($lines1);
- */
- function apply ($from_lines)
- {
- $x = 0;
- $xlim = sizeof($from_lines);
-
- for (reset($this->edits); $edit = current($this->edits); next($this->edits))
- {
- if (is_array($edit))
- {
- reset($edit);
- while (list ($junk, $line) = each($edit))
- {
- $output[] = $line;
- }
- }
- else if ($edit > 0)
- {
- while ($edit--)
- {
- $output[] = $from_lines[$x++];
- }
- }
- else
- {
- $x += -$edit;
- }
- }
-
- if ($x != $xlim)
- {
- die(sprintf("Diff::apply: line count mismatch: %s != %s", $x, $xlim));
- }
-
- return $output;
- }
-
- /**
- * Serialize a Diff.
- *
- * SYNOPSIS:
- *
- * $diff = new Diff($lines1, $lines2);
- * $string = $diff->serialize;
- *
- * // recover Diff from serialized version:
- * $diff2 = new Diff($string);
- */
- function serialize ()
- {
- return serialize($this->edits);
- }
-
- /**
- * Return true if two files were equal.
- */
- function isEmpty()
- {
- if (sizeof($this->edits) > 1)
- {
- return false;
- }
-
- if (sizeof($this->edits) == 0)
- {
- return true;
- }
-
- // Test for: only edit is a copy.
- return !is_array($this->edits[0]) && $this->edits[0] > 0;
- }
-
- /**
- * Compute the length of the Longest Common Subsequence (LCS).
- *
- * This is mostly for diagnostic purposed.
- */
- function lcs()
- {
- $lcs = 0;
- for (reset($this->edits); $edit = current($this->edits); next($this->edits))
- {
- if (!is_array($edit) && $edit > 0)
- {
- $lcs += $edit;
- }
- }
-
- return $lcs;
- }
-
- /**
- * Check a Diff for validity.
- *
- * This is here only for debugging purposes.
- */
- function _check ($from_lines, $to_lines)
- {
- $test = $this->apply($from_lines);
- if (serialize($test) != serialize($to_lines))
- {
- die("Diff::_check: failed");
- }
-
- reset($this->edits);
- $prev = current($this->edits);
- $prevtype = is_array($prev) ? 'a' : ($prev > 0 ? 'c' : 'd');
-
- while ($edit = next($this->edits))
- {
- $type = is_array($edit) ? 'a' : ($edit > 0 ? 'c' : 'd');
- if ($prevtype == $type)
- {
- die("Diff::_check: edit sequence is non-optimal");
- }
- $prevtype = $type;
- }
- $lcs = $this->lcs();
- echo "<strong>Diff Okay: LCS = $lcs</strong>\n";
- }
-}
-
-/**
-* A class to format a Diff as HTML.
-*
-* Usage:
-*
-* $diff = new Diff($lines1, $lines2); // compute diff.
-*
-* $fmt = new DiffFormatter;
-* echo $fmt->format($diff, $lines1); // Output HTMLified standard diff.
-*
-* or to output reverse diff (diff's that would take $lines2 to $lines1):
-*
-* $fmt = new DiffFormatter(true);
-* echo $fmt->format($diff, $lines1);
-*/
-class DiffFormatter
-{
- var $context_lines;
- var $do_reverse_diff;
- var $context_prefix, $deletes_prefix, $adds_prefix;
-
- function DiffFormatter ($reverse = false)
- {
- $this->do_reverse_diff = $reverse;
- $this->context_lines = 0;
- $this->context_prefix = '&nbsp;&nbsp;';
- $this->deletes_prefix = '&lt;&nbsp;';
- $this->adds_prefix = '&gt;&nbsp;';
- }
-
- function format ($diff, $from_lines)
- {
- $html = '<table width="100%" bgcolor="black" cellspacing=2 cellpadding=2 border=0>' . "\n";
- $html .= $this->_format($diff->edits, $from_lines);
- $html .= "</table>\n";
-
- return $html;
- }
-
- function _format ($edits, $from_lines)
- {
- $html = '';
- $x = 0; $y = 0;
- $xlim = sizeof($from_lines);
-
- reset($edits);
- while ($edit = current($edits))
- {
- if (!is_array($edit) && $edit >= 0)
- { // Edit op is a copy.
- $ncopy = $edit;
- }
- else
- {
- $ncopy = 0;
-
- if (empty($hunk))
- {
- // Start of an output hunk.
- $xoff = max(0, $x - $this->context_lines);
- $yoff = $xoff + $y - $x;
-
- if ($xoff < $x)
- {
- // Get leading context.
- $context = array();
-
- for ($i = $xoff; $i < $x; $i++)
- {
- $context[] = $from_lines[$i];
- }
- $hunk['c'] = $context;
- }
- }
-
- if (is_array($edit))
- {
- // Edit op is an add.
- $y += sizeof($edit);
- $hunk[$this->do_reverse_diff ? 'd' : 'a'] = $edit;
- }
- else
- {
- // Edit op is a delete
- $deletes = array();
-
- while ($edit++ < 0)
- {
- $deletes[] = $from_lines[$x++];
- }
-
- $hunk[$this->do_reverse_diff ? 'a' : 'd'] = $deletes;
- }
- }
-
- $next = next($edits);
-
- if (!empty($hunk))
- {
- // Originally $ncopy > 2 * $this->context_lines, but we need to split as early as we can for creating MOD Text Templates. ;)
- if (!$next || $ncopy > $this->context_lines)
- {
- // End of an output hunk.
- $hunks[] = $hunk;
- unset($hunk);
-
- $xend = min($x + $this->context_lines, $xlim);
-
- if ($x < $xend)
- {
- // Get trailing context.
- $context = array();
- for ($i = $x; $i < $xend; $i++)
- {
- $context[] = $from_lines[$i];
- }
- $hunks[] = array('c' => $context);
- }
-
- $xlen = $xend - $xoff;
- $ylen = $xend + $y - $x - $yoff;
- $xbeg = $xlen ? $xoff + 1 : $xoff;
- $ybeg = $ylen ? $yoff + 1 : $yoff;
-
- if ($this->do_reverse_diff)
- {
- list ($xbeg, $xlen, $ybeg, $ylen) = array($ybeg, $ylen, $xbeg, $xlen);
- }
-
- $html .= $this->_emit_diff($xbeg, $xlen, $ybeg, $ylen, $hunks);
- unset($hunks);
- }
- else if ($ncopy)
- {
- $hunks[] = $hunk;
-
- // Copy context.
- $context = array();
- for ($i = $x; $i < $x + $ncopy; $i++)
- {
- $context[] = $from_lines[$i];
- }
- $hunk = array('c' => $context);
- }
- }
-
- $x += $ncopy;
- $y += $ncopy;
- }
-
- return $html;
- }
-
- function _emit_lines($lines, $prefix, $color)
- {
- $html = '';
- reset($lines);
- while (list ($junk, $line) = each($lines))
- {
- $html .= "<tr bgcolor=\"$color\"><td><tt>$prefix</tt>";
- $html .= "<tt>" . htmlspecialchars($line) . "</tt></td></tr>\n";
- }
- return $html;
- }
-
- function _emit_diff ($xbeg,$xlen,$ybeg,$ylen,$hunks)
- {
- // Save hunk...
- $this->diff_hunks[] = $hunks;
-
- $html = '<tr><td><table width="100%" bgcolor="white" cellspacing="0" border="0" cellpadding="4">
- <tr bgcolor="#cccccc"><td><tt>' .
- $this->_diff_header($xbeg, $xlen, $ybeg, $ylen) . '
- </tt></td></tr>\n<tr><td>
- <table width="100%" cellspacing="0" border="0" cellpadding="2">
- ';
-
- $prefix = array('c' => $this->context_prefix, 'a' => $this->adds_prefix, 'd' => $this->deletes_prefix);
- $color = array('c' => '#ffffff', 'a' => '#ffcccc', 'd' => '#ccffcc');
-
- for (reset($hunks); $hunk = current($hunks); next($hunks))
- {
- if (!empty($hunk['c']))
- {
- $html .= $this->_emit_lines($hunk['c'], $this->context_prefix, '#ffffff');
- }
-
- if (!empty($hunk['d']))
- {
- $html .= $this->_emit_lines($hunk['d'], $this->deletes_prefix, '#ccffcc');
- }
-
- if (!empty($hunk['a']))
- {
- $html .= $this->_emit_lines($hunk['a'], $this->adds_prefix, '#ffcccc');
- }
- }
-
- $html .= "</table></td></tr></table></td></tr>\n";
- return $html;
- }
-
- function _diff_header ($xbeg,$xlen,$ybeg,$ylen)
- {
- $what = $xlen ? ($ylen ? 'c' : 'd') : 'a';
- $xlen = $xlen > 1 ? "," . ($xbeg + $xlen - 1) : '';
- $ylen = $ylen > 1 ? "," . ($ybeg + $ylen - 1) : '';
-
- return "$xbeg$xlen$what$ybeg$ylen";
- }
-}
-
-/**
-* A class to format a Diff as a pretty HTML unified diff.
-*
-* Usage:
-*
-* $diff = new Diff($lines1, $lines2); // compute diff.
-*
-* $fmt = new UnifiedDiffFormatter;
-* echo $fmt->format($diff, $lines1); // Output HTMLified unified diff.
-*/
-class UnifiedDiffFormatter extends DiffFormatter
-{
- function UnifiedDiffFormatter ($reverse = false, $context_lines = 3)
- {
- $this->do_reverse_diff = $reverse;
- $this->context_lines = $context_lines;
- $this->context_prefix = '&nbsp;';
- $this->deletes_prefix = '-';
- $this->adds_prefix = '+';
- }
-
- function _diff_header ($xbeg,$xlen,$ybeg,$ylen)
- {
- $xlen = $xlen == 1 ? '' : ",$xlen";
- $ylen = $ylen == 1 ? '' : ",$ylen";
-
- return "@@ -$xbeg$xlen +$ybeg$ylen @@";
- }
-}
-
-/**
-* A class to format a Diff as MOD Template instuctions.
-*
-* Usage:
-*
-* $diff = new Diff($lines1, $lines2); // compute diff.
-*
-* $fmt = new BBCodeDiffFormatter;
-* echo $fmt->format($diff, $lines1); // Output MOD Actions.
-*/
-class BBCodeDiffFormatter extends DiffFormatter
-{
- function BBCodeDiffFormatter ($reverse = false, $context_lines = 3, $debug = false)
- {
- $this->do_reverse_diff = $reverse;
- $this->context_lines = $context_lines;
- $this->context_prefix = '&nbsp;';
- $this->deletes_prefix = '-';
- $this->adds_prefix = '+';
- $this->debug = $debug;
- }
-
- function format ($diff, $from_lines)
- {
- $html = $this->_format($diff->edits, $from_lines);
-
- return $html;
- }
-
- function skip_lines(&$order_array, &$ordering)
- {
- if (sizeof($order_array['find_c']))
- {
- $text = implode('', $order_array['find_c']);
- if ($text === "\n" || $text === "\t" || $text === '')
- {
- if (isset($order_array['first_find_c'][0]) &&
- is_array($order_array['first_find_c'][0]) &&
- trim(implode('', $order_array['first_find_c'][0])) != '' &&
- isset($order_array['replace']))
- {
- $order_array['add'] = $order_array['replace'];
- unset($order_array['replace']);
- // this is actually an after add
- }
- else
- {
- return true;
- }
- }
- }
-
- if (isset($order_array['add']) && sizeof($order_array['add']))
- {
- $text = implode('', $order_array['add']);
- if ($text === "\n" || $text === "\t" || $text === '')
- {
- return true;
- }
- }
-
- if (isset($order_array['replace']) && sizeof($order_array['replace']))
- {
- $text = implode('', $order_array['replace']);
- if ($text === "\n" || $text === "\t" || $text === '')
- {
- return true;
- }
- }
- }
-
- function _emit_lines_bb($ybeg, &$ordering)
- {
- $html = '';
-
- // Now adjust for bbcode display...
- foreach ($ordering as $order_array)
- {
- // Skip useless empty lines...
- if ($this->skip_lines($order_array, $ordering))
- {
- continue;
- }
-
- // Only include double-finds if the last find has very few code location indications...
- if (isset($order_array['first_find_c']) && sizeof($order_array['first_find_c']))
- {
- $text = implode('', $order_array['find_c']);
- if ($text === "\n" || $text === "\t" || $text === '')
- {
- // no real find, use first_find_c if possible!
- //var_dump($order_array);
- if (is_array($order_array['first_find_c'][0]))
- {
- $order_array['find_c'] = $order_array['first_find_c'][0];
- }
- else
- {
- if (isset($order_array['replace']) || isset($order_array['add']) || isset($order_array['delete']))
- {
- echo "skipped an edit!\n";
- var_dump($order_array);
- }
- continue;
- }
- }
- else
- {
- if (strlen(implode('', $order_array['find_c'])) < 50 && is_array($order_array['first_find_c'][0]))
- {
- $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n";
- $html .= implode("", $order_array['first_find_c'][0]);
- $html .= "\n";
- $ybeg += sizeof($order_array['first_find_c'][0]);
- }
- }
- }
-
- // still here but nothing to do? what the heck?
- if (!isset($order_array['replace']) && !isset($order_array['add']) && !isset($order_array['delete']))
- {
- echo "skipped an edit!\n";
- var_dump($order_array);
- continue;
- }
-
- if (sizeof($order_array['find_c']))
- {
- $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n";
- $html .= implode("", $order_array['find_c']);
- $html .= "\n";
- }
-
- if (isset($order_array['replace']))
- {
- $html .= "#\n#-----[ REPLACE WITH ]---------------------------------------------\n#\n";
- $html .= implode("", $order_array['replace']);
- $html .= "\n";
- }
-
- if (isset($order_array['add']))
- {
- $html .= "#\n#-----[ AFTER, ADD ]---------------------------------------------\n#\n";
- $html .= implode("", $order_array['add']);
- $html .= "\n";
- }
-
- // There is no DELETE. :o
- // Let's try to adjust it then...
- if (isset($order_array['delete']))
- {
- $text = implode('', $order_array['delete']);
- if ($text === "\n" || $text === "\t" || $text === '')
- {
- continue;
- }
-
- $ybeg += sizeof($order_array['find_c']);
-
- $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n";
- $html .= implode("", $order_array['delete']);
- $html .= "\n";
-
- $html .= "#\n#-----[ REPLACE WITH ]---------------------------------------------\n# Just remove/delete the lines (replacing with an empty line)\n";
- $html .= "\n";
- $html .= "\n";
- }
- }
-
- return $html;
- }
-
- function format_open($filename)
- {
- $html = '';
- $html .= "#\n#-----[ OPEN ]--------------------------------------------- \n#\n{$filename}\n\n";
-
- return $html;
- }
-
- function format_close($filename)
- {
- return '';
- }
-
- function _emit_diff ($xbeg, $xlen, $ybeg, $ylen, $hunks)
- {
-
- // Go through the hunks to determine which method we are using (AFTER, ADD; REPLACE WITH or DELETE)
-
- // Remove the header...
- if (sizeof($hunks) <= 2 && !isset($hunks[1]['a']) && !isset($hunks[1]['d']))
- {
- $reorder = false;
- $orig_hunks = $hunks;
-
- foreach ($hunks as $key => $hunk)
- {
- if (isset($hunk['a']) && isset($hunk['d']))
- {
- /**/ if (sizeof($hunk['a']) == 1 && sizeof($hunk['d']) == 1)
- {
- if (preg_match('/\* @version \$Id:.+\$$/', $hunk['a'][0]) && preg_match('/\* @version \$Id:.+\$$/', $hunk['d'][0]))
- {
- // Only remove this sole hunk...
- unset($hunks[$key]);
- $reorder = true;
- continue;
- }
- }/**/
-
- // Compare the add and replace one...
- $string_1 = rtrim(trim(implode('', $hunk['a'])));
- $string_2 = rtrim(trim(implode('', $hunk['d'])));
-
- if (strcmp($string_1, $string_2) === 0)
- {
- // Only remove this sole hunk...
- unset($hunks[$key]);
- $reorder = true;
- continue;
- }
- }
- }
-
- if ($reorder)
- {
- // Now check if we have no more 'a' and 'd's
- $hunks = array_merge($hunks, array());
- }
- }
- else
- {
- $reorder = false;
- $orig_hunks = $hunks;
-
- foreach ($hunks as $key => $hunk)
- {
- if (isset($hunk['a']) && isset($hunk['d']))
- {
- /**/ if (sizeof($hunk['a']) == 1 && sizeof($hunk['d']) == 1)
- {
- if (preg_match('/\* @version \$Id:.+\$$/', $hunk['a'][0]) && preg_match('/\* @version \$Id:.+\$$/', $hunk['d'][0]))
- {
- // Only remove this sole hunk...
- unset($hunks[$key]);
- $reorder = true;
- continue;
- }
- }/**/
-
- // Compare the add and replace one...
- $string_1 = rtrim(trim(implode('', $hunk['a'])));
- $string_2 = rtrim(trim(implode('', $hunk['d'])));
-
- if (strcmp($string_1, $string_2) === 0)
- {
- // Only remove this sole hunk...
- unset($hunks[$key]);
- $reorder = true;
- continue;
- }
- }
- }
-
- if ($reorder)
- {
- $hunks = array_merge($hunks, array());
-
- if (sizeof($hunks) == 1 && sizeof($hunks[0]) == 1 && isset($hunks[0]['c']))
- {
- return;
- }
- else
- {
- $hunks = $orig_hunks;
- }
- }
- }
-
- if (sizeof($hunks) == 1 && sizeof($hunks[0]) == 1 && isset($hunks[0]['c']))
- {
- return;
- }
-
- $replace = false;
- foreach ($hunks as $key => $hunk)
- {
- if (isset($hunk['d']) && isset($hunk['a']))
- {
- $replace = true;
- break;
- }
- }
-
- $ordering = array();
- $cur_pos = 0;
-
- // Replace-block
- if ($replace)
- {
- foreach ($hunks as $hunk)
- {
- if (!isset($hunk['a']) && !isset($hunk['d']))
- {
- continue;
- }
-
- if (!empty($hunk['c']))
- {
- if (!isset($ordering[$cur_pos]['find_c']))
- {
- $ordering[$cur_pos]['find_c'] = $hunk['c'];
- }
- else
- {
- $ordering[$cur_pos]['end_c'] = $hunk['c'];
- }
- }
-
- // Make sure we begin fresh...
- if (!isset($ordering[$cur_pos]['replace']))
- {
- $ordering[$cur_pos]['first_find_c'][] = $ordering[$cur_pos]['find_c'];
- $ordering[$cur_pos]['find_c'] = array();
- $ordering[$cur_pos]['replace'] = array();
- }
-
- // Add the middle part if one exist...
- if (isset($ordering[$cur_pos]['end_c']))
- {
- $ordering[$cur_pos]['find_c'] = array_merge($ordering[$cur_pos]['find_c'], $ordering[$cur_pos]['end_c']);
- $ordering[$cur_pos]['replace'] = array_merge($ordering[$cur_pos]['replace'], $ordering[$cur_pos]['end_c']);
- unset($ordering[$cur_pos]['end_c']);
- }
-
- if (isset($hunk['d']))
- {
- $ordering[$cur_pos]['find_c'] = array_merge($ordering[$cur_pos]['find_c'], $hunk['d']);
- }
-
- if (isset($hunk['a']))
- {
- $ordering[$cur_pos]['replace'] = array_merge($ordering[$cur_pos]['replace'], $hunk['a']);
- }
- }
- }
- else
- {
- foreach ($hunks as $hunk)
- {
- if (!empty($hunk['c']))
- {
- if (!isset($ordering[$cur_pos]['find_c']))
- {
- $ordering[$cur_pos]['find_c'] = $hunk['c'];
- }
- else
- {
- $ordering[$cur_pos]['end_c'] = $hunk['c'];
- }
- }
-
- if (!empty($hunk['a']))
- {
- if (isset($ordering[$cur_pos]['delete']))
- {
- // If ordering is set with an delete entry, we will actually begin a new ordering array (to seperate delete from add)
- $cur_pos++;
- $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
- $ordering[$cur_pos]['add'] = $hunk['a'];
- }
- else
- {
- if (isset($ordering[$cur_pos]['add']))
- {
- // Now, we really need to be quite careful here...
- if (isset($ordering[$cur_pos]['end_c']) && isset($hunk['c']) && isset($hunk['a']) && sizeof($hunk) == 2)
- {
- // There is a new find/add entry we did not catch... let's try to add a new entry then... but first check the hunk[a] contents...
- $text = trim(implode("\n", $hunk['c']));
- if ($text == "\n" || !$text)
- {
- $ordering[$cur_pos]['add'] = array_merge($ordering[$cur_pos]['add'], array("\n"), $hunk['a']);
- }
- else if (sizeof($hunk['c']) > 2 || strlen(implode('', $hunk['c'])) > 20)
- {
- $cur_pos++;
- $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
- $ordering[$cur_pos]['add'] = $hunk['a'];
- }
- else
- {
- $cur_pos++;
- $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
- $ordering[$cur_pos]['add'] = $hunk['a'];
-/* echo 'FIND STATEMENT TOO TINY';
- echo ";".rawurlencode($text).";";
- var_dump($hunk);
- exit;*/
- }
- }
- else
- {
- echo 'UNCATCHED ENTRY';
- var_dump($hunks);
- exit;
- }
- }
- else
- {
- $ordering[$cur_pos]['add'] = $hunk['a'];
- }
- }
- }
- else if (!empty($hunk['d']))
- {
- if (isset($ordering[$cur_pos]['add']))
- {
- // If ordering is set with an add entry, we will actually begin a new ordering array (to seperate delete from add)
- $cur_pos++;
- $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
- $ordering[$cur_pos]['delete'] = $hunk['d'];
- }
- else
- {
- $ordering[$cur_pos]['delete'] = $hunk['d'];
- }
- }
- }
- }
-
- $html = '';
-
- return $this->_emit_lines_bb($ybeg, $ordering);
- }
-
- function _diff_header($xbeg, $xlen, $ybeg, $ylen)
- {
- }
-}
-
-
-/**
-* A class to format a Diff as MOD Template instuctions.
-*
-* Usage:
-*
-* $diff = new Diff($lines1, $lines2); // compute diff.
-*
-* $fmt = new BBCodeDiffFormatter;
-* echo $fmt->format($diff, $lines1); // Output MOD Actions.
-*/
-class MODXDiffFormatter extends BBCodeDiffFormatter
-{
- function MODXDiffFormatter ($reverse = false, $context_lines = 3, $debug = false)
- {
- $this->do_reverse_diff = $reverse;
- $this->context_lines = $context_lines;
- $this->context_prefix = '&nbsp;';
- $this->deletes_prefix = '-';
- $this->adds_prefix = '+';
- $this->debug = $debug;
- }
-
- function _emit_lines_bb($ybeg, &$ordering)
- {
- $html = '';
-
- // Now adjust for bbcode display...
- foreach ($ordering as $order_array)
- {
- // Skip useless empty lines...
- if ($this->skip_lines($order_array, $ordering))
- {
- continue;
- }
-
- // Only include double-finds if the last find has very few code location indications...
- if (sizeof($order_array['first_find_c']))
- {
- $text = implode('', $order_array['find_c']);
- if ($text === "\n" || $text === "\t" || $text === '')
- {
- continue;
- }
-
- if (strlen(implode('', $order_array['find_c'])) < 50)
- {
- if (substr($html, -8) !== '</find>' . "\n")
- {
- $html .= ' <edit>' . "\n";
- }
-
- $html .= ' <comment lang="en">Around Line ' . $ybeg . '</comment>' . "\n";
- $html .= ' <find>' . htmlspecialchars(implode('', $order_array['first_find_c'][0])) . '</find>' . "\n";
- $ybeg += sizeof($order_array['first_find_c'][0]);
- }
- }
-
- if (sizeof($order_array['find_c']))
- {
- if (substr($html, -8) !== '</find>' . "\n")
- {
- $html .= ' <edit>' . "\n";
- }
-
-// $html .= ' <edit>' . "\n";
- $html .= ' <comment lang="en">Around Line ' . $ybeg . '</comment>' . "\n";
- $html .= ' <find>' . htmlspecialchars(implode('', $order_array['find_c'])) . '</find>' . "\n";
- }
-
- if (isset($order_array['replace']))
- {
- $html .= ' <action type="replace-with">' . htmlspecialchars(implode('', $order_array['replace'])) . '</action>' . "\n";
- $html .= ' </edit>' . "\n";
- }
-
- if (isset($order_array['add']))
- {
- $html .= ' <action type="after-add">' . htmlspecialchars(implode('', $order_array['add'])) . '</action>' . "\n";
- $html .= ' </edit>' . "\n";
- }
-
- // There is no DELETE. :o
- // Let's try to adjust it then...
- if (isset($order_array['delete']))
- {
- $text = implode('', $order_array['delete']);
- if ($text === "\n" || $text === "\t" || $text === '')
- {
- continue;
- }
-
- $ybeg += sizeof($order_array['find_c']);
-
- if (substr($html, -8) !== '</find>' . "\n")
- {
- $html .= ' <edit>' . "\n";
- }
- $html .= ' <comment lang="en">Around Line ' . $ybeg . ' / Just remove/delete the lines (replacing with an empty line)</comment>' . "\n";
- $html .= ' <find>' . htmlspecialchars(implode('', $order_array['delete'])) . '</find>' . "\n";
- $html .= ' <action type="replace-with"></action>' . "\n";
- $html .= ' </edit>';
- }
- }
-
- return $html;
- }
-
- function format_open($filename)
- {
- return '<open src="' . $filename . '">' . "\n";
- }
-
- function format_close($filename)
- {
- return '</open>' . "\n";
- }
-
- function _diff_header($xbeg, $xlen, $ybeg, $ylen)
- {
- }
-}
diff --git a/build/package.php b/build/package.php
index d05448dfb4..97bfc4ea5c 100755
--- a/build/package.php
+++ b/build/package.php
@@ -174,6 +174,27 @@ if (sizeof($package->old_packages))
$package->run_command('cp ' . $source_filename . ' ' . $dest_filename);
}
+ /**
+ * We try to keep the update packages as small as possible while creating them.
+ * However, we sometimes need to include additional files that are not included
+ * in the diff in order to be able to correctly include the relatively
+ * referenced files from the same or subsequent directories.
+ */
+ $copy_relative_directories = array(
+ 'adm/style/admin.css' => array(
+ 'copied' => false,
+ 'copy' => array(
+ 'adm/images/*' => 'adm/images',
+ ),
+ ),
+ 'config/' => array(
+ 'copied' => false,
+ 'copy' => array(
+ 'config/*.yml' => 'config',
+ ),
+ ),
+ );
+
// Then fill the 'new' directory
foreach ($file_contents['all'] as $file)
{
@@ -185,6 +206,8 @@ if (sizeof($package->old_packages))
continue;
}
+ $filename = $file;
+
// Create Directories along the way?
$file = explode('/', $file);
// Remove filename portion
@@ -205,6 +228,37 @@ if (sizeof($package->old_packages))
}
$package->run_command('cp ' . $source_filename . ' ' . $dest_filename);
+
+ foreach ($copy_relative_directories as $reference => $data)
+ {
+ // Copy all relative referenced files if needed
+ if (strpos($filename, $reference) === 0 && !$data['copied'])
+ {
+ foreach ($data['copy'] as $source_dir_files => $destination_dir)
+ {
+ // Create directories along the way?
+ $directories = explode('/', $destination_dir);
+
+ chdir($dest_filename_dir . '/install/update/new');
+ foreach ($directories as $dir)
+ {
+ $dir = trim($dir);
+ if ($dir)
+ {
+ if (!file_exists('./' . $dir))
+ {
+ $package->run_command('mkdir ' . $dir);
+ }
+ chdir('./' . $dir);
+ }
+ }
+ $source_dir_files = $package->locations['old_versions'] . $package->get('simple_name') . '/' . $source_dir_files;
+ $destination_dir = $dest_filename_dir . '/install/update/new/' . $destination_dir;
+ $package->run_command('cp ' . $source_dir_files . ' ' . $destination_dir);
+ }
+ $copy_relative_directories[$reference]['copied'] = true;
+ }
+ }
}
// Build index.php file for holding the file structure
diff --git a/git-tools/commit-msg-hook-range.sh b/git-tools/commit-msg-hook-range.sh
new file mode 100755
index 0000000000..66628c1d17
--- /dev/null
+++ b/git-tools/commit-msg-hook-range.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# @copyright (c) 2014 phpBB Group
+# @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+#
+# Calls the git commit-msg hook on all non-merge commits in a given commit range.
+#
+set -e
+
+if [ "$#" -ne 1 ];
+then
+ echo "Expected one argument (commit range, e.g. eef1b586...1666476b)."
+ exit
+fi
+
+DIR=$(dirname "$0")
+COMMIT_MSG_HOOK_PATH="$DIR/hooks/commit-msg"
+
+COMMIT_RANGE="$1"
+
+for COMMIT_HASH in $(git rev-list --no-merges "$COMMIT_RANGE")
+do
+ # The git commit-msg hook takes a path to a file containing a commit
+ # message. So we have to extract the commit message into a file first,
+ # which then also needs to be deleted after our work is done.
+ COMMIT_MESSAGE_PATH="$DIR/commit_msg.$COMMIT_HASH"
+ git log -n 1 --pretty=format:%B "$COMMIT_HASH" > "$COMMIT_MESSAGE_PATH"
+ "$COMMIT_MSG_HOOK_PATH" "$COMMIT_MESSAGE_PATH"
+ rm "$COMMIT_MESSAGE_PATH"
+done
diff --git a/phpBB/adm/style/acp_attachments.html b/phpBB/adm/style/acp_attachments.html
index 9ce9fb5f13..9db0fc6521 100644
--- a/phpBB/adm/style/acp_attachments.html
+++ b/phpBB/adm/style/acp_attachments.html
@@ -331,7 +331,7 @@
<fieldset class="tabulated">
<legend>{L_TITLE}</legend>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_FILENAME}</th>
@@ -344,7 +344,7 @@
</thead>
<tbody>
<!-- BEGIN orphan -->
- <!-- IF orphan.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td><a href="{orphan.U_FILE}">{orphan.REAL_FILENAME}</a></td>
<td>{orphan.FILETIME}</td>
<td>{orphan.FILESIZE}</td>
@@ -389,7 +389,7 @@
<!-- ENDIF -->
</div>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_FILENAME}</th>
@@ -400,7 +400,7 @@
</thead>
<tbody>
<!-- BEGIN attachments -->
- <!-- IF attachments.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td>
<!-- IF attachments.S_IN_MESSAGE -->{L_EXTENSION_GROUP}{L_COLON} <strong><!-- IF attachments.EXT_GROUP_NAME -->{attachments.EXT_GROUP_NAME}<!-- ELSE -->{L_NO_EXT_GROUP}<!-- ENDIF --></strong><br />{attachments.L_DOWNLOAD_COUNT}<br />{L_IN} {L_PRIVATE_MESSAGE}
<!-- ELSE --><a href="{attachments.U_FILE}" style="font-weight: bold;">{attachments.REAL_FILENAME}</a><br /><!-- IF attachments.COMMENT -->{attachments.COMMENT}<br /><!-- ENDIF -->{attachments.L_DOWNLOAD_COUNT}<br />{L_TOPIC}{L_COLON} <a href="{attachments.U_VIEW_TOPIC}">{attachments.TOPIC_TITLE}</a><!-- ENDIF -->
diff --git a/phpBB/adm/style/acp_bbcodes.html b/phpBB/adm/style/acp_bbcodes.html
index d4ed5cfd10..8c8b805975 100644
--- a/phpBB/adm/style/acp_bbcodes.html
+++ b/phpBB/adm/style/acp_bbcodes.html
@@ -92,7 +92,7 @@
<fieldset class="tabulated">
<legend>{L_ACP_BBCODES}</legend>
- <table class="table1" id="down">
+ <table class="table1 zebra-table" id="down">
<thead>
<tr>
<th>{L_BBCODE_TAG}</th>
@@ -101,7 +101,7 @@
</thead>
<tbody>
<!-- BEGIN bbcodes -->
- <!-- IF bbcodes.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td style="text-align: center;">{bbcodes.BBCODE_TAG}</td>
<td class="actions"><!-- EVENT acp_bbcodes_actions_prepend --> <a href="{bbcodes.U_EDIT}">{ICON_EDIT}</a> <a href="{bbcodes.U_DELETE}" data-ajax="row_delete">{ICON_DELETE}</a> <!-- EVENT acp_bbcodes_actions_append --></td>
</tr>
diff --git a/phpBB/adm/style/acp_bots.html b/phpBB/adm/style/acp_bots.html
index eeda13303d..b0e61015b6 100644
--- a/phpBB/adm/style/acp_bots.html
+++ b/phpBB/adm/style/acp_bots.html
@@ -62,7 +62,7 @@
<form id="acp_bots" method="post" action="{U_ACTION}">
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_BOT_NAME}</th>
@@ -73,7 +73,7 @@
</thead>
<tbody>
<!-- BEGIN bots -->
- <!-- IF bots.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td style="width: 50%;">{bots.BOT_NAME}</td>
<td style="width: 15%; white-space: nowrap;" align="center">&nbsp;{bots.LAST_VISIT}&nbsp;</td>
<td style="text-align: center;">&nbsp;<a href="{bots.U_ACTIVATE_DEACTIVATE}" data-ajax="activate_deactivate">{bots.L_ACTIVATE_DEACTIVATE}</a>&nbsp;</td>
diff --git a/phpBB/adm/style/acp_groups.html b/phpBB/adm/style/acp_groups.html
index 925d24f3a3..72f4f2b239 100644
--- a/phpBB/adm/style/acp_groups.html
+++ b/phpBB/adm/style/acp_groups.html
@@ -155,7 +155,7 @@
<a href="{U_DEFAULT_ALL}">&raquo; {L_MAKE_DEFAULT_FOR_ALL}</a>
</fieldset>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_USERNAME}</th>
@@ -170,7 +170,7 @@
<td class="row3" colspan="5"><strong>{L_GROUP_LEAD}</strong></td>
</tr>
<!-- BEGIN leader -->
- <!-- IF leader.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td><!-- IF leader.USERNAME_COLOUR --><a href="{leader.U_USER_EDIT}" style="color: #{leader.USERNAME_COLOUR};" class="username-coloured">{leader.USERNAME}</a><!-- ELSE --><a href="{leader.U_USER_EDIT}">{leader.USERNAME}</a><!-- ENDIF --></td>
<td style="text-align: center;"><!-- IF leader.S_GROUP_DEFAULT -->{L_YES}<!-- ELSE -->{L_NO}<!-- ENDIF --></td>
<td style="text-align: center;">{leader.JOINED}</td>
@@ -179,7 +179,7 @@
</tr>
<!-- BEGINELSE -->
<tr>
- <td class="row1" colspan="5" style="text-align: center;">{L_GROUPS_NO_MODS}</td>
+ <td colspan="5" style="text-align: center;">{L_GROUPS_NO_MODS}</td>
</tr>
<!-- END leader -->
<tr>
@@ -191,7 +191,7 @@
<td class="row3" colspan="5"><strong>{L_GROUP_PENDING}</strong></td>
</tr>
<!-- ELSE -->
- <!-- IF member.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td><!-- IF member.USERNAME_COLOUR --><a href="{member.U_USER_EDIT}" style="color: #{member.USERNAME_COLOUR};" class="username-coloured">{member.USERNAME}</a><!-- ELSE --><a href="{member.U_USER_EDIT}">{member.USERNAME}</a><!-- ENDIF --></td>
<td style="text-align: center;"><!-- IF member.S_GROUP_DEFAULT -->{L_YES}<!-- ELSE -->{L_NO}<!-- ENDIF --></td>
<td style="text-align: center;">{member.JOINED}</td>
@@ -201,7 +201,7 @@
<!-- ENDIF -->
<!-- BEGINELSE -->
<tr>
- <td class="row1" colspan="5" style="text-align: center;">{L_GROUPS_NO_MEMBERS}</td>
+ <td colspan="5" style="text-align: center;">{L_GROUPS_NO_MEMBERS}</td>
</tr>
<!-- END member -->
</tbody>
diff --git a/phpBB/adm/style/acp_icons.html b/phpBB/adm/style/acp_icons.html
index bfe4878ba5..9117052d87 100644
--- a/phpBB/adm/style/acp_icons.html
+++ b/phpBB/adm/style/acp_icons.html
@@ -74,7 +74,7 @@
<fieldset class="tabulated">
<legend>{L_TITLE}</legend>
- <table class="table1" id="smilies">
+ <table class="table1 zebra-table" id="smilies">
<thead>
<tr>
<th colspan="{COLSPAN}">{L_CONFIG}</th>
@@ -100,7 +100,7 @@
</thead>
<tbody>
<!-- BEGIN items -->
- <!-- IF items.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td style="text-align: center;"><img src="{items.IMG_SRC}" alt="" title="" /><input type="hidden" name="image[{items.IMG}]" value="1" /></td>
<td style="vertical-align: top;">[{items.IMG}]</td>
@@ -218,7 +218,7 @@
<legend>{L_TITLE}</legend>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_TITLE}</th>
@@ -236,16 +236,18 @@
<td class="row3" colspan="{COLSPAN}" style="text-align: center;">{L_NOT_DISPLAYED}</td>
</tr>
<!-- ENDIF -->
- <!-- IF items.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td style="width: 85%; text-align: center;"><img src="{items.IMG_SRC}" width="{items.WIDTH}" height="{items.HEIGHT}" alt="{items.ALT_TEXT}" title="{items.ALT_TEXT}" /></td>
<!-- IF S_SMILIES -->
<td style="text-align: center;">{items.CODE}</td>
<td style="text-align: center;">{items.EMOTION}</td>
<!-- ENDIF -->
- <td style="text-align: right; white-space: nowrap;">
- <!-- IF items.S_FIRST_ROW and not U_PREVIOUS_PAGE -->{ICON_MOVE_UP_DISABLED}<!-- ELSE --><a href="{items.U_MOVE_UP}">{ICON_MOVE_UP}</a><!-- ENDIF -->&nbsp;
- <!-- IF items.S_LAST_ROW and not U_NEXT_PAGE -->{ICON_MOVE_DOWN_DISABLED}<!-- ELSE --><a href="{items.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a><!-- ENDIF -->
- &nbsp;<a href="{items.U_EDIT}">{ICON_EDIT}</a> <a href="{items.U_DELETE}" data-ajax="row_delete">{ICON_DELETE}</a>
+ <td class="actions" style="text-align: right;">
+ <span class="up-disabled" style="display:none;">{ICON_MOVE_UP_DISABLED}</span>
+ <span class="up"><a href="{items.U_MOVE_UP}" data-ajax="row_up">{ICON_MOVE_UP}</a></span>
+ <span class="down-disabled" style="display:none;">{ICON_MOVE_DOWN_DISABLED}</span>
+ <span class="down"><a href="{items.U_MOVE_DOWN}" data-ajax="row_down">{ICON_MOVE_DOWN}</a></span>
+ <a href="{items.U_EDIT}">{ICON_EDIT}</a> <a href="{items.U_DELETE}" data-ajax="row_delete">{ICON_DELETE}</a>
</td>
</tr>
<!-- BEGINELSE -->
diff --git a/phpBB/adm/style/acp_inactive.html b/phpBB/adm/style/acp_inactive.html
index 1c42c93f76..1cdc1abe6b 100644
--- a/phpBB/adm/style/acp_inactive.html
+++ b/phpBB/adm/style/acp_inactive.html
@@ -16,7 +16,7 @@
</div>
<!-- ENDIF -->
-<table class="table1">
+<table class="table1 zebra-table">
<thead>
<tr>
<th>{L_USERNAME}</th>
@@ -29,8 +29,7 @@
</thead>
<tbody>
<!-- BEGIN inactive -->
- <!-- IF inactive.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
-
+ <tr>
<td style="vertical-align: top;">
{inactive.USERNAME_FULL}
<!-- IF inactive.POSTS --><br />{L_POSTS}{L_COLON} <strong>{inactive.POSTS}</strong> [<a href="{inactive.U_SEARCH_USER}">{L_SEARCH_USER_POSTS}</a>]<!-- ENDIF -->
diff --git a/phpBB/adm/style/acp_language.html b/phpBB/adm/style/acp_language.html
index d4fd572376..d32f6b7eac 100644
--- a/phpBB/adm/style/acp_language.html
+++ b/phpBB/adm/style/acp_language.html
@@ -147,7 +147,7 @@
</tr>
<tr>
<td class="row3" style="text-align: right;">
- <!-- IF ALLOW_UPLOAD -->&nbsp;&nbsp;{L_UPLOAD_METHOD}{L_COLON}&nbsp;<!-- BEGIN buttons--><input type="radio" class="radio"<!-- IF buttons.S_FIRST_ROW --> id="method" checked="checked"<!-- ENDIF --> value="{buttons.VALUE}" name="method" />&nbsp;{buttons.VALUE}&nbsp;<!-- END buttons --><input type="submit" name="upload_file" class="button2" value="{L_SUBMIT_AND_UPLOAD}" /><!-- ENDIF --></td>
+ <!-- IF ALLOW_UPLOAD -->&nbsp;&nbsp;{L_UPLOAD_METHOD}{L_COLON}&nbsp;<!-- BEGIN buttons --><input type="radio" class="radio"<!-- IF buttons.S_FIRST_ROW --> id="method" checked="checked"<!-- ENDIF --> value="{buttons.VALUE}" name="method" />&nbsp;{buttons.VALUE}&nbsp;<!-- END buttons --><input type="submit" name="upload_file" class="button2" value="{L_SUBMIT_AND_UPLOAD}" /><!-- ENDIF --></td>
</tr>
</thead>
<tbody>
@@ -211,7 +211,7 @@
<p>{L_ACP_LANGUAGE_PACKS_EXPLAIN}</p>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_LANGUAGE_PACK_NAME}</th>
@@ -226,7 +226,7 @@
<td class="row3" colspan="5"><strong>{L_INSTALLED_LANGUAGE_PACKS}</strong></td>
</tr>
<!-- BEGIN lang -->
- <!-- IF lang.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td><a href="{lang.U_DETAILS}">{lang.ENGLISH_NAME}</a> {lang.TAG}</td>
<td>{lang.LOCAL_NAME}</td>
<td style="text-align: center;"><strong>{lang.ISO}</strong></td>
@@ -240,7 +240,7 @@
</tr>
<!-- ENDIF -->
<!-- BEGIN notinst -->
- <!-- IF notinst.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td>{notinst.NAME}</td>
<td>{notinst.LOCAL_NAME}</td>
<td style="text-align: center;"><strong>{notinst.ISO}</strong></td>
diff --git a/phpBB/adm/style/acp_logs.html b/phpBB/adm/style/acp_logs.html
index f22eb67a72..592b5bbc16 100644
--- a/phpBB/adm/style/acp_logs.html
+++ b/phpBB/adm/style/acp_logs.html
@@ -22,7 +22,7 @@
<div><br style="clear: both;" /></div>
<!-- IF .log -->
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_USERNAME}</th>
@@ -36,7 +36,7 @@
</thead>
<tbody>
<!-- BEGIN log -->
- <!-- IF log.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td>
{log.USERNAME}
<!-- IF log.REPORTEE_USERNAME -->
diff --git a/phpBB/adm/style/acp_main.html b/phpBB/adm/style/acp_main.html
index f5b030d954..6e28c7a0cc 100644
--- a/phpBB/adm/style/acp_main.html
+++ b/phpBB/adm/style/acp_main.html
@@ -222,7 +222,7 @@
<div style="text-align: right;"><a href="{U_ADMIN_LOG}">&raquo; {L_VIEW_ADMIN_LOG}</a></div>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_USERNAME}</th>
@@ -233,8 +233,7 @@
</thead>
<tbody>
<!-- BEGIN log -->
- <!-- IF log.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
-
+ <tr>
<td>{log.USERNAME}</td>
<td style="text-align: center;">{log.IP}</td>
<td style="text-align: center;">{log.DATE}</td>
@@ -255,7 +254,7 @@
<div style="text-align: right;"><a href="{U_INACTIVE_USERS}">&raquo; {L_VIEW_INACTIVE_USERS}</a></div>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_USERNAME}</th>
@@ -267,8 +266,7 @@
</thead>
<tbody>
<!-- BEGIN inactive -->
- <!-- IF inactive.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
-
+ <tr>
<td style="vertical-align: top;">
{inactive.USERNAME_FULL}
<!-- IF inactive.POSTS --><br />{L_POSTS}{L_COLON} <strong>{inactive.POSTS}</strong> [<a href="{inactive.U_SEARCH_USER}">{L_SEARCH_USER_POSTS}</a>]<!-- ENDIF -->
diff --git a/phpBB/adm/style/acp_modules.html b/phpBB/adm/style/acp_modules.html
index 5024a950b9..c7688a610c 100644
--- a/phpBB/adm/style/acp_modules.html
+++ b/phpBB/adm/style/acp_modules.html
@@ -151,9 +151,9 @@
<td style="width: 15%; white-space: nowrap; text-align: center; vertical-align: middle;">&nbsp;<!-- IF modules.MODULE_ENABLED --><a href="{modules.U_DISABLE}">{L_DISABLE}</a><!-- ELSE --><a href="{modules.U_ENABLE}">{L_ENABLE}</a><!-- ENDIF -->&nbsp;</td>
<td class="actions">
<span class="up-disabled" style="display:none;">{ICON_MOVE_UP_DISABLED}</span>
- <span class="up"><a href="{modules.U_MOVE_UP}">{ICON_MOVE_UP}</a></span>
+ <span class="up"><a href="{modules.U_MOVE_UP}" data-ajax="row_up">{ICON_MOVE_UP}</a></span>
<span class="down-disabled" style="display:none;">{ICON_MOVE_DOWN_DISABLED}</span>
- <span class="down"><a href="{modules.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a></span>
+ <span class="down"><a href="{modules.U_MOVE_DOWN}" data-ajax="row_down">{ICON_MOVE_DOWN}</a></span>
<a href="{modules.U_EDIT}">{ICON_EDIT}</a>
<a href="{modules.U_DELETE}" data-ajax="row_delete">{ICON_DELETE}</a>
</td>
diff --git a/phpBB/adm/style/acp_permission_roles.html b/phpBB/adm/style/acp_permission_roles.html
index b8fdaeb837..b3137f134c 100644
--- a/phpBB/adm/style/acp_permission_roles.html
+++ b/phpBB/adm/style/acp_permission_roles.html
@@ -157,20 +157,11 @@
<!-- IF roles.ROLE_DESCRIPTION --><br /><span>{roles.ROLE_DESCRIPTION}</span><!-- ENDIF -->
</td>
<td style="width: 30%; text-align: center; vertical-align: top; white-space: nowrap;"><!-- IF roles.U_DISPLAY_ITEMS --><a href="{roles.U_DISPLAY_ITEMS}">{L_VIEW_ASSIGNED_ITEMS}</a><!-- ELSE -->{L_VIEW_ASSIGNED_ITEMS}<!-- ENDIF --></td>
- <td style="width: 80px; text-align: right; vertical-align: top; white-space: nowrap;">
- <!-- IF roles.S_FIRST_ROW && not roles.S_LAST_ROW -->
- {ICON_MOVE_UP_DISABLED}
- <a href="{roles.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a>
- <!-- ELSEIF not roles.S_FIRST_ROW && not roles.S_LAST_ROW-->
- <a href="{roles.U_MOVE_UP}">{ICON_MOVE_UP}</a>
- <a href="{roles.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a>
- <!-- ELSEIF roles.S_LAST_ROW && not roles.S_FIRST_ROW -->
- <a href="{roles.U_MOVE_UP}">{ICON_MOVE_UP}</a>
- {ICON_MOVE_DOWN_DISABLED}
- <!-- ELSE -->
- {ICON_MOVE_UP_DISABLED}
- {ICON_MOVE_DOWN_DISABLED}
- <!-- ENDIF -->
+ <td class="actions">
+ <span class="up-disabled" style="display:none;">{ICON_MOVE_UP_DISABLED}</span>
+ <span class="up"><a href="{roles.U_MOVE_UP}" data-ajax="row_up">{ICON_MOVE_UP}</a></span>
+ <span class="down-disabled" style="display:none;">{ICON_MOVE_DOWN_DISABLED}</span>
+ <span class="down"><a href="{roles.U_MOVE_DOWN}" data-ajax="row_down">{ICON_MOVE_DOWN}</a></span>
<a href="{roles.U_EDIT}" title="{L_EDIT_ROLE}">{ICON_EDIT}</a>
<a href="{roles.U_REMOVE}" title="{L_REMOVE_ROLE}" data-ajax="row_delete">{ICON_DELETE}</a>
</td>
diff --git a/phpBB/adm/style/acp_profile.html b/phpBB/adm/style/acp_profile.html
index 3546873f6c..44c9cb6d49 100644
--- a/phpBB/adm/style/acp_profile.html
+++ b/phpBB/adm/style/acp_profile.html
@@ -185,7 +185,7 @@
</div>
<!-- ENDIF -->
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_FIELD_IDENT}</th>
@@ -195,23 +195,16 @@
</thead>
<tbody>
<!-- BEGIN fields -->
- <!-- IF fields.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
-
+ <tr>
<td>{fields.FIELD_IDENT}</td>
<td>{fields.FIELD_TYPE}</td>
<td style="text-align: center;"><a href="{fields.U_ACTIVATE_DEACTIVATE}" data-ajax="activate_deactivate">{fields.L_ACTIVATE_DEACTIVATE}</a><!-- IF fields.S_NEED_EDIT --> | <a href="{fields.U_TRANSLATE}" style="color: red;">{L_TRANSLATE}</a><!-- ENDIF --></td>
- <td style="width: 80px; text-align: right; white-space: nowrap;">
- <!-- IF fields.S_FIRST_ROW && not fields.S_LAST_ROW -->
- {ICON_MOVE_UP_DISABLED}
- <a href="{fields.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a>
- <!-- ELSEIF not fields.S_FIRST_ROW && not fields.S_LAST_ROW-->
- <a href="{fields.U_MOVE_UP}">{ICON_MOVE_UP}</a>
- <a href="{fields.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a>
- <!-- ELSEIF fields.S_LAST_ROW && not fields.S_FIRST_ROW -->
- <a href="{fields.U_MOVE_UP}">{ICON_MOVE_UP}</a>
- {ICON_MOVE_DOWN_DISABLED}
- <!-- ENDIF -->
+ <td class="actions" style="width: 80px;">
+ <span class="up-disabled" style="display:none;">{ICON_MOVE_UP_DISABLED}</span>
+ <span class="up"><a href="{fields.U_MOVE_UP}" data-ajax="row_up">{ICON_MOVE_UP}</a></span>
+ <span class="down-disabled" style="display:none;">{ICON_MOVE_DOWN_DISABLED}</span>
+ <span class="down"><a href="{fields.U_MOVE_DOWN}" data-ajax="row_down">{ICON_MOVE_DOWN}</a></span>
<!-- IF not fields.S_NEED_EDIT -->
<a href="{fields.U_EDIT}">{ICON_EDIT}</a>
<!-- ELSE -->
diff --git a/phpBB/adm/style/acp_prune_forums.html b/phpBB/adm/style/acp_prune_forums.html
index 1be9689cc7..4d748f1cce 100644
--- a/phpBB/adm/style/acp_prune_forums.html
+++ b/phpBB/adm/style/acp_prune_forums.html
@@ -8,7 +8,7 @@
<p>{L_PRUNE_SUCCESS}</p>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_FORUM}</th>
@@ -18,7 +18,7 @@
</thead>
<tbody>
<!-- BEGIN pruned -->
- <!-- IF pruned.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td style="text-align: center;">{pruned.FORUM_NAME}</td>
<td style="text-align: center;">{pruned.NUM_TOPICS}</td>
<td style="text-align: center;">{pruned.NUM_POSTS}</td>
diff --git a/phpBB/adm/style/acp_ranks.html b/phpBB/adm/style/acp_ranks.html
index a1409b15ef..be68dda695 100644
--- a/phpBB/adm/style/acp_ranks.html
+++ b/phpBB/adm/style/acp_ranks.html
@@ -65,7 +65,7 @@
<fieldset class="tabulated">
<legend>{L_ACP_MANAGE_RANKS}</legend>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_RANK_IMAGE}</th>
@@ -76,7 +76,7 @@
</thead>
<tbody>
<!-- BEGIN ranks -->
- <!-- IF ranks.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td style="text-align: center;"><!-- IF ranks.S_RANK_IMAGE --><img src="{ranks.RANK_IMAGE}" alt="{ranks.RANK_TITLE}" title="{ranks.RANK_TITLE}" /><!-- ELSE -->&nbsp; - &nbsp;<!-- ENDIF --></td>
<td style="text-align: center;">{ranks.RANK_TITLE}</td>
<td style="text-align: center;"><!-- IF ranks.S_SPECIAL_RANK -->&nbsp; - &nbsp;<!-- ELSE -->{ranks.MIN_POSTS}<!-- ENDIF --></td>
diff --git a/phpBB/adm/style/acp_reasons.html b/phpBB/adm/style/acp_reasons.html
index ca3aebb338..d629a9553f 100644
--- a/phpBB/adm/style/acp_reasons.html
+++ b/phpBB/adm/style/acp_reasons.html
@@ -86,17 +86,11 @@
<br /><span>{reasons.REASON_DESCRIPTION}</span>
</td>
<td style="width: 100px;">{reasons.REASON_COUNT}</td>
- <td style="width: 80px; text-align: right; white-space: nowrap;">
- <!-- IF reasons.S_FIRST_ROW && not reasons.S_LAST_ROW -->
- {ICON_MOVE_UP_DISABLED}
- <a href="{reasons.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a>
- <!-- ELSEIF not reasons.S_FIRST_ROW && not reasons.S_LAST_ROW-->
- <a href="{reasons.U_MOVE_UP}">{ICON_MOVE_UP}</a>
- <a href="{reasons.U_MOVE_DOWN}">{ICON_MOVE_DOWN}</a>
- <!-- ELSEIF reasons.S_LAST_ROW && not reasons.S_FIRST_ROW -->
- <a href="{reasons.U_MOVE_UP}">{ICON_MOVE_UP}</a>
- {ICON_MOVE_DOWN_DISABLED}
- <!-- ENDIF -->
+ <td class="actions" style="width: 80px;">
+ <span class="up-disabled" style="display:none;">{ICON_MOVE_UP_DISABLED}</span>
+ <span class="up"><a href="{reasons.U_MOVE_UP}" data-ajax="row_up">{ICON_MOVE_UP}</a></span>
+ <span class="down-disabled" style="display:none;">{ICON_MOVE_DOWN_DISABLED}</span>
+ <span class="down"><a href="{reasons.U_MOVE_DOWN}" data-ajax="row_down">{ICON_MOVE_DOWN}</a></span>
<a href="{reasons.U_EDIT}">{ICON_EDIT}</a>
<!-- IF reasons.U_DELETE -->
<a href="{reasons.U_DELETE}" data-ajax="row_delete">{ICON_DELETE}</a>
diff --git a/phpBB/adm/style/acp_users.html b/phpBB/adm/style/acp_users.html
index b213587def..b84e32013f 100644
--- a/phpBB/adm/style/acp_users.html
+++ b/phpBB/adm/style/acp_users.html
@@ -125,7 +125,7 @@
<form id="user_groups" method="post" action="{U_ACTION}">
- <table class="table1">
+ <table class="table1 zebra-table">
<tbody>
<!-- BEGIN group -->
<!-- IF group.S_NEW_GROUP_TYPE -->
@@ -133,7 +133,7 @@
<td class="row3" colspan="4"><strong>{group.GROUP_TYPE}</strong></td>
</tr>
<!-- ELSE -->
- <!-- IF group.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td><a href="{group.U_EDIT_GROUP}">{group.GROUP_NAME}</a></td>
<td><!-- IF group.S_IS_MEMBER --><!-- IF group.S_NO_DEFAULT --><a href="{group.U_DEFAULT}">{L_GROUP_DEFAULT}</a><!-- ELSE --><strong>{L_GROUP_DEFAULT}</strong><!-- ENDIF --><!-- ELSEIF not group.S_IS_MEMBER and group.U_APPROVE --><a href="{group.U_APPROVE}">{L_GROUP_APPROVE}</a><!-- ELSE -->&nbsp;<!-- ENDIF --></td>
<td><!-- IF group.S_IS_MEMBER and not group.S_SPECIAL_GROUP --><a href="{group.U_DEMOTE_PROMOTE}">{group.L_DEMOTE_PROMOTE}</a><!-- ELSE -->&nbsp;<!-- ENDIF --></td>
@@ -164,7 +164,7 @@
</div>
<!-- IF .attach -->
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_FILENAME}</th>
@@ -176,7 +176,7 @@
</thead>
<tbody>
<!-- BEGIN attach -->
- <!-- IF attach.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td><a href="{attach.U_DOWNLOAD}">{attach.REAL_FILENAME}</a><br /><span class="small"><!-- IF attach.S_IN_MESSAGE --><strong>{L_PM}{L_COLON} </strong><!-- ELSE --><strong>{L_POST}{L_COLON} </strong><!-- ENDIF --><a href="{attach.U_VIEW_TOPIC}">{attach.TOPIC_TITLE}</a></span></td>
<td style="text-align: center">{attach.POST_TIME}</td>
<td style="text-align: center">{attach.SIZE}</td>
diff --git a/phpBB/adm/style/acp_users_feedback.html b/phpBB/adm/style/acp_users_feedback.html
index 95892f2822..f251724cd2 100644
--- a/phpBB/adm/style/acp_users_feedback.html
+++ b/phpBB/adm/style/acp_users_feedback.html
@@ -7,7 +7,7 @@
</div>
<!-- IF .log -->
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_REPORT_BY}</th>
@@ -19,8 +19,7 @@
</thead>
<tbody>
<!-- BEGIN log -->
- <!-- IF log.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
-
+ <tr>
<td>{log.USERNAME}</td>
<td style="text-align: center;">{log.IP}</td>
<td style="text-align: center;">{log.DATE}</td>
diff --git a/phpBB/adm/style/acp_users_warnings.html b/phpBB/adm/style/acp_users_warnings.html
index 9d082eda6d..6e7f521415 100644
--- a/phpBB/adm/style/acp_users_warnings.html
+++ b/phpBB/adm/style/acp_users_warnings.html
@@ -1,7 +1,7 @@
<form id="list" method="post" action="{U_ACTION}">
<!-- IF .warn -->
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_REPORT_BY}</th>
@@ -12,7 +12,7 @@
</thead>
<tbody>
<!-- BEGIN warn -->
- <!-- IF warn.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td>{warn.USERNAME}</td>
<td style="text-align: center; nowrap: nowrap;">{warn.DATE}</td>
<td>{warn.ACTION}</td>
diff --git a/phpBB/adm/style/acp_words.html b/phpBB/adm/style/acp_words.html
index 7e91bc4367..6038fc6161 100644
--- a/phpBB/adm/style/acp_words.html
+++ b/phpBB/adm/style/acp_words.html
@@ -47,7 +47,7 @@
<input class="button2" name="add" type="submit" value="{L_ADD_WORD}" />
</p>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th>{L_WORD}</th>
@@ -57,7 +57,7 @@
</thead>
<tbody>
<!-- BEGIN words -->
- <!-- IF words.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
+ <tr>
<td style="text-align: center;">{words.WORD}</td>
<td style="text-align: center;">{words.REPLACEMENT}</td>
<td>&nbsp;<a href="{words.U_EDIT}">{ICON_EDIT}</a>&nbsp;&nbsp;<a href="{words.U_DELETE}" data-ajax="row_delete">{ICON_DELETE}</a>&nbsp;</td>
diff --git a/phpBB/adm/style/admin.css b/phpBB/adm/style/admin.css
index c4f1caf722..aa8622ff61 100644
--- a/phpBB/adm/style/admin.css
+++ b/phpBB/adm/style/admin.css
@@ -220,7 +220,7 @@ li {
}
.rtl #main {
- float: right;
+ float: left;
margin: 0 -210px 0 0;
}
@@ -352,7 +352,7 @@ li {
position: relative;
}
-.rtl #tabs a {
+.rtl #tabs li {
float: right;
}
@@ -840,8 +840,22 @@ td.name {
text-align: right;
}
-.row1 { background-color: #F9F9F9; }
-.row2 { background-color: #DCEBFE; }
+.row1 {
+ background-color: #F9F9F9;
+}
+
+table.zebra-table tbody tr:nth-child(odd) {
+ background-color: #F9F9F9;
+}
+
+.row2 {
+ background-color: #DCEBFE;
+}
+
+table.zebra-table tbody tr:nth-child(even) {
+ background-color: #DCEBFE;
+}
+
.row3 { background-color: #DBDFE2; }
.row4 { background-color: #E4E8EB; }
.col1 { background-color: #DCEBFE; }
diff --git a/phpBB/adm/style/ajax.js b/phpBB/adm/style/ajax.js
index 78fcbd88fd..959580d6c2 100644
--- a/phpBB/adm/style/ajax.js
+++ b/phpBB/adm/style/ajax.js
@@ -8,7 +8,11 @@
* an item is moved up. It moves the row up or down, and deactivates /
* activates any up / down icons that require it (the ones at the top or bottom).
*/
-phpbb.addAjaxCallback('row_down', function() {
+phpbb.addAjaxCallback('row_down', function(res) {
+ if (typeof res.success === 'undefined' || !res.success) {
+ return;
+ }
+
var el = $(this),
tr = el.parents('tr'),
trSwap = tr.next();
@@ -16,7 +20,11 @@ phpbb.addAjaxCallback('row_down', function() {
tr.insertAfter(trSwap);
});
-phpbb.addAjaxCallback('row_up', function() {
+phpbb.addAjaxCallback('row_up', function(res) {
+ if (typeof res.success === 'undefined' || !res.success) {
+ return;
+ }
+
var el = $(this),
tr = el.parents('tr'),
trSwap = tr.prev();
diff --git a/phpBB/adm/style/captcha_qa_acp.html b/phpBB/adm/style/captcha_qa_acp.html
index 4ff7c328fc..6235f9a104 100644
--- a/phpBB/adm/style/captcha_qa_acp.html
+++ b/phpBB/adm/style/captcha_qa_acp.html
@@ -14,7 +14,7 @@
<fieldset class="tabulated">
<legend>{L_QUESTIONS}</legend>
- <table class="table1">
+ <table class="table1 zebra-table">
<thead>
<tr>
<th colspan="3">{L_QUESTIONS}</th>
@@ -27,14 +27,12 @@
</thead>
<tbody>
<!-- BEGIN questions -->
-
- <!-- IF questions.S_ROW_COUNT is even --><tr class="row1"><!-- ELSE --><tr class="row2"><!-- ENDIF -->
-
+ <tr>
<td style="text-align: left;">{questions.QUESTION_TEXT}</td>
<td style="text-align: center;">{questions.QUESTION_LANG}</td>
<td style="text-align: center;"><a href="{questions.U_EDIT}">{ICON_EDIT}</a>&nbsp;<a href="{questions.U_DELETE}">{ICON_DELETE}</a></td>
- </tr>
- <!-- END questions -->
+ </tr>
+ <!-- END questions -->
</tbody>
</table>
<fieldset class="quick">
diff --git a/phpBB/assets/javascript/editor.js b/phpBB/assets/javascript/editor.js
index 5222de9fee..be3d868f13 100644
--- a/phpBB/assets/javascript/editor.js
+++ b/phpBB/assets/javascript/editor.js
@@ -78,14 +78,13 @@ function bbfontstyle(bbopen, bbclose) {
if (theSelection) {
// Add tags around selection
document.selection.createRange().text = bbopen + theSelection + bbclose;
- document.forms[form_name].elements[text_name].focus();
+ textarea.focus();
theSelection = '';
return;
}
- } else if (document.forms[form_name].elements[text_name].selectionEnd
- && (document.forms[form_name].elements[text_name].selectionEnd - document.forms[form_name].elements[text_name].selectionStart > 0)) {
- mozWrap(document.forms[form_name].elements[text_name], bbopen, bbclose);
- document.forms[form_name].elements[text_name].focus();
+ } else if (textarea.selectionEnd && (textarea.selectionEnd - textarea.selectionStart > 0)) {
+ mozWrap(textarea, bbopen, bbclose);
+ textarea.focus();
theSelection = '';
return;
}
diff --git a/phpBB/assets/javascript/plupload.js b/phpBB/assets/javascript/plupload.js
index 1befb88eb6..a90757d487 100644
--- a/phpBB/assets/javascript/plupload.js
+++ b/phpBB/assets/javascript/plupload.js
@@ -1,38 +1,82 @@
plupload.addI18n(phpbb.plupload.i18n);
-plupload.attachment_data = [];
+phpbb.plupload.ids = [];
+
+(function($) { // Avoid conflicts with other libraries
+
+"use strict";
/**
- * Returns the index of the plupload.attachment_data array where the given
- * attach id appears
+ * Set up the uploader.
*
- * @param int id The attachment id of the file
+ * @return undefined
+ */
+phpbb.plupload.initialize = function() {
+ // Initialize the Plupload uploader.
+ uploader.init();
+
+ // Set attachment data.
+ phpbb.plupload.setData(phpbb.plupload.data);
+ phpbb.plupload.updateMultipartParams(phpbb.plupload.getSerializedData());
+
+ // Only execute if Plupload initialized successfully.
+ uploader.bind('Init', function() {
+ phpbb.plupload.form = $(phpbb.plupload.config.form_hook)[0],
+ phpbb.plupload.rowTpl = $('#attach-row-tpl')[0].outerHTML;
+
+ // Hide the basic upload panel and remove the attach row template.
+ $('#attach-row-tpl, #attach-panel-basic').remove();
+ // Show multi-file upload options.
+ $('#attach-panel-multi').show();
+ });
+
+ uploader.bind('PostInit', function() {
+ // Point out the drag-and-drop zone if it's supported.
+ if (uploader.features.dragdrop) {
+ $('#drag-n-drop-message').show();
+ }
+ });
+};
+
+/**
+ * Unsets all elements in the object uploader.settings.multipart_params whose keys
+ * begin with 'attachment_data['
*
- * @return bool Returns false if the id cannot be found
- * @return int Returns the index in the main array where the attachment id
- * was found
+ * @return undefined
*/
-function phpbb_plupload_find_attachment_idx(id) {
- var data = plupload.attachment_data;
- for (var i = 0; i < data.length; i++) {
- if (data[i].attach_id == id) {
- return i;
+phpbb.plupload.clearParams = function() {
+ var obj = uploader.settings.multipart_params;
+ for (var key in obj) {
+ if (!obj.hasOwnProperty(key) || key.indexOf('attachment_data[') !== 0) {
+ continue;
}
+
+ delete uploader.settings.multipart_params[key];
}
+};
- return false;
-}
+/**
+ * Update uploader.settings.multipart_params object with new data.
+ *
+ * @param object obj
+ * @return undefined
+ */
+phpbb.plupload.updateMultipartParams = function(obj) {
+ uploader.settings.multipart_params = $.extend(
+ uploader.settings.multipart_params,
+ obj
+ );
+};
/**
- * Converts an array of objects into an object that PHP would expect as POST
- * data
+ * Convert the array of attachment objects into an object that PHP would expect as POST data.
*
* @return object An object in the form 'attachment_data[i][key]': value as
* expected by the server
*/
-function phpbb_plupload_attachment_data_serialize() {
+phpbb.plupload.getSerializedData = function() {
var obj = {};
- for (var i = 0; i < plupload.attachment_data.length; i++) {
- var datum = plupload.attachment_data[i];
+ for (var i = 0; i < phpbb.plupload.data.length; i++) {
+ var datum = phpbb.plupload.data[i];
for (var key in datum) {
if (!datum.hasOwnProperty(key)) {
continue;
@@ -41,264 +85,596 @@ function phpbb_plupload_attachment_data_serialize() {
obj['attachment_data[' + i + '][' + key + ']'] = datum[key];
}
}
-
return obj;
-}
+};
/**
- * Unsets all elements in an object whose keys begin with 'attachment_data['
+ * Get the index from the phpbb.plupload.data array where the given
+ * attachment id appears.
*
- * @param object The object to be cleared
+ * @param int attach_id The attachment id of the file.
+ * @return bool Returns false if the id cannot be found.
+ * @return int Returns the index of the file if it exists.
+ */
+phpbb.plupload.getIndex = function(attach_id) {
+ var index = $.inArray(Number(attach_id), phpbb.plupload.ids);
+ return (index !== -1) ? index : false;
+};
+
+/**
+ * Set the data in phpbb.plupload.data and phpbb.plupload.ids arrays.
+ *
+ * @param array data Array containing the new data to use. In the form of
+ * array(index => object(property: value). Requires attach_id to be one of the object properties.
*
* @return undefined
*/
-function phpbb_plupload_clear_params(obj) {
- for (var key in obj) {
- if (!obj.hasOwnProperty(key) || key.indexOf('attachment_data[') !== 0) {
- continue;
- }
+phpbb.plupload.setData = function(data) {
+ // Make sure that the array keys are reset.
+ phpbb.plupload.ids = phpbb.plupload.data = [];
+ phpbb.plupload.data = data;
- delete obj[key];
+ for (var i = 0; i < data.length; i++) {
+ phpbb.plupload.ids.push(Number(data[i].attach_id));
}
-}
+};
/**
- * Update hidden attachment inputs in posting form
- * Pre-existing hidden inputs will be removed by comparing the old attachment
- * data (old_data) to the new attachment data (data) that has been sent back
- * by plupload.
- *
- * @param object form Posting form
- * @param object data Current attachment_data
- * @param object old_date Previous attachment_data (before submission)
- *
- * @return void
+ * Update the attachment data in the HTML and the phpbb & phpbb.plupload objects.
+ *
+ * @param array data Array containing the new data to use.
+ * @param string action The action that required the update. Used to update the inline attachment bbcodes.
+ * @param int index The index from phpbb.plupload_ids that was affected by the action.
+ * @param array downloadUrl Optional array of download urls to update.
+ * @return undefined
*/
-phpbb.update_hidden_attachment_inputs = function(form, data, old_data) {
- // Update already existing hidden inputs
- for (var i = 0; i < form.length; i++) {
- if (data.hasOwnProperty(form[i].name)) {
- form[i].value = data[form[i].name];
- delete data[form[i].name];
- } else if (typeof old_data !== 'undefined' && old_data.hasOwnProperty(form[i].name)) {
- var inputRegex = /\b^[a-z_]+[+[0-9]+]/;
- var inputName = inputRegex.exec(form[i].name);
- if (typeof inputName !== 'undefined' && inputName[0] !== '') {
- $("input[type='hidden'][name^='" + inputName[0] + "']").remove();
- }
- }
+phpbb.plupload.update = function(data, action, index, downloadUrl) {
+
+ phpbb.plupload.updateBbcode(action, index);
+ phpbb.plupload.setData(data);
+ phpbb.plupload.updateRows(downloadUrl);
+ phpbb.plupload.clearParams();
+ phpbb.plupload.updateMultipartParams(phpbb.plupload.getSerializedData());
+};
+
+/**
+ * Update the relevant elements and hidden data for all attachments.
+ *
+ * @param array downloadUrl Optional array of download urls to update.
+ * @return undefined
+ */
+phpbb.plupload.updateRows = function(downloadUrl) {
+ for (var i = 0; i < phpbb.plupload.ids.length; i++) {
+ phpbb.plupload.updateRow(i, downloadUrl);
}
+};
- // Append new inputs
- for (var key in data) {
- if (!data.hasOwnProperty(key)) {
- continue;
- }
+/**
+ * Insert a row for a new attachment. This expects an HTML snippet in the HTML
+ * using the id "attach-row-tpl" to be present. This snippet is cloned and the
+ * data for the file inserted into it. The row is then appended or prepended to
+ * #file-list based on the attach_order setting.
+ *
+ * @param object file Plupload file object for the new attachment.
+ * @return undefined
+ */
+phpbb.plupload.insertRow = function(file) {
+ var row = $(phpbb.plupload.rowTpl);
+
+ row.attr('id', file.id);
+ row.find('.file-name').html(file.name);
+ row.find('.file-size').html(plupload.formatSize(file.size));
+ if (phpbb.plupload.order == 'desc') {
+ $('#file-list').prepend(row);
+ } else {
+ $('#file-list').append(row);
+ }
+};
+
+/**
+ * Update the relevant elements and hidden data for an attachment.
+ *
+ * @param int index The index from phpbb.plupload.ids of the attachment to edit.
+ * @param array downloadUrl Optional array of download urls to update.
+ * @return undefined
+ */
+phpbb.plupload.updateRow = function(index, downloadUrl) {
+ var attach = phpbb.plupload.data[index],
+ row = $('[data-attach-id="' + attach.attach_id + '"]');
+
+ // Add the link to the file
+ if (typeof downloadUrl !== 'undefined' && typeof downloadUrl[index] !== 'undefined') {
+ var url = downloadUrl[index].replace('&amp;', '&'),
+ link = $('<a></a>');
+
+ link.attr('href', url).html(attach.real_filename);
+ row.find('.file-name').html(link)
+ }
+
+ row.find('textarea').attr('name', 'comment_list[' + index + ']');
+ phpbb.plupload.updateHiddenData(row, attach, index);
+};
+
+/**
+ * Update hidden input data for an attachment.
+ *
+ * @param object row jQuery object for the attachment row.
+ * @param object attach Attachment data object from phpbb.plupload.data
+ * @param int index Attachment index from phpbb.plupload.ids
+ * @return undefined
+ */
+phpbb.plupload.updateHiddenData = function(row, attach, index) {
+ row.find('input[type="hidden"]').remove();
+
+ for (var key in attach) {
var input = $('<input />')
.attr('type', 'hidden')
- .attr('name', key)
- .attr('value', data[key]);
- $(form).append(input);
+ .attr('name', 'attachment_data[' + index + '][' + key +']')
+ .attr('value', attach[key]);
+ $('textarea', row).after(input);
}
-}
-
-jQuery(function($) {
- $(phpbb.plupload.config.element_hook).pluploadQueue(phpbb.plupload.config);
- var uploader = $(phpbb.plupload.config.element_hook).pluploadQueue();
+};
- // Check the page for already-existing attachment data and add it to the
- // array
- var form = $(phpbb.plupload.config.form_hook)[0];
- for (var i = 0; i < form.length; i++) {
- if (form[i].name.indexOf('attachment_data[') !== 0) {
- continue;
- }
-
- var matches = form[i].name.match(/\[(\d+)\]\[([^\]]+)\]/);
- var index = matches[1];
- var property = matches[2];
-
- if (!plupload.attachment_data[index]) {
- plupload.attachment_data[index] = {};
- }
-
- plupload.attachment_data[index][property] = form[i].value;
- uploader.settings.multipart_params[form[i].name] = form[i].value;
+/**
+ * Deleting a file removes it from the queue and fires an AJAX event to the
+ * server to tell it to remove the temporary attachment. The server
+ * responds with the updated attachment data list so that any future
+ * uploads can maintain state with the server
+ *
+ * @param object row jQuery object for the attachment row.
+ * @param int attachId Attachment id of the file to be removed.
+ *
+ * @return undefined
+ */
+phpbb.plupload.deleteFile = function(row, attachId) {
+ // If there's no attach id, then the file hasn't been uploaded. Simply delete the row.
+ if (typeof attachId === 'undefined') {
+ row.slideUp(100, function() {
+ row.remove();
+ phpbb.plupload.hideEmptyList();
+ });
}
- /**
- * Fires before a given file is about to be uploaded. This allows us to
- * send the real filename along with the chunk. This is necessary because
- * for some reason the filename is set to 'blob' whenever a file is chunked
- *
- * @param object up The plupload.Uploader object
- * @param object file The plupload.File object that is about to be
- * uploaded
- *
- * @return undefined
- */
- uploader.bind('BeforeUpload', function(up, file) {
- up.settings.multipart_params = $.extend(
- up.settings.multipart_params,
- {'real_filename': file.name}
- );
- });
+ var index = phpbb.plupload.getIndex(attachId);
+ row.find('.file-status').toggleClass('file-uploaded file-working');
- /**
- * Fired when a single chunk of any given file is uploaded. This parses the
- * response from the server and checks for an error. If an error occurs it
- * is reported to the user and the upload of this particular file is halted
- *
- * @param object up The plupload.Uploader object
- * @param object file The plupload.File object whose chunk has just
- * been uploaded
- * @param object response The response object from the server
- *
- * @return undefined
- */
- uploader.bind('ChunkUploaded', function(up, file, response) {
- if (response.chunk >= response.chunks - 1) {
- return;
- }
+ if (index === false) {
+ return;
+ }
+ var fields = {};
+ fields['delete_file[' + index + ']'] = 1;
+
+ var always = function() {
+ row.find('.file-status').removeClass('file-working');
+ };
+ var done = function(response) {
var json = {};
try {
- json = $.parseJSON(response.response);
+ json = $.parseJSON(response);
} catch (e) {
- file.status = plupload.FAILED;
- up.trigger('FileUploaded', file, {
- response: JSON.stringify({
- error: {
- message: 'Error parsing server response.'
- }
- })
- });
+ return;
}
- if (json.error) {
- file.status = plupload.FAILED;
- up.trigger('FileUploaded', file, {
- response: JSON.stringify({
- error: {
- message: json.error.message
- }
- })
- });
+ // trigger_error() was called which likely means a permission error was encountered.
+ if (typeof response.title !== 'undefined') {
+ uploader.trigger('Error', {message: response.message});
+ // We will have to assume that the deletion failed. So leave the file status as uploaded.
+ row.find('.file-status').toggleClass('file-uploaded');
+
+ return;
}
- });
+ phpbb.plupload.update(response, 'removal', index);
+ // Check if the user can upload files now if he had reached the max files limit.
+ phpbb.plupload.handleMaxFilesReached();
- /**
- * Fires when an entire file has been uploaded. It checks for errors
- * returned by the server otherwise parses the list of attachment data and
- * appends it to the next file upload so that the server can maintain state
- * with regards to the attachments in a given post
- *
- * @param object up The plupload.Uploader object
- * @param object file The plupload.File object that has just been
- * uploaded
- * @param string response The response string from the server
- *
- * @return undefined
- */
- uploader.bind('FileUploaded', function(up, file, response) {
- var json = {};
- try {
- json = $.parseJSON(response.response);
- } catch (e) {
- file.status = plupload.FAILED;
- file.error = 'Error parsing server response.'
+ if (row.attr('id')) {
+ var file = uploader.getFile(row.attr('id'));
+ uploader.removeFile(file);
+ }
+ row.slideUp(100, function() {
+ row.remove();
+ // Hide the file list if it's empty now.
+ phpbb.plupload.hideEmptyList();
+ });
+ uploader.trigger('FilesRemoved');
+ };
+
+ $.ajax(phpbb.plupload.config.url, {
+ type: 'POST',
+ data: $.extend(fields, phpbb.plupload.getSerializedData()),
+ headers: {'X-PHPBB-USING-PLUPLOAD': '1', 'X-Requested-With': 'XMLHttpRequest'}
+ })
+ .always(always)
+ .done(done);
+};
+
+/**
+ * Check the attachment list and hide its container if it's empty.
+ *
+ * @return undefined
+ */
+phpbb.plupload.hideEmptyList = function() {
+ if (!$('#file-list').children().length) {
+ $('#file-list-container').slideUp(100);
+ }
+}
+
+/**
+ * Update the indices used in inline attachment bbcodes. This ensures that the bbcodes
+ * correspond to the correct file after a file is added or removed. This should be called
+ * before the phpbb.plupload,data and phpbb.plupload.ids arrays are updated, otherwise it will
+ * not work correctly.
+ *
+ * @param string action The action that occurred -- either "addition" or "removal"
+ * @param int index The index of the attachment from phpbb.plupload.ids that was affected.
+ *
+ * @return undefined
+ */
+phpbb.plupload.updateBbcode = function(action, index) {
+ var textarea = $('#message', phpbb.plupload.form),
+ text = textarea.val(),
+ removal = (action === 'removal');
+
+ // Return if the bbcode isn't used at all.
+ if (text.indexOf('[attachment=') === -1) {
+ return;
+ }
+
+ // Private function used to replace the bbcode.
+ var updateBbcode = function(match, fileName) {
+ // Remove the bbcode if the file was removed.
+ if (removal && index === i) {
+ return '';
}
+ var newIndex = i + ((removal) ? -1 : 1);
+ return '[attachment=' + newIndex +']' + fileName + '[/attachment]';
+ };
- if (json.error) {
- file.status = plupload.FAILED;
- file.error = json.error.message;
- } else if (file.status === plupload.DONE) {
- plupload.attachment_data = json;
- file.attachment_data = json[0];
- up.settings.multipart_params = $.extend(
- up.settings.multipart_params,
- phpbb_plupload_attachment_data_serialize()
- );
+ // Private function used to generate search regexp
+ var searchRegexp = function(index) {
+ return new RegExp('\\[attachment=' + index + '\\](.*?)\\[\\/attachment\\]', 'g');
+ }
+ // The update order of the indices is based on the action taken to ensure that we don't corrupt
+ // the bbcode index by updating it several times as we move through the loop.
+ // Removal loop starts at the removed index and moves to the end of the array.
+ // Addition loop starts at the end of the array and moves to the added index at 0.
+ var searchLoop = function() {
+ if (typeof i === 'undefined') {
+ i = (removal) ? index : phpbb.plupload.ids.length - 1;
+ }
+ return (removal) ? (i < phpbb.plupload.ids.length): (i >= index);
+ }
+ var i;
+
+ while (searchLoop()) {
+ text = text.replace(searchRegexp(i), updateBbcode);
+ (removal) ? i++ : i--;
+ }
+ textarea.val(text);
+};
+
+/**
+ * Get Plupload file objects based on their upload status.
+ *
+ * @param int status Plupload status - plupload.DONE, plupload.FAILED, plupload.QUEUED,
+ * plupload.STARTED, plupload.STOPPED
+ *
+ * @return Returns an array of the Plupload file objects matching the status.
+ */
+phpbb.plupload.getFilesByStatus = function(status) {
+ var files = [];
+
+ $.each(uploader.files, function(i, file) {
+ if (file.status === status) {
+ files.push(file);
}
});
+ return files;
+}
- /**
- * Fires when the entire queue of files have been uploaded. It resets the
- * 'add files' button to allow more files to be uploaded and also attaches
- * several events to each row of the currently-uploaded files to facilitate
- * deleting any one of the files.
- *
- * Deleting a file removes it from the queue and fires an ajax event to the
- * server to tell it to remove the temporary attachment. The server
- * responds with the updated attachment data list so that any future
- * uploads can maintain state with the server
- *
- * @param object up The plupload.Uploader object
- * @param array files An array of plupload.File objects that have just
- * been uploaded as part of a queue
- *
- * @return undefined
- */
- uploader.bind('UploadComplete', function(up, files) {
- $('.plupload_upload_status').css('display', 'none');
- $('.plupload_buttons').css('display', 'block');
-
- // Insert a bunch of hidden input elements containing the attachment
- // data so that the save/preview/submit buttons work as expected.
- var form = $(phpbb.plupload.config.form_hook)[0];
- var data = phpbb_plupload_attachment_data_serialize();
-
- phpbb.update_hidden_attachment_inputs(form, data);
-
- files.forEach(function(file) {
- if (file.status !== plupload.DONE) {
- var click = function(evt) {
- alert(file.error);
- }
+/**
+ * Check whether the user has reached the maximun number of files that he's allowed
+ * to upload. If so, disables the uploader and marks the queued files as failed. Otherwise
+ * makes sure that the uploader is enabled.
+ *
+ * @return bool Returns true if the limit has been reached. False if otherwise.
+ */
+phpbb.plupload.handleMaxFilesReached = function() {
+ // If there is no limit, the user is an admin or moderator.
+ if (!phpbb.plupload.maxFiles) {
+ return false;
+ }
- $('#' + file.id).attr('title', file.error);
- $('#' + file.id).click(click);
+ if (phpbb.plupload.maxFiles <= phpbb.plupload.ids.length) {
+ // Fail the rest of the queue.
+ phpbb.plupload.markQueuedFailed(phpbb.plupload.lang.TOO_MANY_ATTACHMENTS);
+ // Disable the uploader.
+ phpbb.plupload.disableUploader();
+ uploader.trigger('Error', {message: phpbb.plupload.lang.TOO_MANY_ATTACHMENTS});
- return;
- }
+ return true;
+ } else if(phpbb.plupload.maxFiles > phpbb.plupload.ids.length) {
+ // Enable the uploader if the user is under the limit
+ phpbb.plupload.enableUploader();
+ }
+ return false;
+}
+
+/**
+ * Disable the uploader
+ *
+ * @return undefined
+ */
+phpbb.plupload.disableUploader = function() {
+ $('#add_files').addClass('disabled');
+ uploader.disableBrowse();
+}
+
+/**
+ * Enable the uploader
+ *
+ * @return undefined
+ */
+phpbb.plupload.enableUploader = function() {
+ $('#add_files').removeClass('disabled');
+ uploader.disableBrowse(false);
+}
+
+/**
+ * Mark all queued files as failed.
+ *
+ * @param string error Error message to present to the user.
+ * @return undefined
+ */
+phpbb.plupload.markQueuedFailed = function(error) {
+ var files = phpbb.plupload.getFilesByStatus(plupload.QUEUED);
+
+ $.each(files, function(i, file) {
+ $('#' + file.id).find('.file-progress').hide();
+ phpbb.plupload.fileError(file, error);
+ });
+}
+
+/**
+ * Marks a file as failed and sets the error message for it.
+ *
+ * @param object file Plupload file object that failed.
+ * @param string error Error message to present to the user.
+ * @return undefined
+ */
+phpbb.plupload.fileError = function(file, error) {
+ file.status = plupload.FAILED;
+ file.error = error;
+ $('#' + file.id).find('.file-status').addClass('file-error').attr({'data-error-title': phpbb.plupload.lang.ERROR, 'data-error-message': error});
+}
+
+
+
+
+/**
+ * Set up the Plupload object and get some basic data.
+ */
+var uploader = new plupload.Uploader(phpbb.plupload.config);
+phpbb.plupload.initialize();
+
+
+
+
+/**
+ * Insert inline attachment bbcode.
+ */
+ $('#file-list').on('click', '.file-inline-bbcode', function(e) {
+ var attachId = $(this).parents('.attach-row').attr('data-attach-id'),
+ index = phpbb.plupload.getIndex(attachId);
+
+ attach_inline(index, phpbb.plupload.data[index].real_filename);
+ e.preventDefault();
+});
+
+/**
+ * Delete a file.
+ */
+$('#file-list').on('click', '.file-delete', function(e) {
+ var row = $(this).parents('.attach-row'),
+ attachId = row.attr('data-attach-id');
+
+ phpbb.plupload.deleteFile(row, attachId);
+ e.preventDefault();
+});
+
+/**
+ * Display the error message for a particular file when the error icon is clicked.
+ */
+$('#file-list').on('click', '.file-error', function(e) {
+ phpbb.alert($(this).attr('data-error-title'), $(this).attr('data-error-message'));
+ e.preventDefault();
+});
+
+/**
+ * Fires when an error occurs.
+ */
+uploader.bind('Error', function(up, error) {
+ // The error message that Plupload provides for these is vague, so we'll be more specific.
+ if (error.code === plupload.FILE_EXTENSION_ERROR) {
+ error.message = plupload.translate('Invalid file extension:') + ' ' + error.file.name;
+ } else if (error.code === plupload.FILE_SIZE_ERROR) {
+ error.message = plupload.translate('File too large:') + ' ' + error.file.name;
+ }
+ phpbb.alert(phpbb.plupload.lang.ERROR, error.message);
+});
+
+/**
+ * Fires before a given file is about to be uploaded. This allows us to
+ * send the real filename along with the chunk. This is necessary because
+ * for some reason the filename is set to 'blob' whenever a file is chunked
+ *
+ * @param object up The plupload.Uploader object
+ * @param object file The plupload.File object that is about to be
+ * uploaded
+ *
+ * @return undefined
+ */
+uploader.bind('BeforeUpload', function(up, file) {
+ if (phpbb.plupload.handleMaxFilesReached()) {
+ return;
+ }
+
+ phpbb.plupload.updateMultipartParams({'real_filename': file.name});
+});
+
+/**
+ * Fired when a single chunk of any given file is uploaded. This parses the
+ * response from the server and checks for an error. If an error occurs it
+ * is reported to the user and the upload of this particular file is halted
+ *
+ * @param object up The plupload.Uploader object
+ * @param object file The plupload.File object whose chunk has just
+ * been uploaded
+ * @param object response The response object from the server
+ *
+ * @return undefined
+ */
+uploader.bind('ChunkUploaded', function(up, file, response) {
+ if (response.chunk >= response.chunks - 1) {
+ return;
+ }
- var click = function(evt) {
- $(evt.target).find('a').addClass('working');
-
- // The index is always found because file.attachment_data is
- // just an element of plupload.attachment_data
- var idx = phpbb_plupload_find_attachment_idx(file.attachment_data.attach_id);
- var fields = {};
- fields['delete_file[' + idx + ']'] = 1;
-
- var always = function() {
- $(evt.target).find('a').removeClass('working');
- };
-
- var done = function(response) {
- up.removeFile(file);
- plupload.attachment_data = response;
- phpbb.update_hidden_attachment_inputs(form, phpbb_plupload_attachment_data_serialize(), data);
- phpbb_plupload_clear_params(up.settings.multipart_params);
- up.settings.multipart_params = $.extend(
- up.settings.multipart_params,
- phpbb_plupload_attachment_data_serialize()
- );
- };
-
- $.ajax(phpbb.plupload.config.url, {
- type: 'POST',
- data: $.extend(fields, phpbb_plupload_attachment_data_serialize()),
- headers: {'X-PHPBB-USING-PLUPLOAD': '1'}
- })
- .always(always)
- .done(done);
- };
-
- $('#' + file.id)
- .addClass('can_delete')
- .click(click);
+ var json = {};
+ try {
+ json = $.parseJSON(response.response);
+ } catch (e) {
+ file.status = plupload.FAILED;
+ up.trigger('FileUploaded', file, {
+ response: JSON.stringify({
+ error: {
+ message: 'Error parsing server response.'
+ }
+ })
});
+ }
+
+ // If trigger_error() was called, then a permission error likely occurred.
+ if (typeof json.title !== 'undefined') {
+ json.error = {message: json.message};
+ }
+
+ if (json.error) {
+ file.status = plupload.FAILED;
+ up.trigger('FileUploaded', file, {
+ response: JSON.stringify({
+ error: {
+ message: json.error.message
+ }
+ })
+ });
+ }
+});
+
+/**
+ * Fires when files are added to the queue.
+ *
+ * @return undefined
+ */
+uploader.bind('FilesAdded', function(up, files) {
+ // Prevent unnecessary requests to the server if the user already uploaded
+ // the maximum number of files allowed.
+ if (phpbb.plupload.handleMaxFilesReached()) {
+ return;
+ }
+
+ // Show the file list if there aren't any files currently.
+ if (!$('#file-list-container').is(':visible')) {
+ $('#file-list-container').show(100);
+ }
+
+ $.each(files, function(i, file) {
+ phpbb.plupload.insertRow(file);
});
+
+ up.bind('UploadProgress', function(up, file) {
+ $('#' + file.id + " .file-progress-bar").css('width', file.percent + '%');
+ $('#file-total-progress-bar').css('width', up.total.percent + '%');
+ });
+
+ // Do not allow more files to be added to the running queue.
+ phpbb.plupload.disableUploader();
+
+ // Start uploading the files once the user has selected them.
+ up.start();
});
+
+
+/**
+ * Fires when an entire file has been uploaded. It checks for errors
+ * returned by the server otherwise parses the list of attachment data and
+ * appends it to the next file upload so that the server can maintain state
+ * with regards to the attachments in a given post
+ *
+ * @param object up The plupload.Uploader object
+ * @param object file The plupload.File object that has just been
+ * uploaded
+ * @param string response The response string from the server
+ *
+ * @return undefined
+ */
+uploader.bind('FileUploaded', function(up, file, response) {
+ var json = {},
+ row = $('#' + file.id),
+ error;
+
+ // Hide the progress indicator.
+ row.find('.file-progress').hide();
+
+ try {
+ json = $.parseJSON(response.response);
+ } catch (e) {
+ error = 'Error parsing server response.';
+ }
+
+ // If trigger_error() was called, then a permission error likely occurred.
+ if (typeof json.title !== 'undefined') {
+ error = json.message;
+ up.trigger('Error', {message: error});
+
+ // The rest of the queue will fail.
+ phpbb.plupload.markQueuedFailed(error);
+ } else if (json.error) {
+ error = json.error.message;
+ }
+
+ if (typeof error !== 'undefined') {
+ phpbb.plupload.fileError(file, error);
+ } else if (file.status === plupload.DONE) {
+ file.attachment_data = json['data'][0];
+
+ row.attr('data-attach-id', file.attachment_data.attach_id);
+ row.find('.file-inline-bbcode').show();
+ row.find('.file-status').addClass('file-uploaded');
+ phpbb.plupload.update(json['data'], 'addition', 0, [json['download_url']]);
+ }
+});
+
+/**
+ * Fires when the entire queue of files have been uploaded.
+ *
+ * @param object up The plupload.Uploader object
+ * @param array files An array of plupload.File objects that have just
+ * been uploaded as part of a queue
+ *
+ * @return undefined
+ */
+uploader.bind('UploadComplete', function(up, files) {
+ // Hide the progress bar
+ setTimeout(function() {
+ $('#file-total-progress-bar').fadeOut(500, function() {
+ $(this).css('width', 0).show();
+ });
+ }, 2000);
+
+ // Re-enable the uploader
+ phpbb.plupload.enableUploader();
+});
+
+})(jQuery); // Avoid conflicts with other libraries
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/done.gif b/phpBB/assets/plupload/img/done.gif
index 29f3ed7c97..29f3ed7c97 100644
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/done.gif
+++ b/phpBB/assets/plupload/img/done.gif
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/error.gif b/phpBB/assets/plupload/img/error.gif
index 4682b63007..4682b63007 100644
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/error.gif
+++ b/phpBB/assets/plupload/img/error.gif
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/throbber.gif b/phpBB/assets/plupload/img/throbber.gif
index 4ae8b16a5a..4ae8b16a5a 100644
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/throbber.gif
+++ b/phpBB/assets/plupload/img/throbber.gif
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css b/phpBB/assets/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css
deleted file mode 100644
index da60fefe2b..0000000000
--- a/phpBB/assets/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- Plupload
-------------------------------------------------------------------- */
-
-.plupload_button {
- display: -moz-inline-box; /* FF < 3*/
- display: inline-block;
- font: normal 12px sans-serif;
- text-decoration: none;
- color: #42454a;
- border: 1px solid #bababa;
- padding: 2px 8px 3px 20px;
- margin-right: 4px;
- background: #f3f3f3 url('../img/buttons.png') no-repeat 0 center;
- outline: 0;
-
- /* Optional rounded corners for browsers that support it */
- -moz-border-radius: 3px;
- -khtml-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
-}
-
-.plupload_button:hover {
- color: #000;
- text-decoration: none;
-}
-
-.plupload_disabled, a.plupload_disabled:hover {
- color: #737373;
- border-color: #c5c5c5;
- background: #ededed url('../img/buttons-disabled.png') no-repeat 0 center;
- cursor: default;
-}
-
-.plupload_add {
- background-position: -181px center;
-}
-
-.plupload_wrapper {
- font: normal 11px Verdana, sans-serif;
- width: 100%;
-}
-
-.plupload_container {
- padding: 8px;
- background: url('../img/transp50.png');
- /*-moz-border-radius: 5px;*/
-}
-
-.plupload_container input {
- border: 1px solid #DDD;
- font: normal 11px Verdana,sans-serif;
- width: 98%;
-}
-
-.plupload_header {background: #2A2C2E url('../img/backgrounds.gif') repeat-x;}
-.plupload_header_content {
- background: url('../img/backgrounds.gif') no-repeat 0 -317px;
- min-height: 56px;
- padding-left: 60px;
- color: #FFF;
-}
-.plupload_header_title {
- font: normal 18px sans-serif;
- padding: 6px 0 3px;
-}
-.plupload_header_text {
- font: normal 12px sans-serif;
-}
-
-.plupload_filelist {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-
-.plupload_scroll .plupload_filelist {
- height: 185px;
- background: #F5F5F5;
- overflow-y: scroll;
-}
-
-.plupload_filelist li {
- padding: 10px 8px;
- background: #F5F5F5 url('../img/backgrounds.gif') repeat-x 0 -156px;
- border-bottom: 1px solid #DDD;
-}
-
-.plupload_filelist_header, .plupload_filelist_footer {
- background: #DFDFDF;
- padding: 8px 8px;
- color: #42454A;
-}
-.plupload_filelist_header {
- border-top: 1px solid #EEE;
- border-bottom: 1px solid #CDCDCD;
-}
-
-.plupload_filelist_footer {border-top: 1px solid #FFF; height: 22px; line-height: 20px; vertical-align: middle;}
-.plupload_file_name {float: left; overflow: hidden}
-.plupload_file_status {color: #777;}
-.plupload_file_status span {color: #42454A;}
-.plupload_file_size, .plupload_file_status, .plupload_progress {
- float: right;
- width: 80px;
-}
-.plupload_file_size, .plupload_file_status, .plupload_file_action {text-align: right;}
-
-.plupload_filelist .plupload_file_name {width: 205px}
-
-.plupload_file_action {
- float: right;
- width: 16px;
- height: 16px;
- margin-left: 15px;
-}
-
-.plupload_file_action * {
- display: none;
- width: 16px;
- height: 16px;
-}
-
-li.plupload_uploading {background: #ECF3DC url('../img/backgrounds.gif') repeat-x 0 -238px;}
-li.plupload_done {color:#AAA}
-
-li.plupload_delete a {
- background: url('../img/delete.gif');
-}
-
-li.plupload_failed a {
- background: url('../img/error.gif');
- cursor: default;
-}
-
-li.plupload_done a {
- background: url('../img/done.gif');
- cursor: default;
-}
-
-.plupload_progress, .plupload_upload_status {
- display: none;
-}
-
-.plupload_progress_container {
- margin-top: 3px;
- border: 1px solid #CCC;
- background: #FFF;
- padding: 1px;
-}
-.plupload_progress_bar {
- width: 0px;
- height: 7px;
- background: #CDEB8B;
-}
-
-.plupload_scroll .plupload_filelist_header .plupload_file_action, .plupload_scroll .plupload_filelist_footer .plupload_file_action {
- margin-right: 17px;
-}
-
-/* Floats */
-
-.plupload_clear,.plupload_clearer {clear: both;}
-.plupload_clearer, .plupload_progress_bar {
- display: block;
- font-size: 0;
- line-height: 0;
-}
-
-li.plupload_droptext {
- background: transparent;
- text-align: center;
- vertical-align: middle;
- border: 0;
- line-height: 165px;
-}
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/backgrounds.gif b/phpBB/assets/plupload/jquery.plupload.queue/img/backgrounds.gif
deleted file mode 100644
index 39e33ebc02..0000000000
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/backgrounds.gif
+++ /dev/null
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/buttons-disabled.png b/phpBB/assets/plupload/jquery.plupload.queue/img/buttons-disabled.png
deleted file mode 100644
index afa11af9b9..0000000000
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/buttons-disabled.png
+++ /dev/null
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/buttons.png b/phpBB/assets/plupload/jquery.plupload.queue/img/buttons.png
deleted file mode 100644
index 153e73885a..0000000000
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/buttons.png
+++ /dev/null
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/delete.gif b/phpBB/assets/plupload/jquery.plupload.queue/img/delete.gif
deleted file mode 100644
index 78ca8b3b49..0000000000
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/delete.gif
+++ /dev/null
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/img/transp50.png b/phpBB/assets/plupload/jquery.plupload.queue/img/transp50.png
deleted file mode 100644
index eb0efe104b..0000000000
--- a/phpBB/assets/plupload/jquery.plupload.queue/img/transp50.png
+++ /dev/null
Binary files differ
diff --git a/phpBB/assets/plupload/jquery.plupload.queue/jquery.plupload.queue.min.js b/phpBB/assets/plupload/jquery.plupload.queue/jquery.plupload.queue.min.js
deleted file mode 100644
index c20be880bc..0000000000
--- a/phpBB/assets/plupload/jquery.plupload.queue/jquery.plupload.queue.min.js
+++ /dev/null
@@ -1 +0,0 @@
-;(function(e){function n(e){return plupload.translate(e)||e}function r(t,r){r.contents().each(function(t,n){n=e(n),n.is(".plupload")||n.remove()}),r.prepend('<div class="plupload_wrapper plupload_scroll"><div id="'+t+'_container" class="plupload_container">'+'<div class="plupload">'+'<div class="plupload_header">'+'<div class="plupload_header_content">'+'<div class="plupload_header_title">'+n("Select files")+"</div>"+'<div class="plupload_header_text">'+n("Add files to the upload queue and click the start button.")+"</div>"+"</div>"+"</div>"+'<div class="plupload_content">'+'<div class="plupload_filelist_header">'+'<div class="plupload_file_name">'+n("Filename")+"</div>"+'<div class="plupload_file_action">&nbsp;</div>'+'<div class="plupload_file_status"><span>'+n("Status")+"</span></div>"+'<div class="plupload_file_size">'+n("Size")+"</div>"+'<div class="plupload_clearer">&nbsp;</div>'+"</div>"+'<ul id="'+t+'_filelist" class="plupload_filelist"></ul>'+'<div class="plupload_filelist_footer">'+'<div class="plupload_file_name">'+'<div class="plupload_buttons">'+'<a href="#" class="plupload_button plupload_add" id="'+t+'_browse">'+n("Add Files")+"</a>"+'<a href="#" class="plupload_button plupload_start">'+n("Start Upload")+"</a>"+"</div>"+'<span class="plupload_upload_status"></span>'+"</div>"+'<div class="plupload_file_action"></div>'+'<div class="plupload_file_status"><span class="plupload_total_status">0%</span></div>'+'<div class="plupload_file_size"><span class="plupload_total_file_size">0 b</span></div>'+'<div class="plupload_progress">'+'<div class="plupload_progress_container">'+'<div class="plupload_progress_bar"></div>'+"</div>"+"</div>"+'<div class="plupload_clearer">&nbsp;</div>'+"</div>"+"</div>"+"</div>"+"</div>"+'<input type="hidden" id="'+t+'_count" name="'+t+'_count" value="0" />'+"</div>")}var t={};e.fn.pluploadQueue=function(i){return i?(this.each(function(){function f(t){var n;t.status==plupload.DONE&&(n="plupload_done"),t.status==plupload.FAILED&&(n="plupload_failed"),t.status==plupload.QUEUED&&(n="plupload_delete"),t.status==plupload.UPLOADING&&(n="plupload_uploading");var r=e("#"+t.id).attr("class",n).find("a").css("display","block");t.hint&&r.attr("title",t.hint)}function l(){e("span.plupload_total_status",o).html(s.total.percent+"%"),e("div.plupload_progress_bar",o).css("width",s.total.percent+"%"),e("span.plupload_upload_status",o).html(n("Uploaded %d/%d files").replace(/%d\/%d/,s.total.uploaded+"/"+s.files.length))}function c(){var t=e("ul.plupload_filelist",o).html(""),r=0,i;e.each(s.files,function(n,o){i="",o.status==plupload.DONE&&(o.target_name&&(i+='<input type="hidden" name="'+u+"_"+r+'_tmpname" value="'+plupload.xmlEncode(o.target_name)+'" />'),i+='<input type="hidden" name="'+u+"_"+r+'_name" value="'+plupload.xmlEncode(o.name)+'" />',i+='<input type="hidden" name="'+u+"_"+r+'_status" value="'+(o.status==plupload.DONE?"done":"failed")+'" />',r++,e("#"+u+"_count").val(r)),t.append('<li id="'+o.id+'">'+'<div class="plupload_file_name"><span>'+o.name+"</span></div>"+'<div class="plupload_file_action"><a href="#"></a></div>'+'<div class="plupload_file_status">'+o.percent+"%</div>"+'<div class="plupload_file_size">'+plupload.formatSize(o.size)+"</div>"+'<div class="plupload_clearer">&nbsp;</div>'+i+"</li>"),f(o),e("#"+o.id+".plupload_delete a").click(function(t){e("#"+o.id).remove(),s.removeFile(o),t.preventDefault()})}),e("span.plupload_total_file_size",o).html(plupload.formatSize(s.total.size)),s.total.queued===0?e("span.plupload_add_text",o).html(n("Add Files")):e("span.plupload_add_text",o).html(n("%d files queued").replace(/%d/,s.total.queued)),e("a.plupload_start",o).toggleClass("plupload_disabled",s.files.length==s.total.uploaded+s.total.failed),t[0].scrollTop=t[0].scrollHeight,l(),!s.files.length&&s.features.dragdrop&&s.settings.dragdrop&&e("#"+u+"_filelist").append('<li class="plupload_droptext">'+n("Drag files here.")+"</li>")}function h(){delete t[u],s.destroy(),o.html(a),s=o=a=null}var s,o,u,a;o=e(this),u=o.attr("id"),u||(u=plupload.guid(),o.attr("id",u)),a=o.html(),r(u,o),s=new plupload.Uploader(e.extend({dragdrop:!0,browse_button:u+"_browse",container:u},i)),t[u]=s,s.bind("UploadFile",function(t,n){e("#"+n.id).addClass("plupload_current_file")}),s.bind("Init",function(t,n){!i.unique_names&&i.rename&&o.on("click","#"+u+"_filelist div.plupload_file_name span",function(n){var r=e(n.target),i,s,o,u="";i=t.getFile(r.parents("li")[0].id),o=i.name,s=/^(.+)(\.[^.]+)$/.exec(o),s&&(o=s[1],u=s[2]),r.hide().after('<input type="text" />'),r.next().val(o).focus().blur(function(){r.show().next().remove()}).keydown(function(t){var n=e(this);t.keyCode==13&&(t.preventDefault(),i.name=n.val()+u,r.html(i.name),n.blur())})}),t.settings.dragdrop&&(t.settings.drop_element=u+"_filelist"),e("#"+u+"_container").attr("title","Using runtime: "+n.runtime),e("a.plupload_start",o).click(function(t){e(this).hasClass("plupload_disabled")||s.start(),t.preventDefault()}),e("a.plupload_stop",o).click(function(e){e.preventDefault(),s.stop()}),e("a.plupload_start",o).addClass("plupload_disabled")}),s.bind("Error",function(t,r){var i=r.file,s;i&&(s=r.message,r.details&&(s+=" ("+r.details+")"),r.code==plupload.FILE_SIZE_ERROR&&alert(n("Error: File too large:")+" "+i.name),r.code==plupload.FILE_EXTENSION_ERROR&&alert(n("Error: Invalid file extension:")+" "+i.name),i.hint=s,e("#"+i.id).attr("class","plupload_failed").find("a").css("display","block").attr("title",s)),r.code===plupload.INIT_ERROR&&setTimeout(function(){h()},1)}),s.bind("PostInit",function(t){t.settings.dragdrop&&t.features.dragdrop&&e("#"+u+"_filelist").append('<li class="plupload_droptext">'+n("Drag files here.")+"</li>")}),s.init(),s.bind("StateChanged",function(){s.state===plupload.STARTED?(e("li.plupload_delete a,div.plupload_buttons",o).hide(),e("span.plupload_upload_status,div.plupload_progress,a.plupload_stop",o).css("display","block"),e("span.plupload_upload_status",o).html("Uploaded "+s.total.uploaded+"/"+s.files.length+" files"),i.multiple_queues&&e("span.plupload_total_status,span.plupload_total_file_size",o).show()):(c(),e("a.plupload_stop,div.plupload_progress",o).hide(),e("a.plupload_delete",o).css("display","block"),i.multiple_queues&&s.total.uploaded+s.total.failed==s.files.length&&(e(".plupload_buttons,.plupload_upload_status",o).css("display","inline"),e(".plupload_start",o).addClass("plupload_disabled"),e("span.plupload_total_status,span.plupload_total_file_size",o).hide()))}),s.bind("QueueChanged",c),s.bind("FileUploaded",function(e,t){f(t)}),s.bind("UploadProgress",function(t,n){e("#"+n.id+" div.plupload_file_status",o).html(n.percent+"%"),f(n),l()}),i.setup&&i.setup(s)}),this):t[e(this[0]).attr("id")]}})(jQuery); \ No newline at end of file
diff --git a/phpBB/assets/plupload/plupload.full.min.js b/phpBB/assets/plupload/plupload.full.min.js
index 12e72ccfcb..69d6ad120c 100644
--- a/phpBB/assets/plupload/plupload.full.min.js
+++ b/phpBB/assets/plupload/plupload.full.min.js
@@ -1,6 +1,6 @@
/**
* mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill
- * v1.0.0
+ * v1.2.0
*
* Copyright 2013, Moxiecode Systems AB
* Released under GPL License.
@@ -8,14 +8,14 @@
* License: http://www.plupload.com/license
* Contributing: http://www.plupload.com/contributing
*
- * Date: 2013-09-23
+ * Date: 2014-01-16
*/
-!function(e,t){"use strict";function n(e,t){for(var n,i=[],r=0;r<e.length;++r){if(n=s[e[r]]||o(e[r]),!n)throw"module definition dependecy not found: "+e[r];i.push(n)}t.apply(null,i)}function i(e,i,r){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(i===t)throw"invalid module definition, dependencies must be specified";if(r===t)throw"invalid module definition, definition function must be specified";n(i,function(){s[e]=r.apply(null,arguments)})}function r(e){return!!s[e]}function o(t){for(var n=e,i=t.split(/[.\/]/),r=0;r<i.length;++r){if(!n[i[r]])return;n=n[i[r]]}return n}function a(n){for(var i=0;i<n.length;i++){for(var r=e,o=n[i],a=o.split(/[.\/]/),u=0;u<a.length-1;++u)r[a[u]]===t&&(r[a[u]]={}),r=r[a[u]];r[a[a.length-1]]=s[o]}}var s={},u="moxie/core/utils/Basic",c="moxie/core/I18n",l="moxie/core/utils/Mime",d="moxie/core/utils/Env",f="moxie/core/utils/Dom",p="moxie/core/Exceptions",h="moxie/core/EventTarget",m="moxie/core/utils/Encode",g="moxie/runtime/Runtime",v="moxie/runtime/RuntimeClient",y="moxie/file/Blob",E="moxie/file/File",_="moxie/file/FileInput",R="moxie/file/FileDrop",w="moxie/runtime/RuntimeTarget",x="moxie/file/FileReader",b="moxie/core/utils/Url",A="moxie/file/FileReaderSync",T="moxie/xhr/FormData",S="moxie/xhr/XMLHttpRequest",O="moxie/runtime/Transporter",I="moxie/core/JSON",D="moxie/image/Image",L="moxie/runtime/html5/Runtime",N="moxie/runtime/html5/file/Blob",M="moxie/core/utils/Events",C="moxie/runtime/html5/file/FileInput",F="moxie/runtime/html5/file/FileDrop",P="moxie/runtime/html5/file/FileReader",H="moxie/runtime/html5/xhr/XMLHttpRequest",B="moxie/runtime/html5/utils/BinaryReader",U="moxie/runtime/html5/image/JPEGHeaders",G="moxie/runtime/html5/image/ExifParser",z="moxie/runtime/html5/image/JPEG",q="moxie/runtime/html5/image/PNG",k="moxie/runtime/html5/image/ImageInfo",X="moxie/runtime/html5/image/MegaPixel",j="moxie/runtime/html5/image/Image",V="moxie/runtime/flash/Runtime",W="moxie/runtime/flash/file/Blob",Y="moxie/runtime/flash/file/FileInput",$="moxie/runtime/flash/file/FileReader",K="moxie/runtime/flash/file/FileReaderSync",Z="moxie/runtime/flash/xhr/XMLHttpRequest",J="moxie/runtime/flash/runtime/Transporter",Q="moxie/runtime/flash/image/Image",et="moxie/runtime/silverlight/Runtime",tt="moxie/runtime/silverlight/file/Blob",nt="moxie/runtime/silverlight/file/FileInput",it="moxie/runtime/silverlight/file/FileDrop",rt="moxie/runtime/silverlight/file/FileReader",ot="moxie/runtime/silverlight/file/FileReaderSync",at="moxie/runtime/silverlight/xhr/XMLHttpRequest",st="moxie/runtime/silverlight/runtime/Transporter",ut="moxie/runtime/silverlight/image/Image",ct="moxie/runtime/html4/Runtime",lt="moxie/runtime/html4/file/FileInput",dt="moxie/runtime/html4/file/FileReader",ft="moxie/runtime/html4/xhr/XMLHttpRequest",pt="moxie/runtime/html4/image/Image";i(u,[],function(){var e=function(e){var t;return e===t?"undefined":null===e?"null":e.nodeType?"node":{}.toString.call(e).match(/\s([a-z|A-Z]+)/)[1].toLowerCase()},t=function(i){var r;return n(arguments,function(a,s){s>0&&n(a,function(n,a){n!==r&&(e(i[a])===e(n)&&~o(e(n),["array","object"])?t(i[a],n):i[a]=n)})}),i},n=function(e,t){var n,i,r,o;if(e){try{n=e.length}catch(a){n=o}if(n===o){for(i in e)if(e.hasOwnProperty(i)&&t(e[i],i)===!1)return}else for(r=0;n>r;r++)if(t(e[r],r)===!1)return}},i=function(t){var n;if(!t||"object"!==e(t))return!0;for(n in t)return!1;return!0},r=function(t,n){function i(r){"function"===e(t[r])&&t[r](function(e){++r<o&&!e?i(r):n(e)})}var r=0,o=t.length;"function"!==e(n)&&(n=function(){}),t&&t.length||n(),i(r)},o=function(e,t){if(t){if(Array.prototype.indexOf)return Array.prototype.indexOf.call(t,e);for(var n=0,i=t.length;i>n;n++)if(t[n]===e)return n}return-1},a=function(t,n){var i=[];"array"!==e(t)&&(t=[t]),"array"!==e(n)&&(n=[n]);for(var r in t)-1===o(t[r],n)&&i.push(t[r]);return i.length?i:!1},s=function(e,t){var i=[];return n(e,function(e){-1!==o(e,t)&&i.push(e)}),i.length?i:null},u=function(e){var t,n=[];for(t=0;t<e.length;t++)n[t]=e[t];return n},c=function(){var e=0;return function(t){var n=(new Date).getTime().toString(32),i;for(i=0;5>i;i++)n+=Math.floor(65535*Math.random()).toString(32);return(t||"o_")+n+(e++).toString(32)}}(),l=function(e){return e?String.prototype.trim?String.prototype.trim.call(e):e.toString().replace(/^\s*/,"").replace(/\s*$/,""):e},d=function(e){if("string"!=typeof e)return e;var t={t:1099511627776,g:1073741824,m:1048576,k:1024},n;return e=/^([0-9]+)([mgk]?)$/.exec(e.toLowerCase().replace(/[^0-9mkg]/g,"")),n=e[2],e=+e[1],t.hasOwnProperty(n)&&(e*=t[n]),e};return{guid:c,typeOf:e,extend:t,each:n,isEmptyObj:i,inSeries:r,inArray:o,arrayDiff:a,arrayIntersect:s,toArray:u,trim:l,parseSizeStr:d}}),i(c,[u],function(e){var t={};return{addI18n:function(n){return e.extend(t,n)},translate:function(e){return t[e]||e},_:function(e){return this.translate(e)},sprintf:function(t){var n=[].slice.call(arguments,1),i="";return e.each(t.split(/%[a-z]/),function(e){i+=e,n.length&&(i+=n.shift())}),i}}}),i(l,[u,c],function(e,t){var n="application/msword,doc dot,application/pdf,pdf,application/pgp-signature,pgp,application/postscript,ps ai eps,application/rtf,rtf,application/vnd.ms-excel,xls xlb,application/vnd.ms-powerpoint,ppt pps pot,application/zip,zip,application/x-shockwave-flash,swf swfl,application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx,application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx,application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx,application/vnd.openxmlformats-officedocument.presentationml.template,potx,application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx,application/x-javascript,js,application/json,json,audio/mpeg,mp3 mpga mpega mp2,audio/x-wav,wav,audio/mp4,m4a,audio/ogg,oga ogg,audio/aiff,aiff aif,audio/flac,flac,audio/aac,aac,audio/ac3,ac3,audio/x-ms-wma,wma,image/bmp,bmp,image/gif,gif,image/jpeg,jpg jpeg jpe,image/photoshop,psd,image/png,png,image/svg+xml,svg svgz,image/tiff,tiff tif,text/plain,asc txt text diff log,text/html,htm html xhtml,text/css,css,text/csv,csv,text/rtf,rtf,video/mpeg,mpeg mpg mpe m2v,video/quicktime,qt mov,video/mp4,mp4,video/x-m4v,m4v,video/x-flv,flv,video/x-ms-wmv,wmv,video/avi,avi,video/webm,webm,video/3gpp,3gpp 3gp,video/3gpp2,3g2,video/vnd.rn-realvideo,rv,video/ogg,ogv,video/x-matroska,mkv,application/vnd.oasis.opendocument.formula-template,otf,application/octet-stream,exe",i={mimes:{},extensions:{},addMimeType:function(e){var t=e.split(/,/),n,i,r;for(n=0;n<t.length;n+=2){for(r=t[n+1].split(/ /),i=0;i<r.length;i++)this.mimes[r[i]]=t[n];this.extensions[t[n]]=r}},extList2mimes:function(t,n){var i=this,r,o,a,s,u=[];for(o=0;o<t.length;o++)for(r=t[o].extensions.split(/\s*,\s*/),a=0;a<r.length;a++){if("*"===r[a])return[];if(s=i.mimes[r[a]])-1===e.inArray(s,u)&&u.push(s);else{if(!n||!/^\w+$/.test(r[a]))return[];u.push("."+r[a])}}return u},mimes2exts:function(t){var n=this,i=[];return e.each(t,function(t){if("*"===t)return i=[],!1;var r=t.match(/^(\w+)\/(\*|\w+)$/);r&&("*"===r[2]?e.each(n.extensions,function(e,t){new RegExp("^"+r[1]+"/").test(t)&&[].push.apply(i,n.extensions[t])}):n.extensions[t]&&[].push.apply(i,n.extensions[t]))}),i},mimes2extList:function(n){var i=[],r=[];return"string"===e.typeOf(n)&&(n=e.trim(n).split(/\s*,\s*/)),r=this.mimes2exts(n),i.push({title:t.translate("Files"),extensions:r.length?r.join(","):"*"}),i.mimes=n,i},getFileExtension:function(e){var t=e&&e.match(/\.([^.]+)$/);return t?t[1].toLowerCase():""},getFileMime:function(e){return this.mimes[this.getFileExtension(e)]||""}};return i.addMimeType(n),i}),i(d,[u],function(e){function t(e){for(var t,n,i=0;i<e.length;i++)if(t=e[i].s1,n=e[i].prop,o=e[i].sv||e[i].id,t){if(-1!=t.indexOf(e[i].s2))return e[i].id}else if(n)return e[i].id}function n(e){var t=e.indexOf(o);if(-1!=t)return parseFloat(e.substring(t+o.length+1))}var i=[{s1:navigator.userAgent,s2:"Android",id:"Android Browser",sv:"Version"},{s1:navigator.userAgent,s2:"Chrome",id:"Chrome"},{s1:navigator.vendor,s2:"Apple",id:"Safari",sv:"Version"},{prop:window.opera&&window.opera.buildNumber,id:"Opera",sv:"Version"},{s1:navigator.vendor,s2:"KDE",id:"Konqueror"},{s1:navigator.userAgent,s2:"Firefox",id:"Firefox"},{s1:navigator.vendor,s2:"Camino",id:"Camino"},{s1:navigator.userAgent,s2:"Netscape",id:"Netscape"},{s1:navigator.userAgent,s2:"MSIE",id:"IE",sv:"MSIE"},{s1:navigator.userAgent,s2:"Gecko",id:"Mozilla",sv:"rv"}],r=[{s1:navigator.platform,s2:"Win",id:"Windows"},{s1:navigator.platform,s2:"Mac",id:"Mac"},{s1:navigator.userAgent,s2:"iPhone",id:"iOS"},{s1:navigator.userAgent,s2:"iPad",id:"iOS"},{s1:navigator.userAgent,s2:"Android",id:"Android"},{s1:navigator.platform,s2:"Linux",id:"Linux"}],o,a=function(){var t={define_property:function(){return!1}(),create_canvas:function(){var e=document.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))}(),return_response_type:function(t){try{if(-1!==e.inArray(t,["","text","document"]))return!0;if(window.XMLHttpRequest){var n=new XMLHttpRequest;if(n.open("get","/"),"responseType"in n)return n.responseType=t,n.responseType!==t?!1:!0}}catch(i){}return!1},use_data_uri:function(){var e=new Image;return e.onload=function(){t.use_data_uri=1===e.width&&1===e.height},setTimeout(function(){e.src="data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="},1),!1}(),use_data_uri_over32kb:function(){return t.use_data_uri&&("IE"!==s.browser||s.version>=9)},use_data_uri_of:function(e){return t.use_data_uri&&33e3>e||t.use_data_uri_over32kb()},use_fileinput:function(){var e=document.createElement("input");return e.setAttribute("type","file"),!e.disabled}};return function(n){var i=[].slice.call(arguments);return i.shift(),"function"===e.typeOf(t[n])?t[n].apply(this,i):!!t[n]}}(),s={can:a,browser:t(i),version:n(navigator.userAgent)||n(navigator.appVersion),OS:t(r),swf_url:"../flash/Moxie.swf",xap_url:"../silverlight/Moxie.xap",global_event_dispatcher:"moxie.core.EventTarget.instance.dispatchEvent"};return s}),i(f,[d],function(e){var t=function(e){return"string"!=typeof e?e:document.getElementById(e)},n=function(e,t){var n;return""===e.className?!1:(n=new RegExp("(^|\\s+)"+t+"(\\s+|$)"),n.test(e.className))},i=function(e,t){n(e,t)||(e.className=""===e.className?t:e.className.replace(/\s+$/,"")+" "+t)},r=function(e,t){var n=new RegExp("(^|\\s+)"+t+"(\\s+|$)");e.className=e.className.replace(n,function(e,t,n){return" "===t&&" "===n?" ":""})},o=function(e,t){return e.currentStyle?e.currentStyle[t]:window.getComputedStyle?window.getComputedStyle(e,null)[t]:void 0},a=function(t,n){function i(e){var t,n,i=0,r=0;return e&&(n=e.getBoundingClientRect(),t="CSS1Compat"===s.compatMode?s.documentElement:s.body,i=n.left+t.scrollLeft,r=n.top+t.scrollTop),{x:i,y:r}}var r=0,o=0,a,s=document,u,c;if(t=t,n=n||s.body,t&&t.getBoundingClientRect&&"IE"===e.browser&&(!s.documentMode||s.documentMode<8))return u=i(t),c=i(n),{x:u.x-c.x,y:u.y-c.y};for(a=t;a&&a!=n&&a.nodeType;)r+=a.offsetLeft||0,o+=a.offsetTop||0,a=a.offsetParent;for(a=t.parentNode;a&&a!=n&&a.nodeType;)r-=a.scrollLeft||0,o-=a.scrollTop||0,a=a.parentNode;return{x:r,y:o}},s=function(e){return{w:e.offsetWidth||e.clientWidth,h:e.offsetHeight||e.clientHeight}};return{get:t,hasClass:n,addClass:i,removeClass:r,getStyle:o,getPos:a,getSize:s}}),i(p,[u],function(e){function t(e,t){var n;for(n in e)if(e[n]===t)return n;return null}return{RuntimeError:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": RuntimeError "+this.code}var i={NOT_INIT_ERR:1,NOT_SUPPORTED_ERR:9,JS_ERR:4};return e.extend(n,i),n.prototype=Error.prototype,n}(),OperationNotAllowedException:function(){function t(e){this.code=e,this.name="OperationNotAllowedException"}return e.extend(t,{NOT_ALLOWED_ERR:1}),t.prototype=Error.prototype,t}(),ImageError:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": ImageError "+this.code}var i={WRONG_FORMAT:1,MAX_RESOLUTION_ERR:2};return e.extend(n,i),n.prototype=Error.prototype,n}(),FileException:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": FileException "+this.code}var i={NOT_FOUND_ERR:1,SECURITY_ERR:2,ABORT_ERR:3,NOT_READABLE_ERR:4,ENCODING_ERR:5,NO_MODIFICATION_ALLOWED_ERR:6,INVALID_STATE_ERR:7,SYNTAX_ERR:8};return e.extend(n,i),n.prototype=Error.prototype,n}(),DOMException:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": DOMException "+this.code}var i={INDEX_SIZE_ERR:1,DOMSTRING_SIZE_ERR:2,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,INVALID_CHARACTER_ERR:5,NO_DATA_ALLOWED_ERR:6,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INUSE_ATTRIBUTE_ERR:10,INVALID_STATE_ERR:11,SYNTAX_ERR:12,INVALID_MODIFICATION_ERR:13,NAMESPACE_ERR:14,INVALID_ACCESS_ERR:15,VALIDATION_ERR:16,TYPE_MISMATCH_ERR:17,SECURITY_ERR:18,NETWORK_ERR:19,ABORT_ERR:20,URL_MISMATCH_ERR:21,QUOTA_EXCEEDED_ERR:22,TIMEOUT_ERR:23,INVALID_NODE_TYPE_ERR:24,DATA_CLONE_ERR:25};return e.extend(n,i),n.prototype=Error.prototype,n}(),EventException:function(){function t(e){this.code=e,this.name="EventException"}return e.extend(t,{UNSPECIFIED_EVENT_TYPE_ERR:0}),t.prototype=Error.prototype,t}()}}),i(h,[p,u],function(e,t){function n(){var n={};t.extend(this,{uid:null,init:function(){this.uid||(this.uid=t.guid("uid_"))},addEventListener:function(e,i,r,o){var a=this,s;return e=t.trim(e),/\s/.test(e)?(t.each(e.split(/\s+/),function(e){a.addEventListener(e,i,r,o)}),void 0):(e=e.toLowerCase(),r=parseInt(r,10)||0,s=n[this.uid]&&n[this.uid][e]||[],s.push({fn:i,priority:r,scope:o||this}),n[this.uid]||(n[this.uid]={}),n[this.uid][e]=s,void 0)},hasEventListener:function(e){return e?!(!n[this.uid]||!n[this.uid][e]):!!n[this.uid]},removeEventListener:function(e,i){e=e.toLowerCase();var r=n[this.uid]&&n[this.uid][e],o;if(r){if(i){for(o=r.length-1;o>=0;o--)if(r[o].fn===i){r.splice(o,1);break}}else r=[];r.length||(delete n[this.uid][e],t.isEmptyObj(n[this.uid])&&delete n[this.uid])}},removeAllEventListeners:function(){n[this.uid]&&delete n[this.uid]},dispatchEvent:function(i){var r,o,a,s,u={};if("string"!==t.typeOf(i)){if(s=i,"string"!==t.typeOf(s.type))throw new e.EventException(e.EventException.UNSPECIFIED_EVENT_TYPE_ERR);i=s.type,s.total&&s.loaded&&(u.total=s.total,u.loaded=s.loaded),u.async=s.async||!1}if(-1!==i.indexOf("::")?function(e){r=e[0],i=e[1]}(i.split("::")):r=this.uid,i=i.toLowerCase(),o=n[r]&&n[r][i]){o.sort(function(e,t){return t.priority-e.priority}),a=[].slice.call(arguments),a.shift(),u.type=i,a.unshift(u);var c=[];t.each(o,function(e){a[0].target=e.scope,u.async?c.push(function(t){setTimeout(function(){t(e.fn.apply(e.scope,a)===!1)},1)}):c.push(function(t){t(e.fn.apply(e.scope,a)===!1)})}),c.length&&t.inSeries(c)}return!0},bind:function(){this.addEventListener.apply(this,arguments)},unbind:function(){this.removeEventListener.apply(this,arguments)},unbindAll:function(){this.removeAllEventListeners.apply(this,arguments)},trigger:function(){this.dispatchEvent.apply(this,arguments)},convertEventPropsToHandlers:function(e){var n;"array"!==t.typeOf(e)&&(e=[e]);for(var i=0;i<e.length;i++)n="on"+e[i],"function"===t.typeOf(this[n])?this.addEventListener(e[i],this[n]):"undefined"===t.typeOf(this[n])&&(this[n]=null)}})}return n.instance=new n,n}),i(m,[],function(){var e=function(e){return unescape(encodeURIComponent(e))},t=function(e){return decodeURIComponent(escape(e))},n=function(e,n){if("function"==typeof window.atob)return n?t(window.atob(e)):window.atob(e);var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",r,o,a,s,u,c,l,d,f=0,p=0,h="",m=[];if(!e)return e;e+="";do s=i.indexOf(e.charAt(f++)),u=i.indexOf(e.charAt(f++)),c=i.indexOf(e.charAt(f++)),l=i.indexOf(e.charAt(f++)),d=s<<18|u<<12|c<<6|l,r=255&d>>16,o=255&d>>8,a=255&d,m[p++]=64==c?String.fromCharCode(r):64==l?String.fromCharCode(r,o):String.fromCharCode(r,o,a);while(f<e.length);return h=m.join(""),n?t(h):h},i=function(t,n){if(n&&e(t),"function"==typeof window.btoa)return window.btoa(t);var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",r,o,a,s,u,c,l,d,f=0,p=0,h="",m=[];if(!t)return t;do r=t.charCodeAt(f++),o=t.charCodeAt(f++),a=t.charCodeAt(f++),d=r<<16|o<<8|a,s=63&d>>18,u=63&d>>12,c=63&d>>6,l=63&d,m[p++]=i.charAt(s)+i.charAt(u)+i.charAt(c)+i.charAt(l);while(f<t.length);h=m.join("");var g=t.length%3;return(g?h.slice(0,g-3):h)+"===".slice(g||3)};return{utf8_encode:e,utf8_decode:t,atob:n,btoa:i}}),i(g,[u,f,h],function(e,t,n){function i(n,r,a,s,u){function c(t,i){var r=null,o=n&&n.required_caps;return i=i||"browser",null!==this.mode?this.mode:(o&&!e.isEmptyObj(t)&&(e.each(o,function(n,i){if(t.hasOwnProperty(i)){var o=t[i](n);if("string"==typeof o&&(o=[o]),r){if(!(r=e.arrayIntersect(r,o)))return r=!1}else r=o}}),r?this.mode=-1!==e.inArray(i,r)?i:r[0]:r===!1&&(this.mode=!1)),null===this.mode&&(this.mode=i),this.mode&&o&&!this.can(o)&&(this.mode=!1),void 0)}var l=this,d,f=e.guid(r+"_");o[f]=this,a=e.extend({access_binary:!1,access_image_binary:!1,display_media:!1,do_cors:!1,drag_and_drop:!1,filter_by_extension:!0,resize_image:!1,report_upload_progress:!1,return_response_headers:!1,return_response_type:!1,return_status_code:!0,send_custom_headers:!1,select_file:!1,select_folder:!1,select_multiple:!0,send_binary_string:!1,send_browser_cookies:!0,send_multipart:!0,slice_blob:!1,stream_upload:!1,summon_file_dialog:!1,upload_filesize:!0,use_http_method:!0},a),d=function(){var t={};return{exec:function(e,n,i,r){return d[n]&&(t[e]||(t[e]={context:this,instance:new d[n]}),t[e].instance[i])?t[e].instance[i].apply(this,r):void 0},removeInstance:function(e){delete t[e]},removeAllInstances:function(){var n=this;e.each(t,function(t,i){"function"===e.typeOf(t.instance.destroy)&&t.instance.destroy.call(t.context),n.removeInstance(i)})}}}(),e.extend(this,{initialized:!1,uid:f,type:r,mode:null,shimid:f+"_container",clients:0,options:n,can:function(t,n){var r=arguments[2]||a;if("string"===e.typeOf(t)&&"undefined"===e.typeOf(n)&&(t=i.parseCaps(t)),"object"===e.typeOf(t)){for(var o in t)if(!this.can(o,t[o],r))return!1;return!0}return"function"===e.typeOf(r[t])?r[t].call(this,n):n===r[t]},getShimContainer:function(){var n,i=t.get(this.shimid);return i||(n=this.options.container?t.get(this.options.container):document.body,i=document.createElement("div"),i.id=this.shimid,i.className="moxie-shim moxie-shim-"+this.type,e.extend(i.style,{position:"absolute",top:"0px",left:"0px",width:"1px",height:"1px",overflow:"hidden"}),n.appendChild(i),n=null),i},getShim:function(){return d},shimExec:function(e,t){var n=[].slice.call(arguments,2);return l.getShim().exec.call(this,this.uid,e,t,n)},exec:function(e,t){var n=[].slice.call(arguments,2);return l[e]&&l[e][t]?l[e][t].apply(this,n):l.shimExec.apply(this,arguments)},destroy:function(){if(l){var e=t.get(this.shimid);e&&e.parentNode.removeChild(e),d&&d.removeAllInstances(),this.unbindAll(),delete o[this.uid],this.uid=null,f=l=d=e=null}}}),c.call(this,s,u)}var r={},o={};return i.order="html5,flash,silverlight,html4",i.getRuntime=function(e){return o[e]?o[e]:!1},i.addConstructor=function(e,t){t.prototype=n.instance,r[e]=t},i.getConstructor=function(e){return r[e]||null},i.getInfo=function(e){var t=i.getRuntime(e);return t?{uid:t.uid,type:t.type,can:function(){return t.can.apply(t,arguments)}}:null},i.parseCaps=function(t){var n={};return"string"!==e.typeOf(t)?t||{}:(e.each(t.split(","),function(e){n[e]=!0}),n)},i.can=function(e,t){var n,r=i.getConstructor(e),o;return r?(n=new r({required_caps:t}),o=n.mode,n.destroy(),!!o):!1},i.thatCan=function(e,t){var n=(t||i.order).split(/\s*,\s*/);for(var r in n)if(i.can(n[r],e))return n[r];return null},i.capTrue=function(){return!0},i.capFalse=function(){return!1},i.capTest=function(e){return function(){return!!e}},i}),i(v,[p,u,g],function(e,t,n){return function i(){var i;t.extend(this,{connectRuntime:function(r){function o(t){var s,u;return t.length?(s=t.shift(),(u=n.getConstructor(s))?(i=new u(r),i.bind("Init",function(){i.initialized=!0,setTimeout(function(){i.clients++,a.trigger("RuntimeInit",i)},1)}),i.bind("Error",function(){i.destroy(),o(t)}),i.mode?(i.init(),void 0):(i.trigger("Error"),void 0)):(o(t),void 0)):(a.trigger("RuntimeError",new e.RuntimeError(e.RuntimeError.NOT_INIT_ERR)),i=null,void 0)}var a=this,s;if("string"===t.typeOf(r)?s=r:"string"===t.typeOf(r.ruid)&&(s=r.ruid),s){if(i=n.getRuntime(s))return i.clients++,i;throw new e.RuntimeError(e.RuntimeError.NOT_INIT_ERR)}o((r.runtime_order||n.order).split(/\s*,\s*/))},getRuntime:function(){return i&&i.uid?i:(i=null,null)},disconnectRuntime:function(){i&&--i.clients<=0&&(i.destroy(),i=null)}})}}),i(y,[u,m,v],function(e,t,n){function i(o,a){function s(t,n,o){var a,s=r[this.uid];return"string"===e.typeOf(s)&&s.length?(a=new i(null,{type:o,size:n-t}),a.detach(s.substr(t,a.size)),a):null}n.call(this),o&&this.connectRuntime(o),a?"string"===e.typeOf(a)&&(a={data:a}):a={},e.extend(this,{uid:a.uid||e.guid("uid_"),ruid:o,size:a.size||0,type:a.type||"",slice:function(e,t,n){return this.isDetached()?s.apply(this,arguments):this.getRuntime().exec.call(this,"Blob","slice",this.getSource(),e,t,n)},getSource:function(){return r[this.uid]?r[this.uid]:null},detach:function(e){this.ruid&&(this.getRuntime().exec.call(this,"Blob","destroy",r[this.uid]),this.disconnectRuntime(),this.ruid=null),e=e||"";var n=e.match(/^data:([^;]*);base64,/);n&&(this.type=n[1],e=t.atob(e.substring(e.indexOf("base64,")+7))),this.size=e.length,r[this.uid]=e},isDetached:function(){return!this.ruid&&"string"===e.typeOf(r[this.uid])},destroy:function(){this.detach(),delete r[this.uid]}}),a.data?this.detach(a.data):r[this.uid]=a}var r={};return i}),i(E,[u,l,y],function(e,t,n){function i(i,r){var o,a;if(r||(r={}),a=r.type&&""!==r.type?r.type:t.getFileMime(r.name),r.name)o=r.name.replace(/\\/g,"/"),o=o.substr(o.lastIndexOf("/")+1);else{var s=a.split("/")[0];o=e.guid((""!==s?s:"file")+"_"),t.extensions[a]&&(o+="."+t.extensions[a][0])}n.apply(this,arguments),e.extend(this,{type:a||"",name:o||e.guid("file_"),lastModifiedDate:r.lastModifiedDate||(new Date).toLocaleString()})}return i.prototype=n.prototype,i}),i(_,[u,l,f,p,h,c,E,g,v],function(e,t,n,i,r,o,a,s,u){function c(r){var c=this,d,f,p;if(-1!==e.inArray(e.typeOf(r),["string","node"])&&(r={browse_button:r}),f=n.get(r.browse_button),!f)throw new i.DOMException(i.DOMException.NOT_FOUND_ERR);p={accept:[{title:o.translate("All Files"),extensions:"*"}],name:"file",multiple:!1,required_caps:!1,container:f.parentNode||document.body},r=e.extend({},p,r),"string"==typeof r.required_caps&&(r.required_caps=s.parseCaps(r.required_caps)),"string"==typeof r.accept&&(r.accept=t.mimes2extList(r.accept)),d=n.get(r.container),d||(d=document.body),"static"===n.getStyle(d,"position")&&(d.style.position="relative"),d=f=null,u.call(c),e.extend(c,{uid:e.guid("uid_"),ruid:null,files:null,init:function(){c.convertEventPropsToHandlers(l),c.bind("RuntimeInit",function(t,i){c.ruid=i.uid,c.bind("Ready",function(){c.trigger("Refresh")},999),c.bind("Change",function(){var t=i.exec.call(c,"FileInput","getFiles");c.files=[],e.each(t,function(e){return 0===e.size?!0:(c.files.push(new a(c.ruid,e)),void 0)})},999),c.bind("Refresh",function(){var t,o,a,s;a=n.get(r.browse_button),s=n.get(i.shimid),a&&(t=n.getPos(a,n.get(r.container)),o=n.getSize(a),s&&e.extend(s.style,{top:t.y+"px",left:t.x+"px",width:o.w+"px",height:o.h+"px"})),s=a=null}),i.exec.call(c,"FileInput","init",r)}),c.connectRuntime(e.extend({},r,{required_caps:{select_file:!0}}))},disable:function(t){var n=this.getRuntime();n&&n.exec.call(this,"FileInput","disable","undefined"===e.typeOf(t)?!0:t)},refresh:function(){c.trigger("Refresh")},destroy:function(){var t=this.getRuntime();t&&(t.exec.call(this,"FileInput","destroy"),this.disconnectRuntime()),"array"===e.typeOf(this.files)&&e.each(this.files,function(e){e.destroy()}),this.files=null}})}var l=["ready","change","cancel","mouseenter","mouseleave","mousedown","mouseup"];return c.prototype=r.instance,c}),i(R,[c,f,p,u,E,v,h,l],function(e,t,n,i,r,o,a,s){function u(n){var a=this,u;"string"==typeof n&&(n={drop_zone:n}),u={accept:[{title:e.translate("All Files"),extensions:"*"}],required_caps:{drag_and_drop:!0}},n="object"==typeof n?i.extend({},u,n):u,n.container=t.get(n.drop_zone)||document.body,"static"===t.getStyle(n.container,"position")&&(n.container.style.position="relative"),"string"==typeof n.accept&&(n.accept=s.mimes2extList(n.accept)),o.call(a),i.extend(a,{uid:i.guid("uid_"),ruid:null,files:null,init:function(){a.convertEventPropsToHandlers(c),a.bind("RuntimeInit",function(e,t){a.ruid=t.uid,a.bind("Drop",function(){var e=t.exec.call(a,"FileDrop","getFiles");a.files=[],i.each(e,function(e){a.files.push(new r(a.ruid,e))})},999),t.exec.call(a,"FileDrop","init",n),a.dispatchEvent("ready")}),a.connectRuntime(n)},destroy:function(){var e=this.getRuntime();e&&(e.exec.call(this,"FileDrop","destroy"),this.disconnectRuntime()),this.files=null}})}var c=["ready","dragenter","dragleave","drop","error"];return u.prototype=a.instance,u}),i(w,[u,v,h],function(e,t,n){function i(){this.uid=e.guid("uid_"),t.call(this),this.destroy=function(){this.disconnectRuntime(),this.unbindAll()}}return i.prototype=n.instance,i}),i(x,[u,m,p,h,y,E,w],function(e,t,n,i,r,o,a){function s(){function i(e,i){function l(e){o.readyState=s.DONE,o.error=e,o.trigger("error"),d()}function d(){c.destroy(),c=null,o.trigger("loadend")}function f(t){c.bind("Error",function(e,t){l(t)}),c.bind("Progress",function(e){o.result=t.exec.call(c,"FileReader","getResult"),o.trigger(e)}),c.bind("Load",function(e){o.readyState=s.DONE,o.result=t.exec.call(c,"FileReader","getResult"),o.trigger(e),d()}),t.exec.call(c,"FileReader","read",e,i)}if(c=new a,this.convertEventPropsToHandlers(u),this.readyState===s.LOADING)return l(new n.DOMException(n.DOMException.INVALID_STATE_ERR));if(this.readyState=s.LOADING,this.trigger("loadstart"),i instanceof r)if(i.isDetached()){var p=i.getSource();switch(e){case"readAsText":case"readAsBinaryString":this.result=p;break;case"readAsDataURL":this.result="data:"+i.type+";base64,"+t.btoa(p)}this.readyState=s.DONE,this.trigger("load"),d()}else f(c.connectRuntime(i.ruid));else l(new n.DOMException(n.DOMException.NOT_FOUND_ERR))}var o=this,c;e.extend(this,{uid:e.guid("uid_"),readyState:s.EMPTY,result:null,error:null,readAsBinaryString:function(e){i.call(this,"readAsBinaryString",e)},readAsDataURL:function(e){i.call(this,"readAsDataURL",e)},readAsText:function(e){i.call(this,"readAsText",e)},abort:function(){this.result=null,-1===e.inArray(this.readyState,[s.EMPTY,s.DONE])&&(this.readyState===s.LOADING&&(this.readyState=s.DONE),c&&c.getRuntime().exec.call(this,"FileReader","abort"),this.trigger("abort"),this.trigger("loadend"))},destroy:function(){this.abort(),c&&(c.getRuntime().exec.call(this,"FileReader","destroy"),c.disconnectRuntime()),o=c=null}})}var u=["loadstart","progress","load","abort","error","loadend"];return s.EMPTY=0,s.LOADING=1,s.DONE=2,s.prototype=i.instance,s}),i(b,[],function(){var e=function(e){for(var t=["source","scheme","authority","userInfo","user","pass","host","port","relative","path","directory","file","query","fragment"],n=t.length,i={http:80,https:443},r={},o=/^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/,a=o.exec(e||"");n--;)a[n]&&(r[t[n]]=a[n]);if(/^[^\/]/.test(r.path)&&!r.scheme){var s=document.location.pathname;/(\/|\/[^\.]+)$/.test(s)||(s=s.replace(/[^\/]+$/,"")),r.host=document.location.hostname,r.path=s+(r.path||"")}return r.scheme||(r.scheme=document.location.protocol.replace(/:$/,"")),r.host||(r.host=document.location.hostname),r.port||(r.port=document.location.port||i[r.scheme]||80),r.port=parseInt(r.port,10),r.path||(r.path="/"),delete r.source,r},t=function(t){var n={http:80,https:443},i=e(t);return i.scheme+"://"+i.host+(i.port!==n[i.scheme]?":"+i.port:"")+i.path+(i.query?i.query:"")},n=function(t){function n(e){return[e.scheme,e.host,e.port].join("/")}return"string"==typeof t&&(t=e(t)),n(e())===n(t)};return{parseUrl:e,resolveUrl:t,hasSameOrigin:n}}),i(A,[u,v,m],function(e,t,n){return function(){function i(e,t){if(!t.isDetached()){var i=this.connectRuntime(t.ruid).exec.call(this,"FileReaderSync","read",e,t);return this.disconnectRuntime(),i}var r=t.getSource();switch(e){case"readAsBinaryString":return r;case"readAsDataURL":return"data:"+t.type+";base64,"+n.btoa(r);case"readAsText":for(var o="",a=0,s=r.length;s>a;a++)o+=String.fromCharCode(r[a]);return o}}t.call(this),e.extend(this,{uid:e.guid("uid_"),readAsBinaryString:function(e){return i.call(this,"readAsBinaryString",e)},readAsDataURL:function(e){return i.call(this,"readAsDataURL",e)},readAsText:function(e){return i.call(this,"readAsText",e)}})}}),i(T,[p,u,y],function(e,t,n){function i(){var e,i={},r="";t.extend(this,{append:function(r,o){var a=this,s=t.typeOf(o);o instanceof n?(e&&delete i[e],e=r,i[r]=[o]):"array"===s?(r+="[]",t.each(o,function(e){a.append.call(a,r,e)})):"object"===s?t.each(o,function(e,t){a.append.call(a,r+"["+t+"]",e)}):(o=o.toString(),i[r]||(i[r]=[]),i[r].push(o))},hasBlob:function(){return!!e},getBlob:function(){return i[e]&&i[e][0]||null},getBlobName:function(){return e||null},each:function(e){t.each(i,function(n,i){t.each(n,function(t){e(t,i)})})},destroy:function(){e=null,r="",i={}}})}return i}),i(S,[u,p,h,m,b,g,w,y,A,T,d,l],function(e,t,n,i,r,o,a,s,u,c,l,d){function f(){this.uid=e.guid("uid_")}function p(){function n(e,t){return y.hasOwnProperty(e)?1===arguments.length?l.can("define_property")?y[e]:v[e]:(l.can("define_property")?y[e]=t:v[e]=t,void 0):void 0}function u(t){function i(){B.destroy(),B=null,s.dispatchEvent("loadend"),s=null}function r(r){B.bind("LoadStart",function(e){n("readyState",p.LOADING),s.dispatchEvent("readystatechange"),s.dispatchEvent(e),I&&s.upload.dispatchEvent(e)}),B.bind("Progress",function(e){n("readyState")!==p.LOADING&&(n("readyState",p.LOADING),s.dispatchEvent("readystatechange")),s.dispatchEvent(e)}),B.bind("UploadProgress",function(e){I&&s.upload.dispatchEvent({type:"progress",lengthComputable:!1,total:e.total,loaded:e.loaded})}),B.bind("Load",function(t){n("readyState",p.DONE),n("status",Number(r.exec.call(B,"XMLHttpRequest","getStatus")||0)),n("statusText",h[n("status")]||""),n("response",r.exec.call(B,"XMLHttpRequest","getResponse",n("responseType"))),~e.inArray(n("responseType"),["text",""])?n("responseText",n("response")):"document"===n("responseType")&&n("responseXML",n("response")),U=r.exec.call(B,"XMLHttpRequest","getAllResponseHeaders"),s.dispatchEvent("readystatechange"),n("status")>0?(I&&s.upload.dispatchEvent(t),s.dispatchEvent(t)):(L=!0,s.dispatchEvent("error")),i()}),B.bind("Abort",function(e){s.dispatchEvent(e),i()}),B.bind("Error",function(e){L=!0,n("readyState",p.DONE),s.dispatchEvent("readystatechange"),D=!0,s.dispatchEvent(e),i()}),r.exec.call(B,"XMLHttpRequest","send",{url:_,method:R,async:E,user:x,password:b,headers:w,mimeType:T,encoding:A,responseType:s.responseType,withCredentials:s.withCredentials,options:H},t)}var s=this;M=(new Date).getTime(),B=new a,"string"==typeof H.required_caps&&(H.required_caps=o.parseCaps(H.required_caps)),H.required_caps=e.extend({},H.required_caps,{return_response_type:s.responseType}),t instanceof c&&(H.required_caps.send_multipart=!0),N||(H.required_caps.do_cors=!0),H.ruid?r(B.connectRuntime(H)):(B.bind("RuntimeInit",function(e,t){r(t)}),B.bind("RuntimeError",function(e,t){s.dispatchEvent("RuntimeError",t)}),B.connectRuntime(H))}function g(){n("responseText",""),n("responseXML",null),n("response",null),n("status",0),n("statusText",""),M=C=null}var v=this,y={timeout:0,readyState:p.UNSENT,withCredentials:!1,status:0,statusText:"",responseType:"",responseXML:null,responseText:null,response:null},E=!0,_,R,w={},x,b,A=null,T=null,S=!1,O=!1,I=!1,D=!1,L=!1,N=!1,M,C,F=null,P=null,H={},B,U="",G;
-e.extend(this,y,{uid:e.guid("uid_"),upload:new f,open:function(o,a,s,u,c){var l;if(!o||!a)throw new t.DOMException(t.DOMException.SYNTAX_ERR);if(/[\u0100-\uffff]/.test(o)||i.utf8_encode(o)!==o)throw new t.DOMException(t.DOMException.SYNTAX_ERR);if(~e.inArray(o.toUpperCase(),["CONNECT","DELETE","GET","HEAD","OPTIONS","POST","PUT","TRACE","TRACK"])&&(R=o.toUpperCase()),~e.inArray(R,["CONNECT","TRACE","TRACK"]))throw new t.DOMException(t.DOMException.SECURITY_ERR);if(a=i.utf8_encode(a),l=r.parseUrl(a),N=r.hasSameOrigin(l),_=r.resolveUrl(a),(u||c)&&!N)throw new t.DOMException(t.DOMException.INVALID_ACCESS_ERR);if(x=u||l.user,b=c||l.pass,E=s||!0,E===!1&&(n("timeout")||n("withCredentials")||""!==n("responseType")))throw new t.DOMException(t.DOMException.INVALID_ACCESS_ERR);S=!E,O=!1,w={},g.call(this),n("readyState",p.OPENED),this.convertEventPropsToHandlers(["readystatechange"]),this.dispatchEvent("readystatechange")},setRequestHeader:function(r,o){var a=["accept-charset","accept-encoding","access-control-request-headers","access-control-request-method","connection","content-length","cookie","cookie2","content-transfer-encoding","date","expect","host","keep-alive","origin","referer","te","trailer","transfer-encoding","upgrade","user-agent","via"];if(n("readyState")!==p.OPENED||O)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(/[\u0100-\uffff]/.test(r)||i.utf8_encode(r)!==r)throw new t.DOMException(t.DOMException.SYNTAX_ERR);return r=e.trim(r).toLowerCase(),~e.inArray(r,a)||/^(proxy\-|sec\-)/.test(r)?!1:(w[r]?w[r]+=", "+o:w[r]=o,!0)},getAllResponseHeaders:function(){return U||""},getResponseHeader:function(t){return t=t.toLowerCase(),L||~e.inArray(t,["set-cookie","set-cookie2"])?null:U&&""!==U&&(G||(G={},e.each(U.split(/\r\n/),function(t){var n=t.split(/:\s+/);2===n.length&&(n[0]=e.trim(n[0]),G[n[0].toLowerCase()]={header:n[0],value:e.trim(n[1])})})),G.hasOwnProperty(t))?G[t].header+": "+G[t].value:null},overrideMimeType:function(i){var r,o;if(~e.inArray(n("readyState"),[p.LOADING,p.DONE]))throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(i=e.trim(i.toLowerCase()),/;/.test(i)&&(r=i.match(/^([^;]+)(?:;\scharset\=)?(.*)$/))&&(i=r[1],r[2]&&(o=r[2])),!d.mimes[i])throw new t.DOMException(t.DOMException.SYNTAX_ERR);F=i,P=o},send:function(n,r){if(H="string"===e.typeOf(r)?{ruid:r}:r?r:{},this.convertEventPropsToHandlers(m),this.upload.convertEventPropsToHandlers(m),this.readyState!==p.OPENED||O)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(n instanceof s)H.ruid=n.ruid,T=n.type||"application/octet-stream";else if(n instanceof c){if(n.hasBlob()){var o=n.getBlob();H.ruid=o.ruid,T=o.type||"application/octet-stream"}}else"string"==typeof n&&(A="UTF-8",T="text/plain;charset=UTF-8",n=i.utf8_encode(n));this.withCredentials||(this.withCredentials=H.required_caps&&H.required_caps.send_browser_cookies&&!N),I=!S&&this.upload.hasEventListener(),L=!1,D=!n,S||(O=!0),u.call(this,n)},abort:function(){if(L=!0,S=!1,~e.inArray(n("readyState"),[p.UNSENT,p.OPENED,p.DONE]))n("readyState",p.UNSENT);else{if(n("readyState",p.DONE),O=!1,!B)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);B.getRuntime().exec.call(B,"XMLHttpRequest","abort",D),D=!0}},destroy:function(){B&&("function"===e.typeOf(B.destroy)&&B.destroy(),B=null),this.unbindAll(),this.upload&&(this.upload.unbindAll(),this.upload=null)}})}var h={100:"Continue",101:"Switching Protocols",102:"Processing",200:"OK",201:"Created",202:"Accepted",203:"Non-Authoritative Information",204:"No Content",205:"Reset Content",206:"Partial Content",207:"Multi-Status",226:"IM Used",300:"Multiple Choices",301:"Moved Permanently",302:"Found",303:"See Other",304:"Not Modified",305:"Use Proxy",306:"Reserved",307:"Temporary Redirect",400:"Bad Request",401:"Unauthorized",402:"Payment Required",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",406:"Not Acceptable",407:"Proxy Authentication Required",408:"Request Timeout",409:"Conflict",410:"Gone",411:"Length Required",412:"Precondition Failed",413:"Request Entity Too Large",414:"Request-URI Too Long",415:"Unsupported Media Type",416:"Requested Range Not Satisfiable",417:"Expectation Failed",422:"Unprocessable Entity",423:"Locked",424:"Failed Dependency",426:"Upgrade Required",500:"Internal Server Error",501:"Not Implemented",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout",505:"HTTP Version Not Supported",506:"Variant Also Negotiates",507:"Insufficient Storage",510:"Not Extended"};f.prototype=n.instance;var m=["loadstart","progress","abort","error","load","timeout","loadend"],g=1,v=2;return p.UNSENT=0,p.OPENED=1,p.HEADERS_RECEIVED=2,p.LOADING=3,p.DONE=4,p.prototype=n.instance,p}),i(O,[u,m,v,h],function(e,t,n,i){function r(){function i(){l=d=0,c=this.result=null}function o(t,n){var i=this;u=n,i.bind("TransportingProgress",function(t){d=t.loaded,l>d&&-1===e.inArray(i.state,[r.IDLE,r.DONE])&&a.call(i)},999),i.bind("TransportingComplete",function(){d=l,i.state=r.DONE,c=null,i.result=u.exec.call(i,"Transporter","getAsBlob",t||"")},999),i.state=r.BUSY,i.trigger("TransportingStarted"),a.call(i)}function a(){var e=this,n,i=l-d;f>i&&(f=i),n=t.btoa(c.substr(d,f)),u.exec.call(e,"Transporter","receive",n,l)}var s,u,c,l,d,f;n.call(this),e.extend(this,{uid:e.guid("uid_"),state:r.IDLE,result:null,transport:function(t,n,r){var a=this;if(r=e.extend({chunk_size:204798},r),(s=r.chunk_size%3)&&(r.chunk_size+=3-s),f=r.chunk_size,i.call(this),c=t,l=t.length,"string"===e.typeOf(r)||r.ruid)o.call(a,n,this.connectRuntime(r));else{var u=function(e,t){a.unbind("RuntimeInit",u),o.call(a,n,t)};this.bind("RuntimeInit",u),this.connectRuntime(r)}},abort:function(){var e=this;e.state=r.IDLE,u&&(u.exec.call(e,"Transporter","clear"),e.trigger("TransportingAborted")),i.call(e)},destroy:function(){this.unbindAll(),u=null,this.disconnectRuntime(),i.call(this)}})}return r.IDLE=0,r.BUSY=1,r.DONE=2,r.prototype=i.instance,r}),i(I,[],function(){return!!window.JSON&&JSON.parse||function(){var e,n,i={'"':'"',"\\":"\\","/":"/",b:"\b",f:"\f",n:"\n",r:"\r",t:" "},r,o=function(t){throw{name:"SyntaxError",message:t,at:e,text:r}},a=function(t){return t&&t!==n&&o("Expected '"+t+"' instead of '"+n+"'"),n=r.charAt(e),e+=1,n},s=function(){var e,t="";for("-"===n&&(t="-",a("-"));n>="0"&&"9">=n;)t+=n,a();if("."===n)for(t+=".";a()&&n>="0"&&"9">=n;)t+=n;if("e"===n||"E"===n)for(t+=n,a(),("-"===n||"+"===n)&&(t+=n,a());n>="0"&&"9">=n;)t+=n,a();return e=+t,isFinite(e)?e:(o("Bad number"),void 0)},u=function(){var e,t,r="",s;if('"'===n)for(;a();){if('"'===n)return a(),r;if("\\"===n)if(a(),"u"===n){for(s=0,t=0;4>t&&(e=parseInt(a(),16),isFinite(e));t+=1)s=16*s+e;r+=String.fromCharCode(s)}else{if("string"!=typeof i[n])break;r+=i[n]}else r+=n}o("Bad string")},c=function(){for(;n&&" ">=n;)a()},l=function(){switch(n){case"t":return a("t"),a("r"),a("u"),a("e"),!0;case"f":return a("f"),a("a"),a("l"),a("s"),a("e"),!1;case"n":return a("n"),a("u"),a("l"),a("l"),null}o("Unexpected '"+n+"'")},d,f=function(){var e=[];if("["===n){if(a("["),c(),"]"===n)return a("]"),e;for(;n;){if(e.push(d()),c(),"]"===n)return a("]"),e;a(","),c()}}o("Bad array")},p=function(){var e,t={};if("{"===n){if(a("{"),c(),"}"===n)return a("}"),t;for(;n;){if(e=u(),c(),a(":"),Object.hasOwnProperty.call(t,e)&&o('Duplicate key "'+e+'"'),t[e]=d(),c(),"}"===n)return a("}"),t;a(","),c()}}o("Bad object")};return d=function(){switch(c(),n){case"{":return p();case"[":return f();case'"':return u();case"-":return s();default:return n>="0"&&"9">=n?s():l()}},function(i,a){var s;return r=i,e=0,n=" ",s=d(),c(),n&&o("Syntax error"),"function"==typeof a?function u(e,n){var i,r,o=e[n];if(o&&"object"==typeof o)for(i in o)Object.prototype.hasOwnProperty.call(o,i)&&(r=u(o,i),r!==t?o[i]=r:delete o[i]);return a.call(e,n,o)}({"":s},""):s}}()}),i(D,[u,f,p,A,S,g,v,O,d,h,y,E,m,I],function(e,t,n,i,r,o,a,s,u,c,l,d,f,p){function h(){function i(t){if(t||(t=this.getRuntime().exec.call(this,"Image","getInfo")),t)if("string"===e.typeOf(t.meta))try{this.meta=p(t.meta)}catch(n){}else this.meta=t.meta;e.extend(this,{size:parseInt(t.size,10),width:parseInt(t.width,10),height:parseInt(t.height,10),type:t.type}),""===this.name&&(this.name=t.name)}function c(t){var i=e.typeOf(t);try{if(t instanceof h){if(!t.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);g.apply(this,arguments)}else if(t instanceof l){if(!~e.inArray(t.type,["image/jpeg","image/png"]))throw new n.ImageError(n.ImageError.WRONG_FORMAT);v.apply(this,arguments)}else if(-1!==e.inArray(i,["blob","file"]))c.call(this,new d(null,t),arguments[1]);else if("string"===i)/^data:[^;]*;base64,/.test(t)?c.call(this,new l(null,{data:t}),arguments[1]):y.apply(this,arguments);else{if("node"!==i||"img"!==t.nodeName.toLowerCase())throw new n.DOMException(n.DOMException.TYPE_MISMATCH_ERR);c.call(this,t.src,arguments[1])}}catch(r){this.trigger("error",r)}}function g(t,n){var i=this.connectRuntime(t.ruid);this.ruid=i.uid,i.exec.call(this,"Image","loadFromImage",t,"undefined"===e.typeOf(n)?!0:n)}function v(t,n){function i(e){r.ruid=e.uid,e.exec.call(r,"Image","loadFromBlob",t)}var r=this;r.name=t.name||"",t.isDetached()?(this.bind("RuntimeInit",function(e,t){i(t)}),n&&"string"==typeof n.required_caps&&(n.required_caps=o.parseCaps(n.required_caps)),this.connectRuntime(e.extend({required_caps:{access_image_binary:!0,resize_image:!0}},n))):i(this.connectRuntime(t.ruid))}function y(e,t){var n=this,i;i=new r,i.open("get",e),i.responseType="blob",i.onprogress=function(e){n.trigger(e)},i.onload=function(){v.call(n,i.response,!0)},i.onerror=function(e){n.trigger(e)},i.onloadend=function(){i.destroy()},i.bind("RuntimeError",function(e,t){n.trigger("RuntimeError",t)}),i.send(null,t)}a.call(this),e.extend(this,{uid:e.guid("uid_"),ruid:null,name:"",size:0,width:0,height:0,type:"",meta:{},clone:function(){this.load.apply(this,arguments)},load:function(){this.bind("Load Resize",function(){i.call(this)},999),this.convertEventPropsToHandlers(m),c.apply(this,arguments)},downsize:function(t,i,r,o){try{if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);if(this.width>h.MAX_RESIZE_WIDTH||this.height>h.MAX_RESIZE_HEIGHT)throw new n.ImageError(n.ImageError.MAX_RESOLUTION_ERR);(!t&&!i||"undefined"===e.typeOf(r))&&(r=!1),t=t||this.width,i=i||this.height,o="undefined"===e.typeOf(o)?!0:!!o,this.getRuntime().exec.call(this,"Image","downsize",t,i,r,o)}catch(a){this.trigger("error",a)}},crop:function(e,t,n){this.downsize(e,t,!0,n)},getAsCanvas:function(){if(!u.can("create_canvas"))throw new n.RuntimeError(n.RuntimeError.NOT_SUPPORTED_ERR);var e=this.connectRuntime(this.ruid);return e.exec.call(this,"Image","getAsCanvas")},getAsBlob:function(e,t){if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);return e||(e="image/jpeg"),"image/jpeg"!==e||t||(t=90),this.getRuntime().exec.call(this,"Image","getAsBlob",e,t)},getAsDataURL:function(e,t){if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);return this.getRuntime().exec.call(this,"Image","getAsDataURL",e,t)},getAsBinaryString:function(e,t){var n=this.getAsDataURL(e,t);return f.atob(n.substring(n.indexOf("base64,")+7))},embed:function(i){function r(){if(u.can("create_canvas")){var t=a.getAsCanvas();if(t)return i.appendChild(t),t=null,a.destroy(),o.trigger("embedded"),void 0}var r=a.getAsDataURL(c,l);if(!r)throw new n.ImageError(n.ImageError.WRONG_FORMAT);if(u.can("use_data_uri_of",r.length))i.innerHTML='<img src="'+r+'" width="'+a.width+'" height="'+a.height+'" />',a.destroy(),o.trigger("embedded");else{var d=new s;d.bind("TransportingComplete",function(){v=o.connectRuntime(this.result.ruid),o.bind("Embedded",function(){e.extend(v.getShimContainer().style,{top:"0px",left:"0px",width:a.width+"px",height:a.height+"px"}),v=null},999),v.exec.call(o,"ImageView","display",this.result.uid,m,g),a.destroy()}),d.transport(f.atob(r.substring(r.indexOf("base64,")+7)),c,e.extend({},p,{required_caps:{display_media:!0},runtime_order:"flash,silverlight",container:i}))}}var o=this,a,c,l,d,p=arguments[1]||{},m=this.width,g=this.height,v;try{if(!(i=t.get(i)))throw new n.DOMException(n.DOMException.INVALID_NODE_TYPE_ERR);if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);if(this.width>h.MAX_RESIZE_WIDTH||this.height>h.MAX_RESIZE_HEIGHT)throw new n.ImageError(n.ImageError.MAX_RESOLUTION_ERR);if(c=p.type||this.type||"image/jpeg",l=p.quality||90,d="undefined"!==e.typeOf(p.crop)?p.crop:!1,p.width)m=p.width,g=p.height||m;else{var y=t.getSize(i);y.w&&y.h&&(m=y.w,g=y.h)}return a=new h,a.bind("Resize",function(){r.call(o)}),a.bind("Load",function(){a.downsize(m,g,d,!1)}),a.clone(this,!1),a}catch(E){this.trigger("error",E)}},destroy:function(){this.ruid&&(this.getRuntime().exec.call(this,"Image","destroy"),this.disconnectRuntime()),this.unbindAll()}})}var m=["progress","load","error","resize","embedded"];return h.MAX_RESIZE_WIDTH=6500,h.MAX_RESIZE_HEIGHT=6500,h.prototype=c.instance,h}),i(L,[u,p,g,d],function(e,t,n,i){function r(t){var r=this,s=n.capTest,u=n.capTrue,c=e.extend({access_binary:s(window.FileReader||window.File&&window.File.getAsDataURL),access_image_binary:function(){return r.can("access_binary")&&!!a.Image},display_media:s(i.can("create_canvas")||i.can("use_data_uri_over32kb")),do_cors:s(window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest),drag_and_drop:s(function(){var e=document.createElement("div");return("draggable"in e||"ondragstart"in e&&"ondrop"in e)&&("IE"!==i.browser||i.version>9)}()),filter_by_extension:s(function(){return"Chrome"===i.browser&&i.version>=28||"IE"===i.browser&&i.version>=10}()),return_response_headers:u,return_response_type:function(e){return"json"===e?!0:i.can("return_response_type",e)},return_status_code:u,report_upload_progress:s(window.XMLHttpRequest&&(new XMLHttpRequest).upload),resize_image:function(){return r.can("access_binary")&&i.can("create_canvas")},select_file:function(){return i.can("use_fileinput")&&window.File},select_folder:function(){return r.can("select_file")&&"Chrome"===i.browser&&i.version>=21},select_multiple:function(){return r.can("select_file")&&!("Safari"===i.browser&&"Windows"===i.OS)},send_binary_string:s(window.XMLHttpRequest&&((new XMLHttpRequest).sendAsBinary||window.Uint8Array&&window.ArrayBuffer)),send_custom_headers:s(window.XMLHttpRequest),send_multipart:function(){return!!(window.XMLHttpRequest&&(new XMLHttpRequest).upload&&window.FormData)||r.can("send_binary_string")},slice_blob:s(window.File&&(File.prototype.mozSlice||File.prototype.webkitSlice||File.prototype.slice)),stream_upload:function(){return r.can("slice_blob")&&r.can("send_multipart")},summon_file_dialog:s(function(){return"Firefox"===i.browser&&i.version>=4||"Opera"===i.browser&&i.version>=12||"IE"===i.browser&&i.version>=10||!!~e.inArray(i.browser,["Chrome","Safari"])}()),upload_filesize:u},arguments[2]);n.call(this,t,arguments[1]||o,c),e.extend(this,{init:function(){this.trigger("Init")},destroy:function(e){return function(){e.call(r),e=r=null}}(this.destroy)}),e.extend(this.getShim(),a)}var o="html5",a={};return n.addConstructor(o,r),a}),i(N,[L,y],function(e,t){function n(){function e(e,t,n){var i;if(!window.File.prototype.slice)return(i=window.File.prototype.webkitSlice||window.File.prototype.mozSlice)?i.call(e,t,n):null;try{return e.slice(),e.slice(t,n)}catch(r){return e.slice(t,n-t)}}this.slice=function(){return new t(this.getRuntime().uid,e.apply(this,arguments))}}return e.Blob=n}),i(M,[u],function(e){function t(){this.returnValue=!1}function n(){this.cancelBubble=!0}var i={},r="moxie_"+e.guid(),o=function(o,a,s,u){var c,l;a=a.toLowerCase(),o.addEventListener?(c=s,o.addEventListener(a,c,!1)):o.attachEvent&&(c=function(){var e=window.event;e.target||(e.target=e.srcElement),e.preventDefault=t,e.stopPropagation=n,s(e)},o.attachEvent("on"+a,c)),o[r]||(o[r]=e.guid()),i.hasOwnProperty(o[r])||(i[o[r]]={}),l=i[o[r]],l.hasOwnProperty(a)||(l[a]=[]),l[a].push({func:c,orig:s,key:u})},a=function(t,n,o){var a,s;if(n=n.toLowerCase(),t[r]&&i[t[r]]&&i[t[r]][n]){a=i[t[r]][n];for(var u=a.length-1;u>=0&&(a[u].orig!==o&&a[u].key!==o||(t.removeEventListener?t.removeEventListener(n,a[u].func,!1):t.detachEvent&&t.detachEvent("on"+n,a[u].func),a[u].orig=null,a[u].func=null,a.splice(u,1),o===s));u--);if(a.length||delete i[t[r]][n],e.isEmptyObj(i[t[r]])){delete i[t[r]];try{delete t[r]}catch(c){t[r]=s}}}},s=function(t,n){t&&t[r]&&e.each(i[t[r]],function(e,i){a(t,i,n)})};return{addEvent:o,removeEvent:a,removeAllEvents:s}}),i(C,[L,u,f,M,l,d],function(e,t,n,i,r,o){function a(){var e=[],a;t.extend(this,{init:function(s){var u=this,c=u.getRuntime(),l,d,f,p,h,m;a=s,e=[],f=a.accept.mimes||r.extList2mimes(a.accept,c.can("filter_by_extension")),d=c.getShimContainer(),d.innerHTML='<input id="'+c.uid+'" type="file" style="font-size:999px;opacity:0;"'+(a.multiple&&c.can("select_multiple")?"multiple":"")+(a.directory&&c.can("select_folder")?"webkitdirectory directory":"")+(f?' accept="'+f.join(",")+'"':"")+" />",l=n.get(c.uid),t.extend(l.style,{position:"absolute",top:0,left:0,width:"100%",height:"100%"}),p=n.get(a.browse_button),c.can("summon_file_dialog")&&("static"===n.getStyle(p,"position")&&(p.style.position="relative"),h=parseInt(n.getStyle(p,"z-index"),10)||1,p.style.zIndex=h,d.style.zIndex=h-1,i.addEvent(p,"click",function(e){var t=n.get(c.uid);t&&!t.disabled&&t.click(),e.preventDefault()},u.uid)),m=c.can("summon_file_dialog")?p:d,i.addEvent(m,"mouseover",function(){u.trigger("mouseenter")},u.uid),i.addEvent(m,"mouseout",function(){u.trigger("mouseleave")},u.uid),i.addEvent(m,"mousedown",function(){u.trigger("mousedown")},u.uid),i.addEvent(n.get(a.container),"mouseup",function(){u.trigger("mouseup")},u.uid),l.onchange=function g(){if(e=[],a.directory?t.each(this.files,function(t){"."!==t.name&&e.push(t)}):e=[].slice.call(this.files),"IE"!==o.browser)this.value="";else{var n=this.cloneNode(!0);this.parentNode.replaceChild(n,this),n.onchange=g}u.trigger("change")},u.trigger({type:"ready",async:!0}),d=null},getFiles:function(){return e},disable:function(e){var t=this.getRuntime(),i;(i=n.get(t.uid))&&(i.disabled=!!e)},destroy:function(){var t=this.getRuntime(),r=t.getShimContainer();i.removeAllEvents(r,this.uid),i.removeAllEvents(a&&n.get(a.container),this.uid),i.removeAllEvents(a&&n.get(a.browse_button),this.uid),r&&(r.innerHTML=""),e=a=null}})}return e.FileInput=a}),i(F,[L,u,f,M,l],function(e,t,n,i,r){function o(){function e(e){for(var n=[],i=0;i<e.length;i++)[].push.apply(n,e[i].extensions.split(/\s*,\s*/));return-1===t.inArray("*",n)?n:[]}function o(e){var n=r.getFileExtension(e.name);return!n||!l.length||-1!==t.inArray(n,l)}function a(e,n){var i=[];t.each(e,function(e){i.push(function(t){s(e,t)})}),t.inSeries(i,function(){n()})}function s(e,t){e.isFile?e.file(function(e){o(e)&&c.push(e),t()},function(){t()}):e.isDirectory?u(e,t):t()}function u(e,t){function n(e){r.readEntries(function(t){t.length?([].push.apply(i,t),n(e)):e()},e)}var i=[],r=e.createReader();n(function(){a(i,t)})}var c=[],l=[],d;t.extend(this,{init:function(n){var r=this,s;d=n,l=e(d.accept),s=d.container,i.addEvent(s,"dragover",function(e){e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="copy"},r.uid),i.addEvent(s,"drop",function(e){if(e.preventDefault(),e.stopPropagation(),c=[],e.dataTransfer.items&&e.dataTransfer.items[0].webkitGetAsEntry){var n=[];t.each(e.dataTransfer.items,function(e){n.push(e.webkitGetAsEntry())}),a(n,function(){r.trigger("drop")})}else t.each(e.dataTransfer.files,function(e){o(e)&&c.push(e)}),r.trigger("drop")},r.uid),i.addEvent(s,"dragenter",function(e){e.preventDefault(),e.stopPropagation(),r.trigger("dragenter")},r.uid),i.addEvent(s,"dragleave",function(e){e.preventDefault(),e.stopPropagation(),r.trigger("dragleave")},r.uid)},getFiles:function(){return c},destroy:function(){i.removeAllEvents(d&&n.get(d.container),this.uid),c=l=d=null}})}return e.FileDrop=o}),i(P,[L,m,u],function(e,t,n){function i(){function e(e){return t.atob(e.substring(e.indexOf("base64,")+7))}var i,r=!1;n.extend(this,{read:function(e,t){var o=this;i=new window.FileReader,i.addEventListener("progress",function(e){o.trigger(e)}),i.addEventListener("load",function(e){o.trigger(e)}),i.addEventListener("error",function(e){o.trigger(e,i.error)}),i.addEventListener("loadend",function(){i=null}),"function"===n.typeOf(i[e])?(r=!1,i[e](t.getSource())):"readAsBinaryString"===e&&(r=!0,i.readAsDataURL(t.getSource()))},getResult:function(){return i&&i.result?r?e(i.result):i.result:null},abort:function(){i&&i.abort()},destroy:function(){i=null}})}return e.FileReader=i}),i(H,[L,u,l,b,E,y,T,p,d,I],function(e,t,n,i,r,o,a,s,u,c){function l(){function e(e,t){var n=this,i,r;i=t.getBlob().getSource(),r=new window.FileReader,r.onload=function(){t.append(t.getBlobName(),new o(null,{type:i.type,data:r.result})),self.send.call(n,e,t)},r.readAsBinaryString(i)}function l(){return!window.XMLHttpRequest||"IE"===u.browser&&u.version<8?function(){for(var e=["Msxml2.XMLHTTP.6.0","Microsoft.XMLHTTP"],t=0;t<e.length;t++)try{return new ActiveXObject(e[t])}catch(n){}}():new window.XMLHttpRequest}function d(e){var t=e.responseXML,n=e.responseText;return"IE"===u.browser&&n&&t&&!t.documentElement&&/[^\/]+\/[^\+]+\+xml/.test(e.getResponseHeader("Content-Type"))&&(t=new window.ActiveXObject("Microsoft.XMLDOM"),t.async=!1,t.validateOnParse=!1,t.loadXML(n)),t&&("IE"===u.browser&&0!==t.parseError||!t.documentElement||"parsererror"===t.documentElement.tagName)?null:t}function f(e){var t="----moxieboundary"+(new Date).getTime(),n="--",i="\r\n",r="",a=this.getRuntime();if(!a.can("send_binary_string"))throw new s.RuntimeError(s.RuntimeError.NOT_SUPPORTED_ERR);return p.setRequestHeader("Content-Type","multipart/form-data; boundary="+t),e.each(function(e,a){r+=e instanceof o?n+t+i+'Content-Disposition: form-data; name="'+a+'"; filename="'+unescape(encodeURIComponent(e.name||"blob"))+'"'+i+"Content-Type: "+e.type+i+i+e.getSource()+i:n+t+i+'Content-Disposition: form-data; name="'+a+'"'+i+i+unescape(encodeURIComponent(e))+i}),r+=n+t+n+i}var p,h;t.extend(this,{send:function(n,r){var s=this,c="Mozilla"===u.browser&&u.version>=4&&u.version<7,d="Android Browser"===u.browser,m=!1;if(h=n.url.replace(/^.+?\/([\w\-\.]+)$/,"$1").toLowerCase(),p=l(),p.open(n.method,n.url,n.async,n.user,n.password),r instanceof o)r.isDetached()&&(m=!0),r=r.getSource();else if(r instanceof a){if(r.hasBlob())if(r.getBlob().isDetached())r=f.call(s,r),m=!0;else if((c||d)&&"blob"===t.typeOf(r.getBlob().getSource())&&window.FileReader)return e.call(s,n,r),void 0;if(r instanceof a){var g=new window.FormData;r.each(function(e,t){e instanceof o?g.append(t,e.getSource()):g.append(t,e)}),r=g}}p.upload?(n.withCredentials&&(p.withCredentials=!0),p.addEventListener("load",function(e){s.trigger(e)}),p.addEventListener("error",function(e){s.trigger(e)}),p.addEventListener("progress",function(e){s.trigger(e)}),p.upload.addEventListener("progress",function(e){s.trigger({type:"UploadProgress",loaded:e.loaded,total:e.total})})):p.onreadystatechange=function v(){switch(p.readyState){case 1:break;case 2:break;case 3:var e,t;try{i.hasSameOrigin(n.url)&&(e=p.getResponseHeader("Content-Length")||0),p.responseText&&(t=p.responseText.length)}catch(r){e=t=0}s.trigger({type:"progress",lengthComputable:!!e,total:parseInt(e,10),loaded:t});break;case 4:p.onreadystatechange=function(){},0===p.status?s.trigger("error"):s.trigger("load")}},t.isEmptyObj(n.headers)||t.each(n.headers,function(e,t){p.setRequestHeader(t,e)}),""!==n.responseType&&"responseType"in p&&(p.responseType="json"!==n.responseType||u.can("return_response_type","json")?n.responseType:"text"),m?p.sendAsBinary?p.sendAsBinary(r):function(){for(var e=new Uint8Array(r.length),t=0;t<r.length;t++)e[t]=255&r.charCodeAt(t);p.send(e.buffer)}():p.send(r),s.trigger("loadstart")},getStatus:function(){try{if(p)return p.status}catch(e){}return 0},getResponse:function(e){var t=this.getRuntime();try{switch(e){case"blob":var i=new r(t.uid,p.response),o=p.getResponseHeader("Content-Disposition");if(o){var a=o.match(/filename=([\'\"'])([^\1]+)\1/);a&&(h=a[2])}return i.name=h,i.type||(i.type=n.getFileMime(h)),i;case"json":return u.can("return_response_type","json")?p.response:200===p.status?c(p.responseText):null;case"document":return d(p);default:return""!==p.responseText?p.responseText:null}}catch(s){return null}},getAllResponseHeaders:function(){try{return p.getAllResponseHeaders()}catch(e){}return""},abort:function(){p&&p.abort()},destroy:function(){self=h=null}})}return e.XMLHttpRequest=l}),i(B,[],function(){return function(){function e(e,t){var n=r?0:-8*(t-1),i=0,a;for(a=0;t>a;a++)i|=o.charCodeAt(e+a)<<Math.abs(n+8*a);return i}function n(e,t,n){n=3===arguments.length?n:o.length-t-1,o=o.substr(0,t)+e+o.substr(n+t)}function i(e,t,i){var o="",a=r?0:-8*(i-1),s;for(s=0;i>s;s++)o+=String.fromCharCode(255&t>>Math.abs(a+8*s));n(o,e,i)}var r=!1,o;return{II:function(e){return e===t?r:(r=e,void 0)},init:function(e){r=!1,o=e},SEGMENT:function(e,t,i){switch(arguments.length){case 1:return o.substr(e,o.length-e-1);case 2:return o.substr(e,t);case 3:n(i,e,t);break;default:return o}},BYTE:function(t){return e(t,1)},SHORT:function(t){return e(t,2)},LONG:function(n,r){return r===t?e(n,4):(i(n,r,4),void 0)},SLONG:function(t){var n=e(t,4);return n>2147483647?n-4294967296:n},STRING:function(t,n){var i="";for(n+=t;n>t;t++)i+=String.fromCharCode(e(t,1));return i}}}}),i(U,[B],function(e){return function t(n){var i=[],r,o,a,s=0;if(r=new e,r.init(n),65496===r.SHORT(0)){for(o=2;o<=n.length;)if(a=r.SHORT(o),a>=65488&&65495>=a)o+=2;else{if(65498===a||65497===a)break;s=r.SHORT(o+2)+2,a>=65505&&65519>=a&&i.push({hex:a,name:"APP"+(15&a),start:o,length:s,segment:r.SEGMENT(o,s)}),o+=s}return r.init(null),{headers:i,restore:function(e){var t,n;for(r.init(e),o=65504==r.SHORT(2)?4+r.SHORT(4):2,n=0,t=i.length;t>n;n++)r.SEGMENT(o,0,i[n].segment),o+=i[n].length;return e=r.SEGMENT(),r.init(null),e},strip:function(e){var n,i,o;for(i=new t(e),n=i.headers,i.purge(),r.init(e),o=n.length;o--;)r.SEGMENT(n[o].start,n[o].length,"");return e=r.SEGMENT(),r.init(null),e},get:function(e){for(var t=[],n=0,r=i.length;r>n;n++)i[n].name===e.toUpperCase()&&t.push(i[n].segment);return t},set:function(e,t){var n=[],r,o,a;for("string"==typeof t?n.push(t):n=t,r=o=0,a=i.length;a>r&&(i[r].name===e.toUpperCase()&&(i[r].segment=n[o],i[r].length=n[o].length,o++),!(o>=n.length));r++);},purge:function(){i=[],r.init(null),r=null}}}}}),i(G,[u,B],function(e,n){return function i(){function i(e,n){var i=a.SHORT(e),r,o,s,u,d,f,p,h,m=[],g={};for(r=0;i>r;r++)if(p=f=e+12*r+2,s=n[a.SHORT(p)],s!==t){switch(u=a.SHORT(p+=2),d=a.LONG(p+=2),p+=4,m=[],u){case 1:case 7:for(d>4&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.BYTE(p+o);break;case 2:d>4&&(p=a.LONG(p)+c.tiffHeader),g[s]=a.STRING(p,d-1);continue;case 3:for(d>2&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.SHORT(p+2*o);break;case 4:for(d>1&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.LONG(p+4*o);break;case 5:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.LONG(p+4*o)/a.LONG(p+4*o+4);break;case 9:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.SLONG(p+4*o);break;case 10:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.SLONG(p+4*o)/a.SLONG(p+4*o+4);break;default:continue}h=1==d?m[0]:m,g[s]=l.hasOwnProperty(s)&&"object"!=typeof h?l[s][h]:h}return g}function r(){var e=c.tiffHeader;return a.II(18761==a.SHORT(e)),42!==a.SHORT(e+=2)?!1:(c.IFD0=c.tiffHeader+a.LONG(e+=2),u=i(c.IFD0,s.tiff),"ExifIFDPointer"in u&&(c.exifIFD=c.tiffHeader+u.ExifIFDPointer,delete u.ExifIFDPointer),"GPSInfoIFDPointer"in u&&(c.gpsIFD=c.tiffHeader+u.GPSInfoIFDPointer,delete u.GPSInfoIFDPointer),!0)}function o(e,t,n){var i,r,o,u=0;if("string"==typeof t){var l=s[e.toLowerCase()];for(var d in l)if(l[d]===t){t=d;break}}i=c[e.toLowerCase()+"IFD"],r=a.SHORT(i);for(var f=0;r>f;f++)if(o=i+12*f+2,a.SHORT(o)==t){u=o+8;break}return u?(a.LONG(u,n),!0):!1}var a,s,u,c={},l;return a=new n,s={tiff:{274:"Orientation",270:"ImageDescription",271:"Make",272:"Model",305:"Software",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer"},exif:{36864:"ExifVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",36867:"DateTimeOriginal",33434:"ExposureTime",33437:"FNumber",34855:"ISOSpeedRatings",37377:"ShutterSpeedValue",37378:"ApertureValue",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37386:"FocalLength",41986:"ExposureMode",41987:"WhiteBalance",41990:"SceneCaptureType",41988:"DigitalZoomRatio",41992:"Contrast",41993:"Saturation",41994:"Sharpness"},gps:{0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude"}},l={ColorSpace:{1:"sRGB",0:"Uncalibrated"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{1:"Daylight",2:"Fliorescent",3:"Tungsten",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 -5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire.",1:"Flash fired.",5:"Strobe return light not detected.",7:"Strobe return light detected.",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},ExposureMode:{0:"Auto exposure",1:"Manual exposure",2:"Auto bracket"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},GPSLatitudeRef:{N:"North latitude",S:"South latitude"},GPSLongitudeRef:{E:"East longitude",W:"West longitude"}},{init:function(e){return c={tiffHeader:10},e!==t&&e.length?(a.init(e),65505===a.SHORT(0)&&"EXIF\0"===a.STRING(4,5).toUpperCase()?r():!1):!1},TIFF:function(){return u},EXIF:function(){var t;if(t=i(c.exifIFD,s.exif),t.ExifVersion&&"array"===e.typeOf(t.ExifVersion)){for(var n=0,r="";n<t.ExifVersion.length;n++)r+=String.fromCharCode(t.ExifVersion[n]);t.ExifVersion=r}return t},GPS:function(){var t;return t=i(c.gpsIFD,s.gps),t.GPSVersionID&&"array"===e.typeOf(t.GPSVersionID)&&(t.GPSVersionID=t.GPSVersionID.join(".")),t},setExif:function(e,t){return"PixelXDimension"!==e&&"PixelYDimension"!==e?!1:o("exif",e,t)},getBinary:function(){return a.SEGMENT()},purge:function(){a.init(null),a=u=null,c={}}}}}),i(z,[u,p,U,B,G],function(e,t,n,i,r){function o(o){function a(){for(var e=0,t,n;e<=u.length;){if(t=c.SHORT(e+=2),t>=65472&&65475>=t)return e+=5,{height:c.SHORT(e),width:c.SHORT(e+=2)};n=c.SHORT(e+=2),e+=n-2}return null}function s(){d&&l&&c&&(d.purge(),l.purge(),c.init(null),u=f=l=d=c=null)}var u,c,l,d,f,p;if(u=o,c=new i,c.init(u),65496!==c.SHORT(0))throw new t.ImageError(t.ImageError.WRONG_FORMAT);l=new n(o),d=new r,p=!!d.init(l.get("app1")[0]),f=a.call(this),e.extend(this,{type:"image/jpeg",size:u.length,width:f&&f.width||0,height:f&&f.height||0,setExif:function(t,n){return p?("object"===e.typeOf(t)?e.each(t,function(e,t){d.setExif(t,e)
-}):d.setExif(t,n),l.set("app1",d.getBinary()),void 0):!1},writeHeaders:function(){return arguments.length?l.restore(arguments[0]):u=l.restore(u)},stripHeaders:function(e){return l.strip(e)},purge:function(){s.call(this)}}),p&&(this.meta={tiff:d.TIFF(),exif:d.EXIF(),gps:d.GPS()})}return o}),i(q,[p,u,B],function(e,t,n){function i(i){function r(){var e,t;return e=a.call(this,8),"IHDR"==e.type?(t=e.start,{width:u.LONG(t),height:u.LONG(t+=4)}):null}function o(){u&&(u.init(null),s=d=c=l=u=null)}function a(e){var t,n,i,r;return t=u.LONG(e),n=u.STRING(e+=4,4),i=e+=4,r=u.LONG(e+t),{length:t,type:n,start:i,CRC:r}}var s,u,c,l,d;s=i,u=new n,u.init(s),function(){var t=0,n=0,i=[35152,20039,3338,6666];for(n=0;n<i.length;n++,t+=2)if(i[n]!=u.SHORT(t))throw new e.ImageError(e.ImageError.WRONG_FORMAT)}(),d=r.call(this),t.extend(this,{type:"image/png",size:s.length,width:d.width,height:d.height,purge:function(){o.call(this)}}),o.call(this)}return i}),i(k,[u,p,z,q],function(e,t,n,i){return function(r){var o=[n,i],a;a=function(){for(var e=0;e<o.length;e++)try{return new o[e](r)}catch(n){}throw new t.ImageError(t.ImageError.WRONG_FORMAT)}(),e.extend(this,{type:"",size:0,width:0,height:0,setExif:function(){},writeHeaders:function(e){return e},stripHeaders:function(e){return e},purge:function(){}}),e.extend(this,a),this.purge=function(){a.purge(),a=null}}}),i(X,[],function(){function e(e,i,r){var o=e.naturalWidth,a=e.naturalHeight,s=r.width,u=r.height,c=r.x||0,l=r.y||0,d=i.getContext("2d");t(e)&&(o/=2,a/=2);var f=1024,p=document.createElement("canvas");p.width=p.height=f;for(var h=p.getContext("2d"),m=n(e,o,a),g=0;a>g;){for(var v=g+f>a?a-g:f,y=0;o>y;){var E=y+f>o?o-y:f;h.clearRect(0,0,f,f),h.drawImage(e,-y,-g);var _=y*s/o+c<<0,R=Math.ceil(E*s/o),w=g*u/a/m+l<<0,x=Math.ceil(v*u/a/m);d.drawImage(p,0,0,E,v,_,w,R,x),y+=f}g+=f}p=h=null}function t(e){var t=e.naturalWidth,n=e.naturalHeight;if(t*n>1048576){var i=document.createElement("canvas");i.width=i.height=1;var r=i.getContext("2d");return r.drawImage(e,-t+1,0),0===r.getImageData(0,0,1,1).data[3]}return!1}function n(e,t,n){var i=document.createElement("canvas");i.width=1,i.height=n;var r=i.getContext("2d");r.drawImage(e,0,0);for(var o=r.getImageData(0,0,1,n).data,a=0,s=n,u=n;u>a;){var c=o[4*(u-1)+3];0===c?s=u:a=u,u=s+a>>1}i=null;var l=u/n;return 0===l?1:l}return{isSubsampled:t,renderTo:e}}),i(j,[L,u,p,m,y,k,X,l,d],function(e,t,n,i,r,o,a,s,u){function c(){function e(){if(!_&&!y)throw new n.ImageError(n.DOMException.INVALID_STATE_ERR);return _||y}function c(e){return i.atob(e.substring(e.indexOf("base64,")+7))}function l(e,t){return"data:"+(t||"")+";base64,"+i.btoa(e)}function d(e){var t=this;y=new Image,y.onerror=function(){g.call(this),t.trigger("error",new n.ImageError(n.ImageError.WRONG_FORMAT))},y.onload=function(){t.trigger("load")},y.src=/^data:[^;]*;base64,/.test(e)?e:l(e,w.type)}function f(e,t){var i=this,r;return window.FileReader?(r=new FileReader,r.onload=function(){t(this.result)},r.onerror=function(){i.trigger("error",new n.FileException(n.FileException.NOT_READABLE_ERR))},r.readAsDataURL(e),void 0):t(e.getAsDataURL())}function p(n,i,r,o){var a=this,s,u,c,l,d,f,p,g,v;if(b=o,v=this.meta&&this.meta.tiff&&this.meta.tiff.Orientation||1,-1!==t.inArray(v,[5,6,7,8])){var y=n;n=i,i=y}return f=e(),c=r?Math.max:Math.min,u=c(n/f.width,i/f.height),u>1&&(!r||o)?(this.trigger("Resize"),void 0):(p=Math.round(f.width*u),g=Math.round(f.height*u),_||(_=document.createElement("canvas")),s=_.getContext("2d"),r?(_.width=n,_.height=i):(_.width=p,_.height=g),l=p>_.width?Math.round((p-_.width)/2):0,d=g>_.height?Math.round((g-_.height)/2):0,b||m(_.width,_.height,v),h.call(this,f,_,-l,-d,p,g),this.width=_.width,this.height=_.height,x=!0,a.trigger("Resize"),void 0)}function h(e,t,n,i,r,o){if("iOS"===u.OS)a.renderTo(e,t,{width:r,height:o,x:n,y:i});else{var s=t.getContext("2d");s.drawImage(e,n,i,r,o)}}function m(e,t,n){switch(n){case 5:case 6:case 7:case 8:_.width=t,_.height=e;break;default:_.width=e,_.height=t}var i=_.getContext("2d");switch(n){case 2:i.translate(e,0),i.scale(-1,1);break;case 3:i.translate(e,t),i.rotate(Math.PI);break;case 4:i.translate(0,t),i.scale(1,-1);break;case 5:i.rotate(.5*Math.PI),i.scale(1,-1);break;case 6:i.rotate(.5*Math.PI),i.translate(0,-t);break;case 7:i.rotate(.5*Math.PI),i.translate(e,-t),i.scale(-1,1);break;case 8:i.rotate(-.5*Math.PI),i.translate(-e,0)}}function g(){E&&(E.purge(),E=null),R=y=_=w=null,x=!1}var v=this,y,E,_,R,w,x=!1,b=!0;t.extend(this,{loadFromBlob:function(e){var t=this,i=t.getRuntime(),r=arguments.length>1?arguments[1]:!0;if(!i.can("access_binary"))throw new n.RuntimeError(n.RuntimeError.NOT_SUPPORTED_ERR);return w=e,e.isDetached()?(R=e.getSource(),d.call(this,R),void 0):(f.call(this,e.getSource(),function(e){r&&(R=c(e)),d.call(t,e)}),void 0)},loadFromImage:function(e,t){this.meta=e.meta,w=new r(null,{name:e.name,size:e.size,type:e.type}),d.call(this,t?R=e.getAsBinaryString():e.getAsDataURL())},getInfo:function(){var t=this.getRuntime(),n;return!E&&R&&t.can("access_image_binary")&&(E=new o(R)),n={width:e().width||0,height:e().height||0,type:w.type||s.getFileMime(w.name),size:R&&R.length||w.size||0,name:w.name||"",meta:E&&E.meta||this.meta||{}}},downsize:function(){p.apply(this,arguments)},getAsCanvas:function(){return _&&(_.id=this.uid+"_canvas"),_},getAsBlob:function(e,t){return e!==this.type&&p.call(this,this.width,this.height,!1),new r(null,{type:e,data:v.getAsBinaryString.call(this,e,t)})},getAsDataURL:function(e){var t=arguments[1]||90;if(!x)return y.src;if("image/jpeg"!==e)return _.toDataURL("image/png");try{return _.toDataURL("image/jpeg",t/100)}catch(n){return _.toDataURL("image/jpeg")}},getAsBinaryString:function(e,t){if(!x)return R||(R=c(v.getAsDataURL(e,t))),R;if("image/jpeg"!==e)R=c(v.getAsDataURL(e,t));else{var n;t||(t=90);try{n=_.toDataURL("image/jpeg",t/100)}catch(i){n=_.toDataURL("image/jpeg")}R=c(n),E&&(R=E.stripHeaders(R),b&&(E.meta&&E.meta.exif&&E.setExif({PixelXDimension:this.width,PixelYDimension:this.height}),R=E.writeHeaders(R)),E.purge(),E=null)}return x=!1,R},destroy:function(){v=null,g.call(this),this.getRuntime().getShim().removeInstance(this.uid)}})}return e.Image=c}),i(V,[u,d,f,p,g],function(e,t,n,i,r){function o(){var e;try{e=navigator.plugins["Shockwave Flash"],e=e.description}catch(t){try{e=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version")}catch(n){e="0.0"}}return e=e.match(/\d+/g),parseFloat(e[0]+"."+e[1])}function a(a){var c=this,l;a=e.extend({swf_url:t.swf_url},a),r.call(this,a,s,{access_binary:function(e){return e&&"browser"===c.mode},access_image_binary:function(e){return e&&"browser"===c.mode},display_media:r.capTrue,do_cors:r.capTrue,drag_and_drop:!1,report_upload_progress:function(){return"client"===c.mode},resize_image:r.capTrue,return_response_headers:!1,return_response_type:function(t){return!e.arrayDiff(t,["","text","json","document"])||"browser"===c.mode},return_status_code:function(t){return"browser"===c.mode||!e.arrayDiff(t,[200,404])},select_file:r.capTrue,select_multiple:r.capTrue,send_binary_string:function(e){return e&&"browser"===c.mode},send_browser_cookies:function(e){return e&&"browser"===c.mode},send_custom_headers:function(e){return e&&"browser"===c.mode},send_multipart:r.capTrue,slice_blob:r.capTrue,stream_upload:function(e){return e&&"browser"===c.mode},summon_file_dialog:!1,upload_filesize:function(t){return e.parseSizeStr(t)<=2097152||"client"===c.mode},use_http_method:function(t){return!e.arrayDiff(t,["GET","POST"])}},{access_binary:function(e){return e?"browser":"client"},access_image_binary:function(e){return e?"browser":"client"},report_upload_progress:function(e){return e?"browser":"client"},return_response_type:function(t){return e.arrayDiff(t,["","text","json","document"])?"browser":["client","browser"]},return_status_code:function(t){return e.arrayDiff(t,[200,404])?"browser":["client","browser"]},send_binary_string:function(e){return e?"browser":"client"},send_browser_cookies:function(e){return e?"browser":"client"},send_custom_headers:function(e){return e?"browser":"client"},stream_upload:function(e){return e?"client":"browser"},upload_filesize:function(t){return e.parseSizeStr(t)>=2097152?"client":"browser"}},"client"),o()<10&&(this.mode=!1),e.extend(this,{getShim:function(){return n.get(this.uid)},shimExec:function(e,t){var n=[].slice.call(arguments,2);return c.getShim().exec(this.uid,e,t,n)},init:function(){var n,r,o;o=this.getShimContainer(),e.extend(o.style,{position:"absolute",top:"-8px",left:"-8px",width:"9px",height:"9px",overflow:"hidden"}),n='<object id="'+this.uid+'" type="application/x-shockwave-flash" data="'+a.swf_url+'" ',"IE"===t.browser&&(n+='classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '),n+='width="100%" height="100%" style="outline:0"><param name="movie" value="'+a.swf_url+'" />'+'<param name="flashvars" value="uid='+escape(this.uid)+"&target="+t.global_event_dispatcher+'" />'+'<param name="wmode" value="transparent" />'+'<param name="allowscriptaccess" value="always" />'+"</object>","IE"===t.browser?(r=document.createElement("div"),o.appendChild(r),r.outerHTML=n,r=o=null):o.innerHTML=n,l=setTimeout(function(){c&&!c.initialized&&c.trigger("Error",new i.RuntimeError(i.RuntimeError.NOT_INIT_ERR))},5e3)},destroy:function(e){return function(){e.call(c),clearTimeout(l),a=l=e=c=null}}(this.destroy)},u)}var s="flash",u={};return r.addConstructor(s,a),u}),i(W,[V,y],function(e,t){var n={slice:function(e,n,i,r){var o=this.getRuntime();return 0>n?n=Math.max(e.size+n,0):n>0&&(n=Math.min(n,e.size)),0>i?i=Math.max(e.size+i,0):i>0&&(i=Math.min(i,e.size)),e=o.shimExec.call(this,"Blob","slice",n,i,r||""),e&&(e=new t(o.uid,e)),e}};return e.Blob=n}),i(Y,[V],function(e){var t={init:function(e){this.getRuntime().shimExec.call(this,"FileInput","init",{name:e.name,accept:e.accept,multiple:e.multiple}),this.trigger("ready")}};return e.FileInput=t}),i($,[V,m],function(e,t){function n(e,n){switch(n){case"readAsText":return t.atob(e,"utf8");case"readAsBinaryString":return t.atob(e);case"readAsDataURL":return e}return null}var i="",r={read:function(e,t){var r=this,o=r.getRuntime();return"readAsDataURL"===e&&(i="data:"+(t.type||"")+";base64,"),r.bind("Progress",function(t,r){r&&(i+=n(r,e))}),o.shimExec.call(this,"FileReader","readAsBase64",t.uid)},getResult:function(){return i},destroy:function(){i=null}};return e.FileReader=r}),i(K,[V,m],function(e,t){function n(e,n){switch(n){case"readAsText":return t.atob(e,"utf8");case"readAsBinaryString":return t.atob(e);case"readAsDataURL":return e}return null}var i={read:function(e,t){var i,r=this.getRuntime();return(i=r.shimExec.call(this,"FileReaderSync","readAsBase64",t.uid))?("readAsDataURL"===e&&(i="data:"+(t.type||"")+";base64,"+i),n(i,e,t.type)):null}};return e.FileReaderSync=i}),i(Z,[V,u,y,E,A,T,O,I],function(e,t,n,i,r,o,a,s){var u={send:function(e,i){function r(){e.transport=l.mode,l.shimExec.call(c,"XMLHttpRequest","send",e,i)}function s(e,t){l.shimExec.call(c,"XMLHttpRequest","appendBlob",e,t.uid),i=null,r()}function u(e,t){var n=new a;n.bind("TransportingComplete",function(){t(this.result)}),n.transport(e.getSource(),e.type,{ruid:l.uid})}var c=this,l=c.getRuntime();if(t.isEmptyObj(e.headers)||t.each(e.headers,function(e,t){l.shimExec.call(c,"XMLHttpRequest","setRequestHeader",t,e.toString())}),i instanceof o){var d;if(i.each(function(e,t){e instanceof n?d=t:l.shimExec.call(c,"XMLHttpRequest","append",t,e)}),i.hasBlob()){var f=i.getBlob();f.isDetached()?u(f,function(e){f.destroy(),s(d,e)}):s(d,f)}else i=null,r()}else i instanceof n?i.isDetached()?u(i,function(e){i.destroy(),i=e.uid,r()}):(i=i.uid,r()):r()},getResponse:function(e){var n,o,a=this.getRuntime();if(o=a.shimExec.call(this,"XMLHttpRequest","getResponseAsBlob")){if(o=new i(a.uid,o),"blob"===e)return o;if(~t.inArray(e,["","text"]))return n=new r,n.readAsText(o);if("arraybuffer"===e);else if("json"===e){n=new r;try{return s(n.readAsText(o))}catch(u){return null}}}return null},abort:function(e){var t=this.getRuntime();t.shimExec.call(this,"XMLHttpRequest","abort"),this.dispatchEvent("readystatechange"),this.dispatchEvent("abort")}};return e.XMLHttpRequest=u}),i(J,[V,y],function(e,t){var n={getAsBlob:function(e){var n=this.getRuntime(),i=n.shimExec.call(this,"Transporter","getAsBlob",e);return i?new t(n.uid,i):null}};return e.Transporter=n}),i(Q,[V,u,O,y,A],function(e,t,n,i,r){var o={loadFromBlob:function(e){function t(e){r.shimExec.call(i,"Image","loadFromBlob",e.uid),i=r=null}var i=this,r=i.getRuntime();if(e.isDetached()){var o=new n;o.bind("TransportingComplete",function(){t(o.result.getSource())}),o.transport(e.getSource(),e.type,{ruid:r.uid})}else t(e.getSource())},loadFromImage:function(e){var t=this.getRuntime();return t.shimExec.call(this,"Image","loadFromImage",e.uid)},getAsBlob:function(e,t){var n=this.getRuntime(),r=n.shimExec.call(this,"Image","getAsBlob",e,t);return r?new i(n.uid,r):null},getAsDataURL:function(){var e=this.getRuntime(),t=e.Image.getAsBlob.apply(this,arguments),n;return t?(n=new r,n.readAsDataURL(t)):null}};return e.Image=o}),i(et,[u,d,f,p,g],function(e,t,n,i,r){function o(e){var t=!1,n=null,i,r,o,a,s,u=0;try{try{n=new ActiveXObject("AgControl.AgControl"),n.IsVersionSupported(e)&&(t=!0),n=null}catch(c){var l=navigator.plugins["Silverlight Plug-In"];if(l){for(i=l.description,"1.0.30226.2"===i&&(i="2.0.30226.2"),r=i.split(".");r.length>3;)r.pop();for(;r.length<4;)r.push(0);for(o=e.split(".");o.length>4;)o.pop();do a=parseInt(o[u],10),s=parseInt(r[u],10),u++;while(u<o.length&&a===s);s>=a&&!isNaN(a)&&(t=!0)}}}catch(d){t=!1}return t}function a(a){var c=this,l;a=e.extend({xap_url:t.xap_url},a),r.call(this,a,s,{access_binary:r.capTrue,access_image_binary:r.capTrue,display_media:r.capTrue,do_cors:r.capTrue,drag_and_drop:!1,report_upload_progress:r.capTrue,resize_image:r.capTrue,return_response_headers:function(e){return e&&"client"===c.mode},return_response_type:r.capTrue,return_status_code:function(t){return"client"===c.mode||!e.arrayDiff(t,[200,404])},select_file:r.capTrue,select_multiple:r.capTrue,send_binary_string:r.capTrue,send_browser_cookies:function(e){return e&&"browser"===c.mode},send_custom_headers:function(e){return e&&"client"===c.mode},send_multipart:r.capTrue,slice_blob:r.capTrue,stream_upload:!0,summon_file_dialog:!1,upload_filesize:r.capTrue,use_http_method:function(t){return"client"===c.mode||!e.arrayDiff(t,["GET","POST"])}},{return_response_headers:function(e){return e?"client":"browser"},return_status_code:function(t){return e.arrayDiff(t,[200,404])?"client":["client","browser"]},send_browser_cookies:function(e){return e?"browser":"client"},send_custom_headers:function(e){return e?"client":"browser"},use_http_method:function(t){return e.arrayDiff(t,["GET","POST"])?"client":["client","browser"]}}),o("2.0.31005.0")&&"Opera"!==t.browser||(this.mode=!1),e.extend(this,{getShim:function(){return n.get(this.uid).content.Moxie},shimExec:function(e,t){var n=[].slice.call(arguments,2);return c.getShim().exec(this.uid,e,t,n)},init:function(){var e;e=this.getShimContainer(),e.innerHTML='<object id="'+this.uid+'" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">'+'<param name="source" value="'+a.xap_url+'"/>'+'<param name="background" value="Transparent"/>'+'<param name="windowless" value="true"/>'+'<param name="enablehtmlaccess" value="true"/>'+'<param name="initParams" value="uid='+this.uid+",target="+t.global_event_dispatcher+'"/>'+"</object>",l=setTimeout(function(){c&&!c.initialized&&c.trigger("Error",new i.RuntimeError(i.RuntimeError.NOT_INIT_ERR))},"Windows"!==t.OS?1e4:5e3)},destroy:function(e){return function(){e.call(c),clearTimeout(l),a=l=e=c=null}}(this.destroy)},u)}var s="silverlight",u={};return r.addConstructor(s,a),u}),i(tt,[et,u,W],function(e,t,n){return e.Blob=t.extend({},n)}),i(nt,[et],function(e){var t={init:function(e){function t(e){for(var t="",n=0;n<e.length;n++)t+=(""!==t?"|":"")+e[n].title+" | *."+e[n].extensions.replace(/,/g,";*.");return t}this.getRuntime().shimExec.call(this,"FileInput","init",t(e.accept),e.name,e.multiple),this.trigger("ready")}};return e.FileInput=t}),i(it,[et,f,M],function(e,t,n){var i={init:function(){var e=this,i=e.getRuntime(),r;return r=i.getShimContainer(),n.addEvent(r,"dragover",function(e){e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="copy"},e.uid),n.addEvent(r,"dragenter",function(e){e.preventDefault();var n=t.get(i.uid).dragEnter(e);n&&e.stopPropagation()},e.uid),n.addEvent(r,"drop",function(e){e.preventDefault();var n=t.get(i.uid).dragDrop(e);n&&e.stopPropagation()},e.uid),i.shimExec.call(this,"FileDrop","init")}};return e.FileDrop=i}),i(rt,[et,u,$],function(e,t,n){return e.FileReader=t.extend({},n)}),i(ot,[et,u,K],function(e,t,n){return e.FileReaderSync=t.extend({},n)}),i(at,[et,u,Z],function(e,t,n){return e.XMLHttpRequest=t.extend({},n)}),i(st,[et,u,J],function(e,t,n){return e.Transporter=t.extend({},n)}),i(ut,[et,u,Q],function(e,t,n){return e.Image=t.extend({},n)}),i(ct,[u,p,g,d],function(e,t,n,i){function r(t){var r=this,s=n.capTest,u=n.capTrue;n.call(this,t,o,{access_binary:s(window.FileReader||window.File&&File.getAsDataURL),access_image_binary:!1,display_media:s(a.Image&&(i.can("create_canvas")||i.can("use_data_uri_over32kb"))),do_cors:!1,drag_and_drop:!1,filter_by_extension:s(function(){return"Chrome"===i.browser&&i.version>=28||"IE"===i.browser&&i.version>=10}()),resize_image:function(){return a.Image&&r.can("access_binary")&&i.can("create_canvas")},report_upload_progress:!1,return_response_headers:!1,return_response_type:function(t){return!!~e.inArray(t,["json","text","document",""])},return_status_code:function(t){return!e.arrayDiff(t,[200,404])},select_file:function(){return i.can("use_fileinput")},select_multiple:!1,send_binary_string:!1,send_custom_headers:!1,send_multipart:!0,slice_blob:!1,stream_upload:function(){return r.can("select_file")},summon_file_dialog:s(function(){return"Firefox"===i.browser&&i.version>=4||"Opera"===i.browser&&i.version>=12||"IE"===i.browser&&i.version>=10||!!~e.inArray(i.browser,["Chrome","Safari"])}()),upload_filesize:u,use_http_method:function(t){return!e.arrayDiff(t,["GET","POST"])}}),e.extend(this,{init:function(){this.trigger("Init")},destroy:function(e){return function(){e.call(r),e=r=null}}(this.destroy)}),e.extend(this.getShim(),a)}var o="html4",a={};return n.addConstructor(o,r),a}),i(lt,[ct,u,f,M,l,d],function(e,t,n,i,r,o){function a(){function e(){var r=this,l=r.getRuntime(),d,f,p,h,m,g;g=t.guid("uid_"),d=l.getShimContainer(),a&&(p=n.get(a+"_form"),p&&t.extend(p.style,{top:"100%"})),h=document.createElement("form"),h.setAttribute("id",g+"_form"),h.setAttribute("method","post"),h.setAttribute("enctype","multipart/form-data"),h.setAttribute("encoding","multipart/form-data"),t.extend(h.style,{overflow:"hidden",position:"absolute",top:0,left:0,width:"100%",height:"100%"}),m=document.createElement("input"),m.setAttribute("id",g),m.setAttribute("type","file"),m.setAttribute("name","Filedata"),m.setAttribute("accept",u.join(",")),t.extend(m.style,{fontSize:"999px",opacity:0}),h.appendChild(m),d.appendChild(h),t.extend(m.style,{position:"absolute",top:0,left:0,width:"100%",height:"100%"}),"IE"===o.browser&&o.version<10&&t.extend(m.style,{filter:"progid:DXImageTransform.Microsoft.Alpha(opacity=0)"}),m.onchange=function(){var t;this.value&&(t=this.files?this.files[0]:{name:this.value},s=[t],this.onchange=function(){},e.call(r),r.bind("change",function(){var e=n.get(g),t=n.get(g+"_form"),i;r.files.length&&e&&t&&(i=r.files[0],e.setAttribute("id",i.uid),t.setAttribute("id",i.uid+"_form"),t.setAttribute("target",i.uid+"_iframe")),e=t=null},998),m=h=null,r.trigger("change"))},l.can("summon_file_dialog")&&(f=n.get(c.browse_button),i.removeEvent(f,"click",r.uid),i.addEvent(f,"click",function(e){m&&!m.disabled&&m.click(),e.preventDefault()},r.uid)),a=g,d=p=f=null,r.trigger({type:"ready",async:!0})}var a,s=[],u=[],c;t.extend(this,{init:function(t){var o=this,a=o.getRuntime(),s;c=t,u=t.accept.mimes||r.extList2mimes(t.accept,a.can("filter_by_extension")),s=a.getShimContainer(),function(){var e,r,u;e=n.get(t.browse_button),a.can("summon_file_dialog")&&("static"===n.getStyle(e,"position")&&(e.style.position="relative"),r=parseInt(n.getStyle(e,"z-index"),10)||1,e.style.zIndex=r,s.style.zIndex=r-1),u=a.can("summon_file_dialog")?e:s,i.addEvent(u,"mouseover",function(){o.trigger("mouseenter")},o.uid),i.addEvent(u,"mouseout",function(){o.trigger("mouseleave")},o.uid),i.addEvent(u,"mousedown",function(){o.trigger("mousedown")},o.uid),i.addEvent(n.get(t.container),"mouseup",function(){o.trigger("mouseup")},o.uid),e=null}(),e.call(this),s=null},getFiles:function(){return s},disable:function(e){var t;(t=n.get(a))&&(t.disabled=!!e)},destroy:function(){var e=this.getRuntime(),t=e.getShimContainer();i.removeAllEvents(t,this.uid),i.removeAllEvents(c&&n.get(c.container),this.uid),i.removeAllEvents(c&&n.get(c.browse_button),this.uid),t&&(t.innerHTML=""),a=s=u=c=null}})}return e.FileInput=a}),i(dt,[ct,P],function(e,t){return e.FileReader=t}),i(ft,[ct,u,f,b,p,M,y,T,I],function(e,t,n,i,r,o,a,s,u){function c(){function e(e){var t=this,i,r,a,s,u=!1;if(d){if(i=d.id.replace(/_iframe$/,""),r=n.get(i+"_form")){for(a=r.getElementsByTagName("input"),s=a.length;s--;)switch(a[s].getAttribute("type")){case"hidden":a[s].parentNode.removeChild(a[s]);break;case"file":u=!0}a=[],u||r.parentNode.removeChild(r),r=null}setTimeout(function(){o.removeEvent(d,"load",t.uid),d.parentNode&&d.parentNode.removeChild(d);var n=t.getRuntime().getShimContainer();n.children.length||n.parentNode.removeChild(n),n=d=null,e()},1)}}var c,l,d;t.extend(this,{send:function(u,f){function p(){var n=m.getShimContainer()||document.body,r=document.createElement("div");r.innerHTML='<iframe id="'+g+'_iframe" name="'+g+'_iframe" src="javascript:&quot;&quot;" style="display:none"></iframe>',d=r.firstChild,n.appendChild(d),o.addEvent(d,"load",function(){var n;try{n=d.contentWindow.document||d.contentDocument||window.frames[d.id].document,/^4\d{2}\s/.test(n.title)&&n.getElementsByTagName("address").length?c=n.title.replace(/^(\d+).*$/,"$1"):(c=200,l=t.trim(n.body.innerHTML),h.trigger({type:"progress",loaded:l.length,total:l.length}),E&&h.trigger({type:"uploadprogress",loaded:E.size||1025,total:E.size||1025}))}catch(r){if(!i.hasSameOrigin(u.url))return e.call(h,function(){h.trigger("error")}),void 0;c=404}e.call(h,function(){h.trigger("load")})},h.uid)}var h=this,m=h.getRuntime(),g,v,y,E;if(c=l=null,f instanceof s&&f.hasBlob()){if(E=f.getBlob(),g=E.uid,y=n.get(g),v=n.get(g+"_form"),!v)throw new r.DOMException(r.DOMException.NOT_FOUND_ERR)}else g=t.guid("uid_"),v=document.createElement("form"),v.setAttribute("id",g+"_form"),v.setAttribute("method",u.method),v.setAttribute("enctype","multipart/form-data"),v.setAttribute("encoding","multipart/form-data"),v.setAttribute("target",g+"_iframe"),m.getShimContainer().appendChild(v);f instanceof s&&f.each(function(e,n){if(e instanceof a)y&&y.setAttribute("name",n);else{var i=document.createElement("input");t.extend(i,{type:"hidden",name:n,value:e}),v.appendChild(i)}}),v.setAttribute("action",u.url),p(),v.submit(),h.trigger("loadstart")},getStatus:function(){return c},getResponse:function(e){if("json"===e&&"string"===t.typeOf(l))try{return u(l.replace(/^\s*<pre[^>]*>/,"").replace(/<\/pre>\s*$/,""))}catch(n){return null}return l},abort:function(){var t=this;d&&d.contentWindow&&(d.contentWindow.stop?d.contentWindow.stop():d.contentWindow.document.execCommand?d.contentWindow.document.execCommand("Stop"):d.src="about:blank"),e.call(this,function(){t.dispatchEvent("abort")})}})}return e.XMLHttpRequest=c}),i(pt,[ct,j],function(e,t){return e.Image=t}),a([u,c,l,d,f,p,h,m,g,v,y,E,_,R,w,x,b,A,T,S,O,I,D,M])}(this);;(function(){"use strict";var e={},t=moxie.core.utils.Basic.inArray;return function n(r){var i,s;for(i in r)s=typeof r[i],s==="object"&&!~t(i,["Exceptions","Env","Mime"])?n(r[i]):s==="function"&&(e[i]=r[i])}(window.moxie),e.Env=window.moxie.core.utils.Env,e.Mime=window.moxie.core.utils.Mime,e.Exceptions=window.moxie.core.Exceptions,window.mOxie=e,window.o||(window.o=e),e})();
+!function(e,t){"use strict";function n(e,t){for(var n,i=[],r=0;r<e.length;++r){if(n=s[e[r]]||o(e[r]),!n)throw"module definition dependecy not found: "+e[r];i.push(n)}t.apply(null,i)}function i(e,i,r){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(i===t)throw"invalid module definition, dependencies must be specified";if(r===t)throw"invalid module definition, definition function must be specified";n(i,function(){s[e]=r.apply(null,arguments)})}function r(e){return!!s[e]}function o(t){for(var n=e,i=t.split(/[.\/]/),r=0;r<i.length;++r){if(!n[i[r]])return;n=n[i[r]]}return n}function a(n){for(var i=0;i<n.length;i++){for(var r=e,o=n[i],a=o.split(/[.\/]/),u=0;u<a.length-1;++u)r[a[u]]===t&&(r[a[u]]={}),r=r[a[u]];r[a[a.length-1]]=s[o]}}var s={},u="moxie/core/utils/Basic",c="moxie/core/I18n",l="moxie/core/utils/Mime",d="moxie/core/utils/Env",f="moxie/core/utils/Dom",p="moxie/core/Exceptions",h="moxie/core/EventTarget",m="moxie/core/utils/Encode",g="moxie/runtime/Runtime",v="moxie/runtime/RuntimeClient",y="moxie/file/Blob",w="moxie/file/File",E="moxie/file/FileInput",_="moxie/file/FileDrop",x="moxie/runtime/RuntimeTarget",R="moxie/file/FileReader",b="moxie/core/utils/Url",T="moxie/file/FileReaderSync",S="moxie/xhr/FormData",A="moxie/xhr/XMLHttpRequest",O="moxie/runtime/Transporter",I="moxie/image/Image",D="moxie/runtime/html5/Runtime",N="moxie/runtime/html5/file/Blob",L="moxie/core/utils/Events",M="moxie/runtime/html5/file/FileInput",C="moxie/runtime/html5/file/FileDrop",F="moxie/runtime/html5/file/FileReader",H="moxie/runtime/html5/xhr/XMLHttpRequest",P="moxie/runtime/html5/utils/BinaryReader",k="moxie/runtime/html5/image/JPEGHeaders",U="moxie/runtime/html5/image/ExifParser",B="moxie/runtime/html5/image/JPEG",z="moxie/runtime/html5/image/PNG",G="moxie/runtime/html5/image/ImageInfo",q="moxie/runtime/html5/image/MegaPixel",X="moxie/runtime/html5/image/Image",j="moxie/runtime/flash/Runtime",V="moxie/runtime/flash/file/Blob",W="moxie/runtime/flash/file/FileInput",Y="moxie/runtime/flash/file/FileReader",$="moxie/runtime/flash/file/FileReaderSync",J="moxie/runtime/flash/xhr/XMLHttpRequest",Z="moxie/runtime/flash/runtime/Transporter",K="moxie/runtime/flash/image/Image",Q="moxie/runtime/silverlight/Runtime",et="moxie/runtime/silverlight/file/Blob",tt="moxie/runtime/silverlight/file/FileInput",nt="moxie/runtime/silverlight/file/FileDrop",it="moxie/runtime/silverlight/file/FileReader",rt="moxie/runtime/silverlight/file/FileReaderSync",ot="moxie/runtime/silverlight/xhr/XMLHttpRequest",at="moxie/runtime/silverlight/runtime/Transporter",st="moxie/runtime/silverlight/image/Image",ut="moxie/runtime/html4/Runtime",ct="moxie/runtime/html4/file/FileInput",lt="moxie/runtime/html4/file/FileReader",dt="moxie/runtime/html4/xhr/XMLHttpRequest",ft="moxie/runtime/html4/image/Image";i(u,[],function(){var e=function(e){var t;return e===t?"undefined":null===e?"null":e.nodeType?"node":{}.toString.call(e).match(/\s([a-z|A-Z]+)/)[1].toLowerCase()},t=function(i){var r;return n(arguments,function(o,s){s>0&&n(o,function(n,o){n!==r&&(e(i[o])===e(n)&&~a(e(n),["array","object"])?t(i[o],n):i[o]=n)})}),i},n=function(e,t){var n,i,r,o;if(e){try{n=e.length}catch(a){n=o}if(n===o){for(i in e)if(e.hasOwnProperty(i)&&t(e[i],i)===!1)return}else for(r=0;n>r;r++)if(t(e[r],r)===!1)return}},i=function(t){var n;if(!t||"object"!==e(t))return!0;for(n in t)return!1;return!0},r=function(t,n){function i(r){"function"===e(t[r])&&t[r](function(e){++r<o&&!e?i(r):n(e)})}var r=0,o=t.length;"function"!==e(n)&&(n=function(){}),t&&t.length||n(),i(r)},o=function(e,t){var i=0,r=e.length,o=new Array(r);n(e,function(e,n){e(function(e){if(e)return t(e);var a=[].slice.call(arguments);a.shift(),o[n]=a,i++,i===r&&(o.unshift(null),t.apply(this,o))})})},a=function(e,t){if(t){if(Array.prototype.indexOf)return Array.prototype.indexOf.call(t,e);for(var n=0,i=t.length;i>n;n++)if(t[n]===e)return n}return-1},s=function(t,n){var i=[];"array"!==e(t)&&(t=[t]),"array"!==e(n)&&(n=[n]);for(var r in t)-1===a(t[r],n)&&i.push(t[r]);return i.length?i:!1},u=function(e,t){var i=[];return n(e,function(e){-1!==a(e,t)&&i.push(e)}),i.length?i:null},c=function(e){var t,n=[];for(t=0;t<e.length;t++)n[t]=e[t];return n},l=function(){var e=0;return function(t){var n=(new Date).getTime().toString(32),i;for(i=0;5>i;i++)n+=Math.floor(65535*Math.random()).toString(32);return(t||"o_")+n+(e++).toString(32)}}(),d=function(e){return e?String.prototype.trim?String.prototype.trim.call(e):e.toString().replace(/^\s*/,"").replace(/\s*$/,""):e},f=function(e){if("string"!=typeof e)return e;var t={t:1099511627776,g:1073741824,m:1048576,k:1024},n;return e=/^([0-9]+)([mgk]?)$/.exec(e.toLowerCase().replace(/[^0-9mkg]/g,"")),n=e[2],e=+e[1],t.hasOwnProperty(n)&&(e*=t[n]),e};return{guid:l,typeOf:e,extend:t,each:n,isEmptyObj:i,inSeries:r,inParallel:o,inArray:a,arrayDiff:s,arrayIntersect:u,toArray:c,trim:d,parseSizeStr:f}}),i(c,[u],function(e){var t={};return{addI18n:function(n){return e.extend(t,n)},translate:function(e){return t[e]||e},_:function(e){return this.translate(e)},sprintf:function(t){var n=[].slice.call(arguments,1);return t.replace(/%[a-z]/g,function(){var t=n.shift();return"undefined"!==e.typeOf(t)?t:""})}}}),i(l,[u,c],function(e,t){var n="application/msword,doc dot,application/pdf,pdf,application/pgp-signature,pgp,application/postscript,ps ai eps,application/rtf,rtf,application/vnd.ms-excel,xls xlb,application/vnd.ms-powerpoint,ppt pps pot,application/zip,zip,application/x-shockwave-flash,swf swfl,application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx,application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx,application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx,application/vnd.openxmlformats-officedocument.presentationml.template,potx,application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx,application/x-javascript,js,application/json,json,audio/mpeg,mp3 mpga mpega mp2,audio/x-wav,wav,audio/x-m4a,m4a,audio/ogg,oga ogg,audio/aiff,aiff aif,audio/flac,flac,audio/aac,aac,audio/ac3,ac3,audio/x-ms-wma,wma,image/bmp,bmp,image/gif,gif,image/jpeg,jpg jpeg jpe,image/photoshop,psd,image/png,png,image/svg+xml,svg svgz,image/tiff,tiff tif,text/plain,asc txt text diff log,text/html,htm html xhtml,text/css,css,text/csv,csv,text/rtf,rtf,video/mpeg,mpeg mpg mpe m2v,video/quicktime,qt mov,video/mp4,mp4,video/x-m4v,m4v,video/x-flv,flv,video/x-ms-wmv,wmv,video/avi,avi,video/webm,webm,video/3gpp,3gpp 3gp,video/3gpp2,3g2,video/vnd.rn-realvideo,rv,video/ogg,ogv,video/x-matroska,mkv,application/vnd.oasis.opendocument.formula-template,otf,application/octet-stream,exe",i={mimes:{},extensions:{},addMimeType:function(e){var t=e.split(/,/),n,i,r;for(n=0;n<t.length;n+=2){for(r=t[n+1].split(/ /),i=0;i<r.length;i++)this.mimes[r[i]]=t[n];this.extensions[t[n]]=r}},extList2mimes:function(t,n){var i=this,r,o,a,s,u=[];for(o=0;o<t.length;o++)for(r=t[o].extensions.split(/\s*,\s*/),a=0;a<r.length;a++){if("*"===r[a])return[];if(s=i.mimes[r[a]])-1===e.inArray(s,u)&&u.push(s);else{if(!n||!/^\w+$/.test(r[a]))return[];u.push("."+r[a])}}return u},mimes2exts:function(t){var n=this,i=[];return e.each(t,function(t){if("*"===t)return i=[],!1;var r=t.match(/^(\w+)\/(\*|\w+)$/);r&&("*"===r[2]?e.each(n.extensions,function(e,t){new RegExp("^"+r[1]+"/").test(t)&&[].push.apply(i,n.extensions[t])}):n.extensions[t]&&[].push.apply(i,n.extensions[t]))}),i},mimes2extList:function(n){var i=[],r=[];return"string"===e.typeOf(n)&&(n=e.trim(n).split(/\s*,\s*/)),r=this.mimes2exts(n),i.push({title:t.translate("Files"),extensions:r.length?r.join(","):"*"}),i.mimes=n,i},getFileExtension:function(e){var t=e&&e.match(/\.([^.]+)$/);return t?t[1].toLowerCase():""},getFileMime:function(e){return this.mimes[this.getFileExtension(e)]||""}};return i.addMimeType(n),i}),i(d,[u],function(e){function t(e,t,n){var i=0,r=0,o=0,a={dev:-6,alpha:-5,a:-5,beta:-4,b:-4,RC:-3,rc:-3,"#":-2,p:1,pl:1},s=function(e){return e=(""+e).replace(/[_\-+]/g,"."),e=e.replace(/([^.\d]+)/g,".$1.").replace(/\.{2,}/g,"."),e.length?e.split("."):[-8]},u=function(e){return e?isNaN(e)?a[e]||-7:parseInt(e,10):0};for(e=s(e),t=s(t),r=Math.max(e.length,t.length),i=0;r>i;i++)if(e[i]!=t[i]){if(e[i]=u(e[i]),t[i]=u(t[i]),e[i]<t[i]){o=-1;break}if(e[i]>t[i]){o=1;break}}if(!n)return o;switch(n){case">":case"gt":return o>0;case">=":case"ge":return o>=0;case"<=":case"le":return 0>=o;case"==":case"=":case"eq":return 0===o;case"<>":case"!=":case"ne":return 0!==o;case"":case"<":case"lt":return 0>o;default:return null}}var n=function(e){var t="",n="?",i="function",r="undefined",o="object",a="major",s="model",u="name",c="type",l="vendor",d="version",f="architecture",p="console",h="mobile",m="tablet",g={has:function(e,t){return-1!==t.toLowerCase().indexOf(e.toLowerCase())},lowerize:function(e){return e.toLowerCase()}},v={rgx:function(){for(var t,n=0,a,s,u,c,l,d,f=arguments;n<f.length;n+=2){var p=f[n],h=f[n+1];if(typeof t===r){t={};for(u in h)c=h[u],typeof c===o?t[c[0]]=e:t[c]=e}for(a=s=0;a<p.length;a++)if(l=p[a].exec(this.getUA())){for(u=0;u<h.length;u++)d=l[++s],c=h[u],typeof c===o&&c.length>0?2==c.length?t[c[0]]=typeof c[1]==i?c[1].call(this,d):c[1]:3==c.length?t[c[0]]=typeof c[1]!==i||c[1].exec&&c[1].test?d?d.replace(c[1],c[2]):e:d?c[1].call(this,d,c[2]):e:4==c.length&&(t[c[0]]=d?c[3].call(this,d.replace(c[1],c[2])):e):t[c]=d?d:e;break}if(l)break}return t},str:function(t,i){for(var r in i)if(typeof i[r]===o&&i[r].length>0){for(var a=0;a<i[r].length;a++)if(g.has(i[r][a],t))return r===n?e:r}else if(g.has(i[r],t))return r===n?e:r;return t}},y={browser:{oldsafari:{major:{1:["/8","/1","/3"],2:"/4","?":"/"},version:{"1.0":"/8",1.2:"/1",1.3:"/3","2.0":"/412","2.0.2":"/416","2.0.3":"/417","2.0.4":"/419","?":"/"}}},device:{sprint:{model:{"Evo Shift 4G":"7373KT"},vendor:{HTC:"APA",Sprint:"Sprint"}}},os:{windows:{version:{ME:"4.90","NT 3.11":"NT3.51","NT 4.0":"NT4.0",2000:"NT 5.0",XP:["NT 5.1","NT 5.2"],Vista:"NT 6.0",7:"NT 6.1",8:"NT 6.2",8.1:"NT 6.3",RT:"ARM"}}}},w={browser:[[/(opera\smini)\/((\d+)?[\w\.-]+)/i,/(opera\s[mobiletab]+).+version\/((\d+)?[\w\.-]+)/i,/(opera).+version\/((\d+)?[\w\.]+)/i,/(opera)[\/\s]+((\d+)?[\w\.]+)/i],[u,d,a],[/\s(opr)\/((\d+)?[\w\.]+)/i],[[u,"Opera"],d,a],[/(kindle)\/((\d+)?[\w\.]+)/i,/(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?((\d+)?[\w\.]+)*/i,/(avant\s|iemobile|slim|baidu)(?:browser)?[\/\s]?((\d+)?[\w\.]*)/i,/(?:ms|\()(ie)\s((\d+)?[\w\.]+)/i,/(rekonq)((?:\/)[\w\.]+)*/i,/(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron)\/((\d+)?[\w\.-]+)/i],[u,d,a],[/(trident).+rv[:\s]((\d+)?[\w\.]+).+like\sgecko/i],[[u,"IE"],d,a],[/(yabrowser)\/((\d+)?[\w\.]+)/i],[[u,"Yandex"],d,a],[/(comodo_dragon)\/((\d+)?[\w\.]+)/i],[[u,/_/g," "],d,a],[/(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?((\d+)?[\w\.]+)/i],[u,d,a],[/(dolfin)\/((\d+)?[\w\.]+)/i],[[u,"Dolphin"],d,a],[/((?:android.+)crmo|crios)\/((\d+)?[\w\.]+)/i],[[u,"Chrome"],d,a],[/((?:android.+))version\/((\d+)?[\w\.]+)\smobile\ssafari/i],[[u,"Android Browser"],d,a],[/version\/((\d+)?[\w\.]+).+?mobile\/\w+\s(safari)/i],[d,a,[u,"Mobile Safari"]],[/version\/((\d+)?[\w\.]+).+?(mobile\s?safari|safari)/i],[d,a,u],[/webkit.+?(mobile\s?safari|safari)((\/[\w\.]+))/i],[u,[a,v.str,y.browser.oldsafari.major],[d,v.str,y.browser.oldsafari.version]],[/(konqueror)\/((\d+)?[\w\.]+)/i,/(webkit|khtml)\/((\d+)?[\w\.]+)/i],[u,d,a],[/(navigator|netscape)\/((\d+)?[\w\.-]+)/i],[[u,"Netscape"],d,a],[/(swiftfox)/i,/(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?((\d+)?[\w\.\+]+)/i,/(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/((\d+)?[\w\.-]+)/i,/(mozilla)\/((\d+)?[\w\.]+).+rv\:.+gecko\/\d+/i,/(uc\s?browser|polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|qqbrowser)[\/\s]?((\d+)?[\w\.]+)/i,/(links)\s\(((\d+)?[\w\.]+)/i,/(gobrowser)\/?((\d+)?[\w\.]+)*/i,/(ice\s?browser)\/v?((\d+)?[\w\._]+)/i,/(mosaic)[\/\s]((\d+)?[\w\.]+)/i],[u,d,a]],engine:[[/(presto)\/([\w\.]+)/i,/(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i,/(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i,/(icab)[\/\s]([23]\.[\d\.]+)/i],[u,d],[/rv\:([\w\.]+).*(gecko)/i],[d,u]],os:[[/(windows)\snt\s6\.2;\s(arm)/i,/(windows\sphone(?:\sos)*|windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i],[u,[d,v.str,y.os.windows.version]],[/(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i],[[u,"Windows"],[d,v.str,y.os.windows.version]],[/\((bb)(10);/i],[[u,"BlackBerry"],d],[/(blackberry)\w*\/?([\w\.]+)*/i,/(tizen)\/([\w\.]+)/i,/(android|webos|palm\os|qnx|bada|rim\stablet\sos|meego)[\/\s-]?([\w\.]+)*/i],[u,d],[/(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i],[[u,"Symbian"],d],[/mozilla.+\(mobile;.+gecko.+firefox/i],[[u,"Firefox OS"],d],[/(nintendo|playstation)\s([wids3portablevu]+)/i,/(mint)[\/\s\(]?(\w+)*/i,/(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk)[\/\s-]?([\w\.-]+)*/i,/(hurd|linux)\s?([\w\.]+)*/i,/(gnu)\s?([\w\.]+)*/i],[u,d],[/(cros)\s[\w]+\s([\w\.]+\w)/i],[[u,"Chromium OS"],d],[/(sunos)\s?([\w\.]+\d)*/i],[[u,"Solaris"],d],[/\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i],[u,d],[/(ip[honead]+)(?:.*os\s*([\w]+)*\slike\smac|;\sopera)/i],[[u,"iOS"],[d,/_/g,"."]],[/(mac\sos\sx)\s?([\w\s\.]+\w)*/i],[u,[d,/_/g,"."]],[/(haiku)\s(\w+)/i,/(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i,/(macintosh|mac(?=_powerpc)|plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos)/i,/(unix)\s?([\w\.]+)*/i],[u,d]]},E=function(e){var n=e||(window&&window.navigator&&window.navigator.userAgent?window.navigator.userAgent:t);this.getBrowser=function(){return v.rgx.apply(this,w.browser)},this.getEngine=function(){return v.rgx.apply(this,w.engine)},this.getOS=function(){return v.rgx.apply(this,w.os)},this.getResult=function(){return{ua:this.getUA(),browser:this.getBrowser(),engine:this.getEngine(),os:this.getOS()}},this.getUA=function(){return n},this.setUA=function(e){return n=e,this},this.setUA(n)};return(new E).getResult()}(),i=function(){var t={define_property:function(){return!1}(),create_canvas:function(){var e=document.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))}(),return_response_type:function(t){try{if(-1!==e.inArray(t,["","text","document"]))return!0;if(window.XMLHttpRequest){var n=new XMLHttpRequest;if(n.open("get","/"),"responseType"in n)return n.responseType=t,n.responseType!==t?!1:!0}}catch(i){}return!1},use_data_uri:function(){var e=new Image;return e.onload=function(){t.use_data_uri=1===e.width&&1===e.height},setTimeout(function(){e.src="data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="},1),!1}(),use_data_uri_over32kb:function(){return t.use_data_uri&&("IE"!==r.browser||r.version>=9)},use_data_uri_of:function(e){return t.use_data_uri&&33e3>e||t.use_data_uri_over32kb()},use_fileinput:function(){var e=document.createElement("input");return e.setAttribute("type","file"),!e.disabled}};return function(n){var i=[].slice.call(arguments);return i.shift(),"function"===e.typeOf(t[n])?t[n].apply(this,i):!!t[n]}}(),r={can:i,browser:n.browser.name,version:parseFloat(n.browser.major),os:n.os.name,osVersion:n.os.version,verComp:t,swf_url:"../flash/Moxie.swf",xap_url:"../silverlight/Moxie.xap",global_event_dispatcher:"moxie.core.EventTarget.instance.dispatchEvent"};return r.OS=r.os,r}),i(f,[d],function(e){var t=function(e){return"string"!=typeof e?e:document.getElementById(e)},n=function(e,t){if(!e.className)return!1;var n=new RegExp("(^|\\s+)"+t+"(\\s+|$)");return n.test(e.className)},i=function(e,t){n(e,t)||(e.className=e.className?e.className.replace(/\s+$/,"")+" "+t:t)},r=function(e,t){if(e.className){var n=new RegExp("(^|\\s+)"+t+"(\\s+|$)");e.className=e.className.replace(n,function(e,t,n){return" "===t&&" "===n?" ":""})}},o=function(e,t){return e.currentStyle?e.currentStyle[t]:window.getComputedStyle?window.getComputedStyle(e,null)[t]:void 0},a=function(t,n){function i(e){var t,n,i=0,r=0;return e&&(n=e.getBoundingClientRect(),t="CSS1Compat"===s.compatMode?s.documentElement:s.body,i=n.left+t.scrollLeft,r=n.top+t.scrollTop),{x:i,y:r}}var r=0,o=0,a,s=document,u,c;if(t=t,n=n||s.body,t&&t.getBoundingClientRect&&"IE"===e.browser&&(!s.documentMode||s.documentMode<8))return u=i(t),c=i(n),{x:u.x-c.x,y:u.y-c.y};for(a=t;a&&a!=n&&a.nodeType;)r+=a.offsetLeft||0,o+=a.offsetTop||0,a=a.offsetParent;for(a=t.parentNode;a&&a!=n&&a.nodeType;)r-=a.scrollLeft||0,o-=a.scrollTop||0,a=a.parentNode;return{x:r,y:o}},s=function(e){return{w:e.offsetWidth||e.clientWidth,h:e.offsetHeight||e.clientHeight}};return{get:t,hasClass:n,addClass:i,removeClass:r,getStyle:o,getPos:a,getSize:s}}),i(p,[u],function(e){function t(e,t){var n;for(n in e)if(e[n]===t)return n;return null}return{RuntimeError:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": RuntimeError "+this.code}var i={NOT_INIT_ERR:1,NOT_SUPPORTED_ERR:9,JS_ERR:4};return e.extend(n,i),n.prototype=Error.prototype,n}(),OperationNotAllowedException:function(){function t(e){this.code=e,this.name="OperationNotAllowedException"}return e.extend(t,{NOT_ALLOWED_ERR:1}),t.prototype=Error.prototype,t}(),ImageError:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": ImageError "+this.code}var i={WRONG_FORMAT:1,MAX_RESOLUTION_ERR:2};return e.extend(n,i),n.prototype=Error.prototype,n}(),FileException:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": FileException "+this.code}var i={NOT_FOUND_ERR:1,SECURITY_ERR:2,ABORT_ERR:3,NOT_READABLE_ERR:4,ENCODING_ERR:5,NO_MODIFICATION_ALLOWED_ERR:6,INVALID_STATE_ERR:7,SYNTAX_ERR:8};return e.extend(n,i),n.prototype=Error.prototype,n}(),DOMException:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": DOMException "+this.code}var i={INDEX_SIZE_ERR:1,DOMSTRING_SIZE_ERR:2,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,INVALID_CHARACTER_ERR:5,NO_DATA_ALLOWED_ERR:6,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INUSE_ATTRIBUTE_ERR:10,INVALID_STATE_ERR:11,SYNTAX_ERR:12,INVALID_MODIFICATION_ERR:13,NAMESPACE_ERR:14,INVALID_ACCESS_ERR:15,VALIDATION_ERR:16,TYPE_MISMATCH_ERR:17,SECURITY_ERR:18,NETWORK_ERR:19,ABORT_ERR:20,URL_MISMATCH_ERR:21,QUOTA_EXCEEDED_ERR:22,TIMEOUT_ERR:23,INVALID_NODE_TYPE_ERR:24,DATA_CLONE_ERR:25};return e.extend(n,i),n.prototype=Error.prototype,n}(),EventException:function(){function t(e){this.code=e,this.name="EventException"}return e.extend(t,{UNSPECIFIED_EVENT_TYPE_ERR:0}),t.prototype=Error.prototype,t}()}}),i(h,[p,u],function(e,t){function n(){var n={};t.extend(this,{uid:null,init:function(){this.uid||(this.uid=t.guid("uid_"))},addEventListener:function(e,i,r,o){var a=this,s;return e=t.trim(e),/\s/.test(e)?(t.each(e.split(/\s+/),function(e){a.addEventListener(e,i,r,o)}),void 0):(e=e.toLowerCase(),r=parseInt(r,10)||0,s=n[this.uid]&&n[this.uid][e]||[],s.push({fn:i,priority:r,scope:o||this}),n[this.uid]||(n[this.uid]={}),n[this.uid][e]=s,void 0)},hasEventListener:function(e){return e?!(!n[this.uid]||!n[this.uid][e]):!!n[this.uid]},removeEventListener:function(e,i){e=e.toLowerCase();var r=n[this.uid]&&n[this.uid][e],o;if(r){if(i){for(o=r.length-1;o>=0;o--)if(r[o].fn===i){r.splice(o,1);break}}else r=[];r.length||(delete n[this.uid][e],t.isEmptyObj(n[this.uid])&&delete n[this.uid])}},removeAllEventListeners:function(){n[this.uid]&&delete n[this.uid]},dispatchEvent:function(i){var r,o,a,s,u={},c=!0,l;if("string"!==t.typeOf(i)){if(s=i,"string"!==t.typeOf(s.type))throw new e.EventException(e.EventException.UNSPECIFIED_EVENT_TYPE_ERR);i=s.type,s.total!==l&&s.loaded!==l&&(u.total=s.total,u.loaded=s.loaded),u.async=s.async||!1}if(-1!==i.indexOf("::")?function(e){r=e[0],i=e[1]}(i.split("::")):r=this.uid,i=i.toLowerCase(),o=n[r]&&n[r][i]){o.sort(function(e,t){return t.priority-e.priority}),a=[].slice.call(arguments),a.shift(),u.type=i,a.unshift(u);var d=[];t.each(o,function(e){a[0].target=e.scope,u.async?d.push(function(t){setTimeout(function(){t(e.fn.apply(e.scope,a)===!1)},1)}):d.push(function(t){t(e.fn.apply(e.scope,a)===!1)})}),d.length&&t.inSeries(d,function(e){c=!e})}return c},bind:function(){this.addEventListener.apply(this,arguments)},unbind:function(){this.removeEventListener.apply(this,arguments)},unbindAll:function(){this.removeAllEventListeners.apply(this,arguments)},trigger:function(){return this.dispatchEvent.apply(this,arguments)},convertEventPropsToHandlers:function(e){var n;"array"!==t.typeOf(e)&&(e=[e]);for(var i=0;i<e.length;i++)n="on"+e[i],"function"===t.typeOf(this[n])?this.addEventListener(e[i],this[n]):"undefined"===t.typeOf(this[n])&&(this[n]=null)}})}return n.instance=new n,n}),i(m,[],function(){var e=function(e){return unescape(encodeURIComponent(e))},t=function(e){return decodeURIComponent(escape(e))},n=function(e,n){if("function"==typeof window.atob)return n?t(window.atob(e)):window.atob(e);var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",r,o,a,s,u,c,l,d,f=0,p=0,h="",m=[];if(!e)return e;e+="";do s=i.indexOf(e.charAt(f++)),u=i.indexOf(e.charAt(f++)),c=i.indexOf(e.charAt(f++)),l=i.indexOf(e.charAt(f++)),d=s<<18|u<<12|c<<6|l,r=255&d>>16,o=255&d>>8,a=255&d,m[p++]=64==c?String.fromCharCode(r):64==l?String.fromCharCode(r,o):String.fromCharCode(r,o,a);while(f<e.length);return h=m.join(""),n?t(h):h},i=function(t,n){if(n&&e(t),"function"==typeof window.btoa)return window.btoa(t);var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",r,o,a,s,u,c,l,d,f=0,p=0,h="",m=[];if(!t)return t;do r=t.charCodeAt(f++),o=t.charCodeAt(f++),a=t.charCodeAt(f++),d=r<<16|o<<8|a,s=63&d>>18,u=63&d>>12,c=63&d>>6,l=63&d,m[p++]=i.charAt(s)+i.charAt(u)+i.charAt(c)+i.charAt(l);while(f<t.length);h=m.join("");var g=t.length%3;return(g?h.slice(0,g-3):h)+"===".slice(g||3)};return{utf8_encode:e,utf8_decode:t,atob:n,btoa:i}}),i(g,[u,f,h],function(e,t,n){function i(n,r,a,s,u){var c=this,l,d=e.guid(r+"_"),f=u||"browser";n=n||{},o[d]=this,a=e.extend({access_binary:!1,access_image_binary:!1,display_media:!1,do_cors:!1,drag_and_drop:!1,filter_by_extension:!0,resize_image:!1,report_upload_progress:!1,return_response_headers:!1,return_response_type:!1,return_status_code:!0,send_custom_headers:!1,select_file:!1,select_folder:!1,select_multiple:!0,send_binary_string:!1,send_browser_cookies:!0,send_multipart:!0,slice_blob:!1,stream_upload:!1,summon_file_dialog:!1,upload_filesize:!0,use_http_method:!0},a),n.preferred_caps&&(f=i.getMode(s,n.preferred_caps,f)),l=function(){var t={};return{exec:function(e,n,i,r){return l[n]&&(t[e]||(t[e]={context:this,instance:new l[n]}),t[e].instance[i])?t[e].instance[i].apply(this,r):void 0},removeInstance:function(e){delete t[e]},removeAllInstances:function(){var n=this;e.each(t,function(t,i){"function"===e.typeOf(t.instance.destroy)&&t.instance.destroy.call(t.context),n.removeInstance(i)})}}}(),e.extend(this,{initialized:!1,uid:d,type:r,mode:i.getMode(s,n.required_caps,f),shimid:d+"_container",clients:0,options:n,can:function(t,n){var r=arguments[2]||a;if("string"===e.typeOf(t)&&"undefined"===e.typeOf(n)&&(t=i.parseCaps(t)),"object"===e.typeOf(t)){for(var o in t)if(!this.can(o,t[o],r))return!1;return!0}return"function"===e.typeOf(r[t])?r[t].call(this,n):n===r[t]},getShimContainer:function(){var n,i=t.get(this.shimid);return i||(n=this.options.container?t.get(this.options.container):document.body,i=document.createElement("div"),i.id=this.shimid,i.className="moxie-shim moxie-shim-"+this.type,e.extend(i.style,{position:"absolute",top:"0px",left:"0px",width:"1px",height:"1px",overflow:"hidden"}),n.appendChild(i),n=null),i},getShim:function(){return l},shimExec:function(e,t){var n=[].slice.call(arguments,2);return c.getShim().exec.call(this,this.uid,e,t,n)},exec:function(e,t){var n=[].slice.call(arguments,2);return c[e]&&c[e][t]?c[e][t].apply(this,n):c.shimExec.apply(this,arguments)},destroy:function(){if(c){var e=t.get(this.shimid);e&&e.parentNode.removeChild(e),l&&l.removeAllInstances(),this.unbindAll(),delete o[this.uid],this.uid=null,d=c=l=e=null}}}),this.mode&&n.required_caps&&!this.can(n.required_caps)&&(this.mode=!1)}var r={},o={};return i.order="html5,flash,silverlight,html4",i.getRuntime=function(e){return o[e]?o[e]:!1},i.addConstructor=function(e,t){t.prototype=n.instance,r[e]=t},i.getConstructor=function(e){return r[e]||null},i.getInfo=function(e){var t=i.getRuntime(e);return t?{uid:t.uid,type:t.type,mode:t.mode,can:function(){return t.can.apply(t,arguments)}}:null},i.parseCaps=function(t){var n={};return"string"!==e.typeOf(t)?t||{}:(e.each(t.split(","),function(e){n[e]=!0}),n)},i.can=function(e,t){var n,r=i.getConstructor(e),o;return r?(n=new r({required_caps:t}),o=n.mode,n.destroy(),!!o):!1},i.thatCan=function(e,t){var n=(t||i.order).split(/\s*,\s*/);for(var r in n)if(i.can(n[r],e))return n[r];return null},i.getMode=function(t,n,i){var r=null;if("undefined"===e.typeOf(i)&&(i="browser"),n&&!e.isEmptyObj(t)){if(e.each(n,function(n,i){if(t.hasOwnProperty(i)){var o=t[i](n);if("string"==typeof o&&(o=[o]),r){if(!(r=e.arrayIntersect(r,o)))return r=!1}else r=o}}),r)return-1!==e.inArray(i,r)?i:r[0];if(r===!1)return!1}return i},i.capTrue=function(){return!0},i.capFalse=function(){return!1},i.capTest=function(e){return function(){return!!e}},i}),i(v,[p,u,g],function(e,t,n){return function i(){var i;t.extend(this,{connectRuntime:function(r){function o(t){var s,u;return t.length?(s=t.shift(),(u=n.getConstructor(s))?(i=new u(r),i.bind("Init",function(){i.initialized=!0,setTimeout(function(){i.clients++,a.trigger("RuntimeInit",i)},1)}),i.bind("Error",function(){i.destroy(),o(t)}),i.mode?(i.init(),void 0):(i.trigger("Error"),void 0)):(o(t),void 0)):(a.trigger("RuntimeError",new e.RuntimeError(e.RuntimeError.NOT_INIT_ERR)),i=null,void 0)}var a=this,s;if("string"===t.typeOf(r)?s=r:"string"===t.typeOf(r.ruid)&&(s=r.ruid),s){if(i=n.getRuntime(s))return i.clients++,i;throw new e.RuntimeError(e.RuntimeError.NOT_INIT_ERR)}o((r.runtime_order||n.order).split(/\s*,\s*/))},getRuntime:function(){return i&&i.uid?i:(i=null,null)},disconnectRuntime:function(){i&&--i.clients<=0&&(i.destroy(),i=null)}})}}),i(y,[u,m,v],function(e,t,n){function i(o,a){function s(t,n,o){var a,s=r[this.uid];return"string"===e.typeOf(s)&&s.length?(a=new i(null,{type:o,size:n-t}),a.detach(s.substr(t,a.size)),a):null}n.call(this),o&&this.connectRuntime(o),a?"string"===e.typeOf(a)&&(a={data:a}):a={},e.extend(this,{uid:a.uid||e.guid("uid_"),ruid:o,size:a.size||0,type:a.type||"",slice:function(e,t,n){return this.isDetached()?s.apply(this,arguments):this.getRuntime().exec.call(this,"Blob","slice",this.getSource(),e,t,n)},getSource:function(){return r[this.uid]?r[this.uid]:null},detach:function(e){this.ruid&&(this.getRuntime().exec.call(this,"Blob","destroy",r[this.uid]),this.disconnectRuntime(),this.ruid=null),e=e||"";var n=e.match(/^data:([^;]*);base64,/);n&&(this.type=n[1],e=t.atob(e.substring(e.indexOf("base64,")+7))),this.size=e.length,r[this.uid]=e},isDetached:function(){return!this.ruid&&"string"===e.typeOf(r[this.uid])},destroy:function(){this.detach(),delete r[this.uid]}}),a.data?this.detach(a.data):r[this.uid]=a}var r={};return i}),i(w,[u,l,y],function(e,t,n){function i(i,r){var o,a;if(r||(r={}),a=r.type&&""!==r.type?r.type:t.getFileMime(r.name),r.name)o=r.name.replace(/\\/g,"/"),o=o.substr(o.lastIndexOf("/")+1);else{var s=a.split("/")[0];o=e.guid((""!==s?s:"file")+"_"),t.extensions[a]&&(o+="."+t.extensions[a][0])}n.apply(this,arguments),e.extend(this,{type:a||"",name:o||e.guid("file_"),lastModifiedDate:r.lastModifiedDate||(new Date).toLocaleString()})}return i.prototype=n.prototype,i}),i(E,[u,l,f,p,h,c,w,g,v],function(e,t,n,i,r,o,a,s,u){function c(r){var c=this,d,f,p;if(-1!==e.inArray(e.typeOf(r),["string","node"])&&(r={browse_button:r}),f=n.get(r.browse_button),!f)throw new i.DOMException(i.DOMException.NOT_FOUND_ERR);p={accept:[{title:o.translate("All Files"),extensions:"*"}],name:"file",multiple:!1,required_caps:!1,container:f.parentNode||document.body},r=e.extend({},p,r),"string"==typeof r.required_caps&&(r.required_caps=s.parseCaps(r.required_caps)),"string"==typeof r.accept&&(r.accept=t.mimes2extList(r.accept)),d=n.get(r.container),d||(d=document.body),"static"===n.getStyle(d,"position")&&(d.style.position="relative"),d=f=null,u.call(c),e.extend(c,{uid:e.guid("uid_"),ruid:null,shimid:null,files:null,init:function(){c.convertEventPropsToHandlers(l),c.bind("RuntimeInit",function(t,i){c.ruid=i.uid,c.shimid=i.shimid,c.bind("Ready",function(){c.trigger("Refresh")},999),c.bind("Change",function(){var t=i.exec.call(c,"FileInput","getFiles");c.files=[],e.each(t,function(e){return 0===e.size?!0:(c.files.push(new a(c.ruid,e)),void 0)})},999),c.bind("Refresh",function(){var t,o,a,s;a=n.get(r.browse_button),s=n.get(i.shimid),a&&(t=n.getPos(a,n.get(r.container)),o=n.getSize(a),s&&e.extend(s.style,{top:t.y+"px",left:t.x+"px",width:o.w+"px",height:o.h+"px"})),s=a=null}),i.exec.call(c,"FileInput","init",r)}),c.connectRuntime(e.extend({},r,{required_caps:{select_file:!0}}))},disable:function(t){var n=this.getRuntime();n&&n.exec.call(this,"FileInput","disable","undefined"===e.typeOf(t)?!0:t)},refresh:function(){c.trigger("Refresh")},destroy:function(){var t=this.getRuntime();t&&(t.exec.call(this,"FileInput","destroy"),this.disconnectRuntime()),"array"===e.typeOf(this.files)&&e.each(this.files,function(e){e.destroy()}),this.files=null}})}var l=["ready","change","cancel","mouseenter","mouseleave","mousedown","mouseup"];return c.prototype=r.instance,c}),i(_,[c,f,p,u,w,v,h,l],function(e,t,n,i,r,o,a,s){function u(n){var a=this,u;"string"==typeof n&&(n={drop_zone:n}),u={accept:[{title:e.translate("All Files"),extensions:"*"}],required_caps:{drag_and_drop:!0}},n="object"==typeof n?i.extend({},u,n):u,n.container=t.get(n.drop_zone)||document.body,"static"===t.getStyle(n.container,"position")&&(n.container.style.position="relative"),"string"==typeof n.accept&&(n.accept=s.mimes2extList(n.accept)),o.call(a),i.extend(a,{uid:i.guid("uid_"),ruid:null,files:null,init:function(){a.convertEventPropsToHandlers(c),a.bind("RuntimeInit",function(e,t){a.ruid=t.uid,a.bind("Drop",function(){var e=t.exec.call(a,"FileDrop","getFiles");a.files=[],i.each(e,function(e){a.files.push(new r(a.ruid,e))})},999),t.exec.call(a,"FileDrop","init",n),a.dispatchEvent("ready")}),a.connectRuntime(n)},destroy:function(){var e=this.getRuntime();e&&(e.exec.call(this,"FileDrop","destroy"),this.disconnectRuntime()),this.files=null}})}var c=["ready","dragenter","dragleave","drop","error"];return u.prototype=a.instance,u}),i(x,[u,v,h],function(e,t,n){function i(){this.uid=e.guid("uid_"),t.call(this),this.destroy=function(){this.disconnectRuntime(),this.unbindAll()}}return i.prototype=n.instance,i}),i(R,[u,m,p,h,y,w,x],function(e,t,n,i,r,o,a){function s(){function i(e,i){function l(e){o.readyState=s.DONE,o.error=e,o.trigger("error"),d()}function d(){c.destroy(),c=null,o.trigger("loadend")}function f(t){c.bind("Error",function(e,t){l(t)}),c.bind("Progress",function(e){o.result=t.exec.call(c,"FileReader","getResult"),o.trigger(e)}),c.bind("Load",function(e){o.readyState=s.DONE,o.result=t.exec.call(c,"FileReader","getResult"),o.trigger(e),d()}),t.exec.call(c,"FileReader","read",e,i)}if(c=new a,this.convertEventPropsToHandlers(u),this.readyState===s.LOADING)return l(new n.DOMException(n.DOMException.INVALID_STATE_ERR));if(this.readyState=s.LOADING,this.trigger("loadstart"),i instanceof r)if(i.isDetached()){var p=i.getSource();switch(e){case"readAsText":case"readAsBinaryString":this.result=p;break;case"readAsDataURL":this.result="data:"+i.type+";base64,"+t.btoa(p)}this.readyState=s.DONE,this.trigger("load"),d()}else f(c.connectRuntime(i.ruid));else l(new n.DOMException(n.DOMException.NOT_FOUND_ERR))}var o=this,c;e.extend(this,{uid:e.guid("uid_"),readyState:s.EMPTY,result:null,error:null,readAsBinaryString:function(e){i.call(this,"readAsBinaryString",e)},readAsDataURL:function(e){i.call(this,"readAsDataURL",e)},readAsText:function(e){i.call(this,"readAsText",e)
+},abort:function(){this.result=null,-1===e.inArray(this.readyState,[s.EMPTY,s.DONE])&&(this.readyState===s.LOADING&&(this.readyState=s.DONE),c&&c.getRuntime().exec.call(this,"FileReader","abort"),this.trigger("abort"),this.trigger("loadend"))},destroy:function(){this.abort(),c&&(c.getRuntime().exec.call(this,"FileReader","destroy"),c.disconnectRuntime()),o=c=null}})}var u=["loadstart","progress","load","abort","error","loadend"];return s.EMPTY=0,s.LOADING=1,s.DONE=2,s.prototype=i.instance,s}),i(b,[],function(){var e=function(t,n){for(var i=["source","scheme","authority","userInfo","user","pass","host","port","relative","path","directory","file","query","fragment"],r=i.length,o={http:80,https:443},a={},s=/^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/,u=s.exec(t||"");r--;)u[r]&&(a[i[r]]=u[r]);if(!a.scheme){n&&"string"!=typeof n||(n=e(n||document.location.href)),a.scheme=n.scheme,a.host=n.host,a.port=n.port;var c="";/^[^\/]/.test(a.path)&&(c=n.path,/(\/|\/[^\.]+)$/.test(c)?c+="/":c=c.replace(/\/[^\/]+$/,"/")),a.path=c+(a.path||"")}return a.port||(a.port=o[a.scheme]||80),a.port=parseInt(a.port,10),a.path||(a.path="/"),delete a.source,a},t=function(t){var n={http:80,https:443},i=e(t);return i.scheme+"://"+i.host+(i.port!==n[i.scheme]?":"+i.port:"")+i.path+(i.query?i.query:"")},n=function(t){function n(e){return[e.scheme,e.host,e.port].join("/")}return"string"==typeof t&&(t=e(t)),n(e())===n(t)};return{parseUrl:e,resolveUrl:t,hasSameOrigin:n}}),i(T,[u,v,m],function(e,t,n){return function(){function i(e,t){if(!t.isDetached()){var i=this.connectRuntime(t.ruid).exec.call(this,"FileReaderSync","read",e,t);return this.disconnectRuntime(),i}var r=t.getSource();switch(e){case"readAsBinaryString":return r;case"readAsDataURL":return"data:"+t.type+";base64,"+n.btoa(r);case"readAsText":for(var o="",a=0,s=r.length;s>a;a++)o+=String.fromCharCode(r[a]);return o}}t.call(this),e.extend(this,{uid:e.guid("uid_"),readAsBinaryString:function(e){return i.call(this,"readAsBinaryString",e)},readAsDataURL:function(e){return i.call(this,"readAsDataURL",e)},readAsText:function(e){return i.call(this,"readAsText",e)}})}}),i(S,[p,u,y],function(e,t,n){function i(){var e,i=[];t.extend(this,{append:function(r,o){var a=this,s=t.typeOf(o);o instanceof n?e={name:r,value:o}:"array"===s?(r+="[]",t.each(o,function(e){a.append(r,e)})):"object"===s?t.each(o,function(e,t){a.append(r+"["+t+"]",e)}):"null"===s||"undefined"===s||"number"===s&&isNaN(o)?a.append(r,"false"):i.push({name:r,value:o.toString()})},hasBlob:function(){return!!this.getBlob()},getBlob:function(){return e&&e.value||null},getBlobName:function(){return e&&e.name||null},each:function(n){t.each(i,function(e){n(e.value,e.name)}),e&&n(e.value,e.name)},destroy:function(){e=null,i=[]}})}return i}),i(A,[u,p,h,m,b,g,x,y,T,S,d,l],function(e,t,n,i,r,o,a,s,u,c,l,d){function f(){this.uid=e.guid("uid_")}function p(){function n(e,t){return y.hasOwnProperty(e)?1===arguments.length?l.can("define_property")?y[e]:v[e]:(l.can("define_property")?y[e]=t:v[e]=t,void 0):void 0}function u(t){function i(){k.destroy(),k=null,s.dispatchEvent("loadend"),s=null}function r(r){k.bind("LoadStart",function(e){n("readyState",p.LOADING),s.dispatchEvent("readystatechange"),s.dispatchEvent(e),I&&s.upload.dispatchEvent(e)}),k.bind("Progress",function(e){n("readyState")!==p.LOADING&&(n("readyState",p.LOADING),s.dispatchEvent("readystatechange")),s.dispatchEvent(e)}),k.bind("UploadProgress",function(e){I&&s.upload.dispatchEvent({type:"progress",lengthComputable:!1,total:e.total,loaded:e.loaded})}),k.bind("Load",function(t){n("readyState",p.DONE),n("status",Number(r.exec.call(k,"XMLHttpRequest","getStatus")||0)),n("statusText",h[n("status")]||""),n("response",r.exec.call(k,"XMLHttpRequest","getResponse",n("responseType"))),~e.inArray(n("responseType"),["text",""])?n("responseText",n("response")):"document"===n("responseType")&&n("responseXML",n("response")),U=r.exec.call(k,"XMLHttpRequest","getAllResponseHeaders"),s.dispatchEvent("readystatechange"),n("status")>0?(I&&s.upload.dispatchEvent(t),s.dispatchEvent(t)):(N=!0,s.dispatchEvent("error")),i()}),k.bind("Abort",function(e){s.dispatchEvent(e),i()}),k.bind("Error",function(e){N=!0,n("readyState",p.DONE),s.dispatchEvent("readystatechange"),D=!0,s.dispatchEvent(e),i()}),r.exec.call(k,"XMLHttpRequest","send",{url:E,method:_,async:w,user:R,password:b,headers:x,mimeType:S,encoding:T,responseType:s.responseType,withCredentials:s.withCredentials,options:P},t)}var s=this;M=(new Date).getTime(),k=new a,"string"==typeof P.required_caps&&(P.required_caps=o.parseCaps(P.required_caps)),P.required_caps=e.extend({},P.required_caps,{return_response_type:s.responseType}),t instanceof c&&(P.required_caps.send_multipart=!0),L||(P.required_caps.do_cors=!0),P.ruid?r(k.connectRuntime(P)):(k.bind("RuntimeInit",function(e,t){r(t)}),k.bind("RuntimeError",function(e,t){s.dispatchEvent("RuntimeError",t)}),k.connectRuntime(P))}function g(){n("responseText",""),n("responseXML",null),n("response",null),n("status",0),n("statusText",""),M=C=null}var v=this,y={timeout:0,readyState:p.UNSENT,withCredentials:!1,status:0,statusText:"",responseType:"",responseXML:null,responseText:null,response:null},w=!0,E,_,x={},R,b,T=null,S=null,A=!1,O=!1,I=!1,D=!1,N=!1,L=!1,M,C,F=null,H=null,P={},k,U="",B;e.extend(this,y,{uid:e.guid("uid_"),upload:new f,open:function(o,a,s,u,c){var l;if(!o||!a)throw new t.DOMException(t.DOMException.SYNTAX_ERR);if(/[\u0100-\uffff]/.test(o)||i.utf8_encode(o)!==o)throw new t.DOMException(t.DOMException.SYNTAX_ERR);if(~e.inArray(o.toUpperCase(),["CONNECT","DELETE","GET","HEAD","OPTIONS","POST","PUT","TRACE","TRACK"])&&(_=o.toUpperCase()),~e.inArray(_,["CONNECT","TRACE","TRACK"]))throw new t.DOMException(t.DOMException.SECURITY_ERR);if(a=i.utf8_encode(a),l=r.parseUrl(a),L=r.hasSameOrigin(l),E=r.resolveUrl(a),(u||c)&&!L)throw new t.DOMException(t.DOMException.INVALID_ACCESS_ERR);if(R=u||l.user,b=c||l.pass,w=s||!0,w===!1&&(n("timeout")||n("withCredentials")||""!==n("responseType")))throw new t.DOMException(t.DOMException.INVALID_ACCESS_ERR);A=!w,O=!1,x={},g.call(this),n("readyState",p.OPENED),this.convertEventPropsToHandlers(["readystatechange"]),this.dispatchEvent("readystatechange")},setRequestHeader:function(r,o){var a=["accept-charset","accept-encoding","access-control-request-headers","access-control-request-method","connection","content-length","cookie","cookie2","content-transfer-encoding","date","expect","host","keep-alive","origin","referer","te","trailer","transfer-encoding","upgrade","user-agent","via"];if(n("readyState")!==p.OPENED||O)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(/[\u0100-\uffff]/.test(r)||i.utf8_encode(r)!==r)throw new t.DOMException(t.DOMException.SYNTAX_ERR);return r=e.trim(r).toLowerCase(),~e.inArray(r,a)||/^(proxy\-|sec\-)/.test(r)?!1:(x[r]?x[r]+=", "+o:x[r]=o,!0)},getAllResponseHeaders:function(){return U||""},getResponseHeader:function(t){return t=t.toLowerCase(),N||~e.inArray(t,["set-cookie","set-cookie2"])?null:U&&""!==U&&(B||(B={},e.each(U.split(/\r\n/),function(t){var n=t.split(/:\s+/);2===n.length&&(n[0]=e.trim(n[0]),B[n[0].toLowerCase()]={header:n[0],value:e.trim(n[1])})})),B.hasOwnProperty(t))?B[t].header+": "+B[t].value:null},overrideMimeType:function(i){var r,o;if(~e.inArray(n("readyState"),[p.LOADING,p.DONE]))throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(i=e.trim(i.toLowerCase()),/;/.test(i)&&(r=i.match(/^([^;]+)(?:;\scharset\=)?(.*)$/))&&(i=r[1],r[2]&&(o=r[2])),!d.mimes[i])throw new t.DOMException(t.DOMException.SYNTAX_ERR);F=i,H=o},send:function(n,r){if(P="string"===e.typeOf(r)?{ruid:r}:r?r:{},this.convertEventPropsToHandlers(m),this.upload.convertEventPropsToHandlers(m),this.readyState!==p.OPENED||O)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(n instanceof s)P.ruid=n.ruid,S=n.type||"application/octet-stream";else if(n instanceof c){if(n.hasBlob()){var o=n.getBlob();P.ruid=o.ruid,S=o.type||"application/octet-stream"}}else"string"==typeof n&&(T="UTF-8",S="text/plain;charset=UTF-8",n=i.utf8_encode(n));this.withCredentials||(this.withCredentials=P.required_caps&&P.required_caps.send_browser_cookies&&!L),I=!A&&this.upload.hasEventListener(),N=!1,D=!n,A||(O=!0),u.call(this,n)},abort:function(){if(N=!0,A=!1,~e.inArray(n("readyState"),[p.UNSENT,p.OPENED,p.DONE]))n("readyState",p.UNSENT);else{if(n("readyState",p.DONE),O=!1,!k)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);k.getRuntime().exec.call(k,"XMLHttpRequest","abort",D),D=!0}},destroy:function(){k&&("function"===e.typeOf(k.destroy)&&k.destroy(),k=null),this.unbindAll(),this.upload&&(this.upload.unbindAll(),this.upload=null)}})}var h={100:"Continue",101:"Switching Protocols",102:"Processing",200:"OK",201:"Created",202:"Accepted",203:"Non-Authoritative Information",204:"No Content",205:"Reset Content",206:"Partial Content",207:"Multi-Status",226:"IM Used",300:"Multiple Choices",301:"Moved Permanently",302:"Found",303:"See Other",304:"Not Modified",305:"Use Proxy",306:"Reserved",307:"Temporary Redirect",400:"Bad Request",401:"Unauthorized",402:"Payment Required",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",406:"Not Acceptable",407:"Proxy Authentication Required",408:"Request Timeout",409:"Conflict",410:"Gone",411:"Length Required",412:"Precondition Failed",413:"Request Entity Too Large",414:"Request-URI Too Long",415:"Unsupported Media Type",416:"Requested Range Not Satisfiable",417:"Expectation Failed",422:"Unprocessable Entity",423:"Locked",424:"Failed Dependency",426:"Upgrade Required",500:"Internal Server Error",501:"Not Implemented",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout",505:"HTTP Version Not Supported",506:"Variant Also Negotiates",507:"Insufficient Storage",510:"Not Extended"};f.prototype=n.instance;var m=["loadstart","progress","abort","error","load","timeout","loadend"],g=1,v=2;return p.UNSENT=0,p.OPENED=1,p.HEADERS_RECEIVED=2,p.LOADING=3,p.DONE=4,p.prototype=n.instance,p}),i(O,[u,m,v,h],function(e,t,n,i){function r(){function i(){l=d=0,c=this.result=null}function o(t,n){var i=this;u=n,i.bind("TransportingProgress",function(t){d=t.loaded,l>d&&-1===e.inArray(i.state,[r.IDLE,r.DONE])&&a.call(i)},999),i.bind("TransportingComplete",function(){d=l,i.state=r.DONE,c=null,i.result=u.exec.call(i,"Transporter","getAsBlob",t||"")},999),i.state=r.BUSY,i.trigger("TransportingStarted"),a.call(i)}function a(){var e=this,n,i=l-d;f>i&&(f=i),n=t.btoa(c.substr(d,f)),u.exec.call(e,"Transporter","receive",n,l)}var s,u,c,l,d,f;n.call(this),e.extend(this,{uid:e.guid("uid_"),state:r.IDLE,result:null,transport:function(t,n,r){var a=this;if(r=e.extend({chunk_size:204798},r),(s=r.chunk_size%3)&&(r.chunk_size+=3-s),f=r.chunk_size,i.call(this),c=t,l=t.length,"string"===e.typeOf(r)||r.ruid)o.call(a,n,this.connectRuntime(r));else{var u=function(e,t){a.unbind("RuntimeInit",u),o.call(a,n,t)};this.bind("RuntimeInit",u),this.connectRuntime(r)}},abort:function(){var e=this;e.state=r.IDLE,u&&(u.exec.call(e,"Transporter","clear"),e.trigger("TransportingAborted")),i.call(e)},destroy:function(){this.unbindAll(),u=null,this.disconnectRuntime(),i.call(this)}})}return r.IDLE=0,r.BUSY=1,r.DONE=2,r.prototype=i.instance,r}),i(I,[u,f,p,T,A,g,v,O,d,h,y,w,m],function(e,t,n,i,r,o,a,s,u,c,l,d,f){function p(){function i(e){e||(e=this.getRuntime().exec.call(this,"Image","getInfo")),this.size=e.size,this.width=e.width,this.height=e.height,this.type=e.type,this.meta=e.meta,""===this.name&&(this.name=e.name)}function c(t){var i=e.typeOf(t);try{if(t instanceof p){if(!t.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);m.apply(this,arguments)}else if(t instanceof l){if(!~e.inArray(t.type,["image/jpeg","image/png"]))throw new n.ImageError(n.ImageError.WRONG_FORMAT);g.apply(this,arguments)}else if(-1!==e.inArray(i,["blob","file"]))c.call(this,new d(null,t),arguments[1]);else if("string"===i)/^data:[^;]*;base64,/.test(t)?c.call(this,new l(null,{data:t}),arguments[1]):v.apply(this,arguments);else{if("node"!==i||"img"!==t.nodeName.toLowerCase())throw new n.DOMException(n.DOMException.TYPE_MISMATCH_ERR);c.call(this,t.src,arguments[1])}}catch(r){this.trigger("error",r)}}function m(t,n){var i=this.connectRuntime(t.ruid);this.ruid=i.uid,i.exec.call(this,"Image","loadFromImage",t,"undefined"===e.typeOf(n)?!0:n)}function g(t,n){function i(e){r.ruid=e.uid,e.exec.call(r,"Image","loadFromBlob",t)}var r=this;r.name=t.name||"",t.isDetached()?(this.bind("RuntimeInit",function(e,t){i(t)}),n&&"string"==typeof n.required_caps&&(n.required_caps=o.parseCaps(n.required_caps)),this.connectRuntime(e.extend({required_caps:{access_image_binary:!0,resize_image:!0}},n))):i(this.connectRuntime(t.ruid))}function v(e,t){var n=this,i;i=new r,i.open("get",e),i.responseType="blob",i.onprogress=function(e){n.trigger(e)},i.onload=function(){g.call(n,i.response,!0)},i.onerror=function(e){n.trigger(e)},i.onloadend=function(){i.destroy()},i.bind("RuntimeError",function(e,t){n.trigger("RuntimeError",t)}),i.send(null,t)}a.call(this),e.extend(this,{uid:e.guid("uid_"),ruid:null,name:"",size:0,width:0,height:0,type:"",meta:{},clone:function(){this.load.apply(this,arguments)},load:function(){this.bind("Load Resize",function(){i.call(this)},999),this.convertEventPropsToHandlers(h),c.apply(this,arguments)},downsize:function(t,i,r,o){try{if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);if(this.width>p.MAX_RESIZE_WIDTH||this.height>p.MAX_RESIZE_HEIGHT)throw new n.ImageError(n.ImageError.MAX_RESOLUTION_ERR);(!t&&!i||"undefined"===e.typeOf(r))&&(r=!1),t=t||this.width,i=i||this.height,o="undefined"===e.typeOf(o)?!0:!!o,this.getRuntime().exec.call(this,"Image","downsize",t,i,r,o)}catch(a){this.trigger("error",a)}},crop:function(e,t,n){this.downsize(e,t,!0,n)},getAsCanvas:function(){if(!u.can("create_canvas"))throw new n.RuntimeError(n.RuntimeError.NOT_SUPPORTED_ERR);var e=this.connectRuntime(this.ruid);return e.exec.call(this,"Image","getAsCanvas")},getAsBlob:function(e,t){if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);return e||(e="image/jpeg"),"image/jpeg"!==e||t||(t=90),this.getRuntime().exec.call(this,"Image","getAsBlob",e,t)},getAsDataURL:function(e,t){if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);return this.getRuntime().exec.call(this,"Image","getAsDataURL",e,t)},getAsBinaryString:function(e,t){var n=this.getAsDataURL(e,t);return f.atob(n.substring(n.indexOf("base64,")+7))},embed:function(i){function r(){if(u.can("create_canvas")){var t=a.getAsCanvas();if(t)return i.appendChild(t),t=null,a.destroy(),o.trigger("embedded"),void 0}var r=a.getAsDataURL(c,l);if(!r)throw new n.ImageError(n.ImageError.WRONG_FORMAT);if(u.can("use_data_uri_of",r.length))i.innerHTML='<img src="'+r+'" width="'+a.width+'" height="'+a.height+'" />',a.destroy(),o.trigger("embedded");else{var d=new s;d.bind("TransportingComplete",function(){v=o.connectRuntime(this.result.ruid),o.bind("Embedded",function(){e.extend(v.getShimContainer().style,{top:"0px",left:"0px",width:a.width+"px",height:a.height+"px"}),v=null},999),v.exec.call(o,"ImageView","display",this.result.uid,m,g),a.destroy()}),d.transport(f.atob(r.substring(r.indexOf("base64,")+7)),c,e.extend({},h,{required_caps:{display_media:!0},runtime_order:"flash,silverlight",container:i}))}}var o=this,a,c,l,d,h=arguments[1]||{},m=this.width,g=this.height,v;try{if(!(i=t.get(i)))throw new n.DOMException(n.DOMException.INVALID_NODE_TYPE_ERR);if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);if(this.width>p.MAX_RESIZE_WIDTH||this.height>p.MAX_RESIZE_HEIGHT)throw new n.ImageError(n.ImageError.MAX_RESOLUTION_ERR);if(c=h.type||this.type||"image/jpeg",l=h.quality||90,d="undefined"!==e.typeOf(h.crop)?h.crop:!1,h.width)m=h.width,g=h.height||m;else{var y=t.getSize(i);y.w&&y.h&&(m=y.w,g=y.h)}return a=new p,a.bind("Resize",function(){r.call(o)}),a.bind("Load",function(){a.downsize(m,g,d,!1)}),a.clone(this,!1),a}catch(w){this.trigger("error",w)}},destroy:function(){this.ruid&&(this.getRuntime().exec.call(this,"Image","destroy"),this.disconnectRuntime()),this.unbindAll()}})}var h=["progress","load","error","resize","embedded"];return p.MAX_RESIZE_WIDTH=6500,p.MAX_RESIZE_HEIGHT=6500,p.prototype=c.instance,p}),i(D,[u,p,g,d],function(e,t,n,i){function r(t){var r=this,s=n.capTest,u=n.capTrue,c=e.extend({access_binary:s(window.FileReader||window.File&&window.File.getAsDataURL),access_image_binary:function(){return r.can("access_binary")&&!!a.Image},display_media:s(i.can("create_canvas")||i.can("use_data_uri_over32kb")),do_cors:s(window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest),drag_and_drop:s(function(){var e=document.createElement("div");return("draggable"in e||"ondragstart"in e&&"ondrop"in e)&&("IE"!==i.browser||i.version>9)}()),filter_by_extension:s(function(){return"Chrome"===i.browser&&i.version>=28||"IE"===i.browser&&i.version>=10}()),return_response_headers:u,return_response_type:function(e){return"json"===e&&window.JSON?!0:i.can("return_response_type",e)},return_status_code:u,report_upload_progress:s(window.XMLHttpRequest&&(new XMLHttpRequest).upload),resize_image:function(){return r.can("access_binary")&&i.can("create_canvas")},select_file:function(){return i.can("use_fileinput")&&window.File},select_folder:function(){return r.can("select_file")&&"Chrome"===i.browser&&i.version>=21},select_multiple:function(){return!(!r.can("select_file")||"Safari"===i.browser&&"Windows"===i.os||"iOS"===i.os&&i.verComp(i.osVersion,"7.0.4","<"))},send_binary_string:s(window.XMLHttpRequest&&((new XMLHttpRequest).sendAsBinary||window.Uint8Array&&window.ArrayBuffer)),send_custom_headers:s(window.XMLHttpRequest),send_multipart:function(){return!!(window.XMLHttpRequest&&(new XMLHttpRequest).upload&&window.FormData)||r.can("send_binary_string")},slice_blob:s(window.File&&(File.prototype.mozSlice||File.prototype.webkitSlice||File.prototype.slice)),stream_upload:function(){return r.can("slice_blob")&&r.can("send_multipart")},summon_file_dialog:s(function(){return"Firefox"===i.browser&&i.version>=4||"Opera"===i.browser&&i.version>=12||"IE"===i.browser&&i.version>=10||!!~e.inArray(i.browser,["Chrome","Safari"])}()),upload_filesize:u},arguments[2]);n.call(this,t,arguments[1]||o,c),e.extend(this,{init:function(){this.trigger("Init")},destroy:function(e){return function(){e.call(r),e=r=null}}(this.destroy)}),e.extend(this.getShim(),a)}var o="html5",a={};return n.addConstructor(o,r),a}),i(N,[D,y],function(e,t){function n(){function e(e,t,n){var i;if(!window.File.prototype.slice)return(i=window.File.prototype.webkitSlice||window.File.prototype.mozSlice)?i.call(e,t,n):null;try{return e.slice(),e.slice(t,n)}catch(r){return e.slice(t,n-t)}}this.slice=function(){return new t(this.getRuntime().uid,e.apply(this,arguments))}}return e.Blob=n}),i(L,[u],function(e){function t(){this.returnValue=!1}function n(){this.cancelBubble=!0}var i={},r="moxie_"+e.guid(),o=function(o,a,s,u){var c,l;a=a.toLowerCase(),o.addEventListener?(c=s,o.addEventListener(a,c,!1)):o.attachEvent&&(c=function(){var e=window.event;e.target||(e.target=e.srcElement),e.preventDefault=t,e.stopPropagation=n,s(e)},o.attachEvent("on"+a,c)),o[r]||(o[r]=e.guid()),i.hasOwnProperty(o[r])||(i[o[r]]={}),l=i[o[r]],l.hasOwnProperty(a)||(l[a]=[]),l[a].push({func:c,orig:s,key:u})},a=function(t,n,o){var a,s;if(n=n.toLowerCase(),t[r]&&i[t[r]]&&i[t[r]][n]){a=i[t[r]][n];for(var u=a.length-1;u>=0&&(a[u].orig!==o&&a[u].key!==o||(t.removeEventListener?t.removeEventListener(n,a[u].func,!1):t.detachEvent&&t.detachEvent("on"+n,a[u].func),a[u].orig=null,a[u].func=null,a.splice(u,1),o===s));u--);if(a.length||delete i[t[r]][n],e.isEmptyObj(i[t[r]])){delete i[t[r]];try{delete t[r]}catch(c){t[r]=s}}}},s=function(t,n){t&&t[r]&&e.each(i[t[r]],function(e,i){a(t,i,n)})};return{addEvent:o,removeEvent:a,removeAllEvents:s}}),i(M,[D,u,f,L,l,d],function(e,t,n,i,r,o){function a(){var e=[],a;t.extend(this,{init:function(s){var u=this,c=u.getRuntime(),l,d,f,p,h,m;a=s,e=[],f=a.accept.mimes||r.extList2mimes(a.accept,c.can("filter_by_extension")),d=c.getShimContainer(),d.innerHTML='<input id="'+c.uid+'" type="file" style="font-size:999px;opacity:0;"'+(a.multiple&&c.can("select_multiple")?"multiple":"")+(a.directory&&c.can("select_folder")?"webkitdirectory directory":"")+(f?' accept="'+f.join(",")+'"':"")+" />",l=n.get(c.uid),t.extend(l.style,{position:"absolute",top:0,left:0,width:"100%",height:"100%"}),p=n.get(a.browse_button),c.can("summon_file_dialog")&&("static"===n.getStyle(p,"position")&&(p.style.position="relative"),h=parseInt(n.getStyle(p,"z-index"),10)||1,p.style.zIndex=h,d.style.zIndex=h-1,i.addEvent(p,"click",function(e){var t=n.get(c.uid);t&&!t.disabled&&t.click(),e.preventDefault()},u.uid)),m=c.can("summon_file_dialog")?p:d,i.addEvent(m,"mouseover",function(){u.trigger("mouseenter")},u.uid),i.addEvent(m,"mouseout",function(){u.trigger("mouseleave")},u.uid),i.addEvent(m,"mousedown",function(){u.trigger("mousedown")},u.uid),i.addEvent(n.get(a.container),"mouseup",function(){u.trigger("mouseup")},u.uid),l.onchange=function g(){if(e=[],a.directory?t.each(this.files,function(t){"."!==t.name&&e.push(t)}):e=[].slice.call(this.files),"IE"!==o.browser)this.value="";else{var n=this.cloneNode(!0);this.parentNode.replaceChild(n,this),n.onchange=g}u.trigger("change")},u.trigger({type:"ready",async:!0}),d=null},getFiles:function(){return e},disable:function(e){var t=this.getRuntime(),i;(i=n.get(t.uid))&&(i.disabled=!!e)},destroy:function(){var t=this.getRuntime(),r=t.getShim(),o=t.getShimContainer();i.removeAllEvents(o,this.uid),i.removeAllEvents(a&&n.get(a.container),this.uid),i.removeAllEvents(a&&n.get(a.browse_button),this.uid),o&&(o.innerHTML=""),r.removeInstance(this.uid),e=a=o=r=null}})}return e.FileInput=a}),i(C,[D,u,f,L,l],function(e,t,n,i,r){function o(){function e(e){for(var n=[],i=0;i<e.length;i++)[].push.apply(n,e[i].extensions.split(/\s*,\s*/));return-1===t.inArray("*",n)?n:[]}function o(e){var n=r.getFileExtension(e.name);return!n||!d.length||-1!==t.inArray(n,d)}function a(e,n){var i=[];t.each(e,function(e){var t=e.webkitGetAsEntry();if(t)if(t.isFile){var n=e.getAsFile();o(n)&&l.push(n)}else i.push(t)}),i.length?s(i,n):n()}function s(e,n){var i=[];t.each(e,function(e){i.push(function(t){u(e,t)})}),t.inSeries(i,function(){n()})}function u(e,t){e.isFile?e.file(function(e){o(e)&&l.push(e),t()},function(){t()}):e.isDirectory?c(e,t):t()}function c(e,t){function n(e){r.readEntries(function(t){t.length?([].push.apply(i,t),n(e)):e()},e)}var i=[],r=e.createReader();n(function(){s(i,t)})}var l=[],d=[],f;t.extend(this,{init:function(n){var r=this,s;f=n,d=e(f.accept),s=f.container,i.addEvent(s,"dragover",function(e){e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="copy"},r.uid),i.addEvent(s,"drop",function(e){e.preventDefault(),e.stopPropagation(),l=[],e.dataTransfer.items&&e.dataTransfer.items[0].webkitGetAsEntry?a(e.dataTransfer.items,function(){r.trigger("drop")}):(t.each(e.dataTransfer.files,function(e){o(e)&&l.push(e)}),r.trigger("drop"))},r.uid),i.addEvent(s,"dragenter",function(e){e.preventDefault(),e.stopPropagation(),r.trigger("dragenter")},r.uid),i.addEvent(s,"dragleave",function(e){e.preventDefault(),e.stopPropagation(),r.trigger("dragleave")},r.uid)},getFiles:function(){return l},destroy:function(){i.removeAllEvents(f&&n.get(f.container),this.uid),l=d=f=null}})}return e.FileDrop=o}),i(F,[D,m,u],function(e,t,n){function i(){function e(e){return t.atob(e.substring(e.indexOf("base64,")+7))}var i,r=!1;n.extend(this,{read:function(e,t){var o=this;i=new window.FileReader,i.addEventListener("progress",function(e){o.trigger(e)}),i.addEventListener("load",function(e){o.trigger(e)}),i.addEventListener("error",function(e){o.trigger(e,i.error)}),i.addEventListener("loadend",function(){i=null}),"function"===n.typeOf(i[e])?(r=!1,i[e](t.getSource())):"readAsBinaryString"===e&&(r=!0,i.readAsDataURL(t.getSource()))},getResult:function(){return i&&i.result?r?e(i.result):i.result:null},abort:function(){i&&i.abort()},destroy:function(){i=null}})}return e.FileReader=i}),i(H,[D,u,l,b,w,y,S,p,d],function(e,t,n,i,r,o,a,s,u){function c(){function e(e,t){var n=this,i,r;i=t.getBlob().getSource(),r=new window.FileReader,r.onload=function(){t.append(t.getBlobName(),new o(null,{type:i.type,data:r.result})),f.send.call(n,e,t)},r.readAsBinaryString(i)}function c(){return!window.XMLHttpRequest||"IE"===u.browser&&u.version<8?function(){for(var e=["Msxml2.XMLHTTP.6.0","Microsoft.XMLHTTP"],t=0;t<e.length;t++)try{return new ActiveXObject(e[t])}catch(n){}}():new window.XMLHttpRequest}function l(e){var t=e.responseXML,n=e.responseText;return"IE"===u.browser&&n&&t&&!t.documentElement&&/[^\/]+\/[^\+]+\+xml/.test(e.getResponseHeader("Content-Type"))&&(t=new window.ActiveXObject("Microsoft.XMLDOM"),t.async=!1,t.validateOnParse=!1,t.loadXML(n)),t&&("IE"===u.browser&&0!==t.parseError||!t.documentElement||"parsererror"===t.documentElement.tagName)?null:t}function d(e){var t="----moxieboundary"+(new Date).getTime(),n="--",i="\r\n",r="",a=this.getRuntime();if(!a.can("send_binary_string"))throw new s.RuntimeError(s.RuntimeError.NOT_SUPPORTED_ERR);return p.setRequestHeader("Content-Type","multipart/form-data; boundary="+t),e.each(function(e,a){r+=e instanceof o?n+t+i+'Content-Disposition: form-data; name="'+a+'"; filename="'+unescape(encodeURIComponent(e.name||"blob"))+'"'+i+"Content-Type: "+(e.type||"application/octet-stream")+i+i+e.getSource()+i:n+t+i+'Content-Disposition: form-data; name="'+a+'"'+i+i+unescape(encodeURIComponent(e))+i}),r+=n+t+n+i}var f=this,p,h;t.extend(this,{send:function(n,r){var s=this,l="Mozilla"===u.browser&&u.version>=4&&u.version<7,f="Android Browser"===u.browser,m=!1;if(h=n.url.replace(/^.+?\/([\w\-\.]+)$/,"$1").toLowerCase(),p=c(),p.open(n.method,n.url,n.async,n.user,n.password),r instanceof o)r.isDetached()&&(m=!0),r=r.getSource();else if(r instanceof a){if(r.hasBlob())if(r.getBlob().isDetached())r=d.call(s,r),m=!0;else if((l||f)&&"blob"===t.typeOf(r.getBlob().getSource())&&window.FileReader)return e.call(s,n,r),void 0;if(r instanceof a){var g=new window.FormData;r.each(function(e,t){e instanceof o?g.append(t,e.getSource()):g.append(t,e)}),r=g}}p.upload?(n.withCredentials&&(p.withCredentials=!0),p.addEventListener("load",function(e){s.trigger(e)}),p.addEventListener("error",function(e){s.trigger(e)}),p.addEventListener("progress",function(e){s.trigger(e)}),p.upload.addEventListener("progress",function(e){s.trigger({type:"UploadProgress",loaded:e.loaded,total:e.total})})):p.onreadystatechange=function v(){switch(p.readyState){case 1:break;case 2:break;case 3:var e,t;try{i.hasSameOrigin(n.url)&&(e=p.getResponseHeader("Content-Length")||0),p.responseText&&(t=p.responseText.length)}catch(r){e=t=0}s.trigger({type:"progress",lengthComputable:!!e,total:parseInt(e,10),loaded:t});break;case 4:p.onreadystatechange=function(){},0===p.status?s.trigger("error"):s.trigger("load")}},t.isEmptyObj(n.headers)||t.each(n.headers,function(e,t){p.setRequestHeader(t,e)}),""!==n.responseType&&"responseType"in p&&(p.responseType="json"!==n.responseType||u.can("return_response_type","json")?n.responseType:"text"),m?p.sendAsBinary?p.sendAsBinary(r):function(){for(var e=new Uint8Array(r.length),t=0;t<r.length;t++)e[t]=255&r.charCodeAt(t);p.send(e.buffer)}():p.send(r),s.trigger("loadstart")},getStatus:function(){try{if(p)return p.status}catch(e){}return 0},getResponse:function(e){var t=this.getRuntime();try{switch(e){case"blob":var i=new r(t.uid,p.response),o=p.getResponseHeader("Content-Disposition");if(o){var a=o.match(/filename=([\'\"'])([^\1]+)\1/);a&&(h=a[2])}return i.name=h,i.type||(i.type=n.getFileMime(h)),i;case"json":return u.can("return_response_type","json")?p.response:200===p.status&&window.JSON?JSON.parse(p.responseText):null;case"document":return l(p);default:return""!==p.responseText?p.responseText:null}}catch(s){return null}},getAllResponseHeaders:function(){try{return p.getAllResponseHeaders()}catch(e){}return""},abort:function(){p&&p.abort()},destroy:function(){f=h=null}})}return e.XMLHttpRequest=c}),i(P,[],function(){return function(){function e(e,t){var n=r?0:-8*(t-1),i=0,a;for(a=0;t>a;a++)i|=o.charCodeAt(e+a)<<Math.abs(n+8*a);return i}function n(e,t,n){n=3===arguments.length?n:o.length-t-1,o=o.substr(0,t)+e+o.substr(n+t)}function i(e,t,i){var o="",a=r?0:-8*(i-1),s;for(s=0;i>s;s++)o+=String.fromCharCode(255&t>>Math.abs(a+8*s));n(o,e,i)}var r=!1,o;return{II:function(e){return e===t?r:(r=e,void 0)},init:function(e){r=!1,o=e},SEGMENT:function(e,t,i){switch(arguments.length){case 1:return o.substr(e,o.length-e-1);case 2:return o.substr(e,t);case 3:n(i,e,t);break;default:return o}},BYTE:function(t){return e(t,1)},SHORT:function(t){return e(t,2)},LONG:function(n,r){return r===t?e(n,4):(i(n,r,4),void 0)},SLONG:function(t){var n=e(t,4);return n>2147483647?n-4294967296:n},STRING:function(t,n){var i="";for(n+=t;n>t;t++)i+=String.fromCharCode(e(t,1));return i}}}}),i(k,[P],function(e){return function t(n){var i=[],r,o,a,s=0;if(r=new e,r.init(n),65496===r.SHORT(0)){for(o=2;o<=n.length;)if(a=r.SHORT(o),a>=65488&&65495>=a)o+=2;else{if(65498===a||65497===a)break;s=r.SHORT(o+2)+2,a>=65505&&65519>=a&&i.push({hex:a,name:"APP"+(15&a),start:o,length:s,segment:r.SEGMENT(o,s)}),o+=s}return r.init(null),{headers:i,restore:function(e){var t,n;for(r.init(e),o=65504==r.SHORT(2)?4+r.SHORT(4):2,n=0,t=i.length;t>n;n++)r.SEGMENT(o,0,i[n].segment),o+=i[n].length;return e=r.SEGMENT(),r.init(null),e},strip:function(e){var n,i,o;for(i=new t(e),n=i.headers,i.purge(),r.init(e),o=n.length;o--;)r.SEGMENT(n[o].start,n[o].length,"");return e=r.SEGMENT(),r.init(null),e},get:function(e){for(var t=[],n=0,r=i.length;r>n;n++)i[n].name===e.toUpperCase()&&t.push(i[n].segment);return t},set:function(e,t){var n=[],r,o,a;for("string"==typeof t?n.push(t):n=t,r=o=0,a=i.length;a>r&&(i[r].name===e.toUpperCase()&&(i[r].segment=n[o],i[r].length=n[o].length,o++),!(o>=n.length));r++);},purge:function(){i=[],r.init(null),r=null}}}}}),i(U,[u,P],function(e,n){return function i(){function i(e,n){var i=a.SHORT(e),r,o,s,u,d,f,p,h,m=[],g={};for(r=0;i>r;r++)if(p=f=e+12*r+2,s=n[a.SHORT(p)],s!==t){switch(u=a.SHORT(p+=2),d=a.LONG(p+=2),p+=4,m=[],u){case 1:case 7:for(d>4&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.BYTE(p+o);break;case 2:d>4&&(p=a.LONG(p)+c.tiffHeader),g[s]=a.STRING(p,d-1);continue;case 3:for(d>2&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.SHORT(p+2*o);break;case 4:for(d>1&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.LONG(p+4*o);break;case 5:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.LONG(p+4*o)/a.LONG(p+4*o+4);break;case 9:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.SLONG(p+4*o);break;case 10:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.SLONG(p+4*o)/a.SLONG(p+4*o+4);break;default:continue}h=1==d?m[0]:m,g[s]=l.hasOwnProperty(s)&&"object"!=typeof h?l[s][h]:h}return g}function r(){var e=c.tiffHeader;return a.II(18761==a.SHORT(e)),42!==a.SHORT(e+=2)?!1:(c.IFD0=c.tiffHeader+a.LONG(e+=2),u=i(c.IFD0,s.tiff),"ExifIFDPointer"in u&&(c.exifIFD=c.tiffHeader+u.ExifIFDPointer,delete u.ExifIFDPointer),"GPSInfoIFDPointer"in u&&(c.gpsIFD=c.tiffHeader+u.GPSInfoIFDPointer,delete u.GPSInfoIFDPointer),!0)}function o(e,t,n){var i,r,o,u=0;if("string"==typeof t){var l=s[e.toLowerCase()];for(var d in l)if(l[d]===t){t=d;break}}i=c[e.toLowerCase()+"IFD"],r=a.SHORT(i);for(var f=0;r>f;f++)if(o=i+12*f+2,a.SHORT(o)==t){u=o+8;break}return u?(a.LONG(u,n),!0):!1}var a,s,u,c={},l;return a=new n,s={tiff:{274:"Orientation",270:"ImageDescription",271:"Make",272:"Model",305:"Software",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer"},exif:{36864:"ExifVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",36867:"DateTimeOriginal",33434:"ExposureTime",33437:"FNumber",34855:"ISOSpeedRatings",37377:"ShutterSpeedValue",37378:"ApertureValue",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37386:"FocalLength",41986:"ExposureMode",41987:"WhiteBalance",41990:"SceneCaptureType",41988:"DigitalZoomRatio",41992:"Contrast",41993:"Saturation",41994:"Sharpness"},gps:{0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude"}},l={ColorSpace:{1:"sRGB",0:"Uncalibrated"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{1:"Daylight",2:"Fliorescent",3:"Tungsten",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 -5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire.",1:"Flash fired.",5:"Strobe return light not detected.",7:"Strobe return light detected.",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},ExposureMode:{0:"Auto exposure",1:"Manual exposure",2:"Auto bracket"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},GPSLatitudeRef:{N:"North latitude",S:"South latitude"},GPSLongitudeRef:{E:"East longitude",W:"West longitude"}},{init:function(e){return c={tiffHeader:10},e!==t&&e.length?(a.init(e),65505===a.SHORT(0)&&"EXIF\0"===a.STRING(4,5).toUpperCase()?r():!1):!1
+},TIFF:function(){return u},EXIF:function(){var t;if(t=i(c.exifIFD,s.exif),t.ExifVersion&&"array"===e.typeOf(t.ExifVersion)){for(var n=0,r="";n<t.ExifVersion.length;n++)r+=String.fromCharCode(t.ExifVersion[n]);t.ExifVersion=r}return t},GPS:function(){var t;return t=i(c.gpsIFD,s.gps),t.GPSVersionID&&"array"===e.typeOf(t.GPSVersionID)&&(t.GPSVersionID=t.GPSVersionID.join(".")),t},setExif:function(e,t){return"PixelXDimension"!==e&&"PixelYDimension"!==e?!1:o("exif",e,t)},getBinary:function(){return a.SEGMENT()},purge:function(){a.init(null),a=u=null,c={}}}}}),i(B,[u,p,k,P,U],function(e,t,n,i,r){function o(o){function a(){for(var e=0,t,n;e<=u.length;){if(t=c.SHORT(e+=2),t>=65472&&65475>=t)return e+=5,{height:c.SHORT(e),width:c.SHORT(e+=2)};n=c.SHORT(e+=2),e+=n-2}return null}function s(){d&&l&&c&&(d.purge(),l.purge(),c.init(null),u=f=l=d=c=null)}var u,c,l,d,f,p;if(u=o,c=new i,c.init(u),65496!==c.SHORT(0))throw new t.ImageError(t.ImageError.WRONG_FORMAT);l=new n(o),d=new r,p=!!d.init(l.get("app1")[0]),f=a.call(this),e.extend(this,{type:"image/jpeg",size:u.length,width:f&&f.width||0,height:f&&f.height||0,setExif:function(t,n){return p?("object"===e.typeOf(t)?e.each(t,function(e,t){d.setExif(t,e)}):d.setExif(t,n),l.set("app1",d.getBinary()),void 0):!1},writeHeaders:function(){return arguments.length?l.restore(arguments[0]):u=l.restore(u)},stripHeaders:function(e){return l.strip(e)},purge:function(){s.call(this)}}),p&&(this.meta={tiff:d.TIFF(),exif:d.EXIF(),gps:d.GPS()})}return o}),i(z,[p,u,P],function(e,t,n){function i(i){function r(){var e,t;return e=a.call(this,8),"IHDR"==e.type?(t=e.start,{width:u.LONG(t),height:u.LONG(t+=4)}):null}function o(){u&&(u.init(null),s=d=c=l=u=null)}function a(e){var t,n,i,r;return t=u.LONG(e),n=u.STRING(e+=4,4),i=e+=4,r=u.LONG(e+t),{length:t,type:n,start:i,CRC:r}}var s,u,c,l,d;s=i,u=new n,u.init(s),function(){var t=0,n=0,i=[35152,20039,3338,6666];for(n=0;n<i.length;n++,t+=2)if(i[n]!=u.SHORT(t))throw new e.ImageError(e.ImageError.WRONG_FORMAT)}(),d=r.call(this),t.extend(this,{type:"image/png",size:s.length,width:d.width,height:d.height,purge:function(){o.call(this)}}),o.call(this)}return i}),i(G,[u,p,B,z],function(e,t,n,i){return function(r){var o=[n,i],a;a=function(){for(var e=0;e<o.length;e++)try{return new o[e](r)}catch(n){}throw new t.ImageError(t.ImageError.WRONG_FORMAT)}(),e.extend(this,{type:"",size:0,width:0,height:0,setExif:function(){},writeHeaders:function(e){return e},stripHeaders:function(e){return e},purge:function(){}}),e.extend(this,a),this.purge=function(){a.purge(),a=null}}}),i(q,[],function(){function e(e,i,r){var o=e.naturalWidth,a=e.naturalHeight,s=r.width,u=r.height,c=r.x||0,l=r.y||0,d=i.getContext("2d");t(e)&&(o/=2,a/=2);var f=1024,p=document.createElement("canvas");p.width=p.height=f;for(var h=p.getContext("2d"),m=n(e,o,a),g=0;a>g;){for(var v=g+f>a?a-g:f,y=0;o>y;){var w=y+f>o?o-y:f;h.clearRect(0,0,f,f),h.drawImage(e,-y,-g);var E=y*s/o+c<<0,_=Math.ceil(w*s/o),x=g*u/a/m+l<<0,R=Math.ceil(v*u/a/m);d.drawImage(p,0,0,w,v,E,x,_,R),y+=f}g+=f}p=h=null}function t(e){var t=e.naturalWidth,n=e.naturalHeight;if(t*n>1048576){var i=document.createElement("canvas");i.width=i.height=1;var r=i.getContext("2d");return r.drawImage(e,-t+1,0),0===r.getImageData(0,0,1,1).data[3]}return!1}function n(e,t,n){var i=document.createElement("canvas");i.width=1,i.height=n;var r=i.getContext("2d");r.drawImage(e,0,0);for(var o=r.getImageData(0,0,1,n).data,a=0,s=n,u=n;u>a;){var c=o[4*(u-1)+3];0===c?s=u:a=u,u=s+a>>1}i=null;var l=u/n;return 0===l?1:l}return{isSubsampled:t,renderTo:e}}),i(X,[D,u,p,m,w,G,q,l,d],function(e,t,n,i,r,o,a,s,u){function c(){function e(){if(!E&&!y)throw new n.ImageError(n.DOMException.INVALID_STATE_ERR);return E||y}function c(e){return i.atob(e.substring(e.indexOf("base64,")+7))}function l(e,t){return"data:"+(t||"")+";base64,"+i.btoa(e)}function d(e){var t=this;y=new Image,y.onerror=function(){g.call(this),t.trigger("error",new n.ImageError(n.ImageError.WRONG_FORMAT))},y.onload=function(){t.trigger("load")},y.src=/^data:[^;]*;base64,/.test(e)?e:l(e,x.type)}function f(e,t){var i=this,r;return window.FileReader?(r=new FileReader,r.onload=function(){t(this.result)},r.onerror=function(){i.trigger("error",new n.FileException(n.FileException.NOT_READABLE_ERR))},r.readAsDataURL(e),void 0):t(e.getAsDataURL())}function p(n,i,r,o){var a=this,s,u,c=0,l=0,d,f,p,g;if(b=o,g=this.meta&&this.meta.tiff&&this.meta.tiff.Orientation||1,-1!==t.inArray(g,[5,6,7,8])){var v=n;n=i,i=v}return d=e(),u=r?Math.max:Math.min,s=u(n/d.width,i/d.height),s>1&&(!r||o)?(this.trigger("Resize"),void 0):(E||(E=document.createElement("canvas")),f=Math.round(d.width*s),p=Math.round(d.height*s),r?(E.width=n,E.height=i,f>n&&(c=Math.round((f-n)/2)),p>i&&(l=Math.round((p-i)/2))):(E.width=f,E.height=p),b||m(E.width,E.height,g),h.call(this,d,E,-c,-l,f,p),this.width=E.width,this.height=E.height,R=!0,a.trigger("Resize"),void 0)}function h(e,t,n,i,r,o){if("iOS"===u.OS)a.renderTo(e,t,{width:r,height:o,x:n,y:i});else{var s=t.getContext("2d");s.drawImage(e,n,i,r,o)}}function m(e,t,n){switch(n){case 5:case 6:case 7:case 8:E.width=t,E.height=e;break;default:E.width=e,E.height=t}var i=E.getContext("2d");switch(n){case 2:i.translate(e,0),i.scale(-1,1);break;case 3:i.translate(e,t),i.rotate(Math.PI);break;case 4:i.translate(0,t),i.scale(1,-1);break;case 5:i.rotate(.5*Math.PI),i.scale(1,-1);break;case 6:i.rotate(.5*Math.PI),i.translate(0,-t);break;case 7:i.rotate(.5*Math.PI),i.translate(e,-t),i.scale(-1,1);break;case 8:i.rotate(-.5*Math.PI),i.translate(-e,0)}}function g(){w&&(w.purge(),w=null),_=y=E=x=null,R=!1}var v=this,y,w,E,_,x,R=!1,b=!0;t.extend(this,{loadFromBlob:function(e){var t=this,i=t.getRuntime(),r=arguments.length>1?arguments[1]:!0;if(!i.can("access_binary"))throw new n.RuntimeError(n.RuntimeError.NOT_SUPPORTED_ERR);return x=e,e.isDetached()?(_=e.getSource(),d.call(this,_),void 0):(f.call(this,e.getSource(),function(e){r&&(_=c(e)),d.call(t,e)}),void 0)},loadFromImage:function(e,t){this.meta=e.meta,x=new r(null,{name:e.name,size:e.size,type:e.type}),d.call(this,t?_=e.getAsBinaryString():e.getAsDataURL())},getInfo:function(){var t=this.getRuntime(),n;return!w&&_&&t.can("access_image_binary")&&(w=new o(_)),n={width:e().width||0,height:e().height||0,type:x.type||s.getFileMime(x.name),size:_&&_.length||x.size||0,name:x.name||"",meta:w&&w.meta||this.meta||{}}},downsize:function(){p.apply(this,arguments)},getAsCanvas:function(){return E&&(E.id=this.uid+"_canvas"),E},getAsBlob:function(e,t){return e!==this.type&&p.call(this,this.width,this.height,!1),new r(null,{name:x.name||"",type:e,data:v.getAsBinaryString.call(this,e,t)})},getAsDataURL:function(e){var t=arguments[1]||90;if(!R)return y.src;if("image/jpeg"!==e)return E.toDataURL("image/png");try{return E.toDataURL("image/jpeg",t/100)}catch(n){return E.toDataURL("image/jpeg")}},getAsBinaryString:function(e,t){if(!R)return _||(_=c(v.getAsDataURL(e,t))),_;if("image/jpeg"!==e)_=c(v.getAsDataURL(e,t));else{var n;t||(t=90);try{n=E.toDataURL("image/jpeg",t/100)}catch(i){n=E.toDataURL("image/jpeg")}_=c(n),w&&(_=w.stripHeaders(_),b&&(w.meta&&w.meta.exif&&w.setExif({PixelXDimension:this.width,PixelYDimension:this.height}),_=w.writeHeaders(_)),w.purge(),w=null)}return R=!1,_},destroy:function(){v=null,g.call(this),this.getRuntime().getShim().removeInstance(this.uid)}})}return e.Image=c}),i(j,[u,d,f,p,g],function(e,t,n,i,r){function o(){var e;try{e=navigator.plugins["Shockwave Flash"],e=e.description}catch(t){try{e=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version")}catch(n){e="0.0"}}return e=e.match(/\d+/g),parseFloat(e[0]+"."+e[1])}function a(a){var c=this,l;a=e.extend({swf_url:t.swf_url},a),r.call(this,a,s,{access_binary:function(e){return e&&"browser"===c.mode},access_image_binary:function(e){return e&&"browser"===c.mode},display_media:r.capTrue,do_cors:r.capTrue,drag_and_drop:!1,report_upload_progress:function(){return"client"===c.mode},resize_image:r.capTrue,return_response_headers:!1,return_response_type:function(t){return"json"===t&&window.JSON?!0:!e.arrayDiff(t,["","text","document"])||"browser"===c.mode},return_status_code:function(t){return"browser"===c.mode||!e.arrayDiff(t,[200,404])},select_file:r.capTrue,select_multiple:r.capTrue,send_binary_string:function(e){return e&&"browser"===c.mode},send_browser_cookies:function(e){return e&&"browser"===c.mode},send_custom_headers:function(e){return e&&"browser"===c.mode},send_multipart:r.capTrue,slice_blob:r.capTrue,stream_upload:function(e){return e&&"browser"===c.mode},summon_file_dialog:!1,upload_filesize:function(t){return e.parseSizeStr(t)<=2097152||"client"===c.mode},use_http_method:function(t){return!e.arrayDiff(t,["GET","POST"])}},{access_binary:function(e){return e?"browser":"client"},access_image_binary:function(e){return e?"browser":"client"},report_upload_progress:function(e){return e?"browser":"client"},return_response_type:function(t){return e.arrayDiff(t,["","text","json","document"])?"browser":["client","browser"]},return_status_code:function(t){return e.arrayDiff(t,[200,404])?"browser":["client","browser"]},send_binary_string:function(e){return e?"browser":"client"},send_browser_cookies:function(e){return e?"browser":"client"},send_custom_headers:function(e){return e?"browser":"client"},stream_upload:function(e){return e?"client":"browser"},upload_filesize:function(t){return e.parseSizeStr(t)>=2097152?"client":"browser"}},"client"),o()<10&&(this.mode=!1),e.extend(this,{getShim:function(){return n.get(this.uid)},shimExec:function(e,t){var n=[].slice.call(arguments,2);return c.getShim().exec(this.uid,e,t,n)},init:function(){var n,r,o;o=this.getShimContainer(),e.extend(o.style,{position:"absolute",top:"-8px",left:"-8px",width:"9px",height:"9px",overflow:"hidden"}),n='<object id="'+this.uid+'" type="application/x-shockwave-flash" data="'+a.swf_url+'" ',"IE"===t.browser&&(n+='classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '),n+='width="100%" height="100%" style="outline:0"><param name="movie" value="'+a.swf_url+'" />'+'<param name="flashvars" value="uid='+escape(this.uid)+"&target="+t.global_event_dispatcher+'" />'+'<param name="wmode" value="transparent" />'+'<param name="allowscriptaccess" value="always" />'+"</object>","IE"===t.browser?(r=document.createElement("div"),o.appendChild(r),r.outerHTML=n,r=o=null):o.innerHTML=n,l=setTimeout(function(){c&&!c.initialized&&c.trigger("Error",new i.RuntimeError(i.RuntimeError.NOT_INIT_ERR))},5e3)},destroy:function(e){return function(){e.call(c),clearTimeout(l),a=l=e=c=null}}(this.destroy)},u)}var s="flash",u={};return r.addConstructor(s,a),u}),i(V,[j,y],function(e,t){var n={slice:function(e,n,i,r){var o=this.getRuntime();return 0>n?n=Math.max(e.size+n,0):n>0&&(n=Math.min(n,e.size)),0>i?i=Math.max(e.size+i,0):i>0&&(i=Math.min(i,e.size)),e=o.shimExec.call(this,"Blob","slice",n,i,r||""),e&&(e=new t(o.uid,e)),e}};return e.Blob=n}),i(W,[j],function(e){var t={init:function(e){this.getRuntime().shimExec.call(this,"FileInput","init",{name:e.name,accept:e.accept,multiple:e.multiple}),this.trigger("ready")}};return e.FileInput=t}),i(Y,[j,m],function(e,t){function n(e,n){switch(n){case"readAsText":return t.atob(e,"utf8");case"readAsBinaryString":return t.atob(e);case"readAsDataURL":return e}return null}var i="",r={read:function(e,t){var r=this,o=r.getRuntime();return"readAsDataURL"===e&&(i="data:"+(t.type||"")+";base64,"),r.bind("Progress",function(t,r){r&&(i+=n(r,e))}),o.shimExec.call(this,"FileReader","readAsBase64",t.uid)},getResult:function(){return i},destroy:function(){i=null}};return e.FileReader=r}),i($,[j,m],function(e,t){function n(e,n){switch(n){case"readAsText":return t.atob(e,"utf8");case"readAsBinaryString":return t.atob(e);case"readAsDataURL":return e}return null}var i={read:function(e,t){var i,r=this.getRuntime();return(i=r.shimExec.call(this,"FileReaderSync","readAsBase64",t.uid))?("readAsDataURL"===e&&(i="data:"+(t.type||"")+";base64,"+i),n(i,e,t.type)):null}};return e.FileReaderSync=i}),i(J,[j,u,y,w,T,S,O],function(e,t,n,i,r,o,a){var s={send:function(e,i){function r(){e.transport=l.mode,l.shimExec.call(c,"XMLHttpRequest","send",e,i)}function s(e,t){l.shimExec.call(c,"XMLHttpRequest","appendBlob",e,t.uid),i=null,r()}function u(e,t){var n=new a;n.bind("TransportingComplete",function(){t(this.result)}),n.transport(e.getSource(),e.type,{ruid:l.uid})}var c=this,l=c.getRuntime();if(t.isEmptyObj(e.headers)||t.each(e.headers,function(e,t){l.shimExec.call(c,"XMLHttpRequest","setRequestHeader",t,e.toString())}),i instanceof o){var d;if(i.each(function(e,t){e instanceof n?d=t:l.shimExec.call(c,"XMLHttpRequest","append",t,e)}),i.hasBlob()){var f=i.getBlob();f.isDetached()?u(f,function(e){f.destroy(),s(d,e)}):s(d,f)}else i=null,r()}else i instanceof n?i.isDetached()?u(i,function(e){i.destroy(),i=e.uid,r()}):(i=i.uid,r()):r()},getResponse:function(e){var n,o,a=this.getRuntime();if(o=a.shimExec.call(this,"XMLHttpRequest","getResponseAsBlob")){if(o=new i(a.uid,o),"blob"===e)return o;try{if(n=new r,~t.inArray(e,["","text"]))return n.readAsText(o);if("json"===e&&window.JSON)return JSON.parse(n.readAsText(o))}finally{o.destroy()}}return null},abort:function(e){var t=this.getRuntime();t.shimExec.call(this,"XMLHttpRequest","abort"),this.dispatchEvent("readystatechange"),this.dispatchEvent("abort")}};return e.XMLHttpRequest=s}),i(Z,[j,y],function(e,t){var n={getAsBlob:function(e){var n=this.getRuntime(),i=n.shimExec.call(this,"Transporter","getAsBlob",e);return i?new t(n.uid,i):null}};return e.Transporter=n}),i(K,[j,u,O,y,T],function(e,t,n,i,r){var o={loadFromBlob:function(e){function t(e){r.shimExec.call(i,"Image","loadFromBlob",e.uid),i=r=null}var i=this,r=i.getRuntime();if(e.isDetached()){var o=new n;o.bind("TransportingComplete",function(){t(o.result.getSource())}),o.transport(e.getSource(),e.type,{ruid:r.uid})}else t(e.getSource())},loadFromImage:function(e){var t=this.getRuntime();return t.shimExec.call(this,"Image","loadFromImage",e.uid)},getAsBlob:function(e,t){var n=this.getRuntime(),r=n.shimExec.call(this,"Image","getAsBlob",e,t);return r?new i(n.uid,r):null},getAsDataURL:function(){var e=this.getRuntime(),t=e.Image.getAsBlob.apply(this,arguments),n;return t?(n=new r,n.readAsDataURL(t)):null}};return e.Image=o}),i(Q,[u,d,f,p,g],function(e,t,n,i,r){function o(e){var t=!1,n=null,i,r,o,a,s,u=0;try{try{n=new ActiveXObject("AgControl.AgControl"),n.IsVersionSupported(e)&&(t=!0),n=null}catch(c){var l=navigator.plugins["Silverlight Plug-In"];if(l){for(i=l.description,"1.0.30226.2"===i&&(i="2.0.30226.2"),r=i.split(".");r.length>3;)r.pop();for(;r.length<4;)r.push(0);for(o=e.split(".");o.length>4;)o.pop();do a=parseInt(o[u],10),s=parseInt(r[u],10),u++;while(u<o.length&&a===s);s>=a&&!isNaN(a)&&(t=!0)}}}catch(d){t=!1}return t}function a(a){var c=this,l;a=e.extend({xap_url:t.xap_url},a),r.call(this,a,s,{access_binary:r.capTrue,access_image_binary:r.capTrue,display_media:r.capTrue,do_cors:r.capTrue,drag_and_drop:!1,report_upload_progress:r.capTrue,resize_image:r.capTrue,return_response_headers:function(e){return e&&"client"===c.mode},return_response_type:function(e){return"json"!==e?!0:!!window.JSON},return_status_code:function(t){return"client"===c.mode||!e.arrayDiff(t,[200,404])},select_file:r.capTrue,select_multiple:r.capTrue,send_binary_string:r.capTrue,send_browser_cookies:function(e){return e&&"browser"===c.mode},send_custom_headers:function(e){return e&&"client"===c.mode},send_multipart:r.capTrue,slice_blob:r.capTrue,stream_upload:!0,summon_file_dialog:!1,upload_filesize:r.capTrue,use_http_method:function(t){return"client"===c.mode||!e.arrayDiff(t,["GET","POST"])}},{return_response_headers:function(e){return e?"client":"browser"},return_status_code:function(t){return e.arrayDiff(t,[200,404])?"client":["client","browser"]},send_browser_cookies:function(e){return e?"browser":"client"},send_custom_headers:function(e){return e?"client":"browser"},use_http_method:function(t){return e.arrayDiff(t,["GET","POST"])?"client":["client","browser"]}}),o("2.0.31005.0")&&"Opera"!==t.browser||(this.mode=!1),e.extend(this,{getShim:function(){return n.get(this.uid).content.Moxie},shimExec:function(e,t){var n=[].slice.call(arguments,2);return c.getShim().exec(this.uid,e,t,n)},init:function(){var e;e=this.getShimContainer(),e.innerHTML='<object id="'+this.uid+'" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">'+'<param name="source" value="'+a.xap_url+'"/>'+'<param name="background" value="Transparent"/>'+'<param name="windowless" value="true"/>'+'<param name="enablehtmlaccess" value="true"/>'+'<param name="initParams" value="uid='+this.uid+",target="+t.global_event_dispatcher+'"/>'+"</object>",l=setTimeout(function(){c&&!c.initialized&&c.trigger("Error",new i.RuntimeError(i.RuntimeError.NOT_INIT_ERR))},"Windows"!==t.OS?1e4:5e3)},destroy:function(e){return function(){e.call(c),clearTimeout(l),a=l=e=c=null}}(this.destroy)},u)}var s="silverlight",u={};return r.addConstructor(s,a),u}),i(et,[Q,u,V],function(e,t,n){return e.Blob=t.extend({},n)}),i(tt,[Q],function(e){var t={init:function(e){function t(e){for(var t="",n=0;n<e.length;n++)t+=(""!==t?"|":"")+e[n].title+" | *."+e[n].extensions.replace(/,/g,";*.");return t}this.getRuntime().shimExec.call(this,"FileInput","init",t(e.accept),e.name,e.multiple),this.trigger("ready")}};return e.FileInput=t}),i(nt,[Q,f,L],function(e,t,n){var i={init:function(){var e=this,i=e.getRuntime(),r;return r=i.getShimContainer(),n.addEvent(r,"dragover",function(e){e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="copy"},e.uid),n.addEvent(r,"dragenter",function(e){e.preventDefault();var n=t.get(i.uid).dragEnter(e);n&&e.stopPropagation()},e.uid),n.addEvent(r,"drop",function(e){e.preventDefault();var n=t.get(i.uid).dragDrop(e);n&&e.stopPropagation()},e.uid),i.shimExec.call(this,"FileDrop","init")}};return e.FileDrop=i}),i(it,[Q,u,Y],function(e,t,n){return e.FileReader=t.extend({},n)}),i(rt,[Q,u,$],function(e,t,n){return e.FileReaderSync=t.extend({},n)}),i(ot,[Q,u,J],function(e,t,n){return e.XMLHttpRequest=t.extend({},n)}),i(at,[Q,u,Z],function(e,t,n){return e.Transporter=t.extend({},n)}),i(st,[Q,u,K],function(e,t,n){return e.Image=t.extend({},n,{getInfo:function(){var e=this.getRuntime(),n=["tiff","exif","gps"],i={meta:{}},r=e.shimExec.call(this,"Image","getInfo");return r.meta&&t.each(n,function(e){var t=r.meta[e],n,o,a,s;if(t&&t.keys)for(i.meta[e]={},o=0,a=t.keys.length;a>o;o++)n=t.keys[o],s=t[n],s&&(/^(\d|[1-9]\d+)$/.test(s)?s=parseInt(s,10):/^\d*\.\d+$/.test(s)&&(s=parseFloat(s)),i.meta[e][n]=s)}),i.width=parseInt(r.width,10),i.height=parseInt(r.height,10),i.size=parseInt(r.size,10),i.type=r.type,i.name=r.name,i}})}),i(ut,[u,p,g,d],function(e,t,n,i){function r(t){var r=this,s=n.capTest,u=n.capTrue;n.call(this,t,o,{access_binary:s(window.FileReader||window.File&&File.getAsDataURL),access_image_binary:!1,display_media:s(a.Image&&(i.can("create_canvas")||i.can("use_data_uri_over32kb"))),do_cors:!1,drag_and_drop:!1,filter_by_extension:s(function(){return"Chrome"===i.browser&&i.version>=28||"IE"===i.browser&&i.version>=10}()),resize_image:function(){return a.Image&&r.can("access_binary")&&i.can("create_canvas")},report_upload_progress:!1,return_response_headers:!1,return_response_type:function(t){return"json"===t&&window.JSON?!0:!!~e.inArray(t,["text","document",""])},return_status_code:function(t){return!e.arrayDiff(t,[200,404])},select_file:function(){return i.can("use_fileinput")},select_multiple:!1,send_binary_string:!1,send_custom_headers:!1,send_multipart:!0,slice_blob:!1,stream_upload:function(){return r.can("select_file")},summon_file_dialog:s(function(){return"Firefox"===i.browser&&i.version>=4||"Opera"===i.browser&&i.version>=12||!!~e.inArray(i.browser,["Chrome","Safari"])}()),upload_filesize:u,use_http_method:function(t){return!e.arrayDiff(t,["GET","POST"])}}),e.extend(this,{init:function(){this.trigger("Init")},destroy:function(e){return function(){e.call(r),e=r=null}}(this.destroy)}),e.extend(this.getShim(),a)}var o="html4",a={};return n.addConstructor(o,r),a}),i(ct,[ut,u,f,L,l,d],function(e,t,n,i,r,o){function a(){function e(){var r=this,l=r.getRuntime(),d,f,p,h,m,g;g=t.guid("uid_"),d=l.getShimContainer(),a&&(p=n.get(a+"_form"),p&&t.extend(p.style,{top:"100%"})),h=document.createElement("form"),h.setAttribute("id",g+"_form"),h.setAttribute("method","post"),h.setAttribute("enctype","multipart/form-data"),h.setAttribute("encoding","multipart/form-data"),t.extend(h.style,{overflow:"hidden",position:"absolute",top:0,left:0,width:"100%",height:"100%"}),m=document.createElement("input"),m.setAttribute("id",g),m.setAttribute("type","file"),m.setAttribute("name",c.name||"Filedata"),m.setAttribute("accept",u.join(",")),t.extend(m.style,{fontSize:"999px",opacity:0}),h.appendChild(m),d.appendChild(h),t.extend(m.style,{position:"absolute",top:0,left:0,width:"100%",height:"100%"}),"IE"===o.browser&&o.version<10&&t.extend(m.style,{filter:"progid:DXImageTransform.Microsoft.Alpha(opacity=0)"}),m.onchange=function(){var t;this.value&&(t=this.files?this.files[0]:{name:this.value},s=[t],this.onchange=function(){},e.call(r),r.bind("change",function i(){var e=n.get(g),t=n.get(g+"_form"),o;r.unbind("change",i),r.files.length&&e&&t&&(o=r.files[0],e.setAttribute("id",o.uid),t.setAttribute("id",o.uid+"_form"),t.setAttribute("target",o.uid+"_iframe")),e=t=null},998),m=h=null,r.trigger("change"))},l.can("summon_file_dialog")&&(f=n.get(c.browse_button),i.removeEvent(f,"click",r.uid),i.addEvent(f,"click",function(e){m&&!m.disabled&&m.click(),e.preventDefault()},r.uid)),a=g,d=p=f=null}var a,s=[],u=[],c;t.extend(this,{init:function(t){var o=this,a=o.getRuntime(),s;c=t,u=t.accept.mimes||r.extList2mimes(t.accept,a.can("filter_by_extension")),s=a.getShimContainer(),function(){var e,r,u;e=n.get(t.browse_button),a.can("summon_file_dialog")&&("static"===n.getStyle(e,"position")&&(e.style.position="relative"),r=parseInt(n.getStyle(e,"z-index"),10)||1,e.style.zIndex=r,s.style.zIndex=r-1),u=a.can("summon_file_dialog")?e:s,i.addEvent(u,"mouseover",function(){o.trigger("mouseenter")},o.uid),i.addEvent(u,"mouseout",function(){o.trigger("mouseleave")},o.uid),i.addEvent(u,"mousedown",function(){o.trigger("mousedown")},o.uid),i.addEvent(n.get(t.container),"mouseup",function(){o.trigger("mouseup")},o.uid),e=null}(),e.call(this),s=null,o.trigger({type:"ready",async:!0})},getFiles:function(){return s},disable:function(e){var t;(t=n.get(a))&&(t.disabled=!!e)},destroy:function(){var e=this.getRuntime(),t=e.getShim(),r=e.getShimContainer();i.removeAllEvents(r,this.uid),i.removeAllEvents(c&&n.get(c.container),this.uid),i.removeAllEvents(c&&n.get(c.browse_button),this.uid),r&&(r.innerHTML=""),t.removeInstance(this.uid),a=s=u=c=r=t=null}})}return e.FileInput=a}),i(lt,[ut,F],function(e,t){return e.FileReader=t}),i(dt,[ut,u,f,b,p,L,y,S],function(e,t,n,i,r,o,a,s){function u(){function e(e){var t=this,i,r,a,s,u=!1;if(l){if(i=l.id.replace(/_iframe$/,""),r=n.get(i+"_form")){for(a=r.getElementsByTagName("input"),s=a.length;s--;)switch(a[s].getAttribute("type")){case"hidden":a[s].parentNode.removeChild(a[s]);break;case"file":u=!0}a=[],u||r.parentNode.removeChild(r),r=null}setTimeout(function(){o.removeEvent(l,"load",t.uid),l.parentNode&&l.parentNode.removeChild(l);var n=t.getRuntime().getShimContainer();n.children.length||n.parentNode.removeChild(n),n=l=null,e()},1)}}var u,c,l;t.extend(this,{send:function(d,f){function p(){var n=m.getShimContainer()||document.body,r=document.createElement("div");r.innerHTML='<iframe id="'+g+'_iframe" name="'+g+'_iframe" src="javascript:&quot;&quot;" style="display:none"></iframe>',l=r.firstChild,n.appendChild(l),o.addEvent(l,"load",function(){var n;try{n=l.contentWindow.document||l.contentDocument||window.frames[l.id].document,/^4(0[0-9]|1[0-7]|2[2346])\s/.test(n.title)?u=n.title.replace(/^(\d+).*$/,"$1"):(u=200,c=t.trim(n.body.innerHTML),h.trigger({type:"progress",loaded:c.length,total:c.length}),w&&h.trigger({type:"uploadprogress",loaded:w.size||1025,total:w.size||1025}))}catch(r){if(!i.hasSameOrigin(d.url))return e.call(h,function(){h.trigger("error")}),void 0;u=404}e.call(h,function(){h.trigger("load")})},h.uid)}var h=this,m=h.getRuntime(),g,v,y,w;if(u=c=null,f instanceof s&&f.hasBlob()){if(w=f.getBlob(),g=w.uid,y=n.get(g),v=n.get(g+"_form"),!v)throw new r.DOMException(r.DOMException.NOT_FOUND_ERR)}else g=t.guid("uid_"),v=document.createElement("form"),v.setAttribute("id",g+"_form"),v.setAttribute("method",d.method),v.setAttribute("enctype","multipart/form-data"),v.setAttribute("encoding","multipart/form-data"),v.setAttribute("target",g+"_iframe"),m.getShimContainer().appendChild(v);f instanceof s&&f.each(function(e,n){if(e instanceof a)y&&y.setAttribute("name",n);else{var i=document.createElement("input");t.extend(i,{type:"hidden",name:n,value:e}),y?v.insertBefore(i,y):v.appendChild(i)}}),v.setAttribute("action",d.url),p(),v.submit(),h.trigger("loadstart")},getStatus:function(){return u},getResponse:function(e){if("json"===e&&"string"===t.typeOf(c)&&window.JSON)try{return JSON.parse(c.replace(/^\s*<pre[^>]*>/,"").replace(/<\/pre>\s*$/,""))}catch(n){return null}return c},abort:function(){var t=this;l&&l.contentWindow&&(l.contentWindow.stop?l.contentWindow.stop():l.contentWindow.document.execCommand?l.contentWindow.document.execCommand("Stop"):l.src="about:blank"),e.call(this,function(){t.dispatchEvent("abort")})}})}return e.XMLHttpRequest=u}),i(ft,[ut,X],function(e,t){return e.Image=t}),a([u,c,l,d,f,p,h,m,g,v,y,w,E,_,x,R,b,T,S,A,O,I,L])}(this);;(function(){"use strict";var e={},t=moxie.core.utils.Basic.inArray;return function n(r){var i,s;for(i in r)s=typeof r[i],s==="object"&&!~t(i,["Exceptions","Env","Mime"])?n(r[i]):s==="function"&&(e[i]=r[i])}(window.moxie),e.Env=window.moxie.core.utils.Env,e.Mime=window.moxie.core.utils.Mime,e.Exceptions=window.moxie.core.Exceptions,window.mOxie=e,window.o||(window.o=e),e})();
/**
* Plupload - multi-runtime File Uploader
- * v2.0.0beta
+ * v2.1.1
*
* Copyright 2013, Moxiecode Systems AB
* Released under GPL License.
@@ -23,6 +23,6 @@ e.extend(this,y,{uid:e.guid("uid_"),upload:new f,open:function(o,a,s,u,c){var l;
* License: http://www.plupload.com/license
* Contributing: http://www.plupload.com/contributing
*
- * Date: 2012-11-30
+ * Date: 2014-01-16
*/
-;(function(e,t,n){function s(e){function r(e,t,r){var i={chunks:"slice_blob",resize:"send_binary_string",jpgresize:"send_binary_string",pngresize:"send_binary_string",progress:"report_upload_progress",multi_selection:"select_multiple",max_file_size:"access_binary",dragdrop:"drag_and_drop",drop_element:"drag_and_drop",headers:"send_custom_headers",canSendBinary:"send_binary",triggerDialog:"summon_file_dialog"};i[e]?n[i[e]]=t:r||(n[e]=t)}var t=e.required_features,n={};return typeof t=="string"?o.each(t.split(/\s*,\s*/),function(e){r(e,!0)}):typeof t=="object"?o.each(t,function(e,t){r(t,e)}):t===!0&&(e.multipart||(n.send_binary_string=!0),e.chunk_size>0&&(n.slice_blob=!0),o.each(e,function(e,t){r(t,!!e,!0)})),n}var r=e.setTimeout,i={},o={VERSION:"2.0.0beta",STOPPED:1,STARTED:2,QUEUED:1,UPLOADING:2,FAILED:4,DONE:5,GENERIC_ERROR:-100,HTTP_ERROR:-200,IO_ERROR:-300,SECURITY_ERROR:-400,INIT_ERROR:-500,FILE_SIZE_ERROR:-600,FILE_EXTENSION_ERROR:-601,FILE_DUPLICATE_ERROR:-602,IMAGE_FORMAT_ERROR:-700,IMAGE_MEMORY_ERROR:-701,IMAGE_DIMENSIONS_ERROR:-702,mimeTypes:t.mimes,ua:t.ua,typeOf:t.typeOf,extend:t.extend,guid:t.guid,each:t.each,getPos:t.getPos,getSize:t.getSize,xmlEncode:function(e){var t={"<":"lt",">":"gt","&":"amp",'"':"quot","'":"#39"},n=/[<>&\"\']/g;return e?(""+e).replace(n,function(e){return t[e]?"&"+t[e]+";":e}):e},toArray:t.toArray,inArray:t.inArray,addI18n:t.addI18n,translate:t.translate,isEmptyObj:t.isEmptyObj,hasClass:t.hasClass,addClass:t.addClass,removeClass:t.removeClass,getStyle:t.getStyle,addEvent:t.addEvent,removeEvent:t.removeEvent,removeAllEvents:t.removeAllEvents,cleanName:function(e){var t,n;n=[/[\300-\306]/g,"A",/[\340-\346]/g,"a",/\307/g,"C",/\347/g,"c",/[\310-\313]/g,"E",/[\350-\353]/g,"e",/[\314-\317]/g,"I",/[\354-\357]/g,"i",/\321/g,"N",/\361/g,"n",/[\322-\330]/g,"O",/[\362-\370]/g,"o",/[\331-\334]/g,"U",/[\371-\374]/g,"u"];for(t=0;t<n.length;t+=2)e=e.replace(n[t],n[t+1]);return e=e.replace(/\s+/g,"_"),e=e.replace(/[^a-z0-9_\-\.]+/gi,""),e},buildUrl:function(e,t){var n="";return o.each(t,function(e,t){n+=(n?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(e)}),n&&(e+=(e.indexOf("?")>0?"&":"?")+n),e},formatSize:function(e){return e===n||/\D/.test(e)?o.translate("N/A"):e>1099511627776?Math.round(e/1099511627776,1)+" "+o.translate("tb"):e>1073741824?Math.round(e/1073741824,1)+" "+o.translate("gb"):e>1048576?Math.round(e/1048576,1)+" "+o.translate("mb"):e>1024?Math.round(e/1024,1)+" "+o.translate("kb"):e+" "+o.translate("b")},parseSize:t.parseSizeStr,predictRuntime:function(e,t){var n,r;return t&&(e.runtimes=t),n=new o.Uploader(e),r=n.runtime,n.destroy(),r},addFileFilter:function(e,t){i[e]=t}};o.addFileFilter("mime_types",function(){function n(e){var t=[];return o.each(e,function(e){o.each(e.extensions.split(/,/),function(e){/^\s*\*\s*$/.test(e)?t.push("\\.*"):t.push("\\."+e.replace(new RegExp("["+"/^$.*+?|()[]{}\\".replace(/./g,"\\$&")+"]","g"),"\\$&"))})}),new RegExp("("+t.join("|")+")$","i")}var e,t;return function(r,i,s){if(!t||r!=e)t=n(r),e=[].slice.call(r);t.test(i.name)?s(!0):(this.trigger("Error",{code:o.FILE_EXTENSION_ERROR,message:o.translate("File extension error."),file:i}),s(!1))}}()),o.addFileFilter("max_file_size",function(e,t,n){var r;t.size!==r&&e&&t.size>e?(this.trigger("Error",{code:o.FILE_SIZE_ERROR,message:o.translate("File size error."),file:t}),n(!1)):n(!0)}),o.addFileFilter("prevent_duplicates",function(e,t,n){if(e){var r=this.files.length;while(r--)if(t.name===this.files[r].name&&t.size===this.files[r].size){this.trigger("Error",{code:o.FILE_DUPLICATE_ERROR,message:o.translate("Duplicate file error."),file:t}),n(!1);return}}n(!0)}),o.Uploader=function(e){function m(){var e,t=0,n;if(this.state==o.STARTED){for(n=0;n<u.length;n++)!e&&u[n].status==o.QUEUED?(e=u[n],this.trigger("BeforeUpload",e)&&(e.status=o.UPLOADING,this.trigger("UploadFile",e))):t++;t==u.length&&(this.state!==o.STOPPED&&(this.state=o.STOPPED,this.trigger("StateChanged")),this.trigger("UploadComplete",u))}}function g(e){e.percent=e.size>0?Math.ceil(e.loaded/e.size*100):100,y()}function y(){var e,t;c.reset();for(e=0;e<u.length;e++)t=u[e],t.size!==n?(c.size+=t.origSize,c.loaded+=t.loaded*t.origSize/t.size):c.size=n,t.status==o.DONE?c.uploaded++:t.status==o.FAILED?c.failed++:c.queued++;c.size===n?c.percent=u.length>0?Math.ceil(c.uploaded/u.length*100):0:(c.bytesPerSec=Math.ceil(c.loaded/((+(new Date)-l||1)/1e3)),c.percent=c.size>0?Math.ceil(c.loaded/c.size*100):0)}function b(){var n=this,r=0,i={accept:e.filters.mime_types,runtime_order:e.runtimes,required_caps:f,swf_url:e.flash_swf_url,xap_url:e.silverlight_xap_url};o.each(e.runtimes.split(/\s*,\s*/),function(t){e[t]&&(i[t]=e[t])}),t.inSeries([function(s){e.browse_button?(p=new t.FileInput(o.extend({},i,{name:e.file_data_name,multiple:e.multi_selection,container:e.container,browse_button:e.browse_button})),p.onready=function(){var e=t.Runtime.getInfo(this.ruid);t.extend(n.features,{chunks:e.can("slice_blob"),multipart:e.can("send_multipart"),multi_selection:e.can("select_multiple")}),r++,s()},p.onchange=function(){n.addFile(this.files)},p.bind("mouseenter mouseleave mousedown mouseup",function(n){if(!h){var r=t.get(e.browse_button);r&&(e.browse_button_hover&&("mouseenter"===n.type?t.addClass(r,e.browse_button_hover):"mouseleave"===n.type&&t.removeClass(r,e.browse_button_hover)),e.browse_button_active&&("mousedown"===n.type?t.addClass(r,e.browse_button_active):"mouseup"===n.type&&t.removeClass(r,e.browse_button_active)),r=null)}}),p.bind("error runtimeerror",function(){p=null,s()}),p.init()):s()},function(s){e.drop_element?(d=new t.FileDrop(o.extend({},i,{drop_zone:e.drop_element})),d.onready=function(){var e=t.Runtime.getInfo(this.ruid);n.features.dragdrop=e.can("drag_and_drop"),r++,s()},d.ondrop=function(){n.addFile(this.files)},d.bind("error runtimeerror",function(){d=null,s()}),d.init()):s()}],function(){typeof e.init=="function"?e.init(n):o.each(e.init,function(e,t){n.bind(t,e)}),r?n.trigger("PostInit"):n.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")})})}function w(e,n){if(e.ruid){var r=t.Runtime.getInfo(e.ruid);if(r)return r.can(n)}return!1}function E(e,n,r){var i=new t.Image;try{i.onload=function(){i.downsize(n.width,n.height,n.crop,n.preserve_headers)},i.onresize=function(){r(this.getAsBlob(e.type,n.quality)),this.destroy()},i.onerror=function(){r(e)},i.load(e)}catch(s){r(e)}}var u=[],a={},f={},l,c,h=!1,p,d,v;c=new o.QueueProgress,e=o.extend({runtimes:t.Runtime.order,max_retries:0,multipart:!0,multi_selection:!0,file_data_name:"file",flash_swf_url:"js/Moxie.swf",silverlight_xap_url:"js/Moxie.xap",send_chunk_number:!0},e),e.resize&&(e.resize=o.extend({preserve_headers:!0,crop:!1},e.resize)),o.typeOf(e.filters)==="array"&&(e.filters={mime_types:e.filters}),e.filters=o.extend({mime_types:[],prevent_duplicates:!!e.prevent_duplicates,max_file_size:e.max_file_size},e.filters),e.filters.max_file_size=o.parseSize(e.filters.max_file_size)||0,e.chunk_size=o.parseSize(e.chunk_size)||0,e.required_features=f=s(o.extend({},e)),o.extend(this,{id:o.guid(),state:o.STOPPED,features:{},runtime:t.Runtime.thatCan(f,e.runtimes),files:u,settings:e,total:c,init:function(){var n=this;e.browse_button=t.get(e.browse_button),e.drop_element=t.get(e.drop_element),typeof e.preinit=="function"?e.preinit(n):o.each(e.preinit,function(e,t){n.bind(t,e)});if(!e.browse_button||!e.url){this.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")});return}n.bind("FilesAdded",function(e,t){[].push.apply(u,t),r(function(){n.trigger("QueueChanged"),n.refresh()},1)}),n.bind("CancelUpload",function(){v&&v.abort()}),e.unique_names&&n.bind("BeforeUpload",function(e,t){var n=t.name.match(/\.([^.]+)$/),r="part";n&&(r=n[1]),t.target_name=t.id+"."+r}),n.bind("UploadFile",function(n,i){function p(){l-->0?r(d,1):(i.loaded=h,n.trigger("Error",{code:o.HTTP_ERROR,message:o.translate("HTTP Error."),file:i,response:v.responseText,status:v.status,responseHeaders:v.getAllResponseHeaders()}))}function d(){var l,m,g,y;if(i.status==o.DONE||i.status==o.FAILED||n.state==o.STOPPED)return;g={name:i.target_name||i.name},a&&u.chunks&&c.size>a?(y=Math.min(a,c.size-h),l=c.slice(h,h+y)):(y=c.size,l=c),a&&u.chunks&&(e.send_chunk_number?(g.chunk=Math.ceil(h/a),g.chunks=Math.ceil(c.size/a)):(g.offset=h,g.total=c.size)),v=new t.XMLHttpRequest,v.upload&&(v.upload.onprogress=function(e){i.loaded=Math.min(i.size,h+e.loaded),n.trigger("UploadProgress",i)}),v.onload=function(){if(v.status>=400){p();return}y<c.size?(l.destroy(),h+=y,i.loaded=Math.min(h,c.size),n.trigger("ChunkUploaded",i,{offset:i.loaded,total:c.size,response:v.responseText,status:v.status,responseHeaders:v.getAllResponseHeaders()}),t.Env.browser==="Android Browser"&&n.trigger("UploadProgress",i)):i.loaded=i.size,l=m=null,!h||h>=c.size?(i.size!=i.origSize&&(c.destroy(),c=null),n.trigger("UploadProgress",i),i.status=o.DONE,n.trigger("FileUploaded",i,{response:v.responseText,status:v.status,responseHeaders:v.getAllResponseHeaders()})):r(d,1)},v.onerror=function(){p()},v.onloadend=function(){this.destroy(),v=null},n.settings.multipart&&u.multipart?(g.name=i.target_name||i.name,v.open("post",s,!0),o.each(n.settings.headers,function(e,t){v.setRequestHeader(t,e)}),m=new t.FormData,o.each(o.extend(g,n.settings.multipart_params),function(e,t){m.append(t,e)}),m.append(n.settings.file_data_name,l),v.send(m,{runtime_order:n.settings.runtimes,required_caps:f,swf_url:n.settings.flash_swf_url,xap_url:n.settings.silverlight_xap_url})):(s=o.buildUrl(n.settings.url,o.extend(g,n.settings.multipart_params)),v.open("post",s,!0),v.setRequestHeader("Content-Type","application/octet-stream"),o.each(n.settings.headers,function(e,t){v.setRequestHeader(t,e)}),v.send(l,{runtime_order:n.settings.runtimes,required_caps:f,swf_url:n.settings.flash_swf_url,xap_url:n.settings.silverlight_xap_url}))}var s=n.settings.url,u=n.features,a=e.chunk_size,l=e.max_retries,c,h=0;i.loaded&&(h=i.loaded=a*Math.floor(i.loaded/a)),c=i.getSource(),!t.isEmptyObj(n.settings.resize)&&w(c,"send_binary_string")&&!!~t.inArray(c.type,["image/jpeg","image/png"])?E.call(this,c,n.settings.resize,function(e){c=e,i.size=e.size,d()}):d()}),n.bind("UploadProgress",function(e,t){g(t)}),n.bind("StateChanged",function(e){if(e.state==o.STARTED)l=+(new Date);else if(e.state==o.STOPPED)for(var t=e.files.length-1;t>=0;t--)e.files[t].status==o.UPLOADING&&(e.files[t].status=o.QUEUED,y())}),n.bind("QueueChanged",y),n.bind("Error",function(e,t){t.file&&(t.file.status=o.FAILED,g(t.file),e.state==o.STARTED&&r(function(){m.call(n)},1))}),n.bind("FileUploaded",function(){y(),r(function(){m.call(n)},1)}),n.trigger("Init",{runtime:this.runtime}),b.call(this)},refresh:function(){p&&p.trigger("Refresh"),this.trigger("Refresh")},start:function(){this.state!=o.STARTED&&(this.state=o.STARTED,this.trigger("StateChanged"),m.call(this))},stop:function(){this.state!=o.STOPPED&&(this.state=o.STOPPED,this.trigger("StateChanged"),this.trigger("CancelUpload"))},disableBrowse:function(){h=arguments[0]!==n?arguments[0]:!0,p&&p.disable(h),this.trigger("DisableBrowse",h)},getFile:function(e){var t;for(t=u.length-1;t>=0;t--)if(u[t].id===e)return u[t]},addFile:function(e,n){function f(){var e=d||p;return e?e.getRuntime().uid:!1}function l(e,n){var s=[];t.each(r.settings.filters,function(t,n){i[n]&&s.push(function(s){i[n].call(r,t,e,function(e){s(!e)})})}),t.inSeries(s,n)}function c(e){var r=t.typeOf(e);if(e instanceof t.File){if(!e.ruid&&!e.isDetached()){if(!a)return!1;e.ruid=a,e.connectRuntime(a)}c(new o.File(e))}else e instanceof t.Blob?(c(e.getSource()),e.destroy()):e instanceof o.File?(n&&(e.name=n),s.push(function(t){l(e,function(n){n||u.push(e),t()})})):t.inArray(r,["file","blob"])!==-1?c(new t.File(null,e)):r==="node"&&t.typeOf(e.files)==="filelist"?t.each(e.files,c):r==="array"&&(n=null,t.each(e,c))}var r=this,s=[],u=[],a;a=f(),c(e),s.length&&t.inSeries(s,function(){u.length&&r.trigger("FilesAdded",u)})},removeFile:function(e){var t=typeof e=="string"?e:e.id;for(var n=u.length-1;n>=0;n--)if(u[n].id===t)return this.splice(n,1)[0]},splice:function(e,t){var r=u.splice(e===n?0:e,t===n?u.length:t);return this.trigger("FilesRemoved",r),this.trigger("QueueChanged"),o.each(r,function(e){e.destroy()}),r},trigger:function(e){var t=a[e.toLowerCase()],n,r;if(t){r=Array.prototype.slice.call(arguments),r[0]=this;for(n=0;n<t.length;n++)if(t[n].func.apply(t[n].scope,r)===!1)return!1}return!0},hasEventListener:function(e){return!!a[e.toLowerCase()]},bind:function(e,t,n){var r;e=e.toLowerCase(),r=a[e]||[],r.push({func:t,scope:n||this}),a[e]=r},unbind:function(e){e=e.toLowerCase();var t=a[e],r,i=arguments[1];if(t){if(i!==n){for(r=t.length-1;r>=0;r--)if(t[r].func===i){t.splice(r,1);break}}else t=[];t.length||delete a[e]}},unbindAll:function(){var e=this;o.each(a,function(t,n){e.unbind(n)})},destroy:function(){this.stop(),o.each(u,function(e){e.destroy()}),u=[],p&&(p.destroy(),p=null),d&&(d.destroy(),d=null),f={},l=c=h=v=null,this.trigger("Destroy"),this.unbindAll(),a={}}})},o.File=function(){function n(n){o.extend(this,{id:o.guid(),name:n.name||n.fileName,type:n.type||"",size:n.size||n.fileSize,origSize:n.size||n.fileSize,loaded:0,percent:0,status:o.QUEUED,lastModifiedDate:n.lastModifiedDate||(new Date).toLocaleString(),getNative:function(){var e=this.getSource().getSource();return t.inArray(t.typeOf(e),["blob","file"])!==-1?e:null},getSource:function(){return e[this.id]?e[this.id]:null},destroy:function(){var t=this.getSource();t&&(t.destroy(),delete e[this.id])}}),e[this.id]=n}var e={};return n}(),o.QueueProgress=function(){var e=this;e.size=0,e.loaded=0,e.uploaded=0,e.failed=0,e.queued=0,e.percent=0,e.bytesPerSec=0,e.reset=function(){e.size=e.loaded=e.uploaded=e.failed=e.queued=e.percent=e.bytesPerSec=0}},e.plupload=o})(window,mOxie); \ No newline at end of file
+;(function(e,t,n){function s(e){function r(e,t,r){var i={chunks:"slice_blob",jpgresize:"send_binary_string",pngresize:"send_binary_string",progress:"report_upload_progress",multi_selection:"select_multiple",dragdrop:"drag_and_drop",drop_element:"drag_and_drop",headers:"send_custom_headers",canSendBinary:"send_binary",triggerDialog:"summon_file_dialog"};i[e]?n[i[e]]=t:r||(n[e]=t)}var t=e.required_features,n={};return typeof t=="string"?o.each(t.split(/\s*,\s*/),function(e){r(e,!0)}):typeof t=="object"?o.each(t,function(e,t){r(t,e)}):t===!0&&(e.multipart||(n.send_binary_string=!0),e.chunk_size>0&&(n.slice_blob=!0),e.resize.enabled&&(n.send_binary_string=!0),o.each(e,function(e,t){r(t,!!e,!0)})),n}var r=e.setTimeout,i={},o={VERSION:"2.1.1",STOPPED:1,STARTED:2,QUEUED:1,UPLOADING:2,FAILED:4,DONE:5,GENERIC_ERROR:-100,HTTP_ERROR:-200,IO_ERROR:-300,SECURITY_ERROR:-400,INIT_ERROR:-500,FILE_SIZE_ERROR:-600,FILE_EXTENSION_ERROR:-601,FILE_DUPLICATE_ERROR:-602,IMAGE_FORMAT_ERROR:-700,IMAGE_MEMORY_ERROR:-701,IMAGE_DIMENSIONS_ERROR:-702,mimeTypes:t.mimes,ua:t.ua,typeOf:t.typeOf,extend:t.extend,guid:t.guid,get:function(n){var r=[],i;t.typeOf(n)!=="array"&&(n=[n]);var s=n.length;while(s--)i=t.get(n[s]),i&&r.push(i);return r.length?r:null},each:t.each,getPos:t.getPos,getSize:t.getSize,xmlEncode:function(e){var t={"<":"lt",">":"gt","&":"amp",'"':"quot","'":"#39"},n=/[<>&\"\']/g;return e?(""+e).replace(n,function(e){return t[e]?"&"+t[e]+";":e}):e},toArray:t.toArray,inArray:t.inArray,addI18n:t.addI18n,translate:t.translate,isEmptyObj:t.isEmptyObj,hasClass:t.hasClass,addClass:t.addClass,removeClass:t.removeClass,getStyle:t.getStyle,addEvent:t.addEvent,removeEvent:t.removeEvent,removeAllEvents:t.removeAllEvents,cleanName:function(e){var t,n;n=[/[\300-\306]/g,"A",/[\340-\346]/g,"a",/\307/g,"C",/\347/g,"c",/[\310-\313]/g,"E",/[\350-\353]/g,"e",/[\314-\317]/g,"I",/[\354-\357]/g,"i",/\321/g,"N",/\361/g,"n",/[\322-\330]/g,"O",/[\362-\370]/g,"o",/[\331-\334]/g,"U",/[\371-\374]/g,"u"];for(t=0;t<n.length;t+=2)e=e.replace(n[t],n[t+1]);return e=e.replace(/\s+/g,"_"),e=e.replace(/[^a-z0-9_\-\.]+/gi,""),e},buildUrl:function(e,t){var n="";return o.each(t,function(e,t){n+=(n?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(e)}),n&&(e+=(e.indexOf("?")>0?"&":"?")+n),e},formatSize:function(e){function t(e,t){return Math.round(e*Math.pow(10,t))/Math.pow(10,t)}if(e===n||/\D/.test(e))return o.translate("N/A");var r=Math.pow(1024,4);return e>r?t(e/r,1)+" "+o.translate("tb"):e>(r/=1024)?t(e/r,1)+" "+o.translate("gb"):e>(r/=1024)?t(e/r,1)+" "+o.translate("mb"):e>1024?Math.round(e/1024)+" "+o.translate("kb"):e+" "+o.translate("b")},parseSize:t.parseSizeStr,predictRuntime:function(e,n){var r,i;return r=new o.Uploader(e),i=t.Runtime.thatCan(r.getOption().required_features,n||e.runtimes),r.destroy(),i},addFileFilter:function(e,t){i[e]=t}};o.addFileFilter("mime_types",function(e,t,n){e.length&&!e.regexp.test(t.name)?(this.trigger("Error",{code:o.FILE_EXTENSION_ERROR,message:o.translate("File extension error."),file:t}),n(!1)):n(!0)}),o.addFileFilter("max_file_size",function(e,t,n){var r;e=o.parseSize(e),t.size!==r&&e&&t.size>e?(this.trigger("Error",{code:o.FILE_SIZE_ERROR,message:o.translate("File size error."),file:t}),n(!1)):n(!0)}),o.addFileFilter("prevent_duplicates",function(e,t,n){if(e){var r=this.files.length;while(r--)if(t.name===this.files[r].name&&t.size===this.files[r].size){this.trigger("Error",{code:o.FILE_DUPLICATE_ERROR,message:o.translate("Duplicate file error."),file:t}),n(!1);return}}n(!0)}),o.Uploader=function(e){function g(){var e,t=0,n;if(this.state==o.STARTED){for(n=0;n<f.length;n++)!e&&f[n].status==o.QUEUED?(e=f[n],this.trigger("BeforeUpload",e)&&(e.status=o.UPLOADING,this.trigger("UploadFile",e))):t++;t==f.length&&(this.state!==o.STOPPED&&(this.state=o.STOPPED,this.trigger("StateChanged")),this.trigger("UploadComplete",f))}}function y(e){e.percent=e.size>0?Math.ceil(e.loaded/e.size*100):100,b()}function b(){var e,t;d.reset();for(e=0;e<f.length;e++)t=f[e],t.size!==n?(d.size+=t.origSize,d.loaded+=t.loaded*t.origSize/t.size):d.size=n,t.status==o.DONE?d.uploaded++:t.status==o.FAILED?d.failed++:d.queued++;d.size===n?d.percent=f.length>0?Math.ceil(d.uploaded/f.length*100):0:(d.bytesPerSec=Math.ceil(d.loaded/((+(new Date)-p||1)/1e3)),d.percent=d.size>0?Math.ceil(d.loaded/d.size*100):0)}function w(){var e=c[0]||h[0];return e?e.getRuntime().uid:!1}function E(e,n){if(e.ruid){var r=t.Runtime.getInfo(e.ruid);if(r)return r.can(n)}return!1}function S(){this.bind("FilesAdded",C),this.bind("CancelUpload",M),this.bind("BeforeUpload",k),this.bind("UploadFile",L),this.bind("UploadProgress",A),this.bind("StateChanged",O),this.bind("QueueChanged",b),this.bind("Error",D),this.bind("FileUploaded",_),this.bind("Destroy",P)}function x(e,n){var r=this,i=0,s=[],u={accept:e.filters.mime_types,runtime_order:e.runtimes,required_caps:e.required_features,preferred_caps:l,swf_url:e.flash_swf_url,xap_url:e.silverlight_xap_url};o.each(e.runtimes.split(/\s*,\s*/),function(t){e[t]&&(u[t]=e[t])}),e.browse_button&&o.each(e.browse_button,function(n){s.push(function(s){var a=new t.FileInput(o.extend({},u,{name:e.file_data_name,multiple:e.multi_selection,container:e.container,browse_button:n}));a.onready=function(){var e=t.Runtime.getInfo(this.ruid);t.extend(r.features,{chunks:e.can("slice_blob"),multipart:e.can("send_multipart"),multi_selection:e.can("select_multiple")}),i++,c.push(this),s()},a.onchange=function(){r.addFile(this.files)},a.bind("mouseenter mouseleave mousedown mouseup",function(r){v||(e.browse_button_hover&&("mouseenter"===r.type?t.addClass(n,e.browse_button_hover):"mouseleave"===r.type&&t.removeClass(n,e.browse_button_hover)),e.browse_button_active&&("mousedown"===r.type?t.addClass(n,e.browse_button_active):"mouseup"===r.type&&t.removeClass(n,e.browse_button_active)))}),a.bind("error runtimeerror",function(){a=null,s()}),a.init()})}),e.drop_element&&o.each(e.drop_element,function(e){s.push(function(n){var s=new t.FileDrop(o.extend({},u,{drop_zone:e}));s.onready=function(){var e=t.Runtime.getInfo(this.ruid);r.features.dragdrop=e.can("drag_and_drop"),i++,h.push(this),n()},s.ondrop=function(){r.addFile(this.files)},s.bind("error runtimeerror",function(){s=null,n()}),s.init()})}),t.inSeries(s,function(){typeof n=="function"&&n(i)})}function T(e,n,r){var i=new t.Image;try{i.onload=function(){i.downsize(n.width,n.height,n.crop,n.preserve_headers)},i.onresize=function(){r(this.getAsBlob(e.type,n.quality)),this.destroy()},i.onerror=function(){r(e)},i.load(e)}catch(s){r(e)}}function N(e,n,r){function f(e,t,n){var r=a[e];switch(e){case"max_file_size":e==="max_file_size"&&(a.max_file_size=a.filters.max_file_size=t);break;case"chunk_size":if(t=o.parseSize(t))a[e]=t;break;case"filters":o.typeOf(t)==="array"&&(t={mime_types:t}),n?o.extend(a.filters,t):a.filters=t,t.mime_types&&(a.filters.mime_types.regexp=function(e){var t=[];return o.each(e,function(e){o.each(e.extensions.split(/,/),function(e){/^\s*\*\s*$/.test(e)?t.push("\\.*"):t.push("\\."+e.replace(new RegExp("["+"/^$.*+?|()[]{}\\".replace(/./g,"\\$&")+"]","g"),"\\$&"))})}),new RegExp("("+t.join("|")+")$","i")}(a.filters.mime_types));break;case"resize":n?o.extend(a.resize,t,{enabled:!0}):a.resize=t;break;case"prevent_duplicates":a.prevent_duplicates=a.filters.prevent_duplicates=!!t;break;case"browse_button":case"drop_element":t=o.get(t);case"container":case"runtimes":case"multi_selection":case"flash_swf_url":case"silverlight_xap_url":a[e]=t,n||(u=!0);break;default:a[e]=t}n||i.trigger("OptionChanged",e,t,r)}var i=this,u=!1;typeof e=="object"?o.each(e,function(e,t){f(t,e,r)}):f(e,n,r),r?(a.required_features=s(o.extend({},a)),l=s(o.extend({},a,{required_features:!0}))):u&&(i.trigger("Destroy"),x.call(i,a,function(e){e?(i.runtime=t.Runtime.getInfo(w()).type,i.trigger("Init",{runtime:i.runtime}),i.trigger("PostInit")):i.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")})}))}function C(e,t){[].push.apply(f,t),e.trigger("QueueChanged"),e.refresh()}function k(e,t){if(a.unique_names){var n=t.name.match(/\.([^.]+)$/),r="part";n&&(r=n[1]),t.target_name=t.id+"."+r}}function L(e,n){function h(){u-->0?r(p,1e3):(n.loaded=f,e.trigger("Error",{code:o.HTTP_ERROR,message:o.translate("HTTP Error."),file:n,response:m.responseText,status:m.status,responseHeaders:m.getAllResponseHeaders()}))}function p(){var d,v,g,y;if(n.status==o.DONE||n.status==o.FAILED||e.state==o.STOPPED)return;g={name:n.target_name||n.name},s&&a.chunks&&c.size>s?(y=Math.min(s,c.size-f),d=c.slice(f,f+y)):(y=c.size,d=c),s&&a.chunks&&(e.settings.send_chunk_number?(g.chunk=Math.ceil(f/s),g.chunks=Math.ceil(c.size/s)):(g.offset=f,g.total=c.size)),m=new t.XMLHttpRequest,m.upload&&(m.upload.onprogress=function(t){n.loaded=Math.min(n.size,f+t.loaded),e.trigger("UploadProgress",n)}),m.onload=function(){if(m.status>=400){h();return}u=e.settings.max_retries,y<c.size?(d.destroy(),f+=y,n.loaded=Math.min(f,c.size),e.trigger("ChunkUploaded",n,{offset:n.loaded,total:c.size,response:m.responseText,status:m.status,responseHeaders:m.getAllResponseHeaders()}),t.Env.browser==="Android Browser"&&e.trigger("UploadProgress",n)):n.loaded=n.size,d=v=null,!f||f>=c.size?(n.size!=n.origSize&&(c.destroy(),c=null),e.trigger("UploadProgress",n),n.status=o.DONE,e.trigger("FileUploaded",n,{response:m.responseText,status:m.status,responseHeaders:m.getAllResponseHeaders()})):r(p,1)},m.onerror=function(){h()},m.onloadend=function(){this.destroy(),m=null},e.settings.multipart&&a.multipart?(g.name=n.target_name||n.name,m.open("post",i,!0),o.each(e.settings.headers,function(e,t){m.setRequestHeader(t,e)}),v=new t.FormData,o.each(o.extend(g,e.settings.multipart_params),function(e,t){v.append(t,e)}),v.append(e.settings.file_data_name,d),m.send(v,{runtime_order:e.settings.runtimes,required_caps:e.settings.required_features,preferred_caps:l,swf_url:e.settings.flash_swf_url,xap_url:e.settings.silverlight_xap_url})):(i=o.buildUrl(e.settings.url,o.extend(g,e.settings.multipart_params)),m.open("post",i,!0),m.setRequestHeader("Content-Type","application/octet-stream"),o.each(e.settings.headers,function(e,t){m.setRequestHeader(t,e)}),m.send(d,{runtime_order:e.settings.runtimes,required_caps:e.settings.required_features,preferred_caps:l,swf_url:e.settings.flash_swf_url,xap_url:e.settings.silverlight_xap_url}))}var i=e.settings.url,s=e.settings.chunk_size,u=e.settings.max_retries,a=e.features,f=0,c;n.loaded&&(f=n.loaded=s*Math.floor(n.loaded/s)),c=n.getSource(),e.settings.resize.enabled&&E(c,"send_binary_string")&&!!~t.inArray(c.type,["image/jpeg","image/png"])?T.call(this,c,e.settings.resize,function(e){c=e,n.size=e.size,p()}):p()}function A(e,t){y(t)}function O(e){if(e.state==o.STARTED)p=+(new Date);else if(e.state==o.STOPPED)for(var t=e.files.length-1;t>=0;t--)e.files[t].status==o.UPLOADING&&(e.files[t].status=o.QUEUED,b())}function M(){m&&m.abort()}function _(e){b(),r(function(){g.call(e)},1)}function D(e,t){t.file&&(t.file.status=o.FAILED,y(t.file),e.state==o.STARTED&&(e.trigger("CancelUpload"),r(function(){g.call(e)},1)))}function P(e){e.stop(),o.each(f,function(e){e.destroy()}),f=[],c.length&&(o.each(c,function(e){e.destroy()}),c=[]),h.length&&(o.each(h,function(e){e.destroy()}),h=[]),l={},v=!1,p=m=null,d.reset()}var u=o.guid(),a,f=[],l={},c=[],h=[],p,d,v=!1,m;a={runtimes:t.Runtime.order,max_retries:0,chunk_size:0,multipart:!0,multi_selection:!0,file_data_name:"file",flash_swf_url:"js/Moxie.swf",silverlight_xap_url:"js/Moxie.xap",filters:{mime_types:[],prevent_duplicates:!1,max_file_size:0},resize:{enabled:!1,preserve_headers:!0,crop:!1},send_chunk_number:!0},N.call(this,e,null,!0),d=new o.QueueProgress,o.extend(this,{id:u,uid:u,state:o.STOPPED,features:{},runtime:null,files:f,settings:a,total:d,init:function(){var e=this;typeof a.preinit=="function"?a.preinit(e):o.each(a.preinit,function(t,n){e.bind(n,t)});if(!a.browse_button||!a.url){this.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")});return}S.call(this),x.call(this,a,function(n){typeof a.init=="function"?a.init(e):o.each(a.init,function(t,n){e.bind(n,t)}),n?(e.runtime=t.Runtime.getInfo(w()).type,e.trigger("Init",{runtime:e.runtime}),e.trigger("PostInit")):e.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")})})},setOption:function(e,t){N.call(this,e,t,!this.runtime)},getOption:function(e){return e?a[e]:a},refresh:function(){c.length&&o.each(c,function(e){e.trigger("Refresh")}),this.trigger("Refresh")},start:function(){this.state!=o.STARTED&&(this.state=o.STARTED,this.trigger("StateChanged"),g.call(this))},stop:function(){this.state!=o.STOPPED&&(this.state=o.STOPPED,this.trigger("StateChanged"),this.trigger("CancelUpload"))},disableBrowse:function(){v=arguments[0]!==n?arguments[0]:!0,c.length&&o.each(c,function(e){e.disable(v)}),this.trigger("DisableBrowse",v)},getFile:function(e){var t;for(t=f.length-1;t>=0;t--)if(f[t].id===e)return f[t]},addFile:function(e,n){function l(e,n){var r=[];t.each(s.settings.filters,function(t,n){i[n]&&r.push(function(r){i[n].call(s,t,e,function(e){r(!e)})})}),t.inSeries(r,n)}function c(e){var i=t.typeOf(e);if(e instanceof t.File){if(!e.ruid&&!e.isDetached()){if(!f)return!1;e.ruid=f,e.connectRuntime(f)}c(new o.File(e))}else e instanceof t.Blob?(c(e.getSource()),e.destroy()):e instanceof o.File?(n&&(e.name=n),u.push(function(t){l(e,function(n){n||(a.push(e),s.trigger("FileFiltered",e)),r(t,1)})})):t.inArray(i,["file","blob"])!==-1?c(new t.File(null,e)):i==="node"&&t.typeOf(e.files)==="filelist"?t.each(e.files,c):i==="array"&&(n=null,t.each(e,c))}var s=this,u=[],a=[],f;f=w(),c(e),u.length&&t.inSeries(u,function(){a.length&&s.trigger("FilesAdded",a)})},removeFile:function(e){var t=typeof e=="string"?e:e.id;for(var n=f.length-1;n>=0;n--)if(f[n].id===t)return this.splice(n,1)[0]},splice:function(e,t){var r=f.splice(e===n?0:e,t===n?f.length:t),i=!1;return this.state==o.STARTED&&(i=!0,this.stop()),this.trigger("FilesRemoved",r),o.each(r,function(e){e.destroy()}),this.trigger("QueueChanged"),this.refresh(),i&&this.start(),r},bind:function(e,t,n){var r=this;o.Uploader.prototype.bind.call(this,e,function(){var e=[].slice.call(arguments);return e.splice(0,1,r),t.apply(this,e)},0,n)},destroy:function(){this.trigger("Destroy"),a=d=null,this.unbindAll()}})},o.Uploader.prototype=t.EventTarget.instance,o.File=function(){function n(n){o.extend(this,{id:o.guid(),name:n.name||n.fileName,type:n.type||"",size:n.size||n.fileSize,origSize:n.size||n.fileSize,loaded:0,percent:0,status:o.QUEUED,lastModifiedDate:n.lastModifiedDate||(new Date).toLocaleString(),getNative:function(){var e=this.getSource().getSource();return t.inArray(t.typeOf(e),["blob","file"])!==-1?e:null},getSource:function(){return e[this.id]?e[this.id]:null},destroy:function(){var t=this.getSource();t&&(t.destroy(),delete e[this.id])}}),e[this.id]=n}var e={};return n}(),o.QueueProgress=function(){var e=this;e.size=0,e.loaded=0,e.uploaded=0,e.failed=0,e.queued=0,e.percent=0,e.bytesPerSec=0,e.reset=function(){e.size=e.loaded=e.uploaded=e.failed=e.queued=e.percent=e.bytesPerSec=0}},e.plupload=o})(window,mOxie); \ No newline at end of file
diff --git a/phpBB/config/auth_providers.yml b/phpBB/config/auth_providers.yml
index e1c289334e..dac8b9d252 100644
--- a/phpBB/config/auth_providers.yml
+++ b/phpBB/config/auth_providers.yml
@@ -10,6 +10,7 @@ services:
arguments:
- @dbal.conn
- @config
+ - @passwords.manager
- @request
- @user
- %core.root_path%
@@ -21,6 +22,7 @@ services:
arguments:
- @dbal.conn
- @config
+ - @passwords.manager
- @request
- @user
- %core.root_path%
@@ -32,6 +34,7 @@ services:
arguments:
- @dbal.conn
- @config
+ - @passwords.manager
- @user
tags:
- { name: auth.provider }
@@ -40,6 +43,7 @@ services:
arguments:
- @dbal.conn
- @config
+ - @passwords.manager
- @request
- @user
- %tables.auth_provider_oauth_token_storage%
diff --git a/phpBB/config/console.yml b/phpBB/config/console.yml
index c85a9d19ed..a4aae75e40 100644
--- a/phpBB/config/console.yml
+++ b/phpBB/config/console.yml
@@ -1,4 +1,39 @@
services:
+ console.command.config.delete:
+ class: phpbb\console\command\config\delete
+ arguments:
+ - @config
+ tags:
+ - { name: console.command }
+
+ console.command.config.increment:
+ class: phpbb\console\command\config\increment
+ arguments:
+ - @config
+ tags:
+ - { name: console.command }
+
+ console.command.config.get:
+ class: phpbb\console\command\config\get
+ arguments:
+ - @config
+ tags:
+ - { name: console.command }
+
+ console.command.config.set:
+ class: phpbb\console\command\config\set
+ arguments:
+ - @config
+ tags:
+ - { name: console.command }
+
+ console.command.config.set_atomic:
+ class: phpbb\console\command\config\set_atomic
+ arguments:
+ - @config
+ tags:
+ - { name: console.command }
+
console.command.extension.disable:
class: phpbb\console\command\extension\disable
arguments:
diff --git a/phpBB/config/passwords.yml b/phpBB/config/passwords.yml
new file mode 100644
index 0000000000..9e249a2c12
--- /dev/null
+++ b/phpBB/config/passwords.yml
@@ -0,0 +1,62 @@
+parameters:
+ passwords.algorithms:
+ - passwords.driver.bcrypt_2y
+ - passwords.driver.bcrypt
+ - passwords.driver.salted_md5
+ - passwords.driver.phpass
+
+services:
+ passwords.driver.bcrypt:
+ class: phpbb\passwords\driver\bcrypt
+ arguments:
+ - @config
+ - @passwords.driver_helper
+ tags:
+ - { name: passwords.driver }
+
+ passwords.driver.bcrypt_2y:
+ class: phpbb\passwords\driver\bcrypt_2y
+ arguments:
+ - @config
+ - @passwords.driver_helper
+ tags:
+ - { name: passwords.driver }
+
+ passwords.driver.salted_md5:
+ class: phpbb\passwords\driver\salted_md5
+ arguments:
+ - @config
+ - @passwords.driver_helper
+ tags:
+ - { name: passwords.driver }
+
+ passwords.driver.phpass:
+ class: phpbb\passwords\driver\phpass
+ arguments:
+ - @config
+ - @passwords.driver_helper
+ tags:
+ - { name: passwords.driver }
+
+ passwords.driver_collection:
+ class: phpbb\di\service_collection
+ arguments:
+ - @service_container
+ tags:
+ - { name: service_collection, tag: passwords.driver }
+
+ passwords.driver_helper:
+ class: phpbb\passwords\driver\helper
+ arguments:
+ - @config
+
+ passwords.manager:
+ class: phpbb\passwords\manager
+ arguments:
+ - @config
+ - @passwords.driver_collection
+ - @passwords.helper
+ - %passwords.algorithms%
+
+ passwords.helper:
+ class: phpbb\passwords\helper
diff --git a/phpBB/config/profilefields.yml b/phpBB/config/profilefields.yml
new file mode 100644
index 0000000000..5a861a4b56
--- /dev/null
+++ b/phpBB/config/profilefields.yml
@@ -0,0 +1,82 @@
+services:
+ profilefields.manager:
+ class: phpbb\profilefields\manager
+ arguments:
+ - @auth
+ - @dbal.conn
+ - @request
+ - @template
+ - @profilefields.type_collection
+ - @user
+ - %tables.profile_fields%
+ - %tables.profile_fields_language%
+ - %tables.profile_fields_data%
+
+ profilefields.lang_helper:
+ class: phpbb\profilefields\lang_helper
+ arguments:
+ - @dbal.conn
+ - %tables.profile_fields_options_language%
+
+ profilefields.type_collection:
+ class: phpbb\di\service_collection
+ arguments:
+ - @service_container
+ tags:
+ - { name: service_collection, tag: profilefield.type }
+
+ profilefields.type.bool:
+ class: phpbb\profilefields\type\type_bool
+ arguments:
+ - @profilefields.lang_helper
+ - @request
+ - @template
+ - @user
+ tags:
+ - { name: profilefield.type }
+
+ profilefields.type.date:
+ class: phpbb\profilefields\type\type_date
+ arguments:
+ - @request
+ - @template
+ - @user
+ tags:
+ - { name: profilefield.type }
+
+ profilefields.type.dropdown:
+ class: phpbb\profilefields\type\type_dropdown
+ arguments:
+ - @profilefields.lang_helper
+ - @request
+ - @template
+ - @user
+ tags:
+ - { name: profilefield.type }
+
+ profilefields.type.int:
+ class: phpbb\profilefields\type\type_int
+ arguments:
+ - @request
+ - @template
+ - @user
+ tags:
+ - { name: profilefield.type }
+
+ profilefields.type.string:
+ class: phpbb\profilefields\type\type_string
+ arguments:
+ - @request
+ - @template
+ - @user
+ tags:
+ - { name: profilefield.type }
+
+ profilefields.type.text:
+ class: phpbb\profilefields\type\type_text
+ arguments:
+ - @request
+ - @template
+ - @user
+ tags:
+ - { name: profilefield.type }
diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml
index c11184d6b6..735626810f 100644
--- a/phpBB/config/services.yml
+++ b/phpBB/config/services.yml
@@ -8,6 +8,8 @@ imports:
- { resource: auth_providers.yml }
- { resource: console.yml }
- { resource: mimetype_guessers.yml }
+ - { resource: passwords.yml }
+ - { resource: profilefields.yml }
services:
acl.permissions:
diff --git a/phpBB/config/tables.yml b/phpBB/config/tables.yml
index 0d364eb6b0..e4f7bda89b 100644
--- a/phpBB/config/tables.yml
+++ b/phpBB/config/tables.yml
@@ -10,6 +10,10 @@ parameters:
tables.modules: %core.table_prefix%modules
tables.notification_types: %core.table_prefix%notification_types
tables.notifications: %core.table_prefix%notifications
+ tables.profile_fields: %core.table_prefix%profile_fields
+ tables.profile_fields_data: %core.table_prefix%profile_fields_data
+ tables.profile_fields_options_language: %core.table_prefix%profile_fields_lang
+ tables.profile_fields_language: %core.table_prefix%profile_lang
tables.posts: %core.table_prefix%posts
tables.topics: %core.table_prefix%topics
tables.user_notifications: %core.table_prefix%user_notifications
diff --git a/phpBB/docs/AUTHORS b/phpBB/docs/AUTHORS
index d70a1940ff..5ca52c19c0 100644
--- a/phpBB/docs/AUTHORS
+++ b/phpBB/docs/AUTHORS
@@ -23,10 +23,11 @@ involved in phpBB.
phpBB Lead Developer: naderman (Nils Adermann)
phpBB Developers: bantu (Andreas Fischer)
- EXreaction (Nathan Guse)
dhruv.goel92 (Dhruv Goel)
+ EXreaction (Nathan Guse)
imkingdavid (David King)
nickvergessen (Joas Schilling)
+ prototech (Cesar Gallegos)
Contributions by: leviatan21 (Gabriel Vazquez)
Raimon (Raimon Meuldijk)
diff --git a/phpBB/docs/events.md b/phpBB/docs/events.md
index f1df662f76..2d5f6cc990 100644
--- a/phpBB/docs/events.md
+++ b/phpBB/docs/events.md
@@ -211,6 +211,7 @@ overall_header_navigation_append
===
* Locations:
+ styles/prosilver/template/overall_header.html
+ + styles/subsilver2/template/overall_header.html
* Since: 3.1.0-a1
* Purpose: Add links after the navigation links in the header
@@ -218,6 +219,7 @@ overall_header_navigation_prepend
===
* Locations:
+ styles/prosilver/template/overall_header.html
+ + styles/subsilver2/template/overall_header.html
* Since: 3.1.0-a1
* Purpose: Add links before the navigation links in the header
diff --git a/phpBB/includes/acp/acp_forums.php b/phpBB/includes/acp/acp_forums.php
index 029f4b23c9..a1af8c489d 100644
--- a/phpBB/includes/acp/acp_forums.php
+++ b/phpBB/includes/acp/acp_forums.php
@@ -926,7 +926,7 @@ class acp_forums
*/
function update_forum_data(&$forum_data)
{
- global $db, $user, $cache, $phpbb_root_path, $phpbb_dispatcher;
+ global $db, $user, $cache, $phpbb_root_path, $phpbb_container, $phpbb_dispatcher;
$errors = array();
@@ -1030,7 +1030,10 @@ class acp_forums
}
else
{
- $forum_data_sql['forum_password'] = phpbb_hash($forum_data_sql['forum_password']);
+ // Instantiate passwords manager
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+
+ $forum_data_sql['forum_password'] = $passwords_manager->hash($forum_data_sql['forum_password']);
}
unset($forum_data_sql['forum_password_unset']);
diff --git a/phpBB/includes/acp/acp_icons.php b/phpBB/includes/acp/acp_icons.php
index f9295e2b47..20b1f56182 100644
--- a/phpBB/includes/acp/acp_icons.php
+++ b/phpBB/includes/acp/acp_icons.php
@@ -832,9 +832,10 @@ class acp_icons
WHERE {$fields}_order = $switch_order_id
AND {$fields}_id <> $icon_id";
$db->sql_query($sql);
+ $move_executed = (bool) $db->sql_affectedrows();
// Only update the other entry too if the previous entry got updated
- if ($db->sql_affectedrows())
+ if ($move_executed)
{
$sql = "UPDATE $table
SET {$fields}_order = $switch_order_id
@@ -846,6 +847,14 @@ class acp_icons
$cache->destroy('_icons');
$cache->destroy('sql', $table);
+ if ($request->is_ajax())
+ {
+ $json_response = new \phpbb\json_response;
+ $json_response->send(array(
+ 'success' => $move_executed,
+ ));
+ }
+
break;
}
diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php
index 100e33044b..c124377ba9 100644
--- a/phpBB/includes/acp/acp_modules.php
+++ b/phpBB/includes/acp/acp_modules.php
@@ -170,6 +170,14 @@ class acp_modules
$this->remove_cache_file();
}
+ if ($request->is_ajax())
+ {
+ $json_response = new \phpbb\json_response;
+ $json_response->send(array(
+ 'success' => ($move_module_name !== false),
+ ));
+ }
+
break;
case 'quickadd':
diff --git a/phpBB/includes/acp/acp_permission_roles.php b/phpBB/includes/acp/acp_permission_roles.php
index 17e48d6576..aca45575d3 100644
--- a/phpBB/includes/acp/acp_permission_roles.php
+++ b/phpBB/includes/acp/acp_permission_roles.php
@@ -27,6 +27,7 @@ class acp_permission_roles
{
global $db, $user, $auth, $template, $cache, $phpbb_container;
global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx;
+ global $request;
include_once($phpbb_root_path . 'includes/functions_user.' . $phpEx);
include_once($phpbb_root_path . 'includes/acp/auth.' . $phpEx);
@@ -46,6 +47,11 @@ class acp_permission_roles
$form_name = 'acp_permissions';
add_form_key($form_name);
+ if (!$role_id && in_array($action, array('remove', 'edit', 'move_up', 'move_down')))
+ {
+ trigger_error($user->lang['NO_ROLE_SELECTED'] . adm_back_link($this->u_action), E_USER_WARNING);
+ }
+
switch ($mode)
{
case 'admin_roles':
@@ -85,11 +91,6 @@ class acp_permission_roles
{
case 'remove':
- if (!$role_id)
- {
- trigger_error($user->lang['NO_ROLE_SELECTED'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
-
$sql = 'SELECT *
FROM ' . ACL_ROLES_TABLE . '
WHERE role_id = ' . $role_id;
@@ -123,10 +124,6 @@ class acp_permission_roles
break;
case 'edit':
- if (!$role_id)
- {
- trigger_error($user->lang['NO_ROLE_SELECTED'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
// Get role we edit
$sql = 'SELECT *
@@ -273,12 +270,7 @@ class acp_permission_roles
case 'edit':
if ($action == 'edit')
- {
- if (!$role_id)
- {
- trigger_error($user->lang['NO_ROLE_SELECTED'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
-
+ {
$sql = 'SELECT *
FROM ' . ACL_ROLES_TABLE . '
WHERE role_id = ' . $role_id;
@@ -366,7 +358,18 @@ class acp_permission_roles
case 'move_up':
case 'move_down':
- $order = request_var('order', 0);
+ $sql = 'SELECT role_order
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_id = $role_id";
+ $result = $db->sql_query($sql);
+ $order = $db->sql_fetchfield('role_order');
+ $db->sql_freeresult($result);
+
+ if ($order === false || ($order == 0 && $action == 'move_up'))
+ {
+ break;
+ }
+ $order = (int) $order;
$order_total = $order * 2 + (($action == 'move_up') ? -1 : 1);
$sql = 'UPDATE ' . ACL_ROLES_TABLE . '
@@ -375,6 +378,14 @@ class acp_permission_roles
AND role_order IN ($order, " . (($action == 'move_up') ? $order - 1 : $order + 1) . ')';
$db->sql_query($sql);
+ if ($request->is_ajax())
+ {
+ $json_response = new \phpbb\json_response;
+ $json_response->send(array(
+ 'success' => (bool) $db->sql_affectedrows(),
+ ));
+ }
+
break;
}
@@ -421,8 +432,8 @@ class acp_permission_roles
'U_EDIT' => $this->u_action . '&amp;action=edit&amp;role_id=' . $row['role_id'],
'U_REMOVE' => $this->u_action . '&amp;action=remove&amp;role_id=' . $row['role_id'],
- 'U_MOVE_UP' => $this->u_action . '&amp;action=move_up&amp;order=' . $row['role_order'],
- 'U_MOVE_DOWN' => $this->u_action . '&amp;action=move_down&amp;order=' . $row['role_order'],
+ 'U_MOVE_UP' => $this->u_action . '&amp;action=move_up&amp;role_id=' . $row['role_id'],
+ 'U_MOVE_DOWN' => $this->u_action . '&amp;action=move_down&amp;role_id=' . $row['role_id'],
'U_DISPLAY_ITEMS' => ($row['role_id'] == $display_item) ? '' : $this->u_action . '&amp;display_item=' . $row['role_id'] . '#assigned_to')
);
diff --git a/phpBB/includes/acp/acp_profile.php b/phpBB/includes/acp/acp_profile.php
index 4e8145009f..2fa1f38bd3 100644
--- a/phpBB/includes/acp/acp_profile.php
+++ b/phpBB/includes/acp/acp_profile.php
@@ -24,37 +24,34 @@ class acp_profile
var $edit_lang_id;
var $lang_defs;
+ protected $type_collection;
function main($id, $mode)
{
global $config, $db, $user, $auth, $template, $cache;
global $phpbb_root_path, $phpbb_admin_path, $phpEx, $table_prefix;
- global $request;
+ global $request, $phpbb_container;
include($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
- include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
$user->add_lang(array('ucp', 'acp/profile'));
$this->tpl_name = 'acp_profile';
$this->page_title = 'ACP_CUSTOM_PROFILE_FIELDS';
+ $field_id = $request->variable('field_id', 0);
$action = (isset($_POST['create'])) ? 'create' : request_var('action', '');
$error = array();
$s_hidden_fields = '';
- // Define some default values for each field type
- $default_values = array(
- FIELD_STRING => array('field_length' => 10, 'field_minlen' => 0, 'field_maxlen' => 20, 'field_validation' => '.*', 'field_novalue' => '', 'field_default_value' => ''),
- FIELD_TEXT => array('field_length' => '5|80', 'field_minlen' => 0, 'field_maxlen' => 1000, 'field_validation' => '.*', 'field_novalue' => '', 'field_default_value' => ''),
- FIELD_INT => array('field_length' => 5, 'field_minlen' => 0, 'field_maxlen' => 100, 'field_validation' => '', 'field_novalue' => 0, 'field_default_value' => 0),
- FIELD_DATE => array('field_length' => 10, 'field_minlen' => 10, 'field_maxlen' => 10, 'field_validation' => '', 'field_novalue' => ' 0- 0- 0', 'field_default_value' => ' 0- 0- 0'),
- FIELD_BOOL => array('field_length' => 1, 'field_minlen' => 0, 'field_maxlen' => 0, 'field_validation' => '', 'field_novalue' => 0, 'field_default_value' => 0),
- FIELD_DROPDOWN => array('field_length' => 0, 'field_minlen' => 0, 'field_maxlen' => 5, 'field_validation' => '', 'field_novalue' => 0, 'field_default_value' => 0),
- );
+ if (!$field_id && in_array($action, array('delete','activate', 'deactivate', 'move_up', 'move_down', 'edit')))
+ {
+ trigger_error($user->lang['NO_FIELD_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
+ }
- $cp = new custom_profile_admin();
+ $cp = $phpbb_container->get('profilefields.manager');
+ $this->type_collection = $phpbb_container->get('profilefields.type_collection');
// Build Language array
// Based on this, we decide which elements need to be edited later and which language items are missing
@@ -88,22 +85,16 @@ class acp_profile
// Have some fields been defined?
if (isset($this->lang_defs['entry']))
{
- foreach ($this->lang_defs['entry'] as $field_id => $field_ary)
+ foreach ($this->lang_defs['entry'] as $field_ident => $field_ary)
{
// Fill an array with the languages that are missing for each field
- $this->lang_defs['diff'][$field_id] = array_diff(array_values($this->lang_defs['iso']), $field_ary);
+ $this->lang_defs['diff'][$field_ident] = array_diff(array_values($this->lang_defs['iso']), $field_ary);
}
}
switch ($action)
{
case 'delete':
- $field_id = request_var('field_id', 0);
-
- if (!$field_id)
- {
- trigger_error($user->lang['NO_FIELD_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
if (confirm_box(true))
{
@@ -210,12 +201,6 @@ class acp_profile
break;
case 'activate':
- $field_id = request_var('field_id', 0);
-
- if (!$field_id)
- {
- trigger_error($user->lang['NO_FIELD_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
$sql = 'SELECT lang_id
FROM ' . LANG_TABLE . "
@@ -256,12 +241,6 @@ class acp_profile
break;
case 'deactivate':
- $field_id = request_var('field_id', 0);
-
- if (!$field_id)
- {
- trigger_error($user->lang['NO_FIELD_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
$sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . "
SET field_active = 0
@@ -291,7 +270,19 @@ class acp_profile
case 'move_up':
case 'move_down':
- $field_order = request_var('order', 0);
+
+ $sql = 'SELECT field_order
+ FROM ' . PROFILE_FIELDS_TABLE . "
+ WHERE field_id = $field_id";
+ $result = $db->sql_query($sql);
+ $field_order = $db->sql_fetchfield('field_order');
+ $db->sql_freeresult($result);
+
+ if ($field_order === false || ($field_order == 0 && $action == 'move_up'))
+ {
+ break;
+ }
+ $field_order = (int) $field_order;
$order_total = $field_order * 2 + (($action == 'move_up') ? -1 : 1);
$sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . "
@@ -299,12 +290,19 @@ class acp_profile
WHERE field_order IN ($field_order, " . (($action == 'move_up') ? $field_order - 1 : $field_order + 1) . ')';
$db->sql_query($sql);
+ if ($request->is_ajax())
+ {
+ $json_response = new \phpbb\json_response;
+ $json_response->send(array(
+ 'success' => (bool) $db->sql_affectedrows(),
+ ));
+ }
+
break;
case 'create':
case 'edit':
- $field_id = request_var('field_id', 0);
$step = request_var('step', 1);
$submit = (isset($_REQUEST['next']) || isset($_REQUEST['prev'])) ? true : false;
@@ -316,11 +314,6 @@ class acp_profile
// We are editing... we need to grab basic things
if ($action == 'edit')
{
- if (!$field_id)
- {
- trigger_error($user->lang['NO_FIELD_ID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
-
$sql = 'SELECT l.*, f.*
FROM ' . PROFILE_LANG_TABLE . ' l, ' . PROFILE_FIELDS_TABLE . ' f
WHERE l.lang_id = ' . $this->edit_lang_id . "
@@ -350,6 +343,7 @@ class acp_profile
$this->edit_lang_id = $field_row['lang_id'];
}
$field_type = $field_row['field_type'];
+ $profile_field = $this->type_collection[$field_type];
// Get language entries
$sql = 'SELECT *
@@ -373,14 +367,15 @@ class acp_profile
// We are adding a new field, define basic params
$lang_options = $field_row = array();
- $field_type = request_var('field_type', 0);
+ $field_type = request_var('field_type', '');
- if (!$field_type)
+ if (!isset($this->type_collection[$field_type]))
{
trigger_error($user->lang['NO_FIELD_TYPE'] . adm_back_link($this->u_action), E_USER_WARNING);
}
- $field_row = array_merge($default_values[$field_type], array(
+ $profile_field = $this->type_collection[$field_type];
+ $field_row = array_merge($profile_field->get_default_option_values(), array(
'field_ident' => str_replace(' ', '_', utf8_clean_string(request_var('field_ident', '', true))),
'field_required' => 0,
'field_show_novalue'=> 0,
@@ -405,23 +400,6 @@ class acp_profile
3 => array('l_lang_name', 'l_lang_explain', 'l_lang_default_value', 'l_lang_options')
);
- // Text-based fields require the lang_default_value to be excluded
- if ($field_type == FIELD_STRING || $field_type == FIELD_TEXT)
- {
- $exclude[1][] = 'lang_default_value';
- }
-
- // option-specific fields require lang_options to be excluded
- if ($field_type == FIELD_BOOL || $field_type == FIELD_DROPDOWN)
- {
- $exclude[1][] = 'lang_options';
- }
-
- $cp->vars['field_ident'] = ($action == 'create' && $step == 1) ? utf8_clean_string(request_var('field_ident', $field_row['field_ident'], true)) : request_var('field_ident', $field_row['field_ident']);
- $cp->vars['lang_name'] = utf8_normalize_nfc(request_var('lang_name', $field_row['lang_name'], true));
- $cp->vars['lang_explain'] = utf8_normalize_nfc(request_var('lang_explain', $field_row['lang_explain'], true));
- $cp->vars['lang_default_value'] = utf8_normalize_nfc(request_var('lang_default_value', $field_row['lang_default_value'], true));
-
// Visibility Options...
$visibility_ary = array(
'field_required',
@@ -433,6 +411,13 @@ class acp_profile
'field_hide',
);
+ $options = $profile_field->prepare_options_form($exclude, $visibility_ary);
+
+ $cp->vars['field_ident'] = ($action == 'create' && $step == 1) ? utf8_clean_string(request_var('field_ident', $field_row['field_ident'], true)) : request_var('field_ident', $field_row['field_ident']);
+ $cp->vars['lang_name'] = utf8_normalize_nfc(request_var('lang_name', $field_row['lang_name'], true));
+ $cp->vars['lang_explain'] = utf8_normalize_nfc(request_var('lang_explain', $field_row['lang_explain'], true));
+ $cp->vars['lang_default_value'] = utf8_normalize_nfc(request_var('lang_default_value', $field_row['lang_default_value'], true));
+
foreach ($visibility_ary as $val)
{
$cp->vars[$val] = ($submit || $save) ? request_var($val, 0) : $field_row[$val];
@@ -440,16 +425,6 @@ class acp_profile
$cp->vars['field_no_view'] = request_var('field_no_view', (int) $field_row['field_no_view']);
- // A boolean field expects an array as the lang options
- if ($field_type == FIELD_BOOL)
- {
- $options = utf8_normalize_nfc(request_var('lang_options', array(''), true));
- }
- else
- {
- $options = utf8_normalize_nfc(request_var('lang_options', '', true));
- }
-
// If the user has submitted a form with options (i.e. dropdown field)
if ($options)
{
@@ -477,93 +452,9 @@ class acp_profile
{
$var = utf8_normalize_nfc(request_var($key, $field_row[$key], true));
- // Manipulate the intended variables a little bit if needed
- if ($field_type == FIELD_DROPDOWN && $key == 'field_maxlen')
- {
- // Get the number of options if this key is 'field_maxlen'
- $var = sizeof(explode("\n", utf8_normalize_nfc(request_var('lang_options', '', true))));
- }
- else if ($field_type == FIELD_TEXT && $key == 'field_length')
- {
- if (isset($_REQUEST['rows']))
- {
- $cp->vars['rows'] = request_var('rows', 0);
- $cp->vars['columns'] = request_var('columns', 0);
- $var = $cp->vars['rows'] . '|' . $cp->vars['columns'];
- }
- else
- {
- $row_col = explode('|', $var);
- $cp->vars['rows'] = $row_col[0];
- $cp->vars['columns'] = $row_col[1];
- }
- }
- else if ($field_type == FIELD_DATE && $key == 'field_default_value')
- {
- $always_now = request_var('always_now', -1);
-
- if ($always_now == 1 || ($always_now === -1 && $var == 'now'))
- {
- $now = getdate();
-
- $cp->vars['field_default_value_day'] = $now['mday'];
- $cp->vars['field_default_value_month'] = $now['mon'];
- $cp->vars['field_default_value_year'] = $now['year'];
- $var = 'now';
- $request->overwrite('field_default_value', $var, \phpbb\request\request_interface::POST);
- }
- else
- {
- if (isset($_REQUEST['field_default_value_day']))
- {
- $cp->vars['field_default_value_day'] = request_var('field_default_value_day', 0);
- $cp->vars['field_default_value_month'] = request_var('field_default_value_month', 0);
- $cp->vars['field_default_value_year'] = request_var('field_default_value_year', 0);
- $var = sprintf('%2d-%2d-%4d', $cp->vars['field_default_value_day'], $cp->vars['field_default_value_month'], $cp->vars['field_default_value_year']);
- $request->overwrite('field_default_value', $var, \phpbb\request\request_interface::POST);
- }
- else
- {
- list($cp->vars['field_default_value_day'], $cp->vars['field_default_value_month'], $cp->vars['field_default_value_year']) = explode('-', $var);
- }
- }
- }
- else if ($field_type == FIELD_BOOL && $key == 'field_default_value')
- {
- // 'field_length' == 1 defines radio buttons. Possible values are 1 or 2 only.
- // 'field_length' == 2 defines checkbox. Possible values are 0 or 1 only.
- // If we switch the type on step 2, we have to adjust field value.
- // 1 is a common value for the checkbox and radio buttons.
-
- // Adjust unchecked checkbox value.
- // If we return or save settings from 2nd/3rd page
- // and the checkbox is unchecked, set the value to 0.
- if (isset($_REQUEST['step']) && !isset($_REQUEST[$key]))
- {
- $var = 0;
- }
-
- // If we switch to the checkbox type but former radio buttons value was 2,
- // which is not the case for the checkbox, set it to 0 (unchecked).
- if ($cp->vars['field_length'] == 2 && $var == 2)
- {
- $var = 0;
- }
- // If we switch to the radio buttons but the former checkbox value was 0,
- // which is not the case for the radio buttons, set it to 0.
- else if ($cp->vars['field_length'] == 1 && $var == 0)
- {
- $var = 2;
- }
- }
- else if ($field_type == FIELD_INT && $key == 'field_default_value')
- {
- // Permit an empty string
- if ($action == 'create' && request_var('field_default_value', '') === '')
- {
- $var = '';
- }
- }
+ $field_data = $cp->vars;
+ $var = $profile_field->get_excluded_options($key, $action, $var, $field_data, 2);
+ $cp->vars = $field_data;
$cp->vars[$key] = $var;
}
@@ -612,18 +503,10 @@ class acp_profile
{
$cp->vars[$key] = $$key;
}
- else if ($key == 'l_lang_options' && $field_type == FIELD_BOOL)
- {
- $cp->vars[$key] = utf8_normalize_nfc(request_var($key, array(0 => array('')), true));
- }
- else if ($key == 'l_lang_options' && is_array($cp->vars[$key]))
- {
- foreach ($cp->vars[$key] as $lang_id => $options)
- {
- $cp->vars[$key][$lang_id] = explode("\n", $options);
- }
- }
+ $field_data = $cp->vars;
+ $var = $profile_field->get_excluded_options($key, $action, $var, $field_data, 3);
+ $cp->vars = $field_data;
}
// Check for general issues in every step
@@ -650,15 +533,7 @@ class acp_profile
$error[] = $user->lang['EMPTY_USER_FIELD_NAME'];
}
- if ($field_type == FIELD_DROPDOWN && !sizeof($cp->vars['lang_options']))
- {
- $error[] = $user->lang['NO_FIELD_ENTRIES'];
- }
-
- if ($field_type == FIELD_BOOL && (empty($cp->vars['lang_options'][0]) || empty($cp->vars['lang_options'][1])))
- {
- $error[] = $user->lang['NO_FIELD_ENTRIES'];
- }
+ $error = $profile_field->validate_options_on_submit($error, $cp->vars);
// Check for already existing field ident
if ($action != 'edit')
@@ -695,54 +570,16 @@ class acp_profile
$_new_key_ary = array();
+ $field_data = $cp->vars;
foreach ($key_ary as $key)
{
- if ($field_type == FIELD_TEXT && $key == 'field_length' && isset($_REQUEST['rows']))
- {
- $cp->vars['rows'] = request_var('rows', 0);
- $cp->vars['columns'] = request_var('columns', 0);
- $_new_key_ary[$key] = $cp->vars['rows'] . '|' . $cp->vars['columns'];
- }
- else if ($field_type == FIELD_DATE && $key == 'field_default_value')
- {
- $always_now = request_var('always_now', 0);
-
- if ($always_now)
- {
- $_new_key_ary[$key] = 'now';
- }
- else if (isset($_REQUEST['field_default_value_day']))
- {
- $cp->vars['field_default_value_day'] = request_var('field_default_value_day', 0);
- $cp->vars['field_default_value_month'] = request_var('field_default_value_month', 0);
- $cp->vars['field_default_value_year'] = request_var('field_default_value_year', 0);
- $_new_key_ary[$key] = sprintf('%2d-%2d-%4d', $cp->vars['field_default_value_day'], $cp->vars['field_default_value_month'], $cp->vars['field_default_value_year']);
- }
- }
- else if ($field_type == FIELD_BOOL && $key == 'l_lang_options' && isset($_REQUEST['l_lang_options']))
- {
- $_new_key_ary[$key] = utf8_normalize_nfc(request_var($key, array(array('')), true));
- }
- else if ($field_type == FIELD_BOOL && $key == 'field_default_value')
+ $var = $profile_field->prepare_hidden_fields($step, $key, $action, $field_data);
+ if ($var !== null)
{
- $_new_key_ary[$key] = request_var($key, $cp->vars[$key]);
- }
- else
- {
- if (!isset($_REQUEST[$key]))
- {
- $var = false;
- }
- else if ($key == 'field_ident' && isset($cp->vars[$key]))
- {
- $_new_key_ary[$key]= $cp->vars[$key];
- }
- else
- {
- $_new_key_ary[$key] = ($field_type == FIELD_BOOL && $key == 'lang_options') ? utf8_normalize_nfc(request_var($key, array(''), true)) : utf8_normalize_nfc(request_var($key, '', true));
- }
+ $_new_key_ary[$key] = $profile_field->prepare_hidden_fields($step, $key, $action, $field_data);
}
}
+ $cp->vars = $field_data;
$s_hidden_fields .= build_hidden_fields($_new_key_ary);
}
@@ -776,9 +613,7 @@ class acp_profile
{
// Create basic options - only small differences between field types
case 1:
-
- // Build common create options
- $template->assign_vars(array(
+ $template_vars = array(
'S_STEP_ONE' => true,
'S_FIELD_REQUIRED' => ($cp->vars['field_required']) ? true : false,
'S_FIELD_SHOW_NOVALUE'=> ($cp->vars['field_show_novalue']) ? true : false,
@@ -790,53 +625,18 @@ class acp_profile
'S_FIELD_NO_VIEW' => ($cp->vars['field_no_view']) ? true : false,
'L_LANG_SPECIFIC' => sprintf($user->lang['LANG_SPECIFIC_OPTIONS'], $config['default_lang']),
- 'FIELD_TYPE' => $user->lang['FIELD_' . strtoupper($cp->profile_types[$field_type])],
+ 'FIELD_TYPE' => $profile_field->get_name(),
'FIELD_IDENT' => $cp->vars['field_ident'],
'LANG_NAME' => $cp->vars['lang_name'],
- 'LANG_EXPLAIN' => $cp->vars['lang_explain'])
+ 'LANG_EXPLAIN' => $cp->vars['lang_explain'],
);
- // String and Text needs to set default values here...
- if ($field_type == FIELD_STRING || $field_type == FIELD_TEXT)
- {
- $template->assign_vars(array(
- 'S_TEXT' => ($field_type == FIELD_TEXT) ? true : false,
- 'S_STRING' => ($field_type == FIELD_STRING) ? true : false,
-
- 'L_DEFAULT_VALUE_EXPLAIN' => $user->lang[strtoupper($cp->profile_types[$field_type]) . '_DEFAULT_VALUE_EXPLAIN'],
- 'LANG_DEFAULT_VALUE' => $cp->vars['lang_default_value'])
- );
- }
-
- if ($field_type == FIELD_BOOL || $field_type == FIELD_DROPDOWN)
- {
- // Initialize these array elements if we are creating a new field
- if (!sizeof($cp->vars['lang_options']))
- {
- if ($field_type == FIELD_BOOL)
- {
- // No options have been defined for a boolean field.
- $cp->vars['lang_options'][0] = '';
- $cp->vars['lang_options'][1] = '';
- }
- else
- {
- // No options have been defined for the dropdown menu
- $cp->vars['lang_options'] = array();
- }
- }
-
- $template->assign_vars(array(
- 'S_BOOL' => ($field_type == FIELD_BOOL) ? true : false,
- 'S_DROPDOWN' => ($field_type == FIELD_DROPDOWN) ? true : false,
-
- 'L_LANG_OPTIONS_EXPLAIN' => $user->lang[strtoupper($cp->profile_types[$field_type]) . '_ENTRIES_EXPLAIN'],
- 'LANG_OPTIONS' => ($field_type == FIELD_DROPDOWN) ? implode("\n", $cp->vars['lang_options']) : '',
- 'FIRST_LANG_OPTION' => ($field_type == FIELD_BOOL) ? $cp->vars['lang_options'][0] : '',
- 'SECOND_LANG_OPTION' => ($field_type == FIELD_BOOL) ? $cp->vars['lang_options'][1] : '')
- );
- }
+ $field_data = $cp->vars;
+ $profile_field->display_options($template_vars, $field_data);
+ $cp->vars = $field_data;
+ // Build common create options
+ $template->assign_vars($template_vars);
break;
case 2:
@@ -847,8 +647,7 @@ class acp_profile
);
// Build options based on profile type
- $function = 'get_' . $cp->profile_types[$field_type] . '_options';
- $options = $cp->$function();
+ $options = $profile_field->get_options($this->lang_defs['iso'][$config['default_lang']], $cp->vars);
foreach ($options as $num => $option_ary)
{
@@ -910,17 +709,18 @@ class acp_profile
$s_one_need_edit = true;
}
+ $profile_field = $this->type_collection[$row['field_type']];
$template->assign_block_vars('fields', array(
'FIELD_IDENT' => $row['field_ident'],
- 'FIELD_TYPE' => $user->lang['FIELD_' . strtoupper($cp->profile_types[$row['field_type']])],
+ 'FIELD_TYPE' => $profile_field->get_name(),
'L_ACTIVATE_DEACTIVATE' => $user->lang[$active_lang],
'U_ACTIVATE_DEACTIVATE' => $this->u_action . "&amp;action=$active_value&amp;field_id=$id",
'U_EDIT' => $this->u_action . "&amp;action=edit&amp;field_id=$id",
'U_TRANSLATE' => $this->u_action . "&amp;action=edit&amp;field_id=$id&amp;step=3",
'U_DELETE' => $this->u_action . "&amp;action=delete&amp;field_id=$id",
- 'U_MOVE_UP' => $this->u_action . "&amp;action=move_up&amp;order={$row['field_order']}",
- 'U_MOVE_DOWN' => $this->u_action . "&amp;action=move_down&amp;order={$row['field_order']}",
+ 'U_MOVE_UP' => $this->u_action . "&amp;action=move_up&amp;field_id=$id",
+ 'U_MOVE_DOWN' => $this->u_action . "&amp;action=move_down&amp;field_id=$id",
'S_NEED_EDIT' => $s_need_edit)
);
@@ -934,15 +734,15 @@ class acp_profile
}
$s_select_type = '';
- foreach ($cp->profile_types as $key => $value)
+ foreach ($this->type_collection as $key => $profile_field)
{
- $s_select_type .= '<option value="' . $key . '">' . $user->lang['FIELD_' . strtoupper($value)] . '</option>';
+ $s_select_type .= '<option value="' . $key . '">' . $profile_field->get_name() . '</option>';
}
$template->assign_vars(array(
'U_ACTION' => $this->u_action,
- 'S_TYPE_OPTIONS' => $s_select_type)
- );
+ 'S_TYPE_OPTIONS' => $s_select_type,
+ ));
}
/**
@@ -950,7 +750,7 @@ class acp_profile
*/
function build_language_options(&$cp, $field_type, $action = 'create')
{
- global $user, $config, $db;
+ global $user, $config, $db, $phpbb_container;
$default_lang_id = (!empty($this->edit_lang_id)) ? $this->edit_lang_id : $this->lang_defs['iso'][$config['default_lang']];
@@ -967,31 +767,8 @@ class acp_profile
}
$db->sql_freeresult($result);
- $options = array();
- $options['lang_name'] = 'string';
- if ($cp->vars['lang_explain'])
- {
- $options['lang_explain'] = 'text';
- }
-
- switch ($field_type)
- {
- case FIELD_BOOL:
- $options['lang_options'] = 'two_options';
- break;
-
- case FIELD_DROPDOWN:
- $options['lang_options'] = 'optionfield';
- break;
-
- case FIELD_TEXT:
- case FIELD_STRING:
- if (strlen($cp->vars['lang_default_value']))
- {
- $options['lang_default_value'] = ($field_type == FIELD_STRING) ? 'string' : 'text';
- }
- break;
- }
+ $profile_field = $this->type_collection[$field_type];
+ $options = $profile_field->get_language_options($cp->vars);
$lang_options = array();
@@ -1070,7 +847,7 @@ class acp_profile
*/
function save_profile_field(&$cp, $field_type, $action = 'create')
{
- global $db, $config, $user;
+ global $db, $config, $user, $phpbb_container;
$field_id = request_var('field_id', 0);
@@ -1131,10 +908,16 @@ class acp_profile
$db->sql_query($sql);
}
+ $profile_field = $this->type_collection[$field_type];
+
if ($action == 'create')
{
$field_ident = 'pf_' . $field_ident;
- $profile_sql[] = $this->add_field_ident($field_ident, $field_type);
+
+ $db_tools = $phpbb_container->get('dbal.tools');
+
+ list($sql_type, $null) = $db_tools->get_column_type($profile_field->get_database_column_type());
+ $profile_sql[] = $this->add_field_ident($field_ident, $sql_type);
}
$sql_ary = array(
@@ -1188,23 +971,7 @@ class acp_profile
}
}
- // These are always arrays because the key is the language id...
- $cp->vars['l_lang_name'] = utf8_normalize_nfc(request_var('l_lang_name', array(0 => ''), true));
- $cp->vars['l_lang_explain'] = utf8_normalize_nfc(request_var('l_lang_explain', array(0 => ''), true));
- $cp->vars['l_lang_default_value'] = utf8_normalize_nfc(request_var('l_lang_default_value', array(0 => ''), true));
-
- if ($field_type != FIELD_BOOL)
- {
- $cp->vars['l_lang_options'] = utf8_normalize_nfc(request_var('l_lang_options', array(0 => ''), true));
- }
- else
- {
- /**
- * @todo check if this line is correct...
- $cp->vars['l_lang_default_value'] = request_var('l_lang_default_value', array(0 => array('')), true);
- */
- $cp->vars['l_lang_options'] = utf8_normalize_nfc(request_var('l_lang_options', array(0 => array('')), true));
- }
+ $cp->vars = $profile_field->get_language_options_input($cp->vars);
if ($cp->vars['lang_options'])
{
@@ -1224,7 +991,7 @@ class acp_profile
foreach ($cp->vars['lang_options'] as $option_id => $value)
{
$sql_ary = array(
- 'field_type' => (int) $field_type,
+ 'field_type' => $field_type,
'lang_value' => $value
);
@@ -1279,7 +1046,7 @@ class acp_profile
'field_id' => (int) $field_id,
'lang_id' => (int) $lang_id,
'option_id' => (int) $option_id,
- 'field_type' => (int) $field_type,
+ 'field_type' => $field_type,
'lang_value' => $value
);
}
@@ -1409,7 +1176,7 @@ class acp_profile
/**
* Return sql statement for adding a new field ident (profile field) to the profile fields data table
*/
- function add_field_ident($field_ident, $field_type)
+ function add_field_ident($field_ident, $sql_type)
{
global $db;
@@ -1418,73 +1185,11 @@ class acp_profile
case 'mysql':
case 'mysql4':
case 'mysqli':
-
- // We are defining the biggest common value, because of the possibility to edit the min/max values of each field.
- $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD `$field_ident` ";
-
- switch ($field_type)
- {
- case FIELD_STRING:
- $sql .= ' VARCHAR(255) ';
- break;
-
- case FIELD_DATE:
- $sql .= 'VARCHAR(10) ';
- break;
-
- case FIELD_TEXT:
- $sql .= "TEXT";
- // ADD {$field_ident}_bbcode_uid VARCHAR(5) NOT NULL,
- // ADD {$field_ident}_bbcode_bitfield INT(11) UNSIGNED";
- break;
-
- case FIELD_BOOL:
- $sql .= 'TINYINT(2) ';
- break;
-
- case FIELD_DROPDOWN:
- $sql .= 'MEDIUMINT(8) ';
- break;
-
- case FIELD_INT:
- $sql .= 'BIGINT(20) ';
- break;
- }
+ $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD `$field_ident` " . $sql_type;
break;
case 'sqlite':
-
- switch ($field_type)
- {
- case FIELD_STRING:
- $type = ' VARCHAR(255) ';
- break;
-
- case FIELD_DATE:
- $type = 'VARCHAR(10) ';
- break;
-
- case FIELD_TEXT:
- $type = "TEXT(65535)";
- // ADD {$field_ident}_bbcode_uid VARCHAR(5) NOT NULL,
- // ADD {$field_ident}_bbcode_bitfield INT(11) UNSIGNED";
- break;
-
- case FIELD_BOOL:
- $type = 'TINYINT(2) ';
- break;
-
- case FIELD_DROPDOWN:
- $type = 'MEDIUMINT(8) ';
- break;
-
- case FIELD_INT:
- $type = 'BIGINT(20) ';
- break;
- }
-
- // We are defining the biggest common value, because of the possibility to edit the min/max values of each field.
if (version_compare(sqlite_libversion(), '3.0') == -1)
{
$sql = "SELECT sql
@@ -1519,7 +1224,7 @@ class acp_profile
$columns = implode(',', $column_list);
- $new_table_cols = $field_ident . ' ' . $type . ',' . $new_table_cols;
+ $new_table_cols = $field_ident . ' ' . $sql_type . ',' . $new_table_cols;
// create a new table and fill it up. destroy the temp one
$db->sql_query('CREATE TABLE ' . PROFILE_FIELDS_DATA_TABLE . ' (' . $new_table_cols . ');');
@@ -1528,7 +1233,7 @@ class acp_profile
}
else
{
- $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD $field_ident [$type]";
+ $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD $field_ident [$sql_type]";
}
break;
@@ -1536,140 +1241,22 @@ class acp_profile
case 'mssql':
case 'mssql_odbc':
case 'mssqlnative':
-
- // We are defining the biggest common value, because of the possibility to edit the min/max values of each field.
- $sql = 'ALTER TABLE [' . PROFILE_FIELDS_DATA_TABLE . "] ADD [$field_ident] ";
-
- switch ($field_type)
- {
- case FIELD_STRING:
- $sql .= ' [VARCHAR] (255) ';
- break;
-
- case FIELD_DATE:
- $sql .= '[VARCHAR] (10) ';
- break;
-
- case FIELD_TEXT:
- $sql .= "[TEXT]";
- // ADD {$field_ident}_bbcode_uid [VARCHAR] (5) NOT NULL,
- // ADD {$field_ident}_bbcode_bitfield [INT] UNSIGNED";
- break;
-
- case FIELD_BOOL:
- case FIELD_DROPDOWN:
- $sql .= '[INT] ';
- break;
-
- case FIELD_INT:
- $sql .= '[FLOAT] ';
- break;
- }
+ $sql = 'ALTER TABLE [' . PROFILE_FIELDS_DATA_TABLE . "] ADD [$field_ident] " . $sql_type;
break;
case 'postgres':
-
- // We are defining the biggest common value, because of the possibility to edit the min/max values of each field.
- $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD COLUMN \"$field_ident\" ";
-
- switch ($field_type)
- {
- case FIELD_STRING:
- $sql .= ' VARCHAR(255) ';
- break;
-
- case FIELD_DATE:
- $sql .= 'VARCHAR(10) ';
- break;
-
- case FIELD_TEXT:
- $sql .= "TEXT";
- // ADD {$field_ident}_bbcode_uid VARCHAR(5) NOT NULL,
- // ADD {$field_ident}_bbcode_bitfield INT4 UNSIGNED";
- break;
-
- case FIELD_BOOL:
- $sql .= 'INT2 ';
- break;
-
- case FIELD_DROPDOWN:
- $sql .= 'INT4 ';
- break;
-
- case FIELD_INT:
- $sql .= 'INT8 ';
- break;
- }
+ $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD COLUMN \"$field_ident\" " . $sql_type;
break;
case 'firebird':
-
- // We are defining the biggest common value, because of the possibility to edit the min/max values of each field.
- $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . ' ADD "' . strtoupper($field_ident) . '" ';
-
- switch ($field_type)
- {
- case FIELD_STRING:
- $sql .= ' VARCHAR(255) ';
- break;
-
- case FIELD_DATE:
- $sql .= 'VARCHAR(10) ';
- break;
-
- case FIELD_TEXT:
- $sql .= "BLOB SUB_TYPE TEXT";
- // ADD {$field_ident}_bbcode_uid VARCHAR(5) NOT NULL,
- // ADD {$field_ident}_bbcode_bitfield INTEGER UNSIGNED";
- break;
-
- case FIELD_BOOL:
- case FIELD_DROPDOWN:
- $sql .= 'INTEGER ';
- break;
-
- case FIELD_INT:
- $sql .= 'DOUBLE PRECISION ';
- break;
- }
+ $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . ' ADD "' . strtoupper($field_ident) . '" ' . $sql_type;
break;
case 'oracle':
-
- // We are defining the biggest common value, because of the possibility to edit the min/max values of each field.
- $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD $field_ident ";
-
- switch ($field_type)
- {
- case FIELD_STRING:
- $sql .= ' VARCHAR2(255) ';
- break;
-
- case FIELD_DATE:
- $sql .= 'VARCHAR2(10) ';
- break;
-
- case FIELD_TEXT:
- $sql .= "CLOB";
- // ADD {$field_ident}_bbcode_uid VARCHAR2(5) NOT NULL,
- // ADD {$field_ident}_bbcode_bitfield NUMBER(11) UNSIGNED";
- break;
-
- case FIELD_BOOL:
- $sql .= 'NUMBER(2) ';
- break;
-
- case FIELD_DROPDOWN:
- $sql .= 'NUMBER(8) ';
- break;
-
- case FIELD_INT:
- $sql .= 'NUMBER(20) ';
- break;
- }
+ $sql = 'ALTER TABLE ' . PROFILE_FIELDS_DATA_TABLE . " ADD $field_ident " . $sql_type;
break;
}
diff --git a/phpBB/includes/acp/acp_reasons.php b/phpBB/includes/acp/acp_reasons.php
index 71e9108c2c..569bb73ab0 100644
--- a/phpBB/includes/acp/acp_reasons.php
+++ b/phpBB/includes/acp/acp_reasons.php
@@ -26,6 +26,7 @@ class acp_reasons
{
global $db, $user, $auth, $template, $cache;
global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx;
+ global $request;
$user->add_lang(array('mcp', 'acp/posting'));
@@ -280,7 +281,18 @@ class acp_reasons
case 'move_up':
case 'move_down':
- $order = request_var('order', 0);
+ $sql = 'SELECT reason_order
+ FROM ' . REPORTS_REASONS_TABLE . "
+ WHERE reason_id = $reason_id";
+ $result = $db->sql_query($sql);
+ $order = $db->sql_fetchfield('reason_order');
+ $db->sql_freeresult($result);
+
+ if ($order === false || ($order == 0 && $action == 'move_up'))
+ {
+ break;
+ }
+ $order = (int) $order;
$order_total = $order * 2 + (($action == 'move_up') ? -1 : 1);
$sql = 'UPDATE ' . REPORTS_REASONS_TABLE . '
@@ -288,6 +300,13 @@ class acp_reasons
WHERE reason_order IN (' . $order . ', ' . (($action == 'move_up') ? $order - 1 : $order + 1) . ')';
$db->sql_query($sql);
+ if ($request->is_ajax())
+ {
+ $json_response = new \phpbb\json_response;
+ $json_response->send(array(
+ 'success' => (bool) $db->sql_affectedrows(),
+ ));
+ }
break;
}
@@ -363,8 +382,8 @@ class acp_reasons
'U_EDIT' => $this->u_action . '&amp;action=edit&amp;id=' . $row['reason_id'],
'U_DELETE' => (!$other_reason) ? $this->u_action . '&amp;action=delete&amp;id=' . $row['reason_id'] : '',
- 'U_MOVE_UP' => $this->u_action . '&amp;action=move_up&amp;order=' . $row['reason_order'],
- 'U_MOVE_DOWN' => $this->u_action . '&amp;action=move_down&amp;order=' . $row['reason_order'])
+ 'U_MOVE_UP' => $this->u_action . '&amp;action=move_up&amp;id=' . $row['reason_id'],
+ 'U_MOVE_DOWN' => $this->u_action . '&amp;action=move_down&amp;id=' . $row['reason_id'])
);
}
$db->sql_freeresult($result);
diff --git a/phpBB/includes/acp/acp_users.php b/phpBB/includes/acp/acp_users.php
index b24adfc586..bf11e4f8fe 100644
--- a/phpBB/includes/acp/acp_users.php
+++ b/phpBB/includes/acp/acp_users.php
@@ -824,9 +824,12 @@ class acp_users
$error[] = 'FORM_INVALID';
}
+ // Instantiate passwords manager
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+
// 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($data['new_password'], $user_row['user_password'])) ? true : false;
+ $update_password = $data['new_password'] && !$passwords_manager->check($data['new_password'], $user_row['user_password']);
$update_email = ($data['email'] != $user_row['user_email']) ? $data['email'] : false;
if (!sizeof($error))
@@ -910,7 +913,7 @@ class acp_users
if ($update_password)
{
$sql_ary += array(
- 'user_password' => phpbb_hash($data['new_password']),
+ 'user_password' => $passwords_manager->hash($data['new_password']),
'user_passchg' => time(),
'user_pass_convert' => 0,
);
@@ -1343,9 +1346,8 @@ class acp_users
case 'profile':
include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
- include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
$cp_data = $cp_error = array();
diff --git a/phpBB/includes/db/schema_data.php b/phpBB/includes/db/schema_data.php
index 69d39e0f8c..2c1070f459 100644
--- a/phpBB/includes/db/schema_data.php
+++ b/phpBB/includes/db/schema_data.php
@@ -318,7 +318,7 @@ $schema_data['phpbb_forums'] = array(
'forum_desc_options' => array('UINT:11', 7),
'forum_desc_uid' => array('VCHAR:8', ''),
'forum_link' => array('VCHAR_UNI', ''),
- 'forum_password' => array('VCHAR_UNI:40', ''),
+ 'forum_password' => array('VCHAR_UNI', ''),
'forum_style' => array('UINT', 0),
'forum_image' => array('VCHAR', ''),
'forum_rules' => array('TEXT_UNI', ''),
@@ -758,7 +758,7 @@ $schema_data['phpbb_profile_fields'] = array(
'COLUMNS' => array(
'field_id' => array('UINT', NULL, 'auto_increment'),
'field_name' => array('VCHAR_UNI', ''),
- 'field_type' => array('TINT:4', 0),
+ 'field_type' => array('VCHAR:100', ''),
'field_ident' => array('VCHAR:20', ''),
'field_length' => array('VCHAR:20', ''),
'field_minlen' => array('VCHAR', ''),
@@ -796,7 +796,7 @@ $schema_data['phpbb_profile_fields_lang'] = array(
'field_id' => array('UINT', 0),
'lang_id' => array('UINT', 0),
'option_id' => array('UINT', 0),
- 'field_type' => array('TINT:4', 0),
+ 'field_type' => array('VCHAR:100', ''),
'lang_value' => array('VCHAR_UNI', ''),
),
'PRIMARY_KEY' => array('field_id', 'lang_id', 'option_id'),
@@ -1112,7 +1112,7 @@ $schema_data['phpbb_users'] = array(
'user_regdate' => array('TIMESTAMP', 0),
'username' => array('VCHAR_CI', ''),
'username_clean' => array('VCHAR_CI', ''),
- 'user_password' => array('VCHAR_UNI:40', ''),
+ 'user_password' => array('VCHAR_UNI', ''),
'user_passchg' => array('TIMESTAMP', 0),
'user_pass_convert' => array('BOOL', 0),
'user_email' => array('VCHAR_UNI:100', ''),
diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php
index 0d6c7be117..689a682de3 100644
--- a/phpBB/includes/functions.php
+++ b/phpBB/includes/functions.php
@@ -368,73 +368,27 @@ function still_on_time($extra_time = 15)
}
/**
+* Hash the password
*
-* @version Version 0.1 / slightly modified for phpBB 3.1.x (using $H$ as hash type identifier)
-*
-* Portable PHP password hashing framework.
-*
-* Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
-* the public domain.
-*
-* There's absolutely no warranty.
-*
-* The homepage URL for this framework is:
-*
-* http://www.openwall.com/phpass/
-*
-* Please be sure to update the Version line if you edit this file in any way.
-* It is suggested that you leave the main version number intact, but indicate
-* your project name (after the slash) and add your own revision information.
-*
-* Please do not change the "private" password hashing method implemented in
-* here, thereby making your hashes incompatible. However, if you must, please
-* change the hash type identifier (the "$P$") to something different.
-*
-* Obviously, since this code is in the public domain, the above are not
-* requirements (there can be none), but merely suggestions.
+* @deprecated 3.1.0-a2 (To be removed: 3.3.0)
*
+* @param string $password Password to be hashed
*
-* Hash the password
+* @return string|bool Password hash or false if something went wrong during hashing
*/
function phpbb_hash($password)
{
- $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
-
- $random_state = unique_id();
- $random = '';
- $count = 6;
-
- if (($fh = @fopen('/dev/urandom', 'rb')))
- {
- $random = fread($fh, $count);
- fclose($fh);
- }
-
- if (strlen($random) < $count)
- {
- $random = '';
+ global $phpbb_container;
- for ($i = 0; $i < $count; $i += 16)
- {
- $random_state = md5(unique_id() . $random_state);
- $random .= pack('H*', md5($random_state));
- }
- $random = substr($random, 0, $count);
- }
-
- $hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64);
-
- if (strlen($hash) == 34)
- {
- return $hash;
- }
-
- return md5($password);
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+ return $passwords_manager->hash($password);
}
/**
* Check for correct password
*
+* @deprecated 3.1.0-a2 (To be removed: 3.3.0)
+*
* @param string $password The password in plain text
* @param string $hash The stored password hash
*
@@ -442,130 +396,10 @@ function phpbb_hash($password)
*/
function phpbb_check_hash($password, $hash)
{
- if (strlen($password) > 4096)
- {
- // If the password is too huge, we will simply reject it
- // and not let the server try to hash it.
- return false;
- }
-
- $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
- if (strlen($hash) == 34)
- {
- return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
- }
-
- return (md5($password) === $hash) ? true : false;
-}
-
-/**
-* Generate salt for hash generation
-*/
-function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
-{
- if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
- {
- $iteration_count_log2 = 8;
- }
-
- $output = '$H$';
- $output .= $itoa64[min($iteration_count_log2 + 5, 30)];
- $output .= _hash_encode64($input, 6, $itoa64);
-
- return $output;
-}
-
-/**
-* Encode hash
-*/
-function _hash_encode64($input, $count, &$itoa64)
-{
- $output = '';
- $i = 0;
-
- do
- {
- $value = ord($input[$i++]);
- $output .= $itoa64[$value & 0x3f];
-
- if ($i < $count)
- {
- $value |= ord($input[$i]) << 8;
- }
-
- $output .= $itoa64[($value >> 6) & 0x3f];
-
- if ($i++ >= $count)
- {
- break;
- }
-
- if ($i < $count)
- {
- $value |= ord($input[$i]) << 16;
- }
-
- $output .= $itoa64[($value >> 12) & 0x3f];
-
- if ($i++ >= $count)
- {
- break;
- }
-
- $output .= $itoa64[($value >> 18) & 0x3f];
- }
- while ($i < $count);
-
- return $output;
-}
-
-/**
-* The crypt function/replacement
-*/
-function _hash_crypt_private($password, $setting, &$itoa64)
-{
- $output = '*';
-
- // Check for correct hash
- if (substr($setting, 0, 3) != '$H$' && substr($setting, 0, 3) != '$P$')
- {
- return $output;
- }
-
- $count_log2 = strpos($itoa64, $setting[3]);
+ global $phpbb_container;
- if ($count_log2 < 7 || $count_log2 > 30)
- {
- return $output;
- }
-
- $count = 1 << $count_log2;
- $salt = substr($setting, 4, 8);
-
- if (strlen($salt) != 8)
- {
- return $output;
- }
-
- /**
- * We're kind of forced to use MD5 here since it's the only
- * cryptographic primitive available in all versions of PHP
- * currently in use. To implement our own low-level crypto
- * in PHP would result in much worse performance and
- * consequently in lower iteration counts and hashes that are
- * quicker to crack (by non-PHP code).
- */
- $hash = md5($salt . $password, true);
- do
- {
- $hash = md5($hash . $password, true);
- }
- while (--$count);
-
- $output = substr($setting, 0, 12);
- $output .= _hash_encode64($hash, 16, $itoa64);
-
- return $output;
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+ return $passwords_manager->check($password, $hash);
}
/**
@@ -2434,7 +2268,7 @@ function generate_board_url($without_script_path = false)
*/
function redirect($url, $return = false, $disable_cd_check = false)
{
- global $db, $cache, $config, $user, $phpbb_root_path;
+ global $db, $cache, $config, $user, $phpbb_root_path, $phpbb_filesystem, $phpbb_path_helper, $phpEx;
$failover_flag = false;
@@ -2477,78 +2311,34 @@ function redirect($url, $return = false, $disable_cd_check = false)
// Relative uri
$pathinfo = pathinfo($url);
- if (!$disable_cd_check && !file_exists($pathinfo['dirname'] . '/'))
+ // Is the uri pointing to the current directory?
+ if ($pathinfo['dirname'] == '.')
{
- $url = str_replace('../', '', $url);
- $pathinfo = pathinfo($url);
+ $url = str_replace('./', '', $url);
- if (!file_exists($pathinfo['dirname'] . '/'))
+ // Strip / from the beginning
+ if ($url && substr($url, 0, 1) == '/')
{
- // fallback to "last known user page"
- // at least this way we know the user does not leave the phpBB root
- $url = generate_board_url() . '/' . $user->page['page'];
- $failover_flag = true;
+ $url = substr($url, 1);
}
}
- if (!$failover_flag)
- {
- // Is the uri pointing to the current directory?
- if ($pathinfo['dirname'] == '.')
- {
- $url = str_replace('./', '', $url);
-
- // Strip / from the beginning
- if ($url && substr($url, 0, 1) == '/')
- {
- $url = substr($url, 1);
- }
-
- if ($user->page['page_dir'])
- {
- $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . $url;
- }
- else
- {
- $url = generate_board_url() . '/' . $url;
- }
- }
- else
- {
- // Used ./ before, but $phpbb_root_path is working better with urls within another root path
- $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path)));
- $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
- $intersection = array_intersect_assoc($root_dirs, $page_dirs);
-
- $root_dirs = array_diff_assoc($root_dirs, $intersection);
- $page_dirs = array_diff_assoc($page_dirs, $intersection);
+ $url = $phpbb_path_helper->remove_web_root_path($url);
- $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
-
- // Strip / from the end
- if ($dir && substr($dir, -1, 1) == '/')
- {
- $dir = substr($dir, 0, -1);
- }
+ if ($user->page['page_dir'])
+ {
+ $url = $user->page['page_dir'] . '/' . $url;
+ }
- // Strip / from the beginning
- if ($dir && substr($dir, 0, 1) == '/')
- {
- $dir = substr($dir, 1);
- }
+ $url = generate_board_url() . '/' . $url;
+ }
- $url = str_replace($pathinfo['dirname'] . '/', '', $url);
+ // Clean URL and check if we go outside the forum directory
+ $url = $phpbb_path_helper->clean_url($url);
- // Strip / from the beginning
- if (substr($url, 0, 1) == '/')
- {
- $url = substr($url, 1);
- }
-
- $url = (!empty($dir) ? $dir . '/' : '') . $url;
- $url = generate_board_url() . '/' . $url;
- }
- }
+ if (!$disable_cd_check && strpos($url, generate_board_url(true)) === false)
+ {
+ trigger_error('INSECURE_REDIRECT', E_USER_ERROR);
}
// Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
@@ -3250,9 +3040,9 @@ function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = fa
*/
function login_forum_box($forum_data)
{
- global $db, $config, $user, $template, $phpEx;
+ global $db, $phpbb_container, $request, $template, $user;
- $password = request_var('password', '', true);
+ $password = $request->variable('password', '', true);
$sql = 'SELECT forum_id
FROM ' . FORUMS_ACCESS_TABLE . '
@@ -3293,7 +3083,9 @@ function login_forum_box($forum_data)
}
$db->sql_freeresult($result);
- if (phpbb_check_hash($password, $forum_data['forum_password']))
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+
+ if ($passwords_manager->check($password, $forum_data['forum_password']))
{
$sql_ary = array(
'forum_id' => (int) $forum_data['forum_id'],
@@ -5099,7 +4891,7 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
}
$hidden_fields_for_jumpbox = phpbb_build_hidden_fields_for_query_params($request, array('f'));
-
+ $notification_mark_hash = generate_link_hash('mark_all_notifications_read');
// The following assigns all _common_ variables that may be used at any point in a template.
$template->assign_vars(array(
@@ -5119,6 +4911,7 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
'UNREAD_NOTIFICATIONS_COUNT' => ($notifications !== false) ? $notifications['unread_count'] : '',
'NOTIFICATIONS_COUNT' => ($notifications !== false) ? $notifications['unread_count'] : '',
'U_VIEW_ALL_NOTIFICATIONS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications'),
+ 'U_MARK_ALL_NOTIFICATIONS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications&amp;mode=notification_list&amp;mark=all&amp;token=' . $notification_mark_hash),
'U_NOTIFICATION_SETTINGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications&amp;mode=notification_options'),
'S_NOTIFICATIONS_DISPLAY' => $config['load_notifications'],
diff --git a/phpBB/includes/functions_module.php b/phpBB/includes/functions_module.php
index e1259eba12..53055752f6 100644
--- a/phpBB/includes/functions_module.php
+++ b/phpBB/includes/functions_module.php
@@ -334,7 +334,7 @@ class p_master
static function module_auth($module_auth, $forum_id)
{
global $auth, $config;
- global $request;
+ global $request, $phpbb_extension_manager, $phpbb_dispatcher;
$module_auth = trim($module_auth);
@@ -351,6 +351,30 @@ class p_master
[(),] |
[^\s(),]+)/x', $module_auth, $match);
+ // Valid tokens for auth and their replacements
+ $valid_tokens = array(
+ 'acl_([a-z0-9_]+)(,\$id)?' => '(int) $auth->acl_get(\'\\1\'\\2)',
+ '\$id' => '(int) $forum_id',
+ 'aclf_([a-z0-9_]+)' => '(int) $auth->acl_getf_global(\'\\1\')',
+ 'cfg_([a-z0-9_]+)' => '(int) $config[\'\\1\']',
+ 'request_([a-zA-Z0-9_]+)' => '$request->variable(\'\\1\', false)',
+ 'ext_([a-zA-Z0-9_/]+)' => 'array_key_exists(\'\\1\', $phpbb_extension_manager->all_enabled())',
+ );
+
+ /**
+ * Alter tokens for module authorisation check
+ *
+ * @event core.module_auth
+ * @var array valid_tokens Valid tokens and their auth check
+ * replacements
+ * @var string module_auth The module_auth of the current
+ * module
+ * @var int forum_id The current forum_id
+ * @since 3.1-A3
+ */
+ $vars = array('valid_tokens', 'module_auth', 'forum_id');
+ extract($phpbb_dispatcher->trigger_event('core.module_auth', compact($vars)));
+
$tokens = $match[0];
for ($i = 0, $size = sizeof($tokens); $i < $size; $i++)
{
@@ -366,7 +390,7 @@ class p_master
break;
default:
- if (!preg_match('#(?:acl_([a-z0-9_]+)(,\$id)?)|(?:\$id)|(?:aclf_([a-z0-9_]+))|(?:cfg_([a-z0-9_]+))|(?:request_([a-zA-Z0-9_]+))#', $token))
+ if (!preg_match('#(?:' . implode(array_keys($valid_tokens), ')|(?:') . ')#', $token))
{
$token = '';
}
@@ -379,8 +403,17 @@ class p_master
// Make sure $id separation is working fine
$module_auth = str_replace(' , ', ',', $module_auth);
+ $module_auth = preg_replace(
+ // Array keys with # prepended/appended
+ array_map(function($value) {
+ return '#' . $value . '#';
+ }, array_keys($valid_tokens)),
+ array_values($valid_tokens),
+ $module_auth
+ );
+
$is_auth = false;
- eval('$is_auth = (int) (' . preg_replace(array('#acl_([a-z0-9_]+)(,\$id)?#', '#\$id#', '#aclf_([a-z0-9_]+)#', '#cfg_([a-z0-9_]+)#', '#request_([a-zA-Z0-9_]+)#'), array('(int) $auth->acl_get(\'\\1\'\\2)', '(int) $forum_id', '(int) $auth->acl_getf_global(\'\\1\')', '(int) $config[\'\\1\']', '$request->variable(\'\\1\', false)'), $module_auth) . ');');
+ eval('$is_auth = (int) (' . $module_auth . ');');
return $is_auth;
}
diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php
index 172f4403ac..e14e9e27be 100644
--- a/phpBB/includes/functions_posting.php
+++ b/phpBB/includes/functions_posting.php
@@ -862,6 +862,7 @@ function posting_gen_attachment_entry($attachment_data, &$filename_data, $show_a
'ATTACH_ID' => $attach_row['attach_id'],
'S_IS_ORPHAN' => $attach_row['is_orphan'],
'ASSOC_INDEX' => $count,
+ 'FILESIZE' => get_formatted_filesize($attach_row['filesize']),
'U_VIEW_ATTACHMENT' => $download_link,
'S_HIDDEN' => $hidden)
diff --git a/phpBB/includes/functions_profile_fields.php b/phpBB/includes/functions_profile_fields.php
deleted file mode 100644
index 7dd0b0e87d..0000000000
--- a/phpBB/includes/functions_profile_fields.php
+++ /dev/null
@@ -1,1187 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-/**
-* @ignore
-*/
-if (!defined('IN_PHPBB'))
-{
- exit;
-}
-
-/**
-* Custom Profile Fields
-* @package phpBB3
-*/
-class custom_profile
-{
- var $profile_types = array(FIELD_INT => 'int', FIELD_STRING => 'string', FIELD_TEXT => 'text', FIELD_BOOL => 'bool', FIELD_DROPDOWN => 'dropdown', FIELD_DATE => 'date');
- var $profile_cache = array();
- var $options_lang = array();
-
- /**
- * Assign editable fields to template, mode can be profile (for profile change) or register (for registration)
- * Called by ucp_profile and ucp_register
- * @access public
- */
- function generate_profile_fields($mode, $lang_id)
- {
- global $db, $template, $auth;
-
- $sql_where = '';
- switch ($mode)
- {
- case 'register':
- // If the field is required we show it on the registration page
- $sql_where .= ' AND f.field_show_on_reg = 1';
- break;
-
- case 'profile':
- // Show hidden fields to moderators/admins
- if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
- {
- $sql_where .= ' AND f.field_show_profile = 1';
- }
- break;
-
- default:
- trigger_error('Wrong profile mode specified', E_USER_ERROR);
- break;
- }
-
- $sql = 'SELECT l.*, f.*
- FROM ' . PROFILE_LANG_TABLE . ' l, ' . PROFILE_FIELDS_TABLE . " f
- WHERE f.field_active = 1
- $sql_where
- AND l.lang_id = $lang_id
- AND l.field_id = f.field_id
- ORDER BY f.field_order";
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
- {
- // Return templated field
- $tpl_snippet = $this->process_field_row('change', $row);
-
- // Some types are multivalue, we can't give them a field_id as we would not know which to pick
- $type = (int) $row['field_type'];
-
- $template->assign_block_vars('profile_fields', array(
- 'LANG_NAME' => $row['lang_name'],
- 'LANG_EXPLAIN' => $row['lang_explain'],
- 'FIELD' => $tpl_snippet,
- 'FIELD_ID' => ($type == FIELD_DATE || ($type == FIELD_BOOL && $row['field_length'] == '1')) ? '' : 'pf_' . $row['field_ident'],
- 'S_REQUIRED' => ($row['field_required']) ? true : false)
- );
- }
- $db->sql_freeresult($result);
- }
-
- /**
- * Validate entered profile field data
- * @access public
- */
- function validate_profile_field($field_type, &$field_value, $field_data)
- {
- switch ($field_type)
- {
- case FIELD_DATE:
- $field_validate = explode('-', $field_value);
-
- $day = (isset($field_validate[0])) ? (int) $field_validate[0] : 0;
- $month = (isset($field_validate[1])) ? (int) $field_validate[1] : 0;
- $year = (isset($field_validate[2])) ? (int) $field_validate[2] : 0;
-
- if ((!$day || !$month || !$year) && !$field_data['field_required'])
- {
- return false;
- }
-
- if ((!$day || !$month || !$year) && $field_data['field_required'])
- {
- return 'FIELD_REQUIRED';
- }
-
- if ($day < 0 || $day > 31 || $month < 0 || $month > 12 || ($year < 1901 && $year > 0) || $year > gmdate('Y', time()) + 50)
- {
- return 'FIELD_INVALID_DATE';
- }
-
- if (checkdate($month, $day, $year) === false)
- {
- return 'FIELD_INVALID_DATE';
- }
- break;
-
- case FIELD_BOOL:
- $field_value = (bool) $field_value;
-
- if (!$field_value && $field_data['field_required'])
- {
- return 'FIELD_REQUIRED';
- }
- break;
-
- case FIELD_INT:
- if (trim($field_value) === '' && !$field_data['field_required'])
- {
- return false;
- }
-
- $field_value = (int) $field_value;
-
- if ($field_value < $field_data['field_minlen'])
- {
- return 'FIELD_TOO_SMALL';
- }
- else if ($field_value > $field_data['field_maxlen'])
- {
- return 'FIELD_TOO_LARGE';
- }
- break;
-
- case FIELD_DROPDOWN:
- $field_value = (int) $field_value;
-
- // retrieve option lang data if necessary
- if (!isset($this->options_lang[$field_data['field_id']]) || !isset($this->options_lang[$field_data['field_id']][$field_data['lang_id']]) || !sizeof($this->options_lang[$file_data['field_id']][$field_data['lang_id']]))
- {
- $this->get_option_lang($field_data['field_id'], $field_data['lang_id'], FIELD_DROPDOWN, false);
- }
-
- if (!isset($this->options_lang[$field_data['field_id']][$field_data['lang_id']][$field_value]))
- {
- return 'FIELD_INVALID_VALUE';
- }
-
- if ($field_value == $field_data['field_novalue'] && $field_data['field_required'])
- {
- return 'FIELD_REQUIRED';
- }
- break;
-
- case FIELD_STRING:
- case FIELD_TEXT:
- if (trim($field_value) === '' && !$field_data['field_required'])
- {
- return false;
- }
- else if (trim($field_value) === '' && $field_data['field_required'])
- {
- return 'FIELD_REQUIRED';
- }
-
- if ($field_data['field_minlen'] && utf8_strlen($field_value) < $field_data['field_minlen'])
- {
- return 'FIELD_TOO_SHORT';
- }
- else if ($field_data['field_maxlen'] && utf8_strlen($field_value) > $field_data['field_maxlen'])
- {
- return 'FIELD_TOO_LONG';
- }
-
- if (!empty($field_data['field_validation']) && $field_data['field_validation'] != '.*')
- {
- $field_validate = ($field_type == FIELD_STRING) ? $field_value : bbcode_nl2br($field_value);
- if (!preg_match('#^' . str_replace('\\\\', '\\', $field_data['field_validation']) . '$#i', $field_validate))
- {
- return 'FIELD_INVALID_CHARS';
- }
- }
- break;
- }
-
- return false;
- }
-
- /**
- * Build profile cache, used for display
- * @access private
- */
- function build_cache()
- {
- global $db, $user, $auth;
-
- $this->profile_cache = array();
-
- // Display hidden/no_view fields for admin/moderator
- $sql = 'SELECT l.*, f.*
- FROM ' . PROFILE_LANG_TABLE . ' l, ' . PROFILE_FIELDS_TABLE . ' f
- WHERE l.lang_id = ' . $user->get_iso_lang_id() . '
- AND f.field_active = 1 ' .
- ((!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) ? ' AND f.field_hide = 0 ' : '') . '
- AND f.field_no_view = 0
- AND l.field_id = f.field_id
- ORDER BY f.field_order';
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
- {
- $this->profile_cache[$row['field_ident']] = $row;
- }
- $db->sql_freeresult($result);
- }
-
- /**
- * Get language entries for options and store them here for later use
- */
- function get_option_lang($field_id, $lang_id, $field_type, $preview)
- {
- global $db;
-
- if ($preview)
- {
- $lang_options = (!is_array($this->vars['lang_options'])) ? explode("\n", $this->vars['lang_options']) : $this->vars['lang_options'];
-
- foreach ($lang_options as $num => $var)
- {
- $this->options_lang[$field_id][$lang_id][($num + 1)] = $var;
- }
- }
- else
- {
- $sql = 'SELECT option_id, lang_value
- FROM ' . PROFILE_FIELDS_LANG_TABLE . "
- WHERE field_id = $field_id
- AND lang_id = $lang_id
- AND field_type = $field_type
- ORDER BY option_id";
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
- {
- $this->options_lang[$field_id][$lang_id][($row['option_id'] + 1)] = $row['lang_value'];
- }
- $db->sql_freeresult($result);
- }
- }
-
- /**
- * Submit profile field for validation
- * @access public
- */
- function submit_cp_field($mode, $lang_id, &$cp_data, &$cp_error)
- {
- global $auth, $db, $user;
-
- $sql_where = '';
- switch ($mode)
- {
- case 'register':
- // If the field is required we show it on the registration page
- $sql_where .= ' AND f.field_show_on_reg = 1';
- break;
-
- case 'profile':
- // Show hidden fields to moderators/admins
- if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
- {
- $sql_where .= ' AND f.field_show_profile = 1';
- }
- break;
-
- default:
- trigger_error('Wrong profile mode specified', E_USER_ERROR);
- break;
- }
-
- $sql = 'SELECT l.*, f.*
- FROM ' . PROFILE_LANG_TABLE . ' l, ' . PROFILE_FIELDS_TABLE . " f
- WHERE l.lang_id = $lang_id
- AND f.field_active = 1
- $sql_where
- AND l.field_id = f.field_id
- ORDER BY f.field_order";
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
- {
- $cp_data['pf_' . $row['field_ident']] = $this->get_profile_field($row);
- $check_value = $cp_data['pf_' . $row['field_ident']];
-
- if (($cp_result = $this->validate_profile_field($row['field_type'], $check_value, $row)) !== false)
- {
- // If not and only showing common error messages, use this one
- $error = '';
- switch ($cp_result)
- {
- case 'FIELD_INVALID_DATE':
- case 'FIELD_INVALID_VALUE':
- case 'FIELD_REQUIRED':
- $error = $user->lang($cp_result, $row['lang_name']);
- break;
-
- case 'FIELD_TOO_SHORT':
- case 'FIELD_TOO_SMALL':
- $error = $user->lang($cp_result, (int) $row['field_minlen'], $row['lang_name']);
- break;
-
- case 'FIELD_TOO_LONG':
- case 'FIELD_TOO_LARGE':
- $error = $user->lang($cp_result, (int) $row['field_maxlen'], $row['lang_name']);
- break;
-
- case 'FIELD_INVALID_CHARS':
- switch ($row['field_validation'])
- {
- case '[0-9]+':
- $error = $user->lang($cp_result . '_NUMBERS_ONLY', $row['lang_name']);
- break;
-
- case '[\w]+':
- $error = $user->lang($cp_result . '_ALPHA_ONLY', $row['lang_name']);
- break;
-
- case '[\w_\+\. \-\[\]]+':
- $error = $user->lang($cp_result . '_SPACERS_ONLY', $row['lang_name']);
- break;
- }
- break;
- }
-
- if ($error != '')
- {
- $cp_error[] = $error;
- }
- }
- }
- $db->sql_freeresult($result);
- }
-
- /**
- * Update profile field data directly
- */
- function update_profile_field_data($user_id, &$cp_data)
- {
- global $db;
-
- if (!sizeof($cp_data))
- {
- return;
- }
-
- switch ($db->sql_layer)
- {
- case 'oracle':
- case 'firebird':
- case 'postgres':
- $right_delim = $left_delim = '"';
- break;
-
- case 'sqlite':
- case 'mssql':
- case 'mssql_odbc':
- case 'mssqlnative':
- $right_delim = ']';
- $left_delim = '[';
- break;
-
- case 'mysql':
- case 'mysql4':
- case 'mysqli':
- $right_delim = $left_delim = '`';
- break;
- }
-
- // use new array for the UPDATE; changes in the key do not affect the original array
- $cp_data_sql = array();
- foreach ($cp_data as $key => $value)
- {
- // Firebird is case sensitive with delimiter
- $cp_data_sql[$left_delim . (($db->sql_layer == 'firebird' || $db->sql_layer == 'oracle') ? strtoupper($key) : $key) . $right_delim] = $value;
- }
-
- $sql = 'UPDATE ' . PROFILE_FIELDS_DATA_TABLE . '
- SET ' . $db->sql_build_array('UPDATE', $cp_data_sql) . "
- WHERE user_id = $user_id";
- $db->sql_query($sql);
-
- if (!$db->sql_affectedrows())
- {
- $cp_data_sql['user_id'] = (int) $user_id;
-
- $db->sql_return_on_error(true);
-
- $sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' . $db->sql_build_array('INSERT', $cp_data_sql);
- $db->sql_query($sql);
-
- $db->sql_return_on_error(false);
- }
- }
-
- /**
- * Assign fields to template, used for viewprofile, viewtopic and memberlist (if load setting is enabled)
- * This is directly connected to the user -> mode == grab is to grab the user specific fields, mode == show is for assigning the row to the template
- * @access public
- */
- function generate_profile_fields_template($mode, $user_id = 0, $profile_row = false)
- {
- global $db;
-
- if ($mode == 'grab')
- {
- if (!is_array($user_id))
- {
- $user_id = array($user_id);
- }
-
- if (!sizeof($this->profile_cache))
- {
- $this->build_cache();
- }
-
- if (!sizeof($user_id))
- {
- return array();
- }
-
- $sql = 'SELECT *
- FROM ' . PROFILE_FIELDS_DATA_TABLE . '
- WHERE ' . $db->sql_in_set('user_id', array_map('intval', $user_id));
- $result = $db->sql_query($sql);
-
- $field_data = array();
- while ($row = $db->sql_fetchrow($result))
- {
- $field_data[$row['user_id']] = $row;
- }
- $db->sql_freeresult($result);
-
- $user_fields = array();
-
- $user_ids = $user_id;
-
- // Go through the fields in correct order
- foreach (array_keys($this->profile_cache) as $used_ident)
- {
- foreach ($field_data as $user_id => $row)
- {
- $user_fields[$user_id][$used_ident]['value'] = $row['pf_' . $used_ident];
- $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
- }
-
- foreach ($user_ids as $user_id)
- {
- if (!isset($user_fields[$user_id][$used_ident]) && $this->profile_cache[$used_ident]['field_show_novalue'])
- {
- $user_fields[$user_id][$used_ident]['value'] = '';
- $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
- }
- }
- }
-
- return $user_fields;
- }
- else if ($mode == 'show')
- {
- // $profile_row == $user_fields[$row['user_id']];
- $tpl_fields = array();
- $tpl_fields['row'] = $tpl_fields['blockrow'] = array();
-
- foreach ($profile_row as $ident => $ident_ary)
- {
- $value = $this->get_profile_value($ident_ary);
-
- if ($value === NULL)
- {
- continue;
- }
-
- $tpl_fields['row'] += array(
- 'PROFILE_' . strtoupper($ident) . '_VALUE' => $value,
- 'PROFILE_' . strtoupper($ident) . '_TYPE' => $ident_ary['data']['field_type'],
- 'PROFILE_' . strtoupper($ident) . '_NAME' => $ident_ary['data']['lang_name'],
- 'PROFILE_' . strtoupper($ident) . '_EXPLAIN'=> $ident_ary['data']['lang_explain'],
-
- 'S_PROFILE_' . strtoupper($ident) => true
- );
-
- $tpl_fields['blockrow'][] = array(
- 'PROFILE_FIELD_VALUE' => $value,
- 'PROFILE_FIELD_TYPE' => $ident_ary['data']['field_type'],
- 'PROFILE_FIELD_NAME' => $ident_ary['data']['lang_name'],
- 'PROFILE_FIELD_EXPLAIN' => $ident_ary['data']['lang_explain'],
-
- 'S_PROFILE_' . strtoupper($ident) => true
- );
- }
-
- return $tpl_fields;
- }
- else
- {
- trigger_error('Wrong mode for custom profile', E_USER_ERROR);
- }
- }
-
- /**
- * Get Profile Value for display
- */
- function get_profile_value($ident_ary)
- {
- $value = $ident_ary['value'];
- $field_type = $ident_ary['data']['field_type'];
-
- switch ($this->profile_types[$field_type])
- {
- case 'int':
- if ($value === '' && !$ident_ary['data']['field_show_novalue'])
- {
- return NULL;
- }
- return (int) $value;
- break;
-
- case 'string':
- case 'text':
- if (!$value && !$ident_ary['data']['field_show_novalue'])
- {
- return NULL;
- }
-
- $value = make_clickable($value);
- $value = censor_text($value);
- $value = bbcode_nl2br($value);
- return $value;
- break;
-
- // case 'datetime':
- case 'date':
- $date = explode('-', $value);
- $day = (isset($date[0])) ? (int) $date[0] : 0;
- $month = (isset($date[1])) ? (int) $date[1] : 0;
- $year = (isset($date[2])) ? (int) $date[2] : 0;
-
- if (!$day && !$month && !$year && !$ident_ary['data']['field_show_novalue'])
- {
- return NULL;
- }
- else if ($day && $month && $year)
- {
- global $user;
- // Date should display as the same date for every user regardless of timezone
-
- return $user->create_datetime()
- ->setDate($year, $month, $day)
- ->setTime(0, 0, 0)
- ->format($user->lang['DATE_FORMAT'], true);
- }
-
- return $value;
- break;
-
- case 'dropdown':
- $field_id = $ident_ary['data']['field_id'];
- $lang_id = $ident_ary['data']['lang_id'];
- if (!isset($this->options_lang[$field_id][$lang_id]))
- {
- $this->get_option_lang($field_id, $lang_id, FIELD_DROPDOWN, false);
- }
-
- if ($value == $ident_ary['data']['field_novalue'] && !$ident_ary['data']['field_show_novalue'])
- {
- return NULL;
- }
-
- $value = (int) $value;
-
- // User not having a value assigned
- if (!isset($this->options_lang[$field_id][$lang_id][$value]))
- {
- if ($ident_ary['data']['field_show_novalue'])
- {
- $value = $ident_ary['data']['field_novalue'];
- }
- else
- {
- return NULL;
- }
- }
-
- return $this->options_lang[$field_id][$lang_id][$value];
- break;
-
- case 'bool':
- $field_id = $ident_ary['data']['field_id'];
- $lang_id = $ident_ary['data']['lang_id'];
- if (!isset($this->options_lang[$field_id][$lang_id]))
- {
- $this->get_option_lang($field_id, $lang_id, FIELD_BOOL, false);
- }
-
- if (!$value && $ident_ary['data']['field_show_novalue'])
- {
- $value = $ident_ary['data']['field_default_value'];
- }
-
- if ($ident_ary['data']['field_length'] == 1)
- {
- return (isset($this->options_lang[$field_id][$lang_id][(int) $value])) ? $this->options_lang[$field_id][$lang_id][(int) $value] : NULL;
- }
- else if (!$value)
- {
- return NULL;
- }
- else
- {
- return $this->options_lang[$field_id][$lang_id][(int) ($value) + 1];
- }
- break;
-
- default:
- trigger_error('Unknown profile type', E_USER_ERROR);
- break;
- }
- }
-
- /**
- * Get field value for registration/profile
- * @access private
- */
- function get_var($field_validation, &$profile_row, $default_value, $preview)
- {
- global $user;
- global $request;
-
- $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
- $user_ident = $profile_row['field_ident'];
- // checkbox - set the value to "true" if it has been set to 1
- if ($profile_row['field_type'] == FIELD_BOOL && $profile_row['field_length'] == 2)
- {
- $value = (isset($_REQUEST[$profile_row['field_ident']]) && request_var($profile_row['field_ident'], $default_value) == 1) ? true : ((!isset($user->profile_fields[$user_ident]) || $preview) ? $default_value : $user->profile_fields[$user_ident]);
- }
- else if ($profile_row['field_type'] == FIELD_INT)
- {
- if (isset($_REQUEST[$profile_row['field_ident']]))
- {
- $value = ($request->variable($profile_row['field_ident'], '') === '') ? NULL : $request->variable($profile_row['field_ident'], $default_value);
- }
- else
- {
- if (!$preview && array_key_exists($user_ident, $user->profile_fields) && is_null($user->profile_fields[$user_ident]))
- {
- $value = NULL;
- }
- else if (!isset($user->profile_fields[$user_ident]) || $preview)
- {
- $value = $default_value;
- }
- else
- {
- $value = $user->profile_fields[$user_ident];
- }
- }
-
- return (is_null($value) || $value === '') ? '' : (int) $value;
- }
- else
- {
- $value = (isset($_REQUEST[$profile_row['field_ident']])) ? request_var($profile_row['field_ident'], $default_value, true) : ((!isset($user->profile_fields[$user_ident]) || $preview) ? $default_value : $user->profile_fields[$user_ident]);
-
- if (gettype($value) == 'string')
- {
- $value = utf8_normalize_nfc($value);
- }
- }
-
- switch ($field_validation)
- {
- case 'int':
- return (int) $value;
- break;
- }
-
- return $value;
- }
-
- /**
- * Process int-type
- * @access private
- */
- function generate_int($profile_row, $preview = false)
- {
- global $template;
-
- $profile_row['field_value'] = $this->get_var('int', $profile_row, $profile_row['field_default_value'], $preview);
- $template->assign_block_vars($this->profile_types[$profile_row['field_type']], array_change_key_case($profile_row, CASE_UPPER));
- }
-
- /**
- * Process date-type
- * @access private
- */
- function generate_date($profile_row, $preview = false)
- {
- global $user, $template;
-
- $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
- $user_ident = $profile_row['field_ident'];
-
- $now = getdate();
-
- if (!isset($_REQUEST[$profile_row['field_ident'] . '_day']))
- {
- if ($profile_row['field_default_value'] == 'now')
- {
- $profile_row['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
- }
- list($day, $month, $year) = explode('-', ((!isset($user->profile_fields[$user_ident]) || $preview) ? $profile_row['field_default_value'] : $user->profile_fields[$user_ident]));
- }
- else
- {
- if ($preview && $profile_row['field_default_value'] == 'now')
- {
- $profile_row['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
- list($day, $month, $year) = explode('-', ((!isset($user->profile_fields[$user_ident]) || $preview) ? $profile_row['field_default_value'] : $user->profile_fields[$user_ident]));
- }
- else
- {
- $day = request_var($profile_row['field_ident'] . '_day', 0);
- $month = request_var($profile_row['field_ident'] . '_month', 0);
- $year = request_var($profile_row['field_ident'] . '_year', 0);
- }
- }
-
- $profile_row['s_day_options'] = '<option value="0"' . ((!$day) ? ' selected="selected"' : '') . '>--</option>';
- for ($i = 1; $i < 32; $i++)
- {
- $profile_row['s_day_options'] .= '<option value="' . $i . '"' . (($i == $day) ? ' selected="selected"' : '') . ">$i</option>";
- }
-
- $profile_row['s_month_options'] = '<option value="0"' . ((!$month) ? ' selected="selected"' : '') . '>--</option>';
- for ($i = 1; $i < 13; $i++)
- {
- $profile_row['s_month_options'] .= '<option value="' . $i . '"' . (($i == $month) ? ' selected="selected"' : '') . ">$i</option>";
- }
-
- $profile_row['s_year_options'] = '<option value="0"' . ((!$year) ? ' selected="selected"' : '') . '>--</option>';
- for ($i = $now['year'] - 100; $i <= $now['year'] + 100; $i++)
- {
- $profile_row['s_year_options'] .= '<option value="' . $i . '"' . (($i == $year) ? ' selected="selected"' : '') . ">$i</option>";
- }
- unset($now);
-
- $profile_row['field_value'] = 0;
- $template->assign_block_vars($this->profile_types[$profile_row['field_type']], array_change_key_case($profile_row, CASE_UPPER));
- }
-
- /**
- * Process bool-type
- * @access private
- */
- function generate_bool($profile_row, $preview = false)
- {
- global $template;
-
- $value = $this->get_var('int', $profile_row, $profile_row['field_default_value'], $preview);
-
- $profile_row['field_value'] = $value;
- $template->assign_block_vars($this->profile_types[$profile_row['field_type']], array_change_key_case($profile_row, CASE_UPPER));
-
- if ($profile_row['field_length'] == 1)
- {
- if (!isset($this->options_lang[$profile_row['field_id']][$profile_row['lang_id']]) || !sizeof($this->options_lang[$profile_row['field_id']][$profile_row['lang_id']]))
- {
- $this->get_option_lang($profile_row['field_id'], $profile_row['lang_id'], FIELD_BOOL, $preview);
- }
-
- foreach ($this->options_lang[$profile_row['field_id']][$profile_row['lang_id']] as $option_id => $option_value)
- {
- $template->assign_block_vars('bool.options', array(
- 'OPTION_ID' => $option_id,
- 'CHECKED' => ($value == $option_id) ? ' checked="checked"' : '',
- 'VALUE' => $option_value)
- );
- }
- }
- }
-
- /**
- * Process string-type
- * @access private
- */
- function generate_string($profile_row, $preview = false)
- {
- global $template;
-
- $profile_row['field_value'] = $this->get_var('string', $profile_row, $profile_row['lang_default_value'], $preview);
- $template->assign_block_vars($this->profile_types[$profile_row['field_type']], array_change_key_case($profile_row, CASE_UPPER));
- }
-
- /**
- * Process text-type
- * @access private
- */
- function generate_text($profile_row, $preview = false)
- {
- global $template;
- global $user, $phpEx, $phpbb_root_path;
-
- $field_length = explode('|', $profile_row['field_length']);
- $profile_row['field_rows'] = $field_length[0];
- $profile_row['field_cols'] = $field_length[1];
-
- $profile_row['field_value'] = $this->get_var('string', $profile_row, $profile_row['lang_default_value'], $preview);
- $template->assign_block_vars($this->profile_types[$profile_row['field_type']], array_change_key_case($profile_row, CASE_UPPER));
- }
-
- /**
- * Process dropdown-type
- * @access private
- */
- function generate_dropdown($profile_row, $preview = false)
- {
- global $user, $template;
-
- $value = $this->get_var('int', $profile_row, $profile_row['field_default_value'], $preview);
-
- if (!isset($this->options_lang[$profile_row['field_id']]) || !isset($this->options_lang[$profile_row['field_id']][$profile_row['lang_id']]) || !sizeof($this->options_lang[$profile_row['field_id']][$profile_row['lang_id']]))
- {
- $this->get_option_lang($profile_row['field_id'], $profile_row['lang_id'], FIELD_DROPDOWN, $preview);
- }
-
- $profile_row['field_value'] = $value;
- $template->assign_block_vars($this->profile_types[$profile_row['field_type']], array_change_key_case($profile_row, CASE_UPPER));
-
- foreach ($this->options_lang[$profile_row['field_id']][$profile_row['lang_id']] as $option_id => $option_value)
- {
- $template->assign_block_vars('dropdown.options', array(
- 'OPTION_ID' => $option_id,
- 'SELECTED' => ($value == $option_id) ? ' selected="selected"' : '',
- 'VALUE' => $option_value)
- );
- }
- }
-
- /**
- * Return Templated value/field. Possible values for $mode are:
- * change == user is able to set/enter profile values; preview == just show the value
- * @access private
- */
- function process_field_row($mode, $profile_row)
- {
- global $template;
-
- $preview = ($mode == 'preview') ? true : false;
-
- // set template filename
- $template->set_filenames(array(
- 'cp_body' => 'custom_profile_fields.html')
- );
-
- // empty previously filled blockvars
- foreach ($this->profile_types as $field_case => $field_type)
- {
- $template->destroy_block_vars($field_type);
- }
-
- // Assign template variables
- $type_func = 'generate_' . $this->profile_types[$profile_row['field_type']];
- $this->$type_func($profile_row, $preview);
-
- // Return templated data
- return $template->assign_display('cp_body');
- }
-
- /**
- * Build Array for user insertion into custom profile fields table
- */
- function build_insert_sql_array($cp_data)
- {
- global $db, $user, $auth;
-
- $sql_not_in = array();
- foreach ($cp_data as $key => $null)
- {
- $sql_not_in[] = (strncmp($key, 'pf_', 3) === 0) ? substr($key, 3) : $key;
- }
-
- $sql = 'SELECT f.field_type, f.field_ident, f.field_default_value, l.lang_default_value
- FROM ' . PROFILE_LANG_TABLE . ' l, ' . PROFILE_FIELDS_TABLE . ' f
- WHERE l.lang_id = ' . $user->get_iso_lang_id() . '
- ' . ((sizeof($sql_not_in)) ? ' AND ' . $db->sql_in_set('f.field_ident', $sql_not_in, true) : '') . '
- AND l.field_id = f.field_id';
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
- {
- if ($row['field_default_value'] == 'now' && $row['field_type'] == FIELD_DATE)
- {
- $now = getdate();
- $row['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
- }
- else if ($row['field_default_value'] === '' && $row['field_type'] == FIELD_INT)
- {
- // We cannot insert an empty string into an integer column.
- $row['field_default_value'] = NULL;
- }
-
- $cp_data['pf_' . $row['field_ident']] = (in_array($row['field_type'], array(FIELD_TEXT, FIELD_STRING))) ? $row['lang_default_value'] : $row['field_default_value'];
- }
- $db->sql_freeresult($result);
-
- return $cp_data;
- }
-
- /**
- * Get profile field value on submit
- * @access private
- */
- function get_profile_field($profile_row)
- {
- global $phpbb_root_path, $phpEx;
- global $config;
- global $request;
-
- $var_name = 'pf_' . $profile_row['field_ident'];
-
- switch ($profile_row['field_type'])
- {
- case FIELD_DATE:
-
- if (!isset($_REQUEST[$var_name . '_day']))
- {
- if ($profile_row['field_default_value'] == 'now')
- {
- $now = getdate();
- $profile_row['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
- }
- list($day, $month, $year) = explode('-', $profile_row['field_default_value']);
- }
- else
- {
- $day = request_var($var_name . '_day', 0);
- $month = request_var($var_name . '_month', 0);
- $year = request_var($var_name . '_year', 0);
- }
-
- $var = sprintf('%2d-%2d-%4d', $day, $month, $year);
- break;
-
- case FIELD_BOOL:
- // Checkbox
- if ($profile_row['field_length'] == 2)
- {
- $var = (isset($_REQUEST[$var_name])) ? 1 : 0;
- }
- else
- {
- $var = request_var($var_name, (int) $profile_row['field_default_value']);
- }
- break;
-
- case FIELD_STRING:
- case FIELD_TEXT:
- $var = utf8_normalize_nfc(request_var($var_name, (string) $profile_row['field_default_value'], true));
- break;
-
- case FIELD_INT:
- if (isset($_REQUEST[$var_name]) && $request->variable($var_name, '') === '')
- {
- $var = NULL;
- }
- else
- {
- $var = request_var($var_name, (int) $profile_row['field_default_value']);
- }
- break;
-
- case FIELD_DROPDOWN:
- $var = request_var($var_name, (int) $profile_row['field_default_value']);
- break;
-
- default:
- $var = request_var($var_name, $profile_row['field_default_value']);
- break;
- }
-
- return $var;
- }
-}
-
-/**
-* Custom Profile Fields ACP
-* @package phpBB3
-*/
-class custom_profile_admin extends custom_profile
-{
- var $vars = array();
-
- /**
- * Return possible validation options
- */
- function validate_options()
- {
- global $user;
-
- $validate_ary = array('CHARS_ANY' => '.*', 'NUMBERS_ONLY' => '[0-9]+', 'ALPHA_ONLY' => '[\w]+', 'ALPHA_SPACERS' => '[\w_\+\. \-\[\]]+');
-
- $validate_options = '';
- foreach ($validate_ary as $lang => $value)
- {
- $selected = ($this->vars['field_validation'] == $value) ? ' selected="selected"' : '';
- $validate_options .= '<option value="' . $value . '"' . $selected . '>' . $user->lang[$lang] . '</option>';
- }
-
- return $validate_options;
- }
-
- /**
- * Get string options for second step in ACP
- */
- function get_string_options()
- {
- global $user;
-
- $options = array(
- 0 => array('TITLE' => $user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" name="field_length" size="5" value="' . $this->vars['field_length'] . '" />'),
- 1 => array('TITLE' => $user->lang['MIN_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" name="field_minlen" size="5" value="' . $this->vars['field_minlen'] . '" />'),
- 2 => array('TITLE' => $user->lang['MAX_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" size="5" value="' . $this->vars['field_maxlen'] . '" />'),
- 3 => array('TITLE' => $user->lang['FIELD_VALIDATION'], 'FIELD' => '<select name="field_validation">' . $this->validate_options() . '</select>')
- );
-
- return $options;
- }
-
- /**
- * Get text options for second step in ACP
- */
- function get_text_options()
- {
- global $user;
-
- $options = array(
- 0 => array('TITLE' => $user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" max="99999" name="rows" size="5" value="' . $this->vars['rows'] . '" /> ' . $user->lang['ROWS'] . '</dd><dd><input type="number" min="0" max="99999" name="columns" size="5" value="' . $this->vars['columns'] . '" /> ' . $user->lang['COLUMNS'] . ' <input type="hidden" name="field_length" value="' . $this->vars['field_length'] . '" />'),
- 1 => array('TITLE' => $user->lang['MIN_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" max="9999999999" name="field_minlen" size="10" value="' . $this->vars['field_minlen'] . '" />'),
- 2 => array('TITLE' => $user->lang['MAX_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" max="9999999999" name="field_maxlen" size="10" value="' . $this->vars['field_maxlen'] . '" />'),
- 3 => array('TITLE' => $user->lang['FIELD_VALIDATION'], 'FIELD' => '<select name="field_validation">' . $this->validate_options() . '</select>')
- );
-
- return $options;
- }
-
- /**
- * Get int options for second step in ACP
- */
- function get_int_options()
- {
- global $user;
-
- $options = array(
- 0 => array('TITLE' => $user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_length" size="5" value="' . $this->vars['field_length'] . '" />'),
- 1 => array('TITLE' => $user->lang['MIN_FIELD_NUMBER'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_minlen" size="5" value="' . $this->vars['field_minlen'] . '" />'),
- 2 => array('TITLE' => $user->lang['MAX_FIELD_NUMBER'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_maxlen" size="5" value="' . $this->vars['field_maxlen'] . '" />'),
- 3 => array('TITLE' => $user->lang['DEFAULT_VALUE'], 'FIELD' => '<input type="post" name="field_default_value" value="' . $this->vars['field_default_value'] . '" />')
- );
-
- return $options;
- }
-
- /**
- * Get bool options for second step in ACP
- */
- function get_bool_options()
- {
- global $user, $config, $lang_defs;
-
- $default_lang_id = $lang_defs['iso'][$config['default_lang']];
-
- $profile_row = array(
- 'var_name' => 'field_default_value',
- 'field_id' => 1,
- 'lang_name' => $this->vars['lang_name'],
- 'lang_explain' => $this->vars['lang_explain'],
- 'lang_id' => $default_lang_id,
- 'field_default_value' => $this->vars['field_default_value'],
- 'field_ident' => 'field_default_value',
- 'field_type' => FIELD_BOOL,
- 'field_length' => $this->vars['field_length'],
- 'lang_options' => $this->vars['lang_options']
- );
-
- $options = array(
- 0 => array('TITLE' => $user->lang['FIELD_TYPE'], 'EXPLAIN' => $user->lang['BOOL_TYPE_EXPLAIN'], 'FIELD' => '<label><input type="radio" class="radio" name="field_length" value="1"' . (($this->vars['field_length'] == 1) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $user->lang['RADIO_BUTTONS'] . '</label><label><input type="radio" class="radio" name="field_length" value="2"' . (($this->vars['field_length'] == 2) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $user->lang['CHECKBOX'] . '</label>'),
- 1 => array('TITLE' => $user->lang['DEFAULT_VALUE'], 'FIELD' => $this->process_field_row('preview', $profile_row))
- );
-
- return $options;
- }
-
- /**
- * Get dropdown options for second step in ACP
- */
- function get_dropdown_options()
- {
- global $user, $config, $lang_defs;
-
- $default_lang_id = $lang_defs['iso'][$config['default_lang']];
-
- $profile_row[0] = array(
- 'var_name' => 'field_default_value',
- 'field_id' => 1,
- 'lang_name' => $this->vars['lang_name'],
- 'lang_explain' => $this->vars['lang_explain'],
- 'lang_id' => $default_lang_id,
- 'field_default_value' => $this->vars['field_default_value'],
- 'field_ident' => 'field_default_value',
- 'field_type' => FIELD_DROPDOWN,
- 'lang_options' => $this->vars['lang_options']
- );
-
- $profile_row[1] = $profile_row[0];
- $profile_row[1]['var_name'] = 'field_novalue';
- $profile_row[1]['field_ident'] = 'field_novalue';
- $profile_row[1]['field_default_value'] = $this->vars['field_novalue'];
-
- $options = array(
- 0 => array('TITLE' => $user->lang['DEFAULT_VALUE'], 'FIELD' => $this->process_field_row('preview', $profile_row[0])),
- 1 => array('TITLE' => $user->lang['NO_VALUE_OPTION'], 'EXPLAIN' => $user->lang['NO_VALUE_OPTION_EXPLAIN'], 'FIELD' => $this->process_field_row('preview', $profile_row[1]))
- );
-
- return $options;
- }
-
- /**
- * Get date options for second step in ACP
- */
- function get_date_options()
- {
- global $user, $config, $lang_defs;
-
- $default_lang_id = $lang_defs['iso'][$config['default_lang']];
-
- $profile_row = array(
- 'var_name' => 'field_default_value',
- 'lang_name' => $this->vars['lang_name'],
- 'lang_explain' => $this->vars['lang_explain'],
- 'lang_id' => $default_lang_id,
- 'field_default_value' => $this->vars['field_default_value'],
- 'field_ident' => 'field_default_value',
- 'field_type' => FIELD_DATE,
- 'field_length' => $this->vars['field_length']
- );
-
- $always_now = request_var('always_now', -1);
- if ($always_now == -1)
- {
- $s_checked = ($this->vars['field_default_value'] == 'now') ? true : false;
- }
- else
- {
- $s_checked = ($always_now) ? true : false;
- }
-
- $options = array(
- 0 => array('TITLE' => $user->lang['DEFAULT_VALUE'], 'FIELD' => $this->process_field_row('preview', $profile_row)),
- 1 => array('TITLE' => $user->lang['ALWAYS_TODAY'], 'FIELD' => '<label><input type="radio" class="radio" name="always_now" value="1"' . (($s_checked) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $user->lang['YES'] . '</label><label><input type="radio" class="radio" name="always_now" value="0"' . ((!$s_checked) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $user->lang['NO'] . '</label>'),
- );
-
- return $options;
- }
-}
diff --git a/phpBB/includes/functions_transfer.php b/phpBB/includes/functions_transfer.php
index 07c9171c60..9bec17ca8f 100644
--- a/phpBB/includes/functions_transfer.php
+++ b/phpBB/includes/functions_transfer.php
@@ -234,7 +234,7 @@ class transfer
/**
* Determine methods able to be used
*/
- function methods()
+ static public function methods()
{
$methods = array();
$disabled_functions = explode(',', @ini_get('disable_functions'));
@@ -287,7 +287,7 @@ class ftp extends transfer
/**
* Requests data
*/
- function data()
+ static public function data()
{
global $user;
@@ -541,7 +541,7 @@ class ftp_fsock extends transfer
/**
* Requests data
*/
- function data()
+ static public function data()
{
global $user;
diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php
index dba6d3d6c2..17cdd0ce39 100644
--- a/phpBB/includes/functions_user.php
+++ b/phpBB/includes/functions_user.php
@@ -162,7 +162,7 @@ function user_update_name($old_name, $new_name)
function user_add($user_row, $cp_data = false)
{
global $db, $user, $auth, $config, $phpbb_root_path, $phpEx;
- global $phpbb_dispatcher;
+ global $phpbb_dispatcher, $phpbb_container;
if (empty($user_row['username']) || !isset($user_row['group_id']) || !isset($user_row['user_email']) || !isset($user_row['user_type']))
{
@@ -276,12 +276,7 @@ function user_add($user_row, $cp_data = false)
{
$cp_data['user_id'] = (int) $user_id;
- if (!class_exists('custom_profile'))
- {
- include_once($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
- }
-
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
$sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' .
$db->sql_build_array('INSERT', $cp->build_insert_sql_array($cp_data));
$db->sql_query($sql);
diff --git a/phpBB/includes/mcp/mcp_queue.php b/phpBB/includes/mcp/mcp_queue.php
index 3b6897f4e1..9f9c22a5c5 100644
--- a/phpBB/includes/mcp/mcp_queue.php
+++ b/phpBB/includes/mcp/mcp_queue.php
@@ -654,6 +654,11 @@ class mcp_queue
// Handle notifications
foreach ($post_info as $post_id => $post_data)
{
+ // A single topic approval may also happen here, so handle deleting the respective notification.
+ if (!$post_data['topic_posts_approved'])
+ {
+ $phpbb_notifications->delete_notifications('topic_in_queue', $post_data['topic_id']);
+ }
$phpbb_notifications->delete_notifications('post_in_queue', $post_id);
$phpbb_notifications->add_notifications(array(
@@ -773,9 +778,12 @@ class mcp_queue
$notify_poster = ($action == 'approve' && isset($_REQUEST['notify_poster'])) ? true : false;
$phpbb_content_visibility = $phpbb_container->get('content.visibility');
+ $first_post_ids = array();
+
foreach ($topic_info as $topic_id => $topic_data)
{
$phpbb_content_visibility->set_topic_visibility(ITEM_APPROVED, $topic_id, $topic_data['forum_id'], $user->data['user_id'], time(), '');
+ $first_post_ids[$topic_id] = (int) $topic_data['topic_first_post_id'];
$topic_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f={$topic_data['forum_id']}&amp;t={$topic_id}");
@@ -799,23 +807,43 @@ class mcp_queue
// Only send out the mails, when the posts are being approved
if ($action == 'approve')
{
+ // Grab the first post text as it's needed for the quote notification.
+ $sql = 'SELECT topic_id, post_text
+ FROM ' . POSTS_TABLE . '
+ WHERE ' . $db->sql_in_set('post_id', $first_post_ids);
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $topic_info[$row['topic_id']]['post_text'] = $row['post_text'];
+ }
+ $db->sql_freeresult($result);
+
// Handle notifications
$phpbb_notifications = $phpbb_container->get('notification_manager');
foreach ($topic_info as $topic_id => $topic_data)
{
- $phpbb_notifications->delete_notifications('topic_in_queue', $post_data['topic_id']);
+ $topic_data = array_merge($topic_data, array(
+ 'post_id' => $topic_data['topic_first_post_id'],
+ 'post_subject' => $topic_data['topic_title'],
+ 'post_time' => $topic_data['topic_time'],
+ 'poster_id' => $topic_data['topic_poster'],
+ 'username' => $topic_data['topic_first_poster_name'],
+ ));
+
+ $phpbb_notifications->delete_notifications('topic_in_queue', $topic_id);
$phpbb_notifications->add_notifications(array(
'quote',
'topic',
- ), $post_data);
+ ), $topic_data);
- $phpbb_notifications->mark_notifications_read('quote', $post_data['post_id'], $user->data['user_id']);
- $phpbb_notifications->mark_notifications_read('topic', $post_data['topic_id'], $user->data['user_id']);
+ $phpbb_notifications->mark_notifications_read('quote', $topic_data['post_id'], $user->data['user_id']);
+ $phpbb_notifications->mark_notifications_read('topic', $topic_id, $user->data['user_id']);
if ($notify_poster)
{
- $phpbb_notifications->add_notifications('approve_topic', $post_data);
+ $phpbb_notifications->add_notifications('approve_topic', $topic_data);
}
}
}
diff --git a/phpBB/includes/mcp/mcp_reports.php b/phpBB/includes/mcp/mcp_reports.php
index 02892964f1..954f8a8349 100644
--- a/phpBB/includes/mcp/mcp_reports.php
+++ b/phpBB/includes/mcp/mcp_reports.php
@@ -616,23 +616,25 @@ function close_report($report_id_list, $mode, $action, $pm = false)
}
unset($close_report_posts, $close_report_topics);
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
foreach ($reports as $report)
{
if ($pm)
{
add_log('mod', 0, 0, 'LOG_PM_REPORT_' . strtoupper($action) . 'D', $post_info[$report['pm_id']]['message_subject']);
+ $phpbb_notifications->delete_notifications('report_pm', $report['pm_id']);
}
else
{
add_log('mod', $post_info[$report['post_id']]['forum_id'], $post_info[$report['post_id']]['topic_id'], 'LOG_REPORT_' . strtoupper($action) . 'D', $post_info[$report['post_id']]['post_subject']);
+ $phpbb_notifications->delete_notifications('report_post', $report['post_id']);
}
}
// Notify reporters
if (sizeof($notify_reporters))
{
- $phpbb_notifications = $phpbb_container->get('notification_manager');
-
foreach ($notify_reporters as $report_id => $reporter)
{
if ($reporter['user_id'] == ANONYMOUS)
@@ -649,8 +651,6 @@ function close_report($report_id_list, $mode, $action, $pm = false)
'closer_id' => $user->data['user_id'],
'from_user_id' => $post_info[$post_id]['author_id'],
)));
-
- $phpbb_notifications->delete_notifications('report_pm', $post_id);
}
else
{
@@ -658,8 +658,6 @@ function close_report($report_id_list, $mode, $action, $pm = false)
'reporter' => $reporter['user_id'],
'closer_id' => $user->data['user_id'],
)));
-
- $phpbb_notifications->delete_notifications('report_post', $post_id);
}
}
}
diff --git a/phpBB/includes/message_parser.php b/phpBB/includes/message_parser.php
index b29f587385..ad6743b3a3 100644
--- a/phpBB/includes/message_parser.php
+++ b/phpBB/includes/message_parser.php
@@ -1461,6 +1461,7 @@ class parse_message extends bbcode_firstpass
'is_orphan' => 1,
'real_filename' => $filedata['real_filename'],
'attach_comment'=> $this->filename_data['filecomment'],
+ 'filesize' => $filedata['filesize'],
);
$this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
@@ -1572,6 +1573,7 @@ class parse_message extends bbcode_firstpass
'is_orphan' => 1,
'real_filename' => $filedata['real_filename'],
'attach_comment'=> $this->filename_data['filecomment'],
+ 'filesize' => $filedata['filesize'],
);
$this->attachment_data = array_merge(array(0 => $new_entry), $this->attachment_data);
@@ -1580,8 +1582,10 @@ class parse_message extends bbcode_firstpass
if (isset($this->plupload) && $this->plupload->is_active())
{
+ $download_url = append_sid("{$phpbb_root_path}download/file.{$phpEx}", 'mode=view&amp;id=' . $new_entry['attach_id']);
+
// Send the client the attachment data to maintain state
- $json_response->send($this->attachment_data);
+ $json_response->send(array('data' => $this->attachment_data, 'download_url' => $download_url));
}
}
}
@@ -1649,7 +1653,7 @@ class parse_message extends bbcode_firstpass
if (sizeof($not_orphan))
{
// Get the attachment data, based on the poster id...
- $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment
+ $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize
FROM ' . ATTACHMENTS_TABLE . '
WHERE ' . $db->sql_in_set('attach_id', array_keys($not_orphan)) . '
AND poster_id = ' . $check_user_id;
@@ -1674,7 +1678,7 @@ class parse_message extends bbcode_firstpass
// Regenerate newly uploaded attachments
if (sizeof($orphan))
{
- $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment
+ $sql = 'SELECT attach_id, is_orphan, real_filename, attach_comment, filesize
FROM ' . ATTACHMENTS_TABLE . '
WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan)) . '
AND poster_id = ' . $user->data['user_id'] . '
diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php
index 79055095bc..c5963a1ebc 100644
--- a/phpBB/includes/ucp/ucp_notifications.php
+++ b/phpBB/includes/ucp/ucp_notifications.php
@@ -98,7 +98,19 @@ class ucp_notifications
$phpbb_notifications->mark_notifications_read(false, false, $user->data['user_id'], $form_time);
meta_refresh(3, $this->u_action);
- $message = $user->lang['NOTIFICATIONS_MARK_ALL_READ_SUCCESS'] . '<br /><br />' . sprintf($user->lang['RETURN_UCP'], '<a href="' . $this->u_action . '">', '</a>');
+ $message = $user->lang['NOTIFICATIONS_MARK_ALL_READ_SUCCESS'];
+
+ if ($request->is_ajax())
+ {
+ $json_response = new \phpbb\json_response();
+ $json_response->send(array(
+ 'MESSAGE_TITLE' => $user->lang['INFORMATION'],
+ 'MESSAGE_TEXT' => $message,
+ 'success' => true,
+ ));
+ }
+ $message .= '<br /><br />' . $user->lang('RETURN_UCP', '<a href="' . $this->u_action . '">', '</a>');
+
trigger_error($message);
}
else
diff --git a/phpBB/includes/ucp/ucp_pm_viewmessage.php b/phpBB/includes/ucp/ucp_pm_viewmessage.php
index cebbadc7c7..119b84564a 100644
--- a/phpBB/includes/ucp/ucp_pm_viewmessage.php
+++ b/phpBB/includes/ucp/ucp_pm_viewmessage.php
@@ -20,7 +20,7 @@ if (!defined('IN_PHPBB'))
*/
function view_message($id, $mode, $folder_id, $msg_id, $folder, $message_row)
{
- global $user, $template, $auth, $db, $cache;
+ global $user, $template, $auth, $db, $cache, $phpbb_container;
global $phpbb_root_path, $request, $phpEx, $config, $phpbb_dispatcher;
$user->add_lang(array('viewtopic', 'memberlist'));
@@ -61,11 +61,7 @@ function view_message($id, $mode, $folder_id, $msg_id, $folder, $message_row)
// Load the custom profile fields
if ($config['load_cpf_pm'])
{
- if (!class_exists('custom_profile'))
- {
- include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
- }
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
$profile_fields = $cp->generate_profile_fields_template('grab', $author_id);
}
diff --git a/phpBB/includes/ucp/ucp_profile.php b/phpBB/includes/ucp/ucp_profile.php
index 2252b2ea17..6ddc6833b7 100644
--- a/phpBB/includes/ucp/ucp_profile.php
+++ b/phpBB/includes/ucp/ucp_profile.php
@@ -29,8 +29,7 @@ class ucp_profile
function main($id, $mode)
{
global $cache, $config, $db, $user, $auth, $template, $phpbb_root_path, $phpEx;
- global $request;
- global $phpbb_container;
+ global $request, $phpbb_container;
$user->add_lang('posting');
@@ -82,13 +81,16 @@ class ucp_profile
$error[] = ($data['password_confirm']) ? 'NEW_PASSWORD_ERROR' : 'NEW_PASSWORD_CONFIRM_EMPTY';
}
+ // Instantiate passwords manager
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+
// Only check the new password against the previous password if there have been no errors
- if (!sizeof($error) && $auth->acl_get('u_chgpasswd') && $data['new_password'] && phpbb_check_hash($data['new_password'], $user->data['user_password']))
+ if (!sizeof($error) && $auth->acl_get('u_chgpasswd') && $data['new_password'] && $passwords_manager->check($data['new_password'], $user->data['user_password']))
{
$error[] = 'SAME_PASSWORD_ERROR';
}
- if (!phpbb_check_hash($data['cur_password'], $user->data['user_password']))
+ if (!$passwords_manager->check($data['cur_password'], $user->data['user_password']))
{
$error[] = ($data['cur_password']) ? 'CUR_PASSWORD_ERROR' : 'CUR_PASSWORD_EMPTY';
}
@@ -105,7 +107,7 @@ class ucp_profile
'username_clean' => ($auth->acl_get('u_chgname') && $config['allow_namechange']) ? utf8_clean_string($data['username']) : $user->data['username_clean'],
'user_email' => ($auth->acl_get('u_chgemail')) ? $data['email'] : $user->data['user_email'],
'user_email_hash' => ($auth->acl_get('u_chgemail')) ? phpbb_email_hash($data['email']) : $user->data['user_email_hash'],
- 'user_password' => ($auth->acl_get('u_chgpasswd') && $data['new_password']) ? phpbb_hash($data['new_password']) : $user->data['user_password'],
+ 'user_password' => ($auth->acl_get('u_chgpasswd') && $data['new_password']) ? $passwords_manager->hash($data['new_password']) : $user->data['user_password'],
'user_passchg' => ($auth->acl_get('u_chgpasswd') && $data['new_password']) ? time() : 0,
);
@@ -114,7 +116,7 @@ class ucp_profile
add_log('user', $user->data['user_id'], 'LOG_USER_UPDATE_NAME', $user->data['username'], $data['username']);
}
- if ($auth->acl_get('u_chgpasswd') && $data['new_password'] && !phpbb_check_hash($data['new_password'], $user->data['user_password']))
+ if ($auth->acl_get('u_chgpasswd') && $data['new_password'] && !$passwords_manager->check($data['new_password'], $user->data['user_password']))
{
$user->reset_login_keys();
add_log('user', $user->data['user_id'], 'LOG_USER_NEW_PASSWORD', $data['username']);
@@ -257,9 +259,7 @@ class ucp_profile
trigger_error('NO_AUTH_PROFILEINFO');
}
- include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
-
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
$cp_data = $cp_error = array();
diff --git a/phpBB/includes/ucp/ucp_register.php b/phpBB/includes/ucp/ucp_register.php
index 1641c6eef1..ff51ca7b3c 100644
--- a/phpBB/includes/ucp/ucp_register.php
+++ b/phpBB/includes/ucp/ucp_register.php
@@ -35,8 +35,6 @@ class ucp_register
trigger_error('UCP_REGISTER_DISABLE');
}
- include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
-
$coppa = $request->is_set('coppa') ? (int) $request->variable('coppa', false) : false;
$agreed = $request->variable('agreed', false);
$submit = $request->is_set_post('submit');
@@ -78,7 +76,7 @@ class ucp_register
}
}
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
$error = $cp_data = $cp_error = array();
$s_hidden_fields = array();
@@ -294,9 +292,12 @@ class ucp_register
$user_inactive_time = 0;
}
+ // Instantiate passwords manager
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+
$user_row = array(
'username' => $data['username'],
- 'user_password' => phpbb_hash($data['new_password']),
+ 'user_password' => $passwords_manager->hash($data['new_password']),
'user_email' => $data['email'],
'group_id' => (int) $group_id,
'user_timezone' => $data['tz'],
diff --git a/phpBB/includes/ucp/ucp_remind.php b/phpBB/includes/ucp/ucp_remind.php
index b3def63896..44395abb44 100644
--- a/phpBB/includes/ucp/ucp_remind.php
+++ b/phpBB/includes/ucp/ucp_remind.php
@@ -27,7 +27,7 @@ class ucp_remind
function main($id, $mode)
{
global $config, $phpbb_root_path, $phpEx;
- global $db, $user, $auth, $template;
+ global $db, $user, $auth, $template, $phpbb_container;;
if (!$config['allow_password_reset'])
{
@@ -88,8 +88,11 @@ class ucp_remind
// For the activation key a random length between 6 and 10 will do.
$user_actkey = gen_rand_string(mt_rand(6, 10));
+ // Instantiate passwords manager
+ $passwords_manager = $phpbb_container->get('passwords.manager');
+
$sql = 'UPDATE ' . USERS_TABLE . "
- SET user_newpasswd = '" . $db->sql_escape(phpbb_hash($user_password)) . "', user_actkey = '" . $db->sql_escape($user_actkey) . "'
+ SET user_newpasswd = '" . $db->sql_escape($passwords_manager->hash($user_password)) . "', user_actkey = '" . $db->sql_escape($user_actkey) . "'
WHERE user_id = " . $user_row['user_id'];
$db->sql_query($sql);
diff --git a/phpBB/index.php b/phpBB/index.php
index 74fc1b9bda..c363781667 100644
--- a/phpBB/index.php
+++ b/phpBB/index.php
@@ -27,24 +27,44 @@ $user->setup('viewforum');
// Mark notifications read
if (($mark_notification = $request->variable('mark_notification', 0)))
{
- $phpbb_notifications = $phpbb_container->get('notification_manager');
-
- $notification = $phpbb_notifications->load_notifications(array(
- 'notification_id' => $mark_notification
- ));
+ if ($user->data['user_id'] == ANONYMOUS)
+ {
+ if ($request->is_ajax())
+ {
+ trigger_error('LOGIN_REQUIRED');
+ }
+ login_box('', $user->lang['LOGIN_REQUIRED']);
+ }
- if (isset($notification['notifications'][$mark_notification]))
+ if (check_link_hash($request->variable('hash', ''), 'mark_notification_read'))
{
- $notification = $notification['notifications'][$mark_notification];
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
- $notification->mark_read();
+ $notification = $phpbb_notifications->load_notifications(array(
+ 'notification_id' => $mark_notification,
+ ));
- if (($redirect = $request->variable('redirect', '')))
+ if (isset($notification['notifications'][$mark_notification]))
{
- redirect(append_sid($phpbb_root_path . $redirect));
- }
+ $notification = $notification['notifications'][$mark_notification];
+
+ $notification->mark_read();
+
+ if ($request->is_ajax())
+ {
+ $json_response = new \phpbb\json_response();
+ $json_response->send(array(
+ 'success' => true,
+ ));
+ }
- redirect($notification->get_url());
+ if (($redirect = $request->variable('redirect', '')))
+ {
+ redirect(append_sid($phpbb_root_path . $redirect));
+ }
+
+ redirect($notification->get_url());
+ }
}
}
@@ -153,6 +173,7 @@ $template->assign_vars(array(
'FORUM_UNREAD_LOCKED_IMG' => $user->img('forum_unread_locked', 'UNREAD_POSTS_LOCKED'),
'S_LOGIN_ACTION' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login'),
+ 'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
'S_DISPLAY_BIRTHDAY_LIST' => ($config['load_birthdays']) ? true : false,
'U_MARK_FORUMS' => ($user->data['is_registered'] || $config['load_anon_lastread']) ? append_sid("{$phpbb_root_path}index.$phpEx", 'hash=' . generate_link_hash('global') . '&amp;mark=forums&amp;mark_time=' . time()) : '',
diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php
index b79420ab71..bad51e2fe3 100644
--- a/phpBB/install/database_update.php
+++ b/phpBB/install/database_update.php
@@ -203,7 +203,15 @@ $migrations = $finder
$migrator->set_migrations($migrations);
// What is a safe limit of execution time? Half the max execution time should be safe.
-$safe_time_limit = (ini_get('max_execution_time') / 2);
+// No more than 15 seconds so the user isn't sitting and waiting for a very long time
+$phpbb_ini = new \phpbb\php\ini();
+$safe_time_limit = min(15, ($phpbb_ini->get_int('max_execution_time') / 2));
+
+// While we're going to try limit this to half the max execution time,
+// we want to try and take additional measures to prevent hitting the
+// max execution time (if, say, one migration step takes much longer
+// than the max execution time)
+@set_time_limit(0);
while (!$migrator->finished())
{
diff --git a/phpBB/install/schemas/firebird_schema.sql b/phpBB/install/schemas/firebird_schema.sql
index 6c6aca447d..791e4ce6b3 100644
--- a/phpBB/install/schemas/firebird_schema.sql
+++ b/phpBB/install/schemas/firebird_schema.sql
@@ -361,7 +361,7 @@ CREATE TABLE phpbb_forums (
forum_desc_options INTEGER DEFAULT 7 NOT NULL,
forum_desc_uid VARCHAR(8) CHARACTER SET NONE DEFAULT '' NOT NULL,
forum_link VARCHAR(255) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
- forum_password VARCHAR(40) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
+ forum_password VARCHAR(255) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
forum_style INTEGER DEFAULT 0 NOT NULL,
forum_image VARCHAR(255) CHARACTER SET NONE DEFAULT '' NOT NULL,
forum_rules BLOB SUB_TYPE TEXT CHARACTER SET UTF8 DEFAULT '' NOT NULL,
@@ -908,7 +908,7 @@ CREATE INDEX phpbb_privmsgs_to_usr_flder_id ON phpbb_privmsgs_to(user_id, folder
CREATE TABLE phpbb_profile_fields (
field_id INTEGER NOT NULL,
field_name VARCHAR(255) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
- field_type INTEGER DEFAULT 0 NOT NULL,
+ field_type VARCHAR(100) CHARACTER SET NONE DEFAULT '' NOT NULL,
field_ident VARCHAR(20) CHARACTER SET NONE DEFAULT '' NOT NULL,
field_length VARCHAR(20) CHARACTER SET NONE DEFAULT '' NOT NULL,
field_minlen VARCHAR(255) CHARACTER SET NONE DEFAULT '' NOT NULL,
@@ -957,7 +957,7 @@ CREATE TABLE phpbb_profile_fields_lang (
field_id INTEGER DEFAULT 0 NOT NULL,
lang_id INTEGER DEFAULT 0 NOT NULL,
option_id INTEGER DEFAULT 0 NOT NULL,
- field_type INTEGER DEFAULT 0 NOT NULL,
+ field_type VARCHAR(100) CHARACTER SET NONE DEFAULT '' NOT NULL,
lang_value VARCHAR(255) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE
);;
@@ -1365,7 +1365,7 @@ CREATE TABLE phpbb_users (
user_regdate INTEGER DEFAULT 0 NOT NULL,
username VARCHAR(255) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
username_clean VARCHAR(255) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
- user_password VARCHAR(40) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
+ user_password VARCHAR(255) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
user_passchg INTEGER DEFAULT 0 NOT NULL,
user_pass_convert INTEGER DEFAULT 0 NOT NULL,
user_email VARCHAR(100) CHARACTER SET UTF8 DEFAULT '' NOT NULL COLLATE UNICODE,
diff --git a/phpBB/install/schemas/mssql_schema.sql b/phpBB/install/schemas/mssql_schema.sql
index 49804bdcc0..21e27c1f21 100644
--- a/phpBB/install/schemas/mssql_schema.sql
+++ b/phpBB/install/schemas/mssql_schema.sql
@@ -448,7 +448,7 @@ CREATE TABLE [phpbb_forums] (
[forum_desc_options] [int] DEFAULT (7) NOT NULL ,
[forum_desc_uid] [varchar] (8) DEFAULT ('') NOT NULL ,
[forum_link] [varchar] (255) DEFAULT ('') NOT NULL ,
- [forum_password] [varchar] (40) DEFAULT ('') NOT NULL ,
+ [forum_password] [varchar] (255) DEFAULT ('') NOT NULL ,
[forum_style] [int] DEFAULT (0) NOT NULL ,
[forum_image] [varchar] (255) DEFAULT ('') NOT NULL ,
[forum_rules] [varchar] (4000) DEFAULT ('') NOT NULL ,
@@ -1114,7 +1114,7 @@ GO
CREATE TABLE [phpbb_profile_fields] (
[field_id] [int] IDENTITY (1, 1) NOT NULL ,
[field_name] [varchar] (255) DEFAULT ('') NOT NULL ,
- [field_type] [int] DEFAULT (0) NOT NULL ,
+ [field_type] [varchar] (100) DEFAULT ('') NOT NULL ,
[field_ident] [varchar] (20) DEFAULT ('') NOT NULL ,
[field_length] [varchar] (20) DEFAULT ('') NOT NULL ,
[field_minlen] [varchar] (255) DEFAULT ('') NOT NULL ,
@@ -1172,7 +1172,7 @@ CREATE TABLE [phpbb_profile_fields_lang] (
[field_id] [int] DEFAULT (0) NOT NULL ,
[lang_id] [int] DEFAULT (0) NOT NULL ,
[option_id] [int] DEFAULT (0) NOT NULL ,
- [field_type] [int] DEFAULT (0) NOT NULL ,
+ [field_type] [varchar] (100) DEFAULT ('') NOT NULL ,
[lang_value] [varchar] (255) DEFAULT ('') NOT NULL
) ON [PRIMARY]
GO
@@ -1681,7 +1681,7 @@ CREATE TABLE [phpbb_users] (
[user_regdate] [int] DEFAULT (0) NOT NULL ,
[username] [varchar] (255) DEFAULT ('') NOT NULL ,
[username_clean] [varchar] (255) DEFAULT ('') NOT NULL ,
- [user_password] [varchar] (40) DEFAULT ('') NOT NULL ,
+ [user_password] [varchar] (255) DEFAULT ('') NOT NULL ,
[user_passchg] [int] DEFAULT (0) NOT NULL ,
[user_pass_convert] [int] DEFAULT (0) NOT NULL ,
[user_email] [varchar] (100) DEFAULT ('') NOT NULL ,
diff --git a/phpBB/install/schemas/mysql_40_schema.sql b/phpBB/install/schemas/mysql_40_schema.sql
index e287e7dde1..37991ea174 100644
--- a/phpBB/install/schemas/mysql_40_schema.sql
+++ b/phpBB/install/schemas/mysql_40_schema.sql
@@ -246,7 +246,7 @@ CREATE TABLE phpbb_forums (
forum_desc_options int(11) UNSIGNED DEFAULT '7' NOT NULL,
forum_desc_uid varbinary(8) DEFAULT '' NOT NULL,
forum_link blob NOT NULL,
- forum_password varbinary(120) DEFAULT '' NOT NULL,
+ forum_password blob NOT NULL,
forum_style mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_image varbinary(255) DEFAULT '' NOT NULL,
forum_rules blob NOT NULL,
@@ -645,7 +645,7 @@ CREATE TABLE phpbb_privmsgs_to (
CREATE TABLE phpbb_profile_fields (
field_id mediumint(8) UNSIGNED NOT NULL auto_increment,
field_name blob NOT NULL,
- field_type tinyint(4) DEFAULT '0' NOT NULL,
+ field_type varbinary(100) DEFAULT '' NOT NULL,
field_ident varbinary(20) DEFAULT '' NOT NULL,
field_length varbinary(20) DEFAULT '' NOT NULL,
field_minlen varbinary(255) DEFAULT '' NOT NULL,
@@ -681,7 +681,7 @@ CREATE TABLE phpbb_profile_fields_lang (
field_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
lang_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
option_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
- field_type tinyint(4) DEFAULT '0' NOT NULL,
+ field_type varbinary(100) DEFAULT '' NOT NULL,
lang_value blob NOT NULL,
PRIMARY KEY (field_id, lang_id, option_id)
);
@@ -974,7 +974,7 @@ CREATE TABLE phpbb_users (
user_regdate int(11) UNSIGNED DEFAULT '0' NOT NULL,
username blob NOT NULL,
username_clean blob NOT NULL,
- user_password varbinary(120) DEFAULT '' NOT NULL,
+ user_password blob NOT NULL,
user_passchg int(11) UNSIGNED DEFAULT '0' NOT NULL,
user_pass_convert tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
user_email blob NOT NULL,
diff --git a/phpBB/install/schemas/mysql_41_schema.sql b/phpBB/install/schemas/mysql_41_schema.sql
index 870fbd05e4..a88bab9c3c 100644
--- a/phpBB/install/schemas/mysql_41_schema.sql
+++ b/phpBB/install/schemas/mysql_41_schema.sql
@@ -246,7 +246,7 @@ CREATE TABLE phpbb_forums (
forum_desc_options int(11) UNSIGNED DEFAULT '7' NOT NULL,
forum_desc_uid varchar(8) DEFAULT '' NOT NULL,
forum_link varchar(255) DEFAULT '' NOT NULL,
- forum_password varchar(40) DEFAULT '' NOT NULL,
+ forum_password varchar(255) DEFAULT '' NOT NULL,
forum_style mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_image varchar(255) DEFAULT '' NOT NULL,
forum_rules text NOT NULL,
@@ -645,7 +645,7 @@ CREATE TABLE phpbb_privmsgs_to (
CREATE TABLE phpbb_profile_fields (
field_id mediumint(8) UNSIGNED NOT NULL auto_increment,
field_name varchar(255) DEFAULT '' NOT NULL,
- field_type tinyint(4) DEFAULT '0' NOT NULL,
+ field_type varchar(100) DEFAULT '' NOT NULL,
field_ident varchar(20) DEFAULT '' NOT NULL,
field_length varchar(20) DEFAULT '' NOT NULL,
field_minlen varchar(255) DEFAULT '' NOT NULL,
@@ -681,7 +681,7 @@ CREATE TABLE phpbb_profile_fields_lang (
field_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
lang_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
option_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
- field_type tinyint(4) DEFAULT '0' NOT NULL,
+ field_type varchar(100) DEFAULT '' NOT NULL,
lang_value varchar(255) DEFAULT '' NOT NULL,
PRIMARY KEY (field_id, lang_id, option_id)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
@@ -974,7 +974,7 @@ CREATE TABLE phpbb_users (
user_regdate int(11) UNSIGNED DEFAULT '0' NOT NULL,
username varchar(255) DEFAULT '' NOT NULL,
username_clean varchar(255) DEFAULT '' NOT NULL,
- user_password varchar(40) DEFAULT '' NOT NULL,
+ user_password varchar(255) DEFAULT '' NOT NULL,
user_passchg int(11) UNSIGNED DEFAULT '0' NOT NULL,
user_pass_convert tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
user_email varchar(100) DEFAULT '' NOT NULL,
diff --git a/phpBB/install/schemas/oracle_schema.sql b/phpBB/install/schemas/oracle_schema.sql
index 5354e5b1bb..ea5cfdf321 100644
--- a/phpBB/install/schemas/oracle_schema.sql
+++ b/phpBB/install/schemas/oracle_schema.sql
@@ -509,7 +509,7 @@ CREATE TABLE phpbb_forums (
forum_desc_options number(11) DEFAULT '7' NOT NULL,
forum_desc_uid varchar2(8) DEFAULT '' ,
forum_link varchar2(765) DEFAULT '' ,
- forum_password varchar2(120) DEFAULT '' ,
+ forum_password varchar2(765) DEFAULT '' ,
forum_style number(8) DEFAULT '0' NOT NULL,
forum_image varchar2(255) DEFAULT '' ,
forum_rules clob DEFAULT '' ,
@@ -1217,7 +1217,7 @@ CREATE INDEX phpbb_privmsgs_to_usr_flder_id ON phpbb_privmsgs_to (user_id, folde
CREATE TABLE phpbb_profile_fields (
field_id number(8) NOT NULL,
field_name varchar2(765) DEFAULT '' ,
- field_type number(4) DEFAULT '0' NOT NULL,
+ field_type varchar2(100) DEFAULT '' ,
field_ident varchar2(20) DEFAULT '' ,
field_length varchar2(20) DEFAULT '' ,
field_minlen varchar2(255) DEFAULT '' ,
@@ -1277,7 +1277,7 @@ CREATE TABLE phpbb_profile_fields_lang (
field_id number(8) DEFAULT '0' NOT NULL,
lang_id number(8) DEFAULT '0' NOT NULL,
option_id number(8) DEFAULT '0' NOT NULL,
- field_type number(4) DEFAULT '0' NOT NULL,
+ field_type varchar2(100) DEFAULT '' ,
lang_value varchar2(765) DEFAULT '' ,
CONSTRAINT pk_phpbb_profile_fields_lang PRIMARY KEY (field_id, lang_id, option_id)
)
@@ -1798,7 +1798,7 @@ CREATE TABLE phpbb_users (
user_regdate number(11) DEFAULT '0' NOT NULL,
username varchar2(255) DEFAULT '' ,
username_clean varchar2(255) DEFAULT '' ,
- user_password varchar2(120) DEFAULT '' ,
+ user_password varchar2(765) DEFAULT '' ,
user_passchg number(11) DEFAULT '0' NOT NULL,
user_pass_convert number(1) DEFAULT '0' NOT NULL,
user_email varchar2(300) DEFAULT '' ,
diff --git a/phpBB/install/schemas/postgres_schema.sql b/phpBB/install/schemas/postgres_schema.sql
index 7773602c16..f06bcdf2a9 100644
--- a/phpBB/install/schemas/postgres_schema.sql
+++ b/phpBB/install/schemas/postgres_schema.sql
@@ -384,7 +384,7 @@ CREATE TABLE phpbb_forums (
forum_desc_options INT4 DEFAULT '7' NOT NULL CHECK (forum_desc_options >= 0),
forum_desc_uid varchar(8) DEFAULT '' NOT NULL,
forum_link varchar(255) DEFAULT '' NOT NULL,
- forum_password varchar(40) DEFAULT '' NOT NULL,
+ forum_password varchar(255) DEFAULT '' NOT NULL,
forum_style INT4 DEFAULT '0' NOT NULL CHECK (forum_style >= 0),
forum_image varchar(255) DEFAULT '' NOT NULL,
forum_rules varchar(4000) DEFAULT '' NOT NULL,
@@ -853,7 +853,7 @@ CREATE SEQUENCE phpbb_profile_fields_seq;
CREATE TABLE phpbb_profile_fields (
field_id INT4 DEFAULT nextval('phpbb_profile_fields_seq'),
field_name varchar(255) DEFAULT '' NOT NULL,
- field_type INT2 DEFAULT '0' NOT NULL,
+ field_type varchar(100) DEFAULT '' NOT NULL,
field_ident varchar(20) DEFAULT '' NOT NULL,
field_length varchar(20) DEFAULT '' NOT NULL,
field_minlen varchar(255) DEFAULT '' NOT NULL,
@@ -893,7 +893,7 @@ CREATE TABLE phpbb_profile_fields_lang (
field_id INT4 DEFAULT '0' NOT NULL CHECK (field_id >= 0),
lang_id INT4 DEFAULT '0' NOT NULL CHECK (lang_id >= 0),
option_id INT4 DEFAULT '0' NOT NULL CHECK (option_id >= 0),
- field_type INT2 DEFAULT '0' NOT NULL,
+ field_type varchar(100) DEFAULT '' NOT NULL,
lang_value varchar(255) DEFAULT '' NOT NULL,
PRIMARY KEY (field_id, lang_id, option_id)
);
@@ -1246,7 +1246,7 @@ CREATE TABLE phpbb_users (
user_regdate INT4 DEFAULT '0' NOT NULL CHECK (user_regdate >= 0),
username varchar_ci DEFAULT '' NOT NULL,
username_clean varchar_ci DEFAULT '' NOT NULL,
- user_password varchar(40) DEFAULT '' NOT NULL,
+ user_password varchar(255) DEFAULT '' NOT NULL,
user_passchg INT4 DEFAULT '0' NOT NULL CHECK (user_passchg >= 0),
user_pass_convert INT2 DEFAULT '0' NOT NULL CHECK (user_pass_convert >= 0),
user_email varchar(100) DEFAULT '' NOT NULL,
diff --git a/phpBB/install/schemas/sqlite_schema.sql b/phpBB/install/schemas/sqlite_schema.sql
index 079d48bc65..bfb0e211d7 100644
--- a/phpBB/install/schemas/sqlite_schema.sql
+++ b/phpBB/install/schemas/sqlite_schema.sql
@@ -239,7 +239,7 @@ CREATE TABLE phpbb_forums (
forum_desc_options INTEGER UNSIGNED NOT NULL DEFAULT '7',
forum_desc_uid varchar(8) NOT NULL DEFAULT '',
forum_link varchar(255) NOT NULL DEFAULT '',
- forum_password varchar(40) NOT NULL DEFAULT '',
+ forum_password varchar(255) NOT NULL DEFAULT '',
forum_style INTEGER UNSIGNED NOT NULL DEFAULT '0',
forum_image varchar(255) NOT NULL DEFAULT '',
forum_rules text(65535) NOT NULL DEFAULT '',
@@ -626,7 +626,7 @@ CREATE INDEX phpbb_privmsgs_to_usr_flder_id ON phpbb_privmsgs_to (user_id, folde
CREATE TABLE phpbb_profile_fields (
field_id INTEGER PRIMARY KEY NOT NULL ,
field_name varchar(255) NOT NULL DEFAULT '',
- field_type tinyint(4) NOT NULL DEFAULT '0',
+ field_type varchar(100) NOT NULL DEFAULT '',
field_ident varchar(20) NOT NULL DEFAULT '',
field_length varchar(20) NOT NULL DEFAULT '',
field_minlen varchar(255) NOT NULL DEFAULT '',
@@ -661,7 +661,7 @@ CREATE TABLE phpbb_profile_fields_lang (
field_id INTEGER UNSIGNED NOT NULL DEFAULT '0',
lang_id INTEGER UNSIGNED NOT NULL DEFAULT '0',
option_id INTEGER UNSIGNED NOT NULL DEFAULT '0',
- field_type tinyint(4) NOT NULL DEFAULT '0',
+ field_type varchar(100) NOT NULL DEFAULT '',
lang_value varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (field_id, lang_id, option_id)
);
@@ -945,7 +945,7 @@ CREATE TABLE phpbb_users (
user_regdate INTEGER UNSIGNED NOT NULL DEFAULT '0',
username varchar(255) NOT NULL DEFAULT '',
username_clean varchar(255) NOT NULL DEFAULT '',
- user_password varchar(40) NOT NULL DEFAULT '',
+ user_password varchar(255) NOT NULL DEFAULT '',
user_passchg INTEGER UNSIGNED NOT NULL DEFAULT '0',
user_pass_convert INTEGER UNSIGNED NOT NULL DEFAULT '0',
user_email varchar(100) NOT NULL DEFAULT '',
diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php
index 851ffe8ec4..abe930c387 100644
--- a/phpBB/language/en/common.php
+++ b/phpBB/language/en/common.php
@@ -371,6 +371,7 @@ $lang = array_merge($lang, array(
'LOGIN_VIEWFORUM' => 'The board requires you to be registered and logged in to view this forum.',
'LOGIN_EXPLAIN_EDIT' => 'In order to edit posts in this forum you have to be registered and logged in.',
'LOGIN_EXPLAIN_VIEWONLINE' => 'In order to view the online list you have to be registered and logged in.',
+ 'LOGIN_REQUIRED' => 'You need to login to perform this action.',
'LOGOUT' => 'Logout',
'LOGOUT_USER' => 'Logout [ %s ]',
'LOG_ME_IN' => 'Remember me',
@@ -378,6 +379,7 @@ $lang = array_merge($lang, array(
'MAIN' => 'Main',
'MARK' => 'Mark',
'MARK_ALL' => 'Mark all',
+ 'MARK_ALL_READ' => 'Mark all read',
'MARK_FORUMS_READ' => 'Mark forums read',
'MARK_READ' => 'Mark read',
'MARK_SUBFORUMS_READ' => 'Mark subforums read',
diff --git a/phpBB/language/en/plupload.php b/phpBB/language/en/plupload.php
index 74ae8b7c78..6039de7dbf 100644
--- a/phpBB/language/en/plupload.php
+++ b/phpBB/language/en/plupload.php
@@ -42,6 +42,7 @@ $lang = array_merge($lang, array(
'PLUPLOAD_CLOSE' => 'Close',
'PLUPLOAD_DRAG' => 'Drag files here.',
'PLUPLOAD_DUPLICATE_ERROR' => 'Duplicate file error.',
+ 'PLUPLOAD_DRAG_TEXTAREA' => 'You may also attach files by dragging and dropping them in the message box.',
'PLUPLOAD_ERR_INPUT' => 'Failed to open input stream.',
'PLUPLOAD_ERR_MOVE_UPLOADED' => 'Failed to move uploaded file.',
'PLUPLOAD_ERR_OUTPUT' => 'Failed to open output stream.',
@@ -49,7 +50,6 @@ $lang = array_merge($lang, array(
'PLUPLOAD_ERR_FILE_COUNT' => 'File count error.',
'PLUPLOAD_ERR_FILE_INVALID_EXT' => 'Error: Invalid file extension:',
'PLUPLOAD_ERR_RUNTIME_MEMORY' => 'Runtime ran out of available memory.',
- 'PLUPLOAD_ERR_UPLOAD_LIMIT' => 'Upload element accepts only %d file(s) at a time. Extra files were stripped.',
'PLUPLOAD_ERR_UPLOAD_URL' => 'Upload URL might be wrong or does not exist.',
'PLUPLOAD_EXTENSION_ERROR' => 'File extension error.',
'PLUPLOAD_FILE' => 'File: %s',
diff --git a/phpBB/language/en/search.php b/phpBB/language/en/search.php
index 71cbec4b41..f65022fbcb 100644
--- a/phpBB/language/en/search.php
+++ b/phpBB/language/en/search.php
@@ -60,7 +60,10 @@ $lang = array_merge($lang, array(
'LOGIN_EXPLAIN_UNREADSEARCH'=> 'The board requires you to be registered and logged in to view your unread posts.',
'LOGIN_EXPLAIN_NEWPOSTS' => 'The board requires you to be registered and logged in to view new posts since your last visit.',
- 'MAX_NUM_SEARCH_KEYWORDS_REFINE' => 'You specified too many words to search for. Please do not enter more than %1$d words.',
+ 'MAX_NUM_SEARCH_KEYWORDS_REFINE' => array(
+ 1 => 'You specified too many words to search for. Please do not enter more than %1$d word.',
+ 2 => 'You specified too many words to search for. Please do not enter more than %1$d words.',
+ ),
'NO_KEYWORDS' => 'You must specify at least one word to search for. Each word must consist of at least %s and must not contain more than %s excluding wildcards.',
'NO_RECENT_SEARCHES' => 'No searches have been carried out recently.',
diff --git a/phpBB/memberlist.php b/phpBB/memberlist.php
index 14425ca6e4..019b1873a0 100644
--- a/phpBB/memberlist.php
+++ b/phpBB/memberlist.php
@@ -617,8 +617,7 @@ switch ($mode)
$profile_fields = array();
if ($config['load_cpf_viewprofile'])
{
- include_once($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
$profile_fields = $cp->generate_profile_fields_template('grab', $user_id);
$profile_fields = (isset($profile_fields[$user_id])) ? $cp->generate_profile_fields_template('show', false, $profile_fields[$user_id]) : array();
}
@@ -1555,8 +1554,7 @@ switch ($mode)
// Load custom profile fields
if ($config['load_cpf_memberlist'])
{
- include_once($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
// Grab all profile fields from users in id cache for later use - similar to the poster cache
$profile_fields_cache = $cp->generate_profile_fields_template('grab', $user_list);
diff --git a/phpBB/phpbb/auth/provider/apache.php b/phpBB/phpbb/auth/provider/apache.php
index 77bc976938..23cdc89829 100644
--- a/phpBB/phpbb/auth/provider/apache.php
+++ b/phpBB/phpbb/auth/provider/apache.php
@@ -17,19 +17,28 @@ namespace phpbb\auth\provider;
class apache extends \phpbb\auth\provider\base
{
/**
+ * phpBB passwords manager
+ *
+ * @var \phpbb\passwords\manager
+ */
+ protected $passwords_manager;
+
+ /**
* Apache Authentication Constructor
*
* @param \phpbb\db\driver\driver $db
* @param \phpbb\config\config $config
+ * @param \phpbb\passwords\manager $passwords_manager
* @param \phpbb\request\request $request
* @param \phpbb\user $user
* @param string $phpbb_root_path
* @param string $php_ext
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext)
{
$this->db = $db;
$this->config = $config;
+ $this->passwords_manager = $passwords_manager;
$this->request = $request;
$this->user = $user;
$this->phpbb_root_path = $phpbb_root_path;
@@ -220,7 +229,7 @@ class apache extends \phpbb\auth\provider\base
// generate user account data
return array(
'username' => $username,
- 'user_password' => phpbb_hash($password),
+ 'user_password' => $this->passwords_manager->hash($password),
'user_email' => '',
'group_id' => (int) $row['group_id'],
'user_type' => USER_NORMAL,
diff --git a/phpBB/phpbb/auth/provider/db.php b/phpBB/phpbb/auth/provider/db.php
index 6ea04eab36..6bbbc0be16 100644
--- a/phpBB/phpbb/auth/provider/db.php
+++ b/phpBB/phpbb/auth/provider/db.php
@@ -18,21 +18,29 @@ namespace phpbb\auth\provider;
*/
class db extends \phpbb\auth\provider\base
{
+ /**
+ * phpBB passwords manager
+ *
+ * @var \phpbb\passwords\manager
+ */
+ protected $passwords_manager;
/**
* Database Authentication Constructor
*
- * @param \phpbb\db\driver\driver $db
- * @param \phpbb\config\config $config
- * @param \phpbb\request\request $request
- * @param \phpbb\user $user
- * @param string $phpbb_root_path
- * @param string $php_ext
+ * @param \phpbb\db\driver\driver $db
+ * @param \phpbb\config\config $config
+ * @param \phpbb\passwords\manager $passwords_manager
+ * @param \phpbb\request\request $request
+ * @param \phpbb\user $user
+ * @param string $phpbb_root_path
+ * @param string $php_ext
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext)
{
$this->db = $db;
$this->config = $config;
+ $this->passwords_manager = $passwords_manager;
$this->request = $request;
$this->user = $user;
$this->phpbb_root_path = $phpbb_root_path;
@@ -191,10 +199,10 @@ class db extends \phpbb\auth\provider\base
// cp1252 is phpBB2's default encoding, characters outside ASCII range might work when converted into that encoding
// plain md5 support left in for conversions from other systems.
- if ((strlen($row['user_password']) == 34 && (phpbb_check_hash(md5($password_old_format), $row['user_password']) || phpbb_check_hash(md5(utf8_to_cp1252($password_old_format)), $row['user_password'])))
+ if ((strlen($row['user_password']) == 34 && ($this->passwords_manager->check(md5($password_old_format), $row['user_password']) || $this->passwords_manager->check(md5(utf8_to_cp1252($password_old_format)), $row['user_password'])))
|| (strlen($row['user_password']) == 32 && (md5($password_old_format) == $row['user_password'] || md5(utf8_to_cp1252($password_old_format)) == $row['user_password'])))
{
- $hash = phpbb_hash($password_new_format);
+ $hash = $this->passwords_manager->hash($password_new_format);
// Update the password in the users table to the new format and remove user_pass_convert flag
$sql = 'UPDATE ' . USERS_TABLE . '
@@ -226,12 +234,12 @@ class db extends \phpbb\auth\provider\base
}
// Check password ...
- if (!$row['user_pass_convert'] && phpbb_check_hash($password, $row['user_password']))
+ if (!$row['user_pass_convert'] && $this->passwords_manager->check($password, $row['user_password']))
{
// Check for old password hash...
- if (strlen($row['user_password']) == 32)
+ if ($this->passwords_manager->convert_flag || strlen($row['user_password']) == 32)
{
- $hash = phpbb_hash($password);
+ $hash = $this->passwords_manager->hash($password);
// Update the password in the users table to the new format
$sql = 'UPDATE ' . USERS_TABLE . "
diff --git a/phpBB/phpbb/auth/provider/ldap.php b/phpBB/phpbb/auth/provider/ldap.php
index 4ce43853bd..e92a227e16 100644
--- a/phpBB/phpbb/auth/provider/ldap.php
+++ b/phpBB/phpbb/auth/provider/ldap.php
@@ -19,16 +19,25 @@ namespace phpbb\auth\provider;
class ldap extends \phpbb\auth\provider\base
{
/**
+ * phpBB passwords manager
+ *
+ * @var \phpbb\passwords\manager
+ */
+ protected $passwords_manager;
+
+ /**
* LDAP Authentication Constructor
*
- * @param \phpbb\db\driver\driver $db
- * @param \phpbb\config\config $config
- * @param \phpbb\user $user
+ * @param \phpbb\db\driver\driver $db
+ * @param \phpbb\config\config $config
+ * @param \phpbb\passwords\manager $passwords_manager
+ * @param \phpbb\user $user
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\user $user)
+ public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\user $user)
{
$this->db = $db;
$this->config = $config;
+ $this->passwords_manager = $passwords_manager;
$this->user = $user;
}
@@ -235,7 +244,7 @@ class ldap extends \phpbb\auth\provider\base
// generate user account data
$ldap_user_row = array(
'username' => $username,
- 'user_password' => phpbb_hash($password),
+ 'user_password' => $this->passwords_manager->hash($password),
'user_email' => (!empty($this->config['ldap_email'])) ? utf8_htmlspecialchars($ldap_result[0][htmlspecialchars_decode($this->config['ldap_email'])][0]) : '',
'group_id' => (int) $row['group_id'],
'user_type' => USER_NORMAL,
diff --git a/phpBB/phpbb/auth/provider/oauth/oauth.php b/phpBB/phpbb/auth/provider/oauth/oauth.php
index 2749661269..0128c89248 100644
--- a/phpBB/phpbb/auth/provider/oauth/oauth.php
+++ b/phpBB/phpbb/auth/provider/oauth/oauth.php
@@ -34,6 +34,13 @@ class oauth extends \phpbb\auth\provider\base
protected $config;
/**
+ * phpBB passwords manager
+ *
+ * @var \phpbb\passwords\manager
+ */
+ protected $passwords_manager;
+
+ /**
* phpBB request object
*
* @var \phpbb\request\request_interface
@@ -101,6 +108,7 @@ class oauth extends \phpbb\auth\provider\base
*
* @param \phpbb\db\driver\driver $db
* @param \phpbb\config\config $config
+ * @param \phpbb\passwords\manager $passwords_manager
* @param \phpbb\request\request_interface $request
* @param \phpbb\user $user
* @param string $auth_provider_oauth_token_storage_table
@@ -110,10 +118,11 @@ class oauth extends \phpbb\auth\provider\base
* @param string $phpbb_root_path
* @param string $php_ext
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\request\request_interface $request, \phpbb\user $user, $auth_provider_oauth_token_storage_table, $auth_provider_oauth_token_account_assoc, \phpbb\di\service_collection $service_providers, $users_table, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request_interface $request, \phpbb\user $user, $auth_provider_oauth_token_storage_table, $auth_provider_oauth_token_account_assoc, \phpbb\di\service_collection $service_providers, $users_table, $phpbb_root_path, $php_ext)
{
$this->db = $db;
$this->config = $config;
+ $this->passwords_manager = $passwords_manager;
$this->request = $request;
$this->user = $user;
$this->auth_provider_oauth_token_storage_table = $auth_provider_oauth_token_storage_table;
@@ -150,7 +159,7 @@ class oauth extends \phpbb\auth\provider\base
// Temporary workaround for only having one authentication provider available
if (!$this->request->is_set('oauth_service'))
{
- $provider = new \phpbb\auth\provider\db($this->db, $this->config, $this->request, $this->user, $this->phpbb_root_path, $this->php_ext);
+ $provider = new \phpbb\auth\provider\db($this->db, $this->config, $this->passwords_manager, $this->request, $this->user, $this->phpbb_root_path, $this->php_ext);
return $provider->login($username, $password);
}
diff --git a/phpBB/phpbb/console/command/config/command.php b/phpBB/phpbb/console/command/config/command.php
new file mode 100644
index 0000000000..b105bc826d
--- /dev/null
+++ b/phpBB/phpbb/console/command/config/command.php
@@ -0,0 +1,22 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+namespace phpbb\console\command\config;
+
+abstract class command extends \phpbb\console\command\command
+{
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ function __construct(\phpbb\config\config $config)
+ {
+ $this->config = $config;
+
+ parent::__construct();
+ }
+}
diff --git a/phpBB/phpbb/console/command/config/delete.php b/phpBB/phpbb/console/command/config/delete.php
new file mode 100644
index 0000000000..9a2d00561d
--- /dev/null
+++ b/phpBB/phpbb/console/command/config/delete.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+namespace phpbb\console\command\config;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class delete extends command
+{
+ protected function configure()
+ {
+ $this
+ ->setName('config:delete')
+ ->setDescription('Deletes a configuration option')
+ ->addArgument(
+ 'key',
+ InputArgument::REQUIRED,
+ "The configuration option's name"
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $key = $input->getArgument('key');
+
+ if (isset($this->config[$key]))
+ {
+ $this->config->delete($key);
+
+ $output->writeln("<info>Successfully deleted config $key</info>");
+ }
+ else
+ {
+ $output->writeln("<error>Config $key does not exist</error>");
+ }
+ }
+}
diff --git a/phpBB/phpbb/console/command/config/get.php b/phpBB/phpbb/console/command/config/get.php
new file mode 100644
index 0000000000..275c82b53f
--- /dev/null
+++ b/phpBB/phpbb/console/command/config/get.php
@@ -0,0 +1,54 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+namespace phpbb\console\command\config;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class get extends command
+{
+ protected function configure()
+ {
+ $this
+ ->setName('config:get')
+ ->setDescription("Gets a configuration option's value")
+ ->addArgument(
+ 'key',
+ InputArgument::REQUIRED,
+ "The configuration option's name"
+ )
+ ->addOption(
+ 'no-newline',
+ null,
+ InputOption::VALUE_NONE,
+ 'Set this option if the value should be printed without a new line at the end.'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $key = $input->getArgument('key');
+
+ if (isset($this->config[$key]) && $input->getOption('no-newline'))
+ {
+ $output->write($this->config[$key]);
+ }
+ elseif (isset($this->config[$key]))
+ {
+ $output->writeln($this->config[$key]);
+ }
+ else
+ {
+ $output->writeln("<error>Could not get config $key</error>");
+ }
+ }
+}
diff --git a/phpBB/phpbb/console/command/config/increment.php b/phpBB/phpbb/console/command/config/increment.php
new file mode 100644
index 0000000000..bc6b63c6ff
--- /dev/null
+++ b/phpBB/phpbb/console/command/config/increment.php
@@ -0,0 +1,52 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+namespace phpbb\console\command\config;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class increment extends command
+{
+ protected function configure()
+ {
+ $this
+ ->setName('config:increment')
+ ->setDescription("Increments a configuration option's value")
+ ->addArgument(
+ 'key',
+ InputArgument::REQUIRED,
+ "The configuration option's name"
+ )
+ ->addArgument(
+ 'increment',
+ InputArgument::REQUIRED,
+ 'Amount to increment by'
+ )
+ ->addOption(
+ 'dynamic',
+ 'd',
+ InputOption::VALUE_NONE,
+ 'Set this option if the configuration option changes too frequently to be efficiently cached.'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $key = $input->getArgument('key');
+ $increment = $input->getArgument('increment');
+ $use_cache = !$input->getOption('dynamic');
+
+ $this->config->increment($key, $increment, $use_cache);
+
+ $output->writeln("<info>Successfully incremented config $key</info>");
+ }
+}
diff --git a/phpBB/phpbb/console/command/config/set.php b/phpBB/phpbb/console/command/config/set.php
new file mode 100644
index 0000000000..9d471a96ad
--- /dev/null
+++ b/phpBB/phpbb/console/command/config/set.php
@@ -0,0 +1,52 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+namespace phpbb\console\command\config;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class set extends command
+{
+ protected function configure()
+ {
+ $this
+ ->setName('config:set')
+ ->setDescription("Sets a configuration option's value")
+ ->addArgument(
+ 'key',
+ InputArgument::REQUIRED,
+ "The configuration option's name"
+ )
+ ->addArgument(
+ 'value',
+ InputArgument::REQUIRED,
+ 'New configuration value, use 0 and 1 to specify boolean values'
+ )
+ ->addOption(
+ 'dynamic',
+ 'd',
+ InputOption::VALUE_NONE,
+ 'Set this option if the configuration option changes too frequently to be efficiently cached.'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $key = $input->getArgument('key');
+ $value = $input->getArgument('value');
+ $use_cache = !$input->getOption('dynamic');
+
+ $this->config->set($key, $value, $use_cache);
+
+ $output->writeln("<info>Successfully set config $key</info>");
+ }
+}
diff --git a/phpBB/phpbb/console/command/config/set_atomic.php b/phpBB/phpbb/console/command/config/set_atomic.php
new file mode 100644
index 0000000000..03e7a60210
--- /dev/null
+++ b/phpBB/phpbb/console/command/config/set_atomic.php
@@ -0,0 +1,65 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+namespace phpbb\console\command\config;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class set_atomic extends command
+{
+ protected function configure()
+ {
+ $this
+ ->setName('config:set-atomic')
+ ->setDescription("Sets a configuration option's value only if the old matches the current value.")
+ ->addArgument(
+ 'key',
+ InputArgument::REQUIRED,
+ "The configuration option's name"
+ )
+ ->addArgument(
+ 'old',
+ InputArgument::REQUIRED,
+ 'Current configuration value, use 0 and 1 to specify boolean values'
+ )
+ ->addArgument(
+ 'new',
+ InputArgument::REQUIRED,
+ 'New configuration value, use 0 and 1 to specify boolean values'
+ )
+ ->addOption(
+ 'dynamic',
+ 'd',
+ InputOption::VALUE_NONE,
+ 'Set this option if the configuration option changes too frequently to be efficiently cached.'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $key = $input->getArgument('key');
+ $old_value = $input->getArgument('old');
+ $new_value = $input->getArgument('new');
+ $use_cache = !$input->getOption('dynamic');
+
+ if ($this->config->set_atomic($key, $old_value, $new_value, $use_cache))
+ {
+ $output->writeln("<info>Successfully set config $key</info>");
+ return 0;
+ }
+ else
+ {
+ $output->writeln("<error>Could not set config $key</error>");
+ return 1;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords.php b/phpBB/phpbb/db/migration/data/v310/passwords.php
new file mode 100644
index 0000000000..3422f75917
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/passwords.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* @package migration
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class passwords extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_password' => array('VCHAR:255', ''),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_password' => array('VCHAR:255', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_password' => array('VCHAR:40', ''),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_password' => array('VCHAR:40', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_types.php b/phpBB/phpbb/db/migration/data/v310/profilefield_types.php
new file mode 100644
index 0000000000..2152aaee20
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_types.php
@@ -0,0 +1,106 @@
+<?php
+/**
+*
+* @package migration
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_types extends \phpbb\db\migration\migration
+{
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\alpha2',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_type' => array('VCHAR:100', ''),
+ ),
+ $this->table_prefix . 'profile_fields_lang' => array(
+ 'field_type' => array('VCHAR:100', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_profile_fields_type'))),
+ array('custom', array(array($this, 'update_profile_fields_lang_type'))),
+ );
+ }
+
+ public function update_profile_fields_type()
+ {
+ // Update profile field types
+ $sql = 'SELECT field_type
+ FROM ' . $this->table_prefix . 'profile_fields
+ GROUP BY field_type';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "profile_fields
+ SET field_type = '" . $this->db->sql_escape($this->convert_phpbb30_field_type($row['field_type'])) . "'
+ WHERE field_type = '" . $this->db->sql_escape($row['field_type']) . "'";
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_profile_fields_lang_type()
+ {
+ // Update profile field language types
+ $sql = 'SELECT field_type
+ FROM ' . $this->table_prefix . 'profile_fields_lang
+ GROUP BY field_type';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "profile_fields_lang
+ SET field_type = '" . $this->db->sql_escape($this->convert_phpbb30_field_type($row['field_type'])) . "'
+ WHERE field_type = '" . $this->db->sql_escape($row['field_type']) . "'";
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * Determine the new field type for a given phpBB 3.0 field type
+ *
+ * @param $field_type string Field type in 3.0
+ * @return string Field new type which is used since 3.1
+ */
+ public function convert_phpbb30_field_type($field_type)
+ {
+ switch ($field_type)
+ {
+ case FIELD_INT:
+ return 'profilefields.type.int';
+ case FIELD_STRING:
+ return 'profilefields.type.string';
+ case FIELD_TEXT:
+ return 'profilefields.type.text';
+ case FIELD_BOOL:
+ return 'profilefields.type.bool';
+ case FIELD_DROPDOWN:
+ return 'profilefields.type.dropdown';
+ case FIELD_DATE:
+ return 'profilefields.type.date';
+ default:
+ return $field_type;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/tools.php b/phpBB/phpbb/db/tools.php
index 4360c89ac3..65098b643b 100644
--- a/phpBB/phpbb/db/tools.php
+++ b/phpBB/phpbb/db/tools.php
@@ -1474,52 +1474,7 @@ class tools
}
// 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]];
- }
+ list($column_type, $orig_column_type) = $this->get_column_type($column_data[0]);
// Adjust default value if db-dependent specified
if (is_array($column_data[1]))
@@ -1695,6 +1650,65 @@ class tools
}
/**
+ * Get the column's database type from the type map
+ *
+ * @param string $column_map_type
+ * @return array column type for this database
+ * and map type without length
+ */
+ function get_column_type($column_map_type)
+ {
+ if (strpos($column_map_type, ':') !== false)
+ {
+ list($orig_column_type, $column_length) = explode(':', $column_map_type);
+ 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_map_type;
+ $column_type = $this->dbms_type_map[$this->sql_layer][$column_map_type];
+ }
+
+ return array($column_type, $orig_column_type);
+ }
+
+ /**
* Add new column
*/
function sql_column_add($table_name, $column_name, $column_data, $inline = false)
diff --git a/phpBB/phpbb/log/log.php b/phpBB/phpbb/log/log.php
index a6ee06ebf2..62edc6a77f 100644
--- a/phpBB/phpbb/log/log.php
+++ b/phpBB/phpbb/log/log.php
@@ -424,7 +424,7 @@ class log implements \phpbb\log\log_interface
if ($count_logs)
{
$sql = 'SELECT COUNT(l.log_id) AS total_entries
- FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . ' u
+ FROM ' . $this->log_table . ' l, ' . USERS_TABLE . ' u
WHERE l.log_type = ' . (int) $log_type . '
AND l.user_id = u.user_id
AND l.log_time >= ' . (int) $log_time . "
@@ -449,7 +449,7 @@ class log implements \phpbb\log\log_interface
}
$sql = 'SELECT l.*, u.username, u.username_clean, u.user_colour
- FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . ' u
+ FROM ' . $this->log_table . ' l, ' . USERS_TABLE . ' u
WHERE l.log_type = ' . (int) $log_type . '
AND u.user_id = l.user_id
' . (($log_time) ? 'AND l.log_time >= ' . (int) $log_time : '') . "
diff --git a/phpBB/phpbb/notification/type/approve_post.php b/phpBB/phpbb/notification/type/approve_post.php
index 51a9a704b0..e51ff12b3e 100644
--- a/phpBB/phpbb/notification/type/approve_post.php
+++ b/phpBB/phpbb/notification/type/approve_post.php
@@ -35,6 +35,13 @@ class approve_post extends \phpbb\notification\type\post
protected $language_key = 'NOTIFICATION_POST_APPROVED';
/**
+ * Inherit notification read status from post.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = false;
+
+ /**
* Notification option data (for outputting to the user)
*
* @var bool|array False if the service should use it's default data
diff --git a/phpBB/phpbb/notification/type/approve_topic.php b/phpBB/phpbb/notification/type/approve_topic.php
index 6229800c68..11a240e03d 100644
--- a/phpBB/phpbb/notification/type/approve_topic.php
+++ b/phpBB/phpbb/notification/type/approve_topic.php
@@ -35,6 +35,13 @@ class approve_topic extends \phpbb\notification\type\topic
protected $language_key = 'NOTIFICATION_TOPIC_APPROVED';
/**
+ * Inherit notification read status from topic.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = false;
+
+ /**
* Notification option data (for outputting to the user)
*
* @var bool|array False if the service should use it's default data
diff --git a/phpBB/phpbb/notification/type/base.php b/phpBB/phpbb/notification/type/base.php
index 951585853f..10c876b286 100644
--- a/phpBB/phpbb/notification/type/base.php
+++ b/phpBB/phpbb/notification/type/base.php
@@ -282,15 +282,17 @@ abstract class base implements \phpbb\notification\type\type_interface
*/
public function prepare_for_display()
{
+ $mark_hash = generate_link_hash('mark_notification_read');
+
if ($this->get_url())
{
- $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id);
+ $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id . '&amp;hash=' . $mark_hash);
}
else
{
$redirect = (($this->user->page['page_dir']) ? $this->user->page['page_dir'] . '/' : '') . $this->user->page['page_name'] . (($this->user->page['query_string']) ? '?' . $this->user->page['query_string'] : '');
- $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id . '&amp;redirect=' . urlencode($redirect));
+ $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id . '&amp;hash=' . $mark_hash . '&amp;redirect=' . urlencode($redirect));
}
return array(
diff --git a/phpBB/phpbb/notification/type/disapprove_post.php b/phpBB/phpbb/notification/type/disapprove_post.php
index 411d4195c7..70de2a3e10 100644
--- a/phpBB/phpbb/notification/type/disapprove_post.php
+++ b/phpBB/phpbb/notification/type/disapprove_post.php
@@ -35,6 +35,13 @@ class disapprove_post extends \phpbb\notification\type\approve_post
protected $language_key = 'NOTIFICATION_POST_DISAPPROVED';
/**
+ * Inherit notification read status from post.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = false;
+
+ /**
* Notification option data (for outputting to the user)
*
* @var bool|array False if the service should use it's default data
diff --git a/phpBB/phpbb/notification/type/disapprove_topic.php b/phpBB/phpbb/notification/type/disapprove_topic.php
index 19e9d468ce..d39201d928 100644
--- a/phpBB/phpbb/notification/type/disapprove_topic.php
+++ b/phpBB/phpbb/notification/type/disapprove_topic.php
@@ -35,6 +35,13 @@ class disapprove_topic extends \phpbb\notification\type\approve_topic
protected $language_key = 'NOTIFICATION_TOPIC_DISAPPROVED';
/**
+ * Inherit notification read status from topic.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = false;
+
+ /**
* Notification option data (for outputting to the user)
*
* @var bool|array False if the service should use it's default data
diff --git a/phpBB/phpbb/notification/type/post.php b/phpBB/phpbb/notification/type/post.php
index c2854c17af..bc42c4422b 100644
--- a/phpBB/phpbb/notification/type/post.php
+++ b/phpBB/phpbb/notification/type/post.php
@@ -35,6 +35,13 @@ class post extends \phpbb\notification\type\base
protected $language_key = 'NOTIFICATION_POST';
/**
+ * Inherit notification read status from post.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = true;
+
+ /**
* Notification option data (for outputting to the user)
*
* @var bool|array False if the service should use it's default data
@@ -315,7 +322,7 @@ class post extends \phpbb\notification\type\base
*/
public function pre_create_insert_array($post, $notify_users)
{
- if (!sizeof($notify_users))
+ if (!sizeof($notify_users) || !$this->inherit_read_status)
{
return array();
}
@@ -360,7 +367,7 @@ class post extends \phpbb\notification\type\base
// Topics can be "read" before they are public (while awaiting approval).
// Make sure that if the user has read the topic, it's marked as read in the notification
- if (isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time)
+ if ($this->inherit_read_status && isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time)
{
$this->notification_read = true;
}
diff --git a/phpBB/phpbb/notification/type/report_post.php b/phpBB/phpbb/notification/type/report_post.php
index 89b497efa6..9bf035b91e 100644
--- a/phpBB/phpbb/notification/type/report_post.php
+++ b/phpBB/phpbb/notification/type/report_post.php
@@ -35,6 +35,13 @@ class report_post extends \phpbb\notification\type\post_in_queue
protected $language_key = 'NOTIFICATION_REPORT_POST';
/**
+ * Inherit notification read status from post.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = false;
+
+ /**
* Permission to check for (in find_users_for_notification)
*
* @var string Permission name
diff --git a/phpBB/phpbb/notification/type/report_post_closed.php b/phpBB/phpbb/notification/type/report_post_closed.php
index 5874d48e31..fff45612b3 100644
--- a/phpBB/phpbb/notification/type/report_post_closed.php
+++ b/phpBB/phpbb/notification/type/report_post_closed.php
@@ -41,6 +41,13 @@ class report_post_closed extends \phpbb\notification\type\post
*/
protected $language_key = 'NOTIFICATION_REPORT_CLOSED';
+ /**
+ * Inherit notification read status from post.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = false;
+
public function is_available()
{
return false;
diff --git a/phpBB/phpbb/notification/type/topic.php b/phpBB/phpbb/notification/type/topic.php
index 6198881d8d..98f086a50b 100644
--- a/phpBB/phpbb/notification/type/topic.php
+++ b/phpBB/phpbb/notification/type/topic.php
@@ -35,6 +35,13 @@ class topic extends \phpbb\notification\type\base
protected $language_key = 'NOTIFICATION_TOPIC';
/**
+ * Inherit notification read status from topic.
+ *
+ * @var bool
+ */
+ protected $inherit_read_status = true;
+
+ /**
* Notification option data (for outputting to the user)
*
* @var bool|array False if the service should use it's default data
@@ -220,7 +227,7 @@ class topic extends \phpbb\notification\type\base
*/
public function pre_create_insert_array($post, $notify_users)
{
- if (!sizeof($notify_users))
+ if (!sizeof($notify_users) || !$this->inherit_read_status)
{
return array();
}
@@ -261,7 +268,7 @@ class topic extends \phpbb\notification\type\base
// Topics can be "read" before they are public (while awaiting approval).
// Make sure that if the user has read the topic, it's marked as read in the notification
- if (isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time)
+ if ($this->inherit_read_status && isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time)
{
$this->notification_read = true;
}
diff --git a/phpBB/phpbb/passwords/driver/base.php b/phpBB/phpbb/passwords/driver/base.php
new file mode 100644
index 0000000000..8256fd721c
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/base.php
@@ -0,0 +1,45 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+/**
+* @package passwords
+*/
+abstract class base implements driver_interface
+{
+ /** @var phpbb\config\config */
+ protected $config;
+
+ /** @var phpbb\passwords\driver\helper */
+ protected $helper;
+
+ /** @var driver name */
+ protected $name;
+
+ /**
+ * Constructor of passwords driver object
+ *
+ * @param \phpbb\config\config $config phpBB config
+ * @param \phpbb\passwords\driver\helper $helper Password driver helper
+ */
+ public function __construct(\phpbb\config\config $config, helper $helper)
+ {
+ $this->config = $config;
+ $this->helper = $helper;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function is_supported()
+ {
+ return true;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/bcrypt.php b/phpBB/phpbb/passwords/driver/bcrypt.php
new file mode 100644
index 0000000000..1d1b1e267d
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/bcrypt.php
@@ -0,0 +1,104 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+/**
+* @package passwords
+*/
+class bcrypt extends base
+{
+ const PREFIX = '$2a$';
+
+ /**
+ * @inheritdoc
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function hash($password, $salt = '')
+ {
+ // The 2x and 2y prefixes of bcrypt might not be supported
+ // Revert to 2a if this is the case
+ $prefix = (!$this->is_supported()) ? '$2a$' : $this->get_prefix();
+
+ // Do not support 8-bit characters with $2a$ bcrypt
+ // Also see http://www.php.net/security/crypt_blowfish.php
+ if ($prefix === self::PREFIX)
+ {
+ if (ord($password[strlen($password)-1]) & 128)
+ {
+ return false;
+ }
+ }
+
+ if ($salt == '')
+ {
+ $salt = $prefix . '10$' . $this->get_random_salt();
+ }
+
+ $hash = crypt($password, $salt);
+ if (strlen($hash) < 60)
+ {
+ return false;
+ }
+ return $hash;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function check($password, $hash)
+ {
+ $salt = substr($hash, 0, 29);
+ if (strlen($salt) != 29)
+ {
+ return false;
+ }
+
+ if ($hash == $this->hash($password, $salt))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get a random salt value with a length of 22 characters
+ *
+ * @return string Salt for password hashing
+ */
+ protected function get_random_salt()
+ {
+ return $this->helper->hash_encode64($this->helper->get_random_salt(22), 22);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_settings_only($hash, $full = false)
+ {
+ if ($full)
+ {
+ $pos = stripos($hash, '$', 1) + 1;
+ $length = 22 + (strripos($hash, '$') + 1 - $pos);
+ }
+ else
+ {
+ $pos = strripos($hash, '$') + 1;
+ $length = 22;
+ }
+ return substr($hash, $pos, $length);
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/bcrypt_2y.php b/phpBB/phpbb/passwords/driver/bcrypt_2y.php
new file mode 100644
index 0000000000..11c3617e49
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/bcrypt_2y.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+/**
+* @package passwords
+*/
+class bcrypt_2y extends bcrypt
+{
+ const PREFIX = '$2y$';
+
+ /**
+ * @inheritdoc
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function is_supported()
+ {
+ return (version_compare(PHP_VERSION, '5.3.7', '<')) ? false : true;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/driver_interface.php b/phpBB/phpbb/passwords/driver/driver_interface.php
new file mode 100644
index 0000000000..ebaf0626af
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/driver_interface.php
@@ -0,0 +1,60 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+/**
+* @package passwords
+*/
+interface driver_interface
+{
+ /**
+ * Check if hash type is supported
+ *
+ * @return bool True if supported, false if not
+ */
+ public function is_supported();
+
+ /**
+ * Returns the hash prefix
+ *
+ * @return string Hash prefix
+ */
+ public function get_prefix();
+
+ /**
+ * Hash the password
+ *
+ * @param string $password The password that should be hashed
+ *
+ * @return bool|string Password hash or false if something went wrong
+ * during hashing
+ */
+ public function hash($password);
+
+ /**
+ * Check the password against the supplied hash
+ *
+ * @param string $password The password to check
+ * @param string $hash The password hash to check against
+ *
+ * @return bool True if password is correct, else false
+ */
+ public function check($password, $hash);
+
+ /**
+ * Get only the settings of the specified hash
+ *
+ * @param string $hash Password hash
+ * @param bool $full Return full settings or only settings
+ * related to the salt
+ * @return string String containing the hash settings
+ */
+ public function get_settings_only($hash, $full = false);
+}
diff --git a/phpBB/phpbb/passwords/driver/helper.php b/phpBB/phpbb/passwords/driver/helper.php
new file mode 100644
index 0000000000..4b8dc9a123
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/helper.php
@@ -0,0 +1,144 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+/**
+* @package passwords
+*/
+class helper
+{
+ /**
+ * @var phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * base64 alphabet
+ * @var string
+ */
+ public $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+
+ /**
+ * Construct a driver helper object
+ *
+ * @param phpbb\config\config $config phpBB configuration
+ */
+ public function __construct(\phpbb\config\config $config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * Base64 encode hash
+ *
+ * @param string $input Input string
+ * @param int $count Input string length
+ *
+ * @return string base64 encoded string
+ */
+ public function hash_encode64($input, $count)
+ {
+ $output = '';
+ $i = 0;
+
+ do
+ {
+ $value = ord($input[$i++]);
+ $output .= $this->itoa64[$value & 0x3f];
+
+ if ($i < $count)
+ {
+ $value |= ord($input[$i]) << 8;
+ }
+
+ $output .= $this->itoa64[($value >> 6) & 0x3f];
+
+ if ($i++ >= $count)
+ {
+ break;
+ }
+
+ if ($i < $count)
+ {
+ $value |= ord($input[$i]) << 16;
+ }
+
+ $output .= $this->itoa64[($value >> 12) & 0x3f];
+
+ if ($i++ >= $count)
+ {
+ break;
+ }
+
+ $output .= $this->itoa64[($value >> 18) & 0x3f];
+ }
+ while ($i < $count);
+
+ return $output;
+ }
+
+ /**
+ * Return unique id
+ *
+ * @param string $extra Additional entropy
+ *
+ * @return string Unique id
+ */
+ public function unique_id($extra = 'c')
+ {
+ static $dss_seeded = false;
+
+ $val = $this->config['rand_seed'] . microtime();
+ $val = md5($val);
+ $this->config['rand_seed'] = md5($this->config['rand_seed'] . $val . $extra);
+
+ if ($dss_seeded !== true && ($this->config['rand_seed_last_update'] < time() - rand(1,10)))
+ {
+ $this->config->set('rand_seed_last_update', time(), true);
+ $this->config->set('rand_seed', $this->config['rand_seed'], true);
+ $dss_seeded = true;
+ }
+
+ return substr($val, 4, 16);
+ }
+
+ /**
+ * Get random salt with specified length
+ *
+ * @param int $length Salt length
+ * @param string $rand_seed Seed for random data (optional). For tests.
+ *
+ * @return string Random salt with specified length
+ */
+ public function get_random_salt($length, $rand_seed = '/dev/urandom')
+ {
+ $random = '';
+
+ if (($fh = @fopen($rand_seed, 'rb')))
+ {
+ $random = fread($fh, $length);
+ fclose($fh);
+ }
+
+ if (strlen($random) < $length)
+ {
+ $random = '';
+ $random_state = $this->unique_id();
+
+ for ($i = 0; $i < $length; $i += 16)
+ {
+ $random_state = md5($this->unique_id() . $random_state);
+ $random .= pack('H*', md5($random_state));
+ }
+ $random = substr($random, 0, $length);
+ }
+ return $random;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/phpass.php b/phpBB/phpbb/passwords/driver/phpass.php
new file mode 100644
index 0000000000..80c4d7a7f0
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/phpass.php
@@ -0,0 +1,26 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+/**
+* @package passwords
+*/
+class phpass extends salted_md5
+{
+ const PREFIX = '$P$';
+
+ /**
+ * @inheritdoc
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/salted_md5.php b/phpBB/phpbb/passwords/driver/salted_md5.php
new file mode 100644
index 0000000000..5c72726422
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/salted_md5.php
@@ -0,0 +1,160 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+/**
+*
+* @version Version 0.1 / slightly modified for phpBB 3.1.x (using $H$ as hash type identifier)
+*
+* Portable PHP password hashing framework.
+*
+* Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
+* the public domain.
+*
+* There's absolutely no warranty.
+*
+* The homepage URL for this framework is:
+*
+* http://www.openwall.com/phpass/
+*
+* Please be sure to update the Version line if you edit this file in any way.
+* It is suggested that you leave the main version number intact, but indicate
+* your project name (after the slash) and add your own revision information.
+*
+* Please do not change the "private" password hashing method implemented in
+* here, thereby making your hashes incompatible. However, if you must, please
+* change the hash type identifier (the "$P$") to something different.
+*
+* Obviously, since this code is in the public domain, the above are not
+* requirements (there can be none), but merely suggestions.
+*
+*/
+
+/**
+* @package passwords
+*/
+class salted_md5 extends base
+{
+ const PREFIX = '$H$';
+
+ /**
+ * @inheritdoc
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function hash($password, $setting = '')
+ {
+ if ($setting)
+ {
+ if (($settings = $this->get_hash_settings($setting)) === false)
+ {
+ // Return md5 of password if settings do not
+ // comply with our standards. This will only
+ // happen if pre-determined settings are
+ // directly passed to the driver. The manager
+ // will not do this. Same as the old hashing
+ // implementatio in phpBB 3.0
+ return md5($password);
+ }
+ }
+ else
+ {
+ $settings = $this->get_hash_settings($this->generate_salt());
+ }
+
+ $hash = md5($settings['salt'] . $password, true);
+ do
+ {
+ $hash = md5($hash . $password, true);
+ }
+ while (--$settings['count']);
+
+ $output = $settings['full'];
+ $output .= $this->helper->hash_encode64($hash, 16);
+
+ return $output;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function check($password, $hash)
+ {
+ if (strlen($hash) !== 34)
+ {
+ return md5($password) === $hash;
+ }
+
+ return $hash === $this->hash($password, $hash);
+ }
+
+ /**
+ * Generate salt for hashing method
+ *
+ * @return string Salt for hashing method
+ */
+ protected function generate_salt()
+ {
+ $count = 6;
+
+ $random = $this->helper->get_random_salt($count);
+
+ $salt = $this->get_prefix();
+ $salt .= $this->helper->itoa64[min($count + 5, 30)];
+ $salt .= $this->helper->hash_encode64($random, $count);
+
+ return $salt;
+ }
+
+ /**
+ * Get hash settings
+ *
+ * @param string $hash The hash that contains the settings
+ *
+ * @return bool|array Array containing the count_log2, salt, and full
+ * hash settings string or false if supplied hash is empty
+ * or contains incorrect settings
+ */
+ public function get_hash_settings($hash)
+ {
+ if (empty($hash))
+ {
+ return false;
+ }
+
+ $count_log2 = strpos($this->helper->itoa64, $hash[3]);
+ $salt = substr($hash, 4, 8);
+
+ if ($count_log2 < 7 || $count_log2 > 30 || strlen($salt) != 8)
+ {
+ return false;
+ }
+
+ return array(
+ 'count' => 1 << $count_log2,
+ 'salt' => $salt,
+ 'full' => substr($hash, 0, 12),
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_settings_only($hash, $full = false)
+ {
+ return substr($hash, 3, 9);
+ }
+}
diff --git a/phpBB/phpbb/passwords/helper.php b/phpBB/phpbb/passwords/helper.php
new file mode 100644
index 0000000000..95bad5805f
--- /dev/null
+++ b/phpBB/phpbb/passwords/helper.php
@@ -0,0 +1,103 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords;
+
+/**
+* @package passwords
+*/
+class helper
+{
+ /**
+ * Get hash settings from combined hash
+ *
+ * @param string $hash Password hash of combined hash
+ *
+ * @return array An array containing the hash settings for the hash
+ * types in successive order as described by the combined
+ * password hash or an empty array if hash does not
+ * properly fit the combined hash format
+ */
+ public function get_combined_hash_settings($hash)
+ {
+ $output = array();
+
+ preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $hash, $match);
+ $hash_settings = substr($hash, strpos($hash, $match[1]) + strlen($match[1]) + 1);
+ $matches = explode('\\', $match[1]);
+ foreach ($matches as $cur_type)
+ {
+ $dollar_position = strpos($hash_settings, '$');
+ $output[] = substr($hash_settings, 0, ($dollar_position != false) ? $dollar_position : strlen($hash_settings));
+ $hash_settings = substr($hash_settings, $dollar_position + 1);
+ }
+
+ return $output;
+ }
+
+ /**
+ * Combine hash prefixes, settings, and actual hash
+ *
+ * @param array $data Array containing the keys 'prefix' and 'settings'.
+ * It will hold the prefixes and settings
+ * @param string $type Data type of the supplied value
+ * @param string $value Value that should be put into the data array
+ *
+ * @return string|null Return complete combined hash if type is neither
+ * 'prefix' nor 'settings', nothing if it is
+ */
+ public function combine_hash_output(&$data, $type, $value)
+ {
+ if ($type == 'prefix')
+ {
+ $data[$type] .= ($data[$type] !== '$') ? '\\' : '';
+ $data[$type] .= str_replace('$', '', $value);
+ }
+ elseif ($type == 'settings')
+ {
+ $data[$type] .= ($data[$type] !== '$') ? '$' : '';
+ $data[$type] .= $value;
+ }
+ else
+ {
+ // Return full hash
+ return $data['prefix'] . $data['settings'] . '$' . $value;
+ }
+ }
+
+ /**
+ * Rebuild hash for hashing functions
+ *
+ * @param string $prefix Hash prefix
+ * @param string $settings Hash settings
+ *
+ * @return string Rebuilt hash for hashing functions
+ */
+ public function rebuild_hash($prefix, $settings)
+ {
+ $rebuilt_hash = $prefix;
+ if (strpos($settings, '\\') !== false)
+ {
+ $settings = str_replace('\\', '$', $settings);
+ }
+ $rebuilt_hash .= $settings;
+ return $rebuilt_hash;
+ }
+
+ /**
+ * Obtain only the actual hash after the prefixes
+ *
+ * @param string $hash The full password hash
+ * @return string Actual hash (incl. settings)
+ */
+ public function obtain_hash_only($hash)
+ {
+ return substr($hash, strripos($hash, '$') + 1);
+ }
+}
diff --git a/phpBB/phpbb/passwords/manager.php b/phpBB/phpbb/passwords/manager.php
new file mode 100644
index 0000000000..0ac6b05ec4
--- /dev/null
+++ b/phpBB/phpbb/passwords/manager.php
@@ -0,0 +1,341 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\passwords;
+
+/**
+* @package passwords
+*/
+class manager
+{
+ /**
+ * Default hashing method
+ */
+ protected $type = false;
+
+ /**
+ * Hashing algorithm type map
+ * Will be used to map hash prefix to type
+ */
+ protected $type_map = false;
+
+ /**
+ * Service collection of hashing algorithms
+ * Needs to be public for passwords helper
+ */
+ public $algorithms = false;
+
+ /**
+ * Password convert flag. Signals that password should be converted
+ */
+ public $convert_flag = false;
+
+ /**
+ * Passwords helper
+ * @var phpbb\passwords\helper
+ */
+ protected $helper;
+
+ /**
+ * phpBB configuration
+ * @var phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * Construct a passwords object
+ *
+ * @param phpbb\config\config $config phpBB configuration
+ * @param array $hashing_algorithms Hashing driver
+ * service collection
+ * @param phpbb\passwords\helper $helper Passwords helper object
+ * @param string $defaults List of default driver types
+ */
+ public function __construct(\phpbb\config\config $config, $hashing_algorithms, helper $helper, $defaults)
+ {
+ $this->config = $config;
+ $this->helper = $helper;
+
+ $this->fill_type_map($hashing_algorithms);
+ $this->register_default_type($defaults);
+ }
+
+ /**
+ * Register default type
+ * Will register the first supported type from the list of default types
+ *
+ * @param array $defaults List of default types in order from first to
+ * use to last to use
+ */
+ protected function register_default_type($defaults)
+ {
+ foreach ($defaults as $type)
+ {
+ if ($this->algorithms[$type]->is_supported())
+ {
+ $this->type = $this->algorithms[$type]->get_prefix();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Fill algorithm type map
+ *
+ * @param phpbb\di\service_collection $hashing_algorithms
+ */
+ protected function fill_type_map($hashing_algorithms)
+ {
+ foreach ($hashing_algorithms as $algorithm)
+ {
+ if (!isset($this->type_map[$algorithm->get_prefix()]))
+ {
+ $this->type_map[$algorithm->get_prefix()] = $algorithm;
+ }
+ }
+ $this->algorithms = $hashing_algorithms;
+ }
+
+ /**
+ * Get the algorithm specified by a specific prefix
+ *
+ * @param string $prefix Password hash prefix
+ *
+ * @return object|bool The hash type object or false if prefix is not
+ * supported
+ */
+ protected function get_algorithm($prefix)
+ {
+ if (isset($this->type_map[$prefix]))
+ {
+ return $this->type_map[$prefix];
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Detect the hash type of the supplied hash
+ *
+ * @param string $hash Password hash that should be checked
+ *
+ * @return object|bool The hash type object or false if the specified
+ * type is not supported
+ */
+ public function detect_algorithm($hash)
+ {
+ /*
+ * preg_match() will also show hashing algos like $2a\H$, which
+ * is a combination of bcrypt and phpass. Legacy algorithms
+ * like md5 will not be matched by this and need to be treated
+ * differently.
+ */
+ if (!preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $hash, $match))
+ {
+ return $this->get_algorithm('$H$');
+ }
+
+ // Be on the lookout for multiple hashing algorithms
+ // 2 is correct: H\2a > 2, H\P > 2
+ if (strlen($match[1]) > 2)
+ {
+ $hash_types = explode('\\', $match[1]);
+ $return_ary = array();
+ foreach ($hash_types as $type)
+ {
+ // we do not support the same hashing
+ // algorithm more than once
+ if (isset($return_ary[$type]))
+ {
+ return false;
+ }
+
+ $return_ary[$type] = $this->get_algorithm('$' . $type . '$');
+
+ if (empty($return_ary[$type]))
+ {
+ return false;
+ }
+ }
+ return $return_ary;
+ }
+
+ // get_algorithm() will automatically return false if prefix
+ // is not supported
+ return $this->get_algorithm($match[0]);
+ }
+
+ /**
+ * Hash supplied password
+ *
+ * @param string $password Password that should be hashed
+ * @param string $type Hash type. Will default to standard hash type if
+ * none is supplied
+ * @return string|bool Password hash of supplied password or false if
+ * if something went wrong during hashing
+ */
+ public function hash($password, $type = '')
+ {
+ if (strlen($password) > 4096)
+ {
+ // If the password is too huge, we will simply reject it
+ // and not let the server try to hash it.
+ return false;
+ }
+
+ // Try to retrieve algorithm by service name if type doesn't
+ // start with dollar sign
+ if (!is_array($type) && strpos($type, '$') !== 0 && isset($this->algorithms[$type]))
+ {
+ $type = $this->algorithms[$type]->get_prefix();
+ }
+
+ $type = ($type === '') ? $this->type : $type;
+
+ if (is_array($type))
+ {
+ return $this->combined_hash_password($password, $type);
+ }
+
+ if (isset($this->type_map[$type]))
+ {
+ $hashing_algorithm = $this->type_map[$type];
+ }
+ else
+ {
+ return false;
+ }
+
+ return $hashing_algorithm->hash($password);
+ }
+
+ /**
+ * Check supplied password against hash and set convert_flag if password
+ * needs to be converted to different format (preferrably newer one)
+ *
+ * @param string $password Password that should be checked
+ * @param string $hash Stored hash
+ * @return string|bool True if password is correct, false if not
+ */
+ public function check($password, $hash)
+ {
+ if (strlen($password) > 4096)
+ {
+ // If the password is too huge, we will simply reject it
+ // and not let the server try to hash it.
+ return false;
+ }
+
+ // First find out what kind of hash we're dealing with
+ $stored_hash_type = $this->detect_algorithm($hash);
+ if ($stored_hash_type == false)
+ {
+ return false;
+ }
+
+ // Multiple hash passes needed
+ if (is_array($stored_hash_type))
+ {
+ $correct = $this->check_combined_hash($password, $stored_hash_type, $hash);
+ $this->convert_flag = ($correct === true) ? true : false;
+ return $correct;
+ }
+
+ if ($stored_hash_type->get_prefix() !== $this->type)
+ {
+ $this->convert_flag = true;
+ }
+ else
+ {
+ $this->convert_flag = false;
+ }
+
+ return $stored_hash_type->check($password, $hash);
+ }
+
+ /**
+ * Create combined hash from already hashed password
+ *
+ * @param string $password_hash Complete current password hash
+ * @param string $type Type of the hashing algorithm the password hash
+ * should be combined with
+ * @return string|bool Combined password hash if combined hashing was
+ * successful, else false
+ */
+ public function combined_hash_password($password_hash, $type)
+ {
+ $data = array(
+ 'prefix' => '$',
+ 'settings' => '$',
+ );
+ $hash_settings = $this->helper->get_combined_hash_settings($password_hash);
+ $hash = $hash_settings[0];
+
+ // Put settings of current hash into data array
+ $stored_hash_type = $this->detect_algorithm($password_hash);
+ $this->helper->combine_hash_output($data, 'prefix', $stored_hash_type->get_prefix());
+ $this->helper->combine_hash_output($data, 'settings', $stored_hash_type->get_settings_only($password_hash));
+
+ // Hash current hash with the defined types
+ foreach ($type as $cur_type)
+ {
+ if (isset($this->algorithms[$cur_type]))
+ {
+ $new_hash_type = $this->algorithms[$cur_type];
+ }
+ else
+ {
+ $new_hash_type = $this->get_algorithm($cur_type);
+ }
+
+ if (!$new_hash_type)
+ {
+ return false;
+ }
+
+ $new_hash = $new_hash_type->hash(str_replace($stored_hash_type->get_settings_only($password_hash), '', $hash));
+ $this->helper->combine_hash_output($data, 'prefix', $new_hash_type->get_prefix());
+ $this->helper->combine_hash_output($data, 'settings', substr(str_replace('$', '\\', $new_hash_type->get_settings_only($new_hash, true)), 0));
+ $hash = str_replace($new_hash_type->get_settings_only($new_hash), '', $this->helper->obtain_hash_only($new_hash));
+ }
+ return $this->helper->combine_hash_output($data, 'hash', $hash);
+ }
+
+ /**
+ * Check combined password hash against the supplied password
+ *
+ * @param string $password Password entered by user
+ * @param array $stored_hash_type An array containing the hash types
+ * as described by stored password hash
+ * @param string $hash Stored password hash
+ *
+ * @return bool True if password is correct, false if not
+ */
+ public function check_combined_hash($password, $stored_hash_type, $hash)
+ {
+ $i = 0;
+ $data = array(
+ 'prefix' => '$',
+ 'settings' => '$',
+ );
+ $hash_settings = $this->helper->get_combined_hash_settings($hash);
+ foreach ($stored_hash_type as $key => $hash_type)
+ {
+ $rebuilt_hash = $this->helper->rebuild_hash($hash_type->get_prefix(), $hash_settings[$i]);
+ $this->helper->combine_hash_output($data, 'prefix', $key);
+ $this->helper->combine_hash_output($data, 'settings', $hash_settings[$i]);
+ $cur_hash = $hash_type->hash($password, $rebuilt_hash);
+ $password = str_replace($rebuilt_hash, '', $cur_hash);
+ $i++;
+ }
+ return ($hash === $this->helper->combine_hash_output($data, 'hash', $password));
+ }
+}
diff --git a/phpBB/phpbb/path_helper.php b/phpBB/phpbb/path_helper.php
index 8cd8808261..a8e12c4063 100644
--- a/phpBB/phpbb/path_helper.php
+++ b/phpBB/phpbb/path_helper.php
@@ -102,6 +102,27 @@ class path_helper
}
/**
+ * Strips away the web root path and prepends the normal root path
+ *
+ * This replaces get_web_root_path() . some_url with
+ * $phpbb_root_path . some_url
+ *
+ * @param string $path The path to be updated
+ * @return string
+ */
+ public function remove_web_root_path($path)
+ {
+ if (strpos($path, $this->get_web_root_path()) === 0)
+ {
+ $path = substr($path, strlen($this->get_web_root_path()));
+
+ return $this->phpbb_root_path . $path;
+ }
+
+ return $path;
+ }
+
+ /**
* Get a relative root path from the current URL
*
* @return string
@@ -162,4 +183,27 @@ class path_helper
*/
return $this->web_root_path = $this->phpbb_root_path . str_repeat('../', $corrections - 1);
}
+
+ /**
+ * Eliminates useless . and .. components from specified URL
+ *
+ * @param string $url URL to clean
+ *
+ * @return string Cleaned URL
+ */
+ public function clean_url($url)
+ {
+ $delimiter_position = strpos($url, '://');
+ // URL should contain :// but it shouldn't start with it.
+ // Do not clean URLs that do not fit these constraints.
+ if (empty($delimiter_position))
+ {
+ return $url;
+ }
+ $scheme = substr($url, 0, $delimiter_position) . '://';
+ // Add length of URL delimiter to position
+ $path = substr($url, $delimiter_position + 3);
+
+ return $scheme . $this->filesystem->clean_path($path);
+ }
}
diff --git a/phpBB/phpbb/permissions.php b/phpBB/phpbb/permissions.php
index 8319e6d123..a3fddb0b9e 100644
--- a/phpBB/phpbb/permissions.php
+++ b/phpBB/phpbb/permissions.php
@@ -251,6 +251,7 @@ class permissions
'f_reply' => array('lang' => 'ACL_F_REPLY', 'cat' => 'post'),
'f_edit' => array('lang' => 'ACL_F_EDIT', 'cat' => 'post'),
'f_delete' => array('lang' => 'ACL_F_DELETE', 'cat' => 'post'),
+ 'f_softdelete' => array('lang' => 'ACL_F_SOFTDELETE', 'cat' => 'post'),
'f_ignoreflood' => array('lang' => 'ACL_F_IGNOREFLOOD', 'cat' => 'post'),
'f_postcount' => array('lang' => 'ACL_F_POSTCOUNT', 'cat' => 'post'),
'f_noapprove' => array('lang' => 'ACL_F_NOAPPROVE', 'cat' => 'post'),
diff --git a/phpBB/phpbb/profilefields/lang_helper.php b/phpBB/phpbb/profilefields/lang_helper.php
new file mode 100644
index 0000000000..7bae1bdc18
--- /dev/null
+++ b/phpBB/phpbb/profilefields/lang_helper.php
@@ -0,0 +1,130 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields;
+
+/**
+* Custom Profile Fields
+* @package phpBB3
+*/
+class lang_helper
+{
+ /**
+ * Array with the language option, grouped by field and language
+ * @var array
+ */
+ protected $options_lang = array();
+
+ /**
+ * Database object
+ * @var \phpbb\db\driver\driver
+ */
+ protected $db;
+
+ /**
+ * Table where the language strings are stored
+ * @var string
+ */
+ protected $language_table;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\db\driver\driver $db Database object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct($db, $language_table)
+ {
+ $this->db = $db;
+ $this->language_table = $language_table;
+ }
+
+ /**
+ * Get language entries for options and store them here for later use
+ */
+ public function get_option_lang($field_id, $lang_id, $field_type, $preview_options)
+ {
+ if ($preview_options !== false)
+ {
+ $lang_options = (!is_array($preview_options)) ? explode("\n", $preview_options) : $preview_options;
+
+ foreach ($lang_options as $num => $var)
+ {
+ if (!isset($this->options_lang[$field_id]))
+ {
+ $this->options_lang[$field_id] = array();
+ }
+ if (!isset($this->options_lang[$field_id][$lang_id]))
+ {
+ $this->options_lang[$field_id][$lang_id] = array();
+ }
+ $this->options_lang[$field_id][$lang_id][($num + 1)] = $var;
+ }
+ }
+ else
+ {
+ $sql = 'SELECT option_id, lang_value
+ FROM ' . $this->language_table . '
+ WHERE field_id = ' . (int) $field_id . '
+ AND lang_id = ' . (int) $lang_id . "
+ AND field_type = '" . $this->db->sql_escape($field_type) . "'
+ ORDER BY option_id";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->options_lang[$field_id][$lang_id][($row['option_id'] + 1)] = $row['lang_value'];
+ }
+ $this->db->sql_freeresult($result);
+ }
+ }
+
+ /**
+ * Are language options set for this field?
+ *
+ * @param int $field_id Database ID of the field
+ * @param int $lang_id ID of the language
+ * @param int $field_value Selected value of the field
+ * @return boolean
+ */
+ public function is_set($field_id, $lang_id = null, $field_value = null)
+ {
+ $is_set = isset($this->options_lang[$field_id]);
+
+ if ($is_set && (!is_null($lang_id) || !is_null($field_value)))
+ {
+ $is_set = isset($this->options_lang[$field_id][$lang_id]);
+ }
+
+ if ($is_set && !is_null($field_value))
+ {
+ $is_set = isset($this->options_lang[$field_id][$lang_id][$field_value]);
+ }
+
+ return $is_set;
+ }
+
+ /**
+ * Get the selected language string
+ *
+ * @param int $field_id Database ID of the field
+ * @param int $lang_id ID of the language
+ * @param int $field_value Selected value of the field
+ * @return string
+ */
+ public function get($field_id, $lang_id, $field_value = null)
+ {
+ if (is_null($field_value))
+ {
+ return $this->options_lang[$field_id][$lang_id];
+ }
+
+ return $this->options_lang[$field_id][$lang_id][$field_value];
+ }
+}
diff --git a/phpBB/phpbb/profilefields/manager.php b/phpBB/phpbb/profilefields/manager.php
new file mode 100644
index 0000000000..7564c920c9
--- /dev/null
+++ b/phpBB/phpbb/profilefields/manager.php
@@ -0,0 +1,375 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields;
+
+/**
+* Custom Profile Fields
+* @package phpBB3
+*/
+class manager
+{
+ /**
+ * Auth object
+ * @var \phpbb\auth\auth
+ */
+ protected $auth;
+
+ /**
+ * Database object
+ * @var \phpbb\db\driver\driver
+ */
+ protected $db;
+
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * Service Collection object
+ * @var \phpbb\di\service_collection
+ */
+ protected $type_collection;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ protected $fields_table;
+
+ protected $fields_language_table;
+
+ protected $fields_data_table;
+
+ protected $profile_cache = array();
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\auth\auth $auth Auth object
+ * @param \phpbb\db\driver\driver $db Database object
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\di\service_collection $type_collection
+ * @param \phpbb\user $user User object
+ * @param string $fields_table
+ * @param string $fields_language_table
+ * @param string $fields_data_table
+ */
+ public function __construct(\phpbb\auth\auth $auth, \phpbb\db\driver\driver $db, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\di\service_collection $type_collection, \phpbb\user $user, $fields_table, $fields_language_table, $fields_data_table)
+ {
+ $this->auth = $auth;
+ $this->db = $db;
+ $this->request = $request;
+ $this->template = $template;
+ $this->type_collection = $type_collection;
+ $this->user = $user;
+
+ $this->fields_table = $fields_table;
+ $this->fields_language_table = $fields_language_table;
+ $this->fields_data_table = $fields_data_table;
+ }
+
+ /**
+ * Assign editable fields to template, mode can be profile (for profile change) or register (for registration)
+ * Called by ucp_profile and ucp_register
+ */
+ public function generate_profile_fields($mode, $lang_id)
+ {
+ $sql_where = '';
+ switch ($mode)
+ {
+ case 'register':
+ // If the field is required we show it on the registration page
+ $sql_where .= ' AND f.field_show_on_reg = 1';
+ break;
+
+ case 'profile':
+ // Show hidden fields to moderators/admins
+ if (!$this->auth->acl_gets('a_', 'm_') && !$this->auth->acl_getf_global('m_'))
+ {
+ $sql_where .= ' AND f.field_show_profile = 1';
+ }
+ break;
+
+ default:
+ trigger_error('Wrong profile mode specified', E_USER_ERROR);
+ break;
+ }
+
+ $sql = 'SELECT l.*, f.*
+ FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . " f
+ WHERE f.field_active = 1
+ $sql_where
+ AND l.lang_id = " . (int) $lang_id . '
+ AND l.field_id = f.field_id
+ ORDER BY f.field_order';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // Return templated field
+ $profile_field = $this->type_collection[$row['field_type']];
+ $tpl_snippet = $profile_field->process_field_row('change', $row);
+
+ $this->template->assign_block_vars('profile_fields', array(
+ 'LANG_NAME' => $this->user->lang($row['lang_name']),
+ 'LANG_EXPLAIN' => $this->user->lang($row['lang_explain']),
+ 'FIELD' => $tpl_snippet,
+ 'FIELD_ID' => $profile_field->get_field_ident($row),
+ 'S_REQUIRED' => ($row['field_required']) ? true : false,
+ ));
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * Build profile cache, used for display
+ */
+ protected function build_cache()
+ {
+ $this->profile_cache = array();
+
+ // Display hidden/no_view fields for admin/moderator
+ $sql = 'SELECT l.*, f.*
+ FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . ' f
+ WHERE l.lang_id = ' . $this->user->get_iso_lang_id() . '
+ AND f.field_active = 1 ' .
+ ((!$this->auth->acl_gets('a_', 'm_') && !$this->auth->acl_getf_global('m_')) ? ' AND f.field_hide = 0 ' : '') . '
+ AND f.field_no_view = 0
+ AND l.field_id = f.field_id
+ ORDER BY f.field_order';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->profile_cache[$row['field_ident']] = $row;
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * Submit profile field for validation
+ */
+ public function submit_cp_field($mode, $lang_id, &$cp_data, &$cp_error)
+ {
+ $sql_where = '';
+ switch ($mode)
+ {
+ case 'register':
+ // If the field is required we show it on the registration page
+ $sql_where .= ' AND f.field_show_on_reg = 1';
+ break;
+
+ case 'profile':
+ // Show hidden fields to moderators/admins
+ if (!$this->auth->acl_gets('a_', 'm_') && !$this->auth->acl_getf_global('m_'))
+ {
+ $sql_where .= ' AND f.field_show_profile = 1';
+ }
+ break;
+
+ default:
+ trigger_error('Wrong profile mode specified', E_USER_ERROR);
+ break;
+ }
+
+ $sql = 'SELECT l.*, f.*
+ FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . ' f
+ WHERE l.lang_id = ' . (int) $lang_id . "
+ AND f.field_active = 1
+ $sql_where
+ AND l.field_id = f.field_id
+ ORDER BY f.field_order";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $profile_field = $this->type_collection[$row['field_type']];
+ $cp_data['pf_' . $row['field_ident']] = $profile_field->get_profile_field($row);
+ $check_value = $cp_data['pf_' . $row['field_ident']];
+
+ if (($cp_result = $profile_field->validate_profile_field($check_value, $row)) !== false)
+ {
+ // If the result is not false, it's an error message
+ $cp_error[] = $cp_result;
+ }
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * Update profile field data directly
+ */
+ public function update_profile_field_data($user_id, $cp_data)
+ {
+ if (!sizeof($cp_data))
+ {
+ return;
+ }
+
+ $sql = 'UPDATE ' . $this->fields_data_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $cp_data) . '
+ WHERE user_id = ' . (int) $user_id;
+ $this->db->sql_query($sql);
+
+ if (!$this->db->sql_affectedrows())
+ {
+ $cp_data['user_id'] = (int) $user_id;
+
+ $this->db->sql_return_on_error(true);
+
+ $sql = 'INSERT INTO ' . $this->fields_data_table . ' ' . $this->db->sql_build_array('INSERT', $cp_data);
+ $this->db->sql_query($sql);
+
+ $this->db->sql_return_on_error(false);
+ }
+ }
+
+ /**
+ * Assign fields to template, used for viewprofile, viewtopic and memberlist (if load setting is enabled)
+ * This is directly connected to the user -> mode == grab is to grab the user specific fields, mode == show is for assigning the row to the template
+ */
+ public function generate_profile_fields_template($mode, $user_id = 0, $profile_row = false)
+ {
+ if ($mode == 'grab')
+ {
+ if (!is_array($user_id))
+ {
+ $user_id = array($user_id);
+ }
+
+ if (!sizeof($this->profile_cache))
+ {
+ $this->build_cache();
+ }
+
+ if (!sizeof($user_id))
+ {
+ return array();
+ }
+
+ $sql = 'SELECT *
+ FROM ' . $this->fields_data_table . '
+ WHERE ' . $this->db->sql_in_set('user_id', array_map('intval', $user_id));
+ $result = $this->db->sql_query($sql);
+
+ $field_data = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $field_data[$row['user_id']] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ $user_fields = array();
+
+ $user_ids = $user_id;
+
+ // Go through the fields in correct order
+ foreach (array_keys($this->profile_cache) as $used_ident)
+ {
+ foreach ($field_data as $user_id => $row)
+ {
+ $user_fields[$user_id][$used_ident]['value'] = $row['pf_' . $used_ident];
+ $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
+ }
+
+ foreach ($user_ids as $user_id)
+ {
+ if (!isset($user_fields[$user_id][$used_ident]) && $this->profile_cache[$used_ident]['field_show_novalue'])
+ {
+ $user_fields[$user_id][$used_ident]['value'] = '';
+ $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
+ }
+ }
+ }
+
+ return $user_fields;
+ }
+ else if ($mode == 'show')
+ {
+ // $profile_row == $user_fields[$row['user_id']];
+ $tpl_fields = array();
+ $tpl_fields['row'] = $tpl_fields['blockrow'] = array();
+
+ foreach ($profile_row as $ident => $ident_ary)
+ {
+ $profile_field = $this->type_collection[$ident_ary['data']['field_type']];
+ $value = $profile_field->get_profile_value($ident_ary['value'], $ident_ary['data']);
+
+ if ($value === NULL)
+ {
+ continue;
+ }
+
+ $tpl_fields['row'] += array(
+ 'PROFILE_' . strtoupper($ident) . '_VALUE' => $value,
+ 'PROFILE_' . strtoupper($ident) . '_TYPE' => $ident_ary['data']['field_type'],
+ 'PROFILE_' . strtoupper($ident) . '_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
+ 'PROFILE_' . strtoupper($ident) . '_EXPLAIN'=> $this->user->lang($ident_ary['data']['lang_explain']),
+
+ 'S_PROFILE_' . strtoupper($ident) => true,
+ );
+
+ $tpl_fields['blockrow'][] = array(
+ 'PROFILE_FIELD_VALUE' => $value,
+ 'PROFILE_FIELD_TYPE' => $ident_ary['data']['field_type'],
+ 'PROFILE_FIELD_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
+ 'PROFILE_FIELD_EXPLAIN' => $this->user->lang($ident_ary['data']['lang_explain']),
+
+ 'S_PROFILE_' . strtoupper($ident) => true,
+ );
+ }
+
+ return $tpl_fields;
+ }
+ else
+ {
+ trigger_error('Wrong mode for custom profile', E_USER_ERROR);
+ }
+ }
+
+ /**
+ * Build Array for user insertion into custom profile fields table
+ */
+ public function build_insert_sql_array($cp_data)
+ {
+ $sql_not_in = array();
+ foreach ($cp_data as $key => $null)
+ {
+ $sql_not_in[] = (strncmp($key, 'pf_', 3) === 0) ? substr($key, 3) : $key;
+ }
+
+ $sql = 'SELECT f.field_type, f.field_ident, f.field_default_value, l.lang_default_value
+ FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . ' f
+ WHERE l.lang_id = ' . $this->user->get_iso_lang_id() . '
+ ' . ((sizeof($sql_not_in)) ? ' AND ' . $this->db->sql_in_set('f.field_ident', $sql_not_in, true) : '') . '
+ AND l.field_id = f.field_id';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $profile_field = $this->type_collection[$row['field_type']];
+ $cp_data['pf_' . $row['field_ident']] = $profile_field->get_default_field_value($row);
+ }
+ $this->db->sql_freeresult($result);
+
+ return $cp_data;
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_base.php b/phpBB/phpbb/profilefields/type/type_base.php
new file mode 100644
index 0000000000..95e9b8768b
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_base.php
@@ -0,0 +1,175 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+abstract class type_base implements type_interface
+{
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\user $user User object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->request = $request;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name()
+ {
+ return $this->user->lang('FIELD_' . strtoupper($this->get_name_short()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_service_name()
+ {
+ return 'profilefields.type.' . $this->get_name_short();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_template_filename()
+ {
+ return 'profilefields/' . $this->get_name_short() . '.html';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_field_ident($field_data)
+ {
+ return 'pf_' . $field_data['field_ident'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options_input($field_data)
+ {
+ $field_data['l_lang_name'] = $this->request->variable('l_lang_name', array(0 => ''), true);
+ $field_data['l_lang_explain'] = $this->request->variable('l_lang_explain', array(0 => ''), true);
+ $field_data['l_lang_default_value'] = $this->request->variable('l_lang_default_value', array(0 => ''), true);
+ $field_data['l_lang_options'] = $this->request->variable('l_lang_options', array(0 => ''), true);
+
+ return $field_data;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_options_form(&$exclude_options, &$visibility_options)
+ {
+ return $this->request->variable('lang_options', '', true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_options_on_submit($error, $field_data)
+ {
+ return $error;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_excluded_options($key, $action, $current_value, &$field_data, $step)
+ {
+ if ($step == 3 && ($field_data[$key] || $action != 'edit') && $key == 'l_lang_options' && is_array($field_data[$key]))
+ {
+ foreach ($field_data[$key] as $lang_id => $options)
+ {
+ $field_data[$key][$lang_id] = explode("\n", $options);
+ }
+
+ return $current_value;
+ }
+
+ return $current_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_hidden_fields($step, $key, $action, &$field_data)
+ {
+ if (!$this->request->is_set($key))
+ {
+ // Do not set this variable, we will use the default value
+ return null;
+ }
+ else if ($key == 'field_ident' && isset($field_data[$key]))
+ {
+ return $field_data[$key];
+ }
+ else
+ {
+ return $this->request->variable($key, '', true);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function display_options(&$template_vars, &$field_data)
+ {
+ return;
+ }
+
+ /**
+ * Return templated value/field. Possible values for $mode are:
+ * change == user is able to set/enter profile values; preview == just show the value
+ */
+ public function process_field_row($mode, $profile_row)
+ {
+ $preview_options = ($mode == 'preview') ? $profile_row['lang_options'] : false;
+
+ // set template filename
+ $this->template->set_filenames(array(
+ 'cp_body' => $this->get_template_filename(),
+ ));
+
+ // empty previously filled blockvars
+ $this->template->destroy_block_vars($this->get_name_short());
+
+ // Assign template variables
+ $this->generate_field($profile_row, $preview_options);
+
+ return $this->template->assign_display('cp_body');
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_bool.php b/phpBB/phpbb/profilefields/type/type_bool.php
new file mode 100644
index 0000000000..47a5ed4992
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_bool.php
@@ -0,0 +1,387 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+class type_bool extends type_base
+{
+ /**
+ * Profile fields language helper
+ * @var \phpbb\profilefields\lang_helper
+ */
+ protected $lang_helper;
+
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\profilefields\lang_helper $lang_helper Profile fields language helper
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\user $user User object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct(\phpbb\profilefields\lang_helper $lang_helper, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->lang_helper = $lang_helper;
+ $this->request = $request;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name_short()
+ {
+ return 'bool';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_options($default_lang_id, $field_data)
+ {
+ $profile_row = array(
+ 'var_name' => 'field_default_value',
+ 'field_id' => 1,
+ 'lang_name' => $field_data['lang_name'],
+ 'lang_explain' => $field_data['lang_explain'],
+ 'lang_id' => $default_lang_id,
+ 'field_default_value' => $field_data['field_default_value'],
+ 'field_ident' => 'field_default_value',
+ 'field_type' => $this->get_service_name(),
+ 'field_length' => $field_data['field_length'],
+ 'lang_options' => $field_data['lang_options'],
+ );
+
+ $options = array(
+ 0 => array('TITLE' => $this->user->lang['FIELD_TYPE'], 'EXPLAIN' => $this->user->lang['BOOL_TYPE_EXPLAIN'], 'FIELD' => '<label><input type="radio" class="radio" name="field_length" value="1"' . (($field_data['field_length'] == 1) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $this->user->lang['RADIO_BUTTONS'] . '</label><label><input type="radio" class="radio" name="field_length" value="2"' . (($field_data['field_length'] == 2) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $this->user->lang['CHECKBOX'] . '</label>'),
+ 1 => array('TITLE' => $this->user->lang['DEFAULT_VALUE'], 'FIELD' => $this->process_field_row('preview', $profile_row)),
+ );
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => 1,
+ 'field_minlen' => 0,
+ 'field_maxlen' => 0,
+ 'field_validation' => '',
+ 'field_novalue' => 0,
+ 'field_default_value' => 0,
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_field_value($field_data)
+ {
+ return $field_data['field_default_value'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_field($profile_row)
+ {
+ $var_name = 'pf_' . $profile_row['field_ident'];
+
+ // Checkbox
+ if ($profile_row['field_length'] == 2)
+ {
+ return ($this->request->is_set($var_name)) ? 1 : 0;
+ }
+ else
+ {
+ return $this->request->variable($var_name, (int) $profile_row['field_default_value']);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_profile_field(&$field_value, $field_data)
+ {
+ $field_value = (bool) $field_value;
+
+ if (!$field_value && $field_data['field_required'])
+ {
+ return $this->user->lang('FIELD_REQUIRED', $field_data['lang_name']);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_value($field_value, $field_data)
+ {
+ $field_id = $field_data['field_id'];
+ $lang_id = $field_data['lang_id'];
+
+ if (!$this->lang_helper->is_set($field_id, $lang_id))
+ {
+ $this->lang_helper->get_option_lang($field_id, $lang_id, FIELD_BOOL, false);
+ }
+
+ if (!$field_value && $field_data['field_show_novalue'])
+ {
+ $field_value = $field_data['field_default_value'];
+ }
+
+ if ($field_data['field_length'] == 1)
+ {
+ return ($this->lang_helper->is_set($field_id, $lang_id, (int) $field_value)) ? $this->lang_helper->get($field_id, $lang_id, (int) $field_value) : null;
+ }
+ else if (!$field_value)
+ {
+ return null;
+ }
+ else
+ {
+ return $this->lang_helper->is_set($field_id, $lang_id, $field_value + 1);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function generate_field($profile_row, $preview_options = false)
+ {
+ $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
+ $field_ident = $profile_row['field_ident'];
+ $default_value = $profile_row['field_default_value'];
+
+ // checkbox - set the value to "true" if it has been set to 1
+ if ($profile_row['field_length'] == 2)
+ {
+ $value = ($this->request->is_set($field_ident) && $this->request->variable($field_ident, $default_value) == 1) ? true : ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $default_value : $this->user->profile_fields[$field_ident]);
+ }
+ else
+ {
+ $value = ($this->request->is_set($field_ident)) ? $this->request->variable($field_ident, $default_value) : ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $default_value : $this->user->profile_fields[$field_ident]);
+ }
+
+ $profile_row['field_value'] = (int) $value;
+ $this->template->assign_block_vars('bool', array_change_key_case($profile_row, CASE_UPPER));
+
+ if ($profile_row['field_length'] == 1)
+ {
+ if (!$this->lang_helper->is_set($profile_row['field_id'], $profile_row['lang_id'], 1))
+ {
+ $this->lang_helper->get_option_lang($profile_row['field_id'], $profile_row['lang_id'], $this->get_service_name(), $preview_options);
+ }
+
+ $options = $this->lang_helper->get($profile_row['field_id'], $profile_row['lang_id']);
+ foreach ($options as $option_id => $option_value)
+ {
+ $this->template->assign_block_vars('bool.options', array(
+ 'OPTION_ID' => $option_id,
+ 'CHECKED' => ($value == $option_id) ? ' checked="checked"' : '',
+ 'VALUE' => $option_value,
+ ));
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_field_ident($field_data)
+ {
+ return ($field_data['field_length'] == '1') ? '' : 'pf_' . $field_data['field_ident'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_database_column_type()
+ {
+ return 'TINT:2';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options($field_data)
+ {
+ $options = array(
+ 'lang_name' => 'string',
+ 'lang_options' => 'two_options',
+ );
+
+ if ($field_data['lang_explain'])
+ {
+ $options['lang_explain'] = 'text';
+ }
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options_input($field_data)
+ {
+ $field_data['l_lang_name'] = $this->request->variable('l_lang_name', array(0 => ''), true);
+ $field_data['l_lang_explain'] = $this->request->variable('l_lang_explain', array(0 => ''), true);
+ $field_data['l_lang_default_value'] = $this->request->variable('l_lang_default_value', array(0 => ''), true);
+
+ /**
+ * @todo check if this line is correct...
+ $field_data['l_lang_default_value'] = $this->request->variable('l_lang_default_value', array(0 => array('')), true);
+ */
+ $field_data['l_lang_options'] = $this->request->variable('l_lang_options', array(0 => array('')), true);
+
+ return $field_data;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_options_form(&$exclude_options, &$visibility_options)
+ {
+ $exclude_options[1][] = 'lang_options';
+
+ return $this->request->variable('lang_options', array(''), true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_options_on_submit($error, $field_data)
+ {
+ if (empty($field_data['lang_options'][0]) || empty($field_data['lang_options'][1]))
+ {
+ $error[] = $this->user->lang['NO_FIELD_ENTRIES'];
+ }
+
+ return $error;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_excluded_options($key, $action, $current_value, &$field_data, $step)
+ {
+ if ($step == 2 && $key == 'field_default_value')
+ {
+ // 'field_length' == 1 defines radio buttons. Possible values are 1 or 2 only.
+ // 'field_length' == 2 defines checkbox. Possible values are 0 or 1 only.
+ // If we switch the type on step 2, we have to adjust field value.
+ // 1 is a common value for the checkbox and radio buttons.
+
+ // Adjust unchecked checkbox value.
+ // If we return or save settings from 2nd/3rd page
+ // and the checkbox is unchecked, set the value to 0.
+ if ($this->request->is_set('step') && !$this->request->is_set($key))
+ {
+ return 0;
+ }
+
+ // If we switch to the checkbox type but former radio buttons value was 2,
+ // which is not the case for the checkbox, set it to 0 (unchecked).
+ if ($field_data['field_length'] == 2 && $current_value == 2)
+ {
+ return 0;
+ }
+ // If we switch to the radio buttons but the former checkbox value was 0,
+ // which is not the case for the radio buttons, set it to 0.
+ else if ($field_data['field_length'] == 1 && $current_value == 0)
+ {
+ return 2;
+ }
+ }
+
+ if ($step == 3 && ($field_data[$key] || $action != 'edit') && $key == 'l_lang_options')
+ {
+ $field_data[$key] = $this->request->variable($key, array(0 => array('')), true);
+
+ return $current_value;
+ }
+
+ return parent::get_excluded_options($key, $action, $current_value, $field_data, $step);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_hidden_fields($step, $key, $action, &$field_data)
+ {
+ if ($key == 'l_lang_options' && $this->request->is_set('l_lang_options'))
+ {
+ return $this->request->variable($key, array(array('')), true);
+ }
+ else if ($key == 'field_default_value')
+ {
+ return $this->request->variable($key, $field_data[$key]);
+ }
+ else
+ {
+ if (!$this->request->is_set($key))
+ {
+ return false;
+ }
+ else if ($key == 'field_ident' && isset($field_data[$key]))
+ {
+ return $field_data[$key];
+ }
+ else
+ {
+ return ($key == 'lang_options') ? $this->request->variable($key, array(''), true) : $this->request->variable($key, '', true);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function display_options(&$template_vars, &$field_data)
+ {
+ // Initialize these array elements if we are creating a new field
+ if (!sizeof($field_data['lang_options']))
+ {
+ // No options have been defined for a boolean field.
+ $field_data['lang_options'][0] = '';
+ $field_data['lang_options'][1] = '';
+ }
+
+ $template_vars = array_merge($template_vars, array(
+ 'S_BOOL' => true,
+ 'L_LANG_OPTIONS_EXPLAIN' => $this->user->lang['BOOL_ENTRIES_EXPLAIN'],
+ 'FIRST_LANG_OPTION' => $field_data['lang_options'][0],
+ 'SECOND_LANG_OPTION' => $field_data['lang_options'][1],
+ ));
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_date.php b/phpBB/phpbb/profilefields/type/type_date.php
new file mode 100644
index 0000000000..409c2e7be0
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_date.php
@@ -0,0 +1,358 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+class type_date extends type_base
+{
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\user $user User object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->request = $request;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name_short()
+ {
+ return 'date';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_options($default_lang_id, $field_data)
+ {
+ $profile_row = array(
+ 'var_name' => 'field_default_value',
+ 'lang_name' => $field_data['lang_name'],
+ 'lang_explain' => $field_data['lang_explain'],
+ 'lang_id' => $default_lang_id,
+ 'field_default_value' => $field_data['field_default_value'],
+ 'field_ident' => 'field_default_value',
+ 'field_type' => $this->get_service_name(),
+ 'field_length' => $field_data['field_length'],
+ );
+
+ $always_now = request_var('always_now', -1);
+ if ($always_now == -1)
+ {
+ $s_checked = ($field_data['field_default_value'] == 'now') ? true : false;
+ }
+ else
+ {
+ $s_checked = ($always_now) ? true : false;
+ }
+
+ $options = array(
+ 0 => array('TITLE' => $this->user->lang['DEFAULT_VALUE'], 'FIELD' => $this->process_field_row('preview', $profile_row)),
+ 1 => array('TITLE' => $this->user->lang['ALWAYS_TODAY'], 'FIELD' => '<label><input type="radio" class="radio" name="always_now" value="1"' . (($s_checked) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $this->user->lang['YES'] . '</label><label><input type="radio" class="radio" name="always_now" value="0"' . ((!$s_checked) ? ' checked="checked"' : '') . ' onchange="document.getElementById(\'add_profile_field\').submit();" /> ' . $this->user->lang['NO'] . '</label>'),
+ );
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => 10,
+ 'field_minlen' => 10,
+ 'field_maxlen' => 10,
+ 'field_validation' => '',
+ 'field_novalue' => ' 0- 0- 0',
+ 'field_default_value' => ' 0- 0- 0',
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_field_value($field_data)
+ {
+ if ($field_data['field_default_value'] == 'now')
+ {
+ $now = getdate();
+ $field_data['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
+ }
+
+ return $field_data['field_default_value'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_field($profile_row)
+ {
+ $var_name = 'pf_' . $profile_row['field_ident'];
+
+ if (!$this->request->is_set($var_name . '_day'))
+ {
+ if ($profile_row['field_default_value'] == 'now')
+ {
+ $now = getdate();
+ $profile_row['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
+ }
+ list($day, $month, $year) = explode('-', $profile_row['field_default_value']);
+ }
+ else
+ {
+ $day = $this->request->variable($var_name . '_day', 0);
+ $month = $this->request->variable($var_name . '_month', 0);
+ $year = $this->request->variable($var_name . '_year', 0);
+ }
+
+ return sprintf('%2d-%2d-%4d', $day, $month, $year);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_profile_field(&$field_value, $field_data)
+ {
+ $field_validate = explode('-', $field_value);
+
+ $day = (isset($field_validate[0])) ? (int) $field_validate[0] : 0;
+ $month = (isset($field_validate[1])) ? (int) $field_validate[1] : 0;
+ $year = (isset($field_validate[2])) ? (int) $field_validate[2] : 0;
+
+ if ((!$day || !$month || !$year) && !$field_data['field_required'])
+ {
+ return false;
+ }
+
+ if ((!$day || !$month || !$year) && $field_data['field_required'])
+ {
+ return $this->user->lang('FIELD_REQUIRED', $field_data['lang_name']);
+ }
+
+ if ($day < 0 || $day > 31 || $month < 0 || $month > 12 || ($year < 1901 && $year > 0) || $year > gmdate('Y', time()) + 50)
+ {
+ return $this->user->lang('FIELD_INVALID_DATE', $field_data['lang_name']);
+ }
+
+ if (checkdate($month, $day, $year) === false)
+ {
+ return $this->user->lang('FIELD_INVALID_DATE', $field_data['lang_name']);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_value($field_value, $field_data)
+ {
+ $date = explode('-', $field_value);
+ $day = (isset($date[0])) ? (int) $date[0] : 0;
+ $month = (isset($date[1])) ? (int) $date[1] : 0;
+ $year = (isset($date[2])) ? (int) $date[2] : 0;
+
+ if (!$day && !$month && !$year && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+ else if ($day && $month && $year)
+ {
+ // Date should display as the same date for every user regardless of timezone
+ return $this->user->create_datetime()
+ ->setDate($year, $month, $day)
+ ->setTime(0, 0, 0)
+ ->format($this->user->lang['DATE_FORMAT'], true);
+ }
+
+ return $field_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function generate_field($profile_row, $preview_options = false)
+ {
+ $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
+ $field_ident = $profile_row['field_ident'];
+
+ $now = getdate();
+
+ if (!$this->request->is_set($profile_row['field_ident'] . '_day'))
+ {
+ if ($profile_row['field_default_value'] == 'now')
+ {
+ $profile_row['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
+ }
+ list($day, $month, $year) = explode('-', ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $profile_row['field_default_value'] : $this->user->profile_fields[$field_ident]));
+ }
+ else
+ {
+ if ($preview_options !== false && $profile_row['field_default_value'] == 'now')
+ {
+ $profile_row['field_default_value'] = sprintf('%2d-%2d-%4d', $now['mday'], $now['mon'], $now['year']);
+ list($day, $month, $year) = explode('-', ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $profile_row['field_default_value'] : $this->user->profile_fields[$field_ident]));
+ }
+ else
+ {
+ $day = $this->request->variable($profile_row['field_ident'] . '_day', 0);
+ $month = $this->request->variable($profile_row['field_ident'] . '_month', 0);
+ $year = $this->request->variable($profile_row['field_ident'] . '_year', 0);
+ }
+ }
+
+ $profile_row['s_day_options'] = '<option value="0"' . ((!$day) ? ' selected="selected"' : '') . '>--</option>';
+ for ($i = 1; $i < 32; $i++)
+ {
+ $profile_row['s_day_options'] .= '<option value="' . $i . '"' . (($i == $day) ? ' selected="selected"' : '') . ">$i</option>";
+ }
+
+ $profile_row['s_month_options'] = '<option value="0"' . ((!$month) ? ' selected="selected"' : '') . '>--</option>';
+ for ($i = 1; $i < 13; $i++)
+ {
+ $profile_row['s_month_options'] .= '<option value="' . $i . '"' . (($i == $month) ? ' selected="selected"' : '') . ">$i</option>";
+ }
+
+ $profile_row['s_year_options'] = '<option value="0"' . ((!$year) ? ' selected="selected"' : '') . '>--</option>';
+ for ($i = $now['year'] - 100; $i <= $now['year'] + 100; $i++)
+ {
+ $profile_row['s_year_options'] .= '<option value="' . $i . '"' . (($i == $year) ? ' selected="selected"' : '') . ">$i</option>";
+ }
+
+ $profile_row['field_value'] = 0;
+ $this->template->assign_block_vars('date', array_change_key_case($profile_row, CASE_UPPER));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_field_ident($field_data)
+ {
+ return '';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_database_column_type()
+ {
+ return 'VCHAR:10';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options($field_data)
+ {
+ $options = array(
+ 'lang_name' => 'string',
+ );
+
+ if ($field_data['lang_explain'])
+ {
+ $options['lang_explain'] = 'text';
+ }
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_excluded_options($key, $action, $current_value, &$field_data, $step)
+ {
+ if ($step == 2 && $key == 'field_default_value')
+ {
+ $always_now = $this->request->variable('always_now', -1);
+
+ if ($always_now == 1 || ($always_now === -1 && $current_value == 'now'))
+ {
+ $now = getdate();
+
+ $field_data['field_default_value_day'] = $now['mday'];
+ $field_data['field_default_value_month'] = $now['mon'];
+ $field_data['field_default_value_year'] = $now['year'];
+ $current_value = 'now';
+ $this->request->overwrite('field_default_value', $current_value, \phpbb\request\request_interface::POST);
+ }
+ else
+ {
+ if ($this->request->is_set('field_default_value_day'))
+ {
+ $field_data['field_default_value_day'] = $this->request->variable('field_default_value_day', 0);
+ $field_data['field_default_value_month'] = $this->request->variable('field_default_value_month', 0);
+ $field_data['field_default_value_year'] = $this->request->variable('field_default_value_year', 0);
+ $current_value = sprintf('%2d-%2d-%4d', $field_data['field_default_value_day'], $field_data['field_default_value_month'], $field_data['field_default_value_year']);
+ $this->request->overwrite('field_default_value', $current_value, \phpbb\request\request_interface::POST);
+ }
+ else
+ {
+ list($field_data['field_default_value_day'], $field_data['field_default_value_month'], $field_data['field_default_value_year']) = explode('-', $current_value);
+ }
+ }
+
+ return $current_value;
+ }
+
+
+ return parent::get_excluded_options($key, $action, $current_value, $field_data, $step);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_hidden_fields($step, $key, $action, &$field_data)
+ {
+ if ($key == 'field_default_value')
+ {
+ $always_now = $this->request->variable('always_now', 0);
+
+ if ($always_now)
+ {
+ return 'now';
+ }
+ else if ($this->request->is_set('field_default_value_day'))
+ {
+ $field_data['field_default_value_day'] = $this->request->variable('field_default_value_day', 0);
+ $field_data['field_default_value_month'] = $this->request->variable('field_default_value_month', 0);
+ $field_data['field_default_value_year'] = $this->request->variable('field_default_value_year', 0);
+ return sprintf('%2d-%2d-%4d', $field_data['field_default_value_day'], $field_data['field_default_value_month'], $field_data['field_default_value_year']);
+ }
+ }
+
+ return parent::prepare_hidden_fields($step, $key, $action, $field_data);
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_dropdown.php b/phpBB/phpbb/profilefields/type/type_dropdown.php
new file mode 100644
index 0000000000..9a6545249d
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_dropdown.php
@@ -0,0 +1,298 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+class type_dropdown extends type_base
+{
+ /**
+ * Profile fields language helper
+ * @var \phpbb\profilefields\lang_helper
+ */
+ protected $lang_helper;
+
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\profilefields\lang_helper $lang_helper Profile fields language helper
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\user $user User object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct(\phpbb\profilefields\lang_helper $lang_helper, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->lang_helper = $lang_helper;
+ $this->request = $request;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name_short()
+ {
+ return 'dropdown';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_options($default_lang_id, $field_data)
+ {
+ $profile_row[0] = array(
+ 'var_name' => 'field_default_value',
+ 'field_id' => 1,
+ 'lang_name' => $field_data['lang_name'],
+ 'lang_explain' => $field_data['lang_explain'],
+ 'lang_id' => $default_lang_id,
+ 'field_default_value' => $field_data['field_default_value'],
+ 'field_ident' => 'field_default_value',
+ 'field_type' => $this->get_service_name(),
+ 'lang_options' => $field_data['lang_options'],
+ );
+
+ $profile_row[1] = $profile_row[0];
+ $profile_row[1]['var_name'] = 'field_novalue';
+ $profile_row[1]['field_ident'] = 'field_novalue';
+ $profile_row[1]['field_default_value'] = $field_data['field_novalue'];
+
+ $options = array(
+ 0 => array('TITLE' => $this->user->lang['DEFAULT_VALUE'], 'FIELD' => $this->process_field_row('preview', $profile_row[0])),
+ 1 => array('TITLE' => $this->user->lang['NO_VALUE_OPTION'], 'EXPLAIN' => $this->user->lang['NO_VALUE_OPTION_EXPLAIN'], 'FIELD' => $this->process_field_row('preview', $profile_row[1])),
+ );
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => 0,
+ 'field_minlen' => 0,
+ 'field_maxlen' => 5,
+ 'field_validation' => '',
+ 'field_novalue' => 0,
+ 'field_default_value' => 0,
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_field_value($field_data)
+ {
+ return $field_data['field_default_value'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_field($profile_row)
+ {
+ $var_name = 'pf_' . $profile_row['field_ident'];
+ return $this->request->variable($var_name, (int) $profile_row['field_default_value']);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_profile_field(&$field_value, $field_data)
+ {
+ $field_value = (int) $field_value;
+
+ // retrieve option lang data if necessary
+ if (!$this->lang_helper->is_set($field_data['field_id'], $field_data['lang_id'], 1))
+ {
+ $this->lang_helper->get_option_lang($field_data['field_id'], $field_data['lang_id'], $this->get_service_name(), false);
+ }
+
+ if (!$this->lang_helper->is_set($field_data['field_id'], $field_data['lang_id'], $field_value))
+ {
+ return $this->user->lang('FIELD_INVALID_VALUE', $field_data['lang_name']);
+ }
+
+ if ($field_value == $field_data['field_novalue'] && $field_data['field_required'])
+ {
+ return $this->user->lang('FIELD_REQUIRED', $field_data['lang_name']);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_value($field_value, $field_data)
+ {
+ $field_id = $field_data['field_id'];
+ $lang_id = $field_data['lang_id'];
+ if (!$this->lang_helper->is_set($field_id, $lang_id))
+ {
+ $this->lang_helper->get_option_lang($field_id, $lang_id, $this->get_service_name(), false);
+ }
+
+ if ($field_value == $field_data['field_novalue'] && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+
+ $field_value = (int) $field_value;
+
+ // User not having a value assigned
+ if (!$this->lang_helper->is_set($field_id, $lang_id, $field_value))
+ {
+ if ($field_data['field_show_novalue'])
+ {
+ $field_value = $field_data['field_novalue'];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ return $this->lang_helper->get($field_id, $lang_id, $field_value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function generate_field($profile_row, $preview_options = false)
+ {
+ $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
+ $field_ident = $profile_row['field_ident'];
+ $default_value = $profile_row['field_default_value'];
+
+ $value = ($this->request->is_set($field_ident)) ? $this->request->variable($field_ident, $default_value) : ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $default_value : $this->user->profile_fields[$field_ident]);
+
+ if (!$this->lang_helper->is_set($profile_row['field_id'], $profile_row['lang_id'], 1))
+ {
+ $this->lang_helper->get_option_lang($profile_row['field_id'], $profile_row['lang_id'], $this->get_service_name(), $preview_options);
+ }
+
+ $profile_row['field_value'] = (int) $value;
+ $this->template->assign_block_vars('dropdown', array_change_key_case($profile_row, CASE_UPPER));
+
+ $options = $this->lang_helper->get($profile_row['field_id'], $profile_row['lang_id']);
+ foreach ($options as $option_id => $option_value)
+ {
+ $this->template->assign_block_vars('dropdown.options', array(
+ 'OPTION_ID' => $option_id,
+ 'SELECTED' => ($value == $option_id) ? ' selected="selected"' : '',
+ 'VALUE' => $option_value,
+ ));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_database_column_type()
+ {
+ return 'UINT';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options($field_data)
+ {
+ $options = array(
+ 'lang_name' => 'string',
+ 'lang_options' => 'optionfield',
+ );
+
+ if ($field_data['lang_explain'])
+ {
+ $options['lang_explain'] = 'text';
+ }
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_options_form(&$exclude_options, &$visibility_options)
+ {
+ $exclude_options[1][] = 'lang_options';
+
+ return $this->request->variable('lang_options', '', true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_options_on_submit($error, $field_data)
+ {
+ if (!sizeof($field_data['lang_options']))
+ {
+ $error[] = $this->user->lang['NO_FIELD_ENTRIES'];
+ }
+
+ return $error;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_excluded_options($key, $action, $current_value, &$field_data, $step)
+ {
+ if ($step == 2 && $key == 'field_maxlen')
+ {
+ // Get the number of options if this key is 'field_maxlen'
+ return sizeof(explode("\n", $this->request->variable('lang_options', '', true)));
+ }
+
+
+ return parent::get_excluded_options($key, $action, $current_value, $field_data, $step);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function display_options(&$template_vars, &$field_data)
+ {
+ // Initialize these array elements if we are creating a new field
+ if (!sizeof($field_data['lang_options']))
+ {
+ // No options have been defined for the dropdown menu
+ $field_data['lang_options'] = array();
+ }
+
+ $template_vars = array_merge($template_vars, array(
+ 'S_DROPDOWN' => true,
+ 'L_LANG_OPTIONS_EXPLAIN' => $this->user->lang['DROPDOWN_ENTRIES_EXPLAIN'],
+ 'LANG_OPTIONS' => implode("\n", $field_data['lang_options']),
+ ));
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_int.php b/phpBB/phpbb/profilefields/type/type_int.php
new file mode 100644
index 0000000000..6ffc8fbea0
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_int.php
@@ -0,0 +1,234 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+class type_int extends type_base
+{
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\user $user User object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->request = $request;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name_short()
+ {
+ return 'int';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_options($default_lang_id, $field_data)
+ {
+ $options = array(
+ 0 => array('TITLE' => $this->user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_length" size="5" value="' . $field_data['field_length'] . '" />'),
+ 1 => array('TITLE' => $this->user->lang['MIN_FIELD_NUMBER'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_minlen" size="5" value="' . $field_data['field_minlen'] . '" />'),
+ 2 => array('TITLE' => $this->user->lang['MAX_FIELD_NUMBER'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_maxlen" size="5" value="' . $field_data['field_maxlen'] . '" />'),
+ 3 => array('TITLE' => $this->user->lang['DEFAULT_VALUE'], 'FIELD' => '<input type="post" name="field_default_value" value="' . $field_data['field_default_value'] . '" />'),
+ );
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => 5,
+ 'field_minlen' => 0,
+ 'field_maxlen' => 100,
+ 'field_validation' => '',
+ 'field_novalue' => 0,
+ 'field_default_value' => 0,
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_field_value($field_data)
+ {
+ if ($field_data['field_default_value'] === '')
+ {
+ // We cannot insert an empty string into an integer column.
+ return null;
+ }
+
+ return $field_data['field_default_value'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_field($profile_row)
+ {
+ $var_name = 'pf_' . $profile_row['field_ident'];
+ if ($this->request->is_set($var_name) && $this->request->variable($var_name, '') === '')
+ {
+ return null;
+ }
+ else
+ {
+ return $this->request->variable($var_name, (int) $profile_row['field_default_value']);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_profile_field(&$field_value, $field_data)
+ {
+ if (trim($field_value) === '' && !$field_data['field_required'])
+ {
+ return false;
+ }
+
+ $field_value = (int) $field_value;
+
+ if ($field_value < $field_data['field_minlen'])
+ {
+ return $this->user->lang('FIELD_TOO_SMALL', (int) $row['field_minlen'], $row['lang_name']);
+ }
+ else if ($field_value > $field_data['field_maxlen'])
+ {
+ return $this->user->lang('FIELD_TOO_LARGE', (int) $row['field_maxlen'], $row['lang_name']);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_value($field_value, $field_data)
+ {
+ if ($field_value === '' && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+ return (int) $field_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function generate_field($profile_row, $preview_options = false)
+ {
+ $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
+ $field_ident = $profile_row['field_ident'];
+ $default_value = $profile_row['field_default_value'];
+
+ if ($this->request->is_set($field_ident))
+ {
+ $value = ($this->request->variable($field_ident, '') === '') ? null : $this->request->variable($field_ident, $default_value);
+ }
+ else
+ {
+ if ($preview_options === false && array_key_exists($field_ident, $this->user->profile_fields) && is_null($this->user->profile_fields[$field_ident]))
+ {
+ $value = null;
+ }
+ else if (!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false)
+ {
+ $value = $default_value;
+ }
+ else
+ {
+ $value = $this->user->profile_fields[$field_ident];
+ }
+ }
+
+ $profile_row['field_value'] = (is_null($value) || $value === '') ? '' : (int) $value;
+
+ $this->template->assign_block_vars('int', array_change_key_case($profile_row, CASE_UPPER));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_field_ident($field_data)
+ {
+ return 'pf_' . $field_data['field_ident'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_database_column_type()
+ {
+ return 'BINT';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options($field_data)
+ {
+ $options = array(
+ 'lang_name' => 'string',
+ );
+
+ if ($field_data['lang_explain'])
+ {
+ $options['lang_explain'] = 'text';
+ }
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_excluded_options($key, $action, $current_value, &$field_data, $step)
+ {
+ if ($step == 2 && $key == 'field_default_value')
+ {
+ // Permit an empty string
+ if ($action == 'create' && $this->request->variable('field_default_value', '') === '')
+ {
+ return '';
+ }
+ }
+
+ return parent::get_excluded_options($key, $action, $current_value, $field_data, $step);
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_interface.php b/phpBB/phpbb/profilefields/type/type_interface.php
new file mode 100644
index 0000000000..7d0cf7662f
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_interface.php
@@ -0,0 +1,177 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+interface type_interface
+{
+ /**
+ * Get the translated name of the type
+ *
+ * @return string Translated name of the field type
+ */
+ public function get_name();
+
+ /**
+ * Get the short name of the type, used for error messages and template loops
+ *
+ * @return string lowercase version of the fields type
+ */
+ public function get_name_short();
+
+ /**
+ * Get the name of service representing the type
+ *
+ * @return string lowercase version of the fields type
+ */
+ public function get_service_name();
+
+ /**
+ * Get dropdown options for second step in ACP
+ *
+ * @param string $default_lang_id ID of the default language
+ * @param array $field_data Array with data for this field
+ * @return array with the acp options
+ */
+ public function get_options($default_lang_id, $field_data);
+
+ /**
+ * Get default values for the options of this type
+ *
+ * @return array with values like default field size and more
+ */
+ public function get_default_option_values();
+
+ /**
+ * Get default value for this type
+ *
+ * @param array $field_data Array with data for this field
+ * @return mixed default value for new users when no value is given
+ */
+ public function get_default_field_value($field_data);
+
+ /**
+ * Get profile field value on submit
+ *
+ * @param array $profile_row Array with data for this field
+ * @return mixed Submitted value of the profile field
+ */
+ public function get_profile_field($profile_row);
+
+ /**
+ * Validate entered profile field data
+ *
+ * @param mixed $field_value Field value to validate
+ * @param array $field_data Array with requirements of the field
+ * @return mixed String with the error message
+ */
+ public function validate_profile_field(&$field_value, $field_data);
+
+ /**
+ * Get Profile Value for display
+ *
+ * @param mixed $field_value Field value as stored in the database
+ * @param array $field_data Array with requirements of the field
+ * @return mixed Field value to display
+ */
+ public function get_profile_value($field_value, $field_data);
+
+ /**
+ * Generate the input field for display
+ *
+ * @param array $profile_row Array with data for this field
+ * @param mixed $preview_options When previewing we use different data
+ * @return null
+ */
+ public function generate_field($profile_row, $preview_options = false);
+
+ /**
+ * Get the ident of the field
+ *
+ * Some types are multivalue, we can't give them a field_id
+ * as we would not know which to pick.
+ *
+ * @param array $field_data Array with data for this field
+ * @return string ident of the field
+ */
+ public function get_field_ident($field_data);
+
+ /**
+ * Get the column type for the database
+ *
+ * @return string Returns the database column type
+ */
+ public function get_database_column_type();
+
+ /**
+ * Get the options we need to display for the language input fields in the ACP
+ *
+ * @param array $field_data Array with data for this field
+ * @return array Returns the language options we need to generate
+ */
+ public function get_language_options($field_data);
+
+ /**
+ * Get the input for the supplied language options
+ *
+ * @param array $field_data Array with data for this field
+ * @return array Returns the language options we need to generate
+ */
+ public function get_language_options_input($field_data);
+
+ /**
+ * Allows exclusion of options in single steps of the creation process
+ *
+ * @param array $exclude_options Array with options that should be excluded in the steps
+ * @param array $visibility_options Array with options responsible for the fields visibility
+ * @return mixed Returns the provided language options
+ */
+ public function prepare_options_form(&$exclude_options, &$visibility_options);
+
+ /**
+ * Allows exclusion of options in single steps of the creation process
+ *
+ * @param array $error Array with error messages
+ * @param array $field_data Array with data for this field
+ * @return array Array with error messages
+ */
+ public function validate_options_on_submit($error, $field_data);
+
+ /**
+ * Allows manipulating the intended variables if needed
+ *
+ * @param string $key Name of the option
+ * @param string $action Currently performed action (create|edit)
+ * @param mixed $current_value Currently value of the option
+ * @param array $field_data Array with data for this field
+ * @param int $step Step on which the option is excluded
+ * @return mixed Final value of the option
+ */
+ public function get_excluded_options($key, $action, $current_value, &$field_data, $step);
+
+ /**
+ * Allows manipulating the intended variables if needed
+ *
+ * @param string $key Name of the option
+ * @param int $step Step on which the option is hidden
+ * @param string $action Currently performed action (create|edit)
+ * @param array $field_data Array with data for this field
+ * @return mixed Final value of the option
+ */
+ public function prepare_hidden_fields($step, $key, $action, &$field_data);
+
+ /**
+ * Allows assigning of additional template variables
+ *
+ * @param array $template_vars Template variables we are going to assign
+ * @param array $field_data Array with data for this field
+ * @return null
+ */
+ public function display_options(&$template_vars, &$field_data);
+}
diff --git a/phpBB/phpbb/profilefields/type/type_string.php b/phpBB/phpbb/profilefields/type/type_string.php
new file mode 100644
index 0000000000..12a14d9b83
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_string.php
@@ -0,0 +1,156 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+class type_string extends type_string_common
+{
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\user $user User object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->request = $request;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name_short()
+ {
+ return 'string';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_options($default_lang_id, $field_data)
+ {
+ $options = array(
+ 0 => array('TITLE' => $this->user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" name="field_length" size="5" value="' . $field_data['field_length'] . '" />'),
+ 1 => array('TITLE' => $this->user->lang['MIN_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" name="field_minlen" size="5" value="' . $field_data['field_minlen'] . '" />'),
+ 2 => array('TITLE' => $this->user->lang['MAX_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" size="5" value="' . $field_data['field_maxlen'] . '" />'),
+ 3 => array('TITLE' => $this->user->lang['FIELD_VALIDATION'], 'FIELD' => '<select name="field_validation">' . $this->validate_options($field_data) . '</select>'),
+ );
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => 10,
+ 'field_minlen' => 0,
+ 'field_maxlen' => 20,
+ 'field_validation' => '.*',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_field($profile_row)
+ {
+ $var_name = 'pf_' . $profile_row['field_ident'];
+ return $this->request->variable($var_name, (string) $profile_row['field_default_value'], true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_profile_field(&$field_value, $field_data)
+ {
+ return $this->validate_string_profile_field('string', $field_value, $field_data);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function generate_field($profile_row, $preview_options = false)
+ {
+ $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
+ $field_ident = $profile_row['field_ident'];
+ $default_value = $profile_row['lang_default_value'];
+ $profile_row['field_value'] = ($this->request->is_set($field_ident)) ? $this->request->variable($field_ident, $default_value, true) : ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $default_value : $this->user->profile_fields[$field_ident]);
+
+ $this->template->assign_block_vars('string', array_change_key_case($profile_row, CASE_UPPER));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_database_column_type()
+ {
+ return 'VCHAR';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options($field_data)
+ {
+ $options = array(
+ 'lang_name' => 'string',
+ );
+
+ if ($field_data['lang_explain'])
+ {
+ $options['lang_explain'] = 'text';
+ }
+
+ if (strlen($field_data['lang_default_value']))
+ {
+ $options['lang_default_value'] = 'string';
+ }
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function display_options(&$template_vars, &$field_data)
+ {
+ $template_vars = array_merge($template_vars, array(
+ 'S_STRING' => true,
+ 'L_DEFAULT_VALUE_EXPLAIN' => $this->user->lang['STRING_DEFAULT_VALUE_EXPLAIN'],
+ 'LANG_DEFAULT_VALUE' => $field_data['lang_default_value'],
+ ));
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_string_common.php b/phpBB/phpbb/profilefields/type/type_string_common.php
new file mode 100644
index 0000000000..d322099c34
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_string_common.php
@@ -0,0 +1,114 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+abstract class type_string_common extends type_base
+{
+ /**
+ * Return possible validation options
+ */
+ function validate_options($field_data)
+ {
+ $validate_ary = array('CHARS_ANY' => '.*', 'NUMBERS_ONLY' => '[0-9]+', 'ALPHA_ONLY' => '[\w]+', 'ALPHA_SPACERS' => '[\w_\+\. \-\[\]]+');
+
+ $validate_options = '';
+ foreach ($validate_ary as $lang => $value)
+ {
+ $selected = ($field_data['field_validation'] == $value) ? ' selected="selected"' : '';
+ $validate_options .= '<option value="' . $value . '"' . $selected . '>' . $this->user->lang[$lang] . '</option>';
+ }
+
+ return $validate_options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_field_value($field_data)
+ {
+ return $field_data['lang_default_value'];
+ }
+
+ /**
+ * Validate entered profile field data
+ *
+ * @param string $field_type Field type (string or text)
+ * @param mixed $field_value Field value to validate
+ * @param array $field_data Array with requirements of the field
+ * @return mixed String with key of the error language string, false otherwise
+ */
+ public function validate_string_profile_field($field_type, &$field_value, $field_data)
+ {
+ if (trim($field_value) === '' && !$field_data['field_required'])
+ {
+ return false;
+ }
+ else if (trim($field_value) === '' && $field_data['field_required'])
+ {
+ return $this->user->lang('FIELD_REQUIRED', $field_data['lang_name']);
+ }
+
+ if ($field_data['field_minlen'] && utf8_strlen($field_value) < $field_data['field_minlen'])
+ {
+ return $this->user->lang('FIELD_TOO_SHORT', (int) $row['field_minlen'], $row['lang_name']);
+ }
+ else if ($field_data['field_maxlen'] && utf8_strlen($field_value) > $field_data['field_maxlen'])
+ {
+ return $this->user->lang('FIELD_TOO_LONG', (int) $row['field_maxlen'], $row['lang_name']);
+ }
+
+ if (!empty($field_data['field_validation']) && $field_data['field_validation'] != '.*')
+ {
+ $field_validate = ($field_type != 'text') ? $field_value : bbcode_nl2br($field_value);
+ if (!preg_match('#^' . str_replace('\\\\', '\\', $field_data['field_validation']) . '$#i', $field_validate))
+ {
+ switch ($row['field_validation'])
+ {
+ case '[0-9]+':
+ return $this->user->lang('FIELD_INVALID_CHARS_NUMBERS_ONLY', $row['lang_name']);
+
+ case '[\w]+':
+ return $this->user->lang('FIELD_INVALID_CHARS_ALPHA_ONLY', $row['lang_name']);
+
+ case '[\w_\+\. \-\[\]]+':
+ return $this->user->lang('FIELD_INVALID_CHARS_SPACERS_ONLY', $row['lang_name']);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_value($field_value, $field_data)
+ {
+ if (!$field_value && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+
+ $field_value = make_clickable($field_value);
+ $field_value = censor_text($field_value);
+ $field_value = bbcode_nl2br($field_value);
+ return $field_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_options_form(&$exclude_options, &$visibility_options)
+ {
+ $exclude_options[1][] = 'lang_default_value';
+
+ return $this->request->variable('lang_options', '', true);
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_text.php b/phpBB/phpbb/profilefields/type/type_text.php
new file mode 100644
index 0000000000..660bb20ef8
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_text.php
@@ -0,0 +1,201 @@
+<?php
+/**
+*
+* @package phpBB
+* @copyright (c) 2014 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+class type_text extends type_string_common
+{
+ /**
+ * Request object
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * Template object
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\user $user User object
+ * @param string $language_table Table where the language strings are stored
+ */
+ public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->request = $request;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name_short()
+ {
+ return 'text';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_options($default_lang_id, $field_data)
+ {
+ $options = array(
+ 0 => array('TITLE' => $this->user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" max="99999" name="rows" size="5" value="' . $field_data['rows'] . '" /> ' . $this->user->lang['ROWS'] . '</dd><dd><input type="number" min="0" max="99999" name="columns" size="5" value="' . $field_data['columns'] . '" /> ' . $this->user->lang['COLUMNS'] . ' <input type="hidden" name="field_length" value="' . $field_data['field_length'] . '" />'),
+ 1 => array('TITLE' => $this->user->lang['MIN_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" max="9999999999" name="field_minlen" size="10" value="' . $field_data['field_minlen'] . '" />'),
+ 2 => array('TITLE' => $this->user->lang['MAX_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" max="9999999999" name="field_maxlen" size="10" value="' . $field_data['field_maxlen'] . '" />'),
+ 3 => array('TITLE' => $this->user->lang['FIELD_VALIDATION'], 'FIELD' => '<select name="field_validation">' . $this->validate_options($field_data) . '</select>'),
+ );
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => '5|80',
+ 'field_minlen' => 0,
+ 'field_maxlen' => 1000,
+ 'field_validation' => '.*',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_field($profile_row)
+ {
+ $var_name = 'pf_' . $profile_row['field_ident'];
+ return $this->request->variable($var_name, (string) $profile_row['field_default_value'], true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_profile_field(&$field_value, $field_data)
+ {
+ return $this->validate_string_profile_field('text', $field_value, $field_data);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function generate_field($profile_row, $preview_options = false)
+ {
+ $field_length = explode('|', $profile_row['field_length']);
+ $profile_row['field_rows'] = $field_length[0];
+ $profile_row['field_cols'] = $field_length[1];
+ $profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
+ $field_ident = $profile_row['field_ident'];
+ $default_value = $profile_row['lang_default_value'];
+
+ $profile_row['field_value'] = ($this->request->is_set($field_ident)) ? $this->request->variable($field_ident, $default_value, true) : ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $default_value : $this->user->profile_fields[$field_ident]);
+
+ $this->template->assign_block_vars('text', array_change_key_case($profile_row, CASE_UPPER));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_database_column_type()
+ {
+ return 'MTEXT';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_language_options($field_data)
+ {
+ $options = array(
+ 'lang_name' => 'string',
+ );
+
+ if ($field_data['lang_explain'])
+ {
+ $options['lang_explain'] = 'text';
+ }
+
+ if (strlen($field_data['lang_default_value']))
+ {
+ $options['lang_default_value'] = 'text';
+ }
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_excluded_options($key, $action, $current_value, &$field_data, $step)
+ {
+ if ($step == 2 && $key == 'field_length')
+ {
+ if ($this->request->is_set('rows'))
+ {
+ $field_data['rows'] = $this->request->variable('rows', 0);
+ $field_data['columns'] = $this->request->variable('columns', 0);
+ $current_value = $field_data['rows'] . '|' . $field_data['columns'];
+ }
+ else
+ {
+ $row_col = explode('|', $current_value);
+ $field_data['rows'] = $row_col[0];
+ $field_data['columns'] = $row_col[1];
+ }
+
+ return $current_value;
+ }
+
+ return parent::get_excluded_options($key, $action, $current_value, $field_data, $step);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function prepare_hidden_fields($step, $key, $action, &$field_data)
+ {
+ if ($key == 'field_length' && $this->request->is_set('rows'))
+ {
+ $field_data['rows'] = $this->request->variable('rows', 0);
+ $field_data['columns'] = $this->request->variable('columns', 0);
+ return $field_data['rows'] . '|' . $field_data['columns'];
+ }
+
+ return parent::prepare_hidden_fields($step, $key, $action, $field_data);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function display_options(&$template_vars, &$field_data)
+ {
+ $template_vars = array_merge($template_vars, array(
+ 'S_TEXT' => true,
+ 'L_DEFAULT_VALUE_EXPLAIN' => $this->user->lang['TEXT_DEFAULT_VALUE_EXPLAIN'],
+ 'LANG_DEFAULT_VALUE' => $field_data['lang_default_value'],
+ ));
+ }
+}
diff --git a/phpBB/phpbb/search/fulltext_mysql.php b/phpBB/phpbb/search/fulltext_mysql.php
index cdd2da222f..509b73e26e 100644
--- a/phpBB/phpbb/search/fulltext_mysql.php
+++ b/phpBB/phpbb/search/fulltext_mysql.php
@@ -216,7 +216,7 @@ class fulltext_mysql extends \phpbb\search\base
// We limit the number of allowed keywords to minimize load on the database
if ($this->config['max_num_search_keywords'] && sizeof($this->split_words) > $this->config['max_num_search_keywords'])
{
- trigger_error($this->user->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', $this->config['max_num_search_keywords'], sizeof($this->split_words)));
+ trigger_error($this->user->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', (int) $this->config['max_num_search_keywords'], sizeof($this->split_words)));
}
// to allow phrase search, we need to concatenate quoted words
diff --git a/phpBB/phpbb/search/fulltext_native.php b/phpBB/phpbb/search/fulltext_native.php
index 1b314a24d3..1a89182978 100644
--- a/phpBB/phpbb/search/fulltext_native.php
+++ b/phpBB/phpbb/search/fulltext_native.php
@@ -277,7 +277,7 @@ class fulltext_native extends \phpbb\search\base
// We limit the number of allowed keywords to minimize load on the database
if ($this->config['max_num_search_keywords'] && $num_keywords > $this->config['max_num_search_keywords'])
{
- trigger_error($this->user->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', $this->config['max_num_search_keywords'], $num_keywords));
+ trigger_error($this->user->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', (int) $this->config['max_num_search_keywords'], $num_keywords));
}
// $keywords input format: each word separated by a space, words in a bracket are not separated
diff --git a/phpBB/phpbb/template/twig/environment.php b/phpBB/phpbb/template/twig/environment.php
index 24bd55b3c5..aa55f1e011 100644
--- a/phpBB/phpbb/template/twig/environment.php
+++ b/phpBB/phpbb/template/twig/environment.php
@@ -11,15 +11,15 @@ namespace phpbb\template\twig;
class environment extends \Twig_Environment
{
- /** @var array */
- protected $phpbb_extensions;
-
/** @var \phpbb\config\config */
protected $phpbb_config;
/** @var \phpbb\path_helper */
protected $phpbb_path_helper;
+ /** @var \phpbb\extension\manager */
+ protected $extension_manager;
+
/** @var string */
protected $phpbb_root_path;
@@ -33,18 +33,19 @@ class environment extends \Twig_Environment
* Constructor
*
* @param \phpbb\config\config $phpbb_config
- * @param array $phpbb_extensions Array of enabled extensions (name => path)
* @param \phpbb\path_helper
+ * @param \phpbb\extension\manager
* @param string $phpbb_root_path
* @param Twig_LoaderInterface $loader
* @param array $options Array of options to pass to Twig
*/
- public function __construct($phpbb_config, $phpbb_extensions, \phpbb\path_helper $path_helper, \Twig_LoaderInterface $loader = null, $options = array())
+ public function __construct($phpbb_config, \phpbb\path_helper $path_helper, \phpbb\extension\manager $extension_manager = null, \Twig_LoaderInterface $loader = null, $options = array())
{
$this->phpbb_config = $phpbb_config;
- $this->phpbb_extensions = $phpbb_extensions;
$this->phpbb_path_helper = $path_helper;
+ $this->extension_manager = $extension_manager;
+
$this->phpbb_root_path = $this->phpbb_path_helper->get_phpbb_root_path();
$this->web_root_path = $this->phpbb_path_helper->get_web_root_path();
@@ -60,7 +61,7 @@ class environment extends \Twig_Environment
*/
public function get_phpbb_extensions()
{
- return $this->phpbb_extensions;
+ return ($this->extension_manager) ? $this->extension_manager->all_enabled() : array();
}
/**
diff --git a/phpBB/phpbb/template/twig/twig.php b/phpBB/phpbb/template/twig/twig.php
index ddadcfd89a..83630f5992 100644
--- a/phpBB/phpbb/template/twig/twig.php
+++ b/phpBB/phpbb/template/twig/twig.php
@@ -94,8 +94,8 @@ class twig extends \phpbb\template\base
$this->twig = new \phpbb\template\twig\environment(
$this->config,
- ($this->extension_manager) ? $this->extension_manager->all_enabled() : array(),
$this->path_helper,
+ $this->extension_manager,
$loader,
array(
'cache' => (defined('IN_INSTALL')) ? false : $this->cachepath,
diff --git a/phpBB/posting.php b/phpBB/posting.php
index 0d2cff40bc..902f1b2175 100644
--- a/phpBB/posting.php
+++ b/phpBB/posting.php
@@ -255,8 +255,18 @@ if (!$auth->acl_get('f_read', $forum_id))
{
trigger_error('USER_CANNOT_READ');
}
+ $message = $user->lang['LOGIN_EXPLAIN_POST'];
- login_box('', $user->lang['LOGIN_EXPLAIN_POST']);
+ if ($request->is_ajax())
+ {
+ $json = new phpbb\json_response();
+ $json->send(array(
+ 'title' => $user->lang['INFORMATION'],
+ 'message' => $message,
+ ));
+ }
+
+ login_box('', $message);
}
// Permission to do the action asked?
@@ -326,8 +336,18 @@ if (!$is_authed)
{
trigger_error('USER_CANNOT_' . strtoupper($check_auth));
}
+ $message = $user->lang['LOGIN_EXPLAIN_' . strtoupper($mode)];
- login_box('', $user->lang['LOGIN_EXPLAIN_' . strtoupper($mode)]);
+ if ($request->is_ajax())
+ {
+ $json = new phpbb\json_response();
+ $json->send(array(
+ 'title' => $user->lang['INFORMATION'],
+ 'message' => $message,
+ ));
+ }
+
+ login_box('', $message);
}
// Is the user able to post within this forum?
@@ -484,7 +504,7 @@ $message_parser->get_submitted_attachment_data($post_data['poster_id']);
if ($post_data['post_attachment'] && !$submit && !$refresh && !$preview && $mode == 'edit')
{
// Do not change to SELECT *
- $sql = 'SELECT attach_id, is_orphan, attach_comment, real_filename
+ $sql = 'SELECT attach_id, is_orphan, attach_comment, real_filename, filesize
FROM ' . ATTACHMENTS_TABLE . "
WHERE post_msg_id = $post_id
AND in_message = 0
@@ -1464,6 +1484,7 @@ $template->assign_vars(array(
'L_POST_A' => $page_title,
'L_ICON' => ($mode == 'reply' || $mode == 'quote' || ($mode == 'edit' && $post_id != $post_data['topic_first_post_id'])) ? $user->lang['POST_ICON'] : $user->lang['TOPIC_ICON'],
'L_MESSAGE_BODY_EXPLAIN' => $user->lang('MESSAGE_BODY_EXPLAIN', (int) $config['max_post_chars']),
+ 'L_TOO_MANY_ATTACHMENTS' => $user->lang('TOO_MANY_ATTACHMENTS', (int) $config['max_attachments']),
'FORUM_NAME' => $post_data['forum_name'],
'FORUM_DESC' => ($post_data['forum_desc']) ? generate_text_for_display($post_data['forum_desc'], $post_data['forum_desc_uid'], $post_data['forum_desc_bitfield'], $post_data['forum_desc_options']) : '',
@@ -1487,6 +1508,8 @@ $template->assign_vars(array(
'U_VIEW_TOPIC' => ($mode != 'post') ? append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id") : '',
'U_PROGRESS_BAR' => append_sid("{$phpbb_root_path}posting.$phpEx", "f=$forum_id&amp;mode=popup"),
'UA_PROGRESS_BAR' => addslashes(append_sid("{$phpbb_root_path}posting.$phpEx", "f=$forum_id&amp;mode=popup")),
+ 'ATTACH_ORDER' => ($config['display_order']) ? 'asc' : 'desc',
+ 'MAX_ATTACHMENTS' => ($auth->acl_get('a_') || $auth->acl_get('m_', $forum_id)) ? 0 : (int) $config['max_attachments'],
'S_PRIVMSGS' => false,
'S_CLOSE_PROGRESS_WINDOW' => (isset($_POST['add_file'])) ? true : false,
@@ -1525,8 +1548,9 @@ $template->assign_vars(array(
'S_BBCODE_QUOTE' => $quote_status,
'S_POST_ACTION' => $s_action,
- 'S_HIDDEN_FIELDS' => $s_hidden_fields)
-);
+ 'S_HIDDEN_FIELDS' => $s_hidden_fields,
+ 'S_ATTACH_DATA' => json_encode($message_parser->attachment_data),
+));
/**
* This event allows you to modify template variables for the posting screen
diff --git a/phpBB/styles/prosilver/template/ajax.js b/phpBB/styles/prosilver/template/ajax.js
index 28656d47d3..1d70adc48d 100644
--- a/phpBB/styles/prosilver/template/ajax.js
+++ b/phpBB/styles/prosilver/template/ajax.js
@@ -106,6 +106,47 @@ phpbb.addAjaxCallback('mark_topics_read', function(res, update_topic_links) {
phpbb.closeDarkenWrapper(3000);
});
+// This callback will mark all notifications read
+phpbb.addAjaxCallback('notification.mark_all_read', function(res) {
+ if (typeof res.success !== 'undefined') {
+ phpbb.markNotifications($('#notification_list li.bg2'), 0);
+ phpbb.closeDarkenWrapper(3000);
+ }
+});
+
+// This callback will mark a notification read
+phpbb.addAjaxCallback('notification.mark_read', function(res) {
+ if (typeof res.success !== 'undefined') {
+ var unreadCount = Number($('#notification_list_button strong').html()) - 1;
+ phpbb.markNotifications($(this).parent('li.bg2'), unreadCount);
+ }
+});
+
+/**
+ * Mark notification popup rows as read.
+ *
+ * @param {jQuery} el jQuery object(s) to mark read.
+ * @param {int} unreadCount The new unread notifications count.
+ */
+phpbb.markNotifications = function(el, unreadCount) {
+ // Remove the unread status.
+ el.removeClass('bg2');
+ el.find('a.mark_read').remove();
+
+ // Update the notification link to the real URL.
+ el.each(function() {
+ var link = $(this).find('a');
+ link.attr('href', link.attr('data-real-url'));
+ });
+
+ // Update the unread count.
+ $('#notification_list_button strong').html(unreadCount);
+ // Remove the Mark all read link if there are no unread notifications.
+ if (!unreadCount) {
+ $('#mark_all_notifications').remove();
+ }
+};
+
// This callback finds the post from the delete link, and removes it.
phpbb.addAjaxCallback('post_delete', function() {
var el = $(this),
diff --git a/phpBB/styles/prosilver/template/custom_profile_fields.html b/phpBB/styles/prosilver/template/custom_profile_fields.html
deleted file mode 100644
index 7de97f64cb..0000000000
--- a/phpBB/styles/prosilver/template/custom_profile_fields.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- BEGIN dropdown -->
-<select name="{dropdown.FIELD_IDENT}" id="{dropdown.FIELD_IDENT}">
- <!-- BEGIN options --><option value="{dropdown.options.OPTION_ID}"{dropdown.options.SELECTED}>{dropdown.options.VALUE}</option><!-- END options -->
-</select>
-<!-- END dropdown -->
-
-<!-- BEGIN text -->
-<textarea name="{text.FIELD_IDENT}" id="{text.FIELD_IDENT}" rows="{text.FIELD_ROWS}" cols="{text.FIELD_COLS}" class="inputbox autowidth">{text.FIELD_VALUE}</textarea>
-<!-- END text -->
-
-<!-- BEGIN string -->
-<input type="text" class="inputbox autowidth" name="{string.FIELD_IDENT}" id="{string.FIELD_IDENT}" size="{string.FIELD_LENGTH}" maxlength="{string.FIELD_MAXLEN}" value="{string.FIELD_VALUE}" />
-<!-- END string -->
-
-<!-- BEGIN bool -->
-<!-- IF bool.FIELD_LENGTH eq 1 -->
- <!-- BEGIN options --><label for="{bool.FIELD_IDENT}_{bool.options.OPTION_ID}"><input type="radio" class="radio" name="{bool.FIELD_IDENT}" id="{bool.FIELD_IDENT}_{bool.options.OPTION_ID}" value="{bool.options.OPTION_ID}"{bool.options.CHECKED} /> {bool.options.VALUE}</label> <!-- END options -->
-<!-- ELSE -->
- <input type="checkbox" class="radio" name="{bool.FIELD_IDENT}" id="{bool.FIELD_IDENT}"<!-- IF bool.FIELD_VALUE --> checked="checked"<!-- ENDIF --> />
-<!-- ENDIF -->
-<!-- END bool -->
-
-<!-- BEGIN int -->
-<input type="number" min="{int.FIELD_MINLEN}" max="{int.FIELD_MAXLEN}" class="inputbox autowidth" name="{int.FIELD_IDENT}" id="{int.FIELD_IDENT}" size="{int.FIELD_LENGTH}" value="{int.FIELD_VALUE}" />
-<!-- END int -->
-
-<!-- BEGIN date -->
-<label for="{date.FIELD_IDENT}_day">{L_DAY}{L_COLON} <select name="{date.FIELD_IDENT}_day" id="{date.FIELD_IDENT}_day">{date.S_DAY_OPTIONS}</select></label>
-<label for="{date.FIELD_IDENT}_month">{L_MONTH}{L_COLON} <select name="{date.FIELD_IDENT}_month" id="{date.FIELD_IDENT}_month">{date.S_MONTH_OPTIONS}</select></label>
-<label for="{date.FIELD_IDENT}_year">{L_YEAR}{L_COLON} <select name="{date.FIELD_IDENT}_year" id="{date.FIELD_IDENT}_year">{date.S_YEAR_OPTIONS}</select></label>
-<!-- END date -->
diff --git a/phpBB/styles/prosilver/template/forum_fn.js b/phpBB/styles/prosilver/template/forum_fn.js
index a45f750a63..495df2871d 100644
--- a/phpBB/styles/prosilver/template/forum_fn.js
+++ b/phpBB/styles/prosilver/template/forum_fn.js
@@ -747,7 +747,7 @@ function parse_document(container)
/**
* Hide empty responsive tables
*/
- container.find('table.responsive > tbody').each(function() {
+ container.find('table.responsive > tbody').not('.responsive-skip-empty').each(function() {
var items = $(this).children('tr');
if (items.length == 0)
{
diff --git a/phpBB/styles/prosilver/template/index_body.html b/phpBB/styles/prosilver/template/index_body.html
index 58a5420db3..50c7640e62 100644
--- a/phpBB/styles/prosilver/template/index_body.html
+++ b/phpBB/styles/prosilver/template/index_body.html
@@ -31,6 +31,9 @@
<fieldset class="quick-login">
<label for="username"><span>{L_USERNAME}{L_COLON}</span> <input type="text" name="username" id="username" size="10" class="inputbox" title="{L_USERNAME}" /></label>
<label for="password"><span>{L_PASSWORD}{L_COLON}</span> <input type="password" name="password" id="password" size="10" class="inputbox" title="{L_PASSWORD}" /></label>
+ <!-- IF U_SEND_PASSWORD -->
+ <a href="{U_SEND_PASSWORD}">{L_FORGOT_PASS}</a>
+ <!-- ENDIF -->
<!-- IF S_AUTOLOGIN_ENABLED -->
<span class="responsive-hide">|</span> <label for="autologin">{L_LOG_ME_IN} <input type="checkbox" name="autologin" id="autologin" /></label>
<!-- ENDIF -->
diff --git a/phpBB/styles/prosilver/template/mcp_header.html b/phpBB/styles/prosilver/template/mcp_header.html
index e3efe49943..51f496605e 100644
--- a/phpBB/styles/prosilver/template/mcp_header.html
+++ b/phpBB/styles/prosilver/template/mcp_header.html
@@ -39,7 +39,7 @@
</div>
</div>
- <div id="cp-main" class="mcp-main">
+ <div id="cp-main" class="mcp-main panel-container">
<!-- IF MESSAGE -->
<div class="content">
<h2>{L_MESSAGE}</h2>
diff --git a/phpBB/styles/prosilver/template/overall_footer.html b/phpBB/styles/prosilver/template/overall_footer.html
index f7d0269edb..662008b7fa 100644
--- a/phpBB/styles/prosilver/template/overall_footer.html
+++ b/phpBB/styles/prosilver/template/overall_footer.html
@@ -60,8 +60,8 @@
<!-- EVENT overall_footer_after -->
-{$SCRIPTS}
<!-- IF S_PLUPLOAD --><!-- INCLUDE plupload.html --><!-- ENDIF -->
+{$SCRIPTS}
</body>
</html>
diff --git a/phpBB/styles/prosilver/template/overall_header.html b/phpBB/styles/prosilver/template/overall_header.html
index e4e1b3443b..a89585d899 100644
--- a/phpBB/styles/prosilver/template/overall_header.html
+++ b/phpBB/styles/prosilver/template/overall_header.html
@@ -36,7 +36,6 @@
<!-- ENDIF -->
<!-- IF S_PLUPLOAD -->
- <link href="{T_ASSETS_PATH}/plupload/jquery.plupload.queue/css/jquery.plupload.queue.css?assets_version={T_ASSETS_VERSION}" rel="stylesheet" type="text/css" media="screen, projection" />
<link href="{T_THEME_PATH}/plupload.css?assets_version={T_ASSETS_VERSION}" rel="stylesheet" type="text/css" media="screen, projection" />
<!-- ENDIF -->
@@ -107,7 +106,12 @@
<div class="dropdown-contents">
<div class="header">
{L_NOTIFICATIONS}
- <span class="header_settings"><a href="{U_NOTIFICATION_SETTINGS}">{L_SETTINGS}</a></span>
+ <span class="header_settings">
+ <a href="{U_NOTIFICATION_SETTINGS}">{L_SETTINGS}</a>
+ <!-- IF NOTIFICATIONS_COUNT -->
+ <span id="mark_all_notifications"> &bull; <a href="{U_MARK_ALL_NOTIFICATIONS}" data-ajax="notification.mark_all_read">{L_MARK_ALL_READ}</a></span>
+ <!-- ENDIF -->
+ </span>
</div>
<ul>
@@ -118,17 +122,18 @@
<!-- ENDIF -->
<!-- BEGIN notifications -->
<li class="<!-- IF notifications.UNREAD --> bg2<!-- ENDIF -->">
- <!-- IF notifications.URL --><a href="<!-- IF notifications.UNREAD -->{notifications.U_MARK_READ}<!-- ELSE -->{notifications.URL}<!-- ENDIF -->"><!-- ENDIF -->
+ <!-- IF notifications.URL -->
+ <a href="<!-- IF notifications.UNREAD -->{notifications.U_MARK_READ}" data-real-url="{notifications.URL}<!-- ELSE -->{notifications.URL}<!-- ENDIF -->">
+ <!-- ENDIF -->
<!-- IF notifications.AVATAR -->{notifications.AVATAR}<!-- ELSE --><img src="{T_THEME_PATH}/images/no_avatar.gif" alt="" /><!-- ENDIF -->
<div class="notification_text">
<p>{notifications.FORMATTED_TITLE}</p>
<p>&raquo; {notifications.TIME}</p>
-
- <!-- IF not notifications.URL and notifications.U_MARK_READ -->
- <p><a href="{notifications.U_MARK_READ}">{L_MARK_READ}</a></p>
- <!-- ENDIF -->
</div>
<!-- IF notifications.URL --></a><!-- ENDIF -->
+ <!-- IF notifications.UNREAD -->
+ <a href="{notifications.U_MARK_READ}" class="mark_read icon-mark" data-ajax="notification.mark_read" title="{L_MARK_READ}"></a>
+ <!-- ENDIF -->
</li>
<!-- END notifications -->
</ul>
diff --git a/phpBB/styles/prosilver/template/plupload.html b/phpBB/styles/prosilver/template/plupload.html
index 290992ddd6..21acae1c44 100644
--- a/phpBB/styles/prosilver/template/plupload.html
+++ b/phpBB/styles/prosilver/template/plupload.html
@@ -1,5 +1,3 @@
-<script type="text/javascript" src="{T_ASSETS_PATH}/plupload/plupload.full.min.js?assets_version={T_ASSETS_VERSION}"></script>
-<script type="text/javascript" src="{T_ASSETS_PATH}/plupload/jquery.plupload.queue/jquery.plupload.queue.min.js?assets_version={T_ASSETS_VERSION}"></script>
<script type="text/javascript">
//<![CDATA[
phpbb.plupload = {
@@ -37,7 +35,6 @@ phpbb.plupload = {
'Status': '{LA_PLUPLOAD_STATUS}',
'Stop Upload': '{LA_PLUPLOAD_STOP_UPLOAD}',
'Stop current upload': '{LA_PLUPLOAD_STOP_CURRENT_UPLOAD}',
- 'Upload element accepts only %d file(s) at a time. Extra files were stripped.': '{LA_PLUPLOAD_ERR_UPLOAD_LIMIT}',
"Upload URL might be wrong or doesn't exist.": '{LA_PLUPLOAD_ERR_UPLOAD_URL}',
'Uploaded %d/%d files': '{LA_PLUPLOAD_UPLOADED}',
'%d files queued': '{LA_PLUPLOAD_FILES_QUEUED}',
@@ -51,14 +48,22 @@ phpbb.plupload = {
unique_names: true,
filters: [{FILTERS}],
{S_RESIZE}
- headers: {'X-PHPBB-USING-PLUPLOAD': '1'},
+ headers: {'X-PHPBB-USING-PLUPLOAD': '1', 'X-Requested-With': 'XMLHttpRequest'},
file_data_name: 'fileupload',
multipart_params: {'add_file': '{LA_ADD_FILE}'},
- img_path: '{T_ASSETS_PATH}/plupload/jquery.plupload.queue/img',
- element_hook: '#attach-panel .inner',
- form_hook: '#postform'
- }
-};
+ form_hook: '#postform',
+ browse_button: 'add_files',
+ drop_element : 'message',
+ },
+ lang: {
+ ERROR: '{LA_ERROR}',
+ TOO_MANY_ATTACHMENTS: '{LA_TOO_MANY_ATTACHMENTS}',
+ },
+ order: '{ATTACH_ORDER}',
+ maxFiles: {MAX_ATTACHMENTS},
+ data: {S_ATTACH_DATA},
+}
//]]>
</script>
-<script type="text/javascript" src="{T_ASSETS_PATH}/javascript/plupload.js"></script>
+<!-- INCLUDEJS {T_ASSETS_PATH}/plupload/plupload.full.min.js -->
+<!-- INCLUDEJS {T_ASSETS_PATH}/javascript/plupload.js -->
diff --git a/phpBB/styles/prosilver/template/posting_attach_body.html b/phpBB/styles/prosilver/template/posting_attach_body.html
index fa9dd19c09..8a2004c29b 100644
--- a/phpBB/styles/prosilver/template/posting_attach_body.html
+++ b/phpBB/styles/prosilver/template/posting_attach_body.html
@@ -1,9 +1,9 @@
-<div class="panel bg3" id="attach-panel">
+<div class="panel bg3 panel-container" id="attach-panel">
<div class="inner">
- <p>{L_ADD_ATTACHMENT_EXPLAIN}</p>
+ <p>{L_ADD_ATTACHMENT_EXPLAIN} <span class="hidden" id="drag-n-drop-message">{L_PLUPLOAD_DRAG_TEXTAREA}</span></p>
- <fieldset class="fields2">
+ <fieldset class="fields2" id="attach-panel-basic">
<dl>
<dt><label for="fileupload">{L_FILENAME}{L_COLON}</label></dt>
<dd>
@@ -17,5 +17,73 @@
</dl>
</fieldset>
+ <div id="attach-panel-multi">
+ <input type="button" class="button2" value="{L_PLUPLOAD_ADD_FILES}" id="add_files" />
+ </div>
+
+ <div class="panel<!-- IF not .attach_row --> hidden<!-- ENDIF -->" id="file-list-container">
+ <div class="inner">
+ <table class="table1 zebra-list">
+ <thead>
+ <tr>
+ <th class="attach-name">{L_PLUPLOAD_FILENAME}</th>
+ <th class="attach-comment">{L_FILE_COMMENT}</th>
+ <th class="attach-filesize">{L_PLUPLOAD_SIZE}</th>
+ <th class="attach-status">{L_PLUPLOAD_STATUS}</th>
+ </tr>
+ </thead>
+ <tbody class="responsive-skip-empty" id="file-list">
+ <tr class="attach-row" id="attach-row-tpl">
+ <td class="attach-name">
+ <dfn style="display: none;">{L_PLUPLOAD_FILENAME}</dfn>
+ <span class="file-name"></span>
+ <span class="attach-controls">
+ <input type="button" value="{L_PLACE_INLINE}" class="button2 hidden file-inline-bbcode" />&nbsp;
+ <input type="button" value="{L_DELETE_FILE}" class="button2 file-delete" />
+ </span>
+ <span class="clear"></span>
+ </td>
+ <td class="attach-comment">
+ <dfn style="display: none;">{L_FILE_COMMENT}</dfn>
+ <textarea rows="1" cols="30" class="inputbox"></textarea>
+ </td>
+ <td class="attach-filesize">
+ <dfn style="display: none;">{L_PLUPLOAD_SIZE}</dfn>
+ <span class="file-size"></span>
+ </td>
+ <td class="attach-status">
+ <dfn style="display: none;">{L_PLUPLOAD_STATUS}</dfn>
+ <span class="file-progress">
+ <span class="file-progress-bar"></span>
+ </span>
+ <span class="file-status"></span>
+ </td>
+ </tr>
+ <!-- BEGIN attach_row -->
+ <tr class="attach-row" data-attach-id="{attach_row.ATTACH_ID}">
+ <td class="attach-name">
+ <span class="file-name"><a href="{attach_row.U_VIEW_ATTACHMENT}">{attach_row.FILENAME}</a></span>
+ <span class="attach-controls">
+ <!-- IF S_INLINE_ATTACHMENT_OPTIONS --><input type="button" value="{L_PLACE_INLINE}" class="button2 file-inline-bbcode" />&nbsp; <!-- ENDIF -->
+ <input type="submit" name="delete_file[{attach_row.ASSOC_INDEX}]" value="{L_DELETE_FILE}" class="button2 file-delete" />
+ </span>
+ <span class="clear"></span>
+ </td>
+ <td class="attach-comment">
+ <textarea name="comment_list[{attach_row.ASSOC_INDEX}]" rows="1" cols="30" class="inputbox">{attach_row.FILE_COMMENT}</textarea>
+ {attach_row.S_HIDDEN}
+ </td>
+ <td class="attach-filesize">
+ <span class="file-size">{attach_row.FILESIZE}</span>
+ </td>
+ <td class="attach-status">
+ <span class="file-status file-uploaded"></span>
+ </td>
+ </tr>
+ <!-- END attach_row -->
+ </tbody>
+ </table>
+ </div>
+ </div>
</div>
</div>
diff --git a/phpBB/styles/prosilver/template/posting_editor.html b/phpBB/styles/prosilver/template/posting_editor.html
index 5677b92fc3..2ae224bc3a 100644
--- a/phpBB/styles/prosilver/template/posting_editor.html
+++ b/phpBB/styles/prosilver/template/posting_editor.html
@@ -149,34 +149,6 @@
</div>
<!-- ENDIF -->
- <!-- IF S_HAS_ATTACHMENTS -->
- <div class="panel bg2">
- <div class="inner">
- <h3>{L_POSTED_ATTACHMENTS}</h3>
-
- <fieldset class="fields2">
-
- <!-- BEGIN attach_row -->
- <dl>
-
- <dt><label for="comment_list_{attach_row.ASSOC_INDEX}">{L_FILE_COMMENT}{L_COLON}</label></dt>
- <dd><textarea name="comment_list[{attach_row.ASSOC_INDEX}]" id="comment_list_{attach_row.ASSOC_INDEX}" rows="1" cols="35" class="inputbox">{attach_row.FILE_COMMENT}</textarea></dd>
- <dd><a href="{attach_row.U_VIEW_ATTACHMENT}">{attach_row.FILENAME}</a></dd>
- <dd style="margin-top: 5px;">
- <!-- IF S_INLINE_ATTACHMENT_OPTIONS --><input type="button" value="{L_PLACE_INLINE}" onclick="attach_inline({attach_row.ASSOC_INDEX}, '{attach_row.A_FILENAME}');" class="button2" />&nbsp; <!-- ENDIF -->
- <input type="submit" name="delete_file[{attach_row.ASSOC_INDEX}]" value="{L_DELETE_FILE}" class="button2" />
- </dd>
- </dl>
- {attach_row.S_HIDDEN}
- <!-- IF not attach_row.S_LAST_ROW --><hr class="dashed" /><!-- ENDIF -->
- <!-- END attach_row -->
-
- </fieldset>
-
- </div>
- </div>
- <!-- ENDIF -->
-
<!-- IF not S_SHOW_DRAFTS and not $SIG_EDIT eq 1 -->
<div class="panel bg2">
<div class="inner">
@@ -198,7 +170,15 @@
<div id="tabs" class="sub-panels" data-show-panel="options-panel">
<ul>
<li id="options-panel-tab" class="activetab"><a href="#tabs" data-subpanel="options-panel"><span>{L_OPTIONS}</span></a></li>
- <!-- IF S_SHOW_ATTACH_BOX --><li id="attach-panel-tab"><a href="#tabs" data-subpanel="attach-panel"><span>{L_ADD_ATTACHMENT}</span></a></li><!-- ENDIF -->
+ <!-- IF S_SHOW_ATTACH_BOX -->
+ <li id="attach-panel-tab">
+ <a href="#tabs" data-subpanel="attach-panel">
+ <span>
+ {L_ATTACHMENTS} <strong id="file-total-progress"><strong id="file-total-progress-bar"></strong></strong>
+ </span>
+ </a>
+ </li>
+ <!-- ENDIF -->
<!-- IF S_SHOW_POLL_BOX || S_POLL_DELETE --><li id="poll-panel-tab"><a href="#tabs" data-subpanel="poll-panel"><span>{L_ADD_POLL}</span></a></li><!-- ENDIF -->
</ul>
</div>
diff --git a/phpBB/styles/prosilver/template/posting_smilies.html b/phpBB/styles/prosilver/template/posting_smilies.html
index cb542c1a8c..c5371b9b6a 100644
--- a/phpBB/styles/prosilver/template/posting_smilies.html
+++ b/phpBB/styles/prosilver/template/posting_smilies.html
@@ -13,7 +13,7 @@
<div class="inner">
<!-- BEGIN smiley -->
<a href="#" onclick="initInsertions(); insert_text('{smiley.A_SMILEY_CODE}', true, true); return false;"><img src="{smiley.SMILEY_IMG}" width="{smiley.SMILEY_WIDTH}" height="{smiley.SMILEY_HEIGHT}" alt="{smiley.SMILEY_CODE}" title="{smiley.SMILEY_DESC}" /></a>
- <!-- END smiley -->
+ <!-- END smiley -->
</div>
</div>
diff --git a/phpBB/styles/prosilver/template/profilefields/bool.html b/phpBB/styles/prosilver/template/profilefields/bool.html
new file mode 100644
index 0000000000..f1d7ba75f4
--- /dev/null
+++ b/phpBB/styles/prosilver/template/profilefields/bool.html
@@ -0,0 +1,7 @@
+<!-- BEGIN bool -->
+<!-- IF bool.FIELD_LENGTH eq 1 -->
+ <!-- BEGIN options --><label for="{bool.FIELD_IDENT}_{bool.options.OPTION_ID}"><input type="radio" class="radio" name="{bool.FIELD_IDENT}" id="{bool.FIELD_IDENT}_{bool.options.OPTION_ID}" value="{bool.options.OPTION_ID}"{bool.options.CHECKED} /> {bool.options.VALUE}</label> <!-- END options -->
+<!-- ELSE -->
+ <input type="checkbox" class="radio" name="{bool.FIELD_IDENT}" id="{bool.FIELD_IDENT}"<!-- IF bool.FIELD_VALUE --> checked="checked"<!-- ENDIF --> />
+<!-- ENDIF -->
+<!-- END bool -->
diff --git a/phpBB/styles/prosilver/template/profilefields/date.html b/phpBB/styles/prosilver/template/profilefields/date.html
new file mode 100644
index 0000000000..5d5bc04ed6
--- /dev/null
+++ b/phpBB/styles/prosilver/template/profilefields/date.html
@@ -0,0 +1,5 @@
+<!-- BEGIN date -->
+<label for="{date.FIELD_IDENT}_day">{L_DAY}{L_COLON} <select name="{date.FIELD_IDENT}_day" id="{date.FIELD_IDENT}_day">{date.S_DAY_OPTIONS}</select></label>
+<label for="{date.FIELD_IDENT}_month">{L_MONTH}{L_COLON} <select name="{date.FIELD_IDENT}_month" id="{date.FIELD_IDENT}_month">{date.S_MONTH_OPTIONS}</select></label>
+<label for="{date.FIELD_IDENT}_year">{L_YEAR}{L_COLON} <select name="{date.FIELD_IDENT}_year" id="{date.FIELD_IDENT}_year">{date.S_YEAR_OPTIONS}</select></label>
+<!-- END date -->
diff --git a/phpBB/styles/prosilver/template/profilefields/dropdown.html b/phpBB/styles/prosilver/template/profilefields/dropdown.html
new file mode 100644
index 0000000000..243b7039da
--- /dev/null
+++ b/phpBB/styles/prosilver/template/profilefields/dropdown.html
@@ -0,0 +1,5 @@
+<!-- BEGIN dropdown -->
+<select name="{dropdown.FIELD_IDENT}" id="{dropdown.FIELD_IDENT}">
+ <!-- BEGIN options --><option value="{dropdown.options.OPTION_ID}"{dropdown.options.SELECTED}>{dropdown.options.VALUE}</option><!-- END options -->
+</select>
+<!-- END dropdown -->
diff --git a/phpBB/styles/prosilver/template/profilefields/int.html b/phpBB/styles/prosilver/template/profilefields/int.html
new file mode 100644
index 0000000000..a6f9a0a49e
--- /dev/null
+++ b/phpBB/styles/prosilver/template/profilefields/int.html
@@ -0,0 +1,3 @@
+<!-- BEGIN int -->
+<input type="number" min="{int.FIELD_MINLEN}" max="{int.FIELD_MAXLEN}" class="inputbox autowidth" name="{int.FIELD_IDENT}" id="{int.FIELD_IDENT}" size="{int.FIELD_LENGTH}" value="{int.FIELD_VALUE}" />
+<!-- END int -->
diff --git a/phpBB/styles/prosilver/template/profilefields/string.html b/phpBB/styles/prosilver/template/profilefields/string.html
new file mode 100644
index 0000000000..cf457d39ad
--- /dev/null
+++ b/phpBB/styles/prosilver/template/profilefields/string.html
@@ -0,0 +1,3 @@
+<!-- BEGIN string -->
+<input type="text" class="inputbox autowidth" name="{string.FIELD_IDENT}" id="{string.FIELD_IDENT}" size="{string.FIELD_LENGTH}" maxlength="{string.FIELD_MAXLEN}" value="{string.FIELD_VALUE}" />
+<!-- END string -->
diff --git a/phpBB/styles/prosilver/template/profilefields/text.html b/phpBB/styles/prosilver/template/profilefields/text.html
new file mode 100644
index 0000000000..f54c63925c
--- /dev/null
+++ b/phpBB/styles/prosilver/template/profilefields/text.html
@@ -0,0 +1,3 @@
+<!-- BEGIN text -->
+<textarea name="{text.FIELD_IDENT}" id="{text.FIELD_IDENT}" rows="{text.FIELD_ROWS}" cols="{text.FIELD_COLS}" class="inputbox">{text.FIELD_VALUE}</textarea>
+<!-- END text -->
diff --git a/phpBB/styles/prosilver/template/ucp_groups_manage.html b/phpBB/styles/prosilver/template/ucp_groups_manage.html
index eb431e9f3f..5772a735d3 100644
--- a/phpBB/styles/prosilver/template/ucp_groups_manage.html
+++ b/phpBB/styles/prosilver/template/ucp_groups_manage.html
@@ -56,7 +56,7 @@
<dt><label for="group_colour">{L_GROUP_COLOR}{L_COLON}</label><br /><span>{L_GROUP_COLOR_EXPLAIN}</span></dt>
<dd>
<input name="group_colour" type="text" id="group_colour" value="{GROUP_COLOUR}" size="6" maxlength="6" class="inputbox narrow" />
- <span style="background-color: {GROUP_COLOUR};">&nbsp;&nbsp;&nbsp;</span>
+ <span style="background-color: #{GROUP_COLOUR};">&nbsp;&nbsp;&nbsp;</span>
[ <a href="#" id="color_palette_toggle">{L_COLOUR_SWATCH}</a> ]
<div id="color_palette_placeholder" class="hidden" data-orientation="h" data-height="12" data-width="15" data-target="#group_colour"></div>
</dd>
diff --git a/phpBB/styles/prosilver/template/ucp_header.html b/phpBB/styles/prosilver/template/ucp_header.html
index c5d58b8eaa..8ce2c54294 100644
--- a/phpBB/styles/prosilver/template/ucp_header.html
+++ b/phpBB/styles/prosilver/template/ucp_header.html
@@ -98,4 +98,4 @@
</div>
- <div id="cp-main" class="ucp-main">
+ <div id="cp-main" class="ucp-main panel-container">
diff --git a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html
index 4a8d3da08a..4d7346fda8 100644
--- a/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html
+++ b/phpBB/styles/prosilver/template/ucp_pm_viewmessage_print.html
@@ -2,8 +2,6 @@
<html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}">
<head>
<meta charset="utf-8">
-<meta name="keywords" content="" />
-<meta name="description" content="" />
<meta name="robots" content="noindex" />
{META}
<title>{SITENAME} &bull; {PAGE_TITLE}</title>
diff --git a/phpBB/styles/prosilver/template/viewtopic_print.html b/phpBB/styles/prosilver/template/viewtopic_print.html
index fd0b2dc3e6..6c4dcfadf1 100644
--- a/phpBB/styles/prosilver/template/viewtopic_print.html
+++ b/phpBB/styles/prosilver/template/viewtopic_print.html
@@ -2,8 +2,6 @@
<html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}">
<head>
<meta charset="utf-8">
-<meta name="keywords" content="" />
-<meta name="description" content="" />
<meta name="robots" content="noindex" />
{META}
<title>{SITENAME} &bull; {PAGE_TITLE}</title>
@@ -28,7 +26,7 @@
<!-- BEGIN postrow -->
<div class="post">
<h3>{postrow.POST_SUBJECT}</h3>
- <div class="date">{postrow.MINI_POST_IMG}{L_POSTED}{L_COLON} <strong>{postrow.POST_DATE}</strong></div>
+ <div class="date">{L_POSTED}{L_COLON} <strong>{postrow.POST_DATE}</strong></div>
<div class="author">{L_POST_BY_AUTHOR} <strong>{postrow.POST_AUTHOR}</strong></div>
<div class="content">{postrow.MESSAGE}</div>
</div>
diff --git a/phpBB/styles/prosilver/theme/bidi.css b/phpBB/styles/prosilver/theme/bidi.css
index d5e98d6197..57d9a33c12 100644
--- a/phpBB/styles/prosilver/theme/bidi.css
+++ b/phpBB/styles/prosilver/theme/bidi.css
@@ -90,6 +90,13 @@
text-align: left;
}
+/* Bulletin icons for list items
+----------------------------------------*/
+.rtl ul.linklist.bulletin li:before {
+ padding-left: 4px;
+ padding-right: 0;
+}
+
/* Dropdown menu
---------------------------------------- */
.rtl .dropdown-container.topic-tools {
@@ -198,8 +205,9 @@
/* Pagination in viewforum for multipage topics */
.rtl .row .pagination {
+ background-position: 100% 50%;
float: left;
- padding: 1px 0 1px 15px;
+ padding: 1px 15px 1px 0;
}
.rtl .pagination span {
@@ -278,16 +286,6 @@ ul.linklist li.small-icon > a, ul.linklist li.breadcrumbs span:first-child > a {
/**
* content.css
*/
-.rtl ul.topiclist dfn {
- /* Labels for post/view counts */
- position: relative;
- width: 1px;
- height: 1px;
- overflow: hidden;
- display: block;
- left: 0;
-}
-
.rtl ul.topiclist dt, .rtl li.header dt {
float: right;
margin-right: 0;
@@ -357,8 +355,8 @@ ul.linklist li.small-icon > a, ul.linklist li.breadcrumbs span:first-child > a {
}
.rtl li.header dd {
- margin-left: 0;
- margin-right: 1px;
+ padding-left: 0;
+ padding-right: 1px;
}
.rtl dl.icon {
@@ -387,15 +385,18 @@ ul.linklist li.small-icon > a, ul.linklist li.breadcrumbs span:first-child > a {
/* Post body styles
----------------------------------------*/
-.rtl .postbody {
+.rtl .postbody, .rtl .postbody h3 {
float: right;
}
+.rtl p.post-notice {
+ padding-left: 5px;
+ padding-right: 26px;
+}
+
.rtl p.post-notice:before {
left: auto;
right: 0;
- padding-left: 5px;
- padding-right: 26px;
}
/* Topic review panel
@@ -600,6 +601,10 @@ ul.linklist li.small-icon > a, ul.linklist li.breadcrumbs span:first-child > a {
margin-left: 0;
}
+.tabs-container h2 {
+ float: right;
+}
+
/* CP tabbed menu
----------------------------------------*/
.rtl #tabs {
@@ -617,7 +622,12 @@ ul.linklist li.small-icon > a, ul.linklist li.breadcrumbs span:first-child > a {
/* Mini tabbed menu used in MCP
----------------------------------------*/
.rtl #minitabs {
- margin: -20px 0 0 7px;
+ margin-right: 0;
+ margin-left: 7px;
+}
+
+.rtl .tabs-container #minitabs {
+ float: left;
}
.rtl #minitabs li {
@@ -712,7 +722,7 @@ ul.linklist li.small-icon > a, ul.linklist li.breadcrumbs span:first-child > a {
}
.rtl fieldset.fields1 dd {
- margin-right: 10em;
+ margin-right: 15em;
margin-left: 0;
border-right-width: 0;
border-left-width: 1px;
diff --git a/phpBB/styles/prosilver/theme/colours.css b/phpBB/styles/prosilver/theme/colours.css
index 68fbcde4f9..1d4c657a14 100644
--- a/phpBB/styles/prosilver/theme/colours.css
+++ b/phpBB/styles/prosilver/theme/colours.css
@@ -76,8 +76,22 @@ hr {
color: #000000;
}
-.bg1 { background-color: #ECF3F7; }
-.bg2 { background-color: #e1ebf2; }
+.bg1 {
+ background-color: #ECF3F7;
+}
+
+table.zebra-list tr:nth-child(odd) td, ul.zebra-list li:nth-child(odd) {
+ background-color: #ECF3F7;
+}
+
+.bg2 {
+ background-color: #e1ebf2;
+}
+
+table.zebra-list tr:nth-child(even) td, ul.zebra-list li:nth-child(even) {
+ background-color: #e1ebf2;
+}
+
.bg3 { background-color: #cadceb; }
.ucprowbg {
@@ -258,6 +272,11 @@ a:active { color: #368AD2; }
color: #C8E6FF;
}
+/* Notification mark read link */
+#notification_list a.mark_read {
+ background-color: #FFFFFF;
+}
+
/* Links for forum/topic lists */
a.forumtitle {
color: #105289;
@@ -716,6 +735,7 @@ a.sendemail {
.icon-notification { background-image: url("./images/icon_notification.gif"); }
.icon-pm { background-image: url("./images/icon_pm.gif"); }
.icon-download { background-image: url("./images/icon_download.gif"); }
+.icon-mark { background-image: url("./images/icon_mark.gif"); }
/* Profile & navigation icons */
.email-icon, .email-icon a { background-image: url("./images/icon_contact_email.gif"); }
@@ -797,11 +817,11 @@ Colours and backgrounds for cp.css
/* Main CP box
----------------------------------------*/
-#cp-main h3, #cp-main hr, #cp-menu hr {
+.panel-container h3, .panel-container hr, #cp-menu hr {
border-color: #A4B3BF;
}
-#cp-main .panel li.row {
+.panel-container .panel li.row {
border-bottom-color: #B5C1CB;
border-top-color: #F9F9F9;
}
@@ -810,11 +830,11 @@ ul.cplist {
border-top-color: #B5C1CB;
}
-#cp-main .panel li.header dd, #cp-main .panel li.header dt {
+.panel-container .panel li.header dd, .panel-container .panel li.header dt {
color: #000000;
}
-#cp-main table.table1 thead th {
+.panel-container table.table1 thead th {
color: #333333;
border-bottom-color: #333333;
}
@@ -915,11 +935,11 @@ ul.cplist {
/* Preferences pane layout
----------------------------------------*/
-#cp-main h2 {
+.panel-container h2 {
color: #333333;
}
-#cp-main .panel {
+.panel-container .panel {
background-color: #F9F9F9;
}
diff --git a/phpBB/styles/prosilver/theme/common.css b/phpBB/styles/prosilver/theme/common.css
index 66ee77f60b..288477f52a 100644
--- a/phpBB/styles/prosilver/theme/common.css
+++ b/phpBB/styles/prosilver/theme/common.css
@@ -1015,6 +1015,7 @@ form > p.post-notice strong {
list-style-type: none;
font-size: 0.95em;
clear: both;
+ position: relative;
}
#notification_list ul li:before, #notification_list ul li:after {
diff --git a/phpBB/styles/prosilver/theme/cp.css b/phpBB/styles/prosilver/theme/cp.css
index 2003fef954..bf235b3fb5 100644
--- a/phpBB/styles/prosilver/theme/cp.css
+++ b/phpBB/styles/prosilver/theme/cp.css
@@ -20,16 +20,16 @@
padding: 0;
}
-#cp-main .panel p {
+.panel-container .panel p {
font-size: 1.1em;
}
-#cp-main .panel ol {
+.panel-container .panel ol {
margin-left: 2em;
font-size: 1.1em;
}
-#cp-main .panel li.row {
+.panel-container .panel li.row {
border-bottom: 1px solid transparent;
border-top: 1px solid transparent;
}
@@ -39,21 +39,21 @@ ul.cplist {
border-top: 1px solid transparent;
}
-#cp-main .panel li.header dd, #cp-main .panel li.header dt {
+.panel-container .panel li.header dd, .panel-container .panel li.header dt {
margin-bottom: 2px;
}
-#cp-main table.table1 {
+.panel-container table.table1 {
margin-bottom: 1em;
}
-#cp-main table.table1 thead th {
+.panel-container table.table1 thead th {
font-weight: bold;
border-bottom: 1px solid transparent;
padding: 5px;
}
-#cp-main table.table1 tbody th {
+.panel-container table.table1 tbody th {
font-style: italic;
background-color: transparent !important;
border-bottom: none;
@@ -102,12 +102,6 @@ ul.cplist {
margin-bottom: 0px;
}
-.tabs-container #minitabs {
- float: right;
- margin-top: 19px;
- max-width: 50%;
-}
-
.tabs-container:after {
display: block;
clear: both;
@@ -193,6 +187,12 @@ ul.cplist {
margin: -20px 7px 0 0;
}
+.tabs-container #minitabs {
+ float: right;
+ margin-top: 19px;
+ max-width: 50%;
+}
+
#minitabs ul {
margin:0;
padding: 0;
diff --git a/phpBB/styles/prosilver/theme/images/icon_mark.gif b/phpBB/styles/prosilver/theme/images/icon_mark.gif
new file mode 100644
index 0000000000..1a33fc3264
--- /dev/null
+++ b/phpBB/styles/prosilver/theme/images/icon_mark.gif
Binary files differ
diff --git a/phpBB/styles/prosilver/theme/links.css b/phpBB/styles/prosilver/theme/links.css
index 7e3dc094f0..d43886256d 100644
--- a/phpBB/styles/prosilver/theme/links.css
+++ b/phpBB/styles/prosilver/theme/links.css
@@ -49,6 +49,29 @@ ul.linklist li.small-icon > a, ul.linklist li.breadcrumbs span:first-child > a {
padding-left: 17px;
}
+/* Notification mark read link */
+#notification_list a.mark_read {
+ background-position: center center;
+ background-repeat: no-repeat;
+ border-radius: 3px 0 0 3px;
+ display: none;
+ margin-top: -20px;
+ position: absolute;
+ z-index: 2;
+ right: 0;
+ top: 50%;
+ width: 30px;
+ height: 40px;
+}
+
+#notification_list li:hover a.mark_read {
+ display: block;
+}
+
+#notification_list a.mark_read:hover {
+ width: 40px;
+}
+
/* Links for forum/topic lists */
a.forumtitle {
font-family: "Trebuchet MS", Helvetica, Arial, Sans-serif;
diff --git a/phpBB/styles/prosilver/theme/plupload.css b/phpBB/styles/prosilver/theme/plupload.css
index 16c26822b5..8569eca662 100644
--- a/phpBB/styles/prosilver/theme/plupload.css
+++ b/phpBB/styles/prosilver/theme/plupload.css
@@ -1,11 +1,76 @@
-.plupload_filelist li.can_delete:hover {
- cursor: pointer;
+#attach-panel-multi {
+ display: none;
+ margin-bottom: 1em;
}
-.plupload_filelist li.can_delete:hover a {
- background: url('../../../assets/plupload/jquery.plupload.queue/img/delete.gif');
+#file-list td {
+ vertical-align: middle;
}
-.plupload_filelist li a.working {
- background: url('../../../assets/plupload/jquery.plupload.queue/img/throbber.gif');
+.attach-name {
+ width: 50%;
+}
+
+.attach-comment {
+ width: 30%;
+}
+
+.attach-filesize {
+ width: 15%;
+}
+
+.attach-status {
+ width: 5%;
+}
+
+.attach-filesize, .attach-status {
+ text-align: center;
+}
+
+.attach-controls {
+ display: inline-block;
+ float: right;
+}
+
+#attach-row-tpl, .nojs .file-inline-bbcode {
+ display: none;
+}
+
+#file-total-progress {
+ height: 2px;
+ display: block;
+ position: relative;
+ margin: 4px -10px -6px -10px;
+}
+
+.file-progress {
+ background-color: #CCCCCC;
+ display:inline-block;
+ height: 8px;
+ width: 50px;
+}
+
+.file-progress-bar, #file-total-progress-bar {
+ background-color: green;
+ display: block;
+ height: 100%;
+ width: 0;
+}
+
+.file-status.file-working {
+ background: url('../../../assets/plupload/img/throbber.gif');
+}
+
+.file-status.file-uploaded {
+ background: url('../../../assets/plupload/img/done.gif');
+}
+
+.file-status.file-error {
+ background: url('../../../assets/plupload/img/error.gif');
+}
+
+.file-status {
+ display: inline-block;
+ height: 16px;
+ width: 16px;
}
diff --git a/phpBB/styles/prosilver/theme/responsive.css b/phpBB/styles/prosilver/theme/responsive.css
index 397ff12942..bfa2448296 100644
--- a/phpBB/styles/prosilver/theme/responsive.css
+++ b/phpBB/styles/prosilver/theme/responsive.css
@@ -499,6 +499,10 @@ fieldset.display-actions {
margin: 0 25px;
}
+.attach-comment dfn {
+ width: 100%;
+}
+
@media only screen and (max-width: 500px), only screen and (max-device-width: 500px)
{
p.responsive-center {
@@ -526,4 +530,9 @@ fieldset.display-actions {
display: block;
margin-bottom: 5px;
}
+
+ .attach-controls {
+ margin-top: 5px;
+ width: 100%;
+ }
}
diff --git a/phpBB/styles/subsilver2/template/index_body.html b/phpBB/styles/subsilver2/template/index_body.html
index 763028966f..55a62fcf18 100644
--- a/phpBB/styles/subsilver2/template/index_body.html
+++ b/phpBB/styles/subsilver2/template/index_body.html
@@ -83,7 +83,7 @@
<td class="cat"><h4><a href="{U_LOGIN_LOGOUT}">{L_LOGIN_LOGOUT}</a></h4></td>
</tr>
<tr>
- <td class="row1" align="center"><span class="genmed">{L_USERNAME}{L_COLON}</span> <input class="post" type="text" name="username" size="10" />&nbsp; <span class="genmed">{L_PASSWORD}{L_COLON}</span> <input class="post" type="password" name="password" size="10" />&nbsp; <!-- IF S_AUTOLOGIN_ENABLED --> <span class="gensmall">{L_LOG_ME_IN}</span> <input type="checkbox" class="radio" name="autologin" /><!-- ENDIF -->&nbsp; <input type="submit" class="btnmain" name="login" value="{L_LOGIN}" /></td>
+ <td class="row1" align="center"><span class="genmed">{L_USERNAME}{L_COLON}</span> <input class="post" type="text" name="username" size="10" />&nbsp; <span class="genmed">{L_PASSWORD}{L_COLON}</span> <input class="post" type="password" name="password" size="10" />&nbsp; <!-- IF U_SEND_PASSWORD --><a href="{U_SEND_PASSWORD}">{L_FORGOT_PASS}</a>&nbsp; <!-- ENDIF --> <!-- IF S_AUTOLOGIN_ENABLED --> <span class="gensmall">{L_LOG_ME_IN}</span> <input type="checkbox" class="radio" name="autologin" /><!-- ENDIF -->&nbsp; <input type="submit" class="btnmain" name="login" value="{L_LOGIN}" /></td>
</tr>
</table>
{S_LOGIN_REDIRECT}
diff --git a/phpBB/styles/subsilver2/template/overall_header.html b/phpBB/styles/subsilver2/template/overall_header.html
index 16e0f6abaf..9ca989ea8b 100644
--- a/phpBB/styles/subsilver2/template/overall_header.html
+++ b/phpBB/styles/subsilver2/template/overall_header.html
@@ -198,12 +198,14 @@ function marklist(id, name, state)
<!-- IF S_BOARD_DISABLED and S_USER_LOGGED_IN --> &nbsp;<span class="error">{L_BOARD_DISABLED}</span><!-- ENDIF -->
</td>
<td class="genmed" align="{S_CONTENT_FLOW_END}">
+ <!-- EVENT overall_header_navigation_prepend -->
<a href="{U_FAQ}"><img src="{T_THEME_PATH}/images/icon_mini_faq.gif" width="12" height="13" alt="*" /> {L_FAQ}</a>
<!-- IF S_DISPLAY_SEARCH -->&nbsp; &nbsp;<a href="{U_SEARCH}"><img src="{T_THEME_PATH}/images/icon_mini_search.gif" width="12" height="13" alt="*" /> {L_SEARCH}</a><!-- ENDIF -->
<!-- IF not S_IS_BOT -->
<!-- IF S_DISPLAY_MEMBERLIST -->&nbsp; &nbsp;<a href="{U_MEMBERLIST}"><img src="{T_THEME_PATH}/images/icon_mini_members.gif" width="12" height="13" alt="*" /> {L_MEMBERLIST}</a><!-- ENDIF -->
<!-- IF S_USER_LOGGED_IN -->&nbsp; &nbsp;<a href="{U_PROFILE}"><img src="{T_THEME_PATH}/images/icon_mini_profile.gif" width="12" height="13" alt="*" /> {L_PROFILE}</a><!-- ENDIF -->
<!-- ENDIF -->
+ <!-- EVENT overall_header_navigation_append -->
</td>
</tr>
</table>
diff --git a/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html b/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html
index fd0244a79d..9b1eb3b230 100644
--- a/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html
+++ b/phpBB/styles/subsilver2/template/ucp_pm_viewmessage_print.html
@@ -2,6 +2,7 @@
<html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}">
<head>
<meta charset="utf-8">
+<meta name="robots" content="noindex" />
<title>{SITENAME} :: {PAGE_TITLE}</title>
<style type="text/css">
diff --git a/phpBB/styles/subsilver2/template/viewtopic_print.html b/phpBB/styles/subsilver2/template/viewtopic_print.html
index 78fab17d02..b788d699cb 100644
--- a/phpBB/styles/subsilver2/template/viewtopic_print.html
+++ b/phpBB/styles/subsilver2/template/viewtopic_print.html
@@ -2,6 +2,7 @@
<html dir="{S_CONTENT_DIRECTION}" lang="{S_USER_LANG}">
<head>
<meta charset="utf-8">
+<meta name="robots" content="noindex" />
<title>{SITENAME} :: {PAGE_TITLE}</title>
<style type="text/css">
diff --git a/phpBB/viewforum.php b/phpBB/viewforum.php
index 2c7a0a4829..9a229a0770 100644
--- a/phpBB/viewforum.php
+++ b/phpBB/viewforum.php
@@ -556,20 +556,26 @@ if (sizeof($topic_list))
// If we have some shadow topics, update the rowset to reflect their topic information
if (sizeof($shadow_topic_list))
{
- $sql = 'SELECT *
- FROM ' . TOPICS_TABLE . '
- WHERE ' . $db->sql_in_set('topic_id', array_keys($shadow_topic_list));
+ // SQL array for obtaining shadow topics
+ $sql_array = array(
+ 'SELECT' => 't.*',
+ 'FROM' => array(
+ TOPICS_TABLE => 't'
+ ),
+ 'WHERE' => $db->sql_in_set('t.topic_id', array_keys($shadow_topic_list)),
+ );
/**
* Event to modify the SQL query before the shadowtopic data is retrieved
*
* @event core.viewforum_get_shadowtopic_data
- * @var string sql The SQL string to get the data of any shadowtopics
+ * @var array sql_array SQL array to get the data of any shadowtopics
* @since 3.1-A1
*/
- $vars = array('sql');
+ $vars = array('sql_array');
extract($phpbb_dispatcher->trigger_event('core.viewforum_get_shadowtopic_data', compact($vars)));
+ $sql = $db->sql_build_query('SELECT', $sql_array);
$result = $db->sql_query($sql);
while ($row = $db->sql_fetchrow($result))
diff --git a/phpBB/viewtopic.php b/phpBB/viewtopic.php
index 14040176cb..227cfdd029 100644
--- a/phpBB/viewtopic.php
+++ b/phpBB/viewtopic.php
@@ -1265,11 +1265,7 @@ $db->sql_freeresult($result);
// Load custom profile fields
if ($config['load_cpf_viewtopic'])
{
- if (!class_exists('custom_profile'))
- {
- include($phpbb_root_path . 'includes/functions_profile_fields.' . $phpEx);
- }
- $cp = new custom_profile();
+ $cp = $phpbb_container->get('profilefields.manager');
// Grab all profile fields from users in id cache for later use - similar to the poster cache
$profile_fields_tmp = $cp->generate_profile_fields_template('grab', $id_cache);
diff --git a/tests/auth/fixtures/user.xml b/tests/auth/fixtures/user.xml
index 34584babbf..6d475930a4 100644
--- a/tests/auth/fixtures/user.xml
+++ b/tests/auth/fixtures/user.xml
@@ -18,6 +18,21 @@
<value>1</value>
<value>foobar</value>
<value>foobar</value>
+ <value>$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i</value>
+ <value>0</value>
+ <value>0</value>
+ <value>example@example.com</value>
+ <value>0</value>
+ <value>0</value>
+ <value></value>
+ <value></value>
+ <value></value>
+ <value></value>
+ </row>
+ <row>
+ <value>2</value>
+ <value>foobar2</value>
+ <value>foobar2</value>
<value>$H$9E45lK6J8nLTSm9oJE5aNCSTFK9wqa/</value>
<value>0</value>
<value>0</value>
diff --git a/tests/auth/fixtures/user_533.xml b/tests/auth/fixtures/user_533.xml
new file mode 100644
index 0000000000..c2a86ea0f9
--- /dev/null
+++ b/tests/auth/fixtures/user_533.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<dataset>
+ <table name="phpbb_users">
+ <column>user_id</column>
+ <column>username</column>
+ <column>username_clean</column>
+ <column>user_password</column>
+ <column>user_passchg</column>
+ <column>user_pass_convert</column>
+ <column>user_email</column>
+ <column>user_type</column>
+ <column>user_login_attempts</column>
+ <column>user_permissions</column>
+ <column>user_sig</column>
+ <column>user_occ</column>
+ <column>user_interests</column>
+ <row>
+ <value>1</value>
+ <value>foobar</value>
+ <value>foobar</value>
+ <value>$2a$10$e01Syh9PbJjUkio66eFuUu4FhCE2nRgG7QPc1JACalsPXcIuG2bbi</value>
+ <value>0</value>
+ <value>0</value>
+ <value>example@example.com</value>
+ <value>0</value>
+ <value>0</value>
+ <value></value>
+ <value></value>
+ <value></value>
+ <value></value>
+ </row>
+ <row>
+ <value>2</value>
+ <value>foobar2</value>
+ <value>foobar2</value>
+ <value>$H$9E45lK6J8nLTSm9oJE5aNCSTFK9wqa/</value>
+ <value>0</value>
+ <value>0</value>
+ <value>example@example.com</value>
+ <value>0</value>
+ <value>0</value>
+ <value></value>
+ <value></value>
+ <value></value>
+ <value></value>
+ </row>
+ </table>
+</dataset>
diff --git a/tests/auth/provider_apache_test.php b/tests/auth/provider_apache_test.php
index d7509a72bf..1231c16a4c 100644
--- a/tests/auth/provider_apache_test.php
+++ b/tests/auth/provider_apache_test.php
@@ -26,13 +26,40 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case
$config = new \phpbb\config\config(array());
$this->request = $this->getMock('\phpbb\request\request');
$this->user = $this->getMock('\phpbb\user');
+ $driver_helper = new \phpbb\passwords\driver\helper($config);
+ $passwords_drivers = array(
+ 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $driver_helper),
+ 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $driver_helper),
+ 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $driver_helper),
+ 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $driver_helper),
+ );
+
+ $passwords_helper = new \phpbb\passwords\helper;
+ // Set up passwords manager
+ $passwords_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers));
+
+ if (version_compare(PHP_VERSION, '5.3.7', '<'))
+ {
+ $this->password_hash = '$2a$10$e01Syh9PbJjUkio66eFuUu4FhCE2nRgG7QPc1JACalsPXcIuG2bbi';
+ }
+ else
+ {
+ $this->password_hash = '$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i';
+ }
- $this->provider = new \phpbb\auth\provider\apache($db, $config, $this->request, $this->user, $phpbb_root_path, $phpEx);
+ $this->provider = new \phpbb\auth\provider\apache($db, $config, $passwords_manager, $this->request, $this->user, $phpbb_root_path, $phpEx);
}
public function getDataSet()
{
- return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml');
+ if ((version_compare(PHP_VERSION, '5.3.7', '<')))
+ {
+ return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user_533.xml');
+ }
+ else
+ {
+ return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml');
+ }
}
/**
@@ -79,7 +106,7 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case
'user_row' => array(
'user_id' => '1',
'username' => 'foobar',
- 'user_password' => '$H$9E45lK6J8nLTSm9oJE5aNCSTFK9wqa/',
+ 'user_password' => $this->password_hash,
'user_passchg' => '0',
'user_email' => 'example@example.com',
'user_type' => '0',
@@ -115,7 +142,7 @@ class phpbb_auth_provider_apache_test extends phpbb_database_test_case
'user_regdate' => '0',
'username' => 'foobar',
'username_clean' => 'foobar',
- 'user_password' => '$H$9E45lK6J8nLTSm9oJE5aNCSTFK9wqa/',
+ 'user_password' => $this->password_hash,
'user_passchg' => '0',
'user_pass_convert' => '0',
'user_email' => 'example@example.com',
diff --git a/tests/auth/provider_db_test.php b/tests/auth/provider_db_test.php
index 45a893220b..91ffcdc2a7 100644
--- a/tests/auth/provider_db_test.php
+++ b/tests/auth/provider_db_test.php
@@ -14,7 +14,14 @@ class phpbb_auth_provider_db_test extends phpbb_database_test_case
{
public function getDataSet()
{
- return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml');
+ if ((version_compare(PHP_VERSION, '5.3.7', '<')))
+ {
+ return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user_533.xml');
+ }
+ else
+ {
+ return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/user.xml');
+ }
}
public function test_login()
@@ -29,7 +36,27 @@ class phpbb_auth_provider_db_test extends phpbb_database_test_case
));
$request = $this->getMock('\phpbb\request\request');
$user = $this->getMock('\phpbb\user');
- $provider = new \phpbb\auth\provider\db($db, $config, $request, $user, $phpbb_root_path, $phpEx);
+ $driver_helper = new \phpbb\passwords\driver\helper($config);
+ $passwords_drivers = array(
+ 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $driver_helper),
+ 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $driver_helper),
+ 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $driver_helper),
+ 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $driver_helper),
+ );
+
+ $passwords_helper = new \phpbb\passwords\helper;
+ // Set up passwords manager
+ $passwords_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers));
+
+ $provider = new \phpbb\auth\provider\db($db, $config, $passwords_manager, $request, $user, $phpbb_root_path, $phpEx);
+ if (version_compare(PHP_VERSION, '5.3.7', '<'))
+ {
+ $password_hash = '$2a$10$e01Syh9PbJjUkio66eFuUu4FhCE2nRgG7QPc1JACalsPXcIuG2bbi';
+ }
+ else
+ {
+ $password_hash = '$2y$10$4RmpyVu2y8Yf/lP3.yQBquKvE54TCUuEDEBJYY6FDDFN3LcbCGz9i';
+ }
$expected = array(
'status' => LOGIN_SUCCESS,
@@ -37,7 +64,7 @@ class phpbb_auth_provider_db_test extends phpbb_database_test_case
'user_row' => array(
'user_id' => '1',
'username' => 'foobar',
- 'user_password' => '$H$9E45lK6J8nLTSm9oJE5aNCSTFK9wqa/',
+ 'user_password' => $password_hash,
'user_passchg' => '0',
'user_pass_convert' => '0',
'user_email' => 'example@example.com',
@@ -47,5 +74,10 @@ class phpbb_auth_provider_db_test extends phpbb_database_test_case
);
$this->assertEquals($expected, $provider->login('foobar', 'example'));
+
+ // Check if convert works
+ $login_return = $provider->login('foobar2', 'example');
+ $password_start = (version_compare(PHP_VERSION, '5.3.7', '<')) ? '$2a$10$' : '$2y$10$';
+ $this->assertStringStartsWith($password_start, $login_return['user_row']['user_password']);
}
}
diff --git a/tests/extension/ext/vendor2/foo/acp/a_info.php b/tests/extension/ext/vendor2/foo/acp/a_info.php
index 27e67c1556..e1eaa340b7 100644
--- a/tests/extension/ext/vendor2/foo/acp/a_info.php
+++ b/tests/extension/ext/vendor2/foo/acp/a_info.php
@@ -11,7 +11,7 @@ class a_info
'title' => 'Foobar',
'version' => '3.1.0-dev',
'modes' => array(
- 'config' => array('title' => 'Config', 'auth' => '', 'cat' => array('ACP_MODS')),
+ 'config' => array('title' => 'Config', 'auth' => 'ext_vendor2/foo', 'cat' => array('ACP_MODS')),
),
);
}
diff --git a/tests/extension/modules_test.php b/tests/extension/modules_test.php
index 5dcb24c691..c0a136e173 100644
--- a/tests/extension/modules_test.php
+++ b/tests/extension/modules_test.php
@@ -12,6 +12,7 @@ require_once dirname(__FILE__) . '/ext/vendor2/foo/mcp/a_info.php';
require_once dirname(__FILE__) . '/ext/vendor2/foo/acp/fail_info.php';
require_once dirname(__FILE__) . '/ext/vendor2/bar/acp/a_info.php';
require_once dirname(__FILE__) . '/../../phpBB/includes/acp/acp_modules.php';
+require_once dirname(__FILE__) . '/../../phpBB/includes/functions_module.php';
class phpbb_extension_modules_test extends phpbb_test_case
{
@@ -59,7 +60,7 @@ class phpbb_extension_modules_test extends phpbb_test_case
'title' => 'Foobar',
'version' => '3.1.0-dev',
'modes' => array(
- 'config' => array('title' => 'Config', 'auth' => '', 'cat' => array('ACP_MODS')),
+ 'config' => array('title' => 'Config', 'auth' => 'ext_vendor2/foo', 'cat' => array('ACP_MODS')),
),
),
'acp_foobar' => array(
@@ -133,7 +134,7 @@ class phpbb_extension_modules_test extends phpbb_test_case
'title' => 'Foobar',
'version' => '3.1.0-dev',
'modes' => array (
- 'config' => array ('title' => 'Config', 'auth' => '', 'cat' => array ('ACP_MODS')),
+ 'config' => array ('title' => 'Config', 'auth' => 'ext_vendor2/foo', 'cat' => array ('ACP_MODS')),
),
),
), $acp_modules);
@@ -157,7 +158,7 @@ class phpbb_extension_modules_test extends phpbb_test_case
'title' => 'Foobar',
'version' => '3.1.0-dev',
'modes' => array(
- 'config' => array('title' => 'Config', 'auth' => '', 'cat' => array('ACP_MODS')),
+ 'config' => array('title' => 'Config', 'auth' => 'ext_vendor2/foo', 'cat' => array('ACP_MODS')),
),
),
'acp_foobar' => array(
@@ -191,4 +192,41 @@ class phpbb_extension_modules_test extends phpbb_test_case
)
), $acp_modules);
}
+
+ public function module_auth_test_data()
+ {
+ return array(
+ // module_auth, expected result
+ array('ext_foo', false),
+ array('ext_foo/bar', false),
+ array('ext_vendor3/bar', false),
+ array('ext_vendor2/foo', true),
+ );
+ }
+
+ /**
+ * @dataProvider module_auth_test_data
+ */
+ public function test_modules_auth($module_auth, $expected)
+ {
+ global $phpbb_extension_manager;
+
+ $phpbb_extension_manager = $this->extension_manager = new phpbb_mock_extension_manager(
+ dirname(__FILE__) . '/',
+ array(
+ 'vendor2/foo' => array(
+ 'ext_name' => 'vendor2/foo',
+ 'ext_active' => '1',
+ 'ext_path' => 'ext/vendor2/foo/',
+ ),
+ 'vendor3/bar' => array(
+ 'ext_name' => 'vendor3/bar',
+ 'ext_active' => '0',
+ 'ext_path' => 'ext/vendor3/bar/',
+ ),
+ )
+ );
+
+ $this->assertEquals($expected, p_master::module_auth($module_auth, 0));
+ }
}
diff --git a/tests/functional/extension_controller_test.php b/tests/functional/extension_controller_test.php
index 37752b8fbb..4725301141 100644
--- a/tests/functional/extension_controller_test.php
+++ b/tests/functional/extension_controller_test.php
@@ -111,4 +111,32 @@ class phpbb_functional_extension_controller_test extends phpbb_functional_test_c
$this->assert_response_html(404);
$this->assertContains('No route found for "GET /does/not/exist"', $crawler->filter('body')->text());
}
+
+ /**
+ * Check the output of a controller using the template system
+ */
+ public function test_redirect()
+ {
+ $filesystem = new \phpbb\filesystem();
+ $this->phpbb_extension_manager->enable('foo/bar');
+ $crawler = self::request('GET', 'app.php/foo/redirect');
+
+ $nodes = $crawler->filter('div')->extract(array('id'));
+
+ foreach ($nodes as $redirect)
+ {
+ if (strpos($redirect, 'redirect_expected') !== 0)
+ {
+ continue;
+ }
+
+ $row_num = str_replace('redirect_expected_', '', $redirect);
+
+ $redirect = $crawler->filter('#redirect_' . $row_num)->text();
+ $redirect = substr($redirect, 0, strpos($redirect, 'sid') - 1);
+ $this->assertEquals($crawler->filter('#redirect_expected_' . $row_num)->text(), $redirect);
+ }
+
+ $this->phpbb_extension_manager->purge('foo/bar');
+ }
}
diff --git a/tests/functional/fixtures/ext/foo/bar/config/routing.yml b/tests/functional/fixtures/ext/foo/bar/config/routing.yml
index 09a30a8c67..9b1ce3cfd7 100644
--- a/tests/functional/fixtures/ext/foo/bar/config/routing.yml
+++ b/tests/functional/fixtures/ext/foo/bar/config/routing.yml
@@ -13,3 +13,7 @@ foo_template_controller:
foo_exception_controller:
pattern: /foo/exception
defaults: { _controller: foo_bar.controller:exception }
+
+foo_redirect_controller:
+ pattern: /foo/redirect
+ defaults: { _controller: foo_bar.controller:redirect }
diff --git a/tests/functional/fixtures/ext/foo/bar/config/services.yml b/tests/functional/fixtures/ext/foo/bar/config/services.yml
index 64e1163408..cec69f7807 100644
--- a/tests/functional/fixtures/ext/foo/bar/config/services.yml
+++ b/tests/functional/fixtures/ext/foo/bar/config/services.yml
@@ -3,7 +3,12 @@ services:
class: foo\bar\controller\controller
arguments:
- @controller.helper
+ - @path_helper
- @template
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+
foo_bar.listener.permission:
class: foo\bar\event\permission
tags:
@@ -12,4 +17,3 @@ services:
class: foo\bar\event\user_setup
tags:
- { name: event.listener }
-
diff --git a/tests/functional/fixtures/ext/foo/bar/controller/controller.php b/tests/functional/fixtures/ext/foo/bar/controller/controller.php
index 259d548299..558b202948 100644
--- a/tests/functional/fixtures/ext/foo/bar/controller/controller.php
+++ b/tests/functional/fixtures/ext/foo/bar/controller/controller.php
@@ -7,11 +7,18 @@ use Symfony\Component\HttpFoundation\Response;
class controller
{
protected $template;
+ protected $helper;
+ protected $path_helper;
+ protected $config;
- public function __construct(\phpbb\controller\helper $helper, \phpbb\template\template $template)
+ public function __construct(\phpbb\controller\helper $helper, \phpbb\path_helper $path_helper, \phpbb\template\template $template, \phpbb\config\config $config, $root_path, $php_ext)
{
$this->template = $template;
$this->helper = $helper;
+ $this->path_helper = $path_helper;
+ $this->config = $config;
+ $this->root_path = $root_path;
+ $this->php_ext = $php_ext;
}
public function handle()
@@ -35,4 +42,75 @@ class controller
{
throw new \phpbb\controller\exception('Exception thrown from foo/exception route');
}
+
+ public function redirect()
+ {
+ $url_root = generate_board_url();
+
+ $rewrite_prefix = (!empty($this->config['enable_mod_rewrite'])) ? '' : 'app.php/';
+
+ $redirects = array(
+ array(
+ append_sid($this->root_path . 'index.' . $this->php_ext),
+ 'index.php',
+ ),
+ array(
+ append_sid($this->root_path . 'foo/bar/index.' . $this->php_ext),
+ 'foo/bar/index.php',
+ ),
+ array(
+ append_sid($this->root_path . 'tests/index.' . $this->php_ext),
+ 'tests/index.php',
+ ),
+ array(
+ $this->helper->url('index'),
+ $rewrite_prefix . 'index',
+ ),
+ array(
+ $this->helper->url('tests/index'),
+ $rewrite_prefix . 'tests/index',
+ ),
+ array(
+ $this->helper->url('tests/../index'),
+ $rewrite_prefix . 'index',
+ ),
+ /*
+ // helper URLs starting with ../ are prone to failure.
+ // Do not test them right now.
+ array(
+ $this->helper->url('../index'),
+ '../index',
+ ),
+ array(
+ $this->helper->url('../../index'),
+ '../index',
+ ),
+ array(
+ $this->helper->url('../tests/index'),
+ $rewrite_prefix . '../tests/index',
+ ),
+ array(
+ $this->helper->url('../tests/../index'),
+ '../index',
+ ),
+ array(
+ $this->helper->url('../../tests/index'),
+ '../tests/index',
+ ),
+ */
+ );
+
+ foreach ($redirects as $redirect)
+ {
+ $this->template->assign_block_vars('redirects', array(
+ 'URL' => redirect($redirect[0], true),
+ ));
+
+ $this->template->assign_block_vars('redirects_expected', array(
+ 'URL' => $this->path_helper->clean_url($url_root . '/' . $redirect[1]),
+ ));
+ }
+
+ return $this->helper->render('redirect_body.html');
+ }
}
diff --git a/tests/functional/fixtures/ext/foo/bar/styles/prosilver/template/redirect_body.html b/tests/functional/fixtures/ext/foo/bar/styles/prosilver/template/redirect_body.html
new file mode 100644
index 0000000000..2b70b0fe59
--- /dev/null
+++ b/tests/functional/fixtures/ext/foo/bar/styles/prosilver/template/redirect_body.html
@@ -0,0 +1,8 @@
+<!-- INCLUDE overall_header.html -->
+<!-- BEGIN redirects -->
+<div id="redirect_{redirects.S_ROW_COUNT}">{redirects.URL}</div>
+<!-- END redirects -->
+<!-- BEGIN redirects_expected -->
+<div id="redirect_expected_{redirects_expected.S_ROW_COUNT}">{redirects_expected.URL}</div>
+<!-- END redirects_expected -->
+<!-- INCLUDE overall_footer.html -->
diff --git a/tests/functional/forum_password_test.php b/tests/functional/forum_password_test.php
new file mode 100644
index 0000000000..40a8059ad1
--- /dev/null
+++ b/tests/functional/forum_password_test.php
@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @group functional
+*/
+class phpbb_functional_forum_password_test extends phpbb_functional_test_case
+{
+ public function test_setup_forum_with_password()
+ {
+ $this->login();
+ $this->admin_login();
+
+ $crawler = self::request('GET', "adm/index.php?i=acp_forums&mode=manage&sid={$this->sid}");
+ $form = $crawler->selectButton('addforum')->form(array(
+ 'forum_name' => 'Password protected',
+ ));
+ $crawler = self::submit($form);
+ $form = $crawler->selectButton('update')->form(array(
+ 'forum_perm_from' => 2,
+ 'forum_password' => 'foobar',
+ 'forum_password_confirm' => 'foobar',
+ ));
+ $crawler = self::submit($form);
+ }
+
+ public function data_enter_forum_with_password()
+ {
+ return array(
+ array('foowrong', 'WRONG_PASSWORD'),
+ array('foobar', 'NO_TOPICS'),
+ );
+ }
+
+ /**
+ * @dataProvider data_enter_forum_with_password
+ */
+ public function test_enter_forum_with_password($password, $message)
+ {
+ $crawler = self::request('GET', "index.php?sid={$this->sid}");
+ preg_match('/.?f=([0-9])/', $crawler->selectLink('Password protected')->link()->getUri(), $match);
+ $crawler = self::request('GET', "viewforum.php?f={$match[1]}&sid={$this->sid}");
+ $form = $crawler->selectButton('login')->form(array(
+ 'password' => $password,
+ ));
+ $crawler = self::submit($form);
+ $this->assertContainsLang($message, $crawler->text());
+ }
+}
diff --git a/tests/functional/plupload_test.php b/tests/functional/plupload_test.php
index 6dd9224839..a91e70c7bb 100644
--- a/tests/functional/plupload_test.php
+++ b/tests/functional/plupload_test.php
@@ -107,7 +107,7 @@ class phpbb_functional_plupload_test extends phpbb_functional_test_case
else
{
$response = json_decode(self::$client->getResponse()->getContent(), true);
- $this->assertEquals('valid.jpg', $response[0]['real_filename']);
+ $this->assertEquals('valid.jpg', $response['data'][0]['real_filename']);
}
unlink($this->path . 'chunk');
@@ -144,6 +144,6 @@ class phpbb_functional_plupload_test extends phpbb_functional_test_case
);
$response = json_decode(self::$client->getResponse()->getContent(), true);
- $this->assertEquals('valid.jpg', $response[0]['real_filename']);
+ $this->assertEquals('valid.jpg', $response['data'][0]['real_filename']);
}
}
diff --git a/tests/lint_test.php b/tests/lint_test.php
index eba117839b..b0149063bd 100644
--- a/tests/lint_test.php
+++ b/tests/lint_test.php
@@ -9,17 +9,30 @@
class phpbb_lint_test extends phpbb_test_case
{
+ static protected $php_binary;
static protected $exclude;
static public function setUpBeforeClass()
{
+ // Try to use PHP_BINARY constant if available so lint tests are run
+ // using the same php binary as phpunit. If not available (pre PHP
+ // 5.4), assume binary is called 'php' and is in PATH.
+ self::$php_binary = defined('PHP_BINARY') ? escapeshellcmd(PHP_BINARY) : 'php';
+
$output = array();
$status = 1;
- exec('(php -v) 2>&1', $output, $status);
+ exec(sprintf('(%s --version) 2>&1', self::$php_binary), $output, $status);
if ($status)
{
$output = implode("\n", $output);
- self::markTestSkipped("php is not in PATH or broken: $output");
+ if (self::$php_binary === 'php')
+ {
+ self::markTestSkipped(sprintf('php is not in PATH or broken. Output: %s', $output));
+ }
+ else
+ {
+ self::markTestSkipped(sprintf('Could not run PHP_BINARY %s. Output: %s', self::$php_binary, $output));
+ }
}
self::$exclude = array(
@@ -65,13 +78,12 @@ class phpbb_lint_test extends phpbb_test_case
}
else if (substr($filename, strlen($filename)-4) == '.php')
{
- // assume php binary is called php and it is in PATH
- $cmd = '(php -l ' . escapeshellarg($path) . ') 2>&1';
+ $cmd = sprintf('(%s -l %s) 2>&1', self::$php_binary, escapeshellarg($path));
$output = array();
$status = 1;
exec($cmd, $output, $status);
$output = implode("\n", $output);
- $this->assertEquals(0, $status, "php -l failed for $path:\n$output");
+ $this->assertEquals(0, $status, "PHP lint failed for $path:\n$output");
}
}
}
diff --git a/tests/passwords/drivers_test.php b/tests/passwords/drivers_test.php
new file mode 100644
index 0000000000..2d26be7da5
--- /dev/null
+++ b/tests/passwords/drivers_test.php
@@ -0,0 +1,81 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_passwords_helper_test extends PHPUnit_Framework_TestCase
+{
+ public function setUp()
+ {
+ // Prepare dependencies for drivers
+ $config = new \phpbb\config\config(array());
+ $this->driver_helper = new \phpbb\passwords\driver\helper($config);
+
+ $this->passwords_drivers = array(
+ 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $this->driver_helper),
+ 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $this->driver_helper),
+ 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $this->driver_helper),
+ 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $this->driver_helper),
+ );
+ }
+
+ public function data_helper_encode64()
+ {
+ return array(
+ array('foobar', 6, 'axqPW3aQ'),
+ array('foobar', 7, 'axqPW3aQ..'),
+ array('foobar', 5, 'axqPW34'),
+ );
+ }
+
+ /**
+ * @dataProvider data_helper_encode64
+ */
+ public function test_helper_encode64($input, $length, $output)
+ {
+ $return = $this->driver_helper->hash_encode64($input, $length);
+ $this->assertEquals($output, $return);
+ }
+
+ public function data_get_random_salt()
+ {
+ return array(
+ array(24, false),
+ array(24, '/dev/foobar'),
+ );
+ }
+
+ /**
+ * @dataProvider data_get_random_salt
+ */
+ public function test_get_random_salt($length, $rand_seed)
+ {
+ $rand_string = (empty($rand_seed)) ? $this->driver_helper->get_random_salt($length) : $this->driver_helper->get_random_salt($length, $rand_seed);
+ $start = microtime(true);
+
+ // Run each test for max. 1 second
+ while ((microtime(true) - $start) < 1)
+ {
+ $urandom_string = (empty($rand_seed)) ? $this->driver_helper->get_random_salt($length) : $this->driver_helper->get_random_salt($length, $rand_seed);
+ $this->assertEquals($length, strlen($urandom_string));
+ $this->assertNotEquals($rand_string, $urandom_string);
+ }
+ }
+
+ public function test_get_hash_settings_salted_md5()
+ {
+ $settings = $this->passwords_drivers['passwords.driver.salted_md5']->get_hash_settings('$H$9isfrtKXWqrz8PvztXlL3.daw4U0zI1');
+ $this->assertEquals(array(
+ 'count' => pow(2, 11),
+ 'salt' => 'isfrtKXW',
+ 'full' => '$H$9isfrtKXW',
+ ),
+ $settings
+ );
+ $this->assertEquals(false, $this->passwords_drivers['passwords.driver.salted_md5']->get_hash_settings(false));
+ }
+}
diff --git a/tests/passwords/manager_test.php b/tests/passwords/manager_test.php
new file mode 100644
index 0000000000..ee295ff043
--- /dev/null
+++ b/tests/passwords/manager_test.php
@@ -0,0 +1,295 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2013 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class phpbb_passwords_manager_test extends PHPUnit_Framework_TestCase
+{
+ protected $passwords_drivers;
+
+ protected $pw_characters = '0123456789abcdefghijklmnopqrstuvwyzABCDEFGHIJKLMNOPQRSTUVXYZ.,_!?/\\';
+
+ protected $default_pw = 'foobar';
+
+ public function setUp()
+ {
+ // Prepare dependencies for manager and driver
+ $config = new \phpbb\config\config(array());
+ $this->driver_helper = new \phpbb\passwords\driver\helper($config);
+
+ $this->passwords_drivers = array(
+ 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $this->driver_helper),
+ 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $this->driver_helper),
+ 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $this->driver_helper),
+ 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $this->driver_helper),
+ );
+
+ $this->helper = new \phpbb\passwords\helper;
+ // Set up passwords manager
+ $this->manager = new \phpbb\passwords\manager($config, $this->passwords_drivers, $this->helper, array_keys($this->passwords_drivers));
+ }
+
+ public function hash_password_data()
+ {
+ if (version_compare(PHP_VERSION, '5.3.7', '<'))
+ {
+ return array(
+ array('', '2a', 60),
+ array('passwords.driver.bcrypt_2y', '2a', 60),
+ array('passwords.driver.bcrypt', '2a', 60),
+ array('passwords.driver.salted_md5', 'H', 34),
+ array('passwords.driver.foobar', '', false),
+ );
+ }
+ else
+ {
+ return array(
+ array('', '2y', 60),
+ array('passwords.driver.bcrypt_2y', '2y', 60),
+ array('passwords.driver.bcrypt', '2a', 60),
+ array('passwords.driver.salted_md5', 'H', 34),
+ array('passwords.driver.foobar', '', false),
+ );
+ }
+ }
+
+ /**
+ * @dataProvider hash_password_data
+ */
+ public function test_hash_password($type, $prefix, $length)
+ {
+ $password = $this->default_pw;
+
+ if (!$length)
+ {
+ $this->assertEquals(false, $hash = $this->manager->hash($password, $type));
+ return;
+ }
+ $time = microtime(true);
+
+ // Limit each test to 1 second
+ while ((microtime(true) - $time) < 1)
+ {
+ $hash = $this->manager->hash($password, $type);
+ preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $hash, $match);
+ $this->assertEquals($prefix, $match[1]);
+ $this->assertEquals($length, strlen($hash));
+ $password .= $this->pw_characters[mt_rand(0, 66)];
+ }
+ }
+
+ public function check_password_data()
+ {
+ if (version_compare(PHP_VERSION, '5.3.7', '<'))
+ {
+ return array(
+ array('passwords.driver.bcrypt'),
+ array('passwords.driver.salted_md5'),
+ array('passwords.driver.phpass'),
+ );
+ }
+ else
+ {
+ return array(
+ array('passwords.driver.bcrypt_2y'),
+ array('passwords.driver.bcrypt'),
+ array('passwords.driver.salted_md5'),
+ array('passwords.driver.phpass'),
+ );
+ }
+ }
+
+ /**
+ * @dataProvider check_password_data
+ */
+ public function test_check_password($hash_type)
+ {
+ $password = $this->default_pw;
+ $time = microtime(true);
+ // Limit each test to 1 second
+ while ((microtime(true) - $time) < 1)
+ {
+ $hash = $this->manager->hash($password, $hash_type);
+ $this->assertEquals(true, $this->manager->check($password, $hash));
+ $password .= $this->pw_characters[mt_rand(0, 66)];
+ $this->assertEquals(false, $this->manager->check($password, $hash));
+ }
+
+ // Check if convert_flag is correctly set
+ $default_type = (version_compare(PHP_VERSION, '5.3.7', '<')) ? 'passwords.driver.bcrypt' : 'passwords.driver.bcrypt_2y';
+ $this->assertEquals(($hash_type !== $default_type), $this->manager->convert_flag);
+ }
+
+
+ public function check_hash_exceptions_data()
+ {
+ return array(
+ array('foobar', '3858f62230ac3c915f300c664312c63f', true),
+ array('foobar', '$S$b57a939fa4f2c04413a4eea9734a0903647b7adb93181295', false),
+ array('foobar', '$2a\S$kkkkaakdkdiej39023903204j2k3490234jk234j02349', false),
+ array('foobar', '$H$kklk938d023k//k3023', false),
+ array('foobar', '$H$3PtYMgXb39lrIWkgoxYLWtRkZtY3AY/', false),
+ array('foobar', '$2a$kwiweorurlaeirw', false),
+ );
+ }
+
+ /**
+ * @dataProvider check_hash_exceptions_data
+ */
+ public function test_check_hash_exceptions($password, $hash, $expected)
+ {
+ $this->assertEquals($expected, $this->manager->check($password, $hash));
+ }
+
+ public function data_hash_password_length()
+ {
+ return array(
+ array('passwords.driver.bcrypt', false),
+ array('passwords.driver.bcrypt_2y', false),
+ array('passwords.driver.salted_md5', '3858f62230ac3c915f300c664312c63f'),
+ array('passwords.driver.phpass', '3858f62230ac3c915f300c664312c63f'),
+ );
+ }
+
+ /**
+ * @dataProvider data_hash_password_length
+ */
+ public function test_hash_password_length($driver, $expected)
+ {
+ $this->assertEquals($expected, $this->passwords_drivers[$driver]->hash('foobar', 'foobar'));
+ }
+
+ public function test_hash_password_8bit_bcrypt()
+ {
+ $this->assertEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt'));
+ if (version_compare(PHP_VERSION, '5.3.7', '<'))
+ {
+ $this->assertEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt_2y'));
+ }
+ else
+ {
+ $this->assertNotEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt_2y'));
+ }
+ }
+
+ public function test_combined_hash_data()
+ {
+ if (version_compare(PHP_VERSION, '5.3.7', '<'))
+ {
+ return array(
+ array(
+ 'passwords.driver.salted_md5',
+ array('passwords.driver.bcrypt'),
+ ),
+ array(
+ 'passwords.driver.phpass',
+ array('passwords.driver.salted_md5'),
+ ),
+ array(
+ 'passwords.driver.salted_md5',
+ array('passwords.driver.phpass', 'passwords.driver.bcrypt'),
+ ),
+ array(
+ 'passwords.driver.salted_md5',
+ array('passwords.driver.salted_md5'),
+ false,
+ ),
+ array(
+ '$H$',
+ array('$2a$'),
+ ),
+ );
+ }
+ else
+ {
+ return array(
+ array(
+ 'passwords.driver.salted_md5',
+ array('passwords.driver.bcrypt_2y'),
+ ),
+ array(
+ 'passwords.driver.salted_md5',
+ array('passwords.driver.bcrypt'),
+ ),
+ array(
+ 'passwords.driver.phpass',
+ array('passwords.driver.salted_md5'),
+ ),
+ array(
+ 'passwords.driver.salted_md5',
+ array('passwords.driver.bcrypt_2y', 'passwords.driver.bcrypt'),
+ ),
+ array(
+ 'passwords.driver.salted_md5',
+ array('passwords.driver.salted_md5'),
+ false,
+ ),
+ array(
+ 'passwords.driver.bcrypt_2y',
+ array('passwords.driver.salted_md4'),
+ false,
+ ),
+ array(
+ '$H$',
+ array('$2y$'),
+ ),
+ );
+ }
+ }
+
+ /**
+ * @dataProvider test_combined_hash_data
+ */
+ public function test_combined_hash_password($first_type, $second_type, $expected = true)
+ {
+ $password = $this->default_pw;
+ $time = microtime(true);
+ // Limit each test to 1 second
+ while ((microtime(true) - $time) < 1)
+ {
+ $hash = $this->manager->hash($password, $first_type);
+ $combined_hash = $this->manager->hash($hash, $second_type);
+ $this->assertEquals($expected, $this->manager->check($password, $combined_hash));
+ $password .= $this->pw_characters[mt_rand(0, 66)];
+ $this->assertEquals(false, $this->manager->check($password, $combined_hash));
+
+ // If we are expecting the check to fail then there is
+ // no need to run this more than once
+ if (!$expected)
+ {
+ break;
+ }
+ }
+ }
+
+ public function test_unique_id()
+ {
+ $time = microtime(true);
+ $first_id = $this->driver_helper->unique_id();
+ // Limit test to 1 second
+ while ((microtime(true) - $time) < 1)
+ {
+ $this->assertNotEquals($first_id, $this->driver_helper->unique_id());
+ }
+ }
+
+ public function test_check_hash_with_large_input()
+ {
+ // 16 MB password, should be rejected quite fast
+ $start_time = time();
+ $this->assertFalse($this->manager->check(str_repeat('a', 1024 * 1024 * 16), '$H$9isfrtKXWqrz8PvztXlL3.daw4U0zI1'));
+ $this->assertLessThanOrEqual(5, time() - $start_time);
+ }
+
+ public function test_hash_password_with_large_input()
+ {
+ // 16 MB password, should be rejected quite fast
+ $start_time = time();
+ $this->assertFalse($this->manager->hash(str_repeat('a', 1024 * 1024 * 16)));
+ $this->assertLessThanOrEqual(5, time() - $start_time);
+ }
+}
diff --git a/tests/path_helper/web_root_path_test.php b/tests/path_helper/web_root_path_test.php
index 2e1a37e02b..2c22511402 100644
--- a/tests/path_helper/web_root_path_test.php
+++ b/tests/path_helper/web_root_path_test.php
@@ -146,4 +146,27 @@ class phpbb_path_helper_web_root_path_test extends phpbb_test_case
$this->assertEquals($expected, $path_helper->update_web_root_path($input, $symfony_request));
}
+
+ public function clean_url_data()
+ {
+ return array(
+ array('', ''),
+ array('://', '://'),
+ array('http://', 'http://'),
+ array('http://one/two/three', 'http://one/two/three'),
+ array('http://../one/two', 'http://../one/two'),
+ array('http://one/../two/three', 'http://two/three'),
+ array('http://one/two/../three', 'http://one/three'),
+ array('http://one/two/../../three', 'http://three'),
+ array('http://one/two/../../../three', 'http://../three'),
+ );
+ }
+
+ /**
+ * @dataProvider clean_url_data
+ */
+ public function test_clean_url($input, $expected)
+ {
+ $this->assertEquals($expected, $this->path_helper->clean_url($input));
+ }
}
diff --git a/tests/profile/custom_test.php b/tests/profile/custom_test.php
index 1f33b45ba9..e68f1f7c4b 100644
--- a/tests/profile/custom_test.php
+++ b/tests/profile/custom_test.php
@@ -7,49 +7,65 @@
*
*/
-require_once dirname(__FILE__) . '/../../phpBB/includes/functions_profile_fields.php';
-
class phpbb_profile_custom_test extends phpbb_database_test_case
{
public function getDataSet()
{
- return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/profile_fields.xml');
+ return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/profile_fields.xml');
}
- static public function dropdownFields()
+ static public function dropdown_fields()
{
return array(
// note, there is an offset of 1 between option_id (0-indexed)
// in the database and values (1-indexed) to avoid problems with
// transmitting 0 in an HTML form
// required, value, expected
- array(1, '0', 'FIELD_INVALID_VALUE', 'Required field should throw error for out-of-range value'),
- array(1, '1', 'FIELD_REQUIRED', 'Required field should throw error for default value'),
- array(1, '2', false, 'Required field should accept non-default value'),
- array(0, '0', 'FIELD_INVALID_VALUE', 'Optional field should throw error for out-of-range value'),
- array(0, '1', false, 'Optional field should accept default value'),
- array(0, '2', false, 'Optional field should accept non-default value'),
+ array(1, '0', 'FIELD_INVALID_VALUE-field', 'Required field should throw error for out-of-range value'),
+ array(1, '1', 'FIELD_REQUIRED-field', 'Required field should throw error for default value'),
+ array(1, '2', false, 'Required field should accept non-default value'),
+ array(0, '0', 'FIELD_INVALID_VALUE-field', 'Optional field should throw error for out-of-range value'),
+ array(0, '1', false, 'Optional field should accept default value'),
+ array(0, '2', false, 'Optional field should accept non-default value'),
);
}
/**
- * @dataProvider dropdownFields
+ * @dataProvider dropdown_fields
*/
public function test_dropdown_validate($field_required, $field_value, $expected, $description)
{
- global $db;
+ global $db, $table_prefix;
$db = $this->new_dbal();
$field_data = array(
'field_id' => 1,
'lang_id' => 1,
+ 'lang_name' => 'field',
'field_novalue' => 1,
'field_required' => $field_required,
);
+ $user = $this->getMock('\phpbb\user');
+ $user->expects($this->any())
+ ->method('lang')
+ ->will($this->returnCallback(array($this, 'return_callback_implode')));
+
+ $request = $this->getMock('\phpbb\request\request');
+ $template = $this->getMock('\phpbb\template\template');
- $cp = new custom_profile;
- $result = $cp->validate_profile_field(FIELD_DROPDOWN, $field_value, $field_data);
+ $cp = new \phpbb\profilefields\type\type_dropdown(
+ new \phpbb\profilefields\lang_helper($db, $table_prefix . 'profile_fields_lang'),
+ $request,
+ $template,
+ $user
+ );
+ $result = $cp->validate_profile_field($field_value, $field_data);
$this->assertEquals($expected, $result, $description);
}
+
+ public function return_callback_implode()
+ {
+ return implode('-', func_get_args());
+ }
}
diff --git a/tests/profile/fixtures/profile_fields.xml b/tests/profile/fixtures/profile_fields.xml
index 0b2929f625..e0c260bbf5 100644
--- a/tests/profile/fixtures/profile_fields.xml
+++ b/tests/profile/fixtures/profile_fields.xml
@@ -10,21 +10,21 @@
<value>1</value>
<value>1</value>
<value>0</value>
- <value>5</value>
+ <value>profilefields.type.dropdown</value>
<value>Default Option</value>
</row>
<row>
<value>1</value>
<value>1</value>
<value>1</value>
- <value>5</value>
+ <value>profilefields.type.dropdown</value>
<value>First Alternative</value>
</row>
<row>
<value>1</value>
<value>1</value>
<value>2</value>
- <value>5</value>
+ <value>profilefields.type.dropdown</value>
<value>Third Alternative</value>
</row>
</table>
diff --git a/tests/security/hash_test.php b/tests/security/hash_test.php
index e226365ef3..bc1bebd87a 100644
--- a/tests/security/hash_test.php
+++ b/tests/security/hash_test.php
@@ -11,6 +11,31 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
class phpbb_security_hash_test extends phpbb_test_case
{
+ public function setUp()
+ {
+ global $phpbb_container;
+
+ $config = new \phpbb\config\config(array());
+ $phpbb_container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+ $driver_helper = new \phpbb\passwords\driver\helper($config);
+ $passwords_drivers = array(
+ 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $driver_helper),
+ 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $driver_helper),
+ 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $driver_helper),
+ 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $driver_helper),
+ );
+
+ $passwords_helper = new \phpbb\passwords\helper;
+ // Set up passwords manager
+ $passwords_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers));
+
+ $phpbb_container
+ ->expects($this->any())
+ ->method('get')
+ ->with('passwords.manager')
+ ->will($this->returnValue($passwords_manager));
+ }
+
public function test_check_hash_with_phpass()
{
$this->assertTrue(phpbb_check_hash('test', '$H$9isfrtKXWqrz8PvztXlL3.daw4U0zI1'));
diff --git a/tests/security/redirect_test.php b/tests/security/redirect_test.php
index 8e36780ca4..77dc955c26 100644
--- a/tests/security/redirect_test.php
+++ b/tests/security/redirect_test.php
@@ -13,19 +13,87 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
class phpbb_security_redirect_test extends phpbb_security_test_base
{
+ protected $path_helper;
+
+ protected $controller_helper;
+
public function provider()
{
+ $this->controller_helper = $this->get_controller_helper();
// array(Input -> redirect(), expected triggered error (else false), expected returned result url (else false))
return array(
- array('data://x', false, 'http://localhost/phpBB'),
- array('bad://localhost/phpBB/index.php', 'INSECURE_REDIRECT', false),
- array('http://www.otherdomain.com/somescript.php', false, 'http://localhost/phpBB'),
- array("http://localhost/phpBB/memberlist.php\n\rConnection: close", 'INSECURE_REDIRECT', false),
- array('javascript:test', false, 'http://localhost/phpBB/../javascript:test'),
- array('http://localhost/phpBB/index.php;url=', 'INSECURE_REDIRECT', false),
+ array('data://x', false, false, 'http://localhost/phpBB'),
+ array('bad://localhost/phpBB/index.php', false, 'INSECURE_REDIRECT', false),
+ array('http://www.otherdomain.com/somescript.php', false, false, 'http://localhost/phpBB'),
+ array("http://localhost/phpBB/memberlist.php\n\rConnection: close", false, 'INSECURE_REDIRECT', false),
+ array('javascript:test', false, false, 'http://localhost/phpBB/javascript:test'),
+ array('http://localhost/phpBB/index.php;url=', false, 'INSECURE_REDIRECT', false),
+ array('http://localhost/phpBB/app.php/foobar', false, false, 'http://localhost/phpBB/app.php/foobar'),
+ array('./app.php/foobar', false, false, 'http://localhost/phpBB/app.php/foobar'),
+ array('app.php/foobar', false, false, 'http://localhost/phpBB/app.php/foobar'),
+ array('./../app.php/foobar', false, false, 'http://localhost/app.php/foobar'),
+ array('./../app.php/foobar', true, false, 'http://localhost/app.php/foobar'),
+ array('./../app.php/foo/bar', false, false, 'http://localhost/app.php/foo/bar'),
+ array('./../app.php/foo/bar', true, false, 'http://localhost/app.php/foo/bar'),
+ array('./../foo/bar', false, false, 'http://localhost/foo/bar'),
+ array('./../foo/bar', true, false, 'http://localhost/foo/bar'),
+ array('app.php/', false, false, 'http://localhost/phpBB/app.php/'),
+ array($this->controller_helper->url('a'), false, false, 'http://localhost/phpBB/app.php/a'),
+ array($this->controller_helper->url(''), false, false, 'http://localhost/phpBB/app.php/'),
+ array('./app.php/', false, false, 'http://localhost/phpBB/app.php/'),
+ array('foobar', false, false, 'http://localhost/phpBB/foobar'),
+ array('./foobar', false, false, 'http://localhost/phpBB/foobar'),
+ array('foo/bar', false, false, 'http://localhost/phpBB/foo/bar'),
+ array('./foo/bar', false, false, 'http://localhost/phpBB/foo/bar'),
+ array('./../index.php', false, false, 'http://localhost/index.php'),
+ array('./../index.php', true, false, 'http://localhost/index.php'),
+ array('../index.php', false, false, 'http://localhost/index.php'),
+ array('../index.php', true, false, 'http://localhost/index.php'),
+ array('./index.php', false, false, 'http://localhost/phpBB/index.php'),
);
}
+ protected function get_path_helper()
+ {
+ if (!($this->path_helper instanceof \phpbb\path_helper))
+ {
+ $this->path_helper = new \phpbb\path_helper(
+ new \phpbb\symfony_request(
+ new phpbb_mock_request()
+ ),
+ new \phpbb\filesystem(),
+ $this->phpbb_root_path,
+ 'php'
+ );
+ }
+ return $this->path_helper;
+ }
+
+ protected function get_controller_helper()
+ {
+ if (!($this->controller_helper instanceof \phpbb\controller\helper))
+ {
+ global $phpbb_dispatcher;
+
+ $phpbb_dispatcher = new phpbb_mock_event_dispatcher;
+ $this->user = $this->getMock('\phpbb\user');
+ $phpbb_path_helper = new \phpbb\path_helper(
+ new \phpbb\symfony_request(
+ new phpbb_mock_request()
+ ),
+ new \phpbb\filesystem(),
+ $phpbb_root_path,
+ $phpEx
+ );
+ $this->template = new phpbb\template\twig\twig($phpbb_path_helper, $config, $this->user, new \phpbb\template\context());
+
+ // We don't use mod_rewrite in these tests
+ $config = new \phpbb\config\config(array('enable_mod_rewrite' => '0'));
+ $this->controller_helper = new \phpbb\controller\helper($this->template, $this->user, $config, '', 'php');
+ }
+ return $this->controller_helper;
+ }
+
protected function setUp()
{
parent::setUp();
@@ -33,26 +101,41 @@ class phpbb_security_redirect_test extends phpbb_security_test_base
$GLOBALS['config'] = array(
'force_server_vars' => '0',
);
+
+ $this->path_helper = $this->get_path_helper();
+ $this->controller_helper = $this->get_controller_helper();
}
/**
* @dataProvider provider
*/
- public function test_redirect($test, $expected_error, $expected_result)
+ public function test_redirect($test, $disable_cd_check, $expected_error, $expected_result)
{
- global $user;
+ global $user, $phpbb_root_path, $phpbb_path_helper;
+
+ $phpbb_path_helper = $this->path_helper;
+
+ $temp_phpbb_root_path = $phpbb_root_path;
+ $temp_page_dir = $user->page['page_dir'];
+ // We need to hack phpbb_root_path and the user's page_dir here
+ // so it matches the actual fileinfo of the testing script.
+ // Otherwise the paths are returned incorrectly.
+ $phpbb_root_path = '';
+ $user->page['page_dir'] = '';
if ($expected_error !== false)
{
$this->setExpectedTriggerError(E_USER_ERROR, $expected_error);
}
- $result = redirect($test, true);
+ $result = redirect($test, true, $disable_cd_check);
// only verify result if we did not expect an error
if ($expected_error === false)
{
$this->assertEquals($expected_result, $result);
}
+ $phpbb_root_path = $temp_phpbb_root_path;
+ $user->page['page_dir'] = $temp_page_dir;
}
}
diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php
index eba5a2dfdf..71d03746a9 100644
--- a/tests/test_framework/phpbb_functional_test_case.php
+++ b/tests/test_framework/phpbb_functional_test_case.php
@@ -503,6 +503,7 @@ class phpbb_functional_test_case extends phpbb_test_case
set_config(null, null, null, $config);
set_config_count(null, null, null, $config);
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
+ $passwords_manager = $this->get_passwords_manager();
$user_row = array(
'username' => $username,
@@ -512,7 +513,7 @@ class phpbb_functional_test_case extends phpbb_test_case
'user_lang' => 'en',
'user_timezone' => 0,
'user_dateformat' => '',
- 'user_password' => phpbb_hash($username . $username),
+ 'user_password' => $passwords_manager->hash($username . $username),
);
return user_add($user_row);
}
@@ -997,4 +998,29 @@ class phpbb_functional_test_case extends phpbb_test_case
}
return null;
}
+
+ /**
+ * Return a passwords manager instance
+ *
+ * @return phpbb\passwords\manager
+ */
+ public function get_passwords_manager()
+ {
+ // Prepare dependencies for manager and driver
+ $config = new \phpbb\config\config(array());
+ $driver_helper = new \phpbb\passwords\driver\helper($config);
+
+ $passwords_drivers = array(
+ 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $driver_helper),
+ 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $driver_helper),
+ 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $driver_helper),
+ 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $driver_helper),
+ );
+
+ $passwords_helper = new \phpbb\passwords\helper;
+ // Set up passwords manager
+ $manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers));
+
+ return $manager;
+ }
}