aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--phpBB/adm/style/acp_ext_details.html97
-rw-r--r--phpBB/adm/style/acp_ext_disable.html34
-rw-r--r--phpBB/adm/style/acp_ext_enable.html34
-rw-r--r--phpBB/adm/style/acp_ext_list.html61
-rw-r--r--phpBB/adm/style/acp_ext_purge.html34
-rw-r--r--phpBB/adm/style/admin.css10
-rw-r--r--phpBB/config/services.yml1
-rw-r--r--phpBB/includes/acp/acp_extensions.php303
-rw-r--r--phpBB/includes/acp/info/acp_extensions.php34
-rw-r--r--phpBB/includes/extension/exception.php27
-rw-r--r--phpBB/includes/extension/manager.php18
-rw-r--r--phpBB/includes/extension/metadata_manager.php342
-rw-r--r--phpBB/install/database_update.php4
-rw-r--r--phpBB/install/install_install.php6
-rw-r--r--phpBB/install/schemas/schema_data.sql1
-rw-r--r--phpBB/language/en/acp/common.php1
-rw-r--r--phpBB/language/en/acp/extensions.php103
-rw-r--r--phpBB/language/en/acp/permissions_phpbb.php1
-rw-r--r--tests/extension/acp.php205
-rw-r--r--tests/extension/ext/foo/composer.json22
-rw-r--r--tests/extension/ext/vendor/moo/composer.json22
-rw-r--r--tests/extension/manager_test.php2
-rw-r--r--tests/extension/metadata_manager_test.php430
-rw-r--r--tests/test_framework/phpbb_functional_test_case.php55
-rw-r--r--tests/test_framework/phpbb_test_case_helpers.php108
25 files changed, 1949 insertions, 6 deletions
diff --git a/phpBB/adm/style/acp_ext_details.html b/phpBB/adm/style/acp_ext_details.html
new file mode 100644
index 0000000000..e7532691ad
--- /dev/null
+++ b/phpBB/adm/style/acp_ext_details.html
@@ -0,0 +1,97 @@
+<!-- INCLUDE overall_header.html -->
+
+<a name="maincontent"></a>
+
+ <a href="{U_BACK}" style="float: {S_CONTENT_FLOW_END};">&laquo; {L_BACK}</a>
+
+ <h1>{L_EXTENSIONS_ADMIN}</h1>
+
+ <fieldset>
+ <legend>{L_EXT_DETAILS}</legend>
+ <!-- IF META_DISPLAY_NAME -->
+ <dl>
+ <dt><label for="meta_display_name">{L_DISPLAY_NAME}:</label></dt>
+ <dd><strong id="meta_display_name">{META_DISPLAY_NAME}</strong></dd>
+ </dl>
+ <!-- ENDIF -->
+ <dl>
+ <dt><label for="meta_name">{L_CLEAN_NAME}:</label></dt>
+ <dd><strong id="meta_name">{META_NAME}</strong></dd>
+ </dl>
+ <!-- IF META_DESCRIPTION -->
+ <dl>
+ <dt><label for="meta_description">{L_DESCRIPTION}:</label></dt>
+ <dd><p id="meta_description">{META_DESCRIPTION}</p></dd>
+ </dl>
+ <!-- ENDIF -->
+ <dl>
+ <dt><label for="meta_version">{L_VERSION}:</label></dt>
+ <dd><p id="meta_version">{META_VERSION}</p></dd>
+ </dl>
+ <!-- IF META_HOMEPAGE -->
+ <dl>
+ <dt><label for="meta_homepage">{L_HOMEPAGE}:</label></dt>
+ <dd><p id="meta_homepage">{META_HOMEPAGE}</p></dd>
+ </dl>
+ <!-- ENDIF -->
+ <!-- IF META_TIME -->
+ <dl>
+ <dt><label for="meta_time">{L_TIME}:</label></dt>
+ <dd><p id="meta_time">{META_TIME}</p></dd>
+ </dl>
+ <!-- ENDIF -->
+ <dl>
+ <dt><label for="meta_license">{L_LICENCE}:</label></dt>
+ <dd><p id="meta_license">{META_LICENCE}</p></dd>
+ </dl>
+ </fieldset>
+
+ <!-- IF META_REQUIRE_PHPBB || META_REQUIRE_PHP -->
+ <fieldset>
+ <legend>{L_REQUIREMENTS}</legend>
+ <!-- IF META_REQUIRE_PHPBB -->
+ <dl<!-- IF META_REQUIRE_PHPBB_FAIL --> class="requirements_not_met"<!-- ENDIF -->>
+ <dt><label for="require_phpbb">{L_PHPBB_VERSION}:</label></dt>
+ <dd><p id="require_phpbb">{META_REQUIRE_PHPBB}</p></dd>
+ </dl>
+ <!-- ENDIF -->
+ <!-- IF META_REQUIRE_PHP -->
+ <dl<!-- IF META_REQUIRE_PHP_FAIL --> class="requirements_not_met"<!-- ENDIF -->>
+ <dt><label for="require_php">{L_PHP_VERSION}:</label></dt>
+ <dd><p id="require_php">{META_REQUIRE_PHP}</p></dd>
+ </dl>
+ <!-- ENDIF -->
+ </fieldset>
+ <!-- ENDIF -->
+
+ <fieldset>
+ <legend>{L_AUTHOR_INFORMATION}</legend>
+ <!-- BEGIN meta_authors -->
+ <fieldset>
+ <dl>
+ <dt><label for="meta_author_name">{L_AUTHOR_NAME}:</label></dt>
+ <dd><strong id="meta_author_name">{meta_authors.AUTHOR_NAME}</strong></dd>
+ </dl>
+ <!-- IF meta_authors.AUTHOR_EMAIL -->
+ <dl>
+ <dt><label for="meta_author_email">{L_AUTHOR_EMAIL}:</label></dt>
+ <dd><strong id="meta_author_email"><a href="mailto:{meta_authors.AUTHOR_EMAIL}">{meta_authors.AUTHOR_EMAIL}</a></strong></dd>
+ </dl>
+ <!-- ENDIF -->
+ <!-- IF meta_authors.AUTHOR_HOMEPAGE -->
+ <dl>
+ <dt><label for="meta_author_url">{L_AUTHOR_HOMEPAGE}:</label></dt>
+ <dd><strong id="meta_author_url"><a href="{meta_authors.AUTHOR_HOMEPAGE}">{meta_authors.AUTHOR_HOMEPAGE}</a></strong></dd>
+ </dl>
+ <!-- ENDIF -->
+ <!-- IF meta_authors.AUTHOR_ROLE -->
+ <dl>
+ <dt><label for="author_role">{L_AUTHOR_ROLE}:</label></dt>
+ <dd><strong id="meta_author_role">{meta_authors.AUTHOR_ROLE}</strong></dd>
+ </dl>
+ <!-- ENDIF -->
+ </fieldset>
+ <!-- END meta_authors -->
+ </fieldset>
+
+<!-- INCLUDE overall_footer.html -->
diff --git a/phpBB/adm/style/acp_ext_disable.html b/phpBB/adm/style/acp_ext_disable.html
new file mode 100644
index 0000000000..7dc3f6ec97
--- /dev/null
+++ b/phpBB/adm/style/acp_ext_disable.html
@@ -0,0 +1,34 @@
+<!-- INCLUDE overall_header.html -->
+
+ <a name="maincontent"></a>
+
+ <h1>{L_EXTENSIONS_ADMIN}</h1>
+
+ <p>{L_EXTENSIONS_EXPLAIN}</p>
+ <p>{L_DISABLE_EXPLAIN}</p>
+
+ <!-- IF PRE -->
+ <div class="errorbox">
+ <p>{L_DISABLE_CONFIRM}</p>
+ </div>
+
+ <form id="acp_extensions" method="post" action="{U_DISABLE}">
+ <fieldset class="submit-buttons">
+ <legend>{L_DISABLE}</legend>
+ <input class="button1" type="submit" name="disable" value="{L_DISABLE}" />
+ <input class="button2" type="submit" name="cancel" value="{L_CANCEL}" />
+ </fieldset>
+ </form>
+ <!-- ELSEIF S_NEXT_STEP -->
+ <div class="errorbox">
+ <p>{L_DISABLE_IN_PROGRESS}</p>
+ </div>
+ <!-- ELSE -->
+ <div class="successbox">
+ <p>{L_DISABLE_SUCCESS}</p>
+ <br />
+ <p><a href="{U_RETURN}">{L_RETURN}</a></p>
+ </div>
+ <!-- ENDIF -->
+
+<!-- INCLUDE overall_footer.html -->
diff --git a/phpBB/adm/style/acp_ext_enable.html b/phpBB/adm/style/acp_ext_enable.html
new file mode 100644
index 0000000000..3f7be2c847
--- /dev/null
+++ b/phpBB/adm/style/acp_ext_enable.html
@@ -0,0 +1,34 @@
+<!-- INCLUDE overall_header.html -->
+
+ <a name="maincontent"></a>
+
+ <h1>{L_EXTENSIONS_ADMIN}</h1>
+
+ <p>{L_EXTENSIONS_EXPLAIN}</p>
+ <p>{L_ENABLE_EXPLAIN}</p>
+
+ <!-- IF PRE -->
+ <div class="errorbox">
+ <p>{L_ENABLE_CONFIRM}</p>
+ </div>
+
+ <form id="acp_extensions" method="post" action="{U_ENABLE}">
+ <fieldset class="submit-buttons">
+ <legend>{L_ENABLE}</legend>
+ <input class="button1" type="submit" name="enable" value="{L_ENABLE}" />
+ <input class="button2" type="submit" name="cancel" value="{L_CANCEL}" />
+ </fieldset>
+ </form>
+ <!-- ELSEIF S_NEXT_STEP -->
+ <div class="errorbox">
+ <p>{L_ENABLE_IN_PROGRESS}</p>
+ </div>
+ <!-- ELSE -->
+ <div class="successbox">
+ <p>{L_ENABLE_SUCCESS}</p>
+ <br />
+ <p><a href="{U_RETURN}">{L_RETURN}</a></p>
+ </div>
+ <!-- ENDIF -->
+
+<!-- INCLUDE overall_footer.html -->
diff --git a/phpBB/adm/style/acp_ext_list.html b/phpBB/adm/style/acp_ext_list.html
new file mode 100644
index 0000000000..53de0b4d12
--- /dev/null
+++ b/phpBB/adm/style/acp_ext_list.html
@@ -0,0 +1,61 @@
+<!-- INCLUDE overall_header.html -->
+
+<a name="maincontent"></a>
+
+ <h1>{L_EXTENSIONS_ADMIN}</h1>
+
+ <p>{L_EXTENSIONS_EXPLAIN}</p>
+
+ <table cellspacing="1">
+ <col class="row1" ><col class="row2" ><col class="row2" >
+ <thead>
+ <tr>
+ <th>{L_EXTENSION_NAME}</th>
+ <th>{L_EXTENSION_OPTIONS}</th>
+ <th>{L_EXTENSION_ACTIONS}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <!-- IF .enabled -->
+ <tr>
+ <td class="row3" colspan="3">
+ <strong>{L_ENABLED} {L_EXTENSIONS}</strong>
+ </td>
+ </tr>
+ <!-- BEGIN enabled -->
+ <tr class="ext_enabled">
+ <td><strong>{enabled.META_DISPLAY_NAME}</strong></a></td>
+ <td style="text-align: center;"><a href="{enabled.U_DETAILS}">{L_DETAILS}</a></td>
+ <td style="text-align: center;">
+ <!-- BEGIN actions -->
+ <a href="{enabled.actions.U_ACTION}" alt="{enabled.actions.L_ACTION}">{enabled.actions.L_ACTION}</a>
+ <!-- IF not enabled.actions.S_LAST_ROW -->&nbsp;|&nbsp;<!-- ENDIF -->
+ <!-- END actions -->
+ </td>
+ </tr>
+ <!-- END enabled -->
+ <!-- ENDIF -->
+
+ <!-- IF .disabled -->
+ <tr>
+ <td class="row3" colspan="3"><strong>{L_DISABLED} {L_EXTENSIONS}</strong></td>
+ </tr>
+ <!-- BEGIN disabled -->
+ <tr class="ext_disabled">
+ <td><strong>{disabled.META_DISPLAY_NAME}</strong></a></td>
+ <td style="text-align: center;">
+ <!-- IF disabled.U_DETAILS --><a href="{disabled.U_DETAILS}">{L_DETAILS}</a><!-- ENDIF -->
+ </td>
+ <td style="text-align: center;">
+ <!-- BEGIN actions -->
+ <a href="{disabled.actions.U_ACTION}" alt="{disabled.actions.L_ACTION}">{disabled.actions.L_ACTION}</a>
+ <!-- IF not disabled.actions.S_LAST_ROW -->&nbsp;|&nbsp;<!-- ENDIF -->
+ <!-- END actions -->
+ </td>
+ </tr>
+ <!-- END disabled -->
+ <!-- ENDIF -->
+ </tbody>
+ </table>
+
+<!-- INCLUDE overall_footer.html -->
diff --git a/phpBB/adm/style/acp_ext_purge.html b/phpBB/adm/style/acp_ext_purge.html
new file mode 100644
index 0000000000..00a58721cb
--- /dev/null
+++ b/phpBB/adm/style/acp_ext_purge.html
@@ -0,0 +1,34 @@
+<!-- INCLUDE overall_header.html -->
+
+ <a name="maincontent"></a>
+
+ <h1>{L_EXTENSIONS_ADMIN}</h1>
+
+ <p>{L_EXTENSIONS_EXPLAIN}</p>
+ <p>{L_PURGE_EXPLAIN}</p>
+
+ <!-- IF PRE -->
+ <div class="errorbox">
+ <p>{L_PURGE_CONFIRM}</p>
+ </div>
+
+ <form id="acp_extensions" method="post" action="{U_PURGE}">
+ <fieldset class="submit-buttons">
+ <legend>{L_PURGE}</legend>
+ <input class="button1" type="submit" name="purge" value="{L_PURGE}" />
+ <input class="button2" type="submit" name="cancel" value="{L_CANCEL}" />
+ </fieldset>
+ </form>
+ <!-- ELSEIF S_NEXT_STEP -->
+ <div class="errorbox">
+ <p>{L_PURGE_IN_PROGRESS}</p>
+ </div>
+ <!-- ELSE -->
+ <div class="successbox">
+ <p>{L_PURGE_SUCCESS}</p>
+ <br />
+ <p><a href="{U_RETURN}">{L_RETURN}</a></p>
+ </div>
+ <!-- ENDIF -->
+
+<!-- INCLUDE overall_footer.html -->
diff --git a/phpBB/adm/style/admin.css b/phpBB/adm/style/admin.css
index 08613de0dd..585707600d 100644
--- a/phpBB/adm/style/admin.css
+++ b/phpBB/adm/style/admin.css
@@ -1718,3 +1718,13 @@ fieldset.permissions .padding {
.phpinfo td, .phpinfo th, .phpinfo h2, .phpinfo h1 {
text-align: left;
}
+
+.requirements_not_met {
+ padding: 5px;
+ background-color: #BC2A4D;
+}
+
+.requirements_not_met dt label, .requirements_not_met dd p {
+ color: #FFFFFF;
+ font-size: 1.4em;
+} \ No newline at end of file
diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml
index 84de37c28c..133a43b77e 100644
--- a/phpBB/config/services.yml
+++ b/phpBB/config/services.yml
@@ -83,6 +83,7 @@ services:
class: phpbb_extension_manager
arguments:
- @dbal.conn
+ - @config
- %tables.ext%
- %core.root_path%
- .%core.php_ext%
diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php
new file mode 100644
index 0000000000..a0bcf62ecc
--- /dev/null
+++ b/phpBB/includes/acp/acp_extensions.php
@@ -0,0 +1,303 @@
+<?php
+/**
+*
+* @package acp
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* @package acp
+*/
+class acp_extensions
+{
+ var $u_action;
+
+ private $db;
+ private $config;
+ private $template;
+ private $user;
+
+ function main()
+ {
+ // Start the page
+ global $config, $user, $template, $request, $phpbb_extension_manager, $db, $phpbb_root_path, $phpEx;
+
+ $this->db = $db;
+ $this->config = $config;
+ $this->template = $template;
+ $this->user = $user;
+
+ $user->add_lang(array('install', 'acp/extensions'));
+
+ $this->page_title = 'ACP_EXTENSIONS';
+
+ $action = $request->variable('action', 'list');
+ $ext_name = $request->variable('ext_name', '');
+
+ // Cancel action
+ if ($request->is_set_post('cancel'))
+ {
+ $action = 'list';
+ $ext_name = '';
+ }
+
+ // If they've specified an extension, let's load the metadata manager and validate it.
+ if ($ext_name)
+ {
+ $md_manager = new phpbb_extension_metadata_manager($ext_name, $db, $phpbb_extension_manager, $phpbb_root_path, ".$phpEx", $template, $config);
+
+ try
+ {
+ $md_manager->get_metadata('all');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ trigger_error($e);
+ }
+ }
+
+ // What are we doing?
+ switch ($action)
+ {
+ case 'list':
+ default:
+ $this->list_enabled_exts($phpbb_extension_manager);
+ $this->list_disabled_exts($phpbb_extension_manager);
+ $this->list_available_exts($phpbb_extension_manager);
+
+ $this->tpl_name = 'acp_ext_list';
+ break;
+
+ case 'enable_pre':
+ if (!$md_manager->validate_enable())
+ {
+ trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action));
+ }
+
+ if ($phpbb_extension_manager->enabled($ext_name))
+ {
+ redirect($this->u_action);
+ }
+
+ $this->tpl_name = 'acp_ext_enable';
+
+ $template->assign_vars(array(
+ 'PRE' => true,
+ 'U_ENABLE' => $this->u_action . '&amp;action=enable&amp;ext_name=' . urlencode($ext_name),
+ ));
+ break;
+
+ case 'enable':
+ if (!$md_manager->validate_enable())
+ {
+ trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action));
+ }
+
+ if ($phpbb_extension_manager->enable_step($ext_name))
+ {
+ $template->assign_var('S_NEXT_STEP', true);
+
+ meta_refresh(0, $this->u_action . '&amp;action=enable&amp;ext_name=' . urlencode($ext_name));
+ }
+
+ $this->tpl_name = 'acp_ext_enable';
+
+ $template->assign_vars(array(
+ 'U_RETURN' => $this->u_action . '&amp;action=list',
+ ));
+ break;
+
+ case 'disable_pre':
+ if (!$phpbb_extension_manager->enabled($ext_name))
+ {
+ redirect($this->u_action);
+ }
+
+ $this->tpl_name = 'acp_ext_disable';
+
+ $template->assign_vars(array(
+ 'PRE' => true,
+ 'U_DISABLE' => $this->u_action . '&amp;action=disable&amp;ext_name=' . urlencode($ext_name),
+ ));
+ break;
+
+ case 'disable':
+ if ($phpbb_extension_manager->disable_step($ext_name))
+ {
+ $template->assign_var('S_NEXT_STEP', true);
+
+ meta_refresh(0, $this->u_action . '&amp;action=disable&amp;ext_name=' . urlencode($ext_name));
+ }
+
+ $this->tpl_name = 'acp_ext_disable';
+
+ $template->assign_vars(array(
+ 'U_RETURN' => $this->u_action . '&amp;action=list',
+ ));
+ break;
+
+ case 'purge_pre':
+ $this->tpl_name = 'acp_ext_purge';
+
+ $template->assign_vars(array(
+ 'PRE' => true,
+ 'U_PURGE' => $this->u_action . '&amp;action=purge&amp;ext_name=' . urlencode($ext_name),
+ ));
+ break;
+
+ case 'purge':
+ if ($phpbb_extension_manager->purge_step($ext_name))
+ {
+ $template->assign_var('S_NEXT_STEP', true);
+
+ meta_refresh(0, $this->u_action . '&amp;action=purge&amp;ext_name=' . urlencode($ext_name));
+ }
+
+ $this->tpl_name = 'acp_ext_purge';
+
+ $template->assign_vars(array(
+ 'U_RETURN' => $this->u_action . '&amp;action=list',
+ ));
+ break;
+
+ case 'details':
+ // Output it to the template
+ $md_manager->output_template_data();
+
+ $template->assign_var('U_BACK', $this->u_action . '&amp;action=list');
+
+ $this->tpl_name = 'acp_ext_details';
+ break;
+ }
+ }
+
+ /**
+ * Lists all the enabled extensions and dumps to the template
+ *
+ * @param $phpbb_extension_manager An instance of the extension manager
+ * @return null
+ */
+ public function list_enabled_exts(phpbb_extension_manager $phpbb_extension_manager)
+ {
+ foreach ($phpbb_extension_manager->all_enabled() as $name => $location)
+ {
+ $md_manager = $phpbb_extension_manager->create_extension_metadata_manager($name, $this->template);
+
+ try
+ {
+ $this->template->assign_block_vars('enabled', array(
+ 'META_DISPLAY_NAME' => $md_manager->get_metadata('display-name'),
+
+ 'U_DETAILS' => $this->u_action . '&amp;action=details&amp;ext_name=' . urlencode($name),
+ ));
+
+ $this->output_actions('enabled', array(
+ 'DISABLE' => $this->u_action . '&amp;action=disable_pre&amp;ext_name=' . urlencode($name),
+ 'PURGE' => $this->u_action . '&amp;action=purge_pre&amp;ext_name=' . urlencode($name),
+ ));
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->template->assign_block_vars('disabled', array(
+ 'META_DISPLAY_NAME' => $this->user->lang('EXTENSION_INVALID_LIST', $name, $e),
+ ));
+ }
+ }
+ }
+
+ /**
+ * Lists all the disabled extensions and dumps to the template
+ *
+ * @param $phpbb_extension_manager An instance of the extension manager
+ * @return null
+ */
+ public function list_disabled_exts(phpbb_extension_manager $phpbb_extension_manager)
+ {
+ foreach ($phpbb_extension_manager->all_disabled() as $name => $location)
+ {
+ $md_manager = $phpbb_extension_manager->create_extension_metadata_manager($name, $this->template);
+
+ try
+ {
+ $this->template->assign_block_vars('disabled', array(
+ 'META_DISPLAY_NAME' => $md_manager->get_metadata('display-name'),
+
+ 'U_DETAILS' => $this->u_action . '&amp;action=details&amp;ext_name=' . urlencode($name),
+ ));
+
+ $this->output_actions('disabled', array(
+ 'ENABLE' => $this->u_action . '&amp;action=enable_pre&amp;ext_name=' . urlencode($name),
+ 'PURGE' => $this->u_action . '&amp;action=purge_pre&amp;ext_name=' . urlencode($name),
+ ));
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->template->assign_block_vars('disabled', array(
+ 'META_DISPLAY_NAME' => $this->user->lang('EXTENSION_INVALID_LIST', $name, $e),
+ ));
+ }
+ }
+ }
+
+ /**
+ * Lists all the available extensions and dumps to the template
+ *
+ * @param $phpbb_extension_manager An instance of the extension manager
+ * @return null
+ */
+ public function list_available_exts(phpbb_extension_manager $phpbb_extension_manager)
+ {
+ $uninstalled = array_diff_key($phpbb_extension_manager->all_available(), $phpbb_extension_manager->all_configured());
+
+ foreach ($uninstalled as $name => $location)
+ {
+ $md_manager = $phpbb_extension_manager->create_extension_metadata_manager($name, $this->template);
+
+ try
+ {
+ $this->template->assign_block_vars('disabled', array(
+ 'META_DISPLAY_NAME' => $md_manager->get_metadata('display-name'),
+
+ 'U_DETAILS' => $this->u_action . '&amp;action=details&amp;ext_name=' . urlencode($name),
+ ));
+
+ $this->output_actions('disabled', array(
+ 'ENABLE' => $this->u_action . '&amp;action=enable_pre&amp;ext_name=' . urlencode($name),
+ ));
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->template->assign_block_vars('disabled', array(
+ 'META_DISPLAY_NAME' => $this->user->lang('EXTENSION_INVALID_LIST', $name, $e),
+ ));
+ }
+ }
+ }
+
+ /**
+ * Output actions to a block
+ *
+ * @param string $block
+ * @param array $actions
+ */
+ private function output_actions($block, $actions)
+ {
+ foreach ($actions as $lang => $url)
+ {
+ $this->template->assign_block_vars($block . '.actions', array(
+ 'L_ACTION' => $this->user->lang($lang),
+ 'U_ACTION' => $url,
+ ));
+ }
+ }
+}
diff --git a/phpBB/includes/acp/info/acp_extensions.php b/phpBB/includes/acp/info/acp_extensions.php
new file mode 100644
index 0000000000..f5953fb1dd
--- /dev/null
+++ b/phpBB/includes/acp/info/acp_extensions.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* @package acp
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @package module_install
+*/
+class acp_extensions_info
+{
+ function module()
+ {
+ return array(
+ 'filename' => 'acp_extensions',
+ 'title' => 'ACP_EXTENSIONS',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_GENERAL_TASKS')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
diff --git a/phpBB/includes/extension/exception.php b/phpBB/includes/extension/exception.php
new file mode 100644
index 0000000000..e08a8912ea
--- /dev/null
+++ b/phpBB/includes/extension/exception.php
@@ -0,0 +1,27 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+ * Exception class for metadata
+ */
+class phpbb_extension_exception extends UnexpectedValueException
+{
+ public function __toString()
+ {
+ return $this->getMessage();
+ }
+} \ No newline at end of file
diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php
index 86d8fab64b..9a518c215f 100644
--- a/phpBB/includes/extension/manager.php
+++ b/phpBB/includes/extension/manager.php
@@ -22,6 +22,8 @@ if (!defined('IN_PHPBB'))
*/
class phpbb_extension_manager
{
+ protected $db;
+ protected $config;
protected $cache;
protected $php_ext;
protected $extensions;
@@ -33,16 +35,18 @@ class phpbb_extension_manager
* Creates a manager and loads information from database
*
* @param dbal $db A database connection
+ * @param phpbb_config $config phpbb_config
* @param string $extension_table The name of the table holding extensions
* @param string $phpbb_root_path Path to the phpbb includes directory.
* @param string $php_ext php file extension
* @param phpbb_cache_driver_interface $cache A cache instance or null
* @param string $cache_name The name of the cache variable, defaults to _ext
*/
- public function __construct(dbal $db, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
+ public function __construct(dbal $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
{
$this->phpbb_root_path = $phpbb_root_path;
$this->db = $db;
+ $this->config = $config;
$this->cache = $cache;
$this->php_ext = $php_ext;
$this->extension_table = $extension_table;
@@ -121,6 +125,18 @@ class phpbb_extension_manager
}
/**
+ * Instantiates the metadata manager for the extension with the given name
+ *
+ * @param string $name The extension name
+ * @param string $template The template manager
+ * @return phpbb_extension_metadata_manager Instance of the metadata manager
+ */
+ public function create_extension_metadata_manager($name, phpbb_template $template)
+ {
+ return new phpbb_extension_metadata_manager($name, $this->db, $this, $this->phpbb_root_path, $this->php_ext, $template, $this->config);
+ }
+
+ /**
* Runs a step of the extension enabling process.
*
* Allows the exentension to enable in a long running script that works
diff --git a/phpBB/includes/extension/metadata_manager.php b/phpBB/includes/extension/metadata_manager.php
new file mode 100644
index 0000000000..8c570830fe
--- /dev/null
+++ b/phpBB/includes/extension/metadata_manager.php
@@ -0,0 +1,342 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* The extension metadata manager validates and gets meta-data for extensions
+*
+* @package extension
+*/
+class phpbb_extension_metadata_manager
+{
+ protected $phpEx;
+ protected $extension_manager;
+ protected $db;
+ protected $phpbb_root_path;
+ protected $template;
+ protected $ext_name;
+ protected $metadata;
+ protected $metadata_file;
+
+ /**
+ * Creates the metadata manager
+ *
+ * @param dbal $db A database connection
+ * @param string $extension_manager An instance of the phpbb extension manager
+ * @param string $phpbb_root_path Path to the phpbb includes directory.
+ * @param string $phpEx php file extension
+ */
+ public function __construct($ext_name, dbal $db, phpbb_extension_manager $extension_manager, $phpbb_root_path, $phpEx = '.php', phpbb_template $template, phpbb_config $config)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->db = $db;
+ $this->config = $config;
+ $this->phpEx = $phpEx;
+ $this->template = $template;
+ $this->extension_manager = $extension_manager;
+ $this->ext_name = $ext_name;
+ $this->metadata = array();
+ $this->metadata_file = '';
+ }
+
+ /**
+ * Processes and gets the metadata requested
+ *
+ * @param string $element All for all metadata that it has and is valid, otherwise specify which section you want by its shorthand term.
+ * @return array Contains all of the requested metadata, throws an exception on failure
+ */
+ public function get_metadata($element = 'all')
+ {
+ $this->set_metadata_file();
+
+ // Fetch the metadata
+ $this->fetch_metadata();
+
+ // Clean the metadata
+ $this->clean_metadata_array();
+
+ switch ($element)
+ {
+ case 'all':
+ default:
+ // Validate the metadata
+ if (!$this->validate())
+ {
+ return false;
+ }
+
+ return $this->metadata;
+ break;
+
+ case 'name':
+ return ($this->validate('name')) ? $this->metadata['name'] : false;
+ break;
+
+ case 'display-name':
+ if (isset($this->metadata['extra']['display-name']))
+ {
+ return $this->metadata['extra']['display-name'];
+ }
+ else
+ {
+ return ($this->validate('name')) ? $this->metadata['name'] : false;
+ }
+ break;
+ // TODO: Add remaining cases as needed
+ }
+ }
+
+ /**
+ * Sets the filepath of the metadata file
+ *
+ * @return boolean Set to true if it exists, throws an exception on failure
+ */
+ private function set_metadata_file()
+ {
+ $ext_filepath = $this->extension_manager->get_extension_path($this->ext_name);
+ $metadata_filepath = $this->phpbb_root_path . $ext_filepath . 'composer.json';
+
+ $this->metadata_file = $metadata_filepath;
+
+ if (!file_exists($this->metadata_file))
+ {
+ throw new phpbb_extension_exception('The required file does not exist: ' . $this->metadata_file);
+ }
+ }
+
+ /**
+ * Gets the contents of the composer.json file
+ *
+ * @return bool True if success, throws an exception on failure
+ */
+ private function fetch_metadata()
+ {
+ if (!file_exists($this->metadata_file))
+ {
+ throw new phpbb_extension_exception('The required file does not exist: ' . $this->metadata_file);
+ }
+ else
+ {
+ if (!($file_contents = file_get_contents($this->metadata_file)))
+ {
+ throw new phpbb_extension_exception('file_get_contents failed on ' . $this->metadata_file);
+ }
+
+ if (($metadata = json_decode($file_contents, true)) === NULL)
+ {
+ throw new phpbb_extension_exception('json_decode failed on ' . $this->metadata_file);
+ }
+
+ $this->metadata = $metadata;
+
+ return true;
+ }
+ }
+
+ /**
+ * This array handles the cleaning of the array
+ *
+ * @return array Contains the cleaned metadata array
+ */
+ private function clean_metadata_array()
+ {
+// TODO: Remove all parts of the array we don't want or shouldn't be there due to nub mod authors
+// $this->metadata = $metadata_finished;
+
+ return $this->metadata;
+ }
+
+ /**
+ * Validate fields
+ *
+ * @param string $name ("all" for display and enable validation
+ * "display" for name, type, and authors
+ * "name", "type")
+ * @return Bool True if valid, throws an exception if invalid
+ */
+ public function validate($name = 'display')
+ {
+ // Basic fields
+ $fields = array(
+ 'name' => '#^[a-zA-Z0-9_\x7f-\xff]{2,}/[a-zA-Z0-9_\x7f-\xff]{2,}$#',
+ 'type' => '#^phpbb3-extension$#',
+ 'licence' => '#.+#',
+ 'version' => '#.+#',
+ );
+
+ switch ($name)
+ {
+ case 'all':
+ $this->validate('display');
+
+ $this->validate_enable();
+ break;
+
+ case 'display':
+ foreach ($fields as $field => $data)
+ {
+ $this->validate($field);
+ }
+
+ $this->validate_authors();
+ break;
+
+ default:
+ if (isset($fields[$name]))
+ {
+ if (!isset($this->metadata[$name]))
+ {
+ throw new phpbb_extension_exception("Required meta field '$name' has not been set.");
+ }
+
+ if (!preg_match($fields[$name], $this->metadata[$name]))
+ {
+ throw new phpbb_extension_exception("Meta field '$name' is invalid.");
+ }
+ }
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validates the contents of the authors field
+ *
+ * @return boolean True when passes validation, throws exception if invalid
+ */
+ public function validate_authors()
+ {
+ if (empty($this->metadata['authors']))
+ {
+ throw new phpbb_extension_exception("Required meta field 'authors' has not been set.");
+ }
+
+ foreach ($this->metadata['authors'] as $author)
+ {
+ if (!isset($author['name']))
+ {
+ throw new phpbb_extension_exception("Required meta field 'author name' has not been set.");
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * This array handles the verification that this extension can be enabled on this board
+ *
+ * @return bool True if validation succeeded, False if failed
+ */
+ public function validate_enable()
+ {
+ // Check for phpBB, PHP versions
+ if (!$this->validate_require_phpbb() || !$this->validate_require_php())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Validates the contents of the phpbb requirement field
+ *
+ * @return boolean True when passes validation
+ */
+ public function validate_require_phpbb()
+ {
+ if (!isset($this->metadata['require']['phpbb']))
+ {
+ return true;
+ }
+
+ return $this->_validate_version($this->metadata['require']['phpbb'], $this->config['version']);
+ }
+
+ /**
+ * Validates the contents of the php requirement field
+ *
+ * @return boolean True when passes validation
+ */
+ public function validate_require_php()
+ {
+ if (!isset($this->metadata['require']['php']))
+ {
+ return true;
+ }
+
+ return $this->_validate_version($this->metadata['require']['php'], phpversion());
+ }
+
+ /**
+ * Version validation helper
+ *
+ * @param string $string The string for comparing to a version
+ * @param string $current_version The version to compare to
+ * @return bool True/False if meets version requirements
+ */
+ private function _validate_version($string, $current_version)
+ {
+ // Allow them to specify their own comparison operator (ex: <3.1.2, >=3.1.0)
+ $comparison_matches = false;
+ preg_match('#[=<>]+#', $string, $comparison_matches);
+
+ if (!empty($comparison_matches))
+ {
+ return version_compare($current_version, str_replace(array($comparison_matches[0], ' '), '', $string), $comparison_matches[0]);
+ }
+
+ return version_compare($current_version, $string, '>=');
+ }
+
+ /**
+ * Outputs the metadata into the template
+ *
+ * @return null
+ */
+ public function output_template_data()
+ {
+ $this->template->assign_vars(array(
+ 'META_NAME' => htmlspecialchars($this->metadata['name']),
+ 'META_TYPE' => htmlspecialchars($this->metadata['type']),
+ 'META_DESCRIPTION' => (isset($this->metadata['description'])) ? htmlspecialchars($this->metadata['description']) : '',
+ 'META_HOMEPAGE' => (isset($this->metadata['homepage'])) ? $this->metadata['homepage'] : '',
+ 'META_VERSION' => (isset($this->metadata['version'])) ? htmlspecialchars($this->metadata['version']) : '',
+ 'META_TIME' => (isset($this->metadata['time'])) ? htmlspecialchars($this->metadata['time']) : '',
+ 'META_LICENCE' => htmlspecialchars($this->metadata['licence']),
+
+ 'META_REQUIRE_PHP' => (isset($this->metadata['require']['php'])) ? htmlspecialchars($this->metadata['require']['php']) : '',
+ 'META_REQUIRE_PHP_FAIL' => !$this->validate_require_php(),
+
+ 'META_REQUIRE_PHPBB' => (isset($this->metadata['require']['phpbb'])) ? htmlspecialchars($this->metadata['require']['phpbb']) : '',
+ 'META_REQUIRE_PHPBB_FAIL' => !$this->validate_require_phpbb(),
+
+ 'META_DISPLAY_NAME' => (isset($this->metadata['extra']['display-name'])) ? htmlspecialchars($this->metadata['extra']['display-name']) : '',
+ ));
+
+ foreach ($this->metadata['authors'] as $author)
+ {
+ $this->template->assign_block_vars('meta_authors', array(
+ 'AUTHOR_NAME' => htmlspecialchars($author['name']),
+ 'AUTHOR_EMAIL' => (isset($author['email'])) ? $author['email'] : '',
+ 'AUTHOR_HOMEPAGE' => (isset($author['homepage'])) ? $author['homepage'] : '',
+ 'AUTHOR_ROLE' => (isset($author['role'])) ? htmlspecialchars($author['role']) : '',
+ ));
+ }
+ }
+}
diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php
index 502b3bb1a4..0b470ced26 100644
--- a/phpBB/install/database_update.php
+++ b/phpBB/install/database_update.php
@@ -683,12 +683,12 @@ function _write_result($no_updates, $errored, $error_ary)
function _add_modules($modules_to_install)
{
- global $phpbb_root_path, $phpEx, $db, $phpbb_extension_manager;
+ global $phpbb_root_path, $phpEx, $db, $phpbb_extension_manager, $config;
// modules require an extension manager
if (empty($phpbb_extension_manager))
{
- $phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx");
+ $phpbb_extension_manager = new phpbb_extension_manager($db, $config, EXT_TABLE, $phpbb_root_path, ".$phpEx");
}
include_once($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php
index 23593ee51f..9162d5ab60 100644
--- a/phpBB/install/install_install.php
+++ b/phpBB/install/install_install.php
@@ -250,7 +250,7 @@ class install_install extends module
'S_EXPLAIN' => true,
'S_LEGEND' => false,
));
-
+
// Check for php json support
if (@extension_loaded('json'))
{
@@ -1481,12 +1481,12 @@ class install_install extends module
*/
function add_modules($mode, $sub)
{
- global $db, $lang, $phpbb_root_path, $phpEx, $phpbb_extension_manager;
+ global $db, $lang, $phpbb_root_path, $phpEx, $phpbb_extension_manager, $config;
// modules require an extension manager
if (empty($phpbb_extension_manager))
{
- $phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx");
+ $phpbb_extension_manager = new phpbb_extension_manager($db, $config, EXT_TABLE, $phpbb_root_path, ".$phpEx");
}
include_once($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql
index ea7864bd4c..ae7b9fcba3 100644
--- a/phpBB/install/schemas/schema_data.sql
+++ b/phpBB/install/schemas/schema_data.sql
@@ -345,6 +345,7 @@ INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_board', 1);
INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_bots', 1);
INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_clearlogs', 1);
INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_email', 1);
+INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_extensions', 1);
INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_fauth', 1);
INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_forum', 1);
INSERT INTO phpbb_acl_options (auth_option, is_global) VALUES ('a_forumadd', 1);
diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php
index 04df897dba..5eb10d50b3 100644
--- a/phpBB/language/en/acp/common.php
+++ b/phpBB/language/en/acp/common.php
@@ -81,6 +81,7 @@ $lang = array_merge($lang, array(
'ACP_EMAIL_SETTINGS' => 'Email settings',
'ACP_EXTENSION_GROUPS' => 'Manage extension groups',
+ 'ACP_EXTENSIONS' => 'Manage board extensions',
'ACP_FORUM_BASED_PERMISSIONS' => 'Forum based permissions',
'ACP_FORUM_LOGS' => 'Forum logs',
diff --git a/phpBB/language/en/acp/extensions.php b/phpBB/language/en/acp/extensions.php
new file mode 100644
index 0000000000..0adaff10c8
--- /dev/null
+++ b/phpBB/language/en/acp/extensions.php
@@ -0,0 +1,103 @@
+<?php
+/**
+*
+* acp_extensions [English]
+*
+* @package language
+* @copyright (c) 2012 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
+*
+*/
+/**
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* DO NOT CHANGE
+*/
+if (empty($lang) || !is_array($lang))
+{
+ $lang = array();
+}
+
+// DEVELOPERS PLEASE NOTE
+//
+// Placeholders can now contain order information, e.g. instead of
+// 'Page %s of %s' you can (and should) write 'Page %1$s of %2$s', this allows
+// translators to re-order the output of data while ensuring it remains correct
+//
+// You do not need this where single placeholders are used, e.g. 'Message %d' is fine
+// equally where a string contains only two placeholders which are used to wrap text
+// in a url you again do not need to specify an order e.g., 'Click %sHERE%s' is fine
+
+
+$lang = array_merge($lang, array(
+ 'EXTENSION' => 'Extension',
+ 'EXTENSIONS' => 'Extensions',
+ 'EXTENSIONS_ADMIN' => 'Extensions Manager',
+ 'EXTENSIONS_EXPLAIN' => 'The Extensions Manager is a tool in your phpBB Board which allows you to manage all of your extensions statuses and view information about them.',
+ 'EXTENSION_INVALID_LIST' => 'The "%s" extension is not valid.<br /><p>%s</p>',
+ 'EXTENSION_NOT_AVAILABLE' => 'The selected extension is not available for this board, please verify your phpBB and PHP versions are allowed (see the details page).',
+
+ 'DETAILS' => 'Details',
+
+ 'AVAILABLE' => 'Available',
+ 'ENABLED' => 'Enabled',
+ 'DISABLED' => 'Disabled',
+ 'PURGED' => 'Purged',
+ 'UPLOADED' => 'Uploaded',
+
+ 'ENABLE' => 'Enable',
+ 'DISABLE' => 'Disable',
+ 'PURGE' => 'Purge',
+
+ 'ENABLE_EXPLAIN' => 'Enabling an extension allows you to use it on your board.',
+ 'DISABLE_EXPLAIN' => 'Disabling an extension retains its files and settings but removes any functionality added by the extension.',
+ 'PURGE_EXPLAIN' => 'Purging an extension clears an extensions data while retaining its files.',
+ 'DELETE_EXPLAIN' => 'Deleting an extension removes all of its files and settings. Log entries will remain, although any language variables added by the extension will not be available.',
+
+ 'DISABLE_IN_PROGRESS' => 'The extension is currently being disabled, please do not leave this page or refresh until it is completed.',
+ 'ENABLE_IN_PROGRESS' => 'The extension is currently being installed, please do not leave this page or refresh until it is completed.',
+ 'PURGE_IN_PROGRESS' => 'The extension is currently being purged, please do not leave this page or refresh until it is completed.',
+ 'ENABLE_SUCCESS' => 'The extension was enabled successfully',
+ 'DISABLE_SUCCESS' => 'The extension was disabled successfully',
+ 'PURGE_SUCCESS' => 'The extension was purged successfully',
+
+ 'ENABLE_FAIL' => 'The extension could not be enabled',
+ 'DISABLE_FAIL' => 'The extension could not be disabled',
+ 'PURGE_FAIL' => 'The extension could not be purged',
+
+ 'EXTENSION_NAME' => 'Extension Name',
+ 'EXTENSION_ACTIONS' => 'Actions',
+ 'EXTENSION_OPTIONS' => 'Options',
+
+ 'ENABLE_CONFIRM' => 'Are you sure that you wish to enable this extension?',
+ 'DISABLE_CONFIRM' => 'Are you sure that you wish to disable this extension?',
+ 'PURGE_CONFIRM' => 'Are you sure that you wish to purge this extension&#39;s data? This will remove all settings stored for this extension and cannot be undone!',
+
+ 'WARNING' => 'Warning',
+ 'RETURN' => 'Return',
+
+ 'EXT_DETAILS' => 'Extension Details',
+ 'DISPLAY_NAME' => 'Display Name',
+ 'CLEAN_NAME' => 'Clean Name',
+ 'TYPE' => 'Type',
+ 'DESCRIPTION' => 'Description',
+ 'VERSION' => 'Version',
+ 'HOMEPAGE' => 'Homepage',
+ 'PATH' => 'File Path',
+ 'TIME' => 'Release Time',
+ 'LICENCE' => 'Licence',
+
+ 'REQUIREMENTS' => 'Requirements',
+ 'PHPBB_VERSION' => 'phpBB Version',
+ 'PHP_VERSION' => 'PHP Version',
+ 'AUTHOR_INFORMATION' => 'Author Information',
+ 'AUTHOR_NAME' => 'Name',
+ 'AUTHOR_EMAIL' => 'Email',
+ 'AUTHOR_HOMEPAGE' => 'Homepage',
+ 'AUTHOR_ROLE' => 'Role',
+));
diff --git a/phpBB/language/en/acp/permissions_phpbb.php b/phpBB/language/en/acp/permissions_phpbb.php
index 17649693fa..bb068e625f 100644
--- a/phpBB/language/en/acp/permissions_phpbb.php
+++ b/phpBB/language/en/acp/permissions_phpbb.php
@@ -226,6 +226,7 @@ $lang = array_merge($lang, array(
'acl_a_switchperm' => array('lang' => 'Can use others permissions', 'cat' => 'permissions'),
'acl_a_styles' => array('lang' => 'Can manage styles', 'cat' => 'misc'),
+ 'acl_a_extensions' => array('lang' => 'Can manage extensions', 'cat' => 'misc'),
'acl_a_viewlogs' => array('lang' => 'Can view logs', 'cat' => 'misc'),
'acl_a_clearlogs' => array('lang' => 'Can clear logs', 'cat' => 'misc'),
'acl_a_modules' => array('lang' => 'Can manage modules', 'cat' => 'misc'),
diff --git a/tests/extension/acp.php b/tests/extension/acp.php
new file mode 100644
index 0000000000..790df77c0d
--- /dev/null
+++ b/tests/extension/acp.php
@@ -0,0 +1,205 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class acp_test extends phpbb_functional_test_case
+{
+ static private $copied_files = array();
+ static private $helper;
+
+ /**
+ * This should only be called once before the tests are run.
+ * This is used to copy the extensions to the phpBB install
+ */
+ static public function setUpBeforeClass()
+ {
+ global $phpbb_root_path;
+
+ parent::setUpBeforeClass();
+
+ self::$helper = new phpbb_test_case_helpers(self);
+
+ // First, move any extensions setup on the board to a temp directory
+ self::$copied_files = self::$helper->copy_dir($phpbb_root_path . 'ext/', $phpbb_root_path . 'store/temp_ext/');
+
+ // Then empty the ext/ directory on the board (for accurate test cases)
+ self::$helper->empty_dir($phpbb_root_path . 'ext/');
+
+ // Copy our ext/ files from the test case to the board
+ self::$copied_files = array_merge(self::$copied_files, self::$helper->copy_dir(dirname(__FILE__) . '/ext/', $phpbb_root_path . 'ext/'));
+ }
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->get_db();
+
+ // Clear the phpbb_ext table
+ $this->db->sql_query('DELETE FROM phpbb_ext');
+
+ // Insert our base data
+ $insert_rows = array(
+ array(
+ 'ext_name' => 'foo',
+ 'ext_active' => true,
+ 'ext_state' => 'b:0;',
+ ),
+ array(
+ 'ext_name' => 'vendor/moo',
+ 'ext_active' => false,
+ 'ext_state' => 'b:0;',
+ ),
+
+ // do not exist
+ array(
+ 'ext_name' => 'test2',
+ 'ext_active' => true,
+ 'ext_state' => 'b:0;',
+ ),
+ array(
+ 'ext_name' => 'test3',
+ 'ext_active' => false,
+ 'ext_state' => 'b:0;',
+ ),
+ );
+ $this->db->sql_multi_insert('phpbb_ext', $insert_rows);
+
+ $this->login();
+ $this->admin_login();
+
+ $this->add_lang('acp/extensions');
+ }
+
+ /**
+ * This should only be called once after the tests are run.
+ * This is used to remove the files copied to the phpBB install
+ */
+ static public function tearDownAfterClass()
+ {
+ global $phpbb_root_path;
+
+ // Copy back the board installed extensions from the temp directory
+ self::$helper->copy_dir($phpbb_root_path . 'store/temp_ext/', $phpbb_root_path . 'ext/');
+
+ self::$copied_files[] = $phpbb_root_path . 'store/temp_ext/';
+
+ // Remove all of the files we copied around (from board ext -> temp_ext, from test ext -> board ext)
+ self::$helper->remove_files(self::$copied_files);
+ }
+
+ public function test_list()
+ {
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&sid=' . $this->sid);
+
+ $this->assertCount(1, $crawler->filter('.ext_enabled'));
+ $this->assertCount(4, $crawler->filter('.ext_disabled'));
+
+ $this->assertContains('phpBB Foo Extension', $crawler->filter('.ext_enabled')->eq(0)->text());
+ $this->assertContainsLang('PURGE', $crawler->filter('.ext_enabled')->eq(0)->text());
+
+ $this->assertContains('The "test2" extension is not valid.', $crawler->filter('.ext_disabled')->eq(0)->text());
+
+ $this->assertContains('The "test3" extension is not valid.', $crawler->filter('.ext_disabled')->eq(1)->text());
+
+ $this->assertContains('phpBB Moo Extension', $crawler->filter('.ext_disabled')->eq(2)->text());
+ $this->assertContainsLang('DETAILS', $crawler->filter('.ext_disabled')->eq(2)->text());
+ $this->assertContainsLang('ENABLE', $crawler->filter('.ext_disabled')->eq(2)->text());
+ $this->assertContainsLang('PURGE', $crawler->filter('.ext_disabled')->eq(2)->text());
+
+ $this->assertContains('The "bar" extension is not valid.', $crawler->filter('.ext_disabled')->eq(3)->text());
+ }
+
+ public function test_details()
+ {
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=details&ext_name=foo&sid=' . $this->sid);
+
+ $validation = array(
+ 'DISPLAY_NAME' => 'phpBB Foo Extension',
+ 'CLEAN_NAME' => 'foo/example',
+ 'DESCRIPTION' => 'An example/sample extension to be used for testing purposes in phpBB Development.',
+ 'VERSION' => '1.0.0',
+ 'TIME' => '2012-02-15 01:01:01',
+ 'LICENCE' => 'GPL-2.0',
+ 'PHPBB_VERSION' => '3.1.0-dev',
+ 'PHP_VERSION' => '>=5.3',
+ 'AUTHOR_NAME' => 'Nathan Guse',
+ 'AUTHOR_EMAIL' => 'email@phpbb.com',
+ 'AUTHOR_HOMEPAGE' => 'http://lithiumstudios.org',
+ 'AUTHOR_ROLE' => 'N/A',
+ );
+
+ for ($i = 0; $i < $crawler->filter('dl')->count(); $i++)
+ {
+ $text = $crawler->filter('dl')->eq($i)->text();
+
+ $match = false;
+
+ foreach ($validation as $language_key => $expected)
+ {
+ if (strpos($text, $this->lang($language_key)) === 0)
+ {
+ $match = true;
+
+ $this->assertContains($expected, $text);
+ }
+ }
+
+ if (!$match)
+ {
+ $this->fail('Unexpected field: "' . $text . '"');
+ }
+ }
+ }
+
+ public function test_enable_pre()
+ {
+ // Foo is already enabled (redirect to list)
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=enable_pre&ext_name=foo&sid=' . $this->sid);
+ $this->assertContainsLang('EXTENSION_NAME', $crawler->filter('html')->text());
+ $this->assertContainsLang('EXTENSION_OPTIONS', $crawler->filter('html')->text());
+ $this->assertContainsLang('EXTENSION_ACTIONS', $crawler->filter('html')->text());
+
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=enable_pre&ext_name=vendor%2Fmoo&sid=' . $this->sid);
+ $this->assertContainsLang('ENABLE_CONFIRM', $crawler->filter('html')->text());
+ }
+
+ public function test_disable_pre()
+ {
+ // Moo is not enabled (redirect to list)
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=disable_pre&ext_name=vendor%2Fmoo&sid=' . $this->sid);
+ $this->assertContainsLang('EXTENSION_NAME', $crawler->filter('html')->text());
+ $this->assertContainsLang('EXTENSION_OPTIONS', $crawler->filter('html')->text());
+ $this->assertContainsLang('EXTENSION_ACTIONS', $crawler->filter('html')->text());
+
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=disable_pre&ext_name=foo&sid=' . $this->sid);
+ $this->assertContainsLang('DISABLE_CONFIRM', $crawler->filter('html')->text());
+ }
+
+ public function test_purge_pre()
+ {
+ // test2 is not available (error)
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=purge_pre&ext_name=test2&sid=' . $this->sid);
+ $this->assertContains('The required file does not exist', $crawler->filter('html')->text());
+
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=purge_pre&ext_name=foo&sid=' . $this->sid);
+ $this->assertContainsLang('PURGE_CONFIRM', $crawler->filter('html')->text());
+ }
+
+ public function test_actions()
+ {
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=enable&ext_name=vendor%2Fmoo&sid=' . $this->sid);
+ $this->assertContainsLang('ENABLE_SUCCESS', $crawler->filter('html')->text());
+
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=disable&ext_name=vendor%2Fmoo&sid=' . $this->sid);
+ $this->assertContainsLang('DISABLE_SUCCESS', $crawler->filter('html')->text());
+
+ $crawler = $this->request('GET', 'adm/index.php?i=acp_extensions&mode=main&action=purge&ext_name=vendor%2Fmoo&sid=' . $this->sid);
+ $this->assertContainsLang('PURGE_SUCCESS', $crawler->filter('html')->text());
+ }
+} \ No newline at end of file
diff --git a/tests/extension/ext/foo/composer.json b/tests/extension/ext/foo/composer.json
new file mode 100644
index 0000000000..744f7be625
--- /dev/null
+++ b/tests/extension/ext/foo/composer.json
@@ -0,0 +1,22 @@
+{
+ "name": "foo/example",
+ "type": "phpbb3-extension",
+ "description": "An example/sample extension to be used for testing purposes in phpBB Development.",
+ "version": "1.0.0",
+ "time": "2012-02-15 01:01:01",
+ "licence": "GPL-2.0",
+ "authors": [{
+ "name": "Nathan Guse",
+ "username": "EXreaction",
+ "email": "email@phpbb.com",
+ "homepage": "http://lithiumstudios.org",
+ "role": "N/A"
+ }],
+ "require": {
+ "php": ">=5.3",
+ "phpbb": "3.1.0-dev"
+ },
+ "extra": {
+ "display-name": "phpBB Foo Extension"
+ }
+}
diff --git a/tests/extension/ext/vendor/moo/composer.json b/tests/extension/ext/vendor/moo/composer.json
new file mode 100644
index 0000000000..c91a5e027b
--- /dev/null
+++ b/tests/extension/ext/vendor/moo/composer.json
@@ -0,0 +1,22 @@
+{
+ "name": "moo/example",
+ "type": "phpbb3-extension",
+ "description": "An example/sample extension to be used for testing purposes in phpBB Development.",
+ "version": "1.0.0",
+ "time": "2012-02-15 01:01:01",
+ "licence": "GNU GPL v2",
+ "authors": [{
+ "name": "Nathan Guse",
+ "username": "EXreaction",
+ "email": "email@phpbb.com",
+ "homepage": "http://lithiumstudios.org",
+ "role": "N/A"
+ }],
+ "require": {
+ "php": ">=5.3",
+ "phpbb": "3.1.0-dev"
+ },
+ "extra": {
+ "display-name": "phpBB Moo Extension"
+ }
+}
diff --git a/tests/extension/manager_test.php b/tests/extension/manager_test.php
index 45bed247ae..5cde5bccdb 100644
--- a/tests/extension/manager_test.php
+++ b/tests/extension/manager_test.php
@@ -27,6 +27,7 @@ class phpbb_extension_manager_test extends phpbb_database_test_case
$this->extension_manager = new phpbb_extension_manager(
$this->new_dbal(),
+ new phpbb_config(array()),
'phpbb_ext',
dirname(__FILE__) . '/',
'.php',
@@ -90,6 +91,7 @@ class phpbb_extension_manager_test extends phpbb_database_test_case
{
$extension_manager = new phpbb_extension_manager(
$this->new_dbal(),
+ new phpbb_config(array()),
'phpbb_ext',
dirname(__FILE__) . '/',
'.php'
diff --git a/tests/extension/metadata_manager_test.php b/tests/extension/metadata_manager_test.php
new file mode 100644
index 0000000000..9865f14314
--- /dev/null
+++ b/tests/extension/metadata_manager_test.php
@@ -0,0 +1,430 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+*
+*/
+
+class metadata_manager_test extends phpbb_database_test_case
+{
+ protected $class_loader;
+ protected $extension_manager;
+
+ protected $cache;
+ protected $config;
+ protected $db;
+ protected $phpbb_root_path;
+ protected $phpEx;
+ protected $template;
+ protected $user;
+
+ public function getDataSet()
+ {
+ return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/extensions.xml');
+ }
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->cache = new phpbb_mock_cache();
+ $this->config = new phpbb_config(array(
+ 'version' => '3.1.0',
+ ));
+ $this->db = $this->new_dbal();
+ $this->phpbb_root_path = dirname(__FILE__) . '/';
+ $this->phpEx = '.php';
+ $this->user = new phpbb_user();
+
+ $this->template = new phpbb_template(
+ $this->phpbb_root_path,
+ $this->phpEx,
+ $this->config,
+ $this->user,
+ new phpbb_style_resource_locator()
+ );
+
+ $this->extension_manager = new phpbb_extension_manager(
+ $this->db,
+ $this->config,
+ 'phpbb_ext',
+ $this->phpbb_root_path,
+ $this->phpEx,
+ $this->cache
+ );
+ }
+
+ // Should fail from missing composer.json
+ public function test_bar()
+ {
+ $ext_name = 'bar';
+
+ $manager = $this->get_metadata_manager($ext_name);
+
+ try
+ {
+ $manager->get_metadata();
+ }
+ catch(phpbb_extension_exception $e){}
+
+ $this->assertEquals((string) $e, 'The required file does not exist: ' . $this->phpbb_root_path . $this->extension_manager->get_extension_path($ext_name) . 'composer.json');
+ }
+
+ // Should be the same as a direct json_decode of the composer.json file
+ public function test_foo()
+ {
+ $ext_name = 'foo';
+
+ $manager = $this->get_metadata_manager($ext_name);
+
+ try
+ {
+ $metadata = $manager->get_metadata();
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+
+ $json = json_decode(file_get_contents($this->phpbb_root_path . 'ext/foo/composer.json'), true);
+
+ $this->assertEquals($metadata, $json);
+ }
+
+ public function test_validator_non_existant()
+ {
+ $ext_name = 'validator';
+
+ $manager = $this->get_metadata_manager($ext_name);
+
+ // Non-existant data
+ try
+ {
+ $manager->validate('name');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Required meta field \'name\' has not been set.');
+ }
+
+ try
+ {
+ $manager->validate('type');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Required meta field \'type\' has not been set.');
+ }
+
+ try
+ {
+ $manager->validate('licence');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Required meta field \'licence\' has not been set.');
+ }
+
+ try
+ {
+ $manager->validate('version');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Required meta field \'version\' has not been set.');
+ }
+
+ try
+ {
+ $manager->validate_authors();
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Required meta field \'authors\' has not been set.');
+ }
+
+ $manager->merge_metadata(array(
+ 'authors' => array(
+ array(),
+ ),
+ ));
+
+ try
+ {
+ $manager->validate_authors();
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Required meta field \'author name\' has not been set.');
+ }
+ }
+
+
+ public function test_validator_invalid()
+ {
+ $ext_name = 'validator';
+
+ $manager = $this->get_metadata_manager($ext_name);
+
+ // Invalid data
+ $manager->set_metadata(array(
+ 'name' => 'asdf',
+ 'type' => 'asdf',
+ 'licence' => '',
+ 'version' => '',
+ ));
+
+ try
+ {
+ $manager->validate('name');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Meta field \'name\' is invalid.');
+ }
+
+ try
+ {
+ $manager->validate('type');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Meta field \'type\' is invalid.');
+ }
+
+ try
+ {
+ $manager->validate('licence');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Meta field \'licence\' is invalid.');
+ }
+
+ try
+ {
+ $manager->validate('version');
+
+ $this->fail('Exception not triggered');
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->assertEquals((string) $e, 'Meta field \'version\' is invalid.');
+ }
+ }
+
+ public function test_validator_valid()
+ {
+ $ext_name = 'validator';
+
+ $manager = $this->get_metadata_manager($ext_name);
+
+ // Valid data
+ $manager->set_metadata(array(
+ 'name' => 'test/foo',
+ 'type' => 'phpbb3-extension',
+ 'licence' => 'GPL v2',
+ 'version' => '1.0.0',
+ ));
+
+ try
+ {
+ $this->assertEquals(true, $manager->validate('enable'));
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+ }
+
+
+ public function test_validator_requirements()
+ {
+ $ext_name = 'validator';
+
+ $manager = $this->get_metadata_manager($ext_name);
+ // Too high of requirements
+ $manager->merge_metadata(array(
+ 'require' => array(
+ 'php' => '10.0.0',
+ 'phpbb' => '3.2.0', // config is set to 3.1.0
+ ),
+ ));
+
+ try
+ {
+ $this->assertEquals(false, $manager->validate_require_php());
+ $this->assertEquals(false, $manager->validate_require_phpbb());
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+
+
+ // Too high of requirements
+ $manager->merge_metadata(array(
+ 'require' => array(
+ 'php' => '5.3.0',
+ 'phpbb' => '3.1.0-beta', // config is set to 3.1.0
+ ),
+ ));
+
+ try
+ {
+ $this->assertEquals(true, $manager->validate_require_php());
+ $this->assertEquals(true, $manager->validate_require_phpbb());
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+
+
+ // Too high of requirements
+ $manager->merge_metadata(array(
+ 'require' => array(
+ 'php' => '>' . phpversion(),
+ 'phpbb' => '>3.1.0', // config is set to 3.1.0
+ ),
+ ));
+
+ try
+ {
+ $this->assertEquals(false, $manager->validate_require_php());
+ $this->assertEquals(false, $manager->validate_require_phpbb());
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+
+
+ // Too high of current install
+ $manager->merge_metadata(array(
+ 'require' => array(
+ 'php' => '<' . phpversion(),
+ 'phpbb' => '<3.1.0', // config is set to 3.1.0
+ ),
+ ));
+
+ try
+ {
+ $this->assertEquals(false, $manager->validate_require_php());
+ $this->assertEquals(false, $manager->validate_require_phpbb());
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+
+
+ // Matching requirements
+ $manager->merge_metadata(array(
+ 'require' => array(
+ 'php' => phpversion(),
+ 'phpbb' => '3.1.0', // config is set to 3.1.0
+ ),
+ ));
+
+ try
+ {
+ $this->assertEquals(true, $manager->validate_require_php());
+ $this->assertEquals(true, $manager->validate_require_phpbb());
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+
+
+ // Matching requirements
+ $manager->merge_metadata(array(
+ 'require' => array(
+ 'php' => '>=' . phpversion(),
+ 'phpbb' => '>=3.1.0', // config is set to 3.1.0
+ ),
+ ));
+
+ try
+ {
+ $this->assertEquals(true, $manager->validate_require_php());
+ $this->assertEquals(true, $manager->validate_require_phpbb());
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+
+
+ // Matching requirements
+ $manager->merge_metadata(array(
+ 'require' => array(
+ 'php' => '<=' . phpversion(),
+ 'phpbb' => '<=3.1.0', // config is set to 3.1.0
+ ),
+ ));
+
+ try
+ {
+ $this->assertEquals(true, $manager->validate_require_php());
+ $this->assertEquals(true, $manager->validate_require_phpbb());
+ }
+ catch(phpbb_extension_exception $e)
+ {
+ $this->fail($e);
+ }
+ }
+
+ /**
+ * Get an instance of the metadata manager
+ *
+ * @param string $ext_name
+ * @return phpbb_extension_metadata_manager_test
+ */
+ private function get_metadata_manager($ext_name)
+ {
+ return new phpbb_extension_metadata_manager_test(
+ $ext_name,
+ $this->db,
+ $this->extension_manager,
+ $this->phpbb_root_path,
+ $this->phpEx,
+ $this->template,
+ $this->config
+ );
+ }
+}
+
+class phpbb_extension_metadata_manager_test extends phpbb_extension_metadata_manager
+{
+ public function set_metadata($metadata)
+ {
+ $this->metadata = $metadata;
+ }
+
+ public function merge_metadata($metadata)
+ {
+ $this->metadata = array_merge($this->metadata, $metadata);
+ }
+} \ No newline at end of file
diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php
index fca66516ba..d002615e8c 100644
--- a/tests/test_framework/phpbb_functional_test_case.php
+++ b/tests/test_framework/phpbb_functional_test_case.php
@@ -125,6 +125,7 @@ class phpbb_functional_test_case extends phpbb_test_case
{
$this->extension_manager = new phpbb_extension_manager(
$this->get_db(),
+ new phpbb_config(),
self::$config['table_prefix'] . 'ext',
$phpbb_root_path,
".$phpEx",
@@ -251,6 +252,48 @@ class phpbb_functional_test_case extends phpbb_test_case
}
}
+ /**
+ * Login to the ACP
+ * You must run login() before calling this.
+ */
+ protected function admin_login()
+ {
+ $this->add_lang('acp/common');
+
+ // Requires login first!
+ if (empty($this->sid))
+ {
+ $this->fail('$this->sid is empty. Make sure you call login() before admin_login()');
+ return;
+ }
+
+ $crawler = $this->request('GET', 'adm/index.php?sid=' . $this->sid);
+ $this->assertContains($this->lang('LOGIN_ADMIN_CONFIRM'), $crawler->filter('html')->text());
+
+ $form = $crawler->selectButton($this->lang('LOGIN'))->form();
+
+ foreach ($form->getValues() as $field => $value)
+ {
+ if (strpos($field, 'password_') === 0)
+ {
+ $login = $this->client->submit($form, array('username' => 'admin', $field => 'admin'));
+
+ $cookies = $this->cookieJar->all();
+
+ // The session id is stored in a cookie that ends with _sid - we assume there is only one such cookie
+ foreach ($cookies as $cookie);
+ {
+ if (substr($cookie->getName(), -4) == '_sid')
+ {
+ $this->sid = $cookie->getValue();
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
protected function add_lang($lang_file)
{
if (is_array($lang_file))
@@ -287,4 +330,16 @@ class phpbb_functional_test_case extends phpbb_test_case
return call_user_func_array('sprintf', $args);
}
+
+ /**
+ * assertContains for language strings
+ *
+ * @param string $needle Search string
+ * @param string $haystack Search this
+ * @param string $message Optional failure message
+ */
+ public function assertContainsLang($needle, $haystack, $message = null)
+ {
+ $this->assertContains(html_entity_decode($this->lang($needle), ENT_QUOTES), $haystack, $message);
+ }
}
diff --git a/tests/test_framework/phpbb_test_case_helpers.php b/tests/test_framework/phpbb_test_case_helpers.php
index 46feef550a..d10645a732 100644
--- a/tests/test_framework/phpbb_test_case_helpers.php
+++ b/tests/test_framework/phpbb_test_case_helpers.php
@@ -115,4 +115,112 @@ class phpbb_test_case_helpers
return $config;
}
+
+ /**
+ * Recursive directory copying function
+ *
+ * @param string $source
+ * @param string $dest
+ * @return array list of files copied
+ */
+ public function copy_dir($source, $dest)
+ {
+ $source = (substr($source, -1) == '/') ? $source : $source . '/';
+ $dest = (substr($dest, -1) == '/') ? $dest : $dest . '/';
+
+ $copied_files = array();
+
+ if (!is_dir($dest))
+ {
+ $this->makedirs($dest);
+ }
+
+ $files = scandir($source);
+ foreach ($files as $file)
+ {
+ if ($file == '.' || $file == '..')
+ {
+ continue;
+ }
+
+ if (is_dir($source . $file))
+ {
+ $created_dir = false;
+ if (!is_dir($dest . $file))
+ {
+ $created_dir = true;
+ $this->makedirs($dest . $file);
+ }
+
+ $copied_files = array_merge($copied_files, self::copy_dir($source . $file, $dest . $file));
+
+ if ($created_dir)
+ {
+ $copied_files[] = $dest . $file;
+ }
+ }
+ else
+ {
+ if (!file_exists($dest . $file))
+ {
+ copy($source . $file, $dest . $file);
+
+ $copied_files[] = $dest . $file;
+ }
+ }
+ }
+
+ return $copied_files;
+ }
+
+ /**
+ * Remove files/directories that are listed in an array
+ * Designed for use with $this->copy_dir()
+ *
+ * @param array $file_list
+ */
+ public function remove_files($file_list)
+ {
+ foreach ($file_list as $file)
+ {
+ if (is_dir($file))
+ {
+ rmdir($file);
+ }
+ else
+ {
+ unlink($file);
+ }
+ }
+ }
+
+ /**
+ * Empty directory (remove any subdirectories/files below)
+ *
+ * @param array $file_list
+ */
+ public function empty_dir($path)
+ {
+ $path = (substr($path, -1) == '/') ? $path : $path . '/';
+
+ $files = scandir($path);
+ foreach ($files as $file)
+ {
+ if ($file == '.' || $file == '..')
+ {
+ continue;
+ }
+
+ if (is_dir($path . $file))
+ {
+ $this->empty_dir($path . $file);
+
+ rmdir($path . $file);
+ }
+ else
+ {
+ unlink($path . $file);
+ }
+ }
+ }
}