Augmentation vers version 3.3.0
This commit is contained in:
285
install/update/new/phpbb/auth/provider/apache.php
Normal file
285
install/update/new/phpbb/auth/provider/apache.php
Normal file
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\language\language;
|
||||
use phpbb\request\request_interface;
|
||||
use phpbb\request\type_cast_helper;
|
||||
use phpbb\user;
|
||||
|
||||
/**
|
||||
* Apache authentication provider for phpBB3
|
||||
*/
|
||||
class apache extends base
|
||||
{
|
||||
/** @var config phpBB config */
|
||||
protected $config;
|
||||
|
||||
/** @var driver_interface Database object */
|
||||
protected $db;
|
||||
|
||||
/** @var language Language object */
|
||||
protected $language;
|
||||
|
||||
/** @var request_interface Request object */
|
||||
protected $request;
|
||||
|
||||
/** @var user User object */
|
||||
protected $user;
|
||||
|
||||
/** @var string Relative path to phpBB root */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string PHP file extension */
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Apache Authentication Constructor
|
||||
*
|
||||
* @param config $config Config object
|
||||
* @param driver_interface $db Database object
|
||||
* @param language $language Language object
|
||||
* @param request_interface $request Request object
|
||||
* @param user $user User object
|
||||
* @param string $phpbb_root_path Relative path to phpBB root
|
||||
* @param string $php_ext PHP file extension
|
||||
*/
|
||||
public function __construct(config $config, driver_interface $db, language $language, request_interface $request, user $user, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->language = $language;
|
||||
$this->request = $request;
|
||||
$this->user = $user;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!$this->request->is_set('PHP_AUTH_USER', request_interface::SERVER) || $this->user->data['username'] !== htmlspecialchars_decode($this->request->server('PHP_AUTH_USER')))
|
||||
{
|
||||
return $this->language->lang('APACHE_SETUP_BEFORE_USE');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function login($username, $password)
|
||||
{
|
||||
// do not allow empty password
|
||||
if (!$password)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_PASSWORD,
|
||||
'error_msg' => 'NO_PASSWORD_SUPPLIED',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
if (!$username)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_USERNAME,
|
||||
'error_msg' => 'LOGIN_ERROR_USERNAME',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->request->is_set('PHP_AUTH_USER', request_interface::SERVER))
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => 'LOGIN_ERROR_EXTERNAL_AUTH_APACHE',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
$php_auth_user = htmlspecialchars_decode($this->request->server('PHP_AUTH_USER'));
|
||||
$php_auth_pw = htmlspecialchars_decode($this->request->server('PHP_AUTH_PW'));
|
||||
|
||||
if (!empty($php_auth_user) && !empty($php_auth_pw))
|
||||
{
|
||||
if ($php_auth_user !== $username)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_USERNAME,
|
||||
'error_msg' => 'LOGIN_ERROR_USERNAME',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
$sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type
|
||||
FROM ' . USERS_TABLE . "
|
||||
WHERE username = '" . $this->db->sql_escape($php_auth_user) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
// User inactive...
|
||||
if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_ACTIVE,
|
||||
'error_msg' => 'ACTIVE_ERROR',
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
|
||||
// Successful login...
|
||||
return array(
|
||||
'status' => LOGIN_SUCCESS,
|
||||
'error_msg' => false,
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
|
||||
// this is the user's first login so create an empty profile
|
||||
return array(
|
||||
'status' => LOGIN_SUCCESS_CREATE_PROFILE,
|
||||
'error_msg' => false,
|
||||
'user_row' => $this->user_row($php_auth_user),
|
||||
);
|
||||
}
|
||||
|
||||
// Not logged into apache
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => 'LOGIN_ERROR_EXTERNAL_AUTH_APACHE',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function autologin()
|
||||
{
|
||||
if (!$this->request->is_set('PHP_AUTH_USER', request_interface::SERVER))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$php_auth_user = htmlspecialchars_decode($this->request->server('PHP_AUTH_USER'));
|
||||
$php_auth_pw = htmlspecialchars_decode($this->request->server('PHP_AUTH_PW'));
|
||||
|
||||
if (!empty($php_auth_user) && !empty($php_auth_pw))
|
||||
{
|
||||
$type_cast_helper = new type_cast_helper();
|
||||
$type_cast_helper->set_var($php_auth_user, $php_auth_user, 'string', true);
|
||||
|
||||
$sql = 'SELECT *
|
||||
FROM ' . USERS_TABLE . "
|
||||
WHERE username = '" . $this->db->sql_escape($php_auth_user) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
return ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE) ? array() : $row;
|
||||
}
|
||||
|
||||
if (!function_exists('user_add'))
|
||||
{
|
||||
include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
|
||||
}
|
||||
|
||||
// create the user if he does not exist yet
|
||||
user_add($this->user_row($php_auth_user));
|
||||
|
||||
$sql = 'SELECT *
|
||||
FROM ' . USERS_TABLE . "
|
||||
WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($php_auth_user)) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function generates an array which can be passed to the user_add
|
||||
* function in order to create a user
|
||||
*
|
||||
* @param string $username The username of the new user.
|
||||
*
|
||||
* @return array Contains data that can be passed directly to
|
||||
* the user_add function.
|
||||
*/
|
||||
private function user_row($username)
|
||||
{
|
||||
// first retrieve default group id
|
||||
$sql = 'SELECT group_id
|
||||
FROM ' . GROUPS_TABLE . "
|
||||
WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "'
|
||||
AND group_type = " . GROUP_SPECIAL;
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$row)
|
||||
{
|
||||
trigger_error('NO_GROUP');
|
||||
}
|
||||
|
||||
// generate user account data
|
||||
return array(
|
||||
'username' => $username,
|
||||
'user_password' => '',
|
||||
'user_email' => '',
|
||||
'group_id' => (int) $row['group_id'],
|
||||
'user_type' => USER_NORMAL,
|
||||
'user_ip' => $this->user->ip,
|
||||
'user_new' => ($this->config['new_member_post_limit']) ? 1 : 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate_session($user)
|
||||
{
|
||||
// Check if PHP_AUTH_USER is set and handle this case
|
||||
if ($this->request->is_set('PHP_AUTH_USER', request_interface::SERVER))
|
||||
{
|
||||
$php_auth_user = $this->request->server('PHP_AUTH_USER');
|
||||
|
||||
return ($php_auth_user === $user['username']) ? true : false;
|
||||
}
|
||||
|
||||
// PHP_AUTH_USER is not set. A valid session is now determined by the user type (anonymous/bot or not)
|
||||
if ($user['user_type'] == USER_IGNORE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
108
install/update/new/phpbb/auth/provider/base.php
Normal file
108
install/update/new/phpbb/auth/provider/base.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider;
|
||||
|
||||
/**
|
||||
* Base authentication provider class that all other providers should implement
|
||||
*/
|
||||
abstract class base implements provider_interface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function autologin()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function acp()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_acp_template($new_config)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_login_data()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_auth_link_data($user_id = 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function logout($data, $new_session)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate_session($user)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function login_link_has_necessary_data($login_link_data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function link_account(array $link_data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unlink_account(array $link_data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
259
install/update/new/phpbb/auth/provider/db.php
Normal file
259
install/update/new/phpbb/auth/provider/db.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider;
|
||||
|
||||
use phpbb\captcha\factory;
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\passwords\manager;
|
||||
use phpbb\request\request_interface;
|
||||
use phpbb\user;
|
||||
|
||||
/**
|
||||
* Database authentication provider for phpBB3
|
||||
* This is for authentication via the integrated user table
|
||||
*/
|
||||
class db extends base
|
||||
{
|
||||
/** @var factory CAPTCHA factory */
|
||||
protected $captcha_factory;
|
||||
|
||||
/** @var config phpBB config */
|
||||
protected $config;
|
||||
|
||||
/** @var driver_interface DBAL driver instance */
|
||||
protected $db;
|
||||
|
||||
/** @var request_interface Request object */
|
||||
protected $request;
|
||||
|
||||
/** @var user User object */
|
||||
protected $user;
|
||||
|
||||
/** @var string phpBB root path */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string PHP file extension */
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* phpBB passwords manager
|
||||
*
|
||||
* @var manager
|
||||
*/
|
||||
protected $passwords_manager;
|
||||
|
||||
/**
|
||||
* Database Authentication Constructor
|
||||
*
|
||||
* @param factory $captcha_factory
|
||||
* @param config $config
|
||||
* @param driver_interface $db
|
||||
* @param manager $passwords_manager
|
||||
* @param request_interface $request
|
||||
* @param user $user
|
||||
* @param string $phpbb_root_path
|
||||
* @param string $php_ext
|
||||
*/
|
||||
public function __construct(factory $captcha_factory, config $config, driver_interface $db, manager $passwords_manager, request_interface $request, user $user, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->captcha_factory = $captcha_factory;
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->passwords_manager = $passwords_manager;
|
||||
$this->request = $request;
|
||||
$this->user = $user;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function login($username, $password)
|
||||
{
|
||||
// Auth plugins get the password untrimmed.
|
||||
// For compatibility we trim() here.
|
||||
$password = trim($password);
|
||||
|
||||
// do not allow empty password
|
||||
if (!$password)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_PASSWORD,
|
||||
'error_msg' => 'NO_PASSWORD_SUPPLIED',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
if (!$username)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_USERNAME,
|
||||
'error_msg' => 'LOGIN_ERROR_USERNAME',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
$username_clean = utf8_clean_string($username);
|
||||
|
||||
$sql = 'SELECT *
|
||||
FROM ' . USERS_TABLE . "
|
||||
WHERE username_clean = '" . $this->db->sql_escape($username_clean) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (($this->user->ip && !$this->config['ip_login_limit_use_forwarded']) ||
|
||||
($this->user->forwarded_for && $this->config['ip_login_limit_use_forwarded']))
|
||||
{
|
||||
$sql = 'SELECT COUNT(*) AS attempts
|
||||
FROM ' . LOGIN_ATTEMPT_TABLE . '
|
||||
WHERE attempt_time > ' . (time() - (int) $this->config['ip_login_limit_time']);
|
||||
if ($this->config['ip_login_limit_use_forwarded'])
|
||||
{
|
||||
$sql .= " AND attempt_forwarded_for = '" . $this->db->sql_escape($this->user->forwarded_for) . "'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= " AND attempt_ip = '" . $this->db->sql_escape($this->user->ip) . "' ";
|
||||
}
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
$attempts = (int) $this->db->sql_fetchfield('attempts');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$attempt_data = array(
|
||||
'attempt_ip' => $this->user->ip,
|
||||
'attempt_browser' => trim(substr($this->user->browser, 0, 149)),
|
||||
'attempt_forwarded_for' => $this->user->forwarded_for,
|
||||
'attempt_time' => time(),
|
||||
'user_id' => ($row) ? (int) $row['user_id'] : 0,
|
||||
'username' => $username,
|
||||
'username_clean' => $username_clean,
|
||||
);
|
||||
$sql = 'INSERT INTO ' . LOGIN_ATTEMPT_TABLE . $this->db->sql_build_array('INSERT', $attempt_data);
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
$attempts = 0;
|
||||
}
|
||||
|
||||
if (!$row)
|
||||
{
|
||||
if ($this->config['ip_login_limit_max'] && $attempts >= $this->config['ip_login_limit_max'])
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_ATTEMPTS,
|
||||
'error_msg' => 'LOGIN_ERROR_ATTEMPTS',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_USERNAME,
|
||||
'error_msg' => 'LOGIN_ERROR_USERNAME',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
$show_captcha = ($this->config['max_login_attempts'] && $row['user_login_attempts'] >= $this->config['max_login_attempts']) ||
|
||||
($this->config['ip_login_limit_max'] && $attempts >= $this->config['ip_login_limit_max']);
|
||||
|
||||
// If there are too many login attempts, we need to check for a confirm image
|
||||
// Every auth module is able to define what to do by itself...
|
||||
if ($show_captcha)
|
||||
{
|
||||
$captcha = $this->captcha_factory->get_instance($this->config['captcha_plugin']);
|
||||
$captcha->init(CONFIRM_LOGIN);
|
||||
$vc_response = $captcha->validate($row);
|
||||
if ($vc_response)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_ATTEMPTS,
|
||||
'error_msg' => 'LOGIN_ERROR_ATTEMPTS',
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$captcha->reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check password ...
|
||||
if ($this->passwords_manager->check($password, $row['user_password'], $row))
|
||||
{
|
||||
// Check for old password hash...
|
||||
if ($this->passwords_manager->convert_flag || strlen($row['user_password']) == 32)
|
||||
{
|
||||
$hash = $this->passwords_manager->hash($password);
|
||||
|
||||
// Update the password in the users table to the new format
|
||||
$sql = 'UPDATE ' . USERS_TABLE . "
|
||||
SET user_password = '" . $this->db->sql_escape($hash) . "'
|
||||
WHERE user_id = {$row['user_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$row['user_password'] = $hash;
|
||||
}
|
||||
|
||||
$sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . '
|
||||
WHERE user_id = ' . $row['user_id'];
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
if ($row['user_login_attempts'] != 0)
|
||||
{
|
||||
// Successful, reset login attempts (the user passed all stages)
|
||||
$sql = 'UPDATE ' . USERS_TABLE . '
|
||||
SET user_login_attempts = 0
|
||||
WHERE user_id = ' . $row['user_id'];
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
// User inactive...
|
||||
if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_ACTIVE,
|
||||
'error_msg' => 'ACTIVE_ERROR',
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
|
||||
// Successful login... set user_login_attempts to zero...
|
||||
return array(
|
||||
'status' => LOGIN_SUCCESS,
|
||||
'error_msg' => false,
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
|
||||
// Password incorrect - increase login attempts
|
||||
$sql = 'UPDATE ' . USERS_TABLE . '
|
||||
SET user_login_attempts = user_login_attempts + 1
|
||||
WHERE user_id = ' . (int) $row['user_id'] . '
|
||||
AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX;
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
// Give status about wrong password...
|
||||
return array(
|
||||
'status' => ($show_captcha) ? LOGIN_ERROR_ATTEMPTS : LOGIN_ERROR_PASSWORD,
|
||||
'error_msg' => 'LOGIN_ERROR_PASSWORD',
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
}
|
||||
359
install/update/new/phpbb/auth/provider/ldap.php
Normal file
359
install/update/new/phpbb/auth/provider/ldap.php
Normal file
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\language\language;
|
||||
use phpbb\user;
|
||||
|
||||
/**
|
||||
* Database authentication provider for phpBB3
|
||||
* This is for authentication via the integrated user table
|
||||
*/
|
||||
class ldap extends base
|
||||
{
|
||||
/** @var config phpBB config */
|
||||
protected $config;
|
||||
|
||||
/** @var driver_interface DBAL driver interface */
|
||||
protected $db;
|
||||
|
||||
/** @var language phpBB language class */
|
||||
protected $language;
|
||||
|
||||
/** @var user phpBB user */
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* LDAP Authentication Constructor
|
||||
*
|
||||
* @param config $config Config object
|
||||
* @param driver_interface $db DBAL driver interface
|
||||
* @param language $language Language object
|
||||
* @param user $user User object
|
||||
*/
|
||||
public function __construct(config $config, driver_interface $db, language $language, user $user)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->language = $language;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!@extension_loaded('ldap'))
|
||||
{
|
||||
return $this->language->lang('LDAP_NO_LDAP_EXTENSION');
|
||||
}
|
||||
|
||||
$this->config['ldap_port'] = (int) $this->config['ldap_port'];
|
||||
if ($this->config['ldap_port'])
|
||||
{
|
||||
$ldap = @ldap_connect($this->config['ldap_server'], $this->config['ldap_port']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$ldap = @ldap_connect($this->config['ldap_server']);
|
||||
}
|
||||
|
||||
if (!$ldap)
|
||||
{
|
||||
return $this->language->lang('LDAP_NO_SERVER_CONNECTION');
|
||||
}
|
||||
|
||||
@ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
@ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
if ($this->config['ldap_user'] || $this->config['ldap_password'])
|
||||
{
|
||||
if (!@ldap_bind($ldap, htmlspecialchars_decode($this->config['ldap_user']), htmlspecialchars_decode($this->config['ldap_password'])))
|
||||
{
|
||||
return $this->language->lang('LDAP_INCORRECT_USER_PASSWORD');
|
||||
}
|
||||
}
|
||||
|
||||
// ldap_connect only checks whether the specified server is valid, so the connection might still fail
|
||||
$search = @ldap_search(
|
||||
$ldap,
|
||||
htmlspecialchars_decode($this->config['ldap_base_dn']),
|
||||
$this->ldap_user_filter($this->user->data['username']),
|
||||
(empty($this->config['ldap_email'])) ?
|
||||
array(htmlspecialchars_decode($this->config['ldap_uid'])) :
|
||||
array(htmlspecialchars_decode($this->config['ldap_uid']), htmlspecialchars_decode($this->config['ldap_email'])),
|
||||
0,
|
||||
1
|
||||
);
|
||||
|
||||
if ($search === false)
|
||||
{
|
||||
return $this->language->lang('LDAP_SEARCH_FAILED');
|
||||
}
|
||||
|
||||
$result = @ldap_get_entries($ldap, $search);
|
||||
|
||||
@ldap_close($ldap);
|
||||
|
||||
if (!is_array($result) || count($result) < 2)
|
||||
{
|
||||
return $this->language->lang('LDAP_NO_IDENTITY', $this->user->data['username']);
|
||||
}
|
||||
|
||||
if (!empty($this->config['ldap_email']) && !isset($result[0][htmlspecialchars_decode($this->config['ldap_email'])]))
|
||||
{
|
||||
return $this->language->lang('LDAP_NO_EMAIL');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function login($username, $password)
|
||||
{
|
||||
// do not allow empty password
|
||||
if (!$password)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_PASSWORD,
|
||||
'error_msg' => 'NO_PASSWORD_SUPPLIED',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
if (!$username)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_USERNAME,
|
||||
'error_msg' => 'LOGIN_ERROR_USERNAME',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
if (!@extension_loaded('ldap'))
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => 'LDAP_NO_LDAP_EXTENSION',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
$this->config['ldap_port'] = (int) $this->config['ldap_port'];
|
||||
if ($this->config['ldap_port'])
|
||||
{
|
||||
$ldap = @ldap_connect($this->config['ldap_server'], $this->config['ldap_port']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$ldap = @ldap_connect($this->config['ldap_server']);
|
||||
}
|
||||
|
||||
if (!$ldap)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => 'LDAP_NO_SERVER_CONNECTION',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
@ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
@ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
if ($this->config['ldap_user'] || $this->config['ldap_password'])
|
||||
{
|
||||
if (!@ldap_bind($ldap, htmlspecialchars_decode($this->config['ldap_user']), htmlspecialchars_decode($this->config['ldap_password'])))
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => 'LDAP_NO_SERVER_CONNECTION',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$search = @ldap_search(
|
||||
$ldap,
|
||||
htmlspecialchars_decode($this->config['ldap_base_dn']),
|
||||
$this->ldap_user_filter($username),
|
||||
(empty($this->config['ldap_email'])) ?
|
||||
array(htmlspecialchars_decode($this->config['ldap_uid'])) :
|
||||
array(htmlspecialchars_decode($this->config['ldap_uid']), htmlspecialchars_decode($this->config['ldap_email'])),
|
||||
0,
|
||||
1
|
||||
);
|
||||
|
||||
$ldap_result = @ldap_get_entries($ldap, $search);
|
||||
|
||||
if (is_array($ldap_result) && count($ldap_result) > 1)
|
||||
{
|
||||
if (@ldap_bind($ldap, $ldap_result[0]['dn'], htmlspecialchars_decode($password)))
|
||||
{
|
||||
@ldap_close($ldap);
|
||||
|
||||
$sql ='SELECT user_id, username, user_password, user_passchg, user_email, user_type
|
||||
FROM ' . USERS_TABLE . "
|
||||
WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($username)) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
unset($ldap_result);
|
||||
|
||||
// User inactive...
|
||||
if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE)
|
||||
{
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_ACTIVE,
|
||||
'error_msg' => 'ACTIVE_ERROR',
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
|
||||
// Successful login... set user_login_attempts to zero...
|
||||
return array(
|
||||
'status' => LOGIN_SUCCESS,
|
||||
'error_msg' => false,
|
||||
'user_row' => $row,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// retrieve default group id
|
||||
$sql = 'SELECT group_id
|
||||
FROM ' . GROUPS_TABLE . "
|
||||
WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "'
|
||||
AND group_type = " . GROUP_SPECIAL;
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$row)
|
||||
{
|
||||
trigger_error('NO_GROUP');
|
||||
}
|
||||
|
||||
// generate user account data
|
||||
$ldap_user_row = array(
|
||||
'username' => $username,
|
||||
'user_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,
|
||||
'user_ip' => $this->user->ip,
|
||||
'user_new' => ($this->config['new_member_post_limit']) ? 1 : 0,
|
||||
);
|
||||
|
||||
unset($ldap_result);
|
||||
|
||||
// this is the user's first login so create an empty profile
|
||||
return array(
|
||||
'status' => LOGIN_SUCCESS_CREATE_PROFILE,
|
||||
'error_msg' => false,
|
||||
'user_row' => $ldap_user_row,
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($ldap_result);
|
||||
@ldap_close($ldap);
|
||||
|
||||
// Give status about wrong password...
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_PASSWORD,
|
||||
'error_msg' => 'LOGIN_ERROR_PASSWORD',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ldap_close($ldap);
|
||||
|
||||
return array(
|
||||
'status' => LOGIN_ERROR_USERNAME,
|
||||
'error_msg' => 'LOGIN_ERROR_USERNAME',
|
||||
'user_row' => array('user_id' => ANONYMOUS),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function acp()
|
||||
{
|
||||
// These are fields required in the config table
|
||||
return array(
|
||||
'ldap_server', 'ldap_port', 'ldap_base_dn', 'ldap_uid', 'ldap_user_filter', 'ldap_email', 'ldap_user', 'ldap_password',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_acp_template($new_config)
|
||||
{
|
||||
return array(
|
||||
'TEMPLATE_FILE' => 'auth_provider_ldap.html',
|
||||
'TEMPLATE_VARS' => array(
|
||||
'AUTH_LDAP_BASE_DN' => $new_config['ldap_base_dn'],
|
||||
'AUTH_LDAP_EMAIL' => $new_config['ldap_email'],
|
||||
'AUTH_LDAP_PASSORD' => $new_config['ldap_password'] !== '' ? '********' : '',
|
||||
'AUTH_LDAP_PORT' => $new_config['ldap_port'],
|
||||
'AUTH_LDAP_SERVER' => $new_config['ldap_server'],
|
||||
'AUTH_LDAP_UID' => $new_config['ldap_uid'],
|
||||
'AUTH_LDAP_USER' => $new_config['ldap_user'],
|
||||
'AUTH_LDAP_USER_FILTER' => $new_config['ldap_user_filter'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a filter string for ldap_search to find a user
|
||||
*
|
||||
* @param $username string Username identifying the searched user
|
||||
*
|
||||
* @return string A filter string for ldap_search
|
||||
*/
|
||||
private function ldap_user_filter($username)
|
||||
{
|
||||
$filter = '(' . $this->config['ldap_uid'] . '=' . $this->ldap_escape(htmlspecialchars_decode($username)) . ')';
|
||||
if ($this->config['ldap_user_filter'])
|
||||
{
|
||||
$_filter = ($this->config['ldap_user_filter'][0] == '(' && substr($this->config['ldap_user_filter'], -1) == ')') ? $this->config['ldap_user_filter'] : "({$this->config['ldap_user_filter']})";
|
||||
$filter = "(&{$filter}{$_filter})";
|
||||
}
|
||||
return $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes an LDAP AttributeValue
|
||||
*
|
||||
* @param string $string The string to be escaped
|
||||
* @return string The escaped string
|
||||
*/
|
||||
private function ldap_escape($string)
|
||||
{
|
||||
return str_replace(array('*', '\\', '(', ')'), array('\\*', '\\\\', '\\(', '\\)'), $string);
|
||||
}
|
||||
}
|
||||
859
install/update/new/phpbb/auth/provider/oauth/oauth.php
Normal file
859
install/update/new/phpbb/auth/provider/oauth/oauth.php
Normal file
@@ -0,0 +1,859 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth;
|
||||
|
||||
use OAuth\Common\Http\Exception\TokenResponseException;
|
||||
use OAuth\ServiceFactory;
|
||||
use OAuth\Common\Consumer\Credentials;
|
||||
use OAuth\Common\Service\ServiceInterface;
|
||||
use OAuth\OAuth1\Service\AbstractService as OAuth1Service;
|
||||
use OAuth\OAuth2\Service\AbstractService as OAuth2Service;
|
||||
use phpbb\auth\provider\base;
|
||||
use phpbb\auth\provider\db;
|
||||
use phpbb\auth\provider\oauth\service\exception;
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\di\service_collection;
|
||||
use phpbb\event\dispatcher;
|
||||
use phpbb\language\language;
|
||||
use phpbb\request\request_interface;
|
||||
use phpbb\user;
|
||||
|
||||
/**
|
||||
* OAuth authentication provider for phpBB3
|
||||
*/
|
||||
class oauth extends base
|
||||
{
|
||||
/** @var config */
|
||||
protected $config;
|
||||
|
||||
/** @var driver_interface */
|
||||
protected $db;
|
||||
|
||||
/** @var db */
|
||||
protected $db_auth;
|
||||
|
||||
/** @var dispatcher */
|
||||
protected $dispatcher;
|
||||
|
||||
/** @var language */
|
||||
protected $language;
|
||||
|
||||
/** @var request_interface */
|
||||
protected $request;
|
||||
|
||||
/** @var service_collection */
|
||||
protected $service_providers;
|
||||
|
||||
/** @var user */
|
||||
protected $user;
|
||||
|
||||
/** @var string OAuth table: token storage */
|
||||
protected $oauth_token_table;
|
||||
|
||||
/** @var string OAuth table: state */
|
||||
protected $oauth_state_table;
|
||||
|
||||
/** @var string OAuth table: account association */
|
||||
protected $oauth_account_table;
|
||||
|
||||
/** @var string Users table */
|
||||
protected $users_table;
|
||||
|
||||
/** @var string phpBB root path */
|
||||
protected $root_path;
|
||||
|
||||
/** @var string php File extension */
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param config $config Config object
|
||||
* @param driver_interface $db Database object
|
||||
* @param db $db_auth DB auth provider
|
||||
* @param dispatcher $dispatcher Event dispatcher object
|
||||
* @param language $language Language object
|
||||
* @param request_interface $request Request object
|
||||
* @param service_collection $service_providers OAuth providers service collection
|
||||
* @param user $user User object
|
||||
* @param string $oauth_token_table OAuth table: token storage
|
||||
* @param string $oauth_state_table OAuth table: state
|
||||
* @param string $oauth_account_table OAuth table: account association
|
||||
* @param string $users_table User table
|
||||
* @param string $root_path phpBB root path
|
||||
* @param string $php_ext php File extension
|
||||
*/
|
||||
public function __construct(
|
||||
config $config,
|
||||
driver_interface $db,
|
||||
db $db_auth,
|
||||
dispatcher $dispatcher,
|
||||
language $language,
|
||||
request_interface $request,
|
||||
service_collection $service_providers,
|
||||
user $user,
|
||||
$oauth_token_table,
|
||||
$oauth_state_table,
|
||||
$oauth_account_table,
|
||||
$users_table,
|
||||
$root_path,
|
||||
$php_ext
|
||||
)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->db_auth = $db_auth;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->language = $language;
|
||||
$this->service_providers = $service_providers;
|
||||
$this->request = $request;
|
||||
$this->user = $user;
|
||||
|
||||
$this->oauth_token_table = $oauth_token_table;
|
||||
$this->oauth_state_table = $oauth_state_table;
|
||||
$this->oauth_account_table = $oauth_account_table;
|
||||
$this->users_table = $users_table;
|
||||
$this->root_path = $root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
// This does not test whether or not the key and secret provided are valid.
|
||||
foreach ($this->service_providers as $service_provider)
|
||||
{
|
||||
$credentials = $service_provider->get_service_credentials();
|
||||
|
||||
if (($credentials['key'] && !$credentials['secret']) || (!$credentials['key'] && $credentials['secret']))
|
||||
{
|
||||
return $this->language->lang('AUTH_PROVIDER_OAUTH_ERROR_ELEMENT_MISSING');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function login($username, $password)
|
||||
{
|
||||
// Temporary workaround for only having one authentication provider available
|
||||
if (!$this->request->is_set('oauth_service'))
|
||||
{
|
||||
return $this->db_auth->login($username, $password);
|
||||
}
|
||||
|
||||
// Request the name of the OAuth service
|
||||
$provider = $this->request->variable('oauth_service', '', false);
|
||||
$service_name = $this->get_service_name($provider);
|
||||
|
||||
if ($provider === '' || !array_key_exists($service_name, $this->service_providers))
|
||||
{
|
||||
return [
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST',
|
||||
'user_row' => ['user_id' => ANONYMOUS],
|
||||
];
|
||||
}
|
||||
|
||||
// Get the service credentials for the given service
|
||||
$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
|
||||
$query = 'mode=login&login=external&oauth_service=' . $provider;
|
||||
|
||||
try
|
||||
{
|
||||
/** @var OAuth1Service|OAuth2Service $service */
|
||||
$service = $this->get_service($provider, $storage, $query);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return [
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => $e->getMessage(),
|
||||
'user_row' => ['user_id' => ANONYMOUS],
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->is_set_code($service))
|
||||
{
|
||||
$this->service_providers[$service_name]->set_external_service_provider($service);
|
||||
|
||||
try
|
||||
{
|
||||
$unique_id = $this->service_providers[$service_name]->perform_auth_login();
|
||||
}
|
||||
catch (exception $e)
|
||||
{
|
||||
return [
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => $e->getMessage(),
|
||||
'user_row' => ['user_id' => ANONYMOUS],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this provider is already associated with an account.
|
||||
*
|
||||
* Enforcing a data type to make sure it are strings and not integers,
|
||||
* so values are quoted in the SQL WHERE statement.
|
||||
*/
|
||||
$data = [
|
||||
'provider' => (string) utf8_strtolower($provider),
|
||||
'oauth_provider_id' => (string) $unique_id
|
||||
];
|
||||
|
||||
$sql = 'SELECT user_id
|
||||
FROM ' . $this->oauth_account_table . '
|
||||
WHERE ' . $this->db->sql_build_array('SELECT', $data);
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$redirect_data = array(
|
||||
'auth_provider' => 'oauth',
|
||||
'login_link_oauth_service' => $provider,
|
||||
);
|
||||
|
||||
/**
|
||||
* Event is triggered before check if provider is already associated with an account
|
||||
*
|
||||
* @event core.oauth_login_after_check_if_provider_id_has_match
|
||||
* @var array row User row
|
||||
* @var array data Provider data
|
||||
* @var array redirect_data Data to be appended to the redirect url
|
||||
* @var ServiceInterface service OAuth service
|
||||
* @since 3.2.3-RC1
|
||||
* @changed 3.2.6-RC1 Added redirect_data
|
||||
*/
|
||||
$vars = [
|
||||
'row',
|
||||
'data',
|
||||
'redirect_data',
|
||||
'service',
|
||||
];
|
||||
extract($this->dispatcher->trigger_event('core.oauth_login_after_check_if_provider_id_has_match', compact($vars)));
|
||||
|
||||
if (!$row)
|
||||
{
|
||||
// The user does not yet exist, ask to link or create profile
|
||||
return [
|
||||
'status' => LOGIN_SUCCESS_LINK_PROFILE,
|
||||
'error_msg' => 'LOGIN_OAUTH_ACCOUNT_NOT_LINKED',
|
||||
'user_row' => [],
|
||||
'redirect_data' => $redirect_data,
|
||||
];
|
||||
}
|
||||
|
||||
// Retrieve the user's account
|
||||
$sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_ip, user_type, user_login_attempts
|
||||
FROM ' . $this->users_table . '
|
||||
WHERE user_id = ' . (int) $row['user_id'];
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$row)
|
||||
{
|
||||
return [
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => 'AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY',
|
||||
'user_row' => ['user_id' => ANONYMOUS],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is banned.
|
||||
* The fourth parameter (return) has to be true, otherwise the OAuth login is still called and
|
||||
* an uncaught exception is thrown as there is no token stored in the database.
|
||||
*/
|
||||
$ban = $this->user->check_ban($row['user_id'], $row['user_ip'], $row['user_email'], true);
|
||||
|
||||
if (!empty($ban))
|
||||
{
|
||||
$till_date = !empty($ban['ban_end']) ? $this->user->format_date($ban['ban_end']) : '';
|
||||
$message = !empty($ban['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM';
|
||||
|
||||
$contact_link = phpbb_get_board_contact_link($this->config, $this->root_path, $this->php_ext);
|
||||
|
||||
$message = $this->language->lang($message, $till_date, '<a href="' . $contact_link . '">', '</a>');
|
||||
$message .= !empty($ban['ban_give_reason']) ? '<br /><br />' . $this->language->lang('BOARD_BAN_REASON', $ban['ban_give_reason']) : '';
|
||||
$message .= !empty($ban['ban_triggered_by']) ? '<br /><br /><em>' . $this->language->lang('BAN_TRIGGERED_BY_' . utf8_strtoupper($ban['ban_triggered_by'])) . '</em>' : '';
|
||||
|
||||
return [
|
||||
'status' => LOGIN_BREAK,
|
||||
'error_msg' => $message,
|
||||
'user_row' => $row,
|
||||
];
|
||||
}
|
||||
|
||||
// Update token storage to store the user_id
|
||||
$storage->set_user_id($row['user_id']);
|
||||
|
||||
/**
|
||||
* Event is triggered after user is successfully logged in via OAuth.
|
||||
*
|
||||
* @event core.auth_oauth_login_after
|
||||
* @var array row User row
|
||||
* @since 3.1.11-RC1
|
||||
*/
|
||||
$vars = [
|
||||
'row',
|
||||
];
|
||||
extract($this->dispatcher->trigger_event('core.auth_oauth_login_after', compact($vars)));
|
||||
|
||||
// The user is now authenticated and can be logged in
|
||||
return [
|
||||
'status' => LOGIN_SUCCESS,
|
||||
'error_msg' => false,
|
||||
'user_row' => $row,
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->set_redirect($service);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_login_data()
|
||||
{
|
||||
$login_data = [
|
||||
'TEMPLATE_FILE' => 'login_body_oauth.html',
|
||||
'BLOCK_VAR_NAME' => 'oauth',
|
||||
'BLOCK_VARS' => [],
|
||||
];
|
||||
|
||||
foreach ($this->service_providers as $service_name => $service_provider)
|
||||
{
|
||||
// Only include data if the credentials are set
|
||||
$credentials = $service_provider->get_service_credentials();
|
||||
|
||||
if ($credentials['key'] && $credentials['secret'])
|
||||
{
|
||||
$provider = $this->get_provider($service_name);
|
||||
$redirect_url = generate_board_url() . '/ucp.' . $this->php_ext . '?mode=login&login=external&oauth_service=' . $provider;
|
||||
|
||||
$login_data['BLOCK_VARS'][$service_name] = [
|
||||
'REDIRECT_URL' => redirect($redirect_url, true),
|
||||
'SERVICE_NAME' => $this->get_provider_title($provider),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $login_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function acp()
|
||||
{
|
||||
$ret = [];
|
||||
|
||||
foreach ($this->service_providers as $service_name => $service_provider)
|
||||
{
|
||||
$provider = $this->get_provider($service_name);
|
||||
|
||||
$provider = utf8_strtolower($provider);
|
||||
|
||||
$ret[] = 'auth_oauth_' . $provider . '_key';
|
||||
$ret[] = 'auth_oauth_' . $provider . '_secret';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_acp_template($new_config)
|
||||
{
|
||||
$ret = [
|
||||
'BLOCK_VAR_NAME' => 'oauth_services',
|
||||
'BLOCK_VARS' => [],
|
||||
'TEMPLATE_FILE' => 'auth_provider_oauth.html',
|
||||
'TEMPLATE_VARS' => [],
|
||||
];
|
||||
|
||||
foreach ($this->service_providers as $service_name => $service_provider)
|
||||
{
|
||||
$provider = $this->get_provider($service_name);
|
||||
|
||||
$ret['BLOCK_VARS'][$provider] = [
|
||||
'NAME' => $provider,
|
||||
'ACTUAL_NAME' => $this->get_provider_title($provider),
|
||||
'KEY' => $new_config['auth_oauth_' . utf8_strtolower($provider) . '_key'],
|
||||
'SECRET' => $new_config['auth_oauth_' . utf8_strtolower($provider) . '_secret'],
|
||||
];
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function login_link_has_necessary_data($login_link_data)
|
||||
{
|
||||
if (empty($login_link_data))
|
||||
{
|
||||
return 'LOGIN_LINK_NO_DATA_PROVIDED';
|
||||
}
|
||||
|
||||
if (!array_key_exists('oauth_service', $login_link_data) || !$login_link_data['oauth_service'] ||
|
||||
!array_key_exists('link_method', $login_link_data) || !$login_link_data['link_method'])
|
||||
{
|
||||
return 'LOGIN_LINK_MISSING_DATA';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function link_account(array $link_data)
|
||||
{
|
||||
// Check for a valid link method (auth_link or login_link)
|
||||
if (!array_key_exists('link_method', $link_data) ||
|
||||
!in_array($link_data['link_method'], ['auth_link', 'login_link']))
|
||||
{
|
||||
return 'LOGIN_LINK_MISSING_DATA';
|
||||
}
|
||||
|
||||
// We must have an oauth_service listed, check for it two ways
|
||||
if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service'])
|
||||
{
|
||||
$link_data['oauth_service'] = $this->request->variable('oauth_service', '');
|
||||
|
||||
if (!$link_data['oauth_service'])
|
||||
{
|
||||
return 'LOGIN_LINK_MISSING_DATA';
|
||||
}
|
||||
}
|
||||
|
||||
$service_name = $this->get_service_name($link_data['oauth_service']);
|
||||
|
||||
if (!array_key_exists($service_name, $this->service_providers))
|
||||
{
|
||||
return 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST';
|
||||
}
|
||||
|
||||
switch ($link_data['link_method'])
|
||||
{
|
||||
case 'auth_link':
|
||||
return $this->link_account_auth_link($link_data, $service_name);
|
||||
case 'login_link':
|
||||
return $this->link_account_login_link($link_data, $service_name);
|
||||
default:
|
||||
return 'LOGIN_LINK_MISSING_DATA';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function logout($data, $new_session)
|
||||
{
|
||||
// Clear all tokens belonging to the user
|
||||
$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
|
||||
$storage->clearAllTokens();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_auth_link_data($user_id = 0)
|
||||
{
|
||||
$user_ids = [];
|
||||
$block_vars = [];
|
||||
|
||||
$sql = 'SELECT oauth_provider_id, provider
|
||||
FROM ' . $this->oauth_account_table . '
|
||||
WHERE user_id = ' . ($user_id > 0 ? (int) $user_id : (int) $this->user->data['user_id']);
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$user_ids[$row['provider']] = $row['oauth_provider_id'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
foreach ($this->service_providers as $service_name => $service_provider)
|
||||
{
|
||||
// Only include data if the credentials are set
|
||||
$credentials = $service_provider->get_service_credentials();
|
||||
|
||||
if ($credentials['key'] && $credentials['secret'])
|
||||
{
|
||||
$provider = $this->get_provider($service_name);
|
||||
|
||||
$block_vars[$service_name] = [
|
||||
'SERVICE_NAME' => $this->get_provider_title($provider),
|
||||
'UNIQUE_ID' => isset($user_ids[$provider]) ? $user_ids[$provider] : null,
|
||||
'HIDDEN_FIELDS' => [
|
||||
'link' => !isset($user_ids[$provider]),
|
||||
'oauth_service' => $provider,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'BLOCK_VAR_NAME' => 'oauth',
|
||||
'BLOCK_VARS' => $block_vars,
|
||||
|
||||
'TEMPLATE_FILE' => 'ucp_auth_link_oauth.html',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unlink_account(array $link_data)
|
||||
{
|
||||
if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service'])
|
||||
{
|
||||
return 'LOGIN_LINK_MISSING_DATA';
|
||||
}
|
||||
|
||||
// Remove user specified in $link_data if possible
|
||||
$user_id = isset($link_data['user_id']) ? $link_data['user_id'] : $this->user->data['user_id'];
|
||||
|
||||
// Remove the link
|
||||
$sql = 'DELETE FROM ' . $this->oauth_account_table . "
|
||||
WHERE provider = '" . $this->db->sql_escape($link_data['oauth_service']) . "'
|
||||
AND user_id = " . (int) $user_id;
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$service_name = $this->get_service_name($link_data['oauth_service']);
|
||||
|
||||
// Clear all tokens belonging to the user on this service
|
||||
$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
|
||||
$storage->clearToken($service_name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the account linking for login_link.
|
||||
*
|
||||
* @param array $link_data The same variable given to
|
||||
* {@see \phpbb\auth\provider\provider_interface::link_account}
|
||||
* @param string $service_name The name of the service being used in linking.
|
||||
* @return string|false Returns a language key (string) if an error is encountered,
|
||||
* or false on success.
|
||||
*/
|
||||
protected function link_account_login_link(array $link_data, $service_name)
|
||||
{
|
||||
$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
|
||||
|
||||
// Check for an access token, they should have one
|
||||
if (!$storage->has_access_token_by_session($service_name))
|
||||
{
|
||||
return 'LOGIN_LINK_ERROR_OAUTH_NO_ACCESS_TOKEN';
|
||||
}
|
||||
|
||||
// Prepare for an authentication request
|
||||
$query = 'mode=login_link&login_link_oauth_service=' . $link_data['oauth_service'];
|
||||
|
||||
try
|
||||
{
|
||||
$service = $this->get_service($link_data['oauth_service'], $storage, $query);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$this->service_providers[$service_name]->set_external_service_provider($service);
|
||||
|
||||
try
|
||||
{
|
||||
// The user has already authenticated successfully, request to authenticate again
|
||||
$unique_id = $this->service_providers[$service_name]->perform_token_auth();
|
||||
}
|
||||
catch (exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
// Insert into table, they will be able to log in after this
|
||||
$data = [
|
||||
'user_id' => $link_data['user_id'],
|
||||
'provider' => utf8_strtolower($link_data['oauth_service']),
|
||||
'oauth_provider_id' => $unique_id,
|
||||
];
|
||||
|
||||
$this->link_account_perform_link($data);
|
||||
|
||||
// Update token storage to store the user_id
|
||||
$storage->set_user_id($link_data['user_id']);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the account linking for auth_link.
|
||||
*
|
||||
* @param array $link_data The same variable given to
|
||||
* {@see \phpbb\auth\provider\provider_interface::link_account}
|
||||
* @param string $service_name The name of the service being used in linking.
|
||||
* @return string|false Returns a language constant (string) if an error is encountered,
|
||||
* or false on success.
|
||||
*/
|
||||
protected function link_account_auth_link(array $link_data, $service_name)
|
||||
{
|
||||
$storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
|
||||
$query = 'i=ucp_auth_link&mode=auth_link&link=1&oauth_service=' . $link_data['oauth_service'];
|
||||
|
||||
try
|
||||
{
|
||||
/** @var OAuth1Service|OAuth2Service $service */
|
||||
$service = $this->get_service($link_data['oauth_service'], $storage, $query);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($this->is_set_code($service))
|
||||
{
|
||||
$this->service_providers[$service_name]->set_external_service_provider($service);
|
||||
|
||||
try
|
||||
{
|
||||
$unique_id = $this->service_providers[$service_name]->perform_auth_login();
|
||||
}
|
||||
catch (exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
// Insert into table, they will be able to log in after this
|
||||
$data = [
|
||||
'user_id' => $this->user->data['user_id'],
|
||||
'provider' => utf8_strtolower($link_data['oauth_service']),
|
||||
'oauth_provider_id' => $unique_id,
|
||||
];
|
||||
|
||||
$this->link_account_perform_link($data);
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->set_redirect($service);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the query that inserts an account link
|
||||
*
|
||||
* @param array $data This array is passed to db->sql_build_array
|
||||
*/
|
||||
protected function link_account_perform_link(array $data)
|
||||
{
|
||||
// Check if the external account is already associated with other user
|
||||
$sql = 'SELECT user_id
|
||||
FROM ' . $this->oauth_account_table . "
|
||||
WHERE provider = '" . $this->db->sql_escape($data['provider']) . "'
|
||||
AND oauth_provider_id = '" . $this->db->sql_escape($data['oauth_provider_id']) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
trigger_error('AUTH_PROVIDER_OAUTH_ERROR_ALREADY_LINKED');
|
||||
}
|
||||
|
||||
// Link account
|
||||
$sql = 'INSERT INTO ' . $this->oauth_account_table . ' ' . $this->db->sql_build_array('INSERT', $data);
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
/**
|
||||
* Event is triggered after user links account.
|
||||
*
|
||||
* @event core.auth_oauth_link_after
|
||||
* @var array data User row
|
||||
* @since 3.1.11-RC1
|
||||
*/
|
||||
$vars = [
|
||||
'data',
|
||||
];
|
||||
extract($this->dispatcher->trigger_event('core.auth_oauth_link_after', compact($vars)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new service object.
|
||||
*
|
||||
* @param string $provider The name of the provider
|
||||
* @param token_storage $storage Token storage object
|
||||
* @param string $query The query string used for the redirect uri
|
||||
* @return ServiceInterface
|
||||
* @throws exception When OAuth service was not created
|
||||
*/
|
||||
protected function get_service($provider, token_storage $storage, $query)
|
||||
{
|
||||
$service_name = $this->get_service_name($provider);
|
||||
|
||||
/** @see \phpbb\auth\provider\oauth\service\service_interface::get_service_credentials */
|
||||
$service_credentials = $this->service_providers[$service_name]->get_service_credentials();
|
||||
|
||||
/** @see \phpbb\auth\provider\oauth\service\service_interface::get_auth_scope */
|
||||
$scopes = $this->service_providers[$service_name]->get_auth_scope();
|
||||
|
||||
$callback = generate_board_url() . "/ucp.{$this->php_ext}?{$query}";
|
||||
|
||||
// Setup the credentials for the requests
|
||||
$credentials = new Credentials(
|
||||
$service_credentials['key'],
|
||||
$service_credentials['secret'],
|
||||
$callback
|
||||
);
|
||||
|
||||
$service_factory = new ServiceFactory;
|
||||
|
||||
// Allow providers to register a custom class or override the provider name
|
||||
if ($class = $this->service_providers[$service_name]->get_external_service_class())
|
||||
{
|
||||
if (class_exists($class))
|
||||
{
|
||||
try
|
||||
{
|
||||
$service_factory->registerService($provider, $class);
|
||||
}
|
||||
catch (\OAuth\Common\Exception\Exception $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$provider = $class;
|
||||
}
|
||||
}
|
||||
|
||||
$service = $service_factory->createService($provider, $credentials, $storage, $scopes);
|
||||
|
||||
if (!$service)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED');
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the service name for an OAuth provider name.
|
||||
*
|
||||
* @param string $provider The OAuth provider name
|
||||
* @return string The service name
|
||||
*/
|
||||
protected function get_service_name($provider)
|
||||
{
|
||||
if (strpos($provider, 'auth.provider.oauth.service.') !== 0)
|
||||
{
|
||||
$provider = 'auth.provider.oauth.service.' . utf8_strtolower($provider);
|
||||
}
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OAuth provider name from a service name.
|
||||
*
|
||||
* @param string $service_name The service name
|
||||
* @return string The OAuth provider name
|
||||
*/
|
||||
protected function get_provider($service_name)
|
||||
{
|
||||
return str_replace('auth.provider.oauth.service.', '', $service_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the localized title for the OAuth provider.
|
||||
*
|
||||
* @param string $provider The OAuth provider name
|
||||
* @return string The OAuth provider title
|
||||
*/
|
||||
protected function get_provider_title($provider)
|
||||
{
|
||||
return $this->language->lang('AUTH_PROVIDER_OAUTH_SERVICE_' . utf8_strtoupper($provider));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the authorization code is set.
|
||||
*
|
||||
* @param OAuth1Service|OAuth2Service $service The external OAuth service
|
||||
* @return bool Whether or not the authorization code is set in the URL
|
||||
* for the respective OAuth service's version
|
||||
*/
|
||||
protected function is_set_code($service)
|
||||
{
|
||||
switch ($service::OAUTH_VERSION)
|
||||
{
|
||||
case 1:
|
||||
return $this->request->is_set('oauth_token', request_interface::GET);
|
||||
|
||||
case 2:
|
||||
return $this->request->is_set('code', request_interface::GET);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a redirect to the authorization uri.
|
||||
*
|
||||
* @param OAuth1Service|OAuth2Service $service The external OAuth service
|
||||
* @return array|false Array if an error occurred,
|
||||
* false on success
|
||||
*/
|
||||
protected function set_redirect($service)
|
||||
{
|
||||
$parameters = [];
|
||||
|
||||
if ($service::OAUTH_VERSION === 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
$token = $service->requestRequestToken();
|
||||
$parameters = ['oauth_token' => $token->getRequestToken()];
|
||||
}
|
||||
catch (TokenResponseException $e)
|
||||
{
|
||||
return [
|
||||
'status' => LOGIN_ERROR_EXTERNAL_AUTH,
|
||||
'error_msg' => $e->getMessage(),
|
||||
'user_row' => ['user_id' => ANONYMOUS],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
redirect($service->getAuthorizationUri($parameters), false, true);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth\service;
|
||||
|
||||
/**
|
||||
* Base OAuth abstract class that all OAuth services should implement
|
||||
*/
|
||||
abstract class base implements service_interface
|
||||
{
|
||||
/**
|
||||
* External OAuth service provider
|
||||
*
|
||||
* @var \OAuth\Common\Service\ServiceInterface
|
||||
*/
|
||||
protected $service_provider;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_auth_scope()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_external_service_class()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_external_service_provider()
|
||||
{
|
||||
return $this->service_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set_external_service_provider(\OAuth\Common\Service\ServiceInterface $service_provider)
|
||||
{
|
||||
$this->service_provider = $service_provider;
|
||||
}
|
||||
}
|
||||
107
install/update/new/phpbb/auth/provider/oauth/service/bitly.php
Normal file
107
install/update/new/phpbb/auth/provider/oauth/service/bitly.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth\service;
|
||||
|
||||
/**
|
||||
* Bitly OAuth service
|
||||
*/
|
||||
class bitly extends base
|
||||
{
|
||||
/** @var \phpbb\config\config */
|
||||
protected $config;
|
||||
|
||||
/** @var \phpbb\request\request_interface */
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\request\request_interface $request Request object
|
||||
*/
|
||||
public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_service_credentials()
|
||||
{
|
||||
return [
|
||||
'key' => $this->config['auth_oauth_bitly_key'],
|
||||
'secret' => $this->config['auth_oauth_bitly_secret'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_auth_login()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Bitly))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// This was a callback request, get the token
|
||||
$this->service_provider->requestAccessToken($this->request->variable('code', ''));
|
||||
}
|
||||
catch (\OAuth\Common\Http\Exception\TokenResponseException $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('user/info'), true);
|
||||
}
|
||||
catch (\OAuth\Common\Exception\Exception $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
// Return the unique identifier returned from bitly
|
||||
return $result['data']['login'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_token_auth()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Bitly))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('user/info'), true);
|
||||
}
|
||||
catch (\OAuth\Common\Exception\Exception $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
// Return the unique identifier
|
||||
return $result['data']['login'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth\service;
|
||||
|
||||
/**
|
||||
* Facebook OAuth service
|
||||
*/
|
||||
class facebook extends base
|
||||
{
|
||||
/** @var \phpbb\config\config */
|
||||
protected $config;
|
||||
|
||||
/** @var \phpbb\request\request_interface */
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\request\request_interface $request Request object
|
||||
*/
|
||||
public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_service_credentials()
|
||||
{
|
||||
return [
|
||||
'key' => $this->config['auth_oauth_facebook_key'],
|
||||
'secret' => $this->config['auth_oauth_facebook_secret'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_auth_login()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Facebook))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// This was a callback request, get the token
|
||||
$this->service_provider->requestAccessToken($this->request->variable('code', ''));
|
||||
}
|
||||
catch (\OAuth\Common\Http\Exception\TokenResponseException $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('/me'), true);
|
||||
}
|
||||
catch (\OAuth\Common\Exception\Exception $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
// Return the unique identifier
|
||||
return $result['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_token_auth()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Facebook))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('/me'), true);
|
||||
}
|
||||
catch (\OAuth\Common\Exception\Exception $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
// Return the unique identifier
|
||||
return $result['id'];
|
||||
}
|
||||
}
|
||||
118
install/update/new/phpbb/auth/provider/oauth/service/google.php
Normal file
118
install/update/new/phpbb/auth/provider/oauth/service/google.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth\service;
|
||||
|
||||
/**
|
||||
* Google OAuth service
|
||||
*/
|
||||
class google extends base
|
||||
{
|
||||
/** @var \phpbb\config\config */
|
||||
protected $config;
|
||||
|
||||
/** @var \phpbb\request\request_interface */
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\request\request_interface $request Request object
|
||||
*/
|
||||
public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_auth_scope()
|
||||
{
|
||||
return [
|
||||
'userinfo_email',
|
||||
'userinfo_profile',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_service_credentials()
|
||||
{
|
||||
return [
|
||||
'key' => $this->config['auth_oauth_google_key'],
|
||||
'secret' => $this->config['auth_oauth_google_secret'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_auth_login()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Google))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// This was a callback request, get the token
|
||||
$this->service_provider->requestAccessToken($this->request->variable('code', ''));
|
||||
}
|
||||
catch (\OAuth\Common\Http\Exception\TokenResponseException $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('https://www.googleapis.com/oauth2/v1/userinfo'), true);
|
||||
}
|
||||
catch (\OAuth\Common\Exception\Exception $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
// Return the unique identifier
|
||||
return $result['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_token_auth()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Google))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('https://www.googleapis.com/oauth2/v1/userinfo'), true);
|
||||
}
|
||||
catch (\OAuth\Common\Exception\Exception $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
// Return the unique identifier
|
||||
return $result['id'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth\service;
|
||||
|
||||
/**
|
||||
* OAuth service interface
|
||||
*/
|
||||
interface service_interface
|
||||
{
|
||||
/**
|
||||
* Returns an array of the scopes necessary for auth
|
||||
*
|
||||
* @return array An array of the required scopes
|
||||
*/
|
||||
public function get_auth_scope();
|
||||
|
||||
/**
|
||||
* Returns an array containing the service credentials belonging to requested
|
||||
* service.
|
||||
*
|
||||
* @return array An array containing the 'key' and the 'secret' of the
|
||||
* service in the form:
|
||||
* array(
|
||||
* 'key' => string
|
||||
* 'secret' => string
|
||||
* )
|
||||
*/
|
||||
public function get_service_credentials();
|
||||
|
||||
/**
|
||||
* Returns the results of the authentication in json format
|
||||
*
|
||||
* @throws \phpbb\auth\provider\oauth\service\exception
|
||||
* @return string The unique identifier returned by the service provider
|
||||
* that is used to authenticate the user with phpBB.
|
||||
*/
|
||||
public function perform_auth_login();
|
||||
|
||||
/**
|
||||
* Returns the results of the authentication in json format
|
||||
* Use this function when the user already has an access token
|
||||
*
|
||||
* @throws \phpbb\auth\provider\oauth\service\exception
|
||||
* @return string The unique identifier returned by the service provider
|
||||
* that is used to authenticate the user with phpBB.
|
||||
*/
|
||||
public function perform_token_auth();
|
||||
|
||||
/**
|
||||
* Returns the class of external library service provider that has to be used.
|
||||
*
|
||||
* @return string If the string is a class, it will register the provided string as a class,
|
||||
* which later will be generated as the OAuth external service provider.
|
||||
* If the string is not a class, it will use this string,
|
||||
* trying to generate a service for the version 2 and 1 respectively:
|
||||
* \OAuth\OAuth2\Service\<string>
|
||||
* If the string is empty, it will default to OAuth's standard service classes,
|
||||
* trying to generate a service for the version 2 and 1 respectively:
|
||||
* \OAuth\OAuth2\Service\Facebook
|
||||
*/
|
||||
public function get_external_service_class();
|
||||
|
||||
/**
|
||||
* Returns the external library service provider once it has been set
|
||||
*/
|
||||
public function get_external_service_provider();
|
||||
|
||||
/**
|
||||
* Sets the external library service provider
|
||||
*
|
||||
* @param \OAuth\Common\Service\ServiceInterface $service_provider
|
||||
*/
|
||||
public function set_external_service_provider(\OAuth\Common\Service\ServiceInterface $service_provider);
|
||||
}
|
||||
111
install/update/new/phpbb/auth/provider/oauth/service/twitter.php
Normal file
111
install/update/new/phpbb/auth/provider/oauth/service/twitter.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth\service;
|
||||
|
||||
/**
|
||||
* Twitter OAuth service
|
||||
*/
|
||||
class twitter extends base
|
||||
{
|
||||
/** @var \phpbb\config\config */
|
||||
protected $config;
|
||||
|
||||
/** @var \phpbb\request\request_interface */
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\request\request_interface $request Request object
|
||||
*/
|
||||
public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_service_credentials()
|
||||
{
|
||||
return [
|
||||
'key' => $this->config['auth_oauth_twitter_key'],
|
||||
'secret' => $this->config['auth_oauth_twitter_secret'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_auth_login()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth1\Service\Twitter))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
$storage = $this->service_provider->getStorage();
|
||||
|
||||
try
|
||||
{
|
||||
/** @var \OAuth\OAuth1\Token\TokenInterface $token */
|
||||
$token = $storage->retrieveAccessToken('Twitter');
|
||||
}
|
||||
catch (\OAuth\Common\Storage\Exception\TokenNotFoundException $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
$secret = $token->getRequestTokenSecret();
|
||||
|
||||
try
|
||||
{
|
||||
// This was a callback request, get the token
|
||||
$this->service_provider->requestAccessToken(
|
||||
$this->request->variable('oauth_token', ''),
|
||||
$this->request->variable('oauth_verifier', ''),
|
||||
$secret
|
||||
);
|
||||
}
|
||||
catch (\OAuth\Common\Http\Exception\TokenResponseException $e)
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_REQUEST');
|
||||
}
|
||||
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('account/verify_credentials.json'), true);
|
||||
|
||||
// Return the unique identifier
|
||||
return $result['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function perform_token_auth()
|
||||
{
|
||||
if (!($this->service_provider instanceof \OAuth\OAuth1\Service\Twitter))
|
||||
{
|
||||
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
|
||||
}
|
||||
|
||||
// Send a request with it
|
||||
$result = (array) json_decode($this->service_provider->request('account/verify_credentials.json'), true);
|
||||
|
||||
// Return the unique identifier
|
||||
return $result['id'];
|
||||
}
|
||||
}
|
||||
620
install/update/new/phpbb/auth/provider/oauth/token_storage.php
Normal file
620
install/update/new/phpbb/auth/provider/oauth/token_storage.php
Normal file
@@ -0,0 +1,620 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider\oauth;
|
||||
|
||||
use OAuth\OAuth1\Token\StdOAuth1Token;
|
||||
use OAuth\Common\Token\TokenInterface;
|
||||
use OAuth\Common\Storage\TokenStorageInterface;
|
||||
use OAuth\Common\Storage\Exception\TokenNotFoundException;
|
||||
use OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException;
|
||||
|
||||
/**
|
||||
* OAuth storage wrapper for phpBB's cache
|
||||
*/
|
||||
class token_storage implements TokenStorageInterface
|
||||
{
|
||||
/** @var \phpbb\db\driver\driver_interface */
|
||||
protected $db;
|
||||
|
||||
/** @var \phpbb\user */
|
||||
protected $user;
|
||||
|
||||
/** @var string OAuth table: token storage */
|
||||
protected $oauth_token_table;
|
||||
|
||||
/** @var string OAuth table: state */
|
||||
protected $oauth_state_table;
|
||||
|
||||
/** @var TokenInterface OAuth token */
|
||||
protected $cachedToken;
|
||||
|
||||
/** @var string OAuth state */
|
||||
protected $cachedState;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \phpbb\db\driver\driver_interface $db Database object
|
||||
* @param \phpbb\user $user User object
|
||||
* @param string $oauth_token_table OAuth table: token storage
|
||||
* @param string $oauth_state_table OAuth table: state
|
||||
*/
|
||||
public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, $oauth_token_table, $oauth_state_table)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->user = $user;
|
||||
|
||||
$this->oauth_token_table = $oauth_token_table;
|
||||
$this->oauth_state_table = $oauth_state_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function retrieveAccessToken($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedToken instanceof TokenInterface)
|
||||
{
|
||||
return $this->cachedToken;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'user_id' => (int) $this->user->data['user_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$data['session_id'] = $this->user->data['session_id'];
|
||||
}
|
||||
|
||||
return $this->_retrieve_access_token($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function storeAccessToken($service, TokenInterface $token)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
$this->cachedToken = $token;
|
||||
|
||||
$data = [
|
||||
'oauth_token' => $this->json_encode_token($token),
|
||||
];
|
||||
|
||||
$sql = 'UPDATE ' . $this->oauth_token_table . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $data) . '
|
||||
WHERE user_id = ' . (int) $this->user->data['user_id'] . "
|
||||
AND provider = '" . $this->db->sql_escape($service) . "'";
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
|
||||
}
|
||||
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
if (!$this->db->sql_affectedrows())
|
||||
{
|
||||
$data = [
|
||||
'user_id' => (int) $this->user->data['user_id'],
|
||||
'provider' => $service,
|
||||
'oauth_token' => $this->json_encode_token($token),
|
||||
'session_id' => $this->user->data['session_id'],
|
||||
];
|
||||
|
||||
$sql = 'INSERT INTO ' . $this->oauth_token_table . $this->db->sql_build_array('INSERT', $data);
|
||||
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasAccessToken($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedToken)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'user_id' => (int) $this->user->data['user_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$data['session_id'] = $this->user->data['session_id'];
|
||||
}
|
||||
|
||||
return $this->has_access_token($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearToken($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
$this->cachedToken = null;
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->oauth_token_table . '
|
||||
WHERE user_id = ' . (int) $this->user->data['user_id'] . "
|
||||
AND provider = '" . $this->db->sql_escape($service) . "'";
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
|
||||
}
|
||||
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearAllTokens()
|
||||
{
|
||||
$this->cachedToken = null;
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->oauth_token_table . '
|
||||
WHERE user_id = ' . (int) $this->user->data['user_id'];
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
|
||||
}
|
||||
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function storeAuthorizationState($service, $state)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
$this->cachedState = $state;
|
||||
|
||||
$data = [
|
||||
'user_id' => (int) $this->user->data['user_id'],
|
||||
'provider' => $service,
|
||||
'oauth_state' => $state,
|
||||
'session_id' => $this->user->data['session_id'],
|
||||
];
|
||||
|
||||
$sql = 'INSERT INTO ' . $this->oauth_state_table . ' ' . $this->db->sql_build_array('INSERT', $data);
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasAuthorizationState($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedState)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'user_id' => (int) $this->user->data['user_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$data['session_id'] = $this->user->data['session_id'];
|
||||
}
|
||||
|
||||
return (bool) $this->get_state_row($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function retrieveAuthorizationState($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedState)
|
||||
{
|
||||
return $this->cachedState;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'user_id' => (int) $this->user->data['user_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$data['session_id'] = $this->user->data['session_id'];
|
||||
}
|
||||
|
||||
return $this->get_state_row($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearAuthorizationState($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
$this->cachedState = null;
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->oauth_state_table . '
|
||||
WHERE user_id = ' . (int) $this->user->data['user_id'] . "
|
||||
AND provider = '" . $this->db->sql_escape($service) . "'";
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
|
||||
}
|
||||
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearAllAuthorizationStates()
|
||||
{
|
||||
$this->cachedState = null;
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->oauth_state_table . '
|
||||
WHERE user_id = ' . (int) $this->user->data['user_id'];
|
||||
|
||||
if ((int) $this->user->data['user_id'] === ANONYMOUS)
|
||||
{
|
||||
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
|
||||
}
|
||||
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user_id field in the database associated with the token.
|
||||
*
|
||||
* @param int $user_id The user identifier
|
||||
* @return void
|
||||
*/
|
||||
public function set_user_id($user_id)
|
||||
{
|
||||
if (!$this->cachedToken)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'user_id' => (int) $user_id,
|
||||
];
|
||||
|
||||
$sql = 'UPDATE ' . $this->oauth_token_table . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $data) . '
|
||||
WHERE user_id = ' . (int) $this->user->data['user_id'] . "
|
||||
AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if an access token exists solely by the session_id of the user.
|
||||
*
|
||||
* @param string $service The OAuth service name
|
||||
* @return bool true if the user's access token exists,
|
||||
* false if the user's access token does not exist
|
||||
*/
|
||||
public function has_access_token_by_session($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedToken)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'session_id' => $this->user->data['session_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
return $this->has_access_token($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a state exists solely by the session_id of the user.
|
||||
*
|
||||
* @param string $service The OAuth service name
|
||||
* @return bool true if the user's state exists,
|
||||
* false if the user's state does not exist
|
||||
*/
|
||||
public function has_state_by_session($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedState)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'session_id' => $this->user->data['session_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
return (bool) $this->get_state_row($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the query for has access token functions.
|
||||
*
|
||||
* @param array $data The SQL WHERE data
|
||||
* @return bool true if the user's access token exists,
|
||||
* false if the user's access token does not exist
|
||||
*/
|
||||
protected function has_access_token($data)
|
||||
{
|
||||
return (bool) $this->get_access_token_row($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the query for retrieving access token functions by session.
|
||||
* Also checks if the token is a valid token.
|
||||
*
|
||||
* @param string $service The OAuth service provider name
|
||||
* @return TokenInterface
|
||||
* @throws TokenNotFoundException
|
||||
*/
|
||||
public function retrieve_access_token_by_session($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedToken instanceof TokenInterface)
|
||||
{
|
||||
return $this->cachedToken;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'session_id' => $this->user->data['session_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
return $this->_retrieve_access_token($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the query for retrieving state functions by session.
|
||||
*
|
||||
* @param string $service The OAuth service provider name
|
||||
* @return string The OAuth state
|
||||
* @throws AuthorizationStateNotFoundException
|
||||
*/
|
||||
public function retrieve_state_by_session($service)
|
||||
{
|
||||
$service = $this->get_service_name_for_db($service);
|
||||
|
||||
if ($this->cachedState)
|
||||
{
|
||||
return $this->cachedState;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'session_id' => $this->user->data['session_id'],
|
||||
'provider' => $service,
|
||||
];
|
||||
|
||||
return $this->_retrieve_state($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the query for retrieve access token functions.
|
||||
* Also checks if the token is a valid token.
|
||||
*
|
||||
* @param array $data The SQL WHERE data
|
||||
* @return TokenInterface
|
||||
* @throws TokenNotFoundException
|
||||
*/
|
||||
protected function _retrieve_access_token($data)
|
||||
{
|
||||
$row = $this->get_access_token_row($data);
|
||||
|
||||
if (!$row)
|
||||
{
|
||||
throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_NOT_STORED');
|
||||
}
|
||||
|
||||
$token = $this->json_decode_token($row['oauth_token']);
|
||||
|
||||
// Ensure that the token was serialized/unserialized correctly
|
||||
if (!($token instanceof TokenInterface))
|
||||
{
|
||||
$this->clearToken($data['provider']);
|
||||
|
||||
throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_INCORRECTLY_STORED');
|
||||
}
|
||||
|
||||
$this->cachedToken = $token;
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the query for retrieve state functions.
|
||||
*
|
||||
* @param array $data The SQL WHERE data
|
||||
* @return string The OAuth state
|
||||
* @throws AuthorizationStateNotFoundException
|
||||
*/
|
||||
protected function _retrieve_state($data)
|
||||
{
|
||||
$row = $this->get_state_row($data);
|
||||
|
||||
if (!$row)
|
||||
{
|
||||
throw new AuthorizationStateNotFoundException();
|
||||
}
|
||||
|
||||
$this->cachedState = $row['oauth_state'];
|
||||
|
||||
return $this->cachedState;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the query for retrieving an access token.
|
||||
*
|
||||
* @param array $data The SQL WHERE data
|
||||
* @return array|false array with the OAuth token row,
|
||||
* false if the token does not exist
|
||||
*/
|
||||
protected function get_access_token_row($data)
|
||||
{
|
||||
$sql = 'SELECT oauth_token
|
||||
FROM ' . $this->oauth_token_table . '
|
||||
WHERE ' . $this->db->sql_build_array('SELECT', $data);
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the query for retrieving a state.
|
||||
*
|
||||
* @param array $data The SQL WHERE data
|
||||
* @return array|false array with the OAuth state row,
|
||||
* false if the state does not exist
|
||||
*/
|
||||
protected function get_state_row($data)
|
||||
{
|
||||
$sql = 'SELECT oauth_state
|
||||
FROM ' . $this->oauth_state_table . '
|
||||
WHERE ' . $this->db->sql_build_array('SELECT', $data);
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that JSON encodes a TokenInterface's data.
|
||||
*
|
||||
* @param TokenInterface $token
|
||||
* @return string The json encoded TokenInterface's data
|
||||
*/
|
||||
public function json_encode_token(TokenInterface $token)
|
||||
{
|
||||
$members = [
|
||||
'accessToken' => $token->getAccessToken(),
|
||||
'endOfLife' => $token->getEndOfLife(),
|
||||
'extraParams' => $token->getExtraParams(),
|
||||
'refreshToken' => $token->getRefreshToken(),
|
||||
|
||||
'token_class' => get_class($token),
|
||||
];
|
||||
|
||||
// Handle additional data needed for OAuth1 tokens
|
||||
if ($token instanceof StdOAuth1Token)
|
||||
{
|
||||
$members['requestToken'] = $token->getRequestToken();
|
||||
$members['requestTokenSecret'] = $token->getRequestTokenSecret();
|
||||
$members['accessTokenSecret'] = $token->getAccessTokenSecret();
|
||||
}
|
||||
|
||||
return json_encode($members);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that JSON decodes a data string and creates a TokenInterface.
|
||||
*
|
||||
* @param string $json The json encoded TokenInterface's data
|
||||
* @return TokenInterface
|
||||
* @throws TokenNotFoundException
|
||||
*/
|
||||
public function json_decode_token($json)
|
||||
{
|
||||
$token_data = json_decode($json, true);
|
||||
|
||||
if ($token_data === null)
|
||||
{
|
||||
throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_INCORRECTLY_STORED');
|
||||
}
|
||||
|
||||
$token_class = $token_data['token_class'];
|
||||
$access_token = $token_data['accessToken'];
|
||||
$refresh_token = $token_data['refreshToken'];
|
||||
$endOfLife = $token_data['endOfLife'];
|
||||
$extra_params = $token_data['extraParams'];
|
||||
|
||||
/**
|
||||
* Create the token
|
||||
* @var TokenInterface $token
|
||||
*/
|
||||
$token = new $token_class($access_token, $refresh_token, TokenInterface::EOL_NEVER_EXPIRES, $extra_params);
|
||||
$token->setEndOfLife($endOfLife);
|
||||
|
||||
// Handle OAuth 1.0 specific elements
|
||||
if ($token instanceof StdOAuth1Token)
|
||||
{
|
||||
$token->setRequestToken($token_data['requestToken']);
|
||||
$token->setRequestTokenSecret($token_data['requestTokenSecret']);
|
||||
$token->setAccessTokenSecret($token_data['accessTokenSecret']);
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the service name as it must be stored in the database.
|
||||
*
|
||||
* @param string $provider The OAuth provider name
|
||||
* @return string The OAuth service name
|
||||
*/
|
||||
protected function get_service_name_for_db($provider)
|
||||
{
|
||||
// Enforce the naming convention for oauth services
|
||||
if (strpos($provider, 'auth.provider.oauth.service.') !== 0)
|
||||
{
|
||||
$provider = 'auth.provider.oauth.service.' . strtolower($provider);
|
||||
}
|
||||
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
198
install/update/new/phpbb/auth/provider/provider_interface.php
Normal file
198
install/update/new/phpbb/auth/provider/provider_interface.php
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\auth\provider;
|
||||
|
||||
/**
|
||||
* The interface authentication provider classes have to implement.
|
||||
*/
|
||||
interface provider_interface
|
||||
{
|
||||
/**
|
||||
* Checks whether the user is currently identified to the authentication
|
||||
* provider.
|
||||
* Called in acp_board while setting authentication plugins.
|
||||
* Changing to an authentication provider will not be permitted in acp_board
|
||||
* if there is an error.
|
||||
*
|
||||
* @return boolean|string False if the user is identified, otherwise an
|
||||
* error message, or null if not implemented.
|
||||
*/
|
||||
public function init();
|
||||
|
||||
/**
|
||||
* Performs login.
|
||||
*
|
||||
* @param string $username The name of the user being authenticated.
|
||||
* @param string $password The password of the user.
|
||||
* @return array An associative array of the format:
|
||||
* array(
|
||||
* 'status' => status constant
|
||||
* 'error_msg' => string
|
||||
* 'user_row' => array
|
||||
* )
|
||||
* A fourth key of the array may be present:
|
||||
* 'redirect_data' This key is only used when 'status' is
|
||||
* equal to LOGIN_SUCCESS_LINK_PROFILE and its value is an
|
||||
* associative array that is turned into GET variables on
|
||||
* the redirect url.
|
||||
*/
|
||||
public function login($username, $password);
|
||||
|
||||
/**
|
||||
* Autologin function
|
||||
*
|
||||
* @return array|null containing the user row, empty if no auto login
|
||||
* should take place, or null if not implemented.
|
||||
*/
|
||||
public function autologin();
|
||||
|
||||
/**
|
||||
* This function is used to output any required fields in the authentication
|
||||
* admin panel. It also defines any required configuration table fields.
|
||||
*
|
||||
* @return array|null Returns null if not implemented or an array of the
|
||||
* configuration fields of the provider.
|
||||
*/
|
||||
public function acp();
|
||||
|
||||
/**
|
||||
* This function updates the template with variables related to the acp
|
||||
* options with whatever configuration values are passed to it as an array.
|
||||
* It then returns the name of the acp file related to this authentication
|
||||
* provider.
|
||||
*
|
||||
* @param \phpbb\config\config $new_config Contains the new configuration values
|
||||
* that have been set in acp_board.
|
||||
* @return array|null Returns null if not implemented or an array with
|
||||
* the template file name and an array of the vars
|
||||
* that the template needs that must conform to the
|
||||
* following example:
|
||||
* array(
|
||||
* 'TEMPLATE_FILE' => string,
|
||||
* 'TEMPLATE_VARS' => array(...),
|
||||
* )
|
||||
* An optional third element may be added to this
|
||||
* array: 'BLOCK_VAR_NAME'. If this is present,
|
||||
* then its value should be a string that is used
|
||||
* to designate the name of the loop used in the
|
||||
* ACP template file. When this is present, an
|
||||
* additional key named 'BLOCK_VARS' is required.
|
||||
* This must be an array containing at least one
|
||||
* array of variables that will be assigned during
|
||||
* the loop in the template. An example of this is
|
||||
* presented below:
|
||||
* array(
|
||||
* 'BLOCK_VAR_NAME' => string,
|
||||
* 'BLOCK_VARS' => array(
|
||||
* 'KEY IS UNIMPORTANT' => array(...),
|
||||
* ),
|
||||
* 'TEMPLATE_FILE' => string,
|
||||
* 'TEMPLATE_VARS' => array(...),
|
||||
* )
|
||||
*/
|
||||
public function get_acp_template($new_config);
|
||||
|
||||
/**
|
||||
* Returns an array of data necessary to build custom elements on the login
|
||||
* form.
|
||||
*
|
||||
* @return array|null If this function is not implemented on an auth
|
||||
* provider then it returns null. If it is implemented
|
||||
* it will return an array of up to four elements of
|
||||
* which only 'TEMPLATE_FILE'. If 'BLOCK_VAR_NAME' is
|
||||
* present then 'BLOCK_VARS' must also be present in
|
||||
* the array. The fourth element 'VARS' is also
|
||||
* optional. The array, with all four elements present
|
||||
* looks like the following:
|
||||
* array(
|
||||
* 'TEMPLATE_FILE' => string,
|
||||
* 'BLOCK_VAR_NAME' => string,
|
||||
* 'BLOCK_VARS' => array(...),
|
||||
* 'VARS' => array(...),
|
||||
* )
|
||||
*/
|
||||
public function get_login_data();
|
||||
|
||||
/**
|
||||
* Performs additional actions during logout.
|
||||
*
|
||||
* @param array $data An array corresponding to
|
||||
* \phpbb\session::data
|
||||
* @param boolean $new_session True for a new session, false for no new
|
||||
* session.
|
||||
*/
|
||||
public function logout($data, $new_session);
|
||||
|
||||
/**
|
||||
* The session validation function checks whether the user is still logged
|
||||
* into phpBB.
|
||||
*
|
||||
* @param array $user
|
||||
* @return boolean true if the given user is authenticated, false if the
|
||||
* session should be closed, or null if not implemented.
|
||||
*/
|
||||
public function validate_session($user);
|
||||
|
||||
/**
|
||||
* Checks to see if $login_link_data contains all information except for the
|
||||
* user_id of an account needed to successfully link an external account to
|
||||
* a forum account.
|
||||
*
|
||||
* @param array $login_link_data Any data needed to link a phpBB account to
|
||||
* an external account.
|
||||
* @return string|null Returns a string with a language constant if there
|
||||
* is data missing or null if there is no error.
|
||||
*/
|
||||
public function login_link_has_necessary_data($login_link_data);
|
||||
|
||||
/**
|
||||
* Links an external account to a phpBB account.
|
||||
*
|
||||
* @param array $link_data Any data needed to link a phpBB account to
|
||||
* an external account.
|
||||
*/
|
||||
public function link_account(array $link_data);
|
||||
|
||||
/**
|
||||
* Returns an array of data necessary to build the ucp_auth_link page
|
||||
*
|
||||
* @param int $user_id User ID for whom the data should be retrieved.
|
||||
* defaults to 0, which is not a valid ID. The method
|
||||
* should fall back to the current user's ID in this
|
||||
* case.
|
||||
* @return array|null If this function is not implemented on an auth
|
||||
* provider then it returns null. If it is implemented
|
||||
* it will return an array of up to four elements of
|
||||
* which only 'TEMPLATE_FILE'. If 'BLOCK_VAR_NAME' is
|
||||
* present then 'BLOCK_VARS' must also be present in
|
||||
* the array. The fourth element 'VARS' is also
|
||||
* optional. The array, with all four elements present
|
||||
* looks like the following:
|
||||
* array(
|
||||
* 'TEMPLATE_FILE' => string,
|
||||
* 'BLOCK_VAR_NAME' => string,
|
||||
* 'BLOCK_VARS' => array(...),
|
||||
* 'VARS' => array(...),
|
||||
* )
|
||||
*/
|
||||
public function get_auth_link_data($user_id = 0);
|
||||
|
||||
/**
|
||||
* Unlinks an external account from a phpBB account.
|
||||
*
|
||||
* @param array $link_data Any data needed to unlink a phpBB account
|
||||
* from a phpbb account.
|
||||
*/
|
||||
public function unlink_account(array $link_data);
|
||||
}
|
||||
236
install/update/new/phpbb/avatar/driver/remote.php
Normal file
236
install/update/new/phpbb/avatar/driver/remote.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\avatar\driver;
|
||||
|
||||
/**
|
||||
* Handles avatars hosted remotely
|
||||
*/
|
||||
class remote extends \phpbb\avatar\driver\driver
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_data($row)
|
||||
{
|
||||
return array(
|
||||
'src' => $row['avatar'],
|
||||
'width' => $row['avatar_width'],
|
||||
'height' => $row['avatar_height'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare_form($request, $template, $user, $row, &$error)
|
||||
{
|
||||
$template->assign_vars(array(
|
||||
'AVATAR_REMOTE_WIDTH' => ((in_array($row['avatar_type'], array(AVATAR_REMOTE, $this->get_name(), 'remote'))) && $row['avatar_width']) ? $row['avatar_width'] : $request->variable('avatar_remote_width', ''),
|
||||
'AVATAR_REMOTE_HEIGHT' => ((in_array($row['avatar_type'], array(AVATAR_REMOTE, $this->get_name(), 'remote'))) && $row['avatar_height']) ? $row['avatar_height'] : $request->variable('avatar_remote_width', ''),
|
||||
'AVATAR_REMOTE_URL' => ((in_array($row['avatar_type'], array(AVATAR_REMOTE, $this->get_name(), 'remote'))) && $row['avatar']) ? $row['avatar'] : '',
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process_form($request, $template, $user, $row, &$error)
|
||||
{
|
||||
global $phpbb_dispatcher;
|
||||
|
||||
$url = $request->variable('avatar_remote_url', '');
|
||||
$width = $request->variable('avatar_remote_width', 0);
|
||||
$height = $request->variable('avatar_remote_height', 0);
|
||||
|
||||
if (empty($url))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!preg_match('#^(http|https|ftp)://#i', $url))
|
||||
{
|
||||
$url = 'http://' . $url;
|
||||
}
|
||||
|
||||
if (!function_exists('validate_data'))
|
||||
{
|
||||
require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
|
||||
}
|
||||
|
||||
$validate_array = validate_data(
|
||||
array(
|
||||
'url' => $url,
|
||||
),
|
||||
array(
|
||||
'url' => array('string', true, 5, 255),
|
||||
)
|
||||
);
|
||||
|
||||
$error = array_merge($error, $validate_array);
|
||||
|
||||
if (!empty($error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event to make custom validation of avatar upload
|
||||
*
|
||||
* @event core.ucp_profile_avatar_upload_validation
|
||||
* @var string url Image url
|
||||
* @var string width Image width
|
||||
* @var string height Image height
|
||||
* @var array error Error message array
|
||||
* @since 3.2.9-RC1
|
||||
*/
|
||||
$vars = array('url', 'width', 'height', 'error');
|
||||
extract($phpbb_dispatcher->trigger_event('core.ucp_profile_avatar_upload_validation', compact($vars)));
|
||||
|
||||
if (!empty($error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this url looks alright
|
||||
// Do not allow specifying the port (see RFC 3986) or IP addresses
|
||||
if (!preg_match('#^(http|https|ftp)://(?:(.*?\.)*?[a-z0-9\-]+?\.[a-z]{2,4}|(?:\d{1,3}\.){3,5}\d{1,3}):?([0-9]*?).*?\.('. implode('|', $this->allowed_extensions) . ')$#i', $url) ||
|
||||
preg_match('@^(http|https|ftp)://[^/:?#]+:[0-9]+[/:?#]@i', $url) ||
|
||||
preg_match('#^(http|https|ftp)://(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])#i', $url) ||
|
||||
preg_match('#^(http|https|ftp)://(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){0,5}(?:[\dA-F]{1,4}(?::[\dA-F]{1,4})?|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:)|(?:::))#i', $url))
|
||||
{
|
||||
$error[] = 'AVATAR_URL_INVALID';
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get image dimensions
|
||||
if (($width <= 0 || $height <= 0) && (($image_data = $this->imagesize->getImageSize($url)) === false))
|
||||
{
|
||||
$error[] = 'UNABLE_GET_IMAGE_SIZE';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($image_data) && ($image_data['width'] <= 0 || $image_data['height'] <= 0))
|
||||
{
|
||||
$error[] = 'AVATAR_NO_SIZE';
|
||||
return false;
|
||||
}
|
||||
|
||||
$width = ($width && $height) ? $width : $image_data['width'];
|
||||
$height = ($width && $height) ? $height : $image_data['height'];
|
||||
|
||||
if ($width <= 0 || $height <= 0)
|
||||
{
|
||||
$error[] = 'AVATAR_NO_SIZE';
|
||||
return false;
|
||||
}
|
||||
|
||||
$types = \phpbb\files\upload::image_types();
|
||||
$extension = strtolower(\phpbb\files\filespec::get_extension($url));
|
||||
|
||||
// Check if this is actually an image
|
||||
if ($file_stream = @fopen($url, 'r'))
|
||||
{
|
||||
// Timeout after 1 second
|
||||
stream_set_timeout($file_stream, 1);
|
||||
// read some data to ensure headers are present
|
||||
fread($file_stream, 1024);
|
||||
$meta = stream_get_meta_data($file_stream);
|
||||
|
||||
if (isset($meta['wrapper_data']['headers']) && is_array($meta['wrapper_data']['headers']))
|
||||
{
|
||||
$headers = $meta['wrapper_data']['headers'];
|
||||
}
|
||||
else if (isset($meta['wrapper_data']) && is_array($meta['wrapper_data']))
|
||||
{
|
||||
$headers = $meta['wrapper_data'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$headers = array();
|
||||
}
|
||||
|
||||
foreach ($headers as $header)
|
||||
{
|
||||
$header = preg_split('/ /', $header, 2);
|
||||
if (strtr(strtolower(trim($header[0], ':')), '_', '-') === 'content-type')
|
||||
{
|
||||
if (strpos($header[1], 'image/') !== 0)
|
||||
{
|
||||
$error[] = 'AVATAR_URL_INVALID';
|
||||
fclose($file_stream);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose($file_stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$error[] = 'AVATAR_URL_INVALID';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($image_data) && (!isset($types[$image_data['type']]) || !in_array($extension, $types[$image_data['type']])))
|
||||
{
|
||||
if (!isset($types[$image_data['type']]))
|
||||
{
|
||||
$error[] = 'UNABLE_GET_IMAGE_SIZE';
|
||||
}
|
||||
else
|
||||
{
|
||||
$error[] = array('IMAGE_FILETYPE_MISMATCH', $types[$image_data['type']][0], $extension);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->config['avatar_max_width'] || $this->config['avatar_max_height'])
|
||||
{
|
||||
if ($width > $this->config['avatar_max_width'] || $height > $this->config['avatar_max_height'])
|
||||
{
|
||||
$error[] = array('AVATAR_WRONG_SIZE', $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], $width, $height);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->config['avatar_min_width'] || $this->config['avatar_min_height'])
|
||||
{
|
||||
if ($width < $this->config['avatar_min_width'] || $height < $this->config['avatar_min_height'])
|
||||
{
|
||||
$error[] = array('AVATAR_WRONG_SIZE', $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], $width, $height);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'avatar' => $url,
|
||||
'avatar_width' => $width,
|
||||
'avatar_height' => $height,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_template_name()
|
||||
{
|
||||
return 'ucp_avatar_options_remote.html';
|
||||
}
|
||||
}
|
||||
332
install/update/new/phpbb/avatar/driver/upload.php
Normal file
332
install/update/new/phpbb/avatar/driver/upload.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\avatar\driver;
|
||||
|
||||
/**
|
||||
* Handles avatars uploaded to the board
|
||||
*/
|
||||
class upload extends \phpbb\avatar\driver\driver
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\filesystem\filesystem_interface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var \phpbb\event\dispatcher_interface
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* @var \phpbb\files\factory
|
||||
*/
|
||||
protected $files_factory;
|
||||
|
||||
/**
|
||||
* Construct a driver object
|
||||
*
|
||||
* @param \phpbb\config\config $config phpBB configuration
|
||||
* @param string $phpbb_root_path Path to the phpBB root
|
||||
* @param string $php_ext PHP file extension
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem phpBB filesystem helper
|
||||
* @param \phpbb\path_helper $path_helper phpBB path helper
|
||||
* @param \phpbb\event\dispatcher_interface $dispatcher phpBB Event dispatcher object
|
||||
* @param \phpbb\files\factory $files_factory File classes factory
|
||||
* @param \phpbb\cache\driver\driver_interface $cache Cache driver
|
||||
*/
|
||||
public function __construct(\phpbb\config\config $config, $phpbb_root_path, $php_ext, \phpbb\filesystem\filesystem_interface $filesystem, \phpbb\path_helper $path_helper, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\files\factory $files_factory, \phpbb\cache\driver\driver_interface $cache = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->filesystem = $filesystem;
|
||||
$this->path_helper = $path_helper;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->files_factory = $files_factory;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_data($row)
|
||||
{
|
||||
$root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $this->path_helper->get_web_root_path();
|
||||
|
||||
return array(
|
||||
'src' => $root_path . 'download/file.' . $this->php_ext . '?avatar=' . $row['avatar'],
|
||||
'width' => $row['avatar_width'],
|
||||
'height' => $row['avatar_height'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare_form($request, $template, $user, $row, &$error)
|
||||
{
|
||||
if (!$this->can_upload())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$template->assign_vars(array(
|
||||
'S_UPLOAD_AVATAR_URL' => ($this->config['allow_avatar_remote_upload']) ? true : false,
|
||||
'AVATAR_UPLOAD_SIZE' => $this->config['avatar_filesize'],
|
||||
'AVATAR_ALLOWED_EXTENSIONS' => implode(',', preg_replace('/^/', '.', $this->allowed_extensions)),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process_form($request, $template, $user, $row, &$error)
|
||||
{
|
||||
if (!$this->can_upload())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var \phpbb\files\upload $upload */
|
||||
$upload = $this->files_factory->get('upload')
|
||||
->set_error_prefix('AVATAR_')
|
||||
->set_allowed_extensions($this->allowed_extensions)
|
||||
->set_max_filesize($this->config['avatar_filesize'])
|
||||
->set_allowed_dimensions(
|
||||
$this->config['avatar_min_width'],
|
||||
$this->config['avatar_min_height'],
|
||||
$this->config['avatar_max_width'],
|
||||
$this->config['avatar_max_height'])
|
||||
->set_disallowed_content((isset($this->config['mime_triggers']) ? explode('|', $this->config['mime_triggers']) : false));
|
||||
|
||||
$url = $request->variable('avatar_upload_url', '');
|
||||
$upload_file = $request->file('avatar_upload_file');
|
||||
|
||||
if (!empty($upload_file['name']))
|
||||
{
|
||||
$file = $upload->handle_upload('files.types.form', 'avatar_upload_file');
|
||||
}
|
||||
else if (!empty($this->config['allow_avatar_remote_upload']) && !empty($url))
|
||||
{
|
||||
if (!preg_match('#^(http|https|ftp)://#i', $url))
|
||||
{
|
||||
$url = 'http://' . $url;
|
||||
}
|
||||
|
||||
if (!function_exists('validate_data'))
|
||||
{
|
||||
require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
|
||||
}
|
||||
|
||||
$validate_array = validate_data(
|
||||
array(
|
||||
'url' => $url,
|
||||
),
|
||||
array(
|
||||
'url' => array('string', true, 5, 255),
|
||||
)
|
||||
);
|
||||
|
||||
$error = array_merge($error, $validate_array);
|
||||
|
||||
if (!empty($error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do not allow specifying the port (see RFC 3986) or IP addresses
|
||||
// remote_upload() will do its own check for allowed filetypes
|
||||
if (!preg_match('#^(http|https|ftp)://(?:(.*?\.)*?[a-z0-9\-]+?\.[a-z]{2,4}|(?:\d{1,3}\.){3,5}\d{1,3}):?([0-9]*?).*?\.('. implode('|', $this->allowed_extensions) . ')$#i', $url) ||
|
||||
preg_match('@^(http|https|ftp)://[^/:?#]+:[0-9]+[/:?#]@i', $url) ||
|
||||
preg_match('#^(http|https|ftp)://(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])#i', $url) ||
|
||||
preg_match('#^(http|https|ftp)://(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){0,5}(?:[\dA-F]{1,4}(?::[\dA-F]{1,4})?|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:)|(?:::))#i', $url))
|
||||
{
|
||||
$error[] = 'AVATAR_URL_INVALID';
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $upload->handle_upload('files.types.remote', $url);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$prefix = $this->config['avatar_salt'] . '_';
|
||||
$file->clean_filename('avatar', $prefix, $row['id']);
|
||||
|
||||
// If there was an error during upload, then abort operation
|
||||
if (count($file->error))
|
||||
{
|
||||
$file->remove();
|
||||
$error = $file->error;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate new destination
|
||||
$destination = $this->config['avatar_path'];
|
||||
|
||||
// Adjust destination path (no trailing slash)
|
||||
if (substr($destination, -1, 1) == '/' || substr($destination, -1, 1) == '\\')
|
||||
{
|
||||
$destination = substr($destination, 0, -1);
|
||||
}
|
||||
|
||||
$destination = str_replace(array('../', '..\\', './', '.\\'), '', $destination);
|
||||
if ($destination && ($destination[0] == '/' || $destination[0] == "\\"))
|
||||
{
|
||||
$destination = '';
|
||||
}
|
||||
|
||||
$filedata = array(
|
||||
'filename' => $file->get('filename'),
|
||||
'filesize' => $file->get('filesize'),
|
||||
'mimetype' => $file->get('mimetype'),
|
||||
'extension' => $file->get('extension'),
|
||||
'physical_filename' => $file->get('realname'),
|
||||
'real_filename' => $file->get('uploadname'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Before moving new file in place (and eventually overwriting the existing avatar with the newly uploaded avatar)
|
||||
*
|
||||
* @event core.avatar_driver_upload_move_file_before
|
||||
* @var array filedata Array containing uploaded file data
|
||||
* @var \phpbb\files\filespec file Instance of filespec class
|
||||
* @var string destination Destination directory where the file is going to be moved
|
||||
* @var string prefix Prefix for the avatar filename
|
||||
* @var array row Array with avatar row data
|
||||
* @var array error Array of errors, if filled in by this event file will not be moved
|
||||
* @since 3.1.6-RC1
|
||||
* @changed 3.1.9-RC1 Added filedata
|
||||
* @changed 3.2.3-RC1 Added file
|
||||
*/
|
||||
$vars = array(
|
||||
'filedata',
|
||||
'file',
|
||||
'destination',
|
||||
'prefix',
|
||||
'row',
|
||||
'error',
|
||||
);
|
||||
extract($this->dispatcher->trigger_event('core.avatar_driver_upload_move_file_before', compact($vars)));
|
||||
|
||||
unset($filedata);
|
||||
|
||||
if (!count($error))
|
||||
{
|
||||
// Move file and overwrite any existing image
|
||||
$file->move_file($destination, true);
|
||||
}
|
||||
|
||||
// If there was an error during move, then clean up leftovers
|
||||
$error = array_merge($error, $file->error);
|
||||
if (count($error))
|
||||
{
|
||||
$file->remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete current avatar if not overwritten
|
||||
$ext = substr(strrchr($row['avatar'], '.'), 1);
|
||||
if ($ext && $ext !== $file->get('extension'))
|
||||
{
|
||||
$this->delete($row);
|
||||
}
|
||||
|
||||
return array(
|
||||
'avatar' => $row['id'] . '_' . time() . '.' . $file->get('extension'),
|
||||
'avatar_width' => $file->get('width'),
|
||||
'avatar_height' => $file->get('height'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare_form_acp($user)
|
||||
{
|
||||
return array(
|
||||
'allow_avatar_remote_upload'=> array('lang' => 'ALLOW_REMOTE_UPLOAD', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
|
||||
'avatar_filesize' => array('lang' => 'MAX_FILESIZE', 'validate' => 'int:0', 'type' => 'number:0', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']),
|
||||
'avatar_path' => array('lang' => 'AVATAR_STORAGE_PATH', 'validate' => 'rpath', 'type' => 'text:20:255', 'explain' => true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($row)
|
||||
{
|
||||
|
||||
$error = array();
|
||||
$destination = $this->config['avatar_path'];
|
||||
$prefix = $this->config['avatar_salt'] . '_';
|
||||
$ext = substr(strrchr($row['avatar'], '.'), 1);
|
||||
$filename = $this->phpbb_root_path . $destination . '/' . $prefix . $row['id'] . '.' . $ext;
|
||||
|
||||
/**
|
||||
* Before deleting an existing avatar
|
||||
*
|
||||
* @event core.avatar_driver_upload_delete_before
|
||||
* @var string destination Destination directory where the file is going to be deleted
|
||||
* @var string prefix Prefix for the avatar filename
|
||||
* @var array row Array with avatar row data
|
||||
* @var array error Array of errors, if filled in by this event file will not be deleted
|
||||
* @since 3.1.6-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'destination',
|
||||
'prefix',
|
||||
'row',
|
||||
'error',
|
||||
);
|
||||
extract($this->dispatcher->trigger_event('core.avatar_driver_upload_delete_before', compact($vars)));
|
||||
|
||||
if (!count($error) && $this->filesystem->exists($filename))
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->filesystem->remove($filename);
|
||||
return true;
|
||||
}
|
||||
catch (\phpbb\filesystem\exception\filesystem_exception $e)
|
||||
{
|
||||
// Fail is covered by return statement below
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_template_name()
|
||||
{
|
||||
return 'ucp_avatar_options_upload.html';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is able to upload an avatar
|
||||
*
|
||||
* @return bool True if user can upload, false if not
|
||||
*/
|
||||
protected function can_upload()
|
||||
{
|
||||
return ($this->filesystem->exists($this->phpbb_root_path . $this->config['avatar_path']) && $this->filesystem->is_writable($this->phpbb_root_path . $this->config['avatar_path']) && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on'));
|
||||
}
|
||||
}
|
||||
148
install/update/new/phpbb/cache/driver/memcached.php
vendored
Normal file
148
install/update/new/phpbb/cache/driver/memcached.php
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\cache\driver;
|
||||
|
||||
if (!defined('PHPBB_ACM_MEMCACHED_PORT'))
|
||||
{
|
||||
define('PHPBB_ACM_MEMCACHED_PORT', 11211);
|
||||
}
|
||||
|
||||
if (!defined('PHPBB_ACM_MEMCACHED_COMPRESS'))
|
||||
{
|
||||
define('PHPBB_ACM_MEMCACHED_COMPRESS', true);
|
||||
}
|
||||
|
||||
if (!defined('PHPBB_ACM_MEMCACHED_HOST'))
|
||||
{
|
||||
define('PHPBB_ACM_MEMCACHED_HOST', 'localhost');
|
||||
}
|
||||
|
||||
if (!defined('PHPBB_ACM_MEMCACHED'))
|
||||
{
|
||||
//can define multiple servers with host1/port1,host2/port2 format
|
||||
define('PHPBB_ACM_MEMCACHED', PHPBB_ACM_MEMCACHED_HOST . '/' . PHPBB_ACM_MEMCACHED_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* ACM for Memcached
|
||||
*/
|
||||
class memcached extends \phpbb\cache\driver\memory
|
||||
{
|
||||
/** @var string Extension to use */
|
||||
protected $extension = 'memcached';
|
||||
|
||||
/** @var \Memcached Memcached class */
|
||||
protected $memcached;
|
||||
|
||||
/** @var int Flags */
|
||||
protected $flags = 0;
|
||||
|
||||
/**
|
||||
* Memcached constructor
|
||||
*
|
||||
* @param string $memcached_servers Memcached servers string (optional)
|
||||
*/
|
||||
public function __construct($memcached_servers = '')
|
||||
{
|
||||
// Call the parent constructor
|
||||
parent::__construct();
|
||||
|
||||
$memcached_servers = $memcached_servers ?: PHPBB_ACM_MEMCACHED;
|
||||
|
||||
$this->memcached = new \Memcached();
|
||||
$this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
||||
// Memcached defaults to using compression, disable if we don't want
|
||||
// to use it
|
||||
if (!PHPBB_ACM_MEMCACHED_COMPRESS)
|
||||
{
|
||||
$this->memcached->setOption(\Memcached::OPT_COMPRESSION, false);
|
||||
}
|
||||
|
||||
$server_list = [];
|
||||
foreach (explode(',', $memcached_servers) as $u)
|
||||
{
|
||||
if (preg_match('#(.*)/(\d+)#', $u, $parts))
|
||||
{
|
||||
$server_list[] = [trim($parts[1]), (int) trim($parts[2])];
|
||||
}
|
||||
}
|
||||
|
||||
$this->memcached->addServers($server_list);
|
||||
|
||||
if (empty($server_list) || empty($this->memcached->getStats()))
|
||||
{
|
||||
trigger_error('Could not connect to memcached server(s).');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function unload()
|
||||
{
|
||||
parent::unload();
|
||||
|
||||
unset($this->memcached);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$this->memcached->flush();
|
||||
|
||||
parent::purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the cache
|
||||
*
|
||||
* @param string $var Cache key
|
||||
*
|
||||
* @return mixed Cached data
|
||||
*/
|
||||
protected function _read($var)
|
||||
{
|
||||
return $this->memcached->get($this->key_prefix . $var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store data in the cache
|
||||
*
|
||||
* @param string $var Cache key
|
||||
* @param mixed $data Data to store
|
||||
* @param int $ttl Time-to-live of cached data
|
||||
* @return bool True if the operation succeeded
|
||||
*/
|
||||
protected function _write($var, $data, $ttl = 2592000)
|
||||
{
|
||||
if (!$this->memcached->replace($this->key_prefix . $var, $data, $ttl))
|
||||
{
|
||||
return $this->memcached->set($this->key_prefix . $var, $data, $ttl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the cache
|
||||
*
|
||||
* @param string $var Cache key
|
||||
* @return bool True if the operation succeeded
|
||||
*/
|
||||
protected function _delete($var)
|
||||
{
|
||||
return $this->memcached->delete($this->key_prefix . $var);
|
||||
}
|
||||
}
|
||||
282
install/update/new/phpbb/cache/driver/memory.php
vendored
Normal file
282
install/update/new/phpbb/cache/driver/memory.php
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\cache\driver;
|
||||
|
||||
/**
|
||||
* ACM Abstract Memory Class
|
||||
*/
|
||||
abstract class memory extends \phpbb\cache\driver\base
|
||||
{
|
||||
var $key_prefix;
|
||||
|
||||
/**
|
||||
* Set cache path
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
global $dbname, $table_prefix, $phpbb_container;
|
||||
|
||||
$this->cache_dir = $phpbb_container->getParameter('core.cache_dir');
|
||||
$this->key_prefix = substr(md5($dbname . $table_prefix), 0, 8) . '_';
|
||||
|
||||
if (!isset($this->extension) || !extension_loaded($this->extension))
|
||||
{
|
||||
global $acm_type;
|
||||
|
||||
trigger_error("Could not find required extension [{$this->extension}] for the ACM module $acm_type.", E_USER_ERROR);
|
||||
}
|
||||
|
||||
if (isset($this->function) && !function_exists($this->function))
|
||||
{
|
||||
global $acm_type;
|
||||
|
||||
trigger_error("The required function [{$this->function}] is not available for the ACM module $acm_type.", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function load()
|
||||
{
|
||||
// grab the global cache
|
||||
$data = $this->_read('global');
|
||||
|
||||
if ($data !== false)
|
||||
{
|
||||
$this->vars = $data;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function save()
|
||||
{
|
||||
if (!$this->is_modified)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_write('global', $this->vars, 2592000);
|
||||
|
||||
$this->is_modified = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function tidy()
|
||||
{
|
||||
global $config;
|
||||
|
||||
// cache has auto GC, no need to have any code here :)
|
||||
$config->set('cache_last_gc', time(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function get($var_name)
|
||||
{
|
||||
if ($var_name[0] == '_')
|
||||
{
|
||||
if (!$this->_exists($var_name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->_read($var_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ($this->_exists($var_name)) ? $this->vars[$var_name] : false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function put($var_name, $var, $ttl = 2592000)
|
||||
{
|
||||
if ($var_name[0] == '_')
|
||||
{
|
||||
$this->_write($var_name, $var, $ttl);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->vars[$var_name] = $var;
|
||||
$this->is_modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function destroy($var_name, $table = '')
|
||||
{
|
||||
if ($var_name == 'sql' && !empty($table))
|
||||
{
|
||||
if (!is_array($table))
|
||||
{
|
||||
$table = array($table);
|
||||
}
|
||||
|
||||
foreach ($table as $table_name)
|
||||
{
|
||||
// gives us the md5s that we want
|
||||
$temp = $this->_read('sql_' . $table_name);
|
||||
|
||||
if ($temp === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// delete each query ref
|
||||
foreach ($temp as $md5_id => $void)
|
||||
{
|
||||
$this->_delete('sql_' . $md5_id);
|
||||
}
|
||||
|
||||
// delete the table ref
|
||||
$this->_delete('sql_' . $table_name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->_exists($var_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ($var_name[0] == '_')
|
||||
{
|
||||
$this->_delete($var_name);
|
||||
}
|
||||
else if (isset($this->vars[$var_name]))
|
||||
{
|
||||
$this->is_modified = true;
|
||||
unset($this->vars[$var_name]);
|
||||
|
||||
// We save here to let the following cache hits succeed
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function _exists($var_name)
|
||||
{
|
||||
if ($var_name[0] == '_')
|
||||
{
|
||||
return $this->_isset($var_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!count($this->vars))
|
||||
{
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return isset($this->vars[$var_name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl)
|
||||
{
|
||||
// Remove extra spaces and tabs
|
||||
$query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
|
||||
$query_id = md5($query);
|
||||
|
||||
// determine which tables this query belongs to
|
||||
// Some queries use backticks, namely the get_database_size() query
|
||||
// don't check for conformity, the SQL would error and not reach here.
|
||||
if (!preg_match_all('/(?:FROM \\(?(`?\\w+`?(?: \\w+)?(?:, ?`?\\w+`?(?: \\w+)?)*)\\)?)|(?:JOIN (`?\\w+`?(?: \\w+)?))/', $query, $regs, PREG_SET_ORDER))
|
||||
{
|
||||
// Bail out if the match fails.
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
$tables = array();
|
||||
foreach ($regs as $match)
|
||||
{
|
||||
if ($match[0][0] == 'F')
|
||||
{
|
||||
$tables = array_merge($tables, array_map('trim', explode(',', $match[1])));
|
||||
}
|
||||
else
|
||||
{
|
||||
$tables[] = $match[2];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tables as $table_name)
|
||||
{
|
||||
// Remove backticks
|
||||
$table_name = ($table_name[0] == '`') ? substr($table_name, 1, -1) : $table_name;
|
||||
|
||||
if (($pos = strpos($table_name, ' ')) !== false)
|
||||
{
|
||||
$table_name = substr($table_name, 0, $pos);
|
||||
}
|
||||
|
||||
$temp = $this->_read('sql_' . $table_name);
|
||||
|
||||
if ($temp === false)
|
||||
{
|
||||
$temp = array();
|
||||
}
|
||||
|
||||
$temp[$query_id] = true;
|
||||
|
||||
// This must never expire
|
||||
$this->_write('sql_' . $table_name, $temp, 0);
|
||||
}
|
||||
|
||||
// store them in the right place
|
||||
$this->sql_rowset[$query_id] = array();
|
||||
$this->sql_row_pointer[$query_id] = 0;
|
||||
|
||||
while ($row = $db->sql_fetchrow($query_result))
|
||||
{
|
||||
$this->sql_rowset[$query_id][] = $row;
|
||||
}
|
||||
$db->sql_freeresult($query_result);
|
||||
|
||||
$this->_write('sql_' . $query_id, $this->sql_rowset[$query_id], $ttl);
|
||||
|
||||
return $query_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a cache var exists
|
||||
*
|
||||
* @access protected
|
||||
* @param string $var Cache key
|
||||
* @return bool True if it exists, otherwise false
|
||||
*/
|
||||
function _isset($var)
|
||||
{
|
||||
// Most caches don't need to check
|
||||
return true;
|
||||
}
|
||||
}
|
||||
386
install/update/new/phpbb/captcha/non_gd.php
Normal file
386
install/update/new/phpbb/captcha/non_gd.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\captcha;
|
||||
|
||||
/**
|
||||
* Main non-gd captcha class
|
||||
* @ignore
|
||||
*/
|
||||
class non_gd
|
||||
{
|
||||
var $filtered_pngs;
|
||||
var $width = 320;
|
||||
var $height = 50;
|
||||
|
||||
/**
|
||||
* Define filtered pngs on init
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
// If we can we will generate a single filtered png, we avoid nastiness via emulation of some Zlib stuff
|
||||
$this->define_filtered_pngs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the image containing $code with a seed of $seed
|
||||
*/
|
||||
function execute($code, $seed)
|
||||
{
|
||||
$img_height = $this->height - 10;
|
||||
$img_width = 0;
|
||||
|
||||
mt_srand($seed);
|
||||
|
||||
$char_widths = $hold_chars = array();
|
||||
$code_len = strlen($code);
|
||||
|
||||
for ($i = 0; $i < $code_len; $i++)
|
||||
{
|
||||
$char = $code[$i];
|
||||
|
||||
$width = mt_rand(0, 4);
|
||||
$raw_width = $this->filtered_pngs[$char]['width'];
|
||||
$char_widths[$i] = $width;
|
||||
$img_width += $raw_width - $width;
|
||||
|
||||
// Split the char into chunks of $raw_width + 1 length
|
||||
if (empty($hold_chars[$char]))
|
||||
{
|
||||
$hold_chars[$char] = str_split(base64_decode($this->filtered_pngs[$char]['data']), $raw_width + 1);
|
||||
}
|
||||
}
|
||||
|
||||
$offset_x = mt_rand(0, $this->width - $img_width);
|
||||
$offset_y = mt_rand(0, $this->height - $img_height);
|
||||
|
||||
$image = '';
|
||||
for ($i = 0; $i < $this->height; $i++)
|
||||
{
|
||||
$image .= chr(0);
|
||||
|
||||
if ($i > $offset_y && $i < $offset_y + $img_height)
|
||||
{
|
||||
for ($j = 0; $j < $offset_x; $j++)
|
||||
{
|
||||
$image .= chr(mt_rand(140, 255));
|
||||
}
|
||||
|
||||
for ($j = 0; $j < $code_len; $j++)
|
||||
{
|
||||
$image .= $this->randomise(substr($hold_chars[$code[$j]][$i - $offset_y - 1], 1), $char_widths[$j]);
|
||||
}
|
||||
|
||||
for ($j = $offset_x + $img_width; $j < $this->width; $j++)
|
||||
{
|
||||
$image .= chr(mt_rand(140, 255));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ($j = 0; $j < $this->width; $j++)
|
||||
{
|
||||
$image .= chr(mt_rand(140, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($hold_chars);
|
||||
|
||||
$image = $this->create_png($image, $this->width, $this->height);
|
||||
|
||||
// Output image
|
||||
header('Content-Type: image/png');
|
||||
header('Cache-control: no-cache, no-store');
|
||||
echo $image;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is designed to randomise the pixels of the image data within
|
||||
* certain limits so as to keep it readable. It also varies the image
|
||||
* width a little
|
||||
*/
|
||||
function randomise($scanline, $width)
|
||||
{
|
||||
$new_line = '';
|
||||
|
||||
$end = strlen($scanline) - ceil($width/2);
|
||||
for ($i = (int) floor($width / 2); $i < $end; $i++)
|
||||
{
|
||||
$pixel = ord($scanline[$i]);
|
||||
|
||||
if ($pixel < 190)
|
||||
{
|
||||
$new_line .= chr(mt_rand(0, 205));
|
||||
}
|
||||
else if ($pixel > 190)
|
||||
{
|
||||
$new_line .= chr(mt_rand(145, 255));
|
||||
}
|
||||
else
|
||||
{
|
||||
$new_line .= $scanline[$i];
|
||||
}
|
||||
}
|
||||
|
||||
return $new_line;
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a chunk of the given type, with the given data
|
||||
* of the given length adding the relevant crc
|
||||
*/
|
||||
function png_chunk($length, $type, $data)
|
||||
{
|
||||
$raw = $type . $data;
|
||||
|
||||
return pack('N', $length) . $raw . pack('N', crc32($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates greyscale 8bit png - The PNG spec can be found at
|
||||
* http://www.libpng.org/pub/png/spec/PNG-Contents.html we use
|
||||
* png because it's a fully recognised open standard and supported
|
||||
* by practically all modern browsers and OSs
|
||||
*/
|
||||
function create_png($raw_image, $width, $height)
|
||||
{
|
||||
// SIG
|
||||
$image = pack('C8', 137, 80, 78, 71, 13, 10, 26, 10);
|
||||
|
||||
// IHDR
|
||||
$raw = pack('N2', $width, $height);
|
||||
$raw .= pack('C5', 8, 0, 0, 0, 0);
|
||||
$image .= $this->png_chunk(13, 'IHDR', $raw);
|
||||
|
||||
// IDAT
|
||||
if (@extension_loaded('zlib'))
|
||||
{
|
||||
$raw_image = gzcompress($raw_image);
|
||||
$length = strlen($raw_image);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The total length of this image, uncompressed, is just a calculation of pixels
|
||||
$length = ($width + 1) * $height;
|
||||
|
||||
// Adler-32 hash generation
|
||||
// Note: The hash is _backwards_ so we must reverse it
|
||||
|
||||
if (@extension_loaded('hash'))
|
||||
{
|
||||
$adler_hash = strrev(hash('adler32', $raw_image, true));
|
||||
}
|
||||
else if (@extension_loaded('mhash'))
|
||||
{
|
||||
$adler_hash = strrev(mhash(MHASH_ADLER32, $raw_image));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Optimized Adler-32 loop ported from the GNU Classpath project
|
||||
$temp_length = $length;
|
||||
$s1 = 1;
|
||||
$s2 = $index = 0;
|
||||
|
||||
while ($temp_length > 0)
|
||||
{
|
||||
// We can defer the modulo operation:
|
||||
// s1 maximally grows from 65521 to 65521 + 255 * 3800
|
||||
// s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
|
||||
$substract_value = ($temp_length < 3800) ? $temp_length : 3800;
|
||||
$temp_length -= $substract_value;
|
||||
|
||||
while (--$substract_value >= 0)
|
||||
{
|
||||
$s1 += ord($raw_image[$index]);
|
||||
$s2 += $s1;
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
$s1 %= 65521;
|
||||
$s2 %= 65521;
|
||||
}
|
||||
$adler_hash = pack('N', ($s2 << 16) | $s1);
|
||||
}
|
||||
|
||||
// This is the same thing as gzcompress($raw_image, 0) but does not need zlib
|
||||
$raw_image = pack('C3v2', 0x78, 0x01, 0x01, $length, ~$length) . $raw_image . $adler_hash;
|
||||
|
||||
// The Zlib header + Adler hash make us add on 11
|
||||
$length += 11;
|
||||
}
|
||||
|
||||
// IDAT
|
||||
$image .= $this->png_chunk($length, 'IDAT', $raw_image);
|
||||
|
||||
// IEND
|
||||
$image .= $this->png_chunk(0, 'IEND', '');
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* png image data
|
||||
* Each 'data' element is base64_encoded uncompressed IDAT
|
||||
*/
|
||||
function define_filtered_pngs()
|
||||
{
|
||||
$this->filtered_pngs = array(
|
||||
'0' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////olFAkBAAAGDyA4P///M31/////////////wD////////////////0dAgAAAAAAAAAAAAEcPipFGHn////////////AP//////////////6DAAAAAAAAAAAAAAAAAALSEAN+T///////////8A//////////////xAAAAAAAAAAAAAAAAAAAAAACPA/////////////wD/////////////oAAAAAAAAAAAAAAAAAAAAAAAev//////////////AP////////////8oAAAAAAAAPNj/zDAAAAAAAABD//////////////8A////////////1AAAAAAAABjw////5BAAAAAAAADo/////////////wD///////////+QAAAAAAAAbP//////QgAAAAAAAKj/////////////AP///////////1wAAAAAAACs/////8AXAAAAAAAAcP////////////8A////////////OAAAAAAAAND////dNwAAAAAAAABI/////////////wD///////////8gAAAAAAAA4P//7koACwAAAAAAACT/////////////AP///////////wgAAAAAAAD///VqAwaPAAAAAAAAEP////////////8A////////////AAAAAAAAAP/8kQYDavUAAAAAAAAA/////////////wD///////////8AAAAAAAAA/6kNAEru/wAAAAAAAAD/////////////AP///////////wAAAAAAAADAIwA33f//AAAAAAAAAP////////////8A////////////FAAAAAAAADYAI8D///8AAAAAAAAQ/////////////wD///////////8kAAAAAAAAAA2p////5AAAAAAAACD/////////////AP///////////0gAAAAAAAAFkfz////UAAAAAAAAQP////////////8A////////////cAAAAAAAAET1/////7AAAAAAAABo/////////////wD///////////+oAAAAAAAAXfX/////sAAAAAAAAGj/////////////AAAAALgAAAAAAAAwAAAAAAAAAAAAAAD////////////oAAAAAAAACOT////oEAAAAAAAAOD/////////////AP////////////8+AAAAAAAAKMz/zDQAAAAAAAA0//////////////8A////////////7jgAAAAAAAAAAAAAAAAAAAAAAKT//////////////wD///////////VqAwIAAAAAAAAAAAAAAAAAAAA8////////////////AP//////////rQcDaVEAAAAAAAAAAAAAAAAAKOj///////////////8A///////////nblnu/IAIAAAAAAAAAAAAAFzw/////////////////wD////////////79////+iITCAAAAAgSITg////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////w==',
|
||||
'width' => 40
|
||||
),
|
||||
'1' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////8BAAAAAAAP//////////////////AP////////////////////////9sAAAAAAAA//////////////////8A////////////////////////pAAAAAAAAAD//////////////////wD//////////////////////6wEAAAAAAAAAP//////////////////AP////////////////////h4AAAAAAAAAAAA//////////////////8A//////////////////ygJAAAAAAAAAAAAAD//////////////////wD//////////////9x8HAAAAAAAAAAAAAAAAP//////////////////AP//////////////AAAAAAAAAAAAAAAAAAAA//////////////////8A//////////////8AAAAAAAAAAAAAAAAAAAD//////////////////wD//////////////wAAAAAAAAR4AAAAAAAAAP//////////////////AP//////////////AAAAAAA4zP8AAAAAAAAA//////////////////8A//////////////8AAAA4sP///wAAAAAAAAD//////////////////wD//////////////yR80P//////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'2' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////okFAkCAAABCBIfNT///////////////////8A///////////////8hAgAAAAAAAAAAAAAAFTo/////////////////wD//////////////1QAAAAAAAAAAAAAAAAAACjo////////////////AP////////////+MAAAAAAAAAAAAAAAAAAAAADj///////////////8A////////////9BAAAAAAAAAAAAAAAAAAAAAAALD//////////////wD///////////+gAAAAAAAAAHjs+KwMAAAAAAAAVP//////////////AP///////////1gAAAAAAABM/////6QAAAAAAAAU//////////////8A////////////KAAAAAAAALj/////+AAAAAAAAAD//////////////wD///////////+MfGBMOCAI8P/////wAAAAAAAACP//////////////AP///////////////////////////5wAAAAAAAAw//////////////8A///////////////////////////oFAAAAAAAAHz//////////////wD/////////////////////////6CgAAAAAAAAE3P//////////////AP///////////////////////9ggAAAAAAAAAHT///////////////8A//////////////////////+0DAAAAAAAAAA8+P///////////////wD/////////////////////gAAAAAAAAAAAKOj/////////////////AP//////////////////9FAAAAAAAAAAADzw//////////////////8A/////////////////+g4AAAAAAAAAABk/P///////////////////wD////////////////oKAAAAAAAAAAMqP//////////////////////AP//////////////6CgAAAAAAAAAMNz///////////////////////8A//////////////g4AAAAAAAAAFT0/////////////////////////wD/////////////bAAAAAAAAABU/P//////////////////////////AP///////////8wAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A////////////SAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////9AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////xAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'3' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////8sGg0FAAAACA4cLz8////////////////////AP//////////////rBgAAAAAAAAAAAAAACTA//////////////////8A/////////////3QAAAAAAAAAAAAAAAAAAASs/////////////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAjc////////////////AP//////////6AwAAAAAAAAAAAAAAAAAAAAAAGT///////////////8A//////////94AAAAAAAABJDw/8g4AAAAAAAAHP///////////////wD//////////yAAAAAAAACE/////9gAAAAAAAAA////////////////AP///////////NSwiGQ4FOT//////AAAAAAAABD///////////////8A//////////////////////////+YAAAAAAAAVP///////////////wD//////////////////////P/ggAQAAAAAAATM////////////////AP////////////////////9gAAAAAAAAAAAElP////////////////8A/////////////////////0AAAAAAAAAAHLj//////////////////wD/////////////////////OAAAAAAAAAAwkPj/////////////////AP////////////////////8gAAAAAAAAAAAAINj///////////////8A/////////////////////xAAAAAAAAAAAAAAIPD//////////////wD/////////////////////uOz/4HgEAAAAAAAAhP//////////////AP///////////////////////////3wAAAAAAAAw//////////////8A////////////////////////////6AAAAAAAAAj//////////////wD/////////////////////////////AAAAAAAAAP//////////////AP//////////tJh8YEQoDNz//////+AAAAAAAAAY//////////////8A//////////88AAAAAAAAaP//////dAAAAAAAAEz//////////////wD//////////6QAAAAAAAAAdOD/5HQAAAAAAAAApP//////////////AP///////////CgAAAAAAAAAAAAAAAAAAAAAACD4//////////////8A////////////yAQAAAAAAAAAAAAAAAAAAAAEuP///////////////wD/////////////rAQAAAAAAAAAAAAAAAAABJD/////////////////AP//////////////zDQAAAAAAAAAAAAAACTA//////////////////8A/////////////////8BwOCAAAAAUNGi0/P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'4' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////////////nAAAAAAAAAD///////////////8A/////////////////////////8AEAAAAAAAAAP///////////////wD////////////////////////gGAAAAAAAAAAA////////////////AP//////////////////////9DAAAAAAAAAAAAD///////////////8A//////////////////////9UAAAAAAAAAAAAAP///////////////wD/////////////////////hAAAAAAAAAAAAAAA////////////////AP///////////////////7QAAAAAAAAAAAAAAAD///////////////8A///////////////////UDAAAAAAUAAAAAAAAAP///////////////wD/////////////////7CQAAAAABMAAAAAAAAAA////////////////AP////////////////xEAAAAAACU/wAAAAAAAAD///////////////8A////////////////cAAAAAAAZP//AAAAAAAAAP///////////////wD//////////////6AAAAAAADz8//8AAAAAAAAA////////////////AP/////////////IBAAAAAAc6P///wAAAAAAAAD///////////////8A////////////5BgAAAAADMz/////AAAAAAAAAP///////////////wD///////////g0AAAAAACk//////8AAAAAAAAA////////////////AP//////////XAAAAAAAfP///////wAAAAAAAAD///////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP///////////////////////////wAAAAAAAAD///////////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'5' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////8AAAAAAAAAAAAAAAAAAAAAAA//////////////8A///////////////MAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////6wAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////iAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////////9kAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////0QAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////IAAAAAAAYP////////////////////////////8A//////////////wAAAAAAAB8/////////////////////////////wD/////////////3AAAAAAAAIj/////////////////////////////AP////////////+4AAAAAAAAoLRYHAAEKGTE//////////////////8A/////////////5QAAAAAAAAQAAAAAAAAAABY9P///////////////wD/////////////dAAAAAAAAAAAAAAAAAAAAAA89P//////////////AP////////////9QAAAAAAAAAAAAAAAAAAAAAABg//////////////8A/////////////zAAAAAAAAAAAAAAAAAAAAAAAADQ/////////////wD/////////////IAAAAAAAAGjY/+h4BAAAAAAAAGz/////////////AP//////////////9NS0lHSc//////90AAAAAAAALP////////////8A/////////////////////////////9QAAAAAAAAE/////////////wD//////////////////////////////wAAAAAAAAD/////////////AP/////////////////////////////8AAAAAAAAEP////////////8A////////////pIRwWEAgDOD//////8wAAAAAAAA8/////////////wD///////////9EAAAAAAAAaP//////ZAAAAAAAAHz/////////////AP///////////6QAAAAAAAAAaOD/4GQAAAAAAAAE4P////////////8A/////////////CQAAAAAAAAAAAAAAAAAAAAAAGD//////////////wD/////////////yAQAAAAAAAAAAAAAAAAAAAAc7P//////////////AP//////////////rAwAAAAAAAAAAAAAAAAAGNj///////////////8A////////////////0EAAAAAAAAAAAAAAAFTo/////////////////wD//////////////////8h4QCAAAAAcQHzU////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'6' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////+0ZCwMAAAUNGjI////////////////////AP/////////////////EMAAAAAAAAAAAAABM6P////////////////8A////////////////lAQAAAAAAAAAAAAAAAAo6P///////////////wD//////////////6wAAAAAAAAAAAAAAAAAAABI////////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAACw//////////////8A/////////////3AAAAAAAAAoxP/YPAAAAAAAAEj//////////////wD////////////4EAAAAAAACOD////YDCBAVGiAoP//////////////AP///////////7gAAAAAAABY//////////////////////////////8A////////////eAAAAAAAAJT//////////////////////////////wD///////////9MAAAAAAAAvP/IXBgABCx03P//////////////////AP///////////ygAAAAAAADcdAAAAAAAAAAEiP////////////////8A////////////FAAAAAAAAFAAAAAAAAAAAAAAcP///////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAlP//////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAQ8P////////////8A////////////AAAAAAAAAABAyP/kZAAAAAAAAACQ/////////////wD///////////8MAAAAAAAALPj/////WAAAAAAAAET/////////////AP///////////yQAAAAAAACY///////MAAAAAAAAFP////////////8A////////////SAAAAAAAAMD///////wAAAAAAAAA/////////////wD///////////9wAAAAAAAAvP///////wAAAAAAAAD/////////////AP///////////7QAAAAAAACI///////UAAAAAAAAJP////////////8A////////////+AwAAAAAACDw/////2wAAAAAAABY/////////////wD/////////////cAAAAAAAADC8/Ox4AAAAAAAAAKj/////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAAAk/P////////////8A//////////////+oAAAAAAAAAAAAAAAAAAAABLj//////////////wD///////////////+QAAAAAAAAAAAAAAAAAACQ////////////////AP////////////////+0JAAAAAAAAAAAAAAkuP////////////////8A///////////////////8sGg0FAAADCxgqPz//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'7' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAABP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAy4/////////////wD//////////////////////////+QUAAAAAAAEuP//////////////AP/////////////////////////8QAAAAAAAAKT///////////////8A/////////////////////////4wAAAAAAAB0/////////////////wD////////////////////////cCAAAAAAANPz/////////////////AP///////////////////////0QAAAAAAATY//////////////////8A//////////////////////+0AAAAAAAAeP///////////////////wD//////////////////////CQAAAAAABTw////////////////////AP////////////////////+gAAAAAAAAkP////////////////////8A/////////////////////ywAAAAAABDw/////////////////////wD///////////////////+4AAAAAAAAbP//////////////////////AP///////////////////1wAAAAAAADQ//////////////////////8A///////////////////4DAAAAAAAMP///////////////////////wD//////////////////7QAAAAAAAB8////////////////////////AP//////////////////aAAAAAAAAMj///////////////////////8A//////////////////8oAAAAAAAM/P///////////////////////wD/////////////////8AAAAAAAAET/////////////////////////AP////////////////+0AAAAAAAAcP////////////////////////8A/////////////////4wAAAAAAACY/////////////////////////wD/////////////////WAAAAAAAAMD/////////////////////////AP////////////////80AAAAAAAA4P////////////////////////8A/////////////////xAAAAAAAAD4/////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'8' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////////IdDQUAAAEIEiA1P//////////////////AP/////////////////gRAAAAAAAAAAAAAAAROD///////////////8A////////////////0BgAAAAAAAAAAAAAAAAAEMj//////////////wD///////////////AcAAAAAAAAAAAAAAAAAAAAHPD/////////////AP//////////////hAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A//////////////8sAAAAAAAAKMz/zCgAAAAAAAAs/////////////wD//////////////wAAAAAAAADM////zAAAAAAAAAD/////////////AP//////////////BAAAAAAAAP//////AAAAAAAABP////////////8A//////////////8sAAAAAAAAzP///9QAAAAAAAAw/////////////wD//////////////3wAAAAAAAAoyP/YNAAAAAAAAIT/////////////AP//////////////7BgAAAAAAAAAAAAAAAAAAAAc8P////////////8A////////////////xBgAAAAAAAAAAAAAAAAAGNj//////////////wD/////////////////tAQAAAAAAAAAAAAAAACo////////////////AP///////////////HAAAAAAAAAAAAAAAAAAAAB8//////////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB8/////////////wD/////////////wAAAAAAAAABk4P/UWAAAAAAAAATQ////////////AP////////////9UAAAAAAAAaP//////XAAAAAAAAGT///////////8A/////////////xgAAAAAAADg///////cAAAAAAAAJP///////////wD/////////////AAAAAAAAAP////////8AAAAAAAAA////////////AP////////////8AAAAAAAAA4P//////3AAAAAAAAAT///////////8A/////////////ygAAAAAAABg//////9cAAAAAAAALP///////////wD/////////////ZAAAAAAAAABY1P/cXAAAAAAAAABw////////////AP/////////////QAAAAAAAAAAAAAAAAAAAAAAAABNz///////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB0/////////////wD///////////////Q8AAAAAAAAAAAAAAAAAAAAUPz/////////////AP////////////////x4CAAAAAAAAAAAAAAAEIT8//////////////8A///////////////////smFQwGAAAABg0ZKT0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'9' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////ysYCwMAAAUNGiw/P//////////////////AP////////////////+4JAAAAAAAAAAAAAAkuP////////////////8A////////////////lAQAAAAAAAAAAAAAAAAAkP///////////////wD//////////////8AEAAAAAAAAAAAAAAAAAAAAqP//////////////AP/////////////8JAAAAAAAAAAAAAAAAAAAAAAQ7P////////////8A/////////////6wAAAAAAAAAfOz8vCwAAAAAAABw/////////////wD/////////////WAAAAAAAAHD/////7BgAAAAAAAz4////////////AP////////////8kAAAAAAAA1P//////hAAAAAAAALT///////////8A/////////////wAAAAAAAAD///////+4AAAAAAAAcP///////////wD/////////////AAAAAAAAAPz//////8AAAAAAAABI////////////AP////////////8UAAAAAAAAzP//////lAAAAAAAACT///////////8A/////////////0QAAAAAAABY//////gsAAAAAAAADP///////////wD/////////////kAAAAAAAAABw5P/IPAAAAAAAAAAA////////////AP/////////////wEAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////////+UAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////9wAAAAAAAAAAAAAFAAAAAAAAAU////////////AP////////////////+IBAAAAAAAAABw3AAAAAAAACj///////////8A///////////////////cdCwEABhcxP+8AAAAAAAATP///////////wD//////////////////////////////5AAAAAAAAB4////////////AP//////////////////////////////UAAAAAAAALj///////////8A//////////////+kgGxUQCAM2P///+AIAAAAAAAQ+P///////////wD//////////////0gAAAAAAAA42P/EKAAAAAAAAHD/////////////AP//////////////sAAAAAAAAAAAAAAAAAAAAAAQ6P////////////8A////////////////TAAAAAAAAAAAAAAAAAAAAKz//////////////wD////////////////oKAAAAAAAAAAAAAAAAASU////////////////AP/////////////////sUAAAAAAAAAAAAAAwxP////////////////8A////////////////////yHA0FAAADCxktP///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'A' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////+QAAAAAAAAAAAAAAOT/////////////////AP//////////////////kAAAAAAAAAAAAAAAkP////////////////8A//////////////////88AAAAAAAAAAAAAAA8/////////////////wD/////////////////5AAAAAAAAAAAAAAAAADk////////////////AP////////////////+QAAAAAAAAAAAAAAAAAJD///////////////8A/////////////////zwAAAAAAAAAAAAAAAAAPP///////////////wD////////////////kAAAAAAAAAAgAAAAAAAAA5P//////////////AP///////////////5AAAAAAAAAAgAAAAAAAAACQ//////////////8A////////////////PAAAAAAAAAz8HAAAAAAAADz//////////////wD//////////////+QAAAAAAAAAWP9kAAAAAAAAANz/////////////AP//////////////kAAAAAAAAACk/7wAAAAAAAAAhP////////////8A//////////////88AAAAAAAABOz//BQAAAAAAAAw/////////////wD/////////////4AAAAAAAAAA8////ZAAAAAAAAADc////////////AP////////////+EAAAAAAAAAIj///+8AAAAAAAAAIT///////////8A/////////////zAAAAAAAAAA2P////wQAAAAAAAAMP///////////wD////////////cAAAAAAAAACT//////1wAAAAAAAAA3P//////////AP///////////4QAAAAAAAAAAAAAAAAAAAAAAAAAAACE//////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAAAAAADD//////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANz/////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhP////////8A//////////8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw/////////wD/////////3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc////////AP////////+EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIT///////8A/////////zAAAAAAAAAAhP///////////2QAAAAAAAAAMP///////wD////////cAAAAAAAAAADM////////////vAAAAAAAAAAA3P//////AP///////4QAAAAAAAAAHP/////////////4DAAAAAAAAACE//////8A////////MAAAAAAAAABk//////////////9cAAAAAAAAADD//////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'B' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAEDh83P///////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAEhP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAeP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAABY////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAABT///////////8A//////////8AAAAAAAAAAP/////4zEwAAAAAAAAAAP///////////wD//////////wAAAAAAAAAA////////7AAAAAAAAAAQ////////////AP//////////AAAAAAAAAAD////////sAAAAAAAAAEj///////////8A//////////8AAAAAAAAAAP/////4zEQAAAAAAAAAtP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAFz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAiA/P////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAIjPj//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAGKz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJT///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABNz//////////wD//////////wAAAAAAAAAA///////sqCAAAAAAAAAAbP//////////AP//////////AAAAAAAAAAD/////////yAAAAAAAAAAs//////////8A//////////8AAAAAAAAAAP//////////AAAAAAAAAAT//////////wD//////////wAAAAAAAAAA/////////7wAAAAAAAAAAP//////////AP//////////AAAAAAAAAAD//////+ikGAAAAAAAAAAY//////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFT//////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsP//////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAADj///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAc6P///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAATOj/////////////AP//////////AAAAAAAAAAAAAAAAAAAEIEBkkNj///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'C' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////5JRULBAAAAgkTIDQ//////////////////8A////////////////1FAAAAAAAAAAAAAAAABAyP///////////////wD//////////////4gEAAAAAAAAAAAAAAAAAAAElP//////////////AP////////////9wAAAAAAAAAAAAAAAAAAAAAAAAlP////////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAEyP///////////wD//////////9wIAAAAAAAAAAAAAAAAAAAAAAAAAAAw////////////AP//////////WAAAAAAAAAAAWMz/8JwQAAAAAAAAAACw//////////8A/////////+wEAAAAAAAAAID//////9QMAAAAAAAAAET//////////wD/////////nAAAAAAAAAAo/P///////3wAAAAABDBspP//////////AP////////9gAAAAAAAAAIz/////////3BxQjMT0//////////////8A/////////zQAAAAAAAAAzP///////////////////////////////wD/////////GAAAAAAAAADo////////////////////////////////AP////////8AAAAAAAAAAP////////////////////////////////8A/////////wAAAAAAAAAA/////////////////////////////////wD/////////AAAAAAAAAAD/////////////////////////////////AP////////8cAAAAAAAAAOj///////////////////////////////8A/////////zgAAAAAAAAA0P/////////kIGio7P///////////////wD/////////bAAAAAAAAACg/////////5wAAAAAMHS49P//////////AP////////+oAAAAAAAAAEz/////////PAAAAAAAAAAc//////////8A//////////QIAAAAAAAAALz//////6QAAAAAAAAAAGT//////////wD//////////3AAAAAAAAAADIzo/+SEBAAAAAAAAAAAyP//////////AP//////////7BAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////rAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD/////////////fAAAAAAAAAAAAAAAAAAAAAAAAJz/////////////AP//////////////iAQAAAAAAAAAAAAAAAAAAASY//////////////8A////////////////yEAAAAAAAAAAAAAAAAA8yP///////////////wD//////////////////9yIUCwQAAAAIEB4yP//////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'D' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAADChQkOT/////////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAABGjw//////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAACDY/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAABjk////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKj//////////wD///////////8AAAAAAAAAAP///+isSAAAAAAAAAAANP//////////AP///////////wAAAAAAAAAA////////hAAAAAAAAAAA2P////////8A////////////AAAAAAAAAAD/////////MAAAAAAAAACQ/////////wD///////////8AAAAAAAAAAP////////+MAAAAAAAAAFj/////////AP///////////wAAAAAAAAAA/////////8gAAAAAAAAAMP////////8A////////////AAAAAAAAAAD/////////5AAAAAAAAAAY/////////wD///////////8AAAAAAAAAAP//////////AAAAAAAAAAD/////////AP///////////wAAAAAAAAAA//////////8AAAAAAAAAAP////////8A////////////AAAAAAAAAAD//////////wAAAAAAAAAA/////////wD///////////8AAAAAAAAAAP/////////wAAAAAAAAABD/////////AP///////////wAAAAAAAAAA/////////9QAAAAAAAAAJP////////8A////////////AAAAAAAAAAD/////////qAAAAAAAAABI/////////wD///////////8AAAAAAAAAAP////////9QAAAAAAAAAHj/////////AP///////////wAAAAAAAAAA////////uAAAAAAAAAAAvP////////8A////////////AAAAAAAAAAD////w0HwEAAAAAAAAACT8/////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAADz8//////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAY6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAKNz/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAACHT0//////////////8A////////////AAAAAAAAAAAAAAAAABg4bKj0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'E' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'F' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'G' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////MB8TCgQAAAACCA4YJzs////////////////AP///////////////JQcAAAAAAAAAAAAAAAAAAhw8P////////////8A/////////////9gwAAAAAAAAAAAAAAAAAAAAAAAk2P///////////wD////////////EDAAAAAAAAAAAAAAAAAAAAAAAAAAc7P//////////AP//////////2AwAAAAAAAAAAAAAAAAAAAAAAAAAAABY//////////8A//////////wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQ/////////wD/////////kAAAAAAAAAAAEHzQ/P/gmCAAAAAAAAAAAFz/////////AP////////wcAAAAAAAAACjg////////8CwAAAAAAAAgWP////////8A////////vAAAAAAAAAAI2P//////////yBRAcJjI8P///////////wD///////94AAAAAAAAAGD/////////////////////////////////AP///////0AAAAAAAAAAsP////////////////////////////////8A////////IAAAAAAAAADc/////////////////////////////////wD///////8AAAAAAAAAAP///////wAAAAAAAAAAAAAAAAD/////////AP///////wAAAAAAAAAA////////AAAAAAAAAAAAAAAAAP////////8A////////AAAAAAAAAAD///////8AAAAAAAAAAAAAAAAA/////////wD///////8gAAAAAAAAAOD//////wAAAAAAAAAAAAAAAAD/////////AP///////0AAAAAAAAAAtP//////AAAAAAAAAAAAAAAAAP////////8A////////cAAAAAAAAABw//////8AAAAAAAAAAAAAAAAA/////////wD///////+8AAAAAAAAABDs////////////AAAAAAAAAAD/////////AP////////wYAAAAAAAAADz0//////////AAAAAAAAAAAP////////8A/////////5AAAAAAAAAAACCY4P//3KhcCAAAAAAAAAAA/////////wD/////////+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////AP//////////xAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIP////////8A////////////rAQAAAAAAAAAAAAAAAAAAAAAAAAAAGTw/////////wD/////////////vBQAAAAAAAAAAAAAAAAAAAAAADjI////////////AP//////////////8HAQAAAAAAAAAAAAAAAAAEiw//////////////8A//////////////////iwcEAgBAAABCA4aKDk/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'H' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'I' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'J' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAj//////////////wD//////////+zMrIxwUDAQ//////wAAAAAAAAAIP//////////////AP//////////DAAAAAAAAADo////2AAAAAAAAAA0//////////////8A//////////8wAAAAAAAAAKj///+YAAAAAAAAAFj//////////////wD//////////2gAAAAAAAAAIND/yBgAAAAAAAAAkP//////////////AP//////////vAAAAAAAAAAAAAAAAAAAAAAAAADc//////////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAUP///////////////wD////////////EBAAAAAAAAAAAAAAAAAAAABjk////////////////AP////////////+sBAAAAAAAAAAAAAAAAAAY2P////////////////8A///////////////EMAAAAAAAAAAAAAAAVOj//////////////////wD/////////////////vHBAIAAAABg8fNT/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'K' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////8AAAAAAAAAAP//////////wAQAAAAAAAAAAABw////////AP///////wAAAAAAAAAA/////////9AMAAAAAAAAAAAAcP////////8A////////AAAAAAAAAAD////////cGAAAAAAAAAAAAHD//////////wD///////8AAAAAAAAAAP//////6CgAAAAAAAAAAABs////////////AP///////wAAAAAAAAAA//////Q0AAAAAAAAAAAAVPz///////////8A////////AAAAAAAAAAD////8RAAAAAAAAAAAAFT8/////////////wD///////8AAAAAAAAAAP///1gAAAAAAAAAAABU/P//////////////AP///////wAAAAAAAAAA//9wAAAAAAAAAAAASPz///////////////8A////////AAAAAAAAAAD/jAAAAAAAAAAAADz0/////////////////wD///////8AAAAAAAAAAKQAAAAAAAAAAAA89P//////////////////AP///////wAAAAAAAAAABAAAAAAAAAAAFPT///////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAApP///////////////////wD///////8AAAAAAAAAAAAAAAAAAAAAAAAU8P//////////////////AP///////wAAAAAAAAAAAAAAAAAAAAAAAABk//////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAAAADE/////////////////wD///////8AAAAAAAAAAAAAAAAoEAAAAAAAACz8////////////////AP///////wAAAAAAAAAAAAAAGNiAAAAAAAAAAIj///////////////8A////////AAAAAAAAAAAAABjY//gYAAAAAAAACOD//////////////wD///////8AAAAAAAAAAAAY2P///5wAAAAAAAAASP//////////////AP///////wAAAAAAAAAAGNj//////CgAAAAAAAAAqP////////////8A////////AAAAAAAAAADI////////sAAAAAAAAAAc8P///////////wD///////8AAAAAAAAAAP//////////QAAAAAAAAABs////////////AP///////wAAAAAAAAAA///////////IAAAAAAAAAATI//////////8A////////AAAAAAAAAAD///////////9YAAAAAAAAADD8/////////wD///////8AAAAAAAAAAP///////////9wEAAAAAAAAAJD/////////AP///////wAAAAAAAAAA/////////////3AAAAAAAAAADOT///////8A////////AAAAAAAAAAD/////////////7BAAAAAAAAAAUP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'L' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'M' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////8AAAAAAAAAAAAAAHz//////3wAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAATP//////UAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAc//////8cAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAADw////8AAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAAALz////AAAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAAkP///5AAAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAABc////ZAAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAoAAAAADD///8wAAAAACQAAAAAAAAA////////AP//////AAAAAAAAAFwAAAAABPz//AgAAAAAXAAAAAAAAAD///////8A//////8AAAAAAAAAkAAAAAAA0P/UAAAAAACQAAAAAAAAAP///////wD//////wAAAAAAAADMAAAAAACg/6gAAAAAAMQAAAAAAAAA////////AP//////AAAAAAAAAPgEAAAAAHD/dAAAAAAE+AAAAAAAAAD///////8A//////8AAAAAAAAA/zQAAAAAQP9IAAAAADD/AAAAAAAAAP///////wD//////wAAAAAAAAD/bAAAAAAQ/xQAAAAAaP8AAAAAAAAA////////AP//////AAAAAAAAAP+gAAAAAADQAAAAAACc/wAAAAAAAAD///////8A//////8AAAAAAAAA/9QAAAAAAGgAAAAAAND/AAAAAAAAAP///////wD//////wAAAAAAAAD//wwAAAAAFAAAAAAM/P8AAAAAAAAA////////AP//////AAAAAAAAAP//RAAAAAAAAAAAADz//wAAAAAAAAD///////8A//////8AAAAAAAAA//94AAAAAAAAAAAAcP//AAAAAAAAAP///////wD//////wAAAAAAAAD//7AAAAAAAAAAAACo//8AAAAAAAAA////////AP//////AAAAAAAAAP//5AAAAAAAAAAAANz//wAAAAAAAAD///////8A//////8AAAAAAAAA////HAAAAAAAAAAQ////AAAAAAAAAP///////wD//////wAAAAAAAAD///9QAAAAAAAAAEz///8AAAAAAAAA////////AP//////AAAAAAAAAP///4gAAAAAAAAAfP///wAAAAAAAAD///////8A//////8AAAAAAAAA////vAAAAAAAAACw////AAAAAAAAAP///////wD//////wAAAAAAAAD////wAAAAAAAAAOz///8AAAAAAAAA////////AP//////AAAAAAAAAP////8sAAAAAAAc/////wAAAAAAAAD///////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'N' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAALD/////////////AAAAAAAAAP//////////AP////////8AAAAAAAAAFOj///////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAASP///////////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAkP//////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAI1P////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAw+P///////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAABw////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAC8//////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAABzs/////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAFD/////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAJz///8AAAAAAAAA//////////8A/////////wAAAAAAAAAUAAAAAAAADNz//wAAAAAAAAD//////////wD/////////AAAAAAAAALQAAAAAAAAANPz/AAAAAAAAAP//////////AP////////8AAAAAAAAA/2wAAAAAAAAAfP8AAAAAAAAA//////////8A/////////wAAAAAAAAD/+CwAAAAAAAAExAAAAAAAAAD//////////wD/////////AAAAAAAAAP//0AQAAAAAAAAgAAAAAAAAAP//////////AP////////8AAAAAAAAA////jAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////RAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP/////kFAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA//////+sAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD///////9kAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP////////QkAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA/////////8wEAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD//////////4QAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP///////////DwAAAAAAAAAAP//////////AP////////8AAAAAAAAA////////////4BAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////////////qAAAAAAAAAD//////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'O' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////0qGw4HAAAABw4aKT0/////////////////wD////////////////wcAwAAAAAAAAAAAAAAAho6P//////////////AP//////////////uBQAAAAAAAAAAAAAAAAAAAAMoP////////////8A/////////////6AEAAAAAAAAAAAAAAAAAAAAAAAAkP///////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP//////////8BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAM5P////////8A//////////9wAAAAAAAAAAAsrPD/7KQsAAAAAAAAAABg/////////wD/////////+BAAAAAAAAAAUPj///////hQAAAAAAAAAAjs////////AP////////+sAAAAAAAAABDw//////////AYAAAAAAAAAKD///////8A/////////2wAAAAAAAAAdP///////////3wAAAAAAAAAYP///////wD/////////OAAAAAAAAAC4////////////xAAAAAAAAAAw////////AP////////8cAAAAAAAAAOD////////////oAAAAAAAAABT///////8A/////////wAAAAAAAAAA//////////////8AAAAAAAAAAP///////wD/////////AAAAAAAAAAD//////////////wAAAAAAAAAA////////AP////////8AAAAAAAAAAP/////////////8AAAAAAAAAAD///////8A/////////xwAAAAAAAAA5P///////////+AAAAAAAAAAHP///////wD/////////NAAAAAAAAAC8////////////uAAAAAAAAAA4////////AP////////9oAAAAAAAAAHj///////////98AAAAAAAAAGT///////8A/////////6gAAAAAAAAAGPD/////////+BgAAAAAAAAApP///////wD/////////9AwAAAAAAAAAUPz///////xcAAAAAAAAAAjs////////AP//////////cAAAAAAAAAAALKjs//CwOAAAAAAAAAAAYP////////8A///////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzk/////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP////////////+QAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A//////////////+sEAAAAAAAAAAAAAAAAAAAAAyg/////////////wD////////////////oZAgAAAAAAAAAAAAAAARg4P//////////////AP//////////////////9KhsOCAAAAAUMFyc7P////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'P' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////wAAAAAAAAAAAAAAAAAACCxguP////////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAOOD//////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAGOD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAARP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAABo////////////AP///////////wAAAAAAAAAA////6JwMAAAAAAAAADD///////////8A////////////AAAAAAAAAAD//////6AAAAAAAAAADP///////////wD///////////8AAAAAAAAAAP//////9AAAAAAAAAAA////////////AP///////////wAAAAAAAAAA///////0AAAAAAAAAAD///////////8A////////////AAAAAAAAAAD//////5gAAAAAAAAAHP///////////wD///////////8AAAAAAAAAAP///9iICAAAAAAAAABI////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAIT/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAABU/P////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAIhPz//////////////wD///////////8AAAAAAAAAAAAAAAAABCRMkOz/////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'Q' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////SoaDQcAAAAHDhoqPT///////////////////8A//////////////BwDAAAAAAAAAAAAAAACHDo/////////////////wD///////////+4FAAAAAAAAAAAAAAAAAAAABCo////////////////AP//////////nAQAAAAAAAAAAAAAAAAAAAAAAACQ//////////////8A/////////7gEAAAAAAAAAAAAAAAAAAAAAAAAAACg/////////////wD////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzo////////////AP///////3AAAAAAAAAAACyo8P/sqCwAAAAAAAAAAGT///////////8A///////4EAAAAAAAAABM+P///////FQAAAAAAAAACPT//////////wD//////7AAAAAAAAAAFPD/////////9BgAAAAAAAAApP//////////AP//////bAAAAAAAAAB4////////////fAAAAAAAAABk//////////8A//////84AAAAAAAAALz///////////+8AAAAAAAAADT//////////wD//////xwAAAAAAAAA6P///////////+QAAAAAAAAAHP//////////AP//////AAAAAAAAAAD//////////////wAAAAAAAAAA//////////8A//////8AAAAAAAAAAP//////////////AAAAAAAAAAD//////////wD//////wAAAAAAAAAA/P////////////8AAAAAAAAAAP//////////AP//////GAAAAAAAAADg////////////4AAAAAAAAAAc//////////8A//////84AAAAAAAAALT////MJHTo//+8AAAAAAAAADT//////////wD//////2wAAAAAAAAAdP///2AAABCg/3wAAAAAAAAAZP//////////AP//////rAAAAAAAAAAY9P/sCAAAAABMGAAAAAAAAACk//////////8A///////4EAAAAAAAAABU/P+0OAAAAAAAAAAAAAAACPT//////////wD///////94AAAAAAAAAAA4sPD/gAAAAAAAAAAAAABk////////////AP////////AcAAAAAAAAAAAAAAAAAAAAAAAAAAAADOT///////////8A/////////7wEAAAAAAAAAAAAAAAAAAAAAAAAAACQ/////////////wD//////////6wEAAAAAAAAAAAAAAAAAAAAAAAAABSs////////////AP///////////7gUAAAAAAAAAAAAAAAAAAAAAAAAAABAwP////////8A//////////////BwDAAAAAAAAAAAAAAABAgAAAAAAAA8/////////wD////////////////0qGg0GAAAABgwXJjkxBgAAAAAALD/////////AP//////////////////////////////////5DQAAAAk/P////////8A////////////////////////////////////+GwAAJD//////////wD//////////////////////////////////////8A49P//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'R' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////wAAAAAAAAAAAAAAAAAAAAQgOGSk+P///////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAcuP//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAEsP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ6P///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD///////////8A/////////wAAAAAAAAAA///////svDgAAAAAAAAACP///////////wD/////////AAAAAAAAAAD/////////7AAAAAAAAAAA////////////AP////////8AAAAAAAAAAP/////////cAAAAAAAAABD///////////8A/////////wAAAAAAAAAA//////DQoCQAAAAAAAAAQP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACU////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPj///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAzU/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAA02P//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAxctPz///////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAEDY/////////////////wD/////////AAAAAAAAAAD/9LAsAAAAAAAAAAzc////////////////AP////////8AAAAAAAAAAP///+wkAAAAAAAAADD8//////////////8A/////////wAAAAAAAAAA/////8QAAAAAAAAAAJD//////////////wD/////////AAAAAAAAAAD//////1QAAAAAAAAAFPD/////////////AP////////8AAAAAAAAAAP//////3AQAAAAAAAAAgP////////////8A/////////wAAAAAAAAAA////////aAAAAAAAAAAM6P///////////wD/////////AAAAAAAAAAD////////oCAAAAAAAAABs////////////AP////////8AAAAAAAAAAP////////+AAAAAAAAAAATc//////////8A/////////wAAAAAAAAAA//////////AUAAAAAAAAAFj//////////wD/////////AAAAAAAAAAD//////////5AAAAAAAAAAAND/////////AP////////8AAAAAAAAAAP//////////+CQAAAAAAAAAQP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'S' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////8vHBEIAgAAAQgQHC8/P////////////////8A////////////////pCQAAAAAAAAAAAAAAAAcoP///////////////wD//////////////FwAAAAAAAAAAAAAAAAAAAAAXP//////////////AP////////////9oAAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A////////////zAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////9cAAAAAAAAAAAAAAAAAAAAAAAAAACA////////////AP///////////xgAAAAAAAAAUOD/8KwkAAAAAAAAADj///////////8A////////////AAAAAAAAAAD0/////8wABCAgICxASP///////////wD///////////8MAAAAAAAAAMz/////////////////////////////AP///////////0AAAAAAAAAACFiQxPT///////////////////////8A////////////oAAAAAAAAAAAAAAAADBwtPT//////////////////wD////////////8QAAAAAAAAAAAAAAAAAAACFTA////////////////AP/////////////oOAAAAAAAAAAAAAAAAAAAAABM6P////////////8A///////////////4fAgAAAAAAAAAAAAAAAAAAAAY2P///////////wD/////////////////7IwwAAAAAAAAAAAAAAAAAAAo+P//////////AP/////////////////////koGw0BAAAAAAAAAAAAACU//////////8A///////////////////////////4uFgAAAAAAAAAADz//////////wD//////////2BgSEA0IBwA6P///////5QAAAAAAAAADP//////////AP//////////JAAAAAAAAACc/////////AAAAAAAAAAA//////////8A//////////9YAAAAAAAAACDo///////AAAAAAAAAABT//////////wD//////////6QAAAAAAAAAACCk7P/snBQAAAAAAAAAUP//////////AP//////////+BAAAAAAAAAAAAAAAAAAAAAAAAAAAACs//////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAAOP///////////wD////////////8RAAAAAAAAAAAAAAAAAAAAAAAABjc////////////AP/////////////0PAAAAAAAAAAAAAAAAAAAAAAg2P////////////8A///////////////8hBQAAAAAAAAAAAAAAAAMdPT//////////////wD/////////////////+LRwSCAMAAAAHDhoqPT/////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'T' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'U' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////JAAAAAAAAADk/////////+gAAAAAAAAAHP//////////AP////////9MAAAAAAAAAJz/////////nAAAAAAAAABE//////////8A/////////4gAAAAAAAAAHOj//////+ggAAAAAAAAAHz//////////wD/////////0AAAAAAAAAAAIJzs/+ykIAAAAAAAAAAA0P//////////AP//////////QAAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A///////////IBAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAAAAJj/////////////AP////////////+UBAAAAAAAAAAAAAAAAAAAAASU//////////////8A///////////////IPAAAAAAAAAAAAAAAAAAwyP///////////////wD/////////////////0IxYOCAIAAAEIEiAyP//////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'V' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////zAAAAAAAAAAYP//////////////ZAAAAAAAAAAw////////AP//////kAAAAAAAAAAU/P////////////8UAAAAAAAAAJD///////8A///////oBAAAAAAAAADE////////////xAAAAAAAAAAE7P///////wD///////9MAAAAAAAAAHD///////////94AAAAAAAAAEz/////////AP///////6gAAAAAAAAAJP///////////yQAAAAAAAAArP////////8A////////+BAAAAAAAAAA1P/////////YAAAAAAAAABT4/////////wD/////////aAAAAAAAAACE/////////4QAAAAAAAAAbP//////////AP/////////EAAAAAAAAADT/////////OAAAAAAAAADM//////////8A//////////8kAAAAAAAAAOT//////+QAAAAAAAAAKP///////////wD//////////4QAAAAAAAAAmP//////nAAAAAAAAACI////////////AP//////////5AAAAAAAAABE//////9EAAAAAAAABOT///////////8A////////////QAAAAAAAAAT0////9AgAAAAAAABI/////////////wD///////////+gAAAAAAAAAKT///+kAAAAAAAAAKj/////////////AP////////////QIAAAAAAAAXP///1wAAAAAAAAM+P////////////8A/////////////1wAAAAAAAAM+P/8DAAAAAAAAGT//////////////wD/////////////vAAAAAAAAAC8/7wAAAAAAAAAxP//////////////AP//////////////HAAAAAAAAGj/aAAAAAAAACT///////////////8A//////////////94AAAAAAAAHP8cAAAAAAAAhP///////////////wD//////////////9gAAAAAAAAAkAAAAAAAAADk////////////////AP///////////////zgAAAAAAAAQAAAAAAAAQP////////////////8A////////////////lAAAAAAAAAAAAAAAAACg/////////////////wD////////////////sCAAAAAAAAAAAAAAADPT/////////////////AP////////////////9QAAAAAAAAAAAAAABg//////////////////8A/////////////////7AAAAAAAAAAAAAAAMD//////////////////wD//////////////////BQAAAAAAAAAAAAc////////////////////AP//////////////////cAAAAAAAAAAAAHz///////////////////8A///////////////////MAAAAAAAAAAAA3P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'W' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//8cAAAAAAAAALz/////4AAAAAAAAAAA6P////+8AAAAAAAAABz//wD//1QAAAAAAAAAjP////+gAAAAAAAAAACo/////4wAAAAAAAAAUP//AP//jAAAAAAAAABU/////2AAAAAAAAAAAGj/////VAAAAAAAAACM//8A///EAAAAAAAAACT/////IAAAAAAAAAAAKP////8kAAAAAAAAAMT//wD///gEAAAAAAAAAPD//+AAAAAAAAAAAAAA6P//8AAAAAAAAAAE9P//AP///zAAAAAAAAAAvP//oAAAAAAAAAAAAACo//+8AAAAAAAAADD///8A////bAAAAAAAAACM//9gAAAAAAAAAAAAAGT//4wAAAAAAAAAaP///wD///+kAAAAAAAAAFT//yAAAAAAAAAAAAAAIP//VAAAAAAAAACc////AP///9gAAAAAAAAAJP/gAAAAAAAAAAAAAAAA4P8kAAAAAAAAANT///8A/////xAAAAAAAAAA8KAAAAAAAAAAAAAAAACg8AAAAAAAAAAQ/////wD/////TAAAAAAAAAC8YAAAAAAAAAAAAAAAAGC8AAAAAAAAAET/////AP////+AAAAAAAAAAIwgAAAAAAAAAAAAAAAAIIwAAAAAAAAAfP////8A/////7gAAAAAAAAANAAAAAAAACwwAAAAAAAANAAAAAAAAACw/////wD/////8AAAAAAAAAAAAAAAAAAAdHgAAAAAAAAAAAAAAAAAAOz/////AP//////KAAAAAAAAAAAAAAAAAC4vAAAAAAAAAAAAAAAAAAg//////8A//////9gAAAAAAAAAAAAAAAACPj4CAAAAAAAAAAAAAAAAFj//////wD//////5QAAAAAAAAAAAAAAABE//9IAAAAAAAAAAAAAAAAkP//////AP//////0AAAAAAAAAAAAAAAAIj//4wAAAAAAAAAAAAAAADI//////8A///////8DAAAAAAAAAAAAAAAzP//1AAAAAAAAAAAAAAABPj//////wD///////88AAAAAAAAAAAAABT/////GAAAAAAAAAAAAAA0////////AP///////3QAAAAAAAAAAAAAWP////9gAAAAAAAAAAAAAHD///////8A////////sAAAAAAAAAAAAACg/////6QAAAAAAAAAAAAApP///////wD////////kAAAAAAAAAAAAAOT/////6AAAAAAAAAAAAADc////////AP////////8cAAAAAAAAAAAo////////MAAAAAAAAAAAEP////////8A/////////1QAAAAAAAAAAHD///////94AAAAAAAAAABM/////////wD/////////jAAAAAAAAAAAtP///////7wAAAAAAAAAAID/////////AP/////////EAAAAAAAAAAT0////////+AgAAAAAAAAAuP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'X' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////9UAAAAAAAAAKz///////////+sAAAAAAAAAFD/////////AP///////+QQAAAAAAAAFOT/////////8BwAAAAAAAAM5P////////8A/////////5gAAAAAAAAATP////////9kAAAAAAAAAJD//////////wD//////////0AAAAAAAAAAoP//////wAAAAAAAAAA0/P//////////AP//////////2AgAAAAAAAAQ4P////gkAAAAAAAABMz///////////8A////////////iAAAAAAAAABA////dAAAAAAAAABw/////////////wD////////////8MAAAAAAAAACU/9AEAAAAAAAAHPD/////////////AP/////////////IBAAAAAAAAAzYMAAAAAAAAACs//////////////8A//////////////90AAAAAAAAABAAAAAAAAAATP///////////////wD///////////////QgAAAAAAAAAAAAAAAAAAzg////////////////AP///////////////7wAAAAAAAAAAAAAAAAAjP////////////////8A/////////////////2AAAAAAAAAAAAAAADD8/////////////////wD/////////////////7BQAAAAAAAAAAAAEyP//////////////////AP/////////////////gDAAAAAAAAAAAAAjY//////////////////8A/////////////////0AAAAAAAAAAAAAAADj8/////////////////wD///////////////+UAAAAAAAAAAAAAAAAAJD/////////////////AP//////////////4AwAAAAAAAAAAAAAAAAADOD///////////////8A//////////////9AAAAAAAAAAAAAAAAAAAAAQP///////////////wD/////////////nAAAAAAAAAAAWAAAAAAAAAAAlP//////////////AP///////////+QQAAAAAAAAAGD/YAAAAAAAAAAM4P////////////8A////////////TAAAAAAAAAAs9P/0LAAAAAAAAABM/////////////wD//////////6AAAAAAAAAADNT////UDAAAAAAAAACg////////////AP/////////kEAAAAAAAAACg//////+gAAAAAAAAABDk//////////8A/////////0wAAAAAAAAAYP////////9gAAAAAAAAAEz//////////wD///////+oAAAAAAAAACz0//////////QsAAAAAAAAAKT/////////AP//////7BQAAAAAAAAM1P///////////9QMAAAAAAAAFOz///////8A//////9UAAAAAAAAAKD//////////////6AAAAAAAAAAVP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'Y' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////1QAAAAAAAAAAGj//////////2gAAAAAAAAAAFT///////8A////////5BAAAAAAAAAAAMT////////EAAAAAAAAAAAQ5P///////wD/////////mAAAAAAAAAAAKPj/////+CgAAAAAAAAAAJj/////////AP//////////PAAAAAAAAAAAgP////+AAAAAAAAAAAA8//////////8A///////////YCAAAAAAAAAAE2P//2AQAAAAAAAAACNj//////////wD///////////+AAAAAAAAAAAA4//84AAAAAAAAAACA////////////AP////////////woAAAAAAAAAACUlAAAAAAAAAAAKPz///////////8A/////////////8gAAAAAAAAAABAQAAAAAAAAAADI/////////////wD//////////////2wAAAAAAAAAAAAAAAAAAAAAbP//////////////AP//////////////8BwAAAAAAAAAAAAAAAAAABzw//////////////8A////////////////tAAAAAAAAAAAAAAAAAAAtP///////////////wD/////////////////VAAAAAAAAAAAAAAAAFT/////////////////AP/////////////////oEAAAAAAAAAAAAAAQ6P////////////////8A//////////////////+cAAAAAAAAAAAAAJz//////////////////wD///////////////////9AAAAAAAAAAABA////////////////////AP///////////////////9gAAAAAAAAAANj///////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
'Z' => array(
|
||||
'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAQ//////////////8A/////////////////////////1AAAAAAAAAABLz//////////////wD///////////////////////98AAAAAAAAAACY////////////////AP//////////////////////pAAAAAAAAAAAaP////////////////8A/////////////////////8QIAAAAAAAAAET8/////////////////wD////////////////////gGAAAAAAAAAAo9P//////////////////AP//////////////////9CwAAAAAAAAAFNz///////////////////8A//////////////////xMAAAAAAAAAATA/////////////////////wD/////////////////eAAAAAAAAAAAnP//////////////////////AP///////////////5wAAAAAAAAAAHT///////////////////////8A///////////////ABAAAAAAAAABM/P///////////////////////wD/////////////3BQAAAAAAAAALPT/////////////////////////AP////////////QoAAAAAAAAABjg//////////////////////////8A///////////8SAAAAAAAAAAExP///////////////////////////wD//////////2wAAAAAAAAAAKD/////////////////////////////AP////////+YAAAAAAAAAAB8//////////////////////////////8A/////////wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
|
||||
'width' => 40
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1040
install/update/new/phpbb/captcha/plugins/qa.php
Normal file
1040
install/update/new/phpbb/captcha/plugins/qa.php
Normal file
File diff suppressed because it is too large
Load Diff
164
install/update/new/phpbb/class_loader.php
Normal file
164
install/update/new/phpbb/class_loader.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb;
|
||||
|
||||
/**
|
||||
* The class loader resolves class names to file system paths and loads them if
|
||||
* necessary.
|
||||
*
|
||||
* Classes have to be of the form phpbb_(dir_)*(classpart_)*, so directory names
|
||||
* must never contain underscores. Example: phpbb_dir_subdir_class_name is a
|
||||
* valid class name, while phpbb_dir_sub_dir_class_name is not.
|
||||
*
|
||||
* If every part of the class name is a directory, the last directory name is
|
||||
* also used as the filename, e.g. phpbb_dir would resolve to dir/dir.php.
|
||||
*/
|
||||
class class_loader
|
||||
{
|
||||
private $namespace;
|
||||
private $path;
|
||||
private $php_ext;
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* A map of looked up class names to paths relative to $this->path.
|
||||
* This map is stored in cache and looked up if the cache is available.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $cached_paths = array();
|
||||
|
||||
/**
|
||||
* Creates a new \phpbb\class_loader, which loads files with the given
|
||||
* file extension from the given path.
|
||||
*
|
||||
* @param string $namespace Required namespace for files to be loaded
|
||||
* @param string $path Directory to load files from
|
||||
* @param string $php_ext The file extension for PHP files
|
||||
* @param \phpbb\cache\driver\driver_interface $cache An implementation of the phpBB cache interface.
|
||||
*/
|
||||
public function __construct($namespace, $path, $php_ext = 'php', \phpbb\cache\driver\driver_interface $cache = null)
|
||||
{
|
||||
if ($namespace[0] !== '\\')
|
||||
{
|
||||
$namespace = '\\' . $namespace;
|
||||
}
|
||||
|
||||
$this->namespace = $namespace;
|
||||
$this->path = $path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
$this->set_cache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the class loader with a cache to store paths. If set to null, the
|
||||
* the class loader will resolve paths by checking for the existence of every
|
||||
* directory in the class name every time.
|
||||
*
|
||||
* @param \phpbb\cache\driver\driver_interface $cache An implementation of the phpBB cache interface.
|
||||
*/
|
||||
public function set_cache(\phpbb\cache\driver\driver_interface $cache = null)
|
||||
{
|
||||
if ($cache)
|
||||
{
|
||||
$this->cached_paths = $cache->get('class_loader_' . str_replace('\\', '__', $this->namespace));
|
||||
|
||||
if ($this->cached_paths === false)
|
||||
{
|
||||
$this->cached_paths = array();
|
||||
}
|
||||
}
|
||||
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the class loader as an autoloader using SPL.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
spl_autoload_register(array($this, 'load_class'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the class loader from the SPL autoloader stack.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'load_class'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a phpBB class name to a relative path which can be included.
|
||||
*
|
||||
* @param string $class The class name to resolve, must be in the
|
||||
* namespace the loader was constructed with.
|
||||
* Has to begin with \
|
||||
* @return string|bool A relative path to the file containing the
|
||||
* class or false if looking it up failed.
|
||||
*/
|
||||
public function resolve_path($class)
|
||||
{
|
||||
if (isset($this->cached_paths[$class]))
|
||||
{
|
||||
return $this->path . $this->cached_paths[$class] . '.' . $this->php_ext;
|
||||
}
|
||||
|
||||
if (!preg_match('/^' . preg_quote($this->namespace, '/') . '[a-zA-Z0-9_\\\\]+$/', $class))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$relative_path = str_replace('\\', '/', substr($class, strlen($this->namespace)));
|
||||
|
||||
if (!file_exists($this->path . $relative_path . '.' . $this->php_ext))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->cache)
|
||||
{
|
||||
$this->cached_paths[$class] = $relative_path;
|
||||
$this->cache->put('class_loader_' . str_replace('\\', '__', $this->namespace), $this->cached_paths);
|
||||
}
|
||||
|
||||
return $this->path . $relative_path . '.' . $this->php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a class name to a path and then includes it.
|
||||
*
|
||||
* @param string $class The class name which is being loaded.
|
||||
*/
|
||||
public function load_class($class)
|
||||
{
|
||||
// In general $class is not supposed to contain a leading backslash,
|
||||
// but sometimes it does. See tickets PHP-50731 and HHVM-1840.
|
||||
if ($class[0] !== '\\')
|
||||
{
|
||||
$class = '\\' . $class;
|
||||
}
|
||||
|
||||
if (substr($class, 0, strlen($this->namespace)) === $this->namespace)
|
||||
{
|
||||
$path = $this->resolve_path($class);
|
||||
|
||||
if ($path)
|
||||
{
|
||||
require $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
186
install/update/new/phpbb/config/config.php
Normal file
186
install/update/new/phpbb/config/config.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\config;
|
||||
|
||||
/**
|
||||
* Configuration container class
|
||||
*/
|
||||
class config implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
{
|
||||
/**
|
||||
* The configuration data
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Creates a configuration container with a default set of values
|
||||
*
|
||||
* @param array(string => string) $config The configuration data.
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an ArrayIterator over the configuration values.
|
||||
*
|
||||
* @return \ArrayIterator An iterator over all config data
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified config value exists.
|
||||
*
|
||||
* @param string $key The configuration option's name.
|
||||
* @return bool Whether the configuration option exists.
|
||||
*/
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return isset($this->config[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a configuration value.
|
||||
*
|
||||
* @param string $key The configuration option's name.
|
||||
* @return string The configuration value
|
||||
*/
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return (isset($this->config[$key])) ? $this->config[$key] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily overwrites the value of a configuration variable.
|
||||
*
|
||||
* The configuration change will not persist. It will be lost
|
||||
* after the request.
|
||||
*
|
||||
* @param string $key The configuration option's name.
|
||||
* @param string $value The temporary value.
|
||||
*/
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
$this->config[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when deleting a configuration value directly, triggers an error.
|
||||
*
|
||||
* @param string $key The configuration option's name.
|
||||
*/
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
trigger_error('Config values have to be deleted explicitly with the \phpbb\config\config::delete($key) method.', E_USER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the number of configuration options currently set.
|
||||
*
|
||||
* @return int Number of config options
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a configuration option
|
||||
*
|
||||
* @param String $key The configuration option's name
|
||||
* @param bool $use_cache Whether this variable should be cached or if it
|
||||
* changes too frequently to be efficiently cached
|
||||
* @return null
|
||||
*/
|
||||
public function delete($key, $use_cache = true)
|
||||
{
|
||||
unset($this->config[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a configuration option's value
|
||||
*
|
||||
* @param string $key The configuration option's name
|
||||
* @param string $value New configuration value
|
||||
* @param bool $use_cache Whether this variable should be cached or if it
|
||||
* changes too frequently to be efficiently cached.
|
||||
*/
|
||||
public function set($key, $value, $use_cache = true)
|
||||
{
|
||||
$this->config[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a configuration option's value only if the old_value matches the
|
||||
* current configuration value or the configuration value does not exist yet.
|
||||
*
|
||||
* @param string $key The configuration option's name
|
||||
* @param string $old_value Current configuration value
|
||||
* @param string $new_value New configuration value
|
||||
* @param bool $use_cache Whether this variable should be cached or if it
|
||||
* changes too frequently to be efficiently cached.
|
||||
* @return bool True if the value was changed, false otherwise.
|
||||
*/
|
||||
public function set_atomic($key, $old_value, $new_value, $use_cache = true)
|
||||
{
|
||||
if (!isset($this->config[$key]) || $this->config[$key] == $old_value)
|
||||
{
|
||||
$this->config[$key] = $new_value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks configuration option's value only if the new_value matches the
|
||||
* current configuration value and the configuration value does exist.Called
|
||||
* only after set_atomic has been called.
|
||||
*
|
||||
* @param string $key The configuration option's name
|
||||
* @param string $new_value New configuration value
|
||||
* @throws \phpbb\exception\http_exception when config value is set and not equal to new_value.
|
||||
* @return bool True if the value was changed, false otherwise.
|
||||
*/
|
||||
public function ensure_lock($key, $new_value)
|
||||
{
|
||||
if (isset($this->config[$key]) && $this->config[$key] == $new_value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
throw new \phpbb\exception\http_exception(500, 'Failure while aqcuiring locks.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments an integer configuration value.
|
||||
*
|
||||
* @param string $key The configuration option's name
|
||||
* @param int $increment Amount to increment by
|
||||
* @param bool $use_cache Whether this variable should be cached or if it
|
||||
* changes too frequently to be efficiently cached.
|
||||
*/
|
||||
function increment($key, $increment, $use_cache = true)
|
||||
{
|
||||
if (!isset($this->config[$key]))
|
||||
{
|
||||
$this->config[$key] = 0;
|
||||
}
|
||||
|
||||
$this->config[$key] += $increment;
|
||||
}
|
||||
}
|
||||
166
install/update/new/phpbb/config_php_file.php
Normal file
166
install/update/new/phpbb/config_php_file.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb;
|
||||
|
||||
class config_php_file
|
||||
{
|
||||
/** @var string phpBB Root Path */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string php file extension */
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Indicates whether the php config file has been loaded.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $config_loaded = false;
|
||||
|
||||
/**
|
||||
* The content of the php config file
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config_data = array();
|
||||
|
||||
/**
|
||||
* The path to the config file. (Default: $phpbb_root_path . 'config.' . $php_ext)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $config_file;
|
||||
|
||||
private $defined_vars;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $phpbb_root_path phpBB Root Path
|
||||
* @param string $php_ext php file extension
|
||||
*/
|
||||
function __construct($phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->config_file = $this->phpbb_root_path . 'config.' . $this->php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path to the config file.
|
||||
*
|
||||
* @param string $config_file
|
||||
*/
|
||||
public function set_config_file($config_file)
|
||||
{
|
||||
$this->config_file = $config_file;
|
||||
$this->config_loaded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array containing the variables defined by the config file.
|
||||
*
|
||||
* @return array Return the content of the config file or an empty array if the file does not exists.
|
||||
*/
|
||||
public function get_all()
|
||||
{
|
||||
$this->load_config_file();
|
||||
|
||||
return $this->config_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of a variable defined into the config.php file or null if the variable does not exist.
|
||||
*
|
||||
* @param string $variable The name of the variable
|
||||
* @return mixed Value of the variable or null if the variable is not defined.
|
||||
*/
|
||||
public function get($variable)
|
||||
{
|
||||
$this->load_config_file();
|
||||
|
||||
return isset($this->config_data[$variable]) ? $this->config_data[$variable] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the config file and store the information.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function load_config_file()
|
||||
{
|
||||
if (!$this->config_loaded && file_exists($this->config_file))
|
||||
{
|
||||
$this->defined_vars = get_defined_vars();
|
||||
|
||||
require($this->config_file);
|
||||
$this->config_data = array_diff_key(get_defined_vars(), $this->defined_vars);
|
||||
|
||||
$this->config_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert either 3.0 dbms or 3.1 db driver class name to 3.1 db driver class name.
|
||||
*
|
||||
* If $dbms is a valid 3.1 db driver class name, returns it unchanged.
|
||||
* Otherwise prepends phpbb\db\driver\ to the dbms to convert a 3.0 dbms
|
||||
* to 3.1 db driver class name.
|
||||
*
|
||||
* @param string $dbms dbms parameter
|
||||
* @return string driver class
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function convert_30_dbms_to_31($dbms)
|
||||
{
|
||||
// Note: this check is done first because mysqli extension
|
||||
// supplies a mysqli class, and class_exists($dbms) would return
|
||||
// true for mysqli class.
|
||||
// However, per the docblock any valid 3.1 driver name should be
|
||||
// recognized by this function, and have priority over 3.0 dbms.
|
||||
if (strpos($dbms, 'phpbb\db\driver') === false && class_exists('phpbb\db\driver\\' . $dbms))
|
||||
{
|
||||
return 'phpbb\db\driver\\' . $dbms;
|
||||
}
|
||||
|
||||
if (class_exists($dbms))
|
||||
{
|
||||
// Additionally we could check that $dbms extends phpbb\db\driver\driver.
|
||||
// http://php.net/manual/en/class.reflectionclass.php
|
||||
// Beware of possible performance issues:
|
||||
// http://stackoverflow.com/questions/294582/php-5-reflection-api-performance
|
||||
// We could check for interface implementation in all paths or
|
||||
// only when we do not prepend phpbb\db\driver\.
|
||||
|
||||
/*
|
||||
$reflection = new \ReflectionClass($dbms);
|
||||
|
||||
if ($reflection->isSubclassOf('phpbb\db\driver\driver'))
|
||||
{
|
||||
return $dbms;
|
||||
}
|
||||
*/
|
||||
|
||||
return $dbms;
|
||||
}
|
||||
|
||||
// Force use of mysqli when specifying mysql
|
||||
if (preg_match('/(phpbb\\\db\\\driver\\\)?mysql$/i', $dbms))
|
||||
{
|
||||
return 'phpbb\db\driver\mysqli';
|
||||
}
|
||||
|
||||
throw new \RuntimeException("You have specified an invalid dbms driver: $dbms");
|
||||
}
|
||||
}
|
||||
173
install/update/new/phpbb/console/command/cron/run.php
Normal file
173
install/update/new/phpbb/console/command/cron/run.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\console\command\cron;
|
||||
|
||||
use phpbb\exception\runtime_exception;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class run extends \phpbb\console\command\command
|
||||
{
|
||||
/** @var \phpbb\cron\manager */
|
||||
protected $cron_manager;
|
||||
|
||||
/** @var \phpbb\lock\db */
|
||||
protected $lock_db;
|
||||
|
||||
/**
|
||||
* Construct method
|
||||
*
|
||||
* @param \phpbb\user $user The user object (used to get language information)
|
||||
* @param \phpbb\cron\manager $cron_manager The cron manager containing
|
||||
* the cron tasks to be executed.
|
||||
* @param \phpbb\lock\db $lock_db The lock for accessing database.
|
||||
*/
|
||||
public function __construct(\phpbb\user $user, \phpbb\cron\manager $cron_manager, \phpbb\lock\db $lock_db)
|
||||
{
|
||||
$this->cron_manager = $cron_manager;
|
||||
$this->lock_db = $lock_db;
|
||||
parent::__construct($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command name and description
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('cron:run')
|
||||
->setDescription($this->user->lang('CLI_DESCRIPTION_CRON_RUN'))
|
||||
->setHelp($this->user->lang('CLI_HELP_CRON_RUN'))
|
||||
->addArgument('name', InputArgument::OPTIONAL, $this->user->lang('CLI_DESCRIPTION_CRON_RUN_ARGUMENT_1'))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command cron:run.
|
||||
*
|
||||
* Tries to acquire the cron lock, then if no argument has been given runs all ready cron tasks.
|
||||
* If the cron lock can not be obtained, an error message is printed
|
||||
* and the exit status is set to 1.
|
||||
* If the verbose option is specified, each start of a task is printed.
|
||||
* Otherwise there is no output.
|
||||
* If an argument is given to the command, only the task whose name matches the
|
||||
* argument will be started. If verbose option is specified,
|
||||
* an info message containing the name of the task is printed.
|
||||
* If no task matches the argument given, an error message is printed
|
||||
* and the exit status is set to 2.
|
||||
*
|
||||
* @param InputInterface $input The input stream used to get the argument and verboe option.
|
||||
* @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
|
||||
*
|
||||
* @return int 0 if all is ok, 1 if a lock error occurred and 2 if no task matching the argument was found.
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->lock_db->acquire())
|
||||
{
|
||||
$task_name = $input->getArgument('name');
|
||||
if ($task_name)
|
||||
{
|
||||
$exit_status = $this->run_one($input, $output, $task_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
$exit_status = $this->run_all($input, $output);
|
||||
}
|
||||
|
||||
$this->lock_db->release();
|
||||
return $exit_status;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new runtime_exception('CRON_LOCK_ERROR', array(), null, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all ready cron tasks.
|
||||
*
|
||||
* If verbose mode is set, an info message will be printed if there is no task to
|
||||
* be run, or else for each starting task.
|
||||
*
|
||||
* @see execute
|
||||
* @param InputInterface $input The input stream used to get the argument and verbose option.
|
||||
* @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
|
||||
* @return int 0
|
||||
*/
|
||||
protected function run_all(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$run_tasks = $this->cron_manager->find_all_ready_tasks();
|
||||
|
||||
if ($run_tasks)
|
||||
{
|
||||
foreach ($run_tasks as $task)
|
||||
{
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$output->writeln('<info>' . $this->user->lang('RUNNING_TASK', $task->get_name()) . '</info>');
|
||||
}
|
||||
|
||||
$task->run();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$output->writeln('<info>' . $this->user->lang('CRON_NO_TASK') . '</info>');
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given cron task, if it is ready.
|
||||
*
|
||||
* If there is a task whose name matches $task_name, it is run and 0 is returned.
|
||||
* and if verbose mode is set, print an info message with the name of the task.
|
||||
* If there is no task matching $task_name, the function prints an error message
|
||||
* and returns with status 2.
|
||||
*
|
||||
* @see execute
|
||||
*
|
||||
* @param InputInterface $input The input stream used to get the argument and verbose option.
|
||||
* @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
|
||||
* @param string $task_name The name of the task that should be run.
|
||||
*
|
||||
* @return int 0 if all is well, 2 if no task matches $task_name.
|
||||
*/
|
||||
protected function run_one(InputInterface $input, OutputInterface $output, $task_name)
|
||||
{
|
||||
$task = $this->cron_manager->find_task($task_name);
|
||||
if ($task)
|
||||
{
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$output->writeln('<info>' . $this->user->lang('RUNNING_TASK', $task_name) . '</info>');
|
||||
}
|
||||
|
||||
$task->run();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new runtime_exception('CRON_NO_SUCH_TASK', array( $task_name), null, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
namespace phpbb\console\command\extension;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class enable extends command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('extension:enable')
|
||||
->setDescription($this->user->lang('CLI_DESCRIPTION_ENABLE_EXTENSION'))
|
||||
->addArgument(
|
||||
'extension-name',
|
||||
InputArgument::REQUIRED,
|
||||
$this->user->lang('CLI_EXTENSION_NAME')
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$name = $input->getArgument('extension-name');
|
||||
|
||||
if (!$this->manager->is_available($name))
|
||||
{
|
||||
$io->error($this->user->lang('CLI_EXTENSION_NOT_EXIST', $name));
|
||||
return 1;
|
||||
}
|
||||
|
||||
$extension = $this->manager->get_extension($name);
|
||||
|
||||
if (($enableable = $extension->is_enableable()) !== true)
|
||||
{
|
||||
$message = !empty($enableable) ? $enableable : $this->user->lang('CLI_EXTENSION_NOT_ENABLEABLE', $name);
|
||||
$message = is_array($message) ? implode(PHP_EOL, $message) : $message;
|
||||
$io->error($message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($this->manager->is_enabled($name))
|
||||
{
|
||||
$io->error($this->user->lang('CLI_EXTENSION_ENABLED', $name));
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->manager->enable($name);
|
||||
$this->manager->load_extensions();
|
||||
|
||||
if ($this->manager->is_enabled($name))
|
||||
{
|
||||
$this->log->add('admin', ANONYMOUS, '', 'LOG_EXT_ENABLE', time(), array($name));
|
||||
$io->success($this->user->lang('CLI_EXTENSION_ENABLE_SUCCESS', $name));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$io->error($this->user->lang('CLI_EXTENSION_ENABLE_FAILURE', $name));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
332
install/update/new/phpbb/console/command/update/check.php
Normal file
332
install/update/new/phpbb/console/command/update/check.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\console\command\update;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\exception\exception_interface;
|
||||
use phpbb\language\language;
|
||||
use phpbb\user;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
class check extends \phpbb\console\command\command
|
||||
{
|
||||
/** @var \phpbb\config\config */
|
||||
protected $config;
|
||||
|
||||
/** @var \Symfony\Component\DependencyInjection\ContainerBuilder */
|
||||
protected $phpbb_container;
|
||||
/**
|
||||
* @var language
|
||||
*/
|
||||
private $language;
|
||||
|
||||
/**
|
||||
* Construct method
|
||||
*/
|
||||
public function __construct(user $user, config $config, ContainerInterface $phpbb_container, language $language)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->phpbb_container = $phpbb_container;
|
||||
$this->language = $language;
|
||||
|
||||
$this->language->add_lang(array('acp/common', 'acp/extensions'));
|
||||
|
||||
parent::__construct($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the service.
|
||||
*
|
||||
* Sets the name and description of the command.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('update:check')
|
||||
->setDescription($this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK'))
|
||||
->addArgument('ext-name', InputArgument::OPTIONAL, $this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK_ARGUMENT_1'))
|
||||
->addOption('stability', null, InputOption::VALUE_REQUIRED, $this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK_OPTION_STABILITY'))
|
||||
->addOption('cache', 'c', InputOption::VALUE_NONE, $this->language->lang('CLI_DESCRIPTION_UPDATE_CHECK_OPTION_CACHE'))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command.
|
||||
*
|
||||
* Checks if an update is available.
|
||||
* If at least one is available, a message is printed and if verbose mode is set the list of possible updates is printed.
|
||||
* If their is none, nothing is printed unless verbose mode is set.
|
||||
*
|
||||
* @param InputInterface $input Input stream, used to get the options.
|
||||
* @param OutputInterface $output Output stream, used to print messages.
|
||||
* @return int 0 if the board is up to date, 1 if it is not and 2 if an error occurred.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$recheck = true;
|
||||
if ($input->getOption('cache'))
|
||||
{
|
||||
$recheck = false;
|
||||
}
|
||||
|
||||
$stability = null;
|
||||
if ($input->getOption('stability'))
|
||||
{
|
||||
$stability = $input->getOption('stability');
|
||||
if (!($stability == 'stable') && !($stability == 'unstable'))
|
||||
{
|
||||
$io->error($this->language->lang('CLI_ERROR_INVALID_STABILITY', $stability));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
$ext_name = $input->getArgument('ext-name');
|
||||
if ($ext_name != null)
|
||||
{
|
||||
if ($ext_name == 'all')
|
||||
{
|
||||
return $this->check_all_ext($io, $stability, $recheck);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->check_ext($input, $io, $stability, $recheck, $ext_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->check_core($input, $io, $stability, $recheck);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given extension is up to date
|
||||
*
|
||||
* @param InputInterface $input Input stream, used to get the options.
|
||||
* @param SymfonyStyle $io IO handler, for formatted and unified IO
|
||||
* @param string $stability Force a given stability
|
||||
* @param bool $recheck Disallow the use of the cache
|
||||
* @param string $ext_name The extension name
|
||||
* @return int
|
||||
*/
|
||||
protected function check_ext(InputInterface $input, SymfonyStyle $io, $stability, $recheck, $ext_name)
|
||||
{
|
||||
try
|
||||
{
|
||||
$ext_manager = $this->phpbb_container->get('ext.manager');
|
||||
$md_manager = $ext_manager->create_extension_metadata_manager($ext_name);
|
||||
$updates_available = $ext_manager->version_check($md_manager, $recheck, false, $stability);
|
||||
|
||||
$metadata = $md_manager->get_metadata('all');
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$io->title($md_manager->get_metadata('display-name'));
|
||||
|
||||
$io->note($this->language->lang('CURRENT_VERSION') . $this->language->lang('COLON') . ' ' . $metadata['version']);
|
||||
}
|
||||
|
||||
if (!empty($updates_available))
|
||||
{
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$io->caution($this->language->lang('NOT_UP_TO_DATE', $metadata['name']));
|
||||
|
||||
$this->display_versions($io, $updates_available);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$io->success($this->language->lang('UPDATE_NOT_NEEDED'));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (\RuntimeException $e)
|
||||
{
|
||||
$io->error($this->language->lang('EXTENSION_NOT_INSTALLED', $ext_name));
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the core is up to date
|
||||
*
|
||||
* @param InputInterface $input Input stream, used to get the options.
|
||||
* @param SymfonyStyle $io IO handler, for formatted and unified IO
|
||||
* @param string $stability Force a given stability
|
||||
* @param bool $recheck Disallow the use of the cache
|
||||
* @return int
|
||||
*/
|
||||
protected function check_core(InputInterface $input, SymfonyStyle $io, $stability, $recheck)
|
||||
{
|
||||
$version_helper = $this->phpbb_container->get('version_helper');
|
||||
$version_helper->force_stability($stability);
|
||||
|
||||
$updates_available = $version_helper->get_suggested_updates($recheck);
|
||||
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$io->title('phpBB core');
|
||||
|
||||
$io->note( $this->language->lang('CURRENT_VERSION') . $this->language->lang('COLON') . ' ' . $this->config['version']);
|
||||
}
|
||||
|
||||
if (!empty($updates_available))
|
||||
{
|
||||
$io->caution($this->language->lang('UPDATE_NEEDED'));
|
||||
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$this->display_versions($io, $updates_available);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($input->getOption('verbose'))
|
||||
{
|
||||
$io->success($this->language->lang('UPDATE_NOT_NEEDED'));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all the available extensions are up to date
|
||||
*
|
||||
* @param SymfonyStyle $io IO handler, for formatted and unified IO
|
||||
* @param string $stability Stability specifier string
|
||||
* @param bool $recheck Disallow the use of the cache
|
||||
* @return int
|
||||
*/
|
||||
protected function check_all_ext(SymfonyStyle $io, $stability, $recheck)
|
||||
{
|
||||
/** @var \phpbb\extension\manager $ext_manager */
|
||||
$ext_manager = $this->phpbb_container->get('ext.manager');
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($ext_manager->all_available() as $ext_name => $ext_path)
|
||||
{
|
||||
$row = [];
|
||||
$row[] = sprintf("<info>%s</info>", $ext_name);
|
||||
$md_manager = $ext_manager->create_extension_metadata_manager($ext_name);
|
||||
try
|
||||
{
|
||||
$metadata = $md_manager->get_metadata('all');
|
||||
if (isset($metadata['extra']['version-check']))
|
||||
{
|
||||
try {
|
||||
$updates_available = $ext_manager->version_check($md_manager, $recheck, false, $stability);
|
||||
if (!empty($updates_available))
|
||||
{
|
||||
$versions = array_map(function($entry)
|
||||
{
|
||||
return $entry['current'];
|
||||
}, $updates_available);
|
||||
|
||||
$row[] = sprintf("<comment>%s</comment>", $metadata['version']);
|
||||
$row[] = implode(', ', $versions);
|
||||
}
|
||||
else
|
||||
{
|
||||
$row[] = sprintf("<info>%s</info>", $metadata['version']);
|
||||
$row[] = '';
|
||||
}
|
||||
} catch (\RuntimeException $e) {
|
||||
$row[] = $metadata['version'];
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$row[] = $metadata['version'];
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
catch (exception_interface $e)
|
||||
{
|
||||
$exception_message = call_user_func_array(array($this->user, 'lang'), array_merge(array($e->getMessage()), $e->get_parameters()));
|
||||
$row[] = '<error>' . $exception_message . '</error>';
|
||||
}
|
||||
catch (\RuntimeException $e)
|
||||
{
|
||||
$row[] = '<error>' . $e->getMessage() . '</error>';
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$io->table([
|
||||
$this->language->lang('EXTENSION_NAME'),
|
||||
$this->language->lang('CURRENT_VERSION'),
|
||||
$this->language->lang('LATEST_VERSION'),
|
||||
], $rows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the details of the available updates
|
||||
*
|
||||
* @param SymfonyStyle $io IO handler, for formatted and unified IO
|
||||
* @param array $updates_available The list of the available updates
|
||||
*/
|
||||
protected function display_versions(SymfonyStyle $io, $updates_available)
|
||||
{
|
||||
$io->section($this->language->lang('UPDATES_AVAILABLE'));
|
||||
|
||||
$rows = [];
|
||||
foreach ($updates_available as $version_data)
|
||||
{
|
||||
$row = ['', '', ''];
|
||||
$row[0] = $version_data['current'];
|
||||
|
||||
if (isset($version_data['announcement']))
|
||||
{
|
||||
$row[1] = $version_data['announcement'];
|
||||
}
|
||||
|
||||
if (isset($version_data['download']))
|
||||
{
|
||||
$row[2] = $version_data['download'];
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$io->table([
|
||||
$this->language->lang('VERSION'),
|
||||
$this->language->lang('ANNOUNCEMENT_TOPIC'),
|
||||
$this->language->lang('DOWNLOAD_LATEST'),
|
||||
], $rows);
|
||||
}
|
||||
}
|
||||
334
install/update/new/phpbb/console/command/user/add.php
Normal file
334
install/update/new/phpbb/console/command/user/add.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\console\command\user;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\console\command\command;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\exception\runtime_exception;
|
||||
use phpbb\language\language;
|
||||
use phpbb\passwords\manager;
|
||||
use phpbb\user;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class add extends command
|
||||
{
|
||||
/** @var array Array of interactively acquired options */
|
||||
protected $data;
|
||||
|
||||
/** @var driver_interface */
|
||||
protected $db;
|
||||
|
||||
/** @var config */
|
||||
protected $config;
|
||||
|
||||
/** @var language */
|
||||
protected $language;
|
||||
|
||||
/** @var manager */
|
||||
protected $password_manager;
|
||||
|
||||
/**
|
||||
* phpBB root path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* PHP extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Construct method
|
||||
*
|
||||
* @param user $user
|
||||
* @param driver_interface $db
|
||||
* @param config $config
|
||||
* @param language $language
|
||||
* @param manager $password_manager
|
||||
* @param string $phpbb_root_path
|
||||
* @param string $php_ext
|
||||
*/
|
||||
public function __construct(user $user, driver_interface $db, config $config, language $language, manager $password_manager, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->config = $config;
|
||||
$this->language = $language;
|
||||
$this->password_manager = $password_manager;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
$this->language->add_lang('ucp');
|
||||
parent::__construct($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command name and description
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('user:add')
|
||||
->setDescription($this->language->lang('CLI_DESCRIPTION_USER_ADD'))
|
||||
->setHelp($this->language->lang('CLI_HELP_USER_ADD'))
|
||||
->addOption(
|
||||
'username',
|
||||
'U',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
$this->language->lang('CLI_DESCRIPTION_USER_ADD_OPTION_USERNAME')
|
||||
)
|
||||
->addOption(
|
||||
'password',
|
||||
'P',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
$this->language->lang('CLI_DESCRIPTION_USER_ADD_OPTION_PASSWORD')
|
||||
)
|
||||
->addOption(
|
||||
'email',
|
||||
'E',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
$this->language->lang('CLI_DESCRIPTION_USER_ADD_OPTION_EMAIL')
|
||||
)
|
||||
->addOption(
|
||||
'send-email',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
$this->language->lang('CLI_DESCRIPTION_USER_ADD_OPTION_NOTIFY')
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command user:add
|
||||
*
|
||||
* Adds a new user to the database. If options are not provided, it will ask for the username, password and email.
|
||||
* User is added to the registered user group. Language and timezone default to $config settings.
|
||||
*
|
||||
* @param InputInterface $input The input stream used to get the options
|
||||
* @param OutputInterface $output The output stream, used to print messages
|
||||
*
|
||||
* @return int 0 if all is well, 1 if any errors occurred
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
try
|
||||
{
|
||||
$this->validate_user_data();
|
||||
$group_id = $this->get_group_id();
|
||||
}
|
||||
catch (runtime_exception $e)
|
||||
{
|
||||
$io->error($e->getMessage());
|
||||
return 1;
|
||||
}
|
||||
|
||||
$user_row = array(
|
||||
'username' => $this->data['username'],
|
||||
'user_password' => $this->password_manager->hash($this->data['new_password']),
|
||||
'user_email' => $this->data['email'],
|
||||
'group_id' => $group_id,
|
||||
'user_timezone' => $this->config['board_timezone'],
|
||||
'user_lang' => $this->config['default_lang'],
|
||||
'user_type' => USER_NORMAL,
|
||||
'user_regdate' => time(),
|
||||
);
|
||||
|
||||
$user_id = (int) user_add($user_row);
|
||||
|
||||
if (!$user_id)
|
||||
{
|
||||
$io->error($this->language->lang('AUTH_NO_PROFILE_CREATED'));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($input->getOption('send-email') && $this->config['email_enable'])
|
||||
{
|
||||
$this->send_activation_email($user_id);
|
||||
}
|
||||
|
||||
$io->success($this->language->lang('CLI_USER_ADD_SUCCESS', $this->data['username']));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interacts with the user.
|
||||
*
|
||||
* @param InputInterface $input An InputInterface instance
|
||||
* @param OutputInterface $output An OutputInterface instance
|
||||
*/
|
||||
protected function interact(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$helper = $this->getHelper('question');
|
||||
|
||||
$this->data = array(
|
||||
'username' => $input->getOption('username'),
|
||||
'new_password' => $input->getOption('password'),
|
||||
'email' => $input->getOption('email'),
|
||||
);
|
||||
|
||||
if (!$this->data['username'])
|
||||
{
|
||||
$question = new Question($this->ask_user('USERNAME'));
|
||||
$this->data['username'] = $helper->ask($input, $output, $question);
|
||||
}
|
||||
|
||||
if (!$this->data['new_password'])
|
||||
{
|
||||
$question = new Question($this->ask_user('PASSWORD'));
|
||||
$question->setValidator(function ($value) use ($helper, $input, $output) {
|
||||
$question = new Question($this->ask_user('CONFIRM_PASSWORD'));
|
||||
$question->setHidden(true);
|
||||
if ($helper->ask($input, $output, $question) != $value)
|
||||
{
|
||||
throw new runtime_exception($this->language->lang('NEW_PASSWORD_ERROR'));
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$question->setHidden(true);
|
||||
$question->setMaxAttempts(5);
|
||||
|
||||
$this->data['new_password'] = $helper->ask($input, $output, $question);
|
||||
}
|
||||
|
||||
if (!$this->data['email'])
|
||||
{
|
||||
$question = new Question($this->ask_user('EMAIL_ADDRESS'));
|
||||
$this->data['email'] = $helper->ask($input, $output, $question);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the submitted user data
|
||||
*
|
||||
* @throws runtime_exception if any data fails validation
|
||||
* @return null
|
||||
*/
|
||||
protected function validate_user_data()
|
||||
{
|
||||
if (!function_exists('validate_data'))
|
||||
{
|
||||
require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
|
||||
}
|
||||
|
||||
$error = validate_data($this->data, array(
|
||||
'username' => array(
|
||||
array('string', false, $this->config['min_name_chars'], $this->config['max_name_chars']),
|
||||
array('username', '')),
|
||||
'new_password' => array(
|
||||
array('string', false, $this->config['min_pass_chars'], 0),
|
||||
array('password')),
|
||||
'email' => array(
|
||||
array('string', false, 6, 60),
|
||||
array('user_email')),
|
||||
));
|
||||
|
||||
if ($error)
|
||||
{
|
||||
throw new runtime_exception(implode("\n", array_map(array($this->language, 'lang'), $error)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the group id
|
||||
*
|
||||
* Go and find in the database the group_id corresponding to 'REGISTERED'
|
||||
*
|
||||
* @throws runtime_exception if the group id does not exist in database.
|
||||
* @return null
|
||||
*/
|
||||
protected function get_group_id()
|
||||
{
|
||||
$sql = 'SELECT group_id
|
||||
FROM ' . GROUPS_TABLE . "
|
||||
WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "'
|
||||
AND group_type = " . GROUP_SPECIAL;
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$row || !$row['group_id'])
|
||||
{
|
||||
throw new runtime_exception($this->language->lang('NO_GROUP'));
|
||||
}
|
||||
|
||||
return $row['group_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send account activation email
|
||||
*
|
||||
* @param int $user_id The new user's id
|
||||
* @return null
|
||||
*/
|
||||
protected function send_activation_email($user_id)
|
||||
{
|
||||
switch ($this->config['require_activation'])
|
||||
{
|
||||
case USER_ACTIVATION_SELF:
|
||||
$email_template = 'user_welcome_inactive';
|
||||
$user_actkey = gen_rand_string(mt_rand(6, 10));
|
||||
break;
|
||||
case USER_ACTIVATION_ADMIN:
|
||||
$email_template = 'admin_welcome_inactive';
|
||||
$user_actkey = gen_rand_string(mt_rand(6, 10));
|
||||
break;
|
||||
default:
|
||||
$email_template = 'user_welcome';
|
||||
$user_actkey = '';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!class_exists('messenger'))
|
||||
{
|
||||
require($this->phpbb_root_path . 'includes/functions_messenger.' . $this->php_ext);
|
||||
}
|
||||
|
||||
$messenger = new \messenger(false);
|
||||
$messenger->template($email_template, $this->user->lang_name);
|
||||
$messenger->to($this->data['email'], $this->data['username']);
|
||||
$messenger->anti_abuse_headers($this->config, $this->user);
|
||||
$messenger->assign_vars(array(
|
||||
'WELCOME_MSG' => htmlspecialchars_decode($this->language->lang('WELCOME_SUBJECT', $this->config['sitename'])),
|
||||
'USERNAME' => htmlspecialchars_decode($this->data['username']),
|
||||
'PASSWORD' => htmlspecialchars_decode($this->data['new_password']),
|
||||
'U_ACTIVATE' => generate_board_url() . "/ucp.{$this->php_ext}?mode=activate&u=$user_id&k=$user_actkey")
|
||||
);
|
||||
|
||||
$messenger->send(NOTIFY_EMAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to translate questions to the user
|
||||
*
|
||||
* @param string $key The language key
|
||||
* @return string The language key translated with a colon and space appended
|
||||
*/
|
||||
protected function ask_user($key)
|
||||
{
|
||||
return $this->language->lang($key) . $this->language->lang('COLON') . ' ';
|
||||
}
|
||||
}
|
||||
899
install/update/new/phpbb/content_visibility.php
Normal file
899
install/update/new/phpbb/content_visibility.php
Normal file
@@ -0,0 +1,899 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb;
|
||||
|
||||
/**
|
||||
* phpbb_visibility
|
||||
* Handle fetching and setting the visibility for topics and posts
|
||||
*/
|
||||
class content_visibility
|
||||
{
|
||||
/**
|
||||
* Database object
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* User object
|
||||
* @var \phpbb\user
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Auth object
|
||||
* @var \phpbb\auth\auth
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* config object
|
||||
* @var \phpbb\config\config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Event dispatcher object
|
||||
* @var \phpbb\event\dispatcher_interface
|
||||
*/
|
||||
protected $phpbb_dispatcher;
|
||||
|
||||
/**
|
||||
* phpBB root path
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* PHP Extension
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\auth\auth $auth Auth object
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
|
||||
* @param \phpbb\db\driver\driver_interface $db Database object
|
||||
* @param \phpbb\user $user User object
|
||||
* @param string $phpbb_root_path Root path
|
||||
* @param string $php_ext PHP Extension
|
||||
* @param string $forums_table Forums table name
|
||||
* @param string $posts_table Posts table name
|
||||
* @param string $topics_table Topics table name
|
||||
* @param string $users_table Users table name
|
||||
*/
|
||||
public function __construct(\phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\event\dispatcher_interface $phpbb_dispatcher, \phpbb\db\driver\driver_interface $db, \phpbb\user $user, $phpbb_root_path, $php_ext, $forums_table, $posts_table, $topics_table, $users_table)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->config = $config;
|
||||
$this->phpbb_dispatcher = $phpbb_dispatcher;
|
||||
$this->db = $db;
|
||||
$this->user = $user;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->forums_table = $forums_table;
|
||||
$this->posts_table = $posts_table;
|
||||
$this->topics_table = $topics_table;
|
||||
$this->users_table = $users_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current logged-in user soft-delete posts?
|
||||
*
|
||||
* @param $forum_id int Forum ID whose permissions to check
|
||||
* @param $poster_id int Poster ID of the post in question
|
||||
* @param $post_locked bool Is the post locked?
|
||||
* @return bool
|
||||
*/
|
||||
public function can_soft_delete($forum_id, $poster_id, $post_locked)
|
||||
{
|
||||
if ($this->auth->acl_get('m_softdelete', $forum_id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ($this->auth->acl_get('f_softdelete', $forum_id) && $poster_id == $this->user->data['user_id'] && !$post_locked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the topics post count or the forums post/topic count based on permissions
|
||||
*
|
||||
* @param $mode string One of topic_posts, forum_posts or forum_topics
|
||||
* @param $data array Array with the topic/forum data to calculate from
|
||||
* @param $forum_id int The forum id is used for permission checks
|
||||
* @return int Number of posts/topics the user can see in the topic/forum
|
||||
*/
|
||||
public function get_count($mode, $data, $forum_id)
|
||||
{
|
||||
if (!$this->auth->acl_get('m_approve', $forum_id))
|
||||
{
|
||||
return (int) $data[$mode . '_approved'];
|
||||
}
|
||||
|
||||
return (int) $data[$mode . '_approved'] + (int) $data[$mode . '_unapproved'] + (int) $data[$mode . '_softdeleted'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check topic/post visibility for a given forum ID
|
||||
*
|
||||
* Note: Read permissions are not checked.
|
||||
*
|
||||
* @param $mode string Either "topic" or "post"
|
||||
* @param $forum_id int The forum id is used for permission checks
|
||||
* @param $data array Array with item information to check visibility
|
||||
* @return bool True if the item is visible, false if not
|
||||
*/
|
||||
public function is_visible($mode, $forum_id, $data)
|
||||
{
|
||||
$visibility = $data[$mode . '_visibility'];
|
||||
$poster_key = ($mode === 'topic') ? 'topic_poster' : 'poster_id';
|
||||
$is_visible = ($visibility == ITEM_APPROVED) ||
|
||||
($this->config['display_unapproved_posts'] &&
|
||||
($this->user->data['user_id'] != ANONYMOUS) &&
|
||||
($visibility == ITEM_UNAPPROVED || $visibility == ITEM_REAPPROVE) &&
|
||||
($this->user->data['user_id'] == $data[$poster_key])) ||
|
||||
$this->auth->acl_get('m_approve', $forum_id);
|
||||
|
||||
/**
|
||||
* Allow changing the result of calling is_visible
|
||||
*
|
||||
* @event core.phpbb_content_visibility_is_visible
|
||||
* @var bool is_visible Default visibility condition, to be modified by extensions if needed.
|
||||
* @var string mode Either "topic" or "post"
|
||||
* @var int forum_id Forum id of the current item
|
||||
* @var array data Array of item information
|
||||
* @since 3.2.2-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'is_visible',
|
||||
'mode',
|
||||
'forum_id',
|
||||
'data',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_is_visible', compact($vars)));
|
||||
|
||||
return $is_visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create topic/post visibility SQL for a given forum ID
|
||||
*
|
||||
* Note: Read permissions are not checked.
|
||||
*
|
||||
* @param $mode string Either "topic" or "post"
|
||||
* @param $forum_id int The forum id is used for permission checks
|
||||
* @param $table_alias string Table alias to prefix in SQL queries
|
||||
* @return string The appropriate combination SQL logic for topic/post_visibility
|
||||
*/
|
||||
public function get_visibility_sql($mode, $forum_id, $table_alias = '')
|
||||
{
|
||||
$where_sql = '';
|
||||
|
||||
$get_visibility_sql_overwrite = false;
|
||||
|
||||
/**
|
||||
* Allow changing the result of calling get_visibility_sql
|
||||
*
|
||||
* @event core.phpbb_content_visibility_get_visibility_sql_before
|
||||
* @var string where_sql Extra visibility conditions. It must end with either an SQL "AND" or an "OR"
|
||||
* @var string mode Either "topic" or "post" depending on the query this is being used in
|
||||
* @var array forum_id The forum id in which the search is made.
|
||||
* @var string table_alias Table alias to prefix in SQL queries
|
||||
* @var mixed get_visibility_sql_overwrite If a string, forces the function to return get_forums_visibility_sql_overwrite after executing the event
|
||||
* If false, get_visibility_sql continues normally
|
||||
* It must be either boolean or string
|
||||
* @since 3.1.4-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'where_sql',
|
||||
'mode',
|
||||
'forum_id',
|
||||
'table_alias',
|
||||
'get_visibility_sql_overwrite',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_visibility_sql_before', compact($vars)));
|
||||
|
||||
if ($get_visibility_sql_overwrite !== false)
|
||||
{
|
||||
return $get_visibility_sql_overwrite;
|
||||
}
|
||||
|
||||
if ($this->auth->acl_get('m_approve', $forum_id))
|
||||
{
|
||||
$where_sql .= '1 = 1';
|
||||
}
|
||||
else
|
||||
{
|
||||
$visibility_query = $table_alias . $mode . '_visibility = ';
|
||||
|
||||
$where_sql .= '(' . $visibility_query . ITEM_APPROVED . ')';
|
||||
if ($this->config['display_unapproved_posts'] && ($this->user->data['user_id'] != ANONYMOUS))
|
||||
{
|
||||
$poster_key = ($mode === 'topic') ? 'topic_poster' : 'poster_id';
|
||||
$where_sql .= ' OR ((' . $visibility_query . ITEM_UNAPPROVED . ' OR ' . $visibility_query . ITEM_REAPPROVE .')';
|
||||
$where_sql .= ' AND ' . $table_alias . $poster_key . ' = ' . ((int) $this->user->data['user_id']) . ')';
|
||||
}
|
||||
}
|
||||
return '(' . $where_sql . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create topic/post visibility SQL for a set of forums
|
||||
*
|
||||
* Note: Read permissions are not checked. Forums without read permissions
|
||||
* should not be in $forum_ids
|
||||
*
|
||||
* @param $mode string Either "topic" or "post"
|
||||
* @param $forum_ids array Array of forum ids which the posts/topics are limited to
|
||||
* @param $table_alias string Table alias to prefix in SQL queries
|
||||
* @return string The appropriate combination SQL logic for topic/post_visibility
|
||||
*/
|
||||
public function get_forums_visibility_sql($mode, $forum_ids = array(), $table_alias = '')
|
||||
{
|
||||
$where_sql = '';
|
||||
|
||||
$approve_forums = array_keys($this->auth->acl_getf('m_approve', true));
|
||||
if (!empty($forum_ids) && !empty($approve_forums))
|
||||
{
|
||||
$approve_forums = array_intersect($forum_ids, $approve_forums);
|
||||
$forum_ids = array_diff($forum_ids, $approve_forums);
|
||||
}
|
||||
|
||||
$get_forums_visibility_sql_overwrite = false;
|
||||
/**
|
||||
* Allow changing the result of calling get_forums_visibility_sql
|
||||
*
|
||||
* @event core.phpbb_content_visibility_get_forums_visibility_before
|
||||
* @var string where_sql Extra visibility conditions. It must end with either an SQL "AND" or an "OR"
|
||||
* @var string mode Either "topic" or "post" depending on the query this is being used in
|
||||
* @var array forum_ids Array of forum ids which the posts/topics are limited to
|
||||
* @var string table_alias Table alias to prefix in SQL queries
|
||||
* @var array approve_forums Array of forums where the user has m_approve permissions
|
||||
* @var mixed get_forums_visibility_sql_overwrite If a string, forces the function to return get_forums_visibility_sql_overwrite after executing the event
|
||||
* If false, get_forums_visibility_sql continues normally
|
||||
* It must be either boolean or string
|
||||
* @since 3.1.3-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'where_sql',
|
||||
'mode',
|
||||
'forum_ids',
|
||||
'table_alias',
|
||||
'approve_forums',
|
||||
'get_forums_visibility_sql_overwrite',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_forums_visibility_before', compact($vars)));
|
||||
|
||||
if ($get_forums_visibility_sql_overwrite !== false)
|
||||
{
|
||||
return $get_forums_visibility_sql_overwrite;
|
||||
}
|
||||
|
||||
// Moderator can view all posts/topics in the moderated forums
|
||||
$where_sql .= '(' . $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums, false, true) . ' OR ';
|
||||
// Normal user can view approved items only
|
||||
$where_sql .= '(' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . '
|
||||
AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true) . '))';
|
||||
|
||||
return '(' . $where_sql . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create topic/post visibility SQL for all forums on the board
|
||||
*
|
||||
* Note: Read permissions are not checked. Forums without read permissions
|
||||
* should be in $exclude_forum_ids
|
||||
*
|
||||
* @param $mode string Either "topic" or "post"
|
||||
* @param $exclude_forum_ids array Array of forum ids which are excluded
|
||||
* @param $table_alias string Table alias to prefix in SQL queries
|
||||
* @return string The appropriate combination SQL logic for topic/post_visibility
|
||||
*/
|
||||
public function get_global_visibility_sql($mode, $exclude_forum_ids = array(), $table_alias = '')
|
||||
{
|
||||
$where_sqls = array();
|
||||
|
||||
$approve_forums = array_diff(array_keys($this->auth->acl_getf('m_approve', true)), $exclude_forum_ids);
|
||||
|
||||
$visibility_sql_overwrite = null;
|
||||
|
||||
/**
|
||||
* Allow changing the result of calling get_global_visibility_sql
|
||||
*
|
||||
* @event core.phpbb_content_visibility_get_global_visibility_before
|
||||
* @var array where_sqls Array of extra visibility conditions. Will be joined by imploding with "OR".
|
||||
* @var string mode Either "topic" or "post" depending on the query this is being used in
|
||||
* @var array exclude_forum_ids Array of forum ids the current user doesn't have access to
|
||||
* @var string table_alias Table alias to prefix in SQL queries
|
||||
* @var array approve_forums Array of forums where the user has m_approve permissions
|
||||
* @var string visibility_sql_overwrite If not empty, forces the function to return visibility_sql_overwrite after executing the event
|
||||
* @since 3.1.3-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'where_sqls',
|
||||
'mode',
|
||||
'exclude_forum_ids',
|
||||
'table_alias',
|
||||
'approve_forums',
|
||||
'visibility_sql_overwrite',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_global_visibility_before', compact($vars)));
|
||||
|
||||
if ($visibility_sql_overwrite)
|
||||
{
|
||||
return $visibility_sql_overwrite;
|
||||
}
|
||||
|
||||
// Include approved items in all forums but the excluded
|
||||
$where_sqls[] = '(' . $this->db->sql_in_set($table_alias . 'forum_id', $exclude_forum_ids, true, true) . '
|
||||
AND ' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ')';
|
||||
|
||||
// If user has moderator permissions, add everything in the moderated forums
|
||||
if (count($approve_forums))
|
||||
{
|
||||
$where_sqls[] = $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums);
|
||||
}
|
||||
|
||||
return '(' . implode(' OR ', $where_sqls) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Change visibility status of one post or all posts of a topic
|
||||
*
|
||||
* @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
|
||||
* @param $post_id mixed Post ID or array of post IDs to act on,
|
||||
* if it is empty, all posts of topic_id will be modified
|
||||
* @param $topic_id int Topic where $post_id is found
|
||||
* @param $forum_id int Forum where $topic_id is found
|
||||
* @param $user_id int User performing the action
|
||||
* @param $time int Timestamp when the action is performed
|
||||
* @param $reason string Reason why the visibility was changed.
|
||||
* @param $is_starter bool Is this the first post of the topic changed?
|
||||
* @param $is_latest bool Is this the last post of the topic changed?
|
||||
* @param $limit_visibility mixed Limit updating per topic_id to a certain visibility
|
||||
* @param $limit_delete_time mixed Limit updating per topic_id to a certain deletion time
|
||||
* @return array Changed post data, empty array if an error occurred.
|
||||
*/
|
||||
public function set_post_visibility($visibility, $post_id, $topic_id, $forum_id, $user_id, $time, $reason, $is_starter, $is_latest, $limit_visibility = false, $limit_delete_time = false)
|
||||
{
|
||||
if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE)))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($post_id)
|
||||
{
|
||||
if (is_array($post_id))
|
||||
{
|
||||
$where_sql = $this->db->sql_in_set('post_id', array_map('intval', $post_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
$where_sql = 'post_id = ' . (int) $post_id;
|
||||
}
|
||||
$where_sql .= ' AND topic_id = ' . (int) $topic_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
$where_sql = 'topic_id = ' . (int) $topic_id;
|
||||
|
||||
// Limit the posts to a certain visibility and deletion time
|
||||
// This allows us to only restore posts, that were approved
|
||||
// when the topic got soft deleted. So previous soft deleted
|
||||
// and unapproved posts are still soft deleted/unapproved
|
||||
if ($limit_visibility !== false)
|
||||
{
|
||||
$where_sql .= ' AND post_visibility = ' . (int) $limit_visibility;
|
||||
}
|
||||
|
||||
if ($limit_delete_time !== false)
|
||||
{
|
||||
$where_sql .= ' AND post_delete_time = ' . (int) $limit_delete_time;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT poster_id, post_id, post_postcount, post_visibility
|
||||
FROM ' . $this->posts_table . '
|
||||
WHERE ' . $where_sql;
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$post_ids = $poster_postcounts = $postcounts = $postcount_visibility = array();
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$post_ids[] = (int) $row['post_id'];
|
||||
|
||||
if ($row['post_visibility'] != $visibility)
|
||||
{
|
||||
if ($row['post_postcount'] && !isset($poster_postcounts[(int) $row['poster_id']]))
|
||||
{
|
||||
$poster_postcounts[(int) $row['poster_id']] = 1;
|
||||
}
|
||||
else if ($row['post_postcount'])
|
||||
{
|
||||
$poster_postcounts[(int) $row['poster_id']]++;
|
||||
}
|
||||
|
||||
if (!isset($postcount_visibility[$row['post_visibility']]))
|
||||
{
|
||||
$postcount_visibility[$row['post_visibility']] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$postcount_visibility[$row['post_visibility']]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (empty($post_ids))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!function_exists('truncate_string'))
|
||||
{
|
||||
include($this->phpbb_root_path . 'includes/functions_content.' . $this->php_ext);
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'post_visibility' => (int) $visibility,
|
||||
'post_delete_user' => (int) $user_id,
|
||||
'post_delete_time' => ((int) $time) ?: time(),
|
||||
'post_delete_reason' => truncate_string($reason, 255, 255, false),
|
||||
);
|
||||
/**
|
||||
* Perform actions right before the query to change post visibility
|
||||
*
|
||||
* @event core.set_post_visibility_before_sql
|
||||
* @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
|
||||
* @var array post_id Array containing all post IDs to be modified. If blank, all posts within the topic are modified.
|
||||
* @var int topic_id Topic of the post IDs to be modified.
|
||||
* @var int forum_id Forum ID that the topic_id resides in.
|
||||
* @var int user_id User ID doing this action.
|
||||
* @var int time Timestamp of this action.
|
||||
* @var string reason Reason specified by the user for this change.
|
||||
* @var bool is_starter Are we changing the topic's starter?
|
||||
* @var bool is_latest Are we changing the topic's latest post?
|
||||
* @var array data The data array for this action.
|
||||
* @since 3.1.10-RC1
|
||||
* @changed 3.2.2-RC1 Use time instead of non-existent timestamp
|
||||
*/
|
||||
$vars = array(
|
||||
'visibility',
|
||||
'post_id',
|
||||
'topic_id',
|
||||
'forum_id',
|
||||
'user_id',
|
||||
'time',
|
||||
'reason',
|
||||
'is_starter',
|
||||
'is_latest',
|
||||
'data',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.set_post_visibility_before_sql', compact($vars)));
|
||||
$sql = 'UPDATE ' . $this->posts_table . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $data) . '
|
||||
WHERE ' . $this->db->sql_in_set('post_id', $post_ids);
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
// Group the authors by post count, to reduce the number of queries
|
||||
foreach ($poster_postcounts as $poster_id => $num_posts)
|
||||
{
|
||||
$postcounts[$num_posts][] = $poster_id;
|
||||
}
|
||||
|
||||
// Update users postcounts
|
||||
foreach ($postcounts as $num_posts => $poster_ids)
|
||||
{
|
||||
if (in_array($visibility, array(ITEM_REAPPROVE, ITEM_DELETED)))
|
||||
{
|
||||
$sql = 'UPDATE ' . $this->users_table . '
|
||||
SET user_posts = 0
|
||||
WHERE ' . $this->db->sql_in_set('user_id', $poster_ids) . '
|
||||
AND user_posts < ' . $num_posts;
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$sql = 'UPDATE ' . $this->users_table . '
|
||||
SET user_posts = user_posts - ' . $num_posts . '
|
||||
WHERE ' . $this->db->sql_in_set('user_id', $poster_ids) . '
|
||||
AND user_posts >= ' . $num_posts;
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = 'UPDATE ' . $this->users_table . '
|
||||
SET user_posts = user_posts + ' . $num_posts . '
|
||||
WHERE ' . $this->db->sql_in_set('user_id', $poster_ids);
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
$update_topic_postcount = true;
|
||||
|
||||
// Sync the first/last topic information if needed
|
||||
if (!$is_starter && $is_latest)
|
||||
{
|
||||
if (!function_exists('update_post_information'))
|
||||
{
|
||||
include($this->phpbb_root_path . 'includes/functions_posting.' . $this->php_ext);
|
||||
}
|
||||
|
||||
// update_post_information can only update the last post info ...
|
||||
if ($topic_id)
|
||||
{
|
||||
update_post_information('topic', $topic_id, false);
|
||||
}
|
||||
if ($forum_id)
|
||||
{
|
||||
update_post_information('forum', $forum_id, false);
|
||||
}
|
||||
}
|
||||
else if ($is_starter && $topic_id)
|
||||
{
|
||||
if (!function_exists('sync'))
|
||||
{
|
||||
include($this->phpbb_root_path . 'includes/functions_admin.' . $this->php_ext);
|
||||
}
|
||||
|
||||
// ... so we need to use sync, if the first post is changed.
|
||||
// The forum is resynced recursive by sync() itself.
|
||||
sync('topic', 'topic_id', $topic_id, true);
|
||||
|
||||
// sync recalculates the topic replies and forum posts by itself, so we don't do that.
|
||||
$update_topic_postcount = false;
|
||||
}
|
||||
|
||||
$topic_update_array = array();
|
||||
// Update the topic's reply count and the forum's post count
|
||||
if ($update_topic_postcount)
|
||||
{
|
||||
$field_alias = array(
|
||||
ITEM_APPROVED => 'posts_approved',
|
||||
ITEM_UNAPPROVED => 'posts_unapproved',
|
||||
ITEM_DELETED => 'posts_softdeleted',
|
||||
ITEM_REAPPROVE => 'posts_unapproved',
|
||||
);
|
||||
$cur_posts = array_fill_keys($field_alias, 0);
|
||||
|
||||
foreach ($postcount_visibility as $post_visibility => $visibility_posts)
|
||||
{
|
||||
$cur_posts[$field_alias[(int) $post_visibility]] += $visibility_posts;
|
||||
}
|
||||
|
||||
$sql_ary = array();
|
||||
$recipient_field = $field_alias[$visibility];
|
||||
|
||||
foreach ($cur_posts as $field => $count)
|
||||
{
|
||||
// Decrease the count for the old statuses.
|
||||
if ($count && $field != $recipient_field)
|
||||
{
|
||||
$sql_ary[$field] = " - $count";
|
||||
}
|
||||
}
|
||||
// Add up the count from all statuses excluding the recipient status.
|
||||
$count_increase = array_sum(array_diff($cur_posts, array($recipient_field)));
|
||||
|
||||
if ($count_increase)
|
||||
{
|
||||
$sql_ary[$recipient_field] = " + $count_increase";
|
||||
}
|
||||
|
||||
if (count($sql_ary))
|
||||
{
|
||||
$forum_sql = array();
|
||||
|
||||
foreach ($sql_ary as $field => $value_change)
|
||||
{
|
||||
$topic_update_array[] = 'topic_' . $field . ' = topic_' . $field . $value_change;
|
||||
$forum_sql[] = 'forum_' . $field . ' = forum_' . $field . $value_change;
|
||||
}
|
||||
|
||||
$sql = 'UPDATE ' . $this->forums_table . '
|
||||
SET ' . implode(', ', $forum_sql) . '
|
||||
WHERE forum_id = ' . (int) $forum_id;
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
if ($post_id)
|
||||
{
|
||||
$sql = 'SELECT 1 AS has_attachments
|
||||
FROM ' . POSTS_TABLE . '
|
||||
WHERE topic_id = ' . (int) $topic_id . '
|
||||
AND post_attachment = 1
|
||||
AND post_visibility = ' . ITEM_APPROVED . '
|
||||
AND ' . $this->db->sql_in_set('post_id', $post_id, true);
|
||||
$result = $this->db->sql_query_limit($sql, 1);
|
||||
|
||||
$has_attachment = (bool) $this->db->sql_fetchfield('has_attachments');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($has_attachment && $visibility == ITEM_APPROVED)
|
||||
{
|
||||
$topic_update_array[] = 'topic_attachment = 1';
|
||||
}
|
||||
else if (!$has_attachment && $visibility != ITEM_APPROVED)
|
||||
{
|
||||
$topic_update_array[] = 'topic_attachment = 0';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($topic_update_array))
|
||||
{
|
||||
// Update the number for replies and posts, and update the attachments flag
|
||||
$sql = 'UPDATE ' . $this->topics_table . '
|
||||
SET ' . implode(', ', $topic_update_array) . '
|
||||
WHERE topic_id = ' . (int) $topic_id;
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
/**
|
||||
* Perform actions after all steps to changing post visibility
|
||||
*
|
||||
* @event core.set_post_visibility_after
|
||||
* @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
|
||||
* @var array post_id Array containing all post IDs to be modified. If blank, all posts within the topic are modified.
|
||||
* @var int topic_id Topic of the post IDs to be modified.
|
||||
* @var int forum_id Forum ID that the topic_id resides in.
|
||||
* @var int user_id User ID doing this action.
|
||||
* @var int time Timestamp of this action.
|
||||
* @var string reason Reason specified by the user for this change.
|
||||
* @var bool is_starter Are we changing the topic's starter?
|
||||
* @var bool is_latest Are we changing the topic's latest post?
|
||||
* @var array data The data array for this action.
|
||||
* @since 3.1.10-RC1
|
||||
* @changed 3.2.2-RC1 Use time instead of non-existent timestamp
|
||||
*/
|
||||
$vars = array(
|
||||
'visibility',
|
||||
'post_id',
|
||||
'topic_id',
|
||||
'forum_id',
|
||||
'user_id',
|
||||
'time',
|
||||
'reason',
|
||||
'is_starter',
|
||||
'is_latest',
|
||||
'data',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.set_post_visibility_after', compact($vars)));
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set topic visibility
|
||||
*
|
||||
* Allows approving (which is akin to undeleting/restore) or soft deleting an entire topic.
|
||||
* Calls set_post_visibility as needed.
|
||||
*
|
||||
* Note: By default, when a soft deleted topic is restored. Only posts that
|
||||
* were approved at the time of soft deleting, are being restored.
|
||||
* Same applies to soft deleting. Only approved posts will be marked
|
||||
* as soft deleted.
|
||||
* If you want to update all posts, use the force option.
|
||||
*
|
||||
* @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
|
||||
* @param $topic_id mixed Topic ID to act on
|
||||
* @param $forum_id int Forum where $topic_id is found
|
||||
* @param $user_id int User performing the action
|
||||
* @param $time int Timestamp when the action is performed
|
||||
* @param $reason string Reason why the visibilty was changed.
|
||||
* @param $force_update_all bool Force to update all posts within the topic
|
||||
* @return array Changed topic data, empty array if an error occurred.
|
||||
*/
|
||||
public function set_topic_visibility($visibility, $topic_id, $forum_id, $user_id, $time, $reason, $force_update_all = false)
|
||||
{
|
||||
if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE)))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!$force_update_all)
|
||||
{
|
||||
$sql = 'SELECT topic_visibility, topic_delete_time
|
||||
FROM ' . $this->topics_table . '
|
||||
WHERE topic_id = ' . (int) $topic_id;
|
||||
$result = $this->db->sql_query($sql);
|
||||
$original_topic_data = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$original_topic_data)
|
||||
{
|
||||
// The topic does not exist...
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('truncate_string'))
|
||||
{
|
||||
include($this->phpbb_root_path . 'includes/functions_content.' . $this->php_ext);
|
||||
}
|
||||
|
||||
// Note, we do not set a reason for the posts, just for the topic
|
||||
$data = array(
|
||||
'topic_visibility' => (int) $visibility,
|
||||
'topic_delete_user' => (int) $user_id,
|
||||
'topic_delete_time' => ((int) $time) ?: time(),
|
||||
'topic_delete_reason' => truncate_string($reason, 255, 255, false),
|
||||
);
|
||||
/**
|
||||
* Perform actions right before the query to change topic visibility
|
||||
*
|
||||
* @event core.set_topic_visibility_before_sql
|
||||
* @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
|
||||
* @var int topic_id Topic of the post IDs to be modified.
|
||||
* @var int forum_id Forum ID that the topic_id resides in.
|
||||
* @var int user_id User ID doing this action.
|
||||
* @var int time Timestamp of this action.
|
||||
* @var string reason Reason specified by the user for this change.
|
||||
* @var bool force_update_all Force an update on all posts within the topic, regardless of their current approval state.
|
||||
* @var array data The data array for this action.
|
||||
* @since 3.1.10-RC1
|
||||
* @changed 3.2.2-RC1 Use time instead of non-existent timestamp
|
||||
*/
|
||||
$vars = array(
|
||||
'visibility',
|
||||
'topic_id',
|
||||
'forum_id',
|
||||
'user_id',
|
||||
'time',
|
||||
'reason',
|
||||
'force_update_all',
|
||||
'data',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.set_topic_visibility_before_sql', compact($vars)));
|
||||
$sql = 'UPDATE ' . $this->topics_table . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $data) . '
|
||||
WHERE topic_id = ' . (int) $topic_id;
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
if (!$this->db->sql_affectedrows())
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!$force_update_all && $original_topic_data['topic_delete_time'] && $original_topic_data['topic_visibility'] == ITEM_DELETED && $visibility == ITEM_APPROVED)
|
||||
{
|
||||
// If we're restoring a topic we only restore posts, that were soft deleted through the topic soft deletion.
|
||||
$this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility'], $original_topic_data['topic_delete_time']);
|
||||
}
|
||||
else if (!$force_update_all && $original_topic_data['topic_visibility'] == ITEM_APPROVED && $visibility == ITEM_DELETED)
|
||||
{
|
||||
// If we're soft deleting a topic we only mark approved posts as soft deleted.
|
||||
$this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true);
|
||||
}
|
||||
/**
|
||||
* Perform actions after all steps to changing topic visibility
|
||||
*
|
||||
* @event core.set_topic_visibility_after
|
||||
* @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
|
||||
* @var int topic_id Topic of the post IDs to be modified.
|
||||
* @var int forum_id Forum ID that the topic_id resides in.
|
||||
* @var int user_id User ID doing this action.
|
||||
* @var int time Timestamp of this action.
|
||||
* @var string reason Reason specified by the user for this change.
|
||||
* @var bool force_update_all Force an update on all posts within the topic, regardless of their current approval state.
|
||||
* @var array data The data array for this action.
|
||||
* @since 3.1.10-RC1
|
||||
* @changed 3.2.2-RC1 Use time instead of non-existent timestamp
|
||||
*/
|
||||
$vars = array(
|
||||
'visibility',
|
||||
'topic_id',
|
||||
'forum_id',
|
||||
'user_id',
|
||||
'time',
|
||||
'reason',
|
||||
'force_update_all',
|
||||
'data',
|
||||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.set_topic_visibility_after', compact($vars)));
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add post to topic and forum statistics
|
||||
*
|
||||
* @param $data array Contains information from the topics table about given topic
|
||||
* @param &$sql_data array Populated with the SQL changes, may be empty at call time
|
||||
* @return null
|
||||
*/
|
||||
public function add_post_to_statistic($data, &$sql_data)
|
||||
{
|
||||
$sql_data[$this->topics_table] = (($sql_data[$this->topics_table]) ? $sql_data[$this->topics_table] . ', ' : '') . 'topic_posts_approved = topic_posts_approved + 1';
|
||||
|
||||
$sql_data[$this->forums_table] = (($sql_data[$this->forums_table]) ? $sql_data[$this->forums_table] . ', ' : '') . 'forum_posts_approved = forum_posts_approved + 1';
|
||||
|
||||
if ($data['post_postcount'])
|
||||
{
|
||||
$sql_data[$this->users_table] = (($sql_data[$this->users_table]) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts + 1';
|
||||
}
|
||||
|
||||
$this->config->increment('num_posts', 1, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove post from topic and forum statistics
|
||||
*
|
||||
* @param $data array Contains information from the topics table about given topic
|
||||
* @param &$sql_data array Populated with the SQL changes, may be empty at call time
|
||||
* @return null
|
||||
*/
|
||||
public function remove_post_from_statistic($data, &$sql_data)
|
||||
{
|
||||
if ($data['post_visibility'] == ITEM_APPROVED)
|
||||
{
|
||||
$sql_data[$this->topics_table] = ((!empty($sql_data[$this->topics_table])) ? $sql_data[$this->topics_table] . ', ' : '') . 'topic_posts_approved = topic_posts_approved - 1';
|
||||
$sql_data[$this->forums_table] = ((!empty($sql_data[$this->forums_table])) ? $sql_data[$this->forums_table] . ', ' : '') . 'forum_posts_approved = forum_posts_approved - 1';
|
||||
|
||||
if ($data['post_postcount'])
|
||||
{
|
||||
$sql_data[$this->users_table] = ((!empty($sql_data[$this->users_table])) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts - 1';
|
||||
}
|
||||
|
||||
$this->config->increment('num_posts', -1, false);
|
||||
}
|
||||
else if ($data['post_visibility'] == ITEM_UNAPPROVED || $data['post_visibility'] == ITEM_REAPPROVE)
|
||||
{
|
||||
$sql_data[FORUMS_TABLE] = (($sql_data[FORUMS_TABLE]) ? $sql_data[FORUMS_TABLE] . ', ' : '') . 'forum_posts_unapproved = forum_posts_unapproved - 1';
|
||||
$sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_posts_unapproved = topic_posts_unapproved - 1';
|
||||
}
|
||||
else if ($data['post_visibility'] == ITEM_DELETED)
|
||||
{
|
||||
$sql_data[FORUMS_TABLE] = (($sql_data[FORUMS_TABLE]) ? $sql_data[FORUMS_TABLE] . ', ' : '') . 'forum_posts_softdeleted = forum_posts_softdeleted - 1';
|
||||
$sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_posts_softdeleted = topic_posts_softdeleted - 1';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove topic from forum statistics
|
||||
*
|
||||
* @param $data array Post and topic data
|
||||
* @param &$sql_data array Populated with the SQL changes, may be empty at call time
|
||||
* @return null
|
||||
*/
|
||||
public function remove_topic_from_statistic($data, &$sql_data)
|
||||
{
|
||||
if ($data['topic_visibility'] == ITEM_APPROVED)
|
||||
{
|
||||
$sql_data[FORUMS_TABLE] .= 'forum_posts_approved = forum_posts_approved - 1, forum_topics_approved = forum_topics_approved - 1';
|
||||
|
||||
if ($data['post_postcount'])
|
||||
{
|
||||
$sql_data[$this->users_table] = ((!empty($sql_data[$this->users_table])) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts - 1';
|
||||
}
|
||||
}
|
||||
else if ($data['topic_visibility'] == ITEM_UNAPPROVED || $data['post_visibility'] == ITEM_REAPPROVE)
|
||||
{
|
||||
$sql_data[FORUMS_TABLE] .= 'forum_posts_unapproved = forum_posts_unapproved - 1, forum_topics_unapproved = forum_topics_unapproved - 1';
|
||||
}
|
||||
else if ($data['topic_visibility'] == ITEM_DELETED)
|
||||
{
|
||||
$sql_data[FORUMS_TABLE] .= 'forum_posts_softdeleted = forum_posts_softdeleted - 1, forum_topics_softdeleted = forum_topics_softdeleted - 1';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
195
install/update/new/phpbb/controller/helper.php
Normal file
195
install/update/new/phpbb/controller/helper.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Controller helper class, contains methods that do things for controllers
|
||||
*/
|
||||
class helper
|
||||
{
|
||||
/**
|
||||
* Template object
|
||||
* @var \phpbb\template\template
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* User object
|
||||
* @var \phpbb\user
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* config object
|
||||
* @var \phpbb\config\config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/* @var \phpbb\symfony_request */
|
||||
protected $symfony_request;
|
||||
|
||||
/* @var \phpbb\request\request_interface */
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var \phpbb\routing\helper
|
||||
*/
|
||||
protected $routing_helper;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\template\template $template Template object
|
||||
* @param \phpbb\user $user User object
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\symfony_request $symfony_request Symfony Request object
|
||||
* @param \phpbb\request\request_interface $request phpBB request object
|
||||
* @param \phpbb\routing\helper $routing_helper Helper to generate the routes
|
||||
*/
|
||||
public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\config\config $config, \phpbb\symfony_request $symfony_request, \phpbb\request\request_interface $request, \phpbb\routing\helper $routing_helper)
|
||||
{
|
||||
$this->template = $template;
|
||||
$this->user = $user;
|
||||
$this->config = $config;
|
||||
$this->symfony_request = $symfony_request;
|
||||
$this->request = $request;
|
||||
$this->routing_helper = $routing_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automate setting up the page and creating the response object.
|
||||
*
|
||||
* @param string $template_file The template handle to render
|
||||
* @param string $page_title The title of the page to output
|
||||
* @param int $status_code The status code to be sent to the page header
|
||||
* @param bool $display_online_list Do we display online users list
|
||||
* @param int $item_id Restrict online users to item id
|
||||
* @param string $item Restrict online users to a certain session item, e.g. forum for session_forum_id
|
||||
* @param bool $send_headers Whether headers should be sent by page_header(). Defaults to false for controllers.
|
||||
*
|
||||
* @return Response object containing rendered page
|
||||
*/
|
||||
public function render($template_file, $page_title = '', $status_code = 200, $display_online_list = false, $item_id = 0, $item = 'forum', $send_headers = false)
|
||||
{
|
||||
page_header($page_title, $display_online_list, $item_id, $item, $send_headers);
|
||||
|
||||
$this->template->set_filenames(array(
|
||||
'body' => $template_file,
|
||||
));
|
||||
|
||||
page_footer(true, false, false);
|
||||
|
||||
$headers = !empty($this->user->data['is_bot']) ? array('X-PHPBB-IS-BOT' => 'yes') : array();
|
||||
|
||||
return new Response($this->template->assign_display('body'), $status_code, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a URL to a route
|
||||
*
|
||||
* @param string $route Name of the route to travel
|
||||
* @param array $params String or array of additional url parameters
|
||||
* @param bool $is_amp Is url using & (true) or & (false)
|
||||
* @param string|bool $session_id Possibility to use a custom session id instead of the global one
|
||||
* @param bool|string $reference_type The type of reference to be generated (one of the constants)
|
||||
* @return string The URL already passed through append_sid()
|
||||
*/
|
||||
public function route($route, array $params = array(), $is_amp = true, $session_id = false, $reference_type = UrlGeneratorInterface::ABSOLUTE_PATH)
|
||||
{
|
||||
return $this->routing_helper->route($route, $params, $is_amp, $session_id, $reference_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an error, effectively the same thing as trigger_error
|
||||
*
|
||||
* @param string $message The error message
|
||||
* @param int $code The error code (e.g. 404, 500, 503, etc.)
|
||||
* @return Response A Response instance
|
||||
*
|
||||
* @deprecated 3.1.3 (To be removed: 4.0.0) Use exceptions instead.
|
||||
*/
|
||||
public function error($message, $code = 500)
|
||||
{
|
||||
return $this->message($message, array(), 'INFORMATION', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a message
|
||||
*
|
||||
* In case of an error, please throw an exception instead
|
||||
*
|
||||
* @param string $message The message to display (must be a language variable)
|
||||
* @param array $parameters The parameters to use with the language var
|
||||
* @param string $title Title for the message (must be a language variable)
|
||||
* @param int $code The HTTP status code (e.g. 404, 500, 503, etc.)
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
public function message($message, array $parameters = array(), $title = 'INFORMATION', $code = 200)
|
||||
{
|
||||
array_unshift($parameters, $message);
|
||||
$message_text = call_user_func_array(array($this->user, 'lang'), $parameters);
|
||||
$message_title = $this->user->lang($title);
|
||||
|
||||
if ($this->request->is_ajax())
|
||||
{
|
||||
global $refresh_data;
|
||||
|
||||
return new JsonResponse(
|
||||
array(
|
||||
'MESSAGE_TITLE' => $message_title,
|
||||
'MESSAGE_TEXT' => $message_text,
|
||||
'S_USER_WARNING' => false,
|
||||
'S_USER_NOTICE' => false,
|
||||
'REFRESH_DATA' => (!empty($refresh_data)) ? $refresh_data : null
|
||||
),
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
$this->template->assign_vars(array(
|
||||
'MESSAGE_TEXT' => $message_text,
|
||||
'MESSAGE_TITLE' => $message_title,
|
||||
));
|
||||
|
||||
return $this->render('message_body.html', $message_title, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns automatic refresh time meta tag in template
|
||||
*
|
||||
* @param int $time time in seconds, when redirection should occur
|
||||
* @param string $url the URL where the user should be redirected
|
||||
* @return null
|
||||
*/
|
||||
public function assign_meta_refresh_var($time, $url)
|
||||
{
|
||||
$this->template->assign_vars(array(
|
||||
'META' => '<meta http-equiv="refresh" content="' . $time . '; url=' . $url . '" />',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_current_url()
|
||||
{
|
||||
return generate_board_url(true) . $this->request->escape($this->symfony_request->getRequestUri(), true);
|
||||
}
|
||||
}
|
||||
40
install/update/new/phpbb/cron/controller/cron.php
Normal file
40
install/update/new/phpbb/cron/controller/cron.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\cron\controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Controller for running cron jobs
|
||||
*/
|
||||
class cron
|
||||
{
|
||||
/**
|
||||
* Handles CRON requests
|
||||
*
|
||||
* @param string $cron_type
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($cron_type)
|
||||
{
|
||||
$response = new Response();
|
||||
$response->headers->set('Cache-Control', 'no-cache');
|
||||
$response->headers->set('Content-type', 'image/gif');
|
||||
$response->headers->set('Content-length', '43');
|
||||
$response->setContent(base64_decode('R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
103
install/update/new/phpbb/cron/event/cron_runner_listener.php
Normal file
103
install/update/new/phpbb/cron/event/cron_runner_listener.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\cron\event;
|
||||
|
||||
use phpbb\cron\manager;
|
||||
use phpbb\lock\db;
|
||||
use phpbb\request\request_interface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
|
||||
|
||||
/**
|
||||
* Event listener that executes cron tasks, after the response was served
|
||||
*/
|
||||
class cron_runner_listener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\lock\db
|
||||
*/
|
||||
private $cron_lock;
|
||||
|
||||
/**
|
||||
* @var \phpbb\cron\manager
|
||||
*/
|
||||
private $cron_manager;
|
||||
|
||||
/**
|
||||
* @var \phpbb\request\request_interface
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param db $lock
|
||||
* @param manager $manager
|
||||
* @param request_interface $request
|
||||
*/
|
||||
public function __construct(db $lock, manager $manager, request_interface $request)
|
||||
{
|
||||
$this->cron_lock = $lock;
|
||||
$this->cron_manager = $manager;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the cron job after the response was sent
|
||||
*
|
||||
* @param PostResponseEvent $event The event
|
||||
*/
|
||||
public function on_kernel_terminate(PostResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
$controller_name = $request->get('_route');
|
||||
|
||||
if ($controller_name !== 'phpbb_cron_run')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$cron_type = $request->get('cron_type');
|
||||
|
||||
if ($this->cron_lock->acquire())
|
||||
{
|
||||
$task = $this->cron_manager->find_task($cron_type);
|
||||
if ($task)
|
||||
{
|
||||
if ($task->is_parametrized())
|
||||
{
|
||||
$task->parse_parameters($this->request);
|
||||
}
|
||||
|
||||
if ($task->is_ready())
|
||||
{
|
||||
$task->run();
|
||||
}
|
||||
|
||||
$this->cron_lock->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::TERMINATE => 'on_kernel_terminate',
|
||||
);
|
||||
}
|
||||
}
|
||||
164
install/update/new/phpbb/cron/manager.php
Normal file
164
install/update/new/phpbb/cron/manager.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\cron;
|
||||
|
||||
use phpbb\cron\task\wrapper;
|
||||
use phpbb\routing\helper;
|
||||
|
||||
/**
|
||||
* Cron manager class.
|
||||
*
|
||||
* Finds installed cron tasks, stores task objects, provides task selection.
|
||||
*/
|
||||
class manager
|
||||
{
|
||||
/**
|
||||
* @var helper
|
||||
*/
|
||||
protected $routing_helper;
|
||||
|
||||
/**
|
||||
* Set of \phpbb\cron\task\wrapper objects.
|
||||
* Array holding all tasks that have been found.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tasks = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Constructor. Loads all available tasks.
|
||||
*
|
||||
* @param array|\Traversable $tasks Provides an iterable set of task names
|
||||
* @param helper $routing_helper Routing helper
|
||||
* @param string $phpbb_root_path Relative path to phpBB root
|
||||
* @param string $php_ext PHP file extension
|
||||
*/
|
||||
public function __construct($tasks, helper $routing_helper, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->routing_helper = $routing_helper;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
$this->load_tasks($tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads tasks given by name, wraps them
|
||||
* and puts them into $this->tasks.
|
||||
*
|
||||
* @param array|\Traversable $tasks Array of instances of \phpbb\cron\task\task
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function load_tasks($tasks)
|
||||
{
|
||||
foreach ($tasks as $task)
|
||||
{
|
||||
$this->tasks[] = $this->wrap_task($task);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a task that is ready to run.
|
||||
*
|
||||
* If several tasks are ready, any one of them could be returned.
|
||||
*
|
||||
* If no tasks are ready, null is returned.
|
||||
*
|
||||
* @return \phpbb\cron\task\wrapper|null
|
||||
*/
|
||||
public function find_one_ready_task()
|
||||
{
|
||||
shuffle($this->tasks);
|
||||
foreach ($this->tasks as $task)
|
||||
{
|
||||
if ($task->is_ready())
|
||||
{
|
||||
return $task;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all tasks that are ready to run.
|
||||
*
|
||||
* @return array List of tasks which are ready to run (wrapped in \phpbb\cron\task\wrapper).
|
||||
*/
|
||||
public function find_all_ready_tasks()
|
||||
{
|
||||
$tasks = array();
|
||||
foreach ($this->tasks as $task)
|
||||
{
|
||||
if ($task->is_ready())
|
||||
{
|
||||
$tasks[] = $task;
|
||||
}
|
||||
}
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a task by name.
|
||||
*
|
||||
* If there is no task with the specified name, null is returned.
|
||||
*
|
||||
* Web runner uses this method to resolve names to tasks.
|
||||
*
|
||||
* @param string $name Name of the task to look up.
|
||||
* @return \phpbb\cron\task\wrapper A wrapped task corresponding to the given name, or null.
|
||||
*/
|
||||
public function find_task($name)
|
||||
{
|
||||
foreach ($this->tasks as $task)
|
||||
{
|
||||
if ($task->get_name() == $name)
|
||||
{
|
||||
return $task;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all tasks and return them.
|
||||
*
|
||||
* @return array List of all tasks.
|
||||
*/
|
||||
public function get_tasks()
|
||||
{
|
||||
return $this->tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a task inside an instance of \phpbb\cron\task\wrapper.
|
||||
*
|
||||
* @param \phpbb\cron\task\task $task The task.
|
||||
* @return \phpbb\cron\task\wrapper The wrapped task.
|
||||
*/
|
||||
public function wrap_task(\phpbb\cron\task\task $task)
|
||||
{
|
||||
return new wrapper($task, $this->routing_helper, $this->phpbb_root_path, $this->php_ext);
|
||||
}
|
||||
}
|
||||
130
install/update/new/phpbb/cron/task/core/update_hashes.php
Normal file
130
install/update/new/phpbb/cron/task/core/update_hashes.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\cron\task\core;
|
||||
|
||||
/**
|
||||
* Update old hashes to the current default hashing algorithm
|
||||
*
|
||||
* It is intended to gradually update all "old" style hashes to the
|
||||
* current default hashing algorithm.
|
||||
*/
|
||||
class update_hashes extends \phpbb\cron\task\base
|
||||
{
|
||||
/** @var \phpbb\config\config */
|
||||
protected $config;
|
||||
|
||||
/** @var \phpbb\db\driver\driver_interface */
|
||||
protected $db;
|
||||
|
||||
/** @var \phpbb\lock\db */
|
||||
protected $update_lock;
|
||||
|
||||
/** @var \phpbb\passwords\manager */
|
||||
protected $passwords_manager;
|
||||
|
||||
/** @var string Default hashing type */
|
||||
protected $default_type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \phpbb\config\config $config
|
||||
* @param \phpbb\db\driver\driver_interface $db
|
||||
* @param \phpbb\lock\db $update_lock
|
||||
* @param \phpbb\passwords\manager $passwords_manager
|
||||
* @param array $hashing_algorithms Hashing driver
|
||||
* service collection
|
||||
* @param array $defaults Default password types
|
||||
*/
|
||||
public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\lock\db $update_lock, \phpbb\passwords\manager $passwords_manager, $hashing_algorithms, $defaults)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->passwords_manager = $passwords_manager;
|
||||
$this->update_lock = $update_lock;
|
||||
|
||||
foreach ($defaults as $type)
|
||||
{
|
||||
if ($hashing_algorithms[$type]->is_supported() && !$hashing_algorithms[$type] instanceof \phpbb\passwords\driver\base_native)
|
||||
{
|
||||
$this->default_type = $type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_runnable()
|
||||
{
|
||||
return !$this->config['use_system_cron'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function should_run()
|
||||
{
|
||||
if (!empty($this->config['update_hashes_lock']))
|
||||
{
|
||||
$last_run = explode(' ', $this->config['update_hashes_lock']);
|
||||
if ($last_run[0] + 60 >= time())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->config['enable_update_hashes'] && $this->config['update_hashes_last_cron'] < (time() - 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if ($this->update_lock->acquire())
|
||||
{
|
||||
$sql = 'SELECT user_id, user_password
|
||||
FROM ' . USERS_TABLE . '
|
||||
WHERE user_password ' . $this->db->sql_like_expression('$H$' . $this->db->get_any_char()) . '
|
||||
OR user_password ' . $this->db->sql_like_expression('$CP$' . $this->db->get_any_char());
|
||||
$result = $this->db->sql_query_limit($sql, 20);
|
||||
|
||||
$affected_rows = 0;
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$new_hash = $this->passwords_manager->hash($row['user_password'], array($this->default_type));
|
||||
|
||||
// Increase number so we know that users were selected from the database
|
||||
$affected_rows++;
|
||||
|
||||
$sql = 'UPDATE ' . USERS_TABLE . "
|
||||
SET user_password = '" . $this->db->sql_escape($new_hash) . "'
|
||||
WHERE user_id = " . (int) $row['user_id'];
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
$this->config->set('update_hashes_last_cron', time());
|
||||
$this->update_lock->release();
|
||||
|
||||
// Stop cron for good once all hashes are converted
|
||||
if ($affected_rows === 0)
|
||||
{
|
||||
$this->config->set('enable_update_hashes', '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
install/update/new/phpbb/cron/task/wrapper.php
Normal file
117
install/update/new/phpbb/cron/task/wrapper.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\cron\task;
|
||||
|
||||
use phpbb\routing\helper;
|
||||
|
||||
/**
|
||||
* Cron task wrapper class.
|
||||
* Enhances cron tasks with convenience methods that work identically for all tasks.
|
||||
*/
|
||||
class wrapper
|
||||
{
|
||||
/**
|
||||
* @var helper
|
||||
*/
|
||||
protected $routing_helper;
|
||||
|
||||
/**
|
||||
* @var task
|
||||
*/
|
||||
protected $task;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Wraps a task $task, which must implement cron_task interface.
|
||||
*
|
||||
* @param task $task The cron task to wrap.
|
||||
* @param helper $routing_helper Routing helper for route generation
|
||||
* @param string $phpbb_root_path Relative path to phpBB root
|
||||
* @param string $php_ext PHP file extension
|
||||
*/
|
||||
public function __construct(task $task, helper $routing_helper, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->task = $task;
|
||||
$this->routing_helper = $routing_helper;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the wrapped task is parametrised.
|
||||
*
|
||||
* Parametrized tasks accept parameters during initialization and must
|
||||
* normally be scheduled with parameters.
|
||||
*
|
||||
* @return bool Whether or not this task is parametrized.
|
||||
*/
|
||||
public function is_parametrized()
|
||||
{
|
||||
return $this->task instanceof parametrized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the wrapped task is ready to run.
|
||||
*
|
||||
* A task is ready to run when it is runnable according to current configuration
|
||||
* and enough time has passed since it was last run.
|
||||
*
|
||||
* @return bool Whether the wrapped task is ready to run.
|
||||
*/
|
||||
public function is_ready()
|
||||
{
|
||||
return $this->task->is_runnable() && $this->task->should_run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a url through which this task may be invoked via web.
|
||||
*
|
||||
* When system cron is not in use, running a cron task is accomplished
|
||||
* by outputting an image with the url returned by this function as
|
||||
* source.
|
||||
*
|
||||
* @return string URL through which this task may be invoked.
|
||||
*/
|
||||
public function get_url()
|
||||
{
|
||||
$params['cron_type'] = $this->get_name();
|
||||
if ($this->is_parametrized())
|
||||
{
|
||||
$params = array_merge($params, $this->task->get_parameters());
|
||||
}
|
||||
|
||||
return $this->routing_helper->route('phpbb_cron_run', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards all other method calls to the wrapped task implementation.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $args)
|
||||
{
|
||||
return call_user_func_array(array($this->task, $name), $args);
|
||||
}
|
||||
}
|
||||
1246
install/update/new/phpbb/db/driver/driver.php
Normal file
1246
install/update/new/phpbb/db/driver/driver.php
Normal file
File diff suppressed because it is too large
Load Diff
467
install/update/new/phpbb/db/driver/driver_interface.php
Normal file
467
install/update/new/phpbb/db/driver/driver_interface.php
Normal file
@@ -0,0 +1,467 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
interface driver_interface
|
||||
{
|
||||
/**
|
||||
* Set value for load_time debug parameter
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function set_debug_load_time($value);
|
||||
|
||||
/**
|
||||
* Set value for sql_explain debug parameter
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function set_debug_sql_explain($value);
|
||||
|
||||
/**
|
||||
* Gets the name of the sql layer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_sql_layer();
|
||||
|
||||
/**
|
||||
* Gets the name of the database.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_db_name();
|
||||
|
||||
/**
|
||||
* Wildcards for matching any (%) character within LIKE expressions
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_any_char();
|
||||
|
||||
/**
|
||||
* Wildcards for matching exactly one (_) character within LIKE expressions
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_one_char();
|
||||
|
||||
/**
|
||||
* Gets the time spent into the queries
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_sql_time();
|
||||
|
||||
/**
|
||||
* Gets the connect ID.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_db_connect_id();
|
||||
|
||||
/**
|
||||
* Indicates if an error was triggered.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_sql_error_triggered();
|
||||
|
||||
/**
|
||||
* Gets the last faulty query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_sql_error_sql();
|
||||
|
||||
/**
|
||||
* Indicates if we are in a transaction.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_transaction();
|
||||
|
||||
/**
|
||||
* Gets the returned error.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sql_error_returned();
|
||||
|
||||
/**
|
||||
* Indicates if multiple insertion can be used
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_multi_insert();
|
||||
|
||||
/**
|
||||
* Set if multiple insertion can be used
|
||||
*
|
||||
* @param bool $multi_insert
|
||||
*/
|
||||
public function set_multi_insert($multi_insert);
|
||||
|
||||
/**
|
||||
* Gets the exact number of rows in a specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
* @return string Exact number of rows in $table_name.
|
||||
*/
|
||||
public function get_row_count($table_name);
|
||||
|
||||
/**
|
||||
* Gets the estimated number of rows in a specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
* @return string Number of rows in $table_name.
|
||||
* Prefixed with ~ if estimated (otherwise exact).
|
||||
*/
|
||||
public function get_estimated_row_count($table_name);
|
||||
|
||||
/**
|
||||
* Run LOWER() on DB column of type text (i.e. neither varchar nor char).
|
||||
*
|
||||
* @param string $column_name The column name to use
|
||||
* @return string A SQL statement like "LOWER($column_name)"
|
||||
*/
|
||||
public function sql_lower_text($column_name);
|
||||
|
||||
/**
|
||||
* Display sql error page
|
||||
*
|
||||
* @param string $sql The SQL query causing the error
|
||||
* @return mixed Returns the full error message, if $this->return_on_error
|
||||
* is set, null otherwise
|
||||
*/
|
||||
public function sql_error($sql = '');
|
||||
|
||||
/**
|
||||
* Returns whether results of a query need to be buffered to run a
|
||||
* transaction while iterating over them.
|
||||
*
|
||||
* @return bool Whether buffering is required.
|
||||
*/
|
||||
public function sql_buffer_nested_transactions();
|
||||
|
||||
/**
|
||||
* Run binary OR operator on DB column.
|
||||
*
|
||||
* @param string $column_name The column name to use
|
||||
* @param int $bit The value to use for the OR operator,
|
||||
* will be converted to (1 << $bit). Is used by options,
|
||||
* using the number schema... 0, 1, 2...29
|
||||
* @param string $compare Any custom SQL code after the check (e.g. "= 0")
|
||||
* @return string A SQL statement like "$column | (1 << $bit) {$compare}"
|
||||
*/
|
||||
public function sql_bit_or($column_name, $bit, $compare = '');
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
*
|
||||
* @param bool $raw Only return the fetched sql_server_version
|
||||
* @param bool $use_cache Is it safe to retrieve the value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
public function sql_server_info($raw = false, $use_cache = true);
|
||||
|
||||
/**
|
||||
* Return on error or display error message
|
||||
*
|
||||
* @param bool $fail Should we return on errors, or stop
|
||||
* @return null
|
||||
*/
|
||||
public function sql_return_on_error($fail = false);
|
||||
|
||||
/**
|
||||
* Build sql statement from an array
|
||||
*
|
||||
* @param string $query Should be on of the following strings:
|
||||
* INSERT, INSERT_SELECT, UPDATE, SELECT, DELETE
|
||||
* @param array $assoc_ary Array with "column => value" pairs
|
||||
* @return string A SQL statement like "c1 = 'a' AND c2 = 'b'"
|
||||
*/
|
||||
public function sql_build_array($query, $assoc_ary = array());
|
||||
|
||||
/**
|
||||
* Fetch all rows
|
||||
*
|
||||
* @param mixed $query_id Already executed query to get the rows from,
|
||||
* if false, the last query will be used.
|
||||
* @return mixed Nested array if the query had rows, false otherwise
|
||||
*/
|
||||
public function sql_fetchrowset($query_id = false);
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
*
|
||||
* @param string $status Should be one of the following strings:
|
||||
* begin, commit, rollback
|
||||
* @return mixed Buffered, seekable result handle, false on error
|
||||
*/
|
||||
public function sql_transaction($status = 'begin');
|
||||
|
||||
/**
|
||||
* Build a concatenated expression
|
||||
*
|
||||
* @param string $expr1 Base SQL expression where we append the second one
|
||||
* @param string $expr2 SQL expression that is appended to the first expression
|
||||
* @return string Concatenated string
|
||||
*/
|
||||
public function sql_concatenate($expr1, $expr2);
|
||||
|
||||
/**
|
||||
* Build a case expression
|
||||
*
|
||||
* Note: The two statements action_true and action_false must have the same
|
||||
* data type (int, vchar, ...) in the database!
|
||||
*
|
||||
* @param string $condition The condition which must be true,
|
||||
* to use action_true rather then action_else
|
||||
* @param string $action_true SQL expression that is used, if the condition is true
|
||||
* @param mixed $action_false SQL expression that is used, if the condition is false
|
||||
* @return string CASE expression including the condition and statements
|
||||
*/
|
||||
public function sql_case($condition, $action_true, $action_false = false);
|
||||
|
||||
/**
|
||||
* Build sql statement from array for select and select distinct statements
|
||||
*
|
||||
* Possible query values: SELECT, SELECT_DISTINCT
|
||||
*
|
||||
* @param string $query Should be one of: SELECT, SELECT_DISTINCT
|
||||
* @param array $array Array with the query data:
|
||||
* SELECT A comma imploded list of columns to select
|
||||
* FROM Array with "table => alias" pairs,
|
||||
* (alias can also be an array)
|
||||
* Optional: LEFT_JOIN Array of join entries:
|
||||
* FROM Table that should be joined
|
||||
* ON Condition for the join
|
||||
* Optional: WHERE Where SQL statement
|
||||
* Optional: GROUP_BY Group by SQL statement
|
||||
* Optional: ORDER_BY Order by SQL statement
|
||||
* @return string A SQL statement ready for execution
|
||||
*/
|
||||
public function sql_build_query($query, $array);
|
||||
|
||||
/**
|
||||
* Fetch field
|
||||
* if rownum is false, the current row is used, else it is pointing to the row (zero-based)
|
||||
*
|
||||
* @param string $field Name of the column
|
||||
* @param mixed $rownum Row number, if false the current row will be used
|
||||
* and the row curser will point to the next row
|
||||
* Note: $rownum is 0 based
|
||||
* @param mixed $query_id Already executed query to get the rows from,
|
||||
* if false, the last query will be used.
|
||||
* @return mixed String value of the field in the selected row,
|
||||
* false, if the row does not exist
|
||||
*/
|
||||
public function sql_fetchfield($field, $rownum = false, $query_id = false);
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*
|
||||
* @param mixed $query_id Already executed query to get the rows from,
|
||||
* if false, the last query will be used.
|
||||
* @return mixed Array with the current row,
|
||||
* false, if the row does not exist
|
||||
*/
|
||||
public function sql_fetchrow($query_id = false);
|
||||
|
||||
/**
|
||||
* Returns SQL string to cast a string expression to an int.
|
||||
*
|
||||
* @param string $expression An expression evaluating to string
|
||||
* @return string Expression returning an int
|
||||
*/
|
||||
public function cast_expr_to_bigint($expression);
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*
|
||||
* @return string Autoincrement value of the last inserted row
|
||||
*/
|
||||
public function sql_nextid();
|
||||
|
||||
/**
|
||||
* Add to query count
|
||||
*
|
||||
* @param bool $cached Is this query cached?
|
||||
* @return null
|
||||
*/
|
||||
public function sql_add_num_queries($cached = false);
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*
|
||||
* @param string $query The SQL query to execute
|
||||
* @param int $total The number of rows to select
|
||||
* @param int $offset
|
||||
* @param int $cache_ttl Either 0 to avoid caching or
|
||||
* the time in seconds which the result shall be kept in cache
|
||||
* @return mixed Buffered, seekable result handle, false on error
|
||||
*/
|
||||
public function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0);
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query The SQL query to execute
|
||||
* @param int $cache_ttl Either 0 to avoid caching or
|
||||
* the time in seconds which the result shall be kept in cache
|
||||
* @return mixed Buffered, seekable result handle, false on error
|
||||
*/
|
||||
public function sql_query($query = '', $cache_ttl = 0);
|
||||
|
||||
/**
|
||||
* Returns SQL string to cast an integer expression to a string.
|
||||
*
|
||||
* @param string $expression An expression evaluating to int
|
||||
* @return string Expression returning a string
|
||||
*/
|
||||
public function cast_expr_to_string($expression);
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*
|
||||
* @param string $sqlserver Address of the database server
|
||||
* @param string $sqluser User name of the SQL user
|
||||
* @param string $sqlpassword Password of the SQL user
|
||||
* @param string $database Name of the database
|
||||
* @param mixed $port Port of the database server
|
||||
* @param bool $persistency
|
||||
* @param bool $new_link Should a new connection be established
|
||||
* @return mixed Connection ID on success, string error message otherwise
|
||||
*/
|
||||
public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false);
|
||||
|
||||
/**
|
||||
* Run binary AND operator on DB column.
|
||||
* Results in sql statement: "{$column_name} & (1 << {$bit}) {$compare}"
|
||||
*
|
||||
* @param string $column_name The column name to use
|
||||
* @param int $bit The value to use for the AND operator,
|
||||
* will be converted to (1 << $bit). Is used by
|
||||
* options, using the number schema: 0, 1, 2...29
|
||||
* @param string $compare Any custom SQL code after the check (for example "= 0")
|
||||
* @return string A SQL statement like: "{$column} & (1 << {$bit}) {$compare}"
|
||||
*/
|
||||
public function sql_bit_and($column_name, $bit, $compare = '');
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*
|
||||
* @param mixed $query_id Already executed query result,
|
||||
* if false, the last query will be used.
|
||||
* @return null
|
||||
*/
|
||||
public function sql_freeresult($query_id = false);
|
||||
|
||||
/**
|
||||
* Return number of sql queries and cached sql queries used
|
||||
*
|
||||
* @param bool $cached Should we return the number of cached or normal queries?
|
||||
* @return int Number of queries that have been executed
|
||||
*/
|
||||
public function sql_num_queries($cached = false);
|
||||
|
||||
/**
|
||||
* Run more than one insert statement.
|
||||
*
|
||||
* @param string $table Table name to run the statements on
|
||||
* @param array $sql_ary Multi-dimensional array holding the statement data
|
||||
* @return bool false if no statements were executed.
|
||||
*/
|
||||
public function sql_multi_insert($table, $sql_ary);
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*
|
||||
* @return mixed Number of the affected rows by the last query
|
||||
* false if no query has been run before
|
||||
*/
|
||||
public function sql_affectedrows();
|
||||
|
||||
/**
|
||||
* DBAL garbage collection, close SQL connection
|
||||
*
|
||||
* @return mixed False if no connection was opened before,
|
||||
* Server response otherwise
|
||||
*/
|
||||
public function sql_close();
|
||||
|
||||
/**
|
||||
* Seek to given row number
|
||||
*
|
||||
* @param mixed $rownum Row number the curser should point to
|
||||
* Note: $rownum is 0 based
|
||||
* @param mixed $query_id ID of the query to set the row cursor on
|
||||
* if false, the last query will be used.
|
||||
* $query_id will then be set correctly
|
||||
* @return bool False if something went wrong
|
||||
*/
|
||||
public function sql_rowseek($rownum, &$query_id);
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*
|
||||
* @param string $msg String to be escaped
|
||||
* @return string Escaped version of $msg
|
||||
*/
|
||||
public function sql_escape($msg);
|
||||
|
||||
/**
|
||||
* Correctly adjust LIKE expression for special characters
|
||||
* Some DBMS are handling them in a different way
|
||||
*
|
||||
* @param string $expression The expression to use. Every wildcard is
|
||||
* escaped, except $this->any_char and $this->one_char
|
||||
* @return string A SQL statement like: "LIKE 'bertie_%'"
|
||||
*/
|
||||
public function sql_like_expression($expression);
|
||||
|
||||
/**
|
||||
* Correctly adjust NOT LIKE expression for special characters
|
||||
* Some DBMS are handling them in a different way
|
||||
*
|
||||
* @param string $expression The expression to use. Every wildcard is
|
||||
* escaped, except $this->any_char and $this->one_char
|
||||
* @return string A SQL statement like: "NOT LIKE 'bertie_%'"
|
||||
*/
|
||||
public function sql_not_like_expression($expression);
|
||||
|
||||
/**
|
||||
* Explain queries
|
||||
*
|
||||
* @param string $mode Available modes: display, start, stop,
|
||||
* add_select_row, fromcache, record_fromcache
|
||||
* @param string $query The Query that should be explained
|
||||
* @return mixed Either a full HTML page, boolean or null
|
||||
*/
|
||||
public function sql_report($mode, $query = '');
|
||||
|
||||
/**
|
||||
* Build IN or NOT IN sql comparison string, uses <> or = on single element
|
||||
* arrays to improve comparison speed
|
||||
*
|
||||
* @param string $field Name of the sql column that shall be compared
|
||||
* @param array $array Array of values that are (not) allowed
|
||||
* @param bool $negate true for NOT IN (), false for IN ()
|
||||
* @param bool $allow_empty_set If true, allow $array to be empty,
|
||||
* this function will return 1=1 or 1=0 then.
|
||||
* @return string A SQL statement like: "IN (1, 2, 3, 4)" or "= 1"
|
||||
*/
|
||||
public function sql_in_set($field, $array, $negate = false, $allow_empty_set = false);
|
||||
}
|
||||
459
install/update/new/phpbb/db/driver/factory.php
Normal file
459
install/update/new/phpbb/db/driver/factory.php
Normal file
@@ -0,0 +1,459 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
use \Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Database Abstraction Layer
|
||||
*/
|
||||
class factory implements driver_interface
|
||||
{
|
||||
/**
|
||||
* @var driver_interface
|
||||
*/
|
||||
protected $driver = null;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ContainerInterface $container A ContainerInterface instance
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current driver (and retrieved it from the container if necessary)
|
||||
*
|
||||
* @return driver_interface
|
||||
*/
|
||||
protected function get_driver()
|
||||
{
|
||||
if ($this->driver === null)
|
||||
{
|
||||
$this->driver = $this->container->get('dbal.conn.driver');
|
||||
}
|
||||
|
||||
return $this->driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current driver
|
||||
*
|
||||
* @param driver_interface $driver
|
||||
*/
|
||||
public function set_driver(driver_interface $driver)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set_debug_load_time($value)
|
||||
{
|
||||
$this->get_driver()->set_debug_load_time($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set_debug_sql_explain($value)
|
||||
{
|
||||
$this->get_driver()->set_debug_sql_explain($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_sql_layer()
|
||||
{
|
||||
return $this->get_driver()->get_sql_layer();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_db_name()
|
||||
{
|
||||
return $this->get_driver()->get_db_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_any_char()
|
||||
{
|
||||
return $this->get_driver()->get_any_char();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_one_char()
|
||||
{
|
||||
return $this->get_driver()->get_one_char();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_db_connect_id()
|
||||
{
|
||||
return $this->get_driver()->get_db_connect_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_sql_error_triggered()
|
||||
{
|
||||
return $this->get_driver()->get_sql_error_triggered();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_sql_error_sql()
|
||||
{
|
||||
return $this->get_driver()->get_sql_error_sql();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_transaction()
|
||||
{
|
||||
return $this->get_driver()->get_transaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_sql_time()
|
||||
{
|
||||
return $this->get_driver()->get_sql_time();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_sql_error_returned()
|
||||
{
|
||||
return $this->get_driver()->get_sql_error_returned();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_multi_insert()
|
||||
{
|
||||
return $this->get_driver()->get_multi_insert();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set_multi_insert($multi_insert)
|
||||
{
|
||||
$this->get_driver()->set_multi_insert($multi_insert);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_row_count($table_name)
|
||||
{
|
||||
return $this->get_driver()->get_row_count($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_estimated_row_count($table_name)
|
||||
{
|
||||
return $this->get_driver()->get_estimated_row_count($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_lower_text($column_name)
|
||||
{
|
||||
return $this->get_driver()->sql_lower_text($column_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_error($sql = '')
|
||||
{
|
||||
return $this->get_driver()->sql_error($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_buffer_nested_transactions()
|
||||
{
|
||||
return $this->get_driver()->sql_buffer_nested_transactions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_bit_or($column_name, $bit, $compare = '')
|
||||
{
|
||||
return $this->get_driver()->sql_bit_or($column_name, $bit, $compare);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
return $this->get_driver()->sql_server_info($raw, $use_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_return_on_error($fail = false)
|
||||
{
|
||||
return $this->get_driver()->sql_return_on_error($fail);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_build_array($query, $assoc_ary = array())
|
||||
{
|
||||
return $this->get_driver()->sql_build_array($query, $assoc_ary);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_fetchrowset($query_id = false)
|
||||
{
|
||||
return $this->get_driver()->sql_fetchrowset($query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_transaction($status = 'begin')
|
||||
{
|
||||
return $this->get_driver()->sql_transaction($status);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_concatenate($expr1, $expr2)
|
||||
{
|
||||
return $this->get_driver()->sql_concatenate($expr1, $expr2);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_case($condition, $action_true, $action_false = false)
|
||||
{
|
||||
return $this->get_driver()->sql_case($condition, $action_true, $action_false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_build_query($query, $array)
|
||||
{
|
||||
return $this->get_driver()->sql_build_query($query, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_fetchfield($field, $rownum = false, $query_id = false)
|
||||
{
|
||||
return $this->get_driver()->sql_fetchfield($field, $rownum, $query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_fetchrow($query_id = false)
|
||||
{
|
||||
return $this->get_driver()->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cast_expr_to_bigint($expression)
|
||||
{
|
||||
return $this->get_driver()->cast_expr_to_bigint($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_nextid()
|
||||
{
|
||||
return $this->get_driver()->sql_nextid();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_add_num_queries($cached = false)
|
||||
{
|
||||
return $this->get_driver()->sql_add_num_queries($cached);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
return $this->get_driver()->sql_query_limit($query, $total, $offset, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
return $this->get_driver()->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cast_expr_to_string($expression)
|
||||
{
|
||||
return $this->get_driver()->cast_expr_to_string($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
throw new \Exception('Disabled method.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_bit_and($column_name, $bit, $compare = '')
|
||||
{
|
||||
return $this->get_driver()->sql_bit_and($column_name, $bit, $compare);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_freeresult($query_id = false)
|
||||
{
|
||||
return $this->get_driver()->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_num_queries($cached = false)
|
||||
{
|
||||
return $this->get_driver()->sql_num_queries($cached);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_multi_insert($table, $sql_ary)
|
||||
{
|
||||
return $this->get_driver()->sql_multi_insert($table, $sql_ary);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_affectedrows()
|
||||
{
|
||||
return $this->get_driver()->sql_affectedrows();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_close()
|
||||
{
|
||||
return $this->get_driver()->sql_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
return $this->get_driver()->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_escape($msg)
|
||||
{
|
||||
return $this->get_driver()->sql_escape($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_like_expression($expression)
|
||||
{
|
||||
return $this->get_driver()->sql_like_expression($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_not_like_expression($expression)
|
||||
{
|
||||
return $this->get_driver()->sql_not_like_expression($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_report($mode, $query = '')
|
||||
{
|
||||
return $this->get_driver()->sql_report($mode, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
|
||||
{
|
||||
return $this->get_driver()->sql_in_set($field, $array, $negate, $allow_empty_set);
|
||||
}
|
||||
}
|
||||
384
install/update/new/phpbb/db/driver/mssql_odbc.php
Normal file
384
install/update/new/phpbb/db/driver/mssql_odbc.php
Normal file
@@ -0,0 +1,384 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
/**
|
||||
* Unified ODBC functions
|
||||
* Unified ODBC functions support any database having ODBC driver, for example Adabas D, IBM DB2, iODBC, Solid, Sybase SQL Anywhere...
|
||||
* Here we only support MSSQL Server 2000+ because of the provided schema
|
||||
*
|
||||
* @note number of bytes returned for returning data depends on odbc.defaultlrl php.ini setting.
|
||||
* If it is limited to 4K for example only 4K of data is returned max, resulting in incomplete theme data for example.
|
||||
* @note odbc.defaultbinmode may affect UTF8 characters
|
||||
*/
|
||||
class mssql_odbc extends \phpbb\db\driver\mssql_base
|
||||
{
|
||||
var $last_query_text = '';
|
||||
var $connect_error = '';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->dbname = $database;
|
||||
|
||||
$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
|
||||
$this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
|
||||
|
||||
$max_size = @ini_get('odbc.defaultlrl');
|
||||
if (!empty($max_size))
|
||||
{
|
||||
$unit = strtolower(substr($max_size, -1, 1));
|
||||
$max_size = (int) $max_size;
|
||||
|
||||
if ($unit == 'k')
|
||||
{
|
||||
$max_size = floor($max_size / 1024);
|
||||
}
|
||||
else if ($unit == 'g')
|
||||
{
|
||||
$max_size *= 1024;
|
||||
}
|
||||
else if (is_numeric($unit))
|
||||
{
|
||||
$max_size = floor((int) ($max_size . $unit) / 1048576);
|
||||
}
|
||||
$max_size = max(8, $max_size) . 'M';
|
||||
|
||||
@ini_set('odbc.defaultlrl', $max_size);
|
||||
}
|
||||
|
||||
if ($this->persistency)
|
||||
{
|
||||
if (!function_exists('odbc_pconnect'))
|
||||
{
|
||||
$this->connect_error = 'odbc_pconnect function does not exist, is odbc extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$this->db_connect_id = @odbc_pconnect($this->server, $this->user, $sqlpassword);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!function_exists('odbc_connect'))
|
||||
{
|
||||
$this->connect_error = 'odbc_connect function does not exist, is odbc extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$this->db_connect_id = @odbc_connect($this->server, $this->user, $sqlpassword);
|
||||
}
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssqlodbc_version')) === false)
|
||||
{
|
||||
$result_id = @odbc_exec($this->db_connect_id, "SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')");
|
||||
|
||||
$row = false;
|
||||
if ($result_id)
|
||||
{
|
||||
$row = odbc_fetch_array($result_id);
|
||||
odbc_free_result($result_id);
|
||||
}
|
||||
|
||||
$this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mssqlodbc_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
if ($raw)
|
||||
{
|
||||
return $this->sql_server_version;
|
||||
}
|
||||
|
||||
return ($this->sql_server_version) ? 'MSSQL (ODBC)<br />' . $this->sql_server_version : 'MSSQL (ODBC)';
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @odbc_exec($this->db_connect_id, 'BEGIN TRANSACTION');
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @odbc_exec($this->db_connect_id, 'COMMIT TRANSACTION');
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @odbc_exec($this->db_connect_id, 'ROLLBACK TRANSACTION');
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->curtime = microtime(true);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @odbc_exec($this->db_connect_id, $query)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->sql_time += microtime(true) - $this->curtime;
|
||||
}
|
||||
|
||||
if (!$this->query_result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($cache && $cache_ttl)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows)
|
||||
if ($total)
|
||||
{
|
||||
// We need to grab the total number of rows + the offset number of rows to get the correct result
|
||||
if (strpos($query, 'SELECT DISTINCT') === 0)
|
||||
{
|
||||
$query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
$query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->sql_query($query, $cache_ttl);
|
||||
|
||||
// Seek by $offset rows
|
||||
if ($offset)
|
||||
{
|
||||
$this->sql_rowseek($offset, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @odbc_num_rows($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
return ($query_id) ? odbc_fetch_array($query_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$result_id = @odbc_exec($this->db_connect_id, 'SELECT @@IDENTITY');
|
||||
|
||||
if ($result_id)
|
||||
{
|
||||
if (odbc_fetch_array($result_id))
|
||||
{
|
||||
$id = odbc_result($result_id, 1);
|
||||
odbc_free_result($result_id);
|
||||
return $id;
|
||||
}
|
||||
odbc_free_result($result_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return odbc_free_result($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
if (function_exists('odbc_errormsg'))
|
||||
{
|
||||
$error = array(
|
||||
'message' => @odbc_errormsg(),
|
||||
'code' => @odbc_error(),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = array(
|
||||
'message' => $this->connect_error,
|
||||
'code' => '',
|
||||
);
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @odbc_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @odbc_exec($this->db_connect_id, $query);
|
||||
if ($result)
|
||||
{
|
||||
while ($void = odbc_fetch_array($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
odbc_free_result($result);
|
||||
}
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
450
install/update/new/phpbb/db/driver/mssqlnative.php
Normal file
450
install/update/new/phpbb/db/driver/mssqlnative.php
Normal file
@@ -0,0 +1,450 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the MS SQL Server Native database abstraction layer.
|
||||
* PHP mssql native driver required.
|
||||
* @author Chris Pucci
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
class mssqlnative extends \phpbb\db\driver\mssql_base
|
||||
{
|
||||
var $m_insert_id = null;
|
||||
var $last_query_text = '';
|
||||
var $query_options = array();
|
||||
var $connect_error = '';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
// Test for driver support, to avoid suppressed fatal error
|
||||
if (!function_exists('sqlsrv_connect'))
|
||||
{
|
||||
$this->connect_error = 'Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
//set up connection variables
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->dbname = $database;
|
||||
$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
|
||||
$this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
|
||||
|
||||
//connect to database
|
||||
$this->db_connect_id = sqlsrv_connect($this->server, array(
|
||||
'Database' => $this->dbname,
|
||||
'UID' => $this->user,
|
||||
'PWD' => $sqlpassword,
|
||||
'CharacterSet' => 'UTF-8'
|
||||
));
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false)
|
||||
{
|
||||
$arr_server_info = sqlsrv_server_info($this->db_connect_id);
|
||||
$this->sql_server_version = $arr_server_info['SQLServerVersion'];
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mssql_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
if ($raw)
|
||||
{
|
||||
return $this->sql_server_version;
|
||||
}
|
||||
|
||||
return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_buffer_nested_transactions()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return sqlsrv_begin_transaction($this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return sqlsrv_commit($this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return sqlsrv_rollback($this->db_connect_id);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->curtime = microtime(true);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query, array(), $this->query_options)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
// reset options for next query
|
||||
$this->query_options = array();
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->sql_time += microtime(true) - $this->curtime;
|
||||
}
|
||||
|
||||
if (!$this->query_result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($cache && $cache_ttl)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// total == 0 means all results - not zero results
|
||||
if ($offset == 0 && $total !== 0)
|
||||
{
|
||||
if (strpos($query, "SELECT") === false)
|
||||
{
|
||||
$query = "TOP {$total} " . $query;
|
||||
}
|
||||
else
|
||||
{
|
||||
$query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP '.$total, $query);
|
||||
}
|
||||
}
|
||||
else if ($offset > 0)
|
||||
{
|
||||
$query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP(10000000) ', $query);
|
||||
$query = 'SELECT *
|
||||
FROM (SELECT sub2.*, ROW_NUMBER() OVER(ORDER BY sub2.line2) AS line3
|
||||
FROM (SELECT 1 AS line2, sub1.* FROM (' . $query . ') AS sub1) as sub2) AS sub3';
|
||||
|
||||
if ($total > 0)
|
||||
{
|
||||
$query .= ' WHERE line3 BETWEEN ' . ($offset+1) . ' AND ' . ($offset + $total);
|
||||
}
|
||||
else
|
||||
{
|
||||
$query .= ' WHERE line3 > ' . $offset;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->sql_query($query, $cache_ttl);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @sqlsrv_rows_affected($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if (!$query_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
foreach ($row as $key => $value)
|
||||
{
|
||||
$row[$key] = ($value === ' ' || $value === null) ? '' : $value;
|
||||
}
|
||||
|
||||
// remove helper values from LIMIT queries
|
||||
if (isset($row['line2']))
|
||||
{
|
||||
unset($row['line2'], $row['line3']);
|
||||
}
|
||||
}
|
||||
return ($row !== null) ? $row : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$result_id = @sqlsrv_query($this->db_connect_id, 'SELECT @@IDENTITY');
|
||||
|
||||
if ($result_id)
|
||||
{
|
||||
$row = sqlsrv_fetch_array($result_id);
|
||||
$id = $row[0];
|
||||
sqlsrv_free_stmt($result_id);
|
||||
return $id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return sqlsrv_free_stmt($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
if (function_exists('sqlsrv_errors'))
|
||||
{
|
||||
$errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS);
|
||||
$error_message = '';
|
||||
$code = 0;
|
||||
|
||||
if ($errors != null)
|
||||
{
|
||||
foreach ($errors as $error)
|
||||
{
|
||||
$error_message .= "SQLSTATE: " . $error['SQLSTATE'] . "\n";
|
||||
$error_message .= "code: " . $error['code'] . "\n";
|
||||
$code = $error['code'];
|
||||
$error_message .= "message: " . $error['message'] . "\n";
|
||||
}
|
||||
$this->last_error_result = $error_message;
|
||||
$error = $this->last_error_result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
|
||||
}
|
||||
|
||||
$error = array(
|
||||
'message' => $error,
|
||||
'code' => $code,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = array(
|
||||
'message' => $this->connect_error,
|
||||
'code' => '',
|
||||
);
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @sqlsrv_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
$html_table = false;
|
||||
@sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT ON;');
|
||||
if ($result = @sqlsrv_query($this->db_connect_id, $query))
|
||||
{
|
||||
sqlsrv_next_result($result);
|
||||
while ($row = sqlsrv_fetch_array($result))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
sqlsrv_free_stmt($result);
|
||||
}
|
||||
@sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT OFF;');
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @sqlsrv_query($this->db_connect_id, $query);
|
||||
if ($result)
|
||||
{
|
||||
while ($void = sqlsrv_fetch_array($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
sqlsrv_free_stmt($result);
|
||||
}
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method used to retrieve number of rows
|
||||
* Emulates mysql_num_rows
|
||||
* Used in acp_database.php -> write_data_mssqlnative()
|
||||
* Requires a static or keyset cursor to be definde via
|
||||
* mssqlnative_set_query_options()
|
||||
*/
|
||||
function mssqlnative_num_rows($res)
|
||||
{
|
||||
if ($res !== false)
|
||||
{
|
||||
return sqlsrv_num_rows($res);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting mssqlnative specific query options passed to sqlsrv_query as 4th parameter.
|
||||
*/
|
||||
function mssqlnative_set_query_options($options)
|
||||
{
|
||||
$this->query_options = $options;
|
||||
}
|
||||
}
|
||||
492
install/update/new/phpbb/db/driver/mysqli.php
Normal file
492
install/update/new/phpbb/db/driver/mysqli.php
Normal file
@@ -0,0 +1,492 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
/**
|
||||
* MySQLi Database Abstraction Layer
|
||||
* mysqli-extension has to be compiled with:
|
||||
* MySQL 4.1+ or MySQL 5.0+
|
||||
*/
|
||||
class mysqli extends \phpbb\db\driver\mysql_base
|
||||
{
|
||||
var $multi_insert = true;
|
||||
var $connect_error = '';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
if (!function_exists('mysqli_connect'))
|
||||
{
|
||||
$this->connect_error = 'mysqli_connect function does not exist, is mysqli extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
|
||||
// If persistent connection, set dbhost to localhost when empty and prepend it with 'p:' prefix
|
||||
$this->server = ($this->persistency) ? 'p:' . (($sqlserver) ? $sqlserver : 'localhost') : $sqlserver;
|
||||
|
||||
$this->dbname = $database;
|
||||
$port = (!$port) ? null : $port;
|
||||
|
||||
// If port is set and it is not numeric, most likely mysqli socket is set.
|
||||
// Try to map it to the $socket parameter.
|
||||
$socket = null;
|
||||
if ($port)
|
||||
{
|
||||
if (is_numeric($port))
|
||||
{
|
||||
$port = (int) $port;
|
||||
}
|
||||
else
|
||||
{
|
||||
$socket = $port;
|
||||
$port = null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db_connect_id = mysqli_init();
|
||||
|
||||
if (!@mysqli_real_connect($this->db_connect_id, $this->server, $this->user, $sqlpassword, $this->dbname, $port, $socket, MYSQLI_CLIENT_FOUND_ROWS))
|
||||
{
|
||||
$this->db_connect_id = '';
|
||||
}
|
||||
|
||||
if ($this->db_connect_id && $this->dbname != '')
|
||||
{
|
||||
// Disable loading local files on client side
|
||||
@mysqli_options($this->db_connect_id, MYSQLI_OPT_LOCAL_INFILE, false);
|
||||
|
||||
@mysqli_query($this->db_connect_id, "SET NAMES 'utf8'");
|
||||
|
||||
// enforce strict mode on databases that support it
|
||||
if (version_compare($this->sql_server_info(true), '5.0.2', '>='))
|
||||
{
|
||||
$result = @mysqli_query($this->db_connect_id, 'SELECT @@session.sql_mode AS sql_mode');
|
||||
if ($result)
|
||||
{
|
||||
$row = mysqli_fetch_assoc($result);
|
||||
mysqli_free_result($result);
|
||||
|
||||
$modes = array_map('trim', explode(',', $row['sql_mode']));
|
||||
}
|
||||
else
|
||||
{
|
||||
$modes = array();
|
||||
}
|
||||
|
||||
// TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
|
||||
if (!in_array('TRADITIONAL', $modes))
|
||||
{
|
||||
if (!in_array('STRICT_ALL_TABLES', $modes))
|
||||
{
|
||||
$modes[] = 'STRICT_ALL_TABLES';
|
||||
}
|
||||
|
||||
if (!in_array('STRICT_TRANS_TABLES', $modes))
|
||||
{
|
||||
$modes[] = 'STRICT_TRANS_TABLES';
|
||||
}
|
||||
}
|
||||
|
||||
$mode = implode(',', $modes);
|
||||
@mysqli_query($this->db_connect_id, "SET SESSION sql_mode='{$mode}'");
|
||||
}
|
||||
return $this->db_connect_id;
|
||||
}
|
||||
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysqli_version')) === false)
|
||||
{
|
||||
$result = @mysqli_query($this->db_connect_id, 'SELECT VERSION() AS version');
|
||||
if ($result)
|
||||
{
|
||||
$row = mysqli_fetch_assoc($result);
|
||||
mysqli_free_result($result);
|
||||
|
||||
$this->sql_server_version = $row['version'];
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mysqli_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($raw) ? $this->sql_server_version : 'MySQL(i) ' . $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @mysqli_autocommit($this->db_connect_id, false);
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
$result = @mysqli_commit($this->db_connect_id);
|
||||
@mysqli_autocommit($this->db_connect_id, true);
|
||||
return $result;
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
$result = @mysqli_rollback($this->db_connect_id);
|
||||
@mysqli_autocommit($this->db_connect_id, true);
|
||||
return $result;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->curtime = microtime(true);
|
||||
}
|
||||
|
||||
$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @mysqli_query($this->db_connect_id, $query)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->sql_time += microtime(true) - $this->curtime;
|
||||
}
|
||||
|
||||
if (!$this->query_result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($cache && $cache_ttl)
|
||||
{
|
||||
$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
}
|
||||
else if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @mysqli_affected_rows($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if ($query_id)
|
||||
{
|
||||
$result = mysqli_fetch_assoc($query_id);
|
||||
return $result !== null ? $result : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
return ($query_id) ? @mysqli_data_seek($query_id, $rownum) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
return ($this->db_connect_id) ? @mysqli_insert_id($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (!$query_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($query_id === true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return mysqli_free_result($query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return @mysqli_real_escape_string($this->db_connect_id, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
if ($this->db_connect_id)
|
||||
{
|
||||
$error = array(
|
||||
'message' => @mysqli_error($this->db_connect_id),
|
||||
'code' => @mysqli_errno($this->db_connect_id)
|
||||
);
|
||||
}
|
||||
else if (function_exists('mysqli_connect_error'))
|
||||
{
|
||||
$error = array(
|
||||
'message' => @mysqli_connect_error(),
|
||||
'code' => @mysqli_connect_errno(),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = array(
|
||||
'message' => $this->connect_error,
|
||||
'code' => '',
|
||||
);
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @mysqli_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
static $test_prof;
|
||||
|
||||
// current detection method, might just switch to see the existence of INFORMATION_SCHEMA.PROFILING
|
||||
if ($test_prof === null)
|
||||
{
|
||||
$test_prof = false;
|
||||
if (strpos(mysqli_get_server_info($this->db_connect_id), 'community') !== false)
|
||||
{
|
||||
$ver = mysqli_get_server_version($this->db_connect_id);
|
||||
if ($ver >= 50037 && $ver < 50100)
|
||||
{
|
||||
$test_prof = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$explain_query = $query;
|
||||
if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
|
||||
if (preg_match('/^SELECT/', $explain_query))
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
// begin profiling
|
||||
if ($test_prof)
|
||||
{
|
||||
@mysqli_query($this->db_connect_id, 'SET profiling = 1;');
|
||||
}
|
||||
|
||||
if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query"))
|
||||
{
|
||||
while ($row = mysqli_fetch_assoc($result))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
}
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
if ($test_prof)
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
// get the last profile
|
||||
if ($result = @mysqli_query($this->db_connect_id, 'SHOW PROFILE ALL;'))
|
||||
{
|
||||
$this->html_hold .= '<br />';
|
||||
while ($row = mysqli_fetch_assoc($result))
|
||||
{
|
||||
// make <unknown> HTML safe
|
||||
if (!empty($row['Source_function']))
|
||||
{
|
||||
$row['Source_function'] = str_replace(array('<', '>'), array('<', '>'), $row['Source_function']);
|
||||
}
|
||||
|
||||
// remove unsupported features
|
||||
foreach ($row as $key => $val)
|
||||
{
|
||||
if ($val === null)
|
||||
{
|
||||
unset($row[$key]);
|
||||
}
|
||||
}
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
}
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
@mysqli_query($this->db_connect_id, 'SET profiling = 0;');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @mysqli_query($this->db_connect_id, $query);
|
||||
if ($result)
|
||||
{
|
||||
while ($void = mysqli_fetch_assoc($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
}
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
821
install/update/new/phpbb/db/driver/oracle.php
Normal file
821
install/update/new/phpbb/db/driver/oracle.php
Normal file
@@ -0,0 +1,821 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
/**
|
||||
* Oracle Database Abstraction Layer
|
||||
*/
|
||||
class oracle extends \phpbb\db\driver\driver
|
||||
{
|
||||
var $last_query_text = '';
|
||||
var $connect_error = '';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->server = $sqlserver . (($port) ? ':' . $port : '');
|
||||
$this->dbname = $database;
|
||||
|
||||
$connect = $database;
|
||||
|
||||
// support for "easy connect naming"
|
||||
if ($sqlserver !== '' && $sqlserver !== '/')
|
||||
{
|
||||
if (substr($sqlserver, -1, 1) == '/')
|
||||
{
|
||||
$sqlserver == substr($sqlserver, 0, -1);
|
||||
}
|
||||
$connect = $sqlserver . (($port) ? ':' . $port : '') . '/' . $database;
|
||||
}
|
||||
|
||||
if ($new_link)
|
||||
{
|
||||
if (!function_exists('ocinlogon'))
|
||||
{
|
||||
$this->connect_error = 'ocinlogon function does not exist, is oci extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$this->db_connect_id = @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8');
|
||||
}
|
||||
else if ($this->persistency)
|
||||
{
|
||||
if (!function_exists('ociplogon'))
|
||||
{
|
||||
$this->connect_error = 'ociplogon function does not exist, is oci extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$this->db_connect_id = @ociplogon($this->user, $sqlpassword, $connect, 'UTF8');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!function_exists('ocilogon'))
|
||||
{
|
||||
$this->connect_error = 'ocilogon function does not exist, is oci extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$this->db_connect_id = @ocilogon($this->user, $sqlpassword, $connect, 'UTF8');
|
||||
}
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
/**
|
||||
* force $use_cache false. I didn't research why the caching code below is commented out
|
||||
* but I assume its because the Oracle extension provides a direct method to access it
|
||||
* without a query.
|
||||
*/
|
||||
/*
|
||||
global $cache;
|
||||
|
||||
if (empty($cache) || ($this->sql_server_version = $cache->get('oracle_version')) === false)
|
||||
{
|
||||
$result = @ociparse($this->db_connect_id, 'SELECT * FROM v$version WHERE banner LIKE \'Oracle%\'');
|
||||
@ociexecute($result, OCI_DEFAULT);
|
||||
@ocicommit($this->db_connect_id);
|
||||
|
||||
$row = array();
|
||||
@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS);
|
||||
@ocifreestatement($result);
|
||||
$this->sql_server_version = trim($row['BANNER']);
|
||||
|
||||
$cache->put('oracle_version', $this->sql_server_version);
|
||||
}
|
||||
*/
|
||||
$this->sql_server_version = @ociserverversion($this->db_connect_id);
|
||||
|
||||
return $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return true;
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @ocicommit($this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @ocirollback($this->db_connect_id);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle specific code to handle the fact that it does not compare columns properly
|
||||
* @access private
|
||||
*/
|
||||
function _rewrite_col_compare($args)
|
||||
{
|
||||
if (count($args) == 4)
|
||||
{
|
||||
if ($args[2] == '=')
|
||||
{
|
||||
return '(' . $args[0] . ' OR (' . $args[1] . ' is NULL AND ' . $args[3] . ' is NULL))';
|
||||
}
|
||||
else if ($args[2] == '<>')
|
||||
{
|
||||
// really just a fancy way of saying foo <> bar or (foo is NULL XOR bar is NULL) but SQL has no XOR :P
|
||||
return '(' . $args[0] . ' OR ((' . $args[1] . ' is NULL AND ' . $args[3] . ' is NOT NULL) OR (' . $args[1] . ' is NOT NULL AND ' . $args[3] . ' is NULL)))';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->_rewrite_where($args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle specific code to handle it's lack of sanity
|
||||
* @access private
|
||||
*/
|
||||
function _rewrite_where($where_clause)
|
||||
{
|
||||
preg_match_all('/\s*(AND|OR)?\s*([\w_.()]++)\s*(?:(=|<[=>]?|>=?|LIKE)\s*((?>\'(?>[^\']++|\'\')*+\'|[\d-.()]+))|((NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))/', $where_clause, $result, PREG_SET_ORDER);
|
||||
$out = '';
|
||||
foreach ($result as $val)
|
||||
{
|
||||
if (!isset($val[5]))
|
||||
{
|
||||
if ($val[4] !== "''")
|
||||
{
|
||||
$out .= $val[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$out .= ' ' . $val[1] . ' ' . $val[2];
|
||||
if ($val[3] == '=')
|
||||
{
|
||||
$out .= ' is NULL';
|
||||
}
|
||||
else if ($val[3] == '<>')
|
||||
{
|
||||
$out .= ' is NOT NULL';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$in_clause = array();
|
||||
$sub_exp = substr($val[5], strpos($val[5], '(') + 1, -1);
|
||||
$extra = false;
|
||||
preg_match_all('/\'(?>[^\']++|\'\')*+\'|[\d-.]++/', $sub_exp, $sub_vals, PREG_PATTERN_ORDER);
|
||||
$i = 0;
|
||||
foreach ($sub_vals[0] as $sub_val)
|
||||
{
|
||||
// two things:
|
||||
// 1) This determines if an empty string was in the IN clausing, making us turn it into a NULL comparison
|
||||
// 2) This fixes the 1000 list limit that Oracle has (ORA-01795)
|
||||
if ($sub_val !== "''")
|
||||
{
|
||||
$in_clause[(int) $i++/1000][] = $sub_val;
|
||||
}
|
||||
else
|
||||
{
|
||||
$extra = true;
|
||||
}
|
||||
}
|
||||
if (!$extra && $i < 1000)
|
||||
{
|
||||
$out .= $val[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$out .= ' ' . $val[1] . '(';
|
||||
$in_array = array();
|
||||
|
||||
// constuct each IN() clause
|
||||
foreach ($in_clause as $in_values)
|
||||
{
|
||||
$in_array[] = $val[2] . ' ' . (isset($val[6]) ? $val[6] : '') . 'IN(' . implode(', ', $in_values) . ')';
|
||||
}
|
||||
|
||||
// Join the IN() clauses against a few ORs (IN is just a nicer OR anyway)
|
||||
$out .= implode(' OR ', $in_array);
|
||||
|
||||
// handle the empty string case
|
||||
if ($extra)
|
||||
{
|
||||
$out .= ' OR ' . $val[2] . ' is ' . (isset($val[6]) ? $val[6] : '') . 'NULL';
|
||||
}
|
||||
$out .= ')';
|
||||
|
||||
unset($in_array, $in_clause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->curtime = microtime(true);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
$in_transaction = false;
|
||||
if (!$this->transaction)
|
||||
{
|
||||
$this->sql_transaction('begin');
|
||||
}
|
||||
else
|
||||
{
|
||||
$in_transaction = true;
|
||||
}
|
||||
|
||||
$array = array();
|
||||
|
||||
// We overcome Oracle's 4000 char limit by binding vars
|
||||
if (strlen($query) > 4000)
|
||||
{
|
||||
if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/sU', $query, $regs))
|
||||
{
|
||||
if (strlen($regs[3]) > 4000)
|
||||
{
|
||||
$cols = explode(', ', $regs[2]);
|
||||
|
||||
preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER);
|
||||
|
||||
/* The code inside this comment block breaks clob handling, but does allow the
|
||||
database restore script to work. If you want to allow no posts longer than 4KB
|
||||
and/or need the db restore script, uncomment this.
|
||||
|
||||
|
||||
if (count($cols) !== count($vals))
|
||||
{
|
||||
// Try to replace some common data we know is from our restore script or from other sources
|
||||
$regs[3] = str_replace("'||chr(47)||'", '/', $regs[3]);
|
||||
$_vals = explode(', ', $regs[3]);
|
||||
|
||||
$vals = array();
|
||||
$is_in_val = false;
|
||||
$i = 0;
|
||||
$string = '';
|
||||
|
||||
foreach ($_vals as $value)
|
||||
{
|
||||
if (strpos($value, "'") === false && !$is_in_val)
|
||||
{
|
||||
$vals[$i++] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($value, -1) === "'")
|
||||
{
|
||||
$vals[$i] = $string . (($is_in_val) ? ', ' : '') . $value;
|
||||
$string = '';
|
||||
$is_in_val = false;
|
||||
|
||||
if ($vals[$i][0] !== "'")
|
||||
{
|
||||
$vals[$i] = "''" . $vals[$i];
|
||||
}
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$string .= (($is_in_val) ? ', ' : '') . $value;
|
||||
$is_in_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($string)
|
||||
{
|
||||
// New value if cols != value
|
||||
$vals[(count($cols) !== count($vals)) ? $i : $i - 1] .= $string;
|
||||
}
|
||||
|
||||
$vals = array(0 => $vals);
|
||||
}
|
||||
*/
|
||||
|
||||
$inserts = $vals[0];
|
||||
unset($vals);
|
||||
|
||||
foreach ($inserts as $key => $value)
|
||||
{
|
||||
if (!empty($value) && $value[0] === "'" && strlen($value) > 4002) // check to see if this thing is greater than the max + 'x2
|
||||
{
|
||||
$inserts[$key] = ':' . strtoupper($cols[$key]);
|
||||
$array[$inserts[$key]] = str_replace("''", "'", substr($value, 1, -1));
|
||||
}
|
||||
}
|
||||
|
||||
$query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')';
|
||||
}
|
||||
}
|
||||
else if (preg_match_all('/^(UPDATE [\\w_]++\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data, PREG_SET_ORDER))
|
||||
{
|
||||
if (strlen($data[0][2]) > 4000)
|
||||
{
|
||||
$update = $data[0][1];
|
||||
$where = $data[0][3];
|
||||
preg_match_all('/([\\w_]++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[0][2], $temp, PREG_SET_ORDER);
|
||||
unset($data);
|
||||
|
||||
$cols = array();
|
||||
foreach ($temp as $value)
|
||||
{
|
||||
if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 4002) // check to see if this thing is greater than the max + 'x2
|
||||
{
|
||||
$cols[] = $value[1] . '=:' . strtoupper($value[1]);
|
||||
$array[$value[1]] = str_replace("''", "'", substr($value[2], 1, -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
$cols[] = $value[1] . '=' . $value[2];
|
||||
}
|
||||
}
|
||||
|
||||
$query = $update . implode(', ', $cols) . ' ' . $where;
|
||||
unset($cols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (substr($query, 0, 6))
|
||||
{
|
||||
case 'DELETE':
|
||||
if (preg_match('/^(DELETE FROM [\w_]++ WHERE)((?:\s*(?:AND|OR)?\s*[\w_]+\s*(?:(?:=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]+)|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))*+)$/', $query, $regs))
|
||||
{
|
||||
$query = $regs[1] . $this->_rewrite_where($regs[2]);
|
||||
unset($regs);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'UPDATE':
|
||||
if (preg_match('/^(UPDATE [\\w_]++\\s+SET [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++)(?:, [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++))*+\\s+WHERE)(.*)$/s', $query, $regs))
|
||||
{
|
||||
$query = $regs[1] . $this->_rewrite_where($regs[2]);
|
||||
unset($regs);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'SELECT':
|
||||
$query = preg_replace_callback('/([\w_.]++)\s*(?:(=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]++|([\w_.]++))|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]++,? ?)*+\))/', array($this, '_rewrite_col_compare'), $query);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->query_result = @ociparse($this->db_connect_id, $query);
|
||||
|
||||
foreach ($array as $key => $value)
|
||||
{
|
||||
@ocibindbyname($this->query_result, $key, $array[$key], -1);
|
||||
}
|
||||
|
||||
$success = @ociexecute($this->query_result, OCI_DEFAULT);
|
||||
|
||||
if (!$success)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
$this->query_result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$in_transaction)
|
||||
{
|
||||
$this->sql_transaction('commit');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->sql_time += microtime(true) - $this->curtime;
|
||||
}
|
||||
|
||||
if (!$this->query_result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($cache && $cache_ttl)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
$query = 'SELECT * FROM (SELECT /*+ FIRST_ROWS */ rownum AS xrownum, a.* FROM (' . $query . ') a WHERE rownum <= ' . ($offset + $total) . ') WHERE xrownum >= ' . $offset;
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->query_result) ? @ocirowcount($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if ($query_id)
|
||||
{
|
||||
$row = array();
|
||||
$result = ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
|
||||
|
||||
if (!$result || !$row)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result_row = array();
|
||||
foreach ($row as $key => $value)
|
||||
{
|
||||
// Oracle treats empty strings as null
|
||||
if (is_null($value))
|
||||
{
|
||||
$value = '';
|
||||
}
|
||||
|
||||
// OCI->CLOB?
|
||||
if (is_object($value))
|
||||
{
|
||||
$value = $value->load();
|
||||
}
|
||||
|
||||
$result_row[strtolower($key)] = $value;
|
||||
}
|
||||
|
||||
return $result_row;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
if (!$query_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset internal pointer
|
||||
@ociexecute($query_id, OCI_DEFAULT);
|
||||
|
||||
// We do not fetch the row for rownum == 0 because then the next resultset would be the second row
|
||||
for ($i = 0; $i < $rownum; $i++)
|
||||
{
|
||||
if (!$this->sql_fetchrow($query_id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
|
||||
if ($query_id !== false && $this->last_query_text != '')
|
||||
{
|
||||
if (preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename))
|
||||
{
|
||||
$query = 'SELECT ' . $tablename[1] . '_seq.currval FROM DUAL';
|
||||
$stmt = @ociparse($this->db_connect_id, $query);
|
||||
if ($stmt)
|
||||
{
|
||||
$success = @ociexecute($stmt, OCI_DEFAULT);
|
||||
|
||||
if ($success)
|
||||
{
|
||||
$temp_result = ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS);
|
||||
ocifreestatement($stmt);
|
||||
|
||||
if ($temp_result)
|
||||
{
|
||||
return $temp_array['CURRVAL'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return ocifreestatement($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return str_replace(array("'", "\0"), array("''", ''), $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build NOT LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_not_like_expression($expression)
|
||||
{
|
||||
return $expression . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
function _sql_bit_and($column_name, $bit, $compare = '')
|
||||
{
|
||||
return 'BITAND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
|
||||
}
|
||||
|
||||
function _sql_bit_or($column_name, $bit, $compare = '')
|
||||
{
|
||||
return 'BITOR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
if (function_exists('ocierror'))
|
||||
{
|
||||
$error = @ocierror();
|
||||
$error = (!$error) ? @ocierror($this->query_result) : $error;
|
||||
$error = (!$error) ? @ocierror($this->db_connect_id) : $error;
|
||||
|
||||
if ($error)
|
||||
{
|
||||
$this->last_error_result = $error;
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = array(
|
||||
'message' => $this->connect_error,
|
||||
'code' => '',
|
||||
);
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @ocilogoff($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$html_table = false;
|
||||
|
||||
// Grab a plan table, any will do
|
||||
$sql = "SELECT table_name
|
||||
FROM USER_TABLES
|
||||
WHERE table_name LIKE '%PLAN_TABLE%'";
|
||||
$stmt = ociparse($this->db_connect_id, $sql);
|
||||
ociexecute($stmt);
|
||||
$result = array();
|
||||
|
||||
if (ocifetchinto($stmt, $result, OCI_ASSOC + OCI_RETURN_NULLS))
|
||||
{
|
||||
$table = $result['TABLE_NAME'];
|
||||
|
||||
// This is the statement_id that will allow us to track the plan
|
||||
$statement_id = substr(md5($query), 0, 30);
|
||||
|
||||
// Remove any stale plans
|
||||
$stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
|
||||
ociexecute($stmt2);
|
||||
ocifreestatement($stmt2);
|
||||
|
||||
// Explain the plan
|
||||
$sql = "EXPLAIN PLAN
|
||||
SET STATEMENT_ID = '$statement_id'
|
||||
FOR $query";
|
||||
$stmt2 = ociparse($this->db_connect_id, $sql);
|
||||
ociexecute($stmt2);
|
||||
ocifreestatement($stmt2);
|
||||
|
||||
// Get the data from the plan
|
||||
$sql = "SELECT operation, options, object_name, object_type, cardinality, cost
|
||||
FROM plan_table
|
||||
START WITH id = 0 AND statement_id = '$statement_id'
|
||||
CONNECT BY PRIOR id = parent_id
|
||||
AND statement_id = '$statement_id'";
|
||||
$stmt2 = ociparse($this->db_connect_id, $sql);
|
||||
ociexecute($stmt2);
|
||||
|
||||
$row = array();
|
||||
while (ocifetchinto($stmt2, $row, OCI_ASSOC + OCI_RETURN_NULLS))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
|
||||
ocifreestatement($stmt2);
|
||||
|
||||
// Remove the plan we just made, we delete them on request anyway
|
||||
$stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
|
||||
ociexecute($stmt2);
|
||||
ocifreestatement($stmt2);
|
||||
}
|
||||
|
||||
ocifreestatement($stmt);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @ociparse($this->db_connect_id, $query);
|
||||
if ($result)
|
||||
{
|
||||
$success = @ociexecute($result, OCI_DEFAULT);
|
||||
if ($success)
|
||||
{
|
||||
$row = array();
|
||||
|
||||
while (ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@ocifreestatement($result);
|
||||
}
|
||||
}
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
500
install/update/new/phpbb/db/driver/postgres.php
Normal file
500
install/update/new/phpbb/db/driver/postgres.php
Normal file
@@ -0,0 +1,500 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
/**
|
||||
* PostgreSQL Database Abstraction Layer
|
||||
* Minimum Requirement is Version 8.3+
|
||||
*/
|
||||
class postgres extends \phpbb\db\driver\driver
|
||||
{
|
||||
var $multi_insert = true;
|
||||
var $last_query_text = '';
|
||||
var $connect_error = '';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$connect_string = '';
|
||||
|
||||
if ($sqluser)
|
||||
{
|
||||
$connect_string .= "user=$sqluser ";
|
||||
}
|
||||
|
||||
if ($sqlpassword)
|
||||
{
|
||||
$connect_string .= "password=$sqlpassword ";
|
||||
}
|
||||
|
||||
if ($sqlserver)
|
||||
{
|
||||
// $sqlserver can carry a port separated by : for compatibility reasons
|
||||
// If $sqlserver has more than one : it's probably an IPv6 address.
|
||||
// In this case we only allow passing a port via the $port variable.
|
||||
if (substr_count($sqlserver, ':') === 1)
|
||||
{
|
||||
list($sqlserver, $port) = explode(':', $sqlserver);
|
||||
}
|
||||
|
||||
if ($sqlserver !== 'localhost')
|
||||
{
|
||||
$connect_string .= "host=$sqlserver ";
|
||||
}
|
||||
|
||||
if ($port)
|
||||
{
|
||||
$connect_string .= "port=$port ";
|
||||
}
|
||||
}
|
||||
|
||||
$schema = '';
|
||||
|
||||
if ($database)
|
||||
{
|
||||
$this->dbname = $database;
|
||||
if (strpos($database, '.') !== false)
|
||||
{
|
||||
list($database, $schema) = explode('.', $database);
|
||||
}
|
||||
$connect_string .= "dbname=$database";
|
||||
}
|
||||
|
||||
$this->persistency = $persistency;
|
||||
|
||||
if ($this->persistency)
|
||||
{
|
||||
if (!function_exists('pg_pconnect'))
|
||||
{
|
||||
$this->connect_error = 'pg_pconnect function does not exist, is pgsql extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$collector = new \phpbb\error_collector;
|
||||
$collector->install();
|
||||
$this->db_connect_id = (!$new_link) ? @pg_pconnect($connect_string) : @pg_pconnect($connect_string, PGSQL_CONNECT_FORCE_NEW);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!function_exists('pg_connect'))
|
||||
{
|
||||
$this->connect_error = 'pg_connect function does not exist, is pgsql extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$collector = new \phpbb\error_collector;
|
||||
$collector->install();
|
||||
$this->db_connect_id = (!$new_link) ? @pg_connect($connect_string) : @pg_connect($connect_string, PGSQL_CONNECT_FORCE_NEW);
|
||||
}
|
||||
|
||||
$collector->uninstall();
|
||||
|
||||
if ($this->db_connect_id)
|
||||
{
|
||||
if ($schema !== '')
|
||||
{
|
||||
@pg_query($this->db_connect_id, 'SET search_path TO ' . $schema);
|
||||
}
|
||||
return $this->db_connect_id;
|
||||
}
|
||||
|
||||
$this->connect_error = $collector->format_errors();
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('pgsql_version')) === false)
|
||||
{
|
||||
$query_id = @pg_query($this->db_connect_id, 'SELECT VERSION() AS version');
|
||||
if ($query_id)
|
||||
{
|
||||
$row = pg_fetch_assoc($query_id, null);
|
||||
pg_free_result($query_id);
|
||||
|
||||
$this->sql_server_version = (!empty($row['version'])) ? trim(substr($row['version'], 10)) : 0;
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('pgsql_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($raw) ? $this->sql_server_version : 'PostgreSQL ' . $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @pg_query($this->db_connect_id, 'BEGIN');
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @pg_query($this->db_connect_id, 'COMMIT');
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @pg_query($this->db_connect_id, 'ROLLBACK');
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->curtime = microtime(true);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @pg_query($this->db_connect_id, $query)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->sql_time += microtime(true) - $this->curtime;
|
||||
}
|
||||
|
||||
if (!$this->query_result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($cache && $cache_ttl)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// if $total is set to 0 we do not want to limit the number of rows
|
||||
if ($total == 0)
|
||||
{
|
||||
$total = 'ALL';
|
||||
}
|
||||
|
||||
$query .= "\n LIMIT $total OFFSET $offset";
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
return ($query_id) ? pg_fetch_assoc($query_id, null) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
return ($query_id) ? @pg_result_seek($query_id, $rownum) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
|
||||
if ($query_id !== false && $this->last_query_text != '')
|
||||
{
|
||||
if (preg_match("/^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)/is", $this->last_query_text, $tablename))
|
||||
{
|
||||
$query = "SELECT currval('" . $tablename[1] . "_seq') AS last_value";
|
||||
$temp_q_id = @pg_query($this->db_connect_id, $query);
|
||||
|
||||
if (!$temp_q_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$temp_result = pg_fetch_assoc($temp_q_id, null);
|
||||
pg_free_result($query_id);
|
||||
|
||||
return ($temp_result) ? $temp_result['last_value'] : false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return pg_free_result($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return @pg_escape_string($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build NOT LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_not_like_expression($expression)
|
||||
{
|
||||
return $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function cast_expr_to_bigint($expression)
|
||||
{
|
||||
return 'CAST(' . $expression . ' as DECIMAL(255, 0))';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function cast_expr_to_string($expression)
|
||||
{
|
||||
return 'CAST(' . $expression . ' as VARCHAR(255))';
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
// pg_last_error only works when there is an established connection.
|
||||
// Connection errors have to be tracked by us manually.
|
||||
if ($this->db_connect_id)
|
||||
{
|
||||
$message = @pg_last_error($this->db_connect_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
$message = $this->connect_error;
|
||||
}
|
||||
|
||||
return array(
|
||||
'message' => $message,
|
||||
'code' => ''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @pg_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$explain_query = $query;
|
||||
if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
|
||||
if (preg_match('/^SELECT/', $explain_query))
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
if ($result = @pg_query($this->db_connect_id, "EXPLAIN $explain_query"))
|
||||
{
|
||||
while ($row = pg_fetch_assoc($result, null))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
pg_free_result($result);
|
||||
}
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @pg_query($this->db_connect_id, $query);
|
||||
if ($result)
|
||||
{
|
||||
while ($void = pg_fetch_assoc($result, null))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
pg_free_result($result);
|
||||
}
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
430
install/update/new/phpbb/db/driver/sqlite3.php
Normal file
430
install/update/new/phpbb/db/driver/sqlite3.php
Normal file
@@ -0,0 +1,430 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\driver;
|
||||
|
||||
/**
|
||||
* SQLite3 Database Abstraction Layer
|
||||
* Minimum Requirement: 3.6.15+
|
||||
*/
|
||||
class sqlite3 extends \phpbb\db\driver\driver
|
||||
{
|
||||
/**
|
||||
* @var string Stores errors during connection setup in case the driver is not available
|
||||
*/
|
||||
protected $connect_error = '';
|
||||
|
||||
/**
|
||||
* @var \SQLite3 The SQLite3 database object to operate against
|
||||
*/
|
||||
protected $dbo = null;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = false;
|
||||
$this->user = $sqluser;
|
||||
$this->server = $sqlserver . (($port) ? ':' . $port : '');
|
||||
$this->dbname = $database;
|
||||
|
||||
if (!class_exists('SQLite3', false))
|
||||
{
|
||||
$this->connect_error = 'SQLite3 not found, is the extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->dbo = new \SQLite3($this->server, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE);
|
||||
$this->dbo->busyTimeout(60000);
|
||||
$this->db_connect_id = true;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$this->connect_error = $e->getMessage();
|
||||
return array('message' => $this->connect_error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('sqlite_version')) === false)
|
||||
{
|
||||
$version = \SQLite3::version();
|
||||
|
||||
$this->sql_server_version = $version['versionString'];
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('sqlite_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
return ($raw) ? $this->sql_server_version : 'SQLite ' . $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
*
|
||||
* @param string $status Should be one of the following strings:
|
||||
* begin, commit, rollback
|
||||
* @return bool Success/failure of the transaction query
|
||||
*/
|
||||
protected function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return $this->dbo->exec('BEGIN IMMEDIATE');
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return $this->dbo->exec('COMMIT');
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @$this->dbo->exec('ROLLBACK');
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->curtime = microtime(true);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if ($this->transaction === true && strpos($query, 'INSERT') === 0)
|
||||
{
|
||||
$query = preg_replace('/^INSERT INTO/', 'INSERT OR ROLLBACK INTO', $query);
|
||||
}
|
||||
|
||||
if (($this->query_result = @$this->dbo->query($query)) === false)
|
||||
{
|
||||
// Try to recover a lost database connection
|
||||
if ($this->dbo && !@$this->dbo->lastErrorMsg())
|
||||
{
|
||||
if ($this->sql_connect($this->server, $this->user, '', $this->dbname))
|
||||
{
|
||||
$this->query_result = @$this->dbo->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
else if ($this->debug_load_time)
|
||||
{
|
||||
$this->sql_time += microtime(true) - $this->curtime;
|
||||
}
|
||||
|
||||
if (!$this->query_result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($cache && $cache_ttl)
|
||||
{
|
||||
$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
}
|
||||
else if ($this->debug_sql_explain)
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*
|
||||
* @param string $query The SQL query to execute
|
||||
* @param int $total The number of rows to select
|
||||
* @param int $offset
|
||||
* @param int $cache_ttl Either 0 to avoid caching or
|
||||
* the time in seconds which the result shall be kept in cache
|
||||
* @return mixed Buffered, seekable result handle, false on error
|
||||
*/
|
||||
protected function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// if $total is set to 0 we do not want to limit the number of rows
|
||||
if ($total == 0)
|
||||
{
|
||||
$total = -1;
|
||||
}
|
||||
|
||||
$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? $this->dbo->changes() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
/** @var \SQLite3Result $query_id */
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
return is_object($query_id) ? @$query_id->fetchArray(SQLITE3_ASSOC) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_nextid()
|
||||
{
|
||||
return ($this->db_connect_id) ? $this->dbo->lastInsertRowID() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if ($query_id)
|
||||
{
|
||||
return @$query_id->finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_escape($msg)
|
||||
{
|
||||
return \SQLite3::escapeString($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For SQLite an underscore is an unknown character.
|
||||
*/
|
||||
public function sql_like_expression($expression)
|
||||
{
|
||||
// Unlike LIKE, GLOB is unfortunately case sensitive.
|
||||
// We only catch * and ? here, not the character map possible on file globbing.
|
||||
$expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);
|
||||
|
||||
$expression = str_replace(array('?', '*'), array("\?", "\*"), $expression);
|
||||
$expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression);
|
||||
|
||||
return 'GLOB \'' . $this->sql_escape($expression) . '\'';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For SQLite an underscore is an unknown character.
|
||||
*/
|
||||
public function sql_not_like_expression($expression)
|
||||
{
|
||||
// Unlike NOT LIKE, NOT GLOB is unfortunately case sensitive
|
||||
// We only catch * and ? here, not the character map possible on file globbing.
|
||||
$expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);
|
||||
|
||||
$expression = str_replace(array('?', '*'), array("\?", "\*"), $expression);
|
||||
$expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression);
|
||||
|
||||
return 'NOT GLOB \'' . $this->sql_escape($expression) . '\'';
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _sql_error()
|
||||
{
|
||||
if (class_exists('SQLite3', false) && isset($this->dbo))
|
||||
{
|
||||
$error = array(
|
||||
'message' => $this->dbo->lastErrorMsg(),
|
||||
'code' => $this->dbo->lastErrorCode(),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = array(
|
||||
'message' => $this->connect_error,
|
||||
'code' => '',
|
||||
);
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
*
|
||||
* @param string $stage Available stages: FROM, WHERE
|
||||
* @param mixed $data A string containing the CROSS JOIN query or an array of WHERE clauses
|
||||
*
|
||||
* @return string The db-specific query fragment
|
||||
*/
|
||||
protected function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
*
|
||||
* @return bool False if failure
|
||||
*/
|
||||
protected function _sql_close()
|
||||
{
|
||||
return $this->dbo->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
*
|
||||
* @param string $mode Available modes: display, start, stop,
|
||||
* add_select_row, fromcache, record_fromcache
|
||||
* @param string $query The Query that should be explained
|
||||
* @return mixed Either a full HTML page, boolean or null
|
||||
*/
|
||||
protected function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$explain_query = $query;
|
||||
if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
|
||||
if (preg_match('/^SELECT/', $explain_query))
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
if ($result = $this->dbo->query("EXPLAIN QUERY PLAN $explain_query"))
|
||||
{
|
||||
while ($row = $result->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = $this->dbo->query($query);
|
||||
if ($result)
|
||||
{
|
||||
while ($void = $result->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
}
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
301
install/update/new/phpbb/db/extractor/mysql_extractor.php
Normal file
301
install/update/new/phpbb/db/extractor/mysql_extractor.php
Normal file
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\extractor;
|
||||
|
||||
use phpbb\db\extractor\exception\extractor_not_initialized_exception;
|
||||
|
||||
class mysql_extractor extends base_extractor
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write_start($table_prefix)
|
||||
{
|
||||
if (!$this->is_initialized)
|
||||
{
|
||||
throw new extractor_not_initialized_exception();
|
||||
}
|
||||
|
||||
$sql_data = "#\n";
|
||||
$sql_data .= "# phpBB Backup Script\n";
|
||||
$sql_data .= "# Dump of tables for $table_prefix\n";
|
||||
$sql_data .= "# DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n";
|
||||
$sql_data .= "#\n";
|
||||
$this->flush($sql_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write_table($table_name)
|
||||
{
|
||||
static $new_extract;
|
||||
|
||||
if (!$this->is_initialized)
|
||||
{
|
||||
throw new extractor_not_initialized_exception();
|
||||
}
|
||||
|
||||
if ($new_extract === null)
|
||||
{
|
||||
if ($this->db->get_sql_layer() === 'mysqli' || version_compare($this->db->sql_server_info(true), '3.23.20', '>='))
|
||||
{
|
||||
$new_extract = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$new_extract = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($new_extract)
|
||||
{
|
||||
$this->new_write_table($table_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->old_write_table($table_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write_data($table_name)
|
||||
{
|
||||
if (!$this->is_initialized)
|
||||
{
|
||||
throw new extractor_not_initialized_exception();
|
||||
}
|
||||
|
||||
$this->write_data_mysqli($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts data from database table (for MySQLi driver)
|
||||
*
|
||||
* @param string $table_name name of the database table
|
||||
* @return null
|
||||
* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
|
||||
*/
|
||||
protected function write_data_mysqli($table_name)
|
||||
{
|
||||
if (!$this->is_initialized)
|
||||
{
|
||||
throw new extractor_not_initialized_exception();
|
||||
}
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM $table_name";
|
||||
$result = mysqli_query($this->db->get_db_connect_id(), $sql, MYSQLI_USE_RESULT);
|
||||
if ($result != false)
|
||||
{
|
||||
$fields_cnt = mysqli_num_fields($result);
|
||||
|
||||
// Get field information
|
||||
$field = mysqli_fetch_fields($result);
|
||||
$field_set = array();
|
||||
|
||||
for ($j = 0; $j < $fields_cnt; $j++)
|
||||
{
|
||||
$field_set[] = $field[$j]->name;
|
||||
}
|
||||
|
||||
$search = array("\\", "'", "\x00", "\x0a", "\x0d", "\x1a", '"');
|
||||
$replace = array("\\\\", "\\'", '\0', '\n', '\r', '\Z', '\\"');
|
||||
$fields = implode(', ', $field_set);
|
||||
$sql_data = 'INSERT INTO ' . $table_name . ' (' . $fields . ') VALUES ';
|
||||
$first_set = true;
|
||||
$query_len = 0;
|
||||
$max_len = get_usable_memory();
|
||||
|
||||
while ($row = mysqli_fetch_row($result))
|
||||
{
|
||||
$values = array();
|
||||
if ($first_set)
|
||||
{
|
||||
$query = $sql_data . '(';
|
||||
}
|
||||
else
|
||||
{
|
||||
$query .= ',(';
|
||||
}
|
||||
|
||||
for ($j = 0; $j < $fields_cnt; $j++)
|
||||
{
|
||||
if (!isset($row[$j]) || is_null($row[$j]))
|
||||
{
|
||||
$values[$j] = 'NULL';
|
||||
}
|
||||
else if (($field[$j]->flags & 32768) && !($field[$j]->flags & 1024))
|
||||
{
|
||||
$values[$j] = $row[$j];
|
||||
}
|
||||
else
|
||||
{
|
||||
$values[$j] = "'" . str_replace($search, $replace, $row[$j]) . "'";
|
||||
}
|
||||
}
|
||||
$query .= implode(', ', $values) . ')';
|
||||
|
||||
$query_len += strlen($query);
|
||||
if ($query_len > $max_len)
|
||||
{
|
||||
$this->flush($query . ";\n\n");
|
||||
$query = '';
|
||||
$query_len = 0;
|
||||
$first_set = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$first_set = false;
|
||||
}
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
|
||||
// check to make sure we have nothing left to flush
|
||||
if (!$first_set && $query)
|
||||
{
|
||||
$this->flush($query . ";\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts database table structure (for MySQLi or MySQL 3.23.20+)
|
||||
*
|
||||
* @param string $table_name name of the database table
|
||||
* @return null
|
||||
* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
|
||||
*/
|
||||
protected function new_write_table($table_name)
|
||||
{
|
||||
if (!$this->is_initialized)
|
||||
{
|
||||
throw new extractor_not_initialized_exception();
|
||||
}
|
||||
|
||||
$sql = 'SHOW CREATE TABLE ' . $table_name;
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
|
||||
$sql_data = '# Table: ' . $table_name . "\n";
|
||||
$sql_data .= "DROP TABLE IF EXISTS $table_name;\n";
|
||||
$this->flush($sql_data . $row['Create Table'] . ";\n\n");
|
||||
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts database table structure (for MySQL versions older than 3.23.20)
|
||||
*
|
||||
* @param string $table_name name of the database table
|
||||
* @return null
|
||||
* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
|
||||
*/
|
||||
protected function old_write_table($table_name)
|
||||
{
|
||||
if (!$this->is_initialized)
|
||||
{
|
||||
throw new extractor_not_initialized_exception();
|
||||
}
|
||||
|
||||
$sql_data = '# Table: ' . $table_name . "\n";
|
||||
$sql_data .= "DROP TABLE IF EXISTS $table_name;\n";
|
||||
$sql_data .= "CREATE TABLE $table_name(\n";
|
||||
$rows = array();
|
||||
|
||||
$sql = "SHOW FIELDS
|
||||
FROM $table_name";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$line = ' ' . $row['Field'] . ' ' . $row['Type'];
|
||||
|
||||
if (!is_null($row['Default']))
|
||||
{
|
||||
$line .= " DEFAULT '{$row['Default']}'";
|
||||
}
|
||||
|
||||
if ($row['Null'] != 'YES')
|
||||
{
|
||||
$line .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if ($row['Extra'] != '')
|
||||
{
|
||||
$line .= ' ' . $row['Extra'];
|
||||
}
|
||||
|
||||
$rows[] = $line;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$sql = "SHOW KEYS
|
||||
FROM $table_name";
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$index = array();
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$kname = $row['Key_name'];
|
||||
|
||||
if ($kname != 'PRIMARY')
|
||||
{
|
||||
if ($row['Non_unique'] == 0)
|
||||
{
|
||||
$kname = "UNIQUE|$kname";
|
||||
}
|
||||
}
|
||||
|
||||
if ($row['Sub_part'])
|
||||
{
|
||||
$row['Column_name'] .= '(' . $row['Sub_part'] . ')';
|
||||
}
|
||||
$index[$kname][] = $row['Column_name'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
foreach ($index as $key => $columns)
|
||||
{
|
||||
$line = ' ';
|
||||
|
||||
if ($key == 'PRIMARY')
|
||||
{
|
||||
$line .= 'PRIMARY KEY (' . implode(', ', $columns) . ')';
|
||||
}
|
||||
else if (strpos($key, 'UNIQUE') === 0)
|
||||
{
|
||||
$line .= 'UNIQUE ' . substr($key, 7) . ' (' . implode(', ', $columns) . ')';
|
||||
}
|
||||
else if (strpos($key, 'FULLTEXT') === 0)
|
||||
{
|
||||
$line .= 'FULLTEXT ' . substr($key, 9) . ' (' . implode(', ', $columns) . ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$line .= "KEY $key (" . implode(', ', $columns) . ')';
|
||||
}
|
||||
|
||||
$rows[] = $line;
|
||||
}
|
||||
|
||||
$sql_data .= implode(",\n", $rows);
|
||||
$sql_data .= "\n);\n\n";
|
||||
|
||||
$this->flush($sql_data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v30x;
|
||||
|
||||
class release_3_0_4_rc1 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return phpbb_version_compare($this->config['version'], '3.0.4-RC1', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array('\phpbb\db\migration\data\v30x\release_3_0_3');
|
||||
}
|
||||
|
||||
public function update_schema()
|
||||
{
|
||||
return array(
|
||||
'add_columns' => array(
|
||||
$this->table_prefix . 'profile_fields' => array(
|
||||
'field_show_profile' => array('BOOL', 0),
|
||||
),
|
||||
),
|
||||
'change_columns' => array(
|
||||
$this->table_prefix . 'styles' => array(
|
||||
'style_id' => array('UINT', NULL, 'auto_increment'),
|
||||
'template_id' => array('UINT', 0),
|
||||
'theme_id' => array('UINT', 0),
|
||||
'imageset_id' => array('UINT', 0),
|
||||
),
|
||||
$this->table_prefix . 'styles_imageset' => array(
|
||||
'imageset_id' => array('UINT', NULL, 'auto_increment'),
|
||||
),
|
||||
$this->table_prefix . 'styles_imageset_data' => array(
|
||||
'image_id' => array('UINT', NULL, 'auto_increment'),
|
||||
'imageset_id' => array('UINT', 0),
|
||||
),
|
||||
$this->table_prefix . 'styles_theme' => array(
|
||||
'theme_id' => array('UINT', NULL, 'auto_increment'),
|
||||
),
|
||||
$this->table_prefix . 'styles_template' => array(
|
||||
'template_id' => array('UINT', NULL, 'auto_increment'),
|
||||
),
|
||||
$this->table_prefix . 'styles_template_data' => array(
|
||||
'template_id' => array('UINT', 0),
|
||||
),
|
||||
$this->table_prefix . 'forums' => array(
|
||||
'forum_style' => array('UINT', 0),
|
||||
),
|
||||
$this->table_prefix . 'users' => array(
|
||||
'user_style' => array('UINT', 0),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function revert_schema()
|
||||
{
|
||||
return array(
|
||||
'drop_columns' => array(
|
||||
$this->table_prefix . 'profile_fields' => array(
|
||||
'field_show_profile',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('custom', array(array(&$this, 'update_custom_profile_fields'))),
|
||||
|
||||
array('config.update', array('version', '3.0.4-RC1')),
|
||||
);
|
||||
}
|
||||
|
||||
public function update_custom_profile_fields()
|
||||
{
|
||||
// Update the Custom Profile Fields based on previous settings to the new \format
|
||||
$sql = 'SELECT field_id, field_required, field_show_on_reg, field_hide
|
||||
FROM ' . PROFILE_FIELDS_TABLE;
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$sql_ary = array(
|
||||
'field_required' => 0,
|
||||
'field_show_on_reg' => 0,
|
||||
'field_hide' => 0,
|
||||
'field_show_profile'=> 0,
|
||||
);
|
||||
|
||||
if ($row['field_required'])
|
||||
{
|
||||
$sql_ary['field_required'] = $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
|
||||
}
|
||||
else if ($row['field_show_on_reg'])
|
||||
{
|
||||
$sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
|
||||
}
|
||||
else if ($row['field_hide'])
|
||||
{
|
||||
// Only administrators and moderators can see this CPF, if the view is enabled, they can see it, otherwise just admins in the acp_users module
|
||||
$sql_ary['field_hide'] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// equivalent to "none", which is the "Display in user control panel" option
|
||||
$sql_ary['field_show_profile'] = 1;
|
||||
}
|
||||
|
||||
$this->sql_query('UPDATE ' . $this->table_prefix . 'profile_fields SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE field_id = ' . $row['field_id'], $errored, $error_ary);
|
||||
}
|
||||
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v310;
|
||||
|
||||
class softdelete_p1 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return $this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_visibility');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array('\phpbb\db\migration\data\v310\dev');
|
||||
}
|
||||
|
||||
public function update_schema()
|
||||
{
|
||||
return array(
|
||||
'add_columns' => array(
|
||||
$this->table_prefix . 'forums' => array(
|
||||
'forum_posts_approved' => array('UINT', 0),
|
||||
'forum_posts_unapproved' => array('UINT', 0),
|
||||
'forum_posts_softdeleted' => array('UINT', 0),
|
||||
'forum_topics_approved' => array('UINT', 0),
|
||||
'forum_topics_unapproved' => array('UINT', 0),
|
||||
'forum_topics_softdeleted' => array('UINT', 0),
|
||||
),
|
||||
$this->table_prefix . 'posts' => array(
|
||||
'post_visibility' => array('TINT:3', 0),
|
||||
'post_delete_time' => array('TIMESTAMP', 0),
|
||||
'post_delete_reason' => array('STEXT_UNI', ''),
|
||||
'post_delete_user' => array('UINT', 0),
|
||||
),
|
||||
$this->table_prefix . 'topics' => array(
|
||||
'topic_visibility' => array('TINT:3', 0),
|
||||
'topic_delete_time' => array('TIMESTAMP', 0),
|
||||
'topic_delete_reason' => array('STEXT_UNI', ''),
|
||||
'topic_delete_user' => array('UINT', 0),
|
||||
'topic_posts_approved' => array('UINT', 0),
|
||||
'topic_posts_unapproved' => array('UINT', 0),
|
||||
'topic_posts_softdeleted' => array('UINT', 0),
|
||||
),
|
||||
),
|
||||
'add_index' => array(
|
||||
$this->table_prefix . 'posts' => array(
|
||||
'post_visibility' => array('post_visibility'),
|
||||
),
|
||||
$this->table_prefix . 'topics' => array(
|
||||
'topic_visibility' => array('topic_visibility'),
|
||||
'forum_vis_last' => array('forum_id', 'topic_visibility', 'topic_last_post_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function revert_schema()
|
||||
{
|
||||
return array(
|
||||
'drop_columns' => array(
|
||||
$this->table_prefix . 'forums' => array(
|
||||
'forum_posts_approved',
|
||||
'forum_posts_unapproved',
|
||||
'forum_posts_softdeleted',
|
||||
'forum_topics_approved',
|
||||
'forum_topics_unapproved',
|
||||
'forum_topics_softdeleted',
|
||||
),
|
||||
$this->table_prefix . 'posts' => array(
|
||||
'post_visibility',
|
||||
'post_delete_time',
|
||||
'post_delete_reason',
|
||||
'post_delete_user',
|
||||
),
|
||||
$this->table_prefix . 'topics' => array(
|
||||
'topic_visibility',
|
||||
'topic_delete_time',
|
||||
'topic_delete_reason',
|
||||
'topic_delete_user',
|
||||
'topic_posts_approved',
|
||||
'topic_posts_unapproved',
|
||||
'topic_posts_softdeleted',
|
||||
),
|
||||
),
|
||||
'drop_keys' => array(
|
||||
$this->table_prefix . 'posts' => array('post_visibility'),
|
||||
$this->table_prefix . 'topics' => array('topic_visibility', 'forum_vis_last'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('custom', array(array($this, 'update_post_visibility'))),
|
||||
array('custom', array(array($this, 'update_topic_visibility'))),
|
||||
array('custom', array(array($this, 'update_topics_post_counts'))),
|
||||
array('custom', array(array($this, 'update_forums_topic_and_post_counts'))),
|
||||
|
||||
array('permission.add', array('f_softdelete', false)),
|
||||
array('permission.add', array('m_softdelete', false)),
|
||||
);
|
||||
}
|
||||
|
||||
public function update_post_visibility()
|
||||
{
|
||||
$sql = 'UPDATE ' . $this->table_prefix . 'posts
|
||||
SET post_visibility = post_approved';
|
||||
$this->sql_query($sql);
|
||||
}
|
||||
|
||||
public function update_topic_visibility()
|
||||
{
|
||||
$sql = 'UPDATE ' . $this->table_prefix . 'topics
|
||||
SET topic_visibility = topic_approved';
|
||||
$this->sql_query($sql);
|
||||
}
|
||||
|
||||
public function update_topics_post_counts()
|
||||
{
|
||||
/*
|
||||
* Using sql_case here to avoid "BIGINT UNSIGNED value is out of range" errors.
|
||||
* As we update all topics in 2 queries, one broken topic would stop the conversion
|
||||
* for all topics and the suppressed error will cause the admin to not even notice it.
|
||||
*/
|
||||
$sql = 'UPDATE ' . $this->table_prefix . 'topics
|
||||
SET topic_posts_approved = topic_replies + 1,
|
||||
topic_posts_unapproved = ' . $this->db->sql_case('topic_replies_real > topic_replies', 'topic_replies_real - topic_replies', '0') . '
|
||||
WHERE topic_visibility = ' . ITEM_APPROVED;
|
||||
$this->sql_query($sql);
|
||||
|
||||
$sql = 'UPDATE ' . $this->table_prefix . 'topics
|
||||
SET topic_posts_approved = 0,
|
||||
topic_posts_unapproved = (' . $this->db->sql_case('topic_replies_real > topic_replies', 'topic_replies_real - topic_replies', '0') . ') + 1
|
||||
WHERE topic_visibility = ' . ITEM_UNAPPROVED;
|
||||
$this->sql_query($sql);
|
||||
}
|
||||
|
||||
public function update_forums_topic_and_post_counts($start)
|
||||
{
|
||||
$start = (int) $start;
|
||||
$limit = 10;
|
||||
$converted_forums = 0;
|
||||
|
||||
if (!$start)
|
||||
{
|
||||
// Preserve the forum_posts value for link forums as it represents redirects.
|
||||
$sql = 'UPDATE ' . $this->table_prefix . 'forums
|
||||
SET forum_posts_approved = forum_posts
|
||||
WHERE forum_type = ' . FORUM_LINK;
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
$sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS sum_topics, SUM(topic_posts_approved) AS sum_posts_approved, SUM(topic_posts_unapproved) AS sum_posts_unapproved
|
||||
FROM ' . $this->table_prefix . 'topics
|
||||
GROUP BY forum_id, topic_visibility
|
||||
ORDER BY forum_id, topic_visibility';
|
||||
$result = $this->db->sql_query_limit($sql, $limit, $start);
|
||||
|
||||
$update_forums = array();
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$converted_forums++;
|
||||
|
||||
$forum_id = (int) $row['forum_id'];
|
||||
if (!isset($update_forums[$forum_id]))
|
||||
{
|
||||
$update_forums[$forum_id] = array(
|
||||
'forum_posts_approved' => 0,
|
||||
'forum_posts_unapproved' => 0,
|
||||
'forum_topics_approved' => 0,
|
||||
'forum_topics_unapproved' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$update_forums[$forum_id]['forum_posts_approved'] += (int) $row['sum_posts_approved'];
|
||||
$update_forums[$forum_id]['forum_posts_unapproved'] += (int) $row['sum_posts_unapproved'];
|
||||
|
||||
$update_forums[$forum_id][(($row['topic_visibility'] == ITEM_APPROVED) ? 'forum_topics_approved' : 'forum_topics_unapproved')] += (int) $row['sum_topics'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
foreach ($update_forums as $forum_id => $forum_data)
|
||||
{
|
||||
$sql = 'UPDATE ' . FORUMS_TABLE . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $forum_data) . '
|
||||
WHERE forum_id = ' . $forum_id;
|
||||
$this->sql_query($sql);
|
||||
}
|
||||
|
||||
if ($converted_forums < $limit)
|
||||
{
|
||||
// There are no more topics, we are done
|
||||
return;
|
||||
}
|
||||
|
||||
// There are still more topics to query, return the next start value
|
||||
return $start + $limit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v32x;
|
||||
|
||||
class timezone_p3 extends \phpbb\db\migration\migration
|
||||
{
|
||||
static public function depends_on()
|
||||
{
|
||||
return array('\phpbb\db\migration\data\v310\timezone');
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.remove', array('board_dst')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v32x;
|
||||
|
||||
class user_emoji_permission extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
$sql = 'SELECT auth_option_id
|
||||
FROM ' . ACL_OPTIONS_TABLE . "
|
||||
WHERE auth_option = 'u_emoji'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$auth_option_id = $this->db->sql_fetchfield('auth_option_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $auth_option_id !== false;
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return [
|
||||
'\phpbb\db\migration\data\v32x\v329rc1',
|
||||
];
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return [
|
||||
['permission.add', ['u_emoji']],
|
||||
['permission.permission_set', ['REGISTERED', 'u_emoji', 'group']],
|
||||
];
|
||||
}
|
||||
}
|
||||
36
install/update/new/phpbb/db/migration/data/v32x/v328.php
Normal file
36
install/update/new/phpbb/db/migration/data/v32x/v328.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v32x;
|
||||
|
||||
class v328 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return phpbb_version_compare($this->config['version'], '3.2.8', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v32x\v328rc1',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.2.8')),
|
||||
);
|
||||
}
|
||||
}
|
||||
37
install/update/new/phpbb/db/migration/data/v32x/v328rc1.php
Normal file
37
install/update/new/phpbb/db/migration/data/v32x/v328rc1.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v32x;
|
||||
|
||||
class v328rc1 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return phpbb_version_compare($this->config['version'], '3.2.8-RC1', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v32x\timezone_p3',
|
||||
'\phpbb\db\migration\data\v32x\v327',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.2.8-RC1')),
|
||||
);
|
||||
}
|
||||
}
|
||||
37
install/update/new/phpbb/db/migration/data/v32x/v329.php
Normal file
37
install/update/new/phpbb/db/migration/data/v32x/v329.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v32x;
|
||||
|
||||
class v329 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return phpbb_version_compare($this->config['version'], '3.2.9', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v32x\v329rc1',
|
||||
'\phpbb\db\migration\data\v32x\user_emoji_permission',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.2.9')),
|
||||
);
|
||||
}
|
||||
}
|
||||
36
install/update/new/phpbb/db/migration/data/v32x/v329rc1.php
Normal file
36
install/update/new/phpbb/db/migration/data/v32x/v329rc1.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v32x;
|
||||
|
||||
class v329rc1 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return phpbb_version_compare($this->config['version'], '3.2.9-RC1', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v32x\v328',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.2.9-RC1')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class add_display_unapproved_posts_config extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return $this->config->offsetExists('display_unapproved_posts');
|
||||
}
|
||||
|
||||
public static function depends_on()
|
||||
{
|
||||
return ['\phpbb\db\migration\data\v330\dev',];
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return [
|
||||
['config.add', ['display_unapproved_posts', 1]],
|
||||
];
|
||||
}
|
||||
}
|
||||
36
install/update/new/phpbb/db/migration/data/v330/dev.php
Normal file
36
install/update/new/phpbb/db/migration/data/v330/dev.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class dev extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return version_compare($this->config['version'], '3.3.0-dev', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v32x\v327',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.3.0-dev')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class forums_legend_limit extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return $this->db_tools->sql_column_exists($this->table_prefix . 'forums', 'display_subforum_limit');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return ['\phpbb\db\migration\data\v330\v330b1'];
|
||||
}
|
||||
|
||||
public function update_schema()
|
||||
{
|
||||
return [
|
||||
'add_columns' => [
|
||||
$this->table_prefix . 'forums' => [
|
||||
'display_subforum_limit' => ['BOOL', 0, 'after' => 'display_subforum_list'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function revert_schema()
|
||||
{
|
||||
return [
|
||||
'drop_columns' => [
|
||||
$this->table_prefix . 'forums' => [
|
||||
'display_subforum_limit',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class jquery_update extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return $this->config['load_jquery_url'] === '//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js';
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v330\dev',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js')),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class remove_attachment_flash extends \phpbb\db\migration\migration
|
||||
{
|
||||
// Following constants were deprecated in 3.3
|
||||
// and moved from constants.php to compatibility_globals.php,
|
||||
// thus define them as class constants
|
||||
const ATTACHMENT_CATEGORY_FLASH = 5;
|
||||
|
||||
protected $cat_id = array(
|
||||
self::ATTACHMENT_CATEGORY_FLASH,
|
||||
);
|
||||
|
||||
public static function depends_on()
|
||||
{
|
||||
return ['\phpbb\db\migration\data\v330\dev',];
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('custom', array(array($this, 'remove_flash_group'))),
|
||||
);
|
||||
}
|
||||
|
||||
public function remove_flash_group()
|
||||
{
|
||||
// select group ids of outdated media
|
||||
$sql = 'SELECT group_id
|
||||
FROM ' . EXTENSION_GROUPS_TABLE . '
|
||||
WHERE ' . $this->db->sql_in_set('cat_id', $this->cat_id);
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$group_ids = array();
|
||||
while ($group_id = (int) $this->db->sql_fetchfield('group_id'))
|
||||
{
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
// nothing to do, admin has removed all the outdated media extension groups
|
||||
if (empty($group_ids))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the group id of downloadable files
|
||||
$sql = 'SELECT group_id
|
||||
FROM ' . EXTENSION_GROUPS_TABLE . "
|
||||
WHERE group_name = 'DOWNLOADABLE_FILES'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$download_id = (int) $this->db->sql_fetchfield('group_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (empty($download_id))
|
||||
{
|
||||
$sql = 'UPDATE ' . EXTENSIONS_TABLE . '
|
||||
SET group_id = 0
|
||||
WHERE ' . $this->db->sql_in_set('group_id', $group_ids);
|
||||
}
|
||||
else
|
||||
{
|
||||
// move outdated media extensions to downloadable files
|
||||
$sql = 'UPDATE ' . EXTENSIONS_TABLE . "
|
||||
SET group_id = $download_id" . '
|
||||
WHERE ' . $this->db->sql_in_set('group_id', $group_ids);
|
||||
}
|
||||
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
// delete the now empty, outdated media extension groups
|
||||
$sql = 'DELETE FROM ' . EXTENSION_GROUPS_TABLE . '
|
||||
WHERE ' . $this->db->sql_in_set('group_id', $group_ids);
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class remove_email_hash extends \phpbb\db\migration\migration
|
||||
{
|
||||
static public function depends_on()
|
||||
{
|
||||
return ['\phpbb\db\migration\data\v30x\release_3_0_0'];
|
||||
}
|
||||
|
||||
public function update_schema()
|
||||
{
|
||||
return [
|
||||
'add_index' => [
|
||||
$this->table_prefix . 'users' => [
|
||||
'user_email' => ['user_email'],
|
||||
],
|
||||
],
|
||||
'drop_keys' => [
|
||||
$this->table_prefix . 'users' => [
|
||||
'user_email_hash',
|
||||
],
|
||||
],
|
||||
'drop_columns' => [
|
||||
$this->table_prefix . 'users' => ['user_email_hash'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function revert_schema()
|
||||
{
|
||||
return [
|
||||
'add_columns' => [
|
||||
$this->table_prefix . 'users' => [
|
||||
'user_email_hash' => ['BINT', 0],
|
||||
],
|
||||
],
|
||||
'add_index' => [
|
||||
$this->table_prefix . 'users' => [
|
||||
'user_email_hash',
|
||||
],
|
||||
],
|
||||
'drop_keys' => [
|
||||
$this->table_prefix . 'users' => [
|
||||
'user_email' => ['user_email'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class remove_max_pass_chars extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return !$this->config->offsetExists('max_pass_chars');
|
||||
}
|
||||
|
||||
public static function depends_on()
|
||||
{
|
||||
return [
|
||||
'\phpbb\db\migration\data\v330\dev',
|
||||
];
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return [
|
||||
['config.remove', ['max_pass_chars']],
|
||||
];
|
||||
}
|
||||
|
||||
public function revert_data()
|
||||
{
|
||||
return [
|
||||
['config.add', ['max_pass_chars', 100]],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class reset_password extends \phpbb\db\migration\migration
|
||||
{
|
||||
static public function depends_on()
|
||||
{
|
||||
return [
|
||||
'\phpbb\db\migration\data\v330\dev',
|
||||
];
|
||||
}
|
||||
|
||||
public function update_schema()
|
||||
{
|
||||
return [
|
||||
'add_columns' => [
|
||||
$this->table_prefix . 'users' => [
|
||||
'reset_token' => ['VCHAR:64', '', 'after' => 'user_actkey'],
|
||||
'reset_token_expiration' => ['TIMESTAMP', 0, 'after' => 'reset_token'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function revert_schema()
|
||||
{
|
||||
return [
|
||||
'drop_columns' => [
|
||||
$this->table_prefix . 'users' => [
|
||||
'reset_token',
|
||||
'reset_token_expiration',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
37
install/update/new/phpbb/db/migration/data/v330/v330.php
Normal file
37
install/update/new/phpbb/db/migration/data/v330/v330.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class v330 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return version_compare($this->config['version'], '3.3.0', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v32x\v329',
|
||||
'\phpbb\db\migration\data\v330\v330rc1',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.3.0')),
|
||||
);
|
||||
}
|
||||
}
|
||||
41
install/update/new/phpbb/db/migration/data/v330/v330b1.php
Normal file
41
install/update/new/phpbb/db/migration/data/v330/v330b1.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class v330b1 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return version_compare($this->config['version'], '3.3.0-b1', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v330\jquery_update',
|
||||
'\phpbb\db\migration\data\v330\reset_password',
|
||||
'\phpbb\db\migration\data\v330\remove_attachment_flash',
|
||||
'\phpbb\db\migration\data\v330\remove_max_pass_chars',
|
||||
'\phpbb\db\migration\data\v32x\v328',
|
||||
'\phpbb\db\migration\data\v330\dev',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.3.0-b1')),
|
||||
);
|
||||
}
|
||||
}
|
||||
39
install/update/new/phpbb/db/migration/data/v330/v330b2.php
Normal file
39
install/update/new/phpbb/db/migration/data/v330/v330b2.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class v330b2 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return version_compare($this->config['version'], '3.3.0-b2', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v330\add_display_unapproved_posts_config',
|
||||
'\phpbb\db\migration\data\v330\forums_legend_limit',
|
||||
'\phpbb\db\migration\data\v330\remove_email_hash',
|
||||
'\phpbb\db\migration\data\v330\v330b1',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.3.0-b2')),
|
||||
);
|
||||
}
|
||||
}
|
||||
36
install/update/new/phpbb/db/migration/data/v330/v330rc1.php
Normal file
36
install/update/new/phpbb/db/migration/data/v330/v330rc1.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v330;
|
||||
|
||||
class v330rc1 extends \phpbb\db\migration\migration
|
||||
{
|
||||
public function effectively_installed()
|
||||
{
|
||||
return version_compare($this->config['version'], '3.3.0-RC1', '>=');
|
||||
}
|
||||
|
||||
static public function depends_on()
|
||||
{
|
||||
return array(
|
||||
'\phpbb\db\migration\data\v330\v330b2',
|
||||
);
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.update', array('version', '3.3.0-RC1')),
|
||||
);
|
||||
}
|
||||
}
|
||||
556
install/update/new/phpbb/db/migration/tool/module.php
Normal file
556
install/update/new/phpbb/db/migration/tool/module.php
Normal file
@@ -0,0 +1,556 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\tool;
|
||||
|
||||
use phpbb\module\exception\module_exception;
|
||||
|
||||
/**
|
||||
* Migration module management tool
|
||||
*/
|
||||
class module implements \phpbb\db\migration\tool\tool_interface
|
||||
{
|
||||
/** @var \phpbb\cache\service */
|
||||
protected $cache;
|
||||
|
||||
/** @var \phpbb\db\driver\driver_interface */
|
||||
protected $db;
|
||||
|
||||
/** @var \phpbb\user */
|
||||
protected $user;
|
||||
|
||||
/** @var \phpbb\module\module_manager */
|
||||
protected $module_manager;
|
||||
|
||||
/** @var string */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string */
|
||||
protected $php_ext;
|
||||
|
||||
/** @var string */
|
||||
protected $modules_table;
|
||||
|
||||
/** @var array */
|
||||
protected $module_categories = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\db\driver\driver_interface $db
|
||||
* @param \phpbb\cache\service $cache
|
||||
* @param \phpbb\user $user
|
||||
* @param \phpbb\module\module_manager $module_manager
|
||||
* @param string $phpbb_root_path
|
||||
* @param string $php_ext
|
||||
* @param string $modules_table
|
||||
*/
|
||||
public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\user $user, \phpbb\module\module_manager $module_manager, $phpbb_root_path, $php_ext, $modules_table)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->cache = $cache;
|
||||
$this->user = $user;
|
||||
$this->module_manager = $module_manager;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->modules_table = $modules_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return 'module';
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Exists
|
||||
*
|
||||
* Check if a module exists
|
||||
*
|
||||
* @param string $class The module class(acp|mcp|ucp)
|
||||
* @param int|string|bool $parent The parent module_id|module_langname (0 for no parent).
|
||||
* Use false to ignore the parent check and check class wide.
|
||||
* @param int|string $module The module_id|module_langname you would like to
|
||||
* check for to see if it exists
|
||||
* @param bool $lazy Checks lazily if the module exists. Returns true if it exists in at
|
||||
* least one given parent.
|
||||
* @return bool true if module exists in *all* given parents, false if not in any given parent;
|
||||
* true if ignoring parent check and module exists class wide, false if not found at all.
|
||||
*/
|
||||
public function exists($class, $parent, $module, $lazy = false)
|
||||
{
|
||||
// the main root directory should return true
|
||||
if (!$module)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$parent_sqls = [];
|
||||
if ($parent !== false)
|
||||
{
|
||||
$parents = $this->get_parent_module_id($parent, $module, false);
|
||||
if ($parents === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ((array) $parents as $parent_id)
|
||||
{
|
||||
$parent_sqls[] = 'AND parent_id = ' . (int) $parent_id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent_sqls[] = '';
|
||||
}
|
||||
|
||||
foreach ($parent_sqls as $parent_sql)
|
||||
{
|
||||
$sql = 'SELECT module_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
$parent_sql
|
||||
AND " . ((is_numeric($module)) ? 'module_id = ' . (int) $module : "module_langname = '" . $this->db->sql_escape($module) . "'");
|
||||
$result = $this->db->sql_query($sql);
|
||||
$module_id = $this->db->sql_fetchfield('module_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$lazy && !$module_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ($lazy && $module_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true, if modules exist in all parents and false otherwise
|
||||
return !$lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Add
|
||||
*
|
||||
* Add a new module
|
||||
*
|
||||
* @param string $class The module class(acp|mcp|ucp)
|
||||
* @param int|string $parent The parent module_id|module_langname (0 for no parent)
|
||||
* @param array $data an array of the data on the new \module.
|
||||
* This can be setup in two different ways.
|
||||
* 1. The "manual" way. For inserting a category or one at a time.
|
||||
* It will be merged with the base array shown a bit below,
|
||||
* but at the least requires 'module_langname' to be sent, and,
|
||||
* if you want to create a module (instead of just a category) you must
|
||||
* send module_basename and module_mode.
|
||||
* array(
|
||||
* 'module_enabled' => 1,
|
||||
* 'module_display' => 1,
|
||||
* 'module_basename' => '',
|
||||
* 'module_class' => $class,
|
||||
* 'parent_id' => (int) $parent,
|
||||
* 'module_langname' => '',
|
||||
* 'module_mode' => '',
|
||||
* 'module_auth' => '',
|
||||
* )
|
||||
* 2. The "automatic" way. For inserting multiple at a time based on the
|
||||
* specs in the info file for the module(s). For this to work the
|
||||
* modules must be correctly setup in the info file.
|
||||
* An example follows (this would insert the settings, log, and flag
|
||||
* modes from the includes/acp/info/acp_asacp.php file):
|
||||
* array(
|
||||
* 'module_basename' => 'asacp',
|
||||
* 'modes' => array('settings', 'log', 'flag'),
|
||||
* )
|
||||
* Optionally you may not send 'modes' and it will insert all of the
|
||||
* modules in that info file.
|
||||
* path, specify that here
|
||||
* @return null
|
||||
* @throws \phpbb\db\migration\exception
|
||||
*/
|
||||
public function add($class, $parent = 0, $data = array())
|
||||
{
|
||||
global $user, $phpbb_log;
|
||||
|
||||
// allow sending the name as a string in $data to create a category
|
||||
if (!is_array($data))
|
||||
{
|
||||
$data = array('module_langname' => $data);
|
||||
}
|
||||
|
||||
$parents = (array) $this->get_parent_module_id($parent, $data);
|
||||
|
||||
if (!isset($data['module_langname']))
|
||||
{
|
||||
// The "automatic" way
|
||||
$basename = (isset($data['module_basename'])) ? $data['module_basename'] : '';
|
||||
$module = $this->get_module_info($class, $basename);
|
||||
|
||||
foreach ($module['modes'] as $mode => $module_info)
|
||||
{
|
||||
if (!isset($data['modes']) || in_array($mode, $data['modes']))
|
||||
{
|
||||
$new_module = array(
|
||||
'module_basename' => $basename,
|
||||
'module_langname' => $module_info['title'],
|
||||
'module_mode' => $mode,
|
||||
'module_auth' => $module_info['auth'],
|
||||
'module_display' => (isset($module_info['display'])) ? $module_info['display'] : true,
|
||||
'before' => (isset($module_info['before'])) ? $module_info['before'] : false,
|
||||
'after' => (isset($module_info['after'])) ? $module_info['after'] : false,
|
||||
);
|
||||
|
||||
// Run the "manual" way with the data we've collected.
|
||||
foreach ($parents as $parent)
|
||||
{
|
||||
$this->add($class, $parent, $new_module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($parents as $parent)
|
||||
{
|
||||
$data['parent_id'] = $parent;
|
||||
|
||||
// The "manual" way
|
||||
if (!$this->exists($class, false, $parent))
|
||||
{
|
||||
throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent);
|
||||
}
|
||||
|
||||
if ($this->exists($class, $parent, $data['module_langname']))
|
||||
{
|
||||
throw new \phpbb\db\migration\exception('MODULE_EXISTS', $data['module_langname']);
|
||||
}
|
||||
|
||||
$module_data = array(
|
||||
'module_enabled' => (isset($data['module_enabled'])) ? $data['module_enabled'] : 1,
|
||||
'module_display' => (isset($data['module_display'])) ? $data['module_display'] : 1,
|
||||
'module_basename' => (isset($data['module_basename'])) ? $data['module_basename'] : '',
|
||||
'module_class' => $class,
|
||||
'parent_id' => (int) $parent,
|
||||
'module_langname' => (isset($data['module_langname'])) ? $data['module_langname'] : '',
|
||||
'module_mode' => (isset($data['module_mode'])) ? $data['module_mode'] : '',
|
||||
'module_auth' => (isset($data['module_auth'])) ? $data['module_auth'] : '',
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
$this->module_manager->update_module_data($module_data);
|
||||
|
||||
// Success
|
||||
$module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']);
|
||||
$phpbb_log->add('admin', (isset($user->data['user_id'])) ? $user->data['user_id'] : ANONYMOUS, $user->ip, 'LOG_MODULE_ADD', false, array($module_log_name));
|
||||
|
||||
// Move the module if requested above/below an existing one
|
||||
if (isset($data['before']) && $data['before'])
|
||||
{
|
||||
$before_mode = $before_langname = '';
|
||||
if (is_array($data['before']))
|
||||
{
|
||||
// Restore legacy-legacy behaviour from phpBB 3.0
|
||||
list($before_mode, $before_langname) = $data['before'];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy behaviour from phpBB 3.1+
|
||||
$before_langname = $data['before'];
|
||||
}
|
||||
|
||||
$sql = 'SELECT left_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND parent_id = " . (int) $parent . "
|
||||
AND module_langname = '" . $this->db->sql_escape($before_langname) . "'"
|
||||
. (($before_mode) ? " AND module_mode = '" . $this->db->sql_escape($before_mode) . "'" : '');
|
||||
$result = $this->db->sql_query($sql);
|
||||
$to_left = (int) $this->db->sql_fetchfield('left_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . "
|
||||
SET left_id = left_id + 2, right_id = right_id + 2
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND left_id >= $to_left
|
||||
AND left_id < {$module_data['left_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . "
|
||||
SET left_id = $to_left, right_id = " . ($to_left + 1) . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND module_id = {$module_data['module_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
else if (isset($data['after']) && $data['after'])
|
||||
{
|
||||
$after_mode = $after_langname = '';
|
||||
if (is_array($data['after']))
|
||||
{
|
||||
// Restore legacy-legacy behaviour from phpBB 3.0
|
||||
list($after_mode, $after_langname) = $data['after'];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy behaviour from phpBB 3.1+
|
||||
$after_langname = $data['after'];
|
||||
}
|
||||
|
||||
$sql = 'SELECT right_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND parent_id = " . (int) $parent . "
|
||||
AND module_langname = '" . $this->db->sql_escape($after_langname) . "'"
|
||||
. (($after_mode) ? " AND module_mode = '" . $this->db->sql_escape($after_mode) . "'" : '');
|
||||
$result = $this->db->sql_query($sql);
|
||||
$to_right = (int) $this->db->sql_fetchfield('right_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . "
|
||||
SET left_id = left_id + 2, right_id = right_id + 2
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND left_id >= $to_right
|
||||
AND left_id < {$module_data['left_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . '
|
||||
SET left_id = ' . ($to_right + 1) . ', right_id = ' . ($to_right + 2) . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND module_id = {$module_data['module_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
}
|
||||
catch (module_exception $e)
|
||||
{
|
||||
// Error
|
||||
throw new \phpbb\db\migration\exception('MODULE_ERROR', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the Modules Cache
|
||||
$this->module_manager->remove_cache_file($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Remove
|
||||
*
|
||||
* Remove a module
|
||||
*
|
||||
* @param string $class The module class(acp|mcp|ucp)
|
||||
* @param int|string|bool $parent The parent module_id|module_langname(0 for no parent).
|
||||
* Use false to ignore the parent check and check class wide.
|
||||
* @param int|string $module The module id|module_langname
|
||||
* specify that here
|
||||
* @return null
|
||||
* @throws \phpbb\db\migration\exception
|
||||
*/
|
||||
public function remove($class, $parent = 0, $module = '')
|
||||
{
|
||||
// Imitation of module_add's "automatic" and "manual" method so the uninstaller works from the same set of instructions for umil_auto
|
||||
if (is_array($module))
|
||||
{
|
||||
if (isset($module['module_langname']))
|
||||
{
|
||||
// Manual Method
|
||||
return $this->remove($class, $parent, $module['module_langname']);
|
||||
}
|
||||
|
||||
// Failed.
|
||||
if (!isset($module['module_basename']))
|
||||
{
|
||||
throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST');
|
||||
}
|
||||
|
||||
// Automatic method
|
||||
$basename = $module['module_basename'];
|
||||
$module_info = $this->get_module_info($class, $basename);
|
||||
|
||||
foreach ($module_info['modes'] as $mode => $info)
|
||||
{
|
||||
if (!isset($module['modes']) || in_array($mode, $module['modes']))
|
||||
{
|
||||
$this->remove($class, $parent, $info['title']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$this->exists($class, $parent, $module, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$parent_sql = '';
|
||||
if ($parent !== false)
|
||||
{
|
||||
$parents = (array) $this->get_parent_module_id($parent, $module);
|
||||
$parent_sql = 'AND ' . $this->db->sql_in_set('parent_id', $parents);
|
||||
}
|
||||
|
||||
$module_ids = array();
|
||||
if (!is_numeric($module))
|
||||
{
|
||||
$sql = 'SELECT module_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_langname = '" . $this->db->sql_escape($module) . "'
|
||||
AND module_class = '" . $this->db->sql_escape($class) . "'
|
||||
$parent_sql";
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($module_id = $this->db->sql_fetchfield('module_id'))
|
||||
{
|
||||
$module_ids[] = (int) $module_id;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
else
|
||||
{
|
||||
$module_ids[] = (int) $module;
|
||||
}
|
||||
|
||||
foreach ($module_ids as $module_id)
|
||||
{
|
||||
$this->module_manager->delete_module($module_id, $class);
|
||||
}
|
||||
|
||||
$this->module_manager->remove_cache_file($class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
$original_call = array_shift($arguments);
|
||||
|
||||
$call = false;
|
||||
switch ($original_call)
|
||||
{
|
||||
case 'add':
|
||||
$call = 'remove';
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$call = 'add';
|
||||
break;
|
||||
|
||||
case 'reverse':
|
||||
// Reversing a reverse is just the call itself
|
||||
$call = array_shift($arguments);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($call)
|
||||
{
|
||||
return call_user_func_array(array(&$this, $call), $arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for \acp_modules::get_module_infos()
|
||||
*
|
||||
* @param string $class Module Class
|
||||
* @param string $basename Module Basename
|
||||
* @return array Module Information
|
||||
* @throws \phpbb\db\migration\exception
|
||||
*/
|
||||
protected function get_module_info($class, $basename)
|
||||
{
|
||||
$module = $this->module_manager->get_module_infos($class, $basename, true);
|
||||
|
||||
if (empty($module))
|
||||
{
|
||||
throw new \phpbb\db\migration\exception('MODULE_INFO_FILE_NOT_EXIST', $class, $basename);
|
||||
}
|
||||
|
||||
return array_pop($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of installed module categories
|
||||
* key - module_id
|
||||
* value - module_langname
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function get_categories_list()
|
||||
{
|
||||
// Select the top level categories
|
||||
// and 2nd level [sub]categories
|
||||
$sql = 'SELECT m2.module_id, m2.module_langname
|
||||
FROM ' . $this->modules_table . ' m1, ' . $this->modules_table . " m2
|
||||
WHERE m1.parent_id = 0
|
||||
AND (m1.module_id = m2.module_id OR m2.parent_id = m1.module_id)
|
||||
ORDER BY m1.module_id, m2.module_id ASC";
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$this->module_categories[(int) $row['module_id']] = $row['module_langname'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent module id
|
||||
*
|
||||
* @param string|int $parent_id The parent module_id|module_langname
|
||||
* @param int|string|array $data The module_id, module_langname for existence checking or module data array for adding
|
||||
* @param bool $throw_exception The flag indicating if exception should be thrown on error
|
||||
* @return mixed The int parent module_id, an array of int parent module_id values or false
|
||||
* @throws \phpbb\db\migration\exception
|
||||
*/
|
||||
public function get_parent_module_id($parent_id, $data = '', $throw_exception = true)
|
||||
{
|
||||
// Allow '' to be sent as 0
|
||||
$parent_id = $parent_id ?: 0;
|
||||
|
||||
if (!is_numeric($parent_id))
|
||||
{
|
||||
// Refresh the $module_categories array
|
||||
$this->get_categories_list();
|
||||
|
||||
// Search for the parent module_langname
|
||||
$ids = array_keys($this->module_categories, $parent_id);
|
||||
|
||||
switch (count($ids))
|
||||
{
|
||||
// No parent with the given module_langname exist
|
||||
case 0:
|
||||
if ($throw_exception)
|
||||
{
|
||||
throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
break;
|
||||
|
||||
// Return the module id
|
||||
case 1:
|
||||
return (int) $ids[0];
|
||||
break;
|
||||
|
||||
default:
|
||||
// This represents the old behaviour of phpBB 3.0
|
||||
return $ids;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $parent_id;
|
||||
}
|
||||
}
|
||||
1037
install/update/new/phpbb/db/migrator.php
Normal file
1037
install/update/new/phpbb/db/migrator.php
Normal file
File diff suppressed because it is too large
Load Diff
880
install/update/new/phpbb/db/tools/mssql.php
Normal file
880
install/update/new/phpbb/db/tools/mssql.php
Normal file
@@ -0,0 +1,880 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\tools;
|
||||
|
||||
/**
|
||||
* Database Tools for handling cross-db actions such as altering columns, etc.
|
||||
* Currently not supported is returning SQL for creating tables.
|
||||
*/
|
||||
class mssql extends tools
|
||||
{
|
||||
/**
|
||||
* Is the used MS SQL Server a SQL Server 2000?
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_sql_server_2000;
|
||||
|
||||
/**
|
||||
* Get the column types for mssql based databases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_dbms_type_map()
|
||||
{
|
||||
return array(
|
||||
'mssql' => array(
|
||||
'INT:' => '[int]',
|
||||
'BINT' => '[float]',
|
||||
'ULINT' => '[int]',
|
||||
'UINT' => '[int]',
|
||||
'UINT:' => '[int]',
|
||||
'TINT:' => '[int]',
|
||||
'USINT' => '[int]',
|
||||
'BOOL' => '[int]',
|
||||
'VCHAR' => '[varchar] (255)',
|
||||
'VCHAR:' => '[varchar] (%d)',
|
||||
'CHAR:' => '[char] (%d)',
|
||||
'XSTEXT' => '[varchar] (1000)',
|
||||
'STEXT' => '[varchar] (3000)',
|
||||
'TEXT' => '[varchar] (8000)',
|
||||
'MTEXT' => '[text]',
|
||||
'XSTEXT_UNI'=> '[nvarchar] (100)',
|
||||
'STEXT_UNI' => '[nvarchar] (255)',
|
||||
'TEXT_UNI' => '[nvarchar] (4000)',
|
||||
'MTEXT_UNI' => '[ntext]',
|
||||
'TIMESTAMP' => '[int]',
|
||||
'DECIMAL' => '[float]',
|
||||
'DECIMAL:' => '[float]',
|
||||
'PDECIMAL' => '[float]',
|
||||
'PDECIMAL:' => '[float]',
|
||||
'VCHAR_UNI' => '[nvarchar] (255)',
|
||||
'VCHAR_UNI:'=> '[nvarchar] (%d)',
|
||||
'VCHAR_CI' => '[nvarchar] (255)',
|
||||
'VARBINARY' => '[varchar] (255)',
|
||||
),
|
||||
|
||||
'mssqlnative' => array(
|
||||
'INT:' => '[int]',
|
||||
'BINT' => '[float]',
|
||||
'ULINT' => '[int]',
|
||||
'UINT' => '[int]',
|
||||
'UINT:' => '[int]',
|
||||
'TINT:' => '[int]',
|
||||
'USINT' => '[int]',
|
||||
'BOOL' => '[int]',
|
||||
'VCHAR' => '[varchar] (255)',
|
||||
'VCHAR:' => '[varchar] (%d)',
|
||||
'CHAR:' => '[char] (%d)',
|
||||
'XSTEXT' => '[varchar] (1000)',
|
||||
'STEXT' => '[varchar] (3000)',
|
||||
'TEXT' => '[varchar] (8000)',
|
||||
'MTEXT' => '[text]',
|
||||
'XSTEXT_UNI'=> '[nvarchar] (100)',
|
||||
'STEXT_UNI' => '[nvarchar] (255)',
|
||||
'TEXT_UNI' => '[nvarchar] (4000)',
|
||||
'MTEXT_UNI' => '[ntext]',
|
||||
'TIMESTAMP' => '[int]',
|
||||
'DECIMAL' => '[float]',
|
||||
'DECIMAL:' => '[float]',
|
||||
'PDECIMAL' => '[float]',
|
||||
'PDECIMAL:' => '[float]',
|
||||
'VCHAR_UNI' => '[nvarchar] (255)',
|
||||
'VCHAR_UNI:'=> '[nvarchar] (%d)',
|
||||
'VCHAR_CI' => '[nvarchar] (255)',
|
||||
'VARBINARY' => '[varchar] (255)',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Set DB Object and set {@link $return_statements return_statements}.
|
||||
*
|
||||
* @param \phpbb\db\driver\driver_interface $db Database connection
|
||||
* @param bool $return_statements True if only statements should be returned and no SQL being executed
|
||||
*/
|
||||
public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
|
||||
{
|
||||
parent::__construct($db, $return_statements);
|
||||
|
||||
// Determine mapping database type
|
||||
switch ($this->db->get_sql_layer())
|
||||
{
|
||||
case 'mssql_odbc':
|
||||
$this->sql_layer = 'mssql';
|
||||
break;
|
||||
|
||||
case 'mssqlnative':
|
||||
$this->sql_layer = 'mssqlnative';
|
||||
break;
|
||||
}
|
||||
|
||||
$this->dbms_type_map = self::get_dbms_type_map();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_list_tables()
|
||||
{
|
||||
$sql = "SELECT name
|
||||
FROM sysobjects
|
||||
WHERE type='U'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$tables = array();
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$name = current($row);
|
||||
$tables[$name] = $name;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_table($table_name, $table_data)
|
||||
{
|
||||
// holds the DDL for a column
|
||||
$columns = $statements = array();
|
||||
|
||||
if ($this->sql_table_exists($table_name))
|
||||
{
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
$statements[] = 'begin';
|
||||
|
||||
// Determine if we have created a PRIMARY KEY in the earliest
|
||||
$primary_key_gen = false;
|
||||
|
||||
// Determine if the table requires a sequence
|
||||
$create_sequence = false;
|
||||
|
||||
// Begin table sql statement
|
||||
$table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n";
|
||||
|
||||
if (!isset($table_data['PRIMARY_KEY']))
|
||||
{
|
||||
$table_data['COLUMNS']['mssqlindex'] = array('UINT', null, 'auto_increment');
|
||||
$table_data['PRIMARY_KEY'] = 'mssqlindex';
|
||||
}
|
||||
|
||||
// Iterate through the columns to create a table
|
||||
foreach ($table_data['COLUMNS'] as $column_name => $column_data)
|
||||
{
|
||||
// here lies an array, filled with information compiled on the column's data
|
||||
$prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
|
||||
|
||||
if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "${column_name}_gen"
|
||||
{
|
||||
trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// here we add the definition of the new column to the list of columns
|
||||
$columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default'];
|
||||
|
||||
// see if we have found a primary key set due to a column definition if we have found it, we can stop looking
|
||||
if (!$primary_key_gen)
|
||||
{
|
||||
$primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
|
||||
}
|
||||
|
||||
// create sequence DDL based off of the existence of auto incrementing columns
|
||||
if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
|
||||
{
|
||||
$create_sequence = $column_name;
|
||||
}
|
||||
}
|
||||
|
||||
// this makes up all the columns in the create table statement
|
||||
$table_sql .= implode(",\n", $columns);
|
||||
|
||||
// Close the table for two DBMS and add to the statements
|
||||
$table_sql .= "\n);";
|
||||
$statements[] = $table_sql;
|
||||
|
||||
// we have yet to create a primary key for this table,
|
||||
// this means that we can add the one we really wanted instead
|
||||
if (!$primary_key_gen)
|
||||
{
|
||||
// Write primary key
|
||||
if (isset($table_data['PRIMARY_KEY']))
|
||||
{
|
||||
if (!is_array($table_data['PRIMARY_KEY']))
|
||||
{
|
||||
$table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
|
||||
}
|
||||
|
||||
// We need the data here
|
||||
$old_return_statements = $this->return_statements;
|
||||
$this->return_statements = true;
|
||||
|
||||
$primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']);
|
||||
foreach ($primary_key_stmts as $pk_stmt)
|
||||
{
|
||||
$statements[] = $pk_stmt;
|
||||
}
|
||||
|
||||
$this->return_statements = $old_return_statements;
|
||||
}
|
||||
}
|
||||
|
||||
// Write Keys
|
||||
if (isset($table_data['KEYS']))
|
||||
{
|
||||
foreach ($table_data['KEYS'] as $key_name => $key_data)
|
||||
{
|
||||
if (!is_array($key_data[1]))
|
||||
{
|
||||
$key_data[1] = array($key_data[1]);
|
||||
}
|
||||
|
||||
$old_return_statements = $this->return_statements;
|
||||
$this->return_statements = true;
|
||||
|
||||
$key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]);
|
||||
|
||||
foreach ($key_stmts as $key_stmt)
|
||||
{
|
||||
$statements[] = $key_stmt;
|
||||
}
|
||||
|
||||
$this->return_statements = $old_return_statements;
|
||||
}
|
||||
}
|
||||
|
||||
// Commit Transaction
|
||||
$statements[] = 'commit';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_list_columns($table_name)
|
||||
{
|
||||
$columns = array();
|
||||
|
||||
$sql = "SELECT c.name
|
||||
FROM syscolumns c
|
||||
LEFT JOIN sysobjects o ON c.id = o.id
|
||||
WHERE o.name = '{$table_name}'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$column = strtolower(current($row));
|
||||
$columns[$column] = $column;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_index_exists($table_name, $index_name)
|
||||
{
|
||||
$sql = "EXEC sp_statistics '$table_name'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
if ($row['TYPE'] == 3)
|
||||
{
|
||||
if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
|
||||
{
|
||||
$this->db->sql_freeresult($result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_unique_index_exists($table_name, $index_name)
|
||||
{
|
||||
$sql = "EXEC sp_statistics '$table_name'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
// Usually NON_UNIQUE is the column we want to check, but we allow for both
|
||||
if ($row['TYPE'] == 3)
|
||||
{
|
||||
if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
|
||||
{
|
||||
$this->db->sql_freeresult($result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_prepare_column_data($table_name, $column_name, $column_data)
|
||||
{
|
||||
if (strlen($column_name) > 30)
|
||||
{
|
||||
trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// Get type
|
||||
list($column_type, ) = $this->get_column_type($column_data[0]);
|
||||
|
||||
// Adjust default value if db-dependent specified
|
||||
if (is_array($column_data[1]))
|
||||
{
|
||||
$column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default'];
|
||||
}
|
||||
|
||||
$sql = '';
|
||||
|
||||
$return_array = array();
|
||||
|
||||
$sql .= " {$column_type} ";
|
||||
$sql_default = " {$column_type} ";
|
||||
|
||||
// For adding columns we need the default definition
|
||||
if (!is_null($column_data[1]))
|
||||
{
|
||||
// For hexadecimal values do not use single quotes
|
||||
if (strpos($column_data[1], '0x') === 0)
|
||||
{
|
||||
$return_array['default'] = 'DEFAULT (' . $column_data[1] . ') ';
|
||||
$sql_default .= $return_array['default'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') ';
|
||||
$sql_default .= $return_array['default'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
|
||||
{
|
||||
// $sql .= 'IDENTITY (1, 1) ';
|
||||
$sql_default .= 'IDENTITY (1, 1) ';
|
||||
}
|
||||
|
||||
$return_array['textimage'] = $column_type === '[text]';
|
||||
|
||||
if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment'))
|
||||
{
|
||||
$sql .= 'NOT NULL';
|
||||
$sql_default .= 'NOT NULL';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= 'NULL';
|
||||
$sql_default .= 'NULL';
|
||||
}
|
||||
|
||||
$return_array['column_type_sql_default'] = $sql_default;
|
||||
|
||||
$return_array['column_type_sql'] = $sql;
|
||||
|
||||
return $return_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_column_add($table_name, $column_name, $column_data, $inline = false)
|
||||
{
|
||||
$column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
|
||||
$statements = array();
|
||||
|
||||
// Does not support AFTER, only through temporary table
|
||||
$statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default'];
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_column_remove($table_name, $column_name, $inline = false)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
// We need the data here
|
||||
$old_return_statements = $this->return_statements;
|
||||
$this->return_statements = true;
|
||||
|
||||
$indexes = $this->get_existing_indexes($table_name, $column_name);
|
||||
$indexes = array_merge($indexes, $this->get_existing_indexes($table_name, $column_name, true));
|
||||
|
||||
// Drop any indexes
|
||||
$recreate_indexes = array();
|
||||
if (!empty($indexes))
|
||||
{
|
||||
foreach ($indexes as $index_name => $index_data)
|
||||
{
|
||||
$result = $this->sql_index_drop($table_name, $index_name);
|
||||
$statements = array_merge($statements, $result);
|
||||
if (count($index_data) > 1)
|
||||
{
|
||||
// Remove this column from the index and recreate it
|
||||
$recreate_indexes[$index_name] = array_diff($index_data, array($column_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop primary keys depending on this column
|
||||
$result = $this->mssql_get_drop_default_primary_key_queries($table_name, $column_name);
|
||||
$statements = array_merge($statements, $result);
|
||||
|
||||
// Drop default value constraint
|
||||
$result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
|
||||
$statements = array_merge($statements, $result);
|
||||
|
||||
// Remove the column
|
||||
$statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
|
||||
|
||||
if (!empty($recreate_indexes))
|
||||
{
|
||||
// Recreate indexes after we removed the column
|
||||
foreach ($recreate_indexes as $index_name => $index_data)
|
||||
{
|
||||
$result = $this->sql_create_index($table_name, $index_name, $index_data);
|
||||
$statements = array_merge($statements, $result);
|
||||
}
|
||||
}
|
||||
|
||||
$this->return_statements = $old_return_statements;
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_index_drop($table_name, $index_name)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$statements[] = 'DROP INDEX [' . $table_name . '].[' . $index_name . ']';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_table_drop($table_name)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
if (!$this->sql_table_exists($table_name))
|
||||
{
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
// the most basic operation, get rid of the table
|
||||
$statements[] = 'DROP TABLE ' . $table_name;
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_primary_key($table_name, $column, $inline = false)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD ";
|
||||
$sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED (";
|
||||
$sql .= '[' . implode("],\n\t\t[", $column) . ']';
|
||||
$sql .= ')';
|
||||
|
||||
$statements[] = $sql;
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_unique_index($table_name, $index_name, $column)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
if ($this->mssql_is_sql_server_2000())
|
||||
{
|
||||
$this->check_index_name_length($table_name, $index_name);
|
||||
}
|
||||
|
||||
$statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_index($table_name, $index_name, $column)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$this->check_index_name_length($table_name, $index_name);
|
||||
|
||||
// remove index length
|
||||
$column = preg_replace('#:.*$#', '', $column);
|
||||
|
||||
$statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function get_max_index_name_length()
|
||||
{
|
||||
if ($this->mssql_is_sql_server_2000())
|
||||
{
|
||||
return parent::get_max_index_name_length();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 128;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_list_index($table_name)
|
||||
{
|
||||
$index_array = array();
|
||||
$sql = "EXEC sp_statistics '$table_name'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
if ($row['TYPE'] == 3)
|
||||
{
|
||||
$index_array[] = strtolower($row['INDEX_NAME']);
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $index_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_column_change($table_name, $column_name, $column_data, $inline = false)
|
||||
{
|
||||
$column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
|
||||
$statements = array();
|
||||
|
||||
// We need the data here
|
||||
$old_return_statements = $this->return_statements;
|
||||
$this->return_statements = true;
|
||||
|
||||
$indexes = $this->get_existing_indexes($table_name, $column_name);
|
||||
$unique_indexes = $this->get_existing_indexes($table_name, $column_name, true);
|
||||
|
||||
// Drop any indexes
|
||||
if (!empty($indexes) || !empty($unique_indexes))
|
||||
{
|
||||
$drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes));
|
||||
foreach ($drop_indexes as $index_name)
|
||||
{
|
||||
$result = $this->sql_index_drop($table_name, $index_name);
|
||||
$statements = array_merge($statements, $result);
|
||||
}
|
||||
}
|
||||
|
||||
// Drop default value constraint
|
||||
$result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
|
||||
$statements = array_merge($statements, $result);
|
||||
|
||||
// Change the column
|
||||
$statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql'];
|
||||
|
||||
if (!empty($column_data['default']) && !$this->mssql_is_column_identity($table_name, $column_name))
|
||||
{
|
||||
// Add new default value constraint
|
||||
$statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $column_data['default'] . ' FOR [' . $column_name . ']';
|
||||
}
|
||||
|
||||
if (!empty($indexes))
|
||||
{
|
||||
// Recreate indexes after we changed the column
|
||||
foreach ($indexes as $index_name => $index_data)
|
||||
{
|
||||
$result = $this->sql_create_index($table_name, $index_name, $index_data);
|
||||
$statements = array_merge($statements, $result);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($unique_indexes))
|
||||
{
|
||||
// Recreate unique indexes after we changed the column
|
||||
foreach ($unique_indexes as $index_name => $index_data)
|
||||
{
|
||||
$result = $this->sql_create_unique_index($table_name, $index_name, $index_data);
|
||||
$statements = array_merge($statements, $result);
|
||||
}
|
||||
}
|
||||
|
||||
$this->return_statements = $old_return_statements;
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get queries to drop the default constraints of a column
|
||||
*
|
||||
* We need to drop the default constraints of a column,
|
||||
* before being able to change their type or deleting them.
|
||||
*
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @return array Array with SQL statements
|
||||
*/
|
||||
protected function mssql_get_drop_default_constraints_queries($table_name, $column_name)
|
||||
{
|
||||
$statements = array();
|
||||
if ($this->mssql_is_sql_server_2000())
|
||||
{
|
||||
// http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
|
||||
// Deprecated in SQL Server 2005
|
||||
$sql = "SELECT so.name AS def_name
|
||||
FROM sysobjects so
|
||||
JOIN sysconstraints sc ON so.id = sc.constid
|
||||
WHERE object_name(so.parent_obj) = '{$table_name}'
|
||||
AND so.xtype = 'D'
|
||||
AND sc.colid = (SELECT colid FROM syscolumns
|
||||
WHERE id = object_id('{$table_name}')
|
||||
AND name = '{$column_name}')";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = "SELECT dobj.name AS def_name
|
||||
FROM sys.columns col
|
||||
LEFT OUTER JOIN sys.objects dobj ON (dobj.object_id = col.default_object_id AND dobj.type = 'D')
|
||||
WHERE col.object_id = object_id('{$table_name}')
|
||||
AND col.name = '{$column_name}'
|
||||
AND dobj.name IS NOT NULL";
|
||||
}
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $row['def_name'] . ']';
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get queries to drop the primary keys depending on the specified column
|
||||
*
|
||||
* We need to drop primary keys depending on this column before being able
|
||||
* to delete them.
|
||||
*
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @return array Array with SQL statements
|
||||
*/
|
||||
protected function mssql_get_drop_default_primary_key_queries($table_name, $column_name)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$sql = "SELECT ccu.CONSTRAINT_NAME, ccu.COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
||||
JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu ON tc.CONSTRAINT_NAME = ccu.Constraint_name
|
||||
WHERE tc.TABLE_NAME = '{$table_name}'
|
||||
AND tc.CONSTRAINT_TYPE = 'Primary Key'
|
||||
AND ccu.COLUMN_NAME = '{$column_name}'";
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($primary_key = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $primary_key['CONSTRAINT_NAME'] . ']';
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if column is an identity column
|
||||
*
|
||||
* Identity columns cannot have defaults set for them.
|
||||
*
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @return bool true if identity, false if not
|
||||
*/
|
||||
protected function mssql_is_column_identity($table_name, $column_name)
|
||||
{
|
||||
if ($this->mssql_is_sql_server_2000())
|
||||
{
|
||||
// http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
|
||||
// Deprecated in SQL Server 2005
|
||||
$sql = "SELECT COLUMNPROPERTY(object_id('{$table_name}'), '{$column_name}', 'IsIdentity') AS is_identity";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = "SELECT is_identity FROM sys.columns
|
||||
WHERE object_id = object_id('{$table_name}')
|
||||
AND name = '{$column_name}'";
|
||||
}
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
$is_identity = $this->db->sql_fetchfield('is_identity');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return (bool) $is_identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list with existing indexes for the column
|
||||
*
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @param bool $unique Should we get unique indexes or normal ones
|
||||
* @return array Array with Index name => columns
|
||||
*/
|
||||
public function get_existing_indexes($table_name, $column_name, $unique = false)
|
||||
{
|
||||
$existing_indexes = array();
|
||||
if ($this->mssql_is_sql_server_2000())
|
||||
{
|
||||
// http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
|
||||
// Deprecated in SQL Server 2005
|
||||
$sql = "SELECT DISTINCT ix.name AS phpbb_index_name
|
||||
FROM sysindexes ix
|
||||
INNER JOIN sysindexkeys ixc
|
||||
ON ixc.id = ix.id
|
||||
AND ixc.indid = ix.indid
|
||||
INNER JOIN syscolumns cols
|
||||
ON cols.colid = ixc.colid
|
||||
AND cols.id = ix.id
|
||||
WHERE ix.id = object_id('{$table_name}')
|
||||
AND cols.name = '{$column_name}'
|
||||
AND INDEXPROPERTY(ix.id, ix.name, 'IsUnique') = " . ($unique ? '1' : '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = "SELECT DISTINCT ix.name AS phpbb_index_name
|
||||
FROM sys.indexes ix
|
||||
INNER JOIN sys.index_columns ixc
|
||||
ON ixc.object_id = ix.object_id
|
||||
AND ixc.index_id = ix.index_id
|
||||
INNER JOIN sys.columns cols
|
||||
ON cols.column_id = ixc.column_id
|
||||
AND cols.object_id = ix.object_id
|
||||
WHERE ix.object_id = object_id('{$table_name}')
|
||||
AND cols.name = '{$column_name}'
|
||||
AND ix.is_primary_key = 0
|
||||
AND ix.is_unique = " . ($unique ? '1' : '0');
|
||||
}
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
if (!isset($row['is_unique']) || ($unique && $row['is_unique'] == 'UNIQUE') || (!$unique && $row['is_unique'] == 'NONUNIQUE'))
|
||||
{
|
||||
$existing_indexes[$row['phpbb_index_name']] = array();
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (empty($existing_indexes))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($this->mssql_is_sql_server_2000())
|
||||
{
|
||||
$sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
|
||||
FROM sysindexes ix
|
||||
INNER JOIN sysindexkeys ixc
|
||||
ON ixc.id = ix.id
|
||||
AND ixc.indid = ix.indid
|
||||
INNER JOIN syscolumns cols
|
||||
ON cols.colid = ixc.colid
|
||||
AND cols.id = ix.id
|
||||
WHERE ix.id = object_id('{$table_name}')
|
||||
AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
|
||||
FROM sys.indexes ix
|
||||
INNER JOIN sys.index_columns ixc
|
||||
ON ixc.object_id = ix.object_id
|
||||
AND ixc.index_id = ix.index_id
|
||||
INNER JOIN sys.columns cols
|
||||
ON cols.column_id = ixc.column_id
|
||||
AND cols.object_id = ix.object_id
|
||||
WHERE ix.object_id = object_id('{$table_name}')
|
||||
AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
|
||||
}
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$existing_indexes[$row['phpbb_index_name']][] = $row['phpbb_column_name'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $existing_indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the used MS SQL Server a SQL Server 2000?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function mssql_is_sql_server_2000()
|
||||
{
|
||||
if ($this->is_sql_server_2000 === null)
|
||||
{
|
||||
$sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$properties = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
$this->is_sql_server_2000 = $properties['mssql_version'][0] == '8';
|
||||
}
|
||||
|
||||
return $this->is_sql_server_2000;
|
||||
}
|
||||
|
||||
}
|
||||
614
install/update/new/phpbb/db/tools/postgres.php
Normal file
614
install/update/new/phpbb/db/tools/postgres.php
Normal file
@@ -0,0 +1,614 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\tools;
|
||||
|
||||
/**
|
||||
* Database Tools for handling cross-db actions such as altering columns, etc.
|
||||
* Currently not supported is returning SQL for creating tables.
|
||||
*/
|
||||
class postgres extends tools
|
||||
{
|
||||
/**
|
||||
* Get the column types for postgres only
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_dbms_type_map()
|
||||
{
|
||||
return array(
|
||||
'postgres' => array(
|
||||
'INT:' => 'INT4',
|
||||
'BINT' => 'INT8',
|
||||
'ULINT' => 'INT4', // unsigned
|
||||
'UINT' => 'INT4', // unsigned
|
||||
'UINT:' => 'INT4', // unsigned
|
||||
'USINT' => 'INT2', // unsigned
|
||||
'BOOL' => 'INT2', // unsigned
|
||||
'TINT:' => 'INT2',
|
||||
'VCHAR' => 'varchar(255)',
|
||||
'VCHAR:' => 'varchar(%d)',
|
||||
'CHAR:' => 'char(%d)',
|
||||
'XSTEXT' => 'varchar(1000)',
|
||||
'STEXT' => 'varchar(3000)',
|
||||
'TEXT' => 'varchar(8000)',
|
||||
'MTEXT' => 'TEXT',
|
||||
'XSTEXT_UNI'=> 'varchar(100)',
|
||||
'STEXT_UNI' => 'varchar(255)',
|
||||
'TEXT_UNI' => 'varchar(4000)',
|
||||
'MTEXT_UNI' => 'TEXT',
|
||||
'TIMESTAMP' => 'INT4', // unsigned
|
||||
'DECIMAL' => 'decimal(5,2)',
|
||||
'DECIMAL:' => 'decimal(%d,2)',
|
||||
'PDECIMAL' => 'decimal(6,3)',
|
||||
'PDECIMAL:' => 'decimal(%d,3)',
|
||||
'VCHAR_UNI' => 'varchar(255)',
|
||||
'VCHAR_UNI:'=> 'varchar(%d)',
|
||||
'VCHAR_CI' => 'varchar_ci',
|
||||
'VARBINARY' => 'bytea',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Set DB Object and set {@link $return_statements return_statements}.
|
||||
*
|
||||
* @param \phpbb\db\driver\driver_interface $db Database connection
|
||||
* @param bool $return_statements True if only statements should be returned and no SQL being executed
|
||||
*/
|
||||
public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
|
||||
{
|
||||
parent::__construct($db, $return_statements);
|
||||
|
||||
// Determine mapping database type
|
||||
$this->sql_layer = 'postgres';
|
||||
|
||||
$this->dbms_type_map = self::get_dbms_type_map();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_list_tables()
|
||||
{
|
||||
$sql = 'SELECT relname
|
||||
FROM pg_stat_user_tables';
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$tables = array();
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$name = current($row);
|
||||
$tables[$name] = $name;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_table($table_name, $table_data)
|
||||
{
|
||||
// holds the DDL for a column
|
||||
$columns = $statements = array();
|
||||
|
||||
if ($this->sql_table_exists($table_name))
|
||||
{
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
$statements[] = 'begin';
|
||||
|
||||
// Determine if we have created a PRIMARY KEY in the earliest
|
||||
$primary_key_gen = false;
|
||||
|
||||
// Determine if the table requires a sequence
|
||||
$create_sequence = false;
|
||||
|
||||
// Begin table sql statement
|
||||
$table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n";
|
||||
|
||||
// Iterate through the columns to create a table
|
||||
foreach ($table_data['COLUMNS'] as $column_name => $column_data)
|
||||
{
|
||||
// here lies an array, filled with information compiled on the column's data
|
||||
$prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
|
||||
|
||||
if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "${column_name}_gen"
|
||||
{
|
||||
trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// here we add the definition of the new column to the list of columns
|
||||
$columns[] = "\t {$column_name} " . $prepared_column['column_type_sql'];
|
||||
|
||||
// see if we have found a primary key set due to a column definition if we have found it, we can stop looking
|
||||
if (!$primary_key_gen)
|
||||
{
|
||||
$primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
|
||||
}
|
||||
|
||||
// create sequence DDL based off of the existence of auto incrementing columns
|
||||
if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
|
||||
{
|
||||
$create_sequence = $column_name;
|
||||
}
|
||||
}
|
||||
|
||||
// this makes up all the columns in the create table statement
|
||||
$table_sql .= implode(",\n", $columns);
|
||||
|
||||
// we have yet to create a primary key for this table,
|
||||
// this means that we can add the one we really wanted instead
|
||||
if (!$primary_key_gen)
|
||||
{
|
||||
// Write primary key
|
||||
if (isset($table_data['PRIMARY_KEY']))
|
||||
{
|
||||
if (!is_array($table_data['PRIMARY_KEY']))
|
||||
{
|
||||
$table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
|
||||
}
|
||||
|
||||
$table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
|
||||
}
|
||||
}
|
||||
|
||||
// do we need to add a sequence for auto incrementing columns?
|
||||
if ($create_sequence)
|
||||
{
|
||||
$statements[] = "CREATE SEQUENCE {$table_name}_seq;";
|
||||
}
|
||||
|
||||
// close the table
|
||||
$table_sql .= "\n);";
|
||||
$statements[] = $table_sql;
|
||||
|
||||
// Write Keys
|
||||
if (isset($table_data['KEYS']))
|
||||
{
|
||||
foreach ($table_data['KEYS'] as $key_name => $key_data)
|
||||
{
|
||||
if (!is_array($key_data[1]))
|
||||
{
|
||||
$key_data[1] = array($key_data[1]);
|
||||
}
|
||||
|
||||
$old_return_statements = $this->return_statements;
|
||||
$this->return_statements = true;
|
||||
|
||||
$key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]);
|
||||
|
||||
foreach ($key_stmts as $key_stmt)
|
||||
{
|
||||
$statements[] = $key_stmt;
|
||||
}
|
||||
|
||||
$this->return_statements = $old_return_statements;
|
||||
}
|
||||
}
|
||||
|
||||
// Commit Transaction
|
||||
$statements[] = 'commit';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_list_columns($table_name)
|
||||
{
|
||||
$columns = array();
|
||||
|
||||
$sql = "SELECT a.attname
|
||||
FROM pg_class c, pg_attribute a
|
||||
WHERE c.relname = '{$table_name}'
|
||||
AND a.attnum > 0
|
||||
AND a.attrelid = c.oid";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$column = strtolower(current($row));
|
||||
$columns[$column] = $column;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_index_exists($table_name, $index_name)
|
||||
{
|
||||
$sql = "SELECT ic.relname as index_name
|
||||
FROM pg_class bc, pg_class ic, pg_index i
|
||||
WHERE (bc.oid = i.indrelid)
|
||||
AND (ic.oid = i.indexrelid)
|
||||
AND (bc.relname = '" . $table_name . "')
|
||||
AND (i.indisunique != 't')
|
||||
AND (i.indisprimary != 't')";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
// This DBMS prefixes index names with the table name
|
||||
$row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
|
||||
|
||||
if (strtolower($row['index_name']) == strtolower($index_name))
|
||||
{
|
||||
$this->db->sql_freeresult($result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_unique_index_exists($table_name, $index_name)
|
||||
{
|
||||
$sql = "SELECT ic.relname as index_name, i.indisunique
|
||||
FROM pg_class bc, pg_class ic, pg_index i
|
||||
WHERE (bc.oid = i.indrelid)
|
||||
AND (ic.oid = i.indexrelid)
|
||||
AND (bc.relname = '" . $table_name . "')
|
||||
AND (i.indisprimary != 't')";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
if ($row['indisunique'] != 't')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// This DBMS prefixes index names with the table name
|
||||
$row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
|
||||
|
||||
if (strtolower($row['index_name']) == strtolower($index_name))
|
||||
{
|
||||
$this->db->sql_freeresult($result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to prepare some column information for better usage
|
||||
* @access private
|
||||
*/
|
||||
function sql_prepare_column_data($table_name, $column_name, $column_data)
|
||||
{
|
||||
if (strlen($column_name) > 30)
|
||||
{
|
||||
trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// Get type
|
||||
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]))
|
||||
{
|
||||
$column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default'];
|
||||
}
|
||||
|
||||
$sql = " {$column_type} ";
|
||||
|
||||
$return_array = array(
|
||||
'column_type' => $column_type,
|
||||
'auto_increment' => false,
|
||||
);
|
||||
|
||||
if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
|
||||
{
|
||||
$default_val = "nextval('{$table_name}_seq')";
|
||||
$return_array['auto_increment'] = true;
|
||||
}
|
||||
else if (!is_null($column_data[1]))
|
||||
{
|
||||
$default_val = "'" . $column_data[1] . "'";
|
||||
$return_array['null'] = 'NOT NULL';
|
||||
$sql .= 'NOT NULL ';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Integers need to have 0 instead of empty string as default
|
||||
if (strpos($column_type, 'INT') === 0)
|
||||
{
|
||||
$default_val = '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
$default_val = "'" . $column_data[1] . "'";
|
||||
}
|
||||
$return_array['null'] = 'NULL';
|
||||
$sql .= 'NULL ';
|
||||
}
|
||||
|
||||
$return_array['default'] = $default_val;
|
||||
|
||||
$sql .= "DEFAULT {$default_val}";
|
||||
|
||||
// Unsigned? Then add a CHECK contraint
|
||||
if (in_array($orig_column_type, $this->unsigned_types))
|
||||
{
|
||||
$return_array['constraint'] = "CHECK ({$column_name} >= 0)";
|
||||
$sql .= " CHECK ({$column_name} >= 0)";
|
||||
}
|
||||
|
||||
$return_array['column_type_sql'] = $sql;
|
||||
|
||||
return $return_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_column_add($table_name, $column_name, $column_data, $inline = false)
|
||||
{
|
||||
$column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
|
||||
$statements = array();
|
||||
|
||||
// Does not support AFTER, only through temporary table
|
||||
if (version_compare($this->db->sql_server_info(true), '8.0', '>='))
|
||||
{
|
||||
$statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql'];
|
||||
}
|
||||
else
|
||||
{
|
||||
// old versions cannot add columns with default and null information
|
||||
$statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint'];
|
||||
|
||||
if (isset($column_data['null']))
|
||||
{
|
||||
if ($column_data['null'] == 'NOT NULL')
|
||||
{
|
||||
$statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($column_data['default']))
|
||||
{
|
||||
$statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_column_remove($table_name, $column_name, $inline = false)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_index_drop($table_name, $index_name)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name;
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_table_drop($table_name)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
if (!$this->sql_table_exists($table_name))
|
||||
{
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
// the most basic operation, get rid of the table
|
||||
$statements[] = 'DROP TABLE ' . $table_name;
|
||||
|
||||
// PGSQL does not "tightly" bind sequences and tables, we must guess...
|
||||
$sql = "SELECT relname
|
||||
FROM pg_class
|
||||
WHERE relkind = 'S'
|
||||
AND relname = '{$table_name}_seq'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
// We don't even care about storing the results. We already know the answer if we get rows back.
|
||||
if ($this->db->sql_fetchrow($result))
|
||||
{
|
||||
$statements[] = "DROP SEQUENCE IF EXISTS {$table_name}_seq;\n";
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_primary_key($table_name, $column, $inline = false)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_unique_index($table_name, $index_name, $column)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$this->check_index_name_length($table_name, $index_name);
|
||||
|
||||
$statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_create_index($table_name, $index_name, $column)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
$this->check_index_name_length($table_name, $index_name);
|
||||
|
||||
// remove index length
|
||||
$column = preg_replace('#:.*$#', '', $column);
|
||||
|
||||
$statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_list_index($table_name)
|
||||
{
|
||||
$index_array = array();
|
||||
|
||||
$sql = "SELECT ic.relname as index_name
|
||||
FROM pg_class bc, pg_class ic, pg_index i
|
||||
WHERE (bc.oid = i.indrelid)
|
||||
AND (ic.oid = i.indexrelid)
|
||||
AND (bc.relname = '" . $table_name . "')
|
||||
AND (i.indisunique != 't')
|
||||
AND (i.indisprimary != 't')";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
|
||||
|
||||
$index_array[] = $row['index_name'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return array_map('strtolower', $index_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_column_change($table_name, $column_name, $column_data, $inline = false)
|
||||
{
|
||||
$column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
|
||||
$statements = array();
|
||||
|
||||
$sql = 'ALTER TABLE ' . $table_name . ' ';
|
||||
|
||||
$sql_array = array();
|
||||
$sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type'];
|
||||
|
||||
if (isset($column_data['null']))
|
||||
{
|
||||
if ($column_data['null'] == 'NOT NULL')
|
||||
{
|
||||
$sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL';
|
||||
}
|
||||
else if ($column_data['null'] == 'NULL')
|
||||
{
|
||||
$sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($column_data['default']))
|
||||
{
|
||||
$sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
|
||||
}
|
||||
|
||||
// we don't want to double up on constraints if we change different number data types
|
||||
if (isset($column_data['constraint']))
|
||||
{
|
||||
$constraint_sql = "SELECT consrc as constraint_data
|
||||
FROM pg_constraint, pg_class bc
|
||||
WHERE conrelid = bc.oid
|
||||
AND bc.relname = '{$table_name}'
|
||||
AND NOT EXISTS (
|
||||
SELECT *
|
||||
FROM pg_constraint as c, pg_inherits as i
|
||||
WHERE i.inhrelid = pg_constraint.conrelid
|
||||
AND c.conname = pg_constraint.conname
|
||||
AND c.consrc = pg_constraint.consrc
|
||||
AND c.conrelid = i.inhparent
|
||||
)";
|
||||
|
||||
$constraint_exists = false;
|
||||
|
||||
$result = $this->db->sql_query($constraint_sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
if (trim($row['constraint_data']) == trim($column_data['constraint']))
|
||||
{
|
||||
$constraint_exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$constraint_exists)
|
||||
{
|
||||
$sql_array[] = 'ADD ' . $column_data['constraint'];
|
||||
}
|
||||
}
|
||||
|
||||
$sql .= implode(', ', $sql_array);
|
||||
|
||||
$statements[] = $sql;
|
||||
|
||||
return $this->_sql_run_sql($statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list with existing indexes for the column
|
||||
*
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @param bool $unique Should we get unique indexes or normal ones
|
||||
* @return array Array with Index name => columns
|
||||
*/
|
||||
public function get_existing_indexes($table_name, $column_name, $unique = false)
|
||||
{
|
||||
// Not supported
|
||||
throw new \Exception('DBMS is not supported');
|
||||
}
|
||||
}
|
||||
1879
install/update/new/phpbb/db/tools/tools.php
Normal file
1879
install/update/new/phpbb/db/tools/tools.php
Normal file
File diff suppressed because it is too large
Load Diff
678
install/update/new/phpbb/di/container_builder.php
Normal file
678
install/update/new/phpbb/di/container_builder.php
Normal file
@@ -0,0 +1,678 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\di;
|
||||
|
||||
use phpbb\filesystem\filesystem;
|
||||
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
|
||||
use Symfony\Component\Config\ConfigCache;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
|
||||
|
||||
class container_builder
|
||||
{
|
||||
/**
|
||||
* @var string The environment to use.
|
||||
*/
|
||||
protected $environment;
|
||||
|
||||
/**
|
||||
* @var string phpBB Root Path
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string php file extension
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* The container under construction
|
||||
*
|
||||
* @var ContainerBuilder
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $dbal_connection = null;
|
||||
|
||||
/**
|
||||
* Indicates whether extensions should be used (default to true).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $use_extensions = true;
|
||||
|
||||
/**
|
||||
* Defines a custom path to find the configuration of the container (default to $this->phpbb_root_path . 'config')
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $config_path = null;
|
||||
|
||||
/**
|
||||
* Indicates whether the container should be dumped to the filesystem (default to true).
|
||||
*
|
||||
* If DEBUG_CONTAINER is set this option is ignored and a new container is build.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $use_cache = true;
|
||||
|
||||
/**
|
||||
* Indicates if the container should be compiled automatically (default to true).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $compile_container = true;
|
||||
|
||||
/**
|
||||
* Custom parameters to inject into the container.
|
||||
*
|
||||
* Default to:
|
||||
* array(
|
||||
* 'core.root_path', $this->phpbb_root_path,
|
||||
* 'core.php_ext', $this->php_ext,
|
||||
* );
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $custom_parameters = [];
|
||||
|
||||
/**
|
||||
* @var \phpbb\config_php_file
|
||||
*/
|
||||
protected $config_php_file;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cache_dir;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $container_extensions;
|
||||
|
||||
/** @var \Exception */
|
||||
private $build_exception;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $phpbb_root_path Path to the phpbb includes directory.
|
||||
* @param string $php_ext php file extension
|
||||
*/
|
||||
public function __construct($phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return a new Container respecting the current configuration
|
||||
*
|
||||
* @return \phpbb_cache_container|ContainerBuilder
|
||||
*/
|
||||
public function get_container()
|
||||
{
|
||||
try
|
||||
{
|
||||
$container_filename = $this->get_container_filename();
|
||||
$config_cache = new ConfigCache($container_filename, defined('DEBUG'));
|
||||
if ($this->use_cache && $config_cache->isFresh())
|
||||
{
|
||||
if ($this->use_extensions)
|
||||
{
|
||||
$autoload_cache = new ConfigCache($this->get_autoload_filename(), defined('DEBUG'));
|
||||
if (!$autoload_cache->isFresh())
|
||||
{
|
||||
// autoload cache should be refreshed
|
||||
$this->load_extensions();
|
||||
}
|
||||
|
||||
require($this->get_autoload_filename());
|
||||
}
|
||||
|
||||
require($config_cache->getPath());
|
||||
$this->container = new \phpbb_cache_container();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->container_extensions = [
|
||||
new extension\core($this->get_config_path()),
|
||||
];
|
||||
|
||||
if ($this->use_extensions)
|
||||
{
|
||||
$this->load_extensions();
|
||||
}
|
||||
|
||||
// Add tables extension after all extensions
|
||||
$this->container_extensions[] = new extension\tables();
|
||||
|
||||
// Inject the config
|
||||
if ($this->config_php_file)
|
||||
{
|
||||
$this->container_extensions[] = new extension\config($this->config_php_file);
|
||||
}
|
||||
|
||||
$this->container = $this->create_container($this->container_extensions);
|
||||
|
||||
// Easy collections through tags
|
||||
$this->container->addCompilerPass(new pass\collection_pass());
|
||||
|
||||
// Event listeners "phpBB style"
|
||||
$this->container->addCompilerPass(new RegisterListenersPass('dispatcher', 'event.listener_listener', 'event.listener'));
|
||||
|
||||
// Event listeners "Symfony style"
|
||||
$this->container->addCompilerPass(new RegisterListenersPass('dispatcher'));
|
||||
|
||||
if ($this->use_extensions)
|
||||
{
|
||||
$this->register_ext_compiler_pass();
|
||||
}
|
||||
|
||||
$filesystem = new filesystem();
|
||||
$loader = new YamlFileLoader($this->container, new FileLocator($filesystem->realpath($this->get_config_path())));
|
||||
$loader->load($this->container->getParameter('core.environment') . '/config.yml');
|
||||
|
||||
$this->inject_custom_parameters();
|
||||
|
||||
if ($this->compile_container)
|
||||
{
|
||||
$this->container->compile();
|
||||
|
||||
if ($this->use_cache)
|
||||
{
|
||||
$this->dump_container($config_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->compile_container && $this->config_php_file)
|
||||
{
|
||||
$this->container->set('config.php', $this->config_php_file);
|
||||
}
|
||||
|
||||
$this->inject_dbal_driver();
|
||||
|
||||
return $this->container;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Don't try to recover if we are in the development environment
|
||||
if ($this->get_environment() === 'development')
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($this->build_exception === null)
|
||||
{
|
||||
$this->build_exception = $e;
|
||||
|
||||
return $this
|
||||
->without_extensions()
|
||||
->without_cache()
|
||||
->with_custom_parameters(array_merge($this->custom_parameters, [
|
||||
'container_exception' => $e,
|
||||
]))
|
||||
->get_container();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rethrow the original exception if it's still failing
|
||||
throw $this->build_exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the extensions.
|
||||
*
|
||||
* @param string $environment The environment to use
|
||||
* @return $this
|
||||
*/
|
||||
public function with_environment($environment)
|
||||
{
|
||||
$this->environment = $environment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the extensions.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function with_extensions()
|
||||
{
|
||||
$this->use_extensions = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the extensions.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function without_extensions()
|
||||
{
|
||||
$this->use_extensions = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the caching of the container.
|
||||
*
|
||||
* If DEBUG_CONTAINER is set this option is ignored and a new container is build.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function with_cache()
|
||||
{
|
||||
$this->use_cache = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the caching of the container.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function without_cache()
|
||||
{
|
||||
$this->use_cache = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cache directory.
|
||||
*
|
||||
* @param string $cache_dir The cache directory.
|
||||
* @return $this
|
||||
*/
|
||||
public function with_cache_dir($cache_dir)
|
||||
{
|
||||
$this->cache_dir = $cache_dir;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the compilation of the container.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function with_compiled_container()
|
||||
{
|
||||
$this->compile_container = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the compilation of the container.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function without_compiled_container()
|
||||
{
|
||||
$this->compile_container = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom path to find the configuration of the container.
|
||||
*
|
||||
* @param string $config_path
|
||||
* @return $this
|
||||
*/
|
||||
public function with_config_path($config_path)
|
||||
{
|
||||
$this->config_path = $config_path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom parameters to inject into the container.
|
||||
*
|
||||
* @param array $custom_parameters
|
||||
* @return $this
|
||||
*/
|
||||
public function with_custom_parameters($custom_parameters)
|
||||
{
|
||||
$this->custom_parameters = $custom_parameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom parameters to inject into the container.
|
||||
*
|
||||
* @param \phpbb\config_php_file $config_php_file
|
||||
* @return $this
|
||||
*/
|
||||
public function with_config(\phpbb\config_php_file $config_php_file)
|
||||
{
|
||||
$this->config_php_file = $config_php_file;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the container configuration (default: root_path/config)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_config_path()
|
||||
{
|
||||
return $this->config_path ?: $this->phpbb_root_path . 'config';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the cache directory (default: root_path/cache/environment).
|
||||
*
|
||||
* @return string Path to the cache directory.
|
||||
*/
|
||||
protected function get_cache_dir()
|
||||
{
|
||||
return $this->cache_dir ?: $this->phpbb_root_path . 'cache/' . $this->get_environment() . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the enabled extensions.
|
||||
*/
|
||||
protected function load_extensions()
|
||||
{
|
||||
if ($this->config_php_file !== null)
|
||||
{
|
||||
// Build an intermediate container to load the ext list from the database
|
||||
$container_builder = new container_builder($this->phpbb_root_path, $this->php_ext);
|
||||
$ext_container = $container_builder
|
||||
->without_cache()
|
||||
->without_extensions()
|
||||
->with_config($this->config_php_file)
|
||||
->with_config_path($this->get_config_path())
|
||||
->with_environment('production')
|
||||
->without_compiled_container()
|
||||
->get_container()
|
||||
;
|
||||
|
||||
$ext_container->register('cache.driver', '\\phpbb\\cache\\driver\\dummy');
|
||||
$ext_container->compile();
|
||||
|
||||
$extensions = $ext_container->get('ext.manager')->all_enabled();
|
||||
|
||||
// Load each extension found
|
||||
$autoloaders = '<?php
|
||||
/**
|
||||
* Loads all extensions custom auto-loaders.
|
||||
*
|
||||
* This file has been auto-generated
|
||||
* by phpBB while loading the extensions.
|
||||
*/
|
||||
|
||||
';
|
||||
foreach ($extensions as $ext_name => $path)
|
||||
{
|
||||
$extension_class = '\\' . str_replace('/', '\\', $ext_name) . '\\di\\extension';
|
||||
|
||||
if (!class_exists($extension_class))
|
||||
{
|
||||
$extension_class = '\\phpbb\\extension\\di\\extension_base';
|
||||
}
|
||||
|
||||
$this->container_extensions[] = new $extension_class($ext_name, $path);
|
||||
|
||||
// Load extension autoloader
|
||||
$filename = $path . 'vendor/autoload.php';
|
||||
if (file_exists($filename))
|
||||
{
|
||||
$autoloaders .= "require('{$filename}');\n";
|
||||
}
|
||||
}
|
||||
|
||||
$configCache = new ConfigCache($this->get_autoload_filename(), false);
|
||||
$configCache->write($autoloaders);
|
||||
|
||||
require($this->get_autoload_filename());
|
||||
}
|
||||
else
|
||||
{
|
||||
// To load the extensions we need the database credentials.
|
||||
// Automatically disable the extensions if we don't have them.
|
||||
$this->use_extensions = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the container to the disk.
|
||||
*
|
||||
* @param ConfigCache $cache The config cache
|
||||
*/
|
||||
protected function dump_container($cache)
|
||||
{
|
||||
try
|
||||
{
|
||||
$dumper = new PhpDumper($this->container);
|
||||
$proxy_dumper = new ProxyDumper();
|
||||
$dumper->setProxyDumper($proxy_dumper);
|
||||
|
||||
$cached_container_dump = $dumper->dump(array(
|
||||
'class' => 'phpbb_cache_container',
|
||||
'base_class' => 'Symfony\\Component\\DependencyInjection\\Container',
|
||||
));
|
||||
|
||||
$cache->write($cached_container_dump, $this->container->getResources());
|
||||
}
|
||||
catch (IOException $e)
|
||||
{
|
||||
// Don't fail if the cache isn't writeable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ContainerBuilder object
|
||||
*
|
||||
* @param array $extensions Array of Container extension objects
|
||||
* @return ContainerBuilder object
|
||||
*/
|
||||
protected function create_container(array $extensions)
|
||||
{
|
||||
$container = new ContainerBuilder(new ParameterBag($this->get_core_parameters()));
|
||||
$container->setProxyInstantiator(new proxy_instantiator($this->get_cache_dir()));
|
||||
|
||||
$extensions_alias = array();
|
||||
|
||||
foreach ($extensions as $extension)
|
||||
{
|
||||
$container->registerExtension($extension);
|
||||
$extensions_alias[] = $extension->getAlias();
|
||||
}
|
||||
|
||||
$container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions_alias));
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the customs parameters into the container
|
||||
*/
|
||||
protected function inject_custom_parameters()
|
||||
{
|
||||
foreach ($this->custom_parameters as $key => $value)
|
||||
{
|
||||
$this->container->setParameter($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the dbal connection driver into container
|
||||
*/
|
||||
protected function inject_dbal_driver()
|
||||
{
|
||||
if (empty($this->config_php_file))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$config_data = $this->config_php_file->get_all();
|
||||
if (!empty($config_data))
|
||||
{
|
||||
if ($this->dbal_connection === null)
|
||||
{
|
||||
$dbal_driver_class = $this->config_php_file->convert_30_dbms_to_31($this->config_php_file->get('dbms'));
|
||||
/** @var \phpbb\db\driver\driver_interface $dbal_connection */
|
||||
$this->dbal_connection = new $dbal_driver_class();
|
||||
$this->dbal_connection->sql_connect(
|
||||
$this->config_php_file->get('dbhost'),
|
||||
$this->config_php_file->get('dbuser'),
|
||||
$this->config_php_file->get('dbpasswd'),
|
||||
$this->config_php_file->get('dbname'),
|
||||
$this->config_php_file->get('dbport'),
|
||||
false,
|
||||
defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK
|
||||
);
|
||||
}
|
||||
$this->container->set('dbal.conn.driver', $this->dbal_connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the core parameters.
|
||||
*
|
||||
* @return array An array of core parameters
|
||||
*/
|
||||
protected function get_core_parameters()
|
||||
{
|
||||
return array_merge(
|
||||
array(
|
||||
'core.root_path' => $this->phpbb_root_path,
|
||||
'core.php_ext' => $this->php_ext,
|
||||
'core.environment' => $this->get_environment(),
|
||||
'core.debug' => defined('DEBUG') ? DEBUG : false,
|
||||
'core.cache_dir' => $this->get_cache_dir(),
|
||||
),
|
||||
$this->get_env_parameters()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the environment parameters.
|
||||
*
|
||||
* Only the parameters starting with "PHPBB__" are considered.
|
||||
*
|
||||
* @return array An array of parameters
|
||||
*/
|
||||
protected function get_env_parameters()
|
||||
{
|
||||
$parameters = array();
|
||||
foreach ($_SERVER as $key => $value)
|
||||
{
|
||||
if (0 === strpos($key, 'PHPBB__'))
|
||||
{
|
||||
$parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename under which the dumped container will be stored.
|
||||
*
|
||||
* @return string Path for dumped container
|
||||
*/
|
||||
protected function get_container_filename()
|
||||
{
|
||||
$container_params = [
|
||||
'phpbb_root_path' => $this->phpbb_root_path,
|
||||
'use_extensions' => $this->use_extensions,
|
||||
'config_path' => $this->config_path,
|
||||
];
|
||||
|
||||
return $this->get_cache_dir() . 'container_' . md5(implode(',', $container_params)) . '.' . $this->php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename under which the dumped extensions autoloader will be stored.
|
||||
*
|
||||
* @return string Path for dumped extensions autoloader
|
||||
*/
|
||||
protected function get_autoload_filename()
|
||||
{
|
||||
$container_params = [
|
||||
'phpbb_root_path' => $this->phpbb_root_path,
|
||||
'use_extensions' => $this->use_extensions,
|
||||
'config_path' => $this->config_path,
|
||||
];
|
||||
|
||||
return $this->get_cache_dir() . 'autoload_' . md5(implode(',', $container_params)) . '.' . $this->php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the current environment.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_environment()
|
||||
{
|
||||
return $this->environment ?: PHPBB_ENVIRONMENT;
|
||||
}
|
||||
|
||||
private function register_ext_compiler_pass()
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder
|
||||
->name('*_pass.php')
|
||||
->path('di/pass')
|
||||
->files()
|
||||
->ignoreDotFiles(true)
|
||||
->ignoreUnreadableDirs(true)
|
||||
->ignoreVCS(true)
|
||||
->followLinks()
|
||||
->in($this->phpbb_root_path . 'ext')
|
||||
;
|
||||
|
||||
/** @var \SplFileInfo $pass */
|
||||
foreach ($finder as $pass)
|
||||
{
|
||||
$filename = $pass->getPathname();
|
||||
$filename = substr($filename, 0, -strlen('.' . $pass->getExtension()));
|
||||
$filename = str_replace(DIRECTORY_SEPARATOR, '/', $filename);
|
||||
$className = preg_replace('#^.*ext/#', '', $filename);
|
||||
$className = '\\' . str_replace('/', '\\', $className);
|
||||
|
||||
if (class_exists($className) && in_array('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface', class_implements($className), true))
|
||||
{
|
||||
$this->container->addCompilerPass(new $className());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\di\extension;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
class container_configuration implements ConfigurationInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Generates the configuration tree builder.
|
||||
*
|
||||
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
|
||||
*/
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder();
|
||||
$rootNode = $treeBuilder->root('core');
|
||||
$rootNode
|
||||
->children()
|
||||
->booleanNode('require_dev_dependencies')->defaultValue(false)->end()
|
||||
->booleanNode('allow_install_dir')->defaultValue(false)->end()
|
||||
->arrayNode('debug')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('exceptions')->defaultValue(false)->end()
|
||||
->booleanNode('load_time')->defaultValue(false)->end()
|
||||
->booleanNode('sql_explain')->defaultValue(false)->end()
|
||||
->booleanNode('memory')->defaultValue(false)->end()
|
||||
->booleanNode('show_errors')->defaultValue(false)->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('twig')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('debug')->defaultValue(null)->end()
|
||||
->booleanNode('auto_reload')->defaultValue(null)->end()
|
||||
->booleanNode('enable_debug_extension')->defaultValue(false)->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('session')
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('log_errors')->defaultValue(false)->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
132
install/update/new/phpbb/di/extension/core.php
Normal file
132
install/update/new/phpbb/di/extension/core.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\di\extension;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
|
||||
/**
|
||||
* Container core extension
|
||||
*/
|
||||
class core extends Extension
|
||||
{
|
||||
const TWIG_OPTIONS_POSITION = 7;
|
||||
|
||||
/**
|
||||
* Config path
|
||||
* @var string
|
||||
*/
|
||||
protected $config_path;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $config_path Config path
|
||||
*/
|
||||
public function __construct($config_path)
|
||||
{
|
||||
$this->config_path = $config_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a specific configuration.
|
||||
*
|
||||
* @param array $configs An array of configuration values
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*
|
||||
* @throws \InvalidArgumentException When provided tag is not defined in this extension
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$filesystem = new \phpbb\filesystem\filesystem();
|
||||
$loader = new YamlFileLoader($container, new FileLocator($filesystem->realpath($this->config_path)));
|
||||
$loader->load($container->getParameter('core.environment') . '/container/environment.yml');
|
||||
|
||||
$config = $this->getConfiguration($configs, $container);
|
||||
$config = $this->processConfiguration($config, $configs);
|
||||
|
||||
if ($config['require_dev_dependencies'])
|
||||
{
|
||||
if (!class_exists('Goutte\Client', true))
|
||||
{
|
||||
trigger_error(
|
||||
'Composer development dependencies have not been set up for the ' . $container->getParameter('core.environment') . ' environment yet, run ' .
|
||||
"'php ../composer.phar install --dev' from the phpBB directory to do so.",
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$container->setParameter('allow_install_dir', $config['allow_install_dir']);
|
||||
|
||||
// Set the Twig options if defined in the environment
|
||||
$definition = $container->getDefinition('template.twig.environment');
|
||||
$twig_environment_options = $definition->getArgument(static::TWIG_OPTIONS_POSITION);
|
||||
if ($config['twig']['debug'])
|
||||
{
|
||||
$twig_environment_options['debug'] = true;
|
||||
}
|
||||
if ($config['twig']['auto_reload'])
|
||||
{
|
||||
$twig_environment_options['auto_reload'] = true;
|
||||
}
|
||||
|
||||
// Replace the 7th argument, the options passed to the environment
|
||||
$definition->replaceArgument(static::TWIG_OPTIONS_POSITION, $twig_environment_options);
|
||||
|
||||
if ($config['twig']['enable_debug_extension'])
|
||||
{
|
||||
$definition = $container->getDefinition('template.twig.extensions.debug');
|
||||
$definition->addTag('twig.extension');
|
||||
}
|
||||
|
||||
// Set the debug options
|
||||
foreach ($config['debug'] as $name => $value)
|
||||
{
|
||||
$container->setParameter('debug.' . $name, $value);
|
||||
}
|
||||
|
||||
// Set the log options
|
||||
foreach ($config['session'] as $name => $value)
|
||||
{
|
||||
$container->setParameter('session.' . $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||
{
|
||||
$r = new \ReflectionClass('\phpbb\di\extension\container_configuration');
|
||||
$container->addResource(new FileResource($r->getFileName()));
|
||||
|
||||
return new container_configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the recommended alias to use in XML.
|
||||
*
|
||||
* This alias is also the mandatory prefix to use when using YAML.
|
||||
*
|
||||
* @return string The alias
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return 'core';
|
||||
}
|
||||
}
|
||||
59
install/update/new/phpbb/di/extension/tables.php
Normal file
59
install/update/new/phpbb/di/extension/tables.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\di\extension;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
|
||||
/**
|
||||
* Container tables extension
|
||||
*/
|
||||
class tables extends Extension
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
// Tables is a reserved parameter and will be overwritten at all times
|
||||
$tables = [];
|
||||
|
||||
// Add access via 'tables' parameter to acquire array with all tables
|
||||
$parameterBag = $container->getParameterBag();
|
||||
$parameters = $parameterBag->all();
|
||||
foreach ($parameters as $parameter_name => $parameter_value)
|
||||
{
|
||||
if (!preg_match('/tables\.(.+)/', $parameter_name, $matches))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$tables[$matches[1]] = $parameter_value;
|
||||
}
|
||||
|
||||
$container->setParameter('tables', $tables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the recommended alias to use in XML.
|
||||
*
|
||||
* This alias is also the mandatory prefix to use when using YAML.
|
||||
*
|
||||
* @return string The alias
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return 'tables';
|
||||
}
|
||||
}
|
||||
122
install/update/new/phpbb/di/service_collection.php
Normal file
122
install/update/new/phpbb/di/service_collection.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\di;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Collection of services to be configured at container compile time.
|
||||
*/
|
||||
class service_collection extends \ArrayObject
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $service_classes;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param ContainerInterface $container Container object
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->service_classes = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new service_collection_iterator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetGet($index)
|
||||
{
|
||||
return $this->container->get($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a service to the collection
|
||||
*
|
||||
* @param string $name The service name
|
||||
* @return void
|
||||
*/
|
||||
public function add($name)
|
||||
{
|
||||
$this->offsetSet($name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a service's class to the collection
|
||||
*
|
||||
* @param string $service_id
|
||||
* @param string $class
|
||||
*/
|
||||
public function add_service_class($service_id, $class)
|
||||
{
|
||||
$this->service_classes[$service_id] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get services' classes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_service_classes()
|
||||
{
|
||||
return $this->service_classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the service associated to a class
|
||||
*
|
||||
* @return mixed
|
||||
* @throw \RuntimeException if the
|
||||
*/
|
||||
public function get_by_class($class)
|
||||
{
|
||||
$service_id = null;
|
||||
|
||||
foreach ($this->service_classes as $id => $service_class)
|
||||
{
|
||||
if ($service_class === $class)
|
||||
{
|
||||
if ($service_id !== null)
|
||||
{
|
||||
throw new \RuntimeException('More than one service definitions found for class "'.$class.'" in collection.');
|
||||
}
|
||||
|
||||
$service_id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
if ($service_id === null)
|
||||
{
|
||||
throw new \RuntimeException('No service found for class "'.$class.'" in collection.');
|
||||
}
|
||||
|
||||
return $this->offsetGet($service_id);
|
||||
}
|
||||
}
|
||||
568
install/update/new/phpbb/event/md_exporter.php
Normal file
568
install/update/new/phpbb/event/md_exporter.php
Normal file
@@ -0,0 +1,568 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\event;
|
||||
|
||||
/**
|
||||
* Crawls through a markdown file and grabs all events
|
||||
*/
|
||||
class md_exporter
|
||||
{
|
||||
/** @var string Path where we look for files*/
|
||||
protected $path;
|
||||
|
||||
/** @var string phpBB Root Path */
|
||||
protected $root_path;
|
||||
|
||||
/** @var string The minimum version for the events to return */
|
||||
protected $min_version;
|
||||
|
||||
/** @var string The maximum version for the events to return */
|
||||
protected $max_version;
|
||||
|
||||
/** @var string */
|
||||
protected $filter;
|
||||
|
||||
/** @var string */
|
||||
protected $current_event;
|
||||
|
||||
/** @var array */
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* @param string $phpbb_root_path
|
||||
* @param mixed $extension String 'vendor/ext' to filter, null for phpBB core
|
||||
* @param string $min_version
|
||||
* @param string $max_version
|
||||
*/
|
||||
public function __construct($phpbb_root_path, $extension = null, $min_version = null, $max_version = null)
|
||||
{
|
||||
$this->root_path = $phpbb_root_path;
|
||||
$this->path = $this->root_path;
|
||||
if ($extension)
|
||||
{
|
||||
$this->path .= 'ext/' . $extension . '/';
|
||||
}
|
||||
|
||||
$this->events = array();
|
||||
$this->events_by_file = array();
|
||||
$this->filter = $this->current_event = '';
|
||||
$this->min_version = $min_version;
|
||||
$this->max_version = $max_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all events
|
||||
*
|
||||
* @return array Array with events: name => details
|
||||
*/
|
||||
public function get_events()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $md_file Relative from phpBB root
|
||||
* @return int Number of events found
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function crawl_phpbb_directory_adm($md_file)
|
||||
{
|
||||
$this->crawl_eventsmd($md_file, 'adm');
|
||||
|
||||
$file_list = $this->get_recursive_file_list($this->path . 'adm/style/');
|
||||
foreach ($file_list as $file)
|
||||
{
|
||||
$file_name = 'adm/style/' . $file;
|
||||
$this->validate_events_from_file($file_name, $this->crawl_file_for_events($file_name));
|
||||
}
|
||||
|
||||
return count($this->events);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $md_file Relative from phpBB root
|
||||
* @return int Number of events found
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function crawl_phpbb_directory_styles($md_file)
|
||||
{
|
||||
$this->crawl_eventsmd($md_file, 'styles');
|
||||
|
||||
$styles = array('prosilver');
|
||||
foreach ($styles as $style)
|
||||
{
|
||||
$file_list = $this->get_recursive_file_list(
|
||||
$this->path . 'styles/' . $style . '/template/'
|
||||
);
|
||||
|
||||
foreach ($file_list as $file)
|
||||
{
|
||||
$file_name = 'styles/' . $style . '/template/' . $file;
|
||||
$this->validate_events_from_file($file_name, $this->crawl_file_for_events($file_name));
|
||||
}
|
||||
}
|
||||
|
||||
return count($this->events);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $md_file Relative from phpBB root
|
||||
* @param string $filter Should be 'styles' or 'adm'
|
||||
* @return int Number of events found
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function crawl_eventsmd($md_file, $filter)
|
||||
{
|
||||
if (!file_exists($this->path . $md_file))
|
||||
{
|
||||
throw new \LogicException("The event docs file '{$md_file}' could not be found");
|
||||
}
|
||||
|
||||
$file_content = file_get_contents($this->path . $md_file);
|
||||
$this->filter = $filter;
|
||||
|
||||
$events = explode("\n\n", $file_content);
|
||||
foreach ($events as $event)
|
||||
{
|
||||
// Last row of the file
|
||||
if (strpos($event, "\n===\n") === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
list($event_name, $details) = explode("\n===\n", $event, 2);
|
||||
$this->validate_event_name($event_name);
|
||||
$sorted_events = [$this->current_event, $event_name];
|
||||
natsort($sorted_events);
|
||||
$this->current_event = $event_name;
|
||||
|
||||
if (isset($this->events[$this->current_event]))
|
||||
{
|
||||
throw new \LogicException("The event '{$this->current_event}' is defined multiple times");
|
||||
}
|
||||
|
||||
// Use array_values() to get actual first element and check against natural order
|
||||
if (array_values($sorted_events)[0] === $event_name)
|
||||
{
|
||||
throw new \LogicException("The event '{$sorted_events[1]}' should be defined before '{$sorted_events[0]}'");
|
||||
}
|
||||
|
||||
if (($this->filter == 'adm' && strpos($this->current_event, 'acp_') !== 0)
|
||||
|| ($this->filter == 'styles' && strpos($this->current_event, 'acp_') === 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
list($file_details, $details) = explode("\n* Since: ", $details, 2);
|
||||
|
||||
$changed_versions = array();
|
||||
if (strpos($details, "\n* Changed: ") !== false)
|
||||
{
|
||||
list($since, $details) = explode("\n* Changed: ", $details, 2);
|
||||
while (strpos($details, "\n* Changed: ") !== false)
|
||||
{
|
||||
list($changed, $details) = explode("\n* Changed: ", $details, 2);
|
||||
$changed_versions[] = $changed;
|
||||
}
|
||||
list($changed, $description) = explode("\n* Purpose: ", $details, 2);
|
||||
$changed_versions[] = $changed;
|
||||
}
|
||||
else
|
||||
{
|
||||
list($since, $description) = explode("\n* Purpose: ", $details, 2);
|
||||
$changed_versions = array();
|
||||
}
|
||||
|
||||
$files = $this->validate_file_list($file_details);
|
||||
$since = $this->validate_since($since);
|
||||
$changes = array();
|
||||
foreach ($changed_versions as $changed)
|
||||
{
|
||||
list($changed_version, $changed_description) = $this->validate_changed($changed);
|
||||
|
||||
if (isset($changes[$changed_version]))
|
||||
{
|
||||
throw new \LogicException("Duplicate change information found for event '{$this->current_event}'");
|
||||
}
|
||||
|
||||
$changes[$changed_version] = $changed_description;
|
||||
}
|
||||
$description = trim($description, "\n") . "\n";
|
||||
|
||||
if (!$this->version_is_filtered($since))
|
||||
{
|
||||
$is_filtered = false;
|
||||
foreach ($changes as $version => $null)
|
||||
{
|
||||
if ($this->version_is_filtered($version))
|
||||
{
|
||||
$is_filtered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$is_filtered)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->events[$event_name] = array(
|
||||
'event' => $this->current_event,
|
||||
'files' => $files,
|
||||
'since' => $since,
|
||||
'changed' => $changes,
|
||||
'description' => $description,
|
||||
);
|
||||
}
|
||||
|
||||
return count($this->events);
|
||||
}
|
||||
|
||||
/**
|
||||
* The version to check
|
||||
*
|
||||
* @param string $version
|
||||
* @return bool
|
||||
*/
|
||||
protected function version_is_filtered($version)
|
||||
{
|
||||
return (!$this->min_version || phpbb_version_compare($this->min_version, $version, '<='))
|
||||
&& (!$this->max_version || phpbb_version_compare($this->max_version, $version, '>='));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the php events as a wiki table
|
||||
*
|
||||
* @param string $action
|
||||
* @return string Number of events found
|
||||
*/
|
||||
public function export_events_for_wiki($action = '')
|
||||
{
|
||||
if ($this->filter === 'adm')
|
||||
{
|
||||
if ($action === 'diff')
|
||||
{
|
||||
$wiki_page = '=== ACP Template Events ===' . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$wiki_page = '= ACP Template Events =' . "\n";
|
||||
}
|
||||
$wiki_page .= '{| class="zebra sortable" cellspacing="0" cellpadding="5"' . "\n";
|
||||
$wiki_page .= '! Identifier !! Placement !! Added in Release !! Explanation' . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($action === 'diff')
|
||||
{
|
||||
$wiki_page = '=== Template Events ===' . "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$wiki_page = '= Template Events =' . "\n";
|
||||
}
|
||||
$wiki_page .= '{| class="zebra sortable" cellspacing="0" cellpadding="5"' . "\n";
|
||||
$wiki_page .= '! Identifier !! Prosilver Placement (If applicable) !! Added in Release !! Explanation' . "\n";
|
||||
}
|
||||
|
||||
foreach ($this->events as $event_name => $event)
|
||||
{
|
||||
$wiki_page .= "|- id=\"{$event_name}\"\n";
|
||||
$wiki_page .= "| [[#{$event_name}|{$event_name}]] || ";
|
||||
|
||||
if ($this->filter === 'adm')
|
||||
{
|
||||
$wiki_page .= implode(', ', $event['files']['adm']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$wiki_page .= implode(', ', $event['files']['prosilver']);
|
||||
}
|
||||
|
||||
$wiki_page .= " || {$event['since']} || " . str_replace("\n", ' ', $event['description']) . "\n";
|
||||
}
|
||||
$wiki_page .= '|}' . "\n";
|
||||
|
||||
return $wiki_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a template event name
|
||||
*
|
||||
* @param $event_name
|
||||
* @return null
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function validate_event_name($event_name)
|
||||
{
|
||||
if (!preg_match('#^([a-z][a-z0-9]*(?:_[a-z][a-z0-9]*)+)$#', $event_name))
|
||||
{
|
||||
throw new \LogicException("Invalid event name '{$event_name}'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate "Since" Information
|
||||
*
|
||||
* @param string $since
|
||||
* @return string
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function validate_since($since)
|
||||
{
|
||||
if (!$this->validate_version($since))
|
||||
{
|
||||
throw new \LogicException("Invalid since information found for event '{$this->current_event}'");
|
||||
}
|
||||
|
||||
return $since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate "Changed" Information
|
||||
*
|
||||
* @param string $changed
|
||||
* @return string
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function validate_changed($changed)
|
||||
{
|
||||
if (strpos($changed, ' ') !== false)
|
||||
{
|
||||
list($version, $description) = explode(' ', $changed, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
$version = $changed;
|
||||
$description = '';
|
||||
}
|
||||
|
||||
if (!$this->validate_version($version))
|
||||
{
|
||||
throw new \LogicException("Invalid changed information found for event '{$this->current_event}'");
|
||||
}
|
||||
|
||||
return array($version, $description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate "version" Information
|
||||
*
|
||||
* @param string $version
|
||||
* @return bool True if valid, false otherwise
|
||||
*/
|
||||
public function validate_version($version)
|
||||
{
|
||||
return preg_match('#^\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?$#', $version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the files list
|
||||
*
|
||||
* @param string $file_details
|
||||
* @return array
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function validate_file_list($file_details)
|
||||
{
|
||||
$files_list = array(
|
||||
'prosilver' => array(),
|
||||
'adm' => array(),
|
||||
);
|
||||
|
||||
// Multi file list
|
||||
if (strpos($file_details, "* Locations:\n + ") === 0)
|
||||
{
|
||||
$file_details = substr($file_details, strlen("* Locations:\n + "));
|
||||
$files = explode("\n + ", $file_details);
|
||||
foreach ($files as $file)
|
||||
{
|
||||
if (!preg_match('#^([^ ]+)( \([0-9]+\))?$#', $file))
|
||||
{
|
||||
throw new \LogicException("Invalid event instances for file '{$file}' found for event '{$this->current_event}'", 1);
|
||||
}
|
||||
|
||||
list($file) = explode(" ", $file);
|
||||
|
||||
if (!file_exists($this->path . $file) || substr($file, -5) !== '.html')
|
||||
{
|
||||
throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 2);
|
||||
}
|
||||
|
||||
if (($this->filter !== 'adm') && strpos($file, 'styles/prosilver/template/') === 0)
|
||||
{
|
||||
$files_list['prosilver'][] = substr($file, strlen('styles/prosilver/template/'));
|
||||
}
|
||||
else if (($this->filter === 'adm') && strpos($file, 'adm/style/') === 0)
|
||||
{
|
||||
$files_list['adm'][] = substr($file, strlen('adm/style/'));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 3);
|
||||
}
|
||||
|
||||
$this->events_by_file[$file][] = $this->current_event;
|
||||
}
|
||||
}
|
||||
else if ($this->filter == 'adm')
|
||||
{
|
||||
$file = substr($file_details, strlen('* Location: '));
|
||||
if (!file_exists($this->path . $file) || substr($file, -5) !== '.html')
|
||||
{
|
||||
throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 1);
|
||||
}
|
||||
|
||||
$files_list['adm'][] = substr($file, strlen('adm/style/'));
|
||||
|
||||
$this->events_by_file[$file][] = $this->current_event;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \LogicException("Invalid file list found for event '{$this->current_event}'", 1);
|
||||
}
|
||||
|
||||
return $files_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all template events in a template file
|
||||
*
|
||||
* @param string $file
|
||||
* @return array
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function crawl_file_for_events($file)
|
||||
{
|
||||
if (!file_exists($this->path . $file))
|
||||
{
|
||||
throw new \LogicException("File '{$file}' does not exist", 1);
|
||||
}
|
||||
|
||||
$event_list = array();
|
||||
$file_content = file_get_contents($this->path . $file);
|
||||
|
||||
preg_match_all('/(?:{%|<!--) EVENT (.*) (?:%}|-->)/U', $file_content, $event_list);
|
||||
|
||||
return $event_list[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether all events from $file are in the md file and vice-versa
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $events
|
||||
* @return true
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function validate_events_from_file($file, array $events)
|
||||
{
|
||||
if (empty($this->events_by_file[$file]) && empty($events))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (empty($this->events_by_file[$file]))
|
||||
{
|
||||
$event_list = implode("', '", $events);
|
||||
throw new \LogicException("File '{$file}' should not contain events, but contains: "
|
||||
. "'{$event_list}'", 1);
|
||||
}
|
||||
else if (empty($events))
|
||||
{
|
||||
$event_list = implode("', '", $this->events_by_file[$file]);
|
||||
throw new \LogicException("File '{$file}' contains no events, but should contain: "
|
||||
. "'{$event_list}'", 1);
|
||||
}
|
||||
|
||||
$missing_events_from_file = array();
|
||||
foreach ($this->events_by_file[$file] as $event)
|
||||
{
|
||||
if (!in_array($event, $events))
|
||||
{
|
||||
$missing_events_from_file[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($missing_events_from_file))
|
||||
{
|
||||
$event_list = implode("', '", $missing_events_from_file);
|
||||
throw new \LogicException("File '{$file}' does not contain events: '{$event_list}'", 2);
|
||||
}
|
||||
|
||||
$missing_events_from_md = array();
|
||||
foreach ($events as $event)
|
||||
{
|
||||
if (!in_array($event, $this->events_by_file[$file]))
|
||||
{
|
||||
$missing_events_from_md[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($missing_events_from_md))
|
||||
{
|
||||
$event_list = implode("', '", $missing_events_from_md);
|
||||
throw new \LogicException("File '{$file}' contains additional events: '{$event_list}'", 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of files in $dir
|
||||
*
|
||||
* Works recursive with any depth
|
||||
*
|
||||
* @param string $dir Directory to go through
|
||||
* @return array List of files (including directories)
|
||||
*/
|
||||
public function get_recursive_file_list($dir)
|
||||
{
|
||||
try
|
||||
{
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \phpbb\recursive_dot_prefix_filter_iterator(
|
||||
new \RecursiveDirectoryIterator(
|
||||
$dir,
|
||||
\FilesystemIterator::SKIP_DOTS
|
||||
)
|
||||
),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$files = array();
|
||||
foreach ($iterator as $file_info)
|
||||
{
|
||||
/** @var \RecursiveDirectoryIterator $file_info */
|
||||
if ($file_info->isDir())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$relative_path = $iterator->getInnerIterator()->getSubPathname();
|
||||
|
||||
if (substr($relative_path, -5) == '.html')
|
||||
{
|
||||
$files[] = str_replace(DIRECTORY_SEPARATOR, '/', $relative_path);
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
}
|
||||
71
install/update/new/phpbb/extension/extension_interface.php
Normal file
71
install/update/new/phpbb/extension/extension_interface.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\extension;
|
||||
|
||||
/**
|
||||
* The interface extension meta classes have to implement to run custom code
|
||||
* on enable/disable/purge.
|
||||
*/
|
||||
interface extension_interface
|
||||
{
|
||||
/**
|
||||
* Indicate whether or not the extension can be enabled.
|
||||
*
|
||||
* @return bool|array True if extension is enableable, array of reasons
|
||||
* if not, false for generic reason.
|
||||
*/
|
||||
public function is_enableable();
|
||||
|
||||
/**
|
||||
* enable_step is executed on enabling an extension until it returns false.
|
||||
*
|
||||
* Calls to this function can be made in subsequent requests, when the
|
||||
* function is invoked through a webserver with a too low max_execution_time.
|
||||
*
|
||||
* @param mixed $old_state The return value of the previous call
|
||||
* of this method, or false on the first call
|
||||
* @return mixed Returns false after last step, otherwise
|
||||
* temporary state which is passed as an
|
||||
* argument to the next step
|
||||
*/
|
||||
public function enable_step($old_state);
|
||||
|
||||
/**
|
||||
* Disables the extension.
|
||||
*
|
||||
* Calls to this function can be made in subsequent requests, when the
|
||||
* function is invoked through a webserver with a too low max_execution_time.
|
||||
*
|
||||
* @param mixed $old_state The return value of the previous call
|
||||
* of this method, or false on the first call
|
||||
* @return mixed Returns false after last step, otherwise
|
||||
* temporary state which is passed as an
|
||||
* argument to the next step
|
||||
*/
|
||||
public function disable_step($old_state);
|
||||
|
||||
/**
|
||||
* purge_step is executed on purging an extension until it returns false.
|
||||
*
|
||||
* Calls to this function can be made in subsequent requests, when the
|
||||
* function is invoked through a webserver with a too low max_execution_time.
|
||||
*
|
||||
* @param mixed $old_state The return value of the previous call
|
||||
* of this method, or false on the first call
|
||||
* @return mixed Returns false after last step, otherwise
|
||||
* temporary state which is passed as an
|
||||
* argument to the next step
|
||||
*/
|
||||
public function purge_step($old_state);
|
||||
}
|
||||
591
install/update/new/phpbb/extension/manager.php
Normal file
591
install/update/new/phpbb/extension/manager.php
Normal file
@@ -0,0 +1,591 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\extension;
|
||||
|
||||
use phpbb\exception\runtime_exception;
|
||||
use phpbb\file_downloader;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* The extension manager provides means to activate/deactivate extensions.
|
||||
*/
|
||||
class manager
|
||||
{
|
||||
/** @var ContainerInterface */
|
||||
protected $container;
|
||||
|
||||
protected $db;
|
||||
protected $config;
|
||||
protected $cache;
|
||||
protected $php_ext;
|
||||
protected $extensions;
|
||||
protected $extension_table;
|
||||
protected $phpbb_root_path;
|
||||
protected $cache_name;
|
||||
|
||||
/**
|
||||
* Creates a manager and loads information from database
|
||||
*
|
||||
* @param ContainerInterface $container A container
|
||||
* @param \phpbb\db\driver\driver_interface $db A database connection
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem
|
||||
* @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, defaults to php
|
||||
* @param \phpbb\cache\service $cache A cache instance or null
|
||||
* @param string $cache_name The name of the cache variable, defaults to _ext
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, \phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\filesystem\filesystem_interface $filesystem, $extension_table, $phpbb_root_path, $php_ext = 'php', \phpbb\cache\service $cache = null, $cache_name = '_ext')
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->cache_name = $cache_name;
|
||||
$this->config = $config;
|
||||
$this->container = $container;
|
||||
$this->db = $db;
|
||||
$this->extension_table = $extension_table;
|
||||
$this->filesystem = $filesystem;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
$this->extensions = ($this->cache) ? $this->cache->get($this->cache_name) : false;
|
||||
|
||||
if ($this->extensions === false)
|
||||
{
|
||||
$this->load_extensions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all extension information from the database
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function load_extensions()
|
||||
{
|
||||
$this->extensions = array();
|
||||
|
||||
// Do not try to load any extensions if the extension table
|
||||
// does not exist or when installing or updating.
|
||||
// Note: database updater invokes this code, and in 3.0
|
||||
// there is no extension table therefore the rest of this function
|
||||
// fails
|
||||
if (defined('IN_INSTALL') || version_compare($this->config['version'], '3.1.0-dev', '<'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = 'SELECT *
|
||||
FROM ' . $this->extension_table;
|
||||
|
||||
$result = $this->db->sql_query($sql);
|
||||
$extensions = $this->db->sql_fetchrowset($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
foreach ($extensions as $extension)
|
||||
{
|
||||
$extension['ext_path'] = $this->get_extension_path($extension['ext_name']);
|
||||
$this->extensions[$extension['ext_name']] = $extension;
|
||||
}
|
||||
|
||||
ksort($this->extensions);
|
||||
|
||||
if ($this->cache)
|
||||
{
|
||||
$this->cache->put($this->cache_name, $this->extensions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the path to an extension
|
||||
*
|
||||
* @param string $name The name of the extension
|
||||
* @param bool $phpbb_relative Whether the path should be relative to phpbb root
|
||||
* @return string Path to an extension
|
||||
*/
|
||||
public function get_extension_path($name, $phpbb_relative = false)
|
||||
{
|
||||
$name = str_replace('.', '', $name);
|
||||
|
||||
return (($phpbb_relative) ? $this->phpbb_root_path : '') . 'ext/' . $name . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates the extension meta class for the extension with the given name
|
||||
*
|
||||
* @param string $name The extension name
|
||||
* @return \phpbb\extension\extension_interface Instance of the extension meta class or
|
||||
* \phpbb\extension\base if the class does not exist
|
||||
*/
|
||||
public function get_extension($name)
|
||||
{
|
||||
$extension_class_name = str_replace('/', '\\', $name) . '\\ext';
|
||||
|
||||
$migrator = $this->container->get('migrator');
|
||||
|
||||
if (class_exists($extension_class_name))
|
||||
{
|
||||
return new $extension_class_name($this->container, $this->get_finder(), $migrator, $name, $this->get_extension_path($name, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new \phpbb\extension\base($this->container, $this->get_finder(), $migrator, $name, $this->get_extension_path($name, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates the metadata manager for the extension with the given name
|
||||
*
|
||||
* @param string $name The extension name
|
||||
* @return \phpbb\extension\metadata_manager Instance of the metadata manager
|
||||
*/
|
||||
public function create_extension_metadata_manager($name)
|
||||
{
|
||||
if (!isset($this->extensions[$name]['metadata']))
|
||||
{
|
||||
$metadata = new \phpbb\extension\metadata_manager($name, $this->get_extension_path($name, true));
|
||||
$this->extensions[$name]['metadata'] = $metadata;
|
||||
}
|
||||
return $this->extensions[$name]['metadata'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the database entry for an extension
|
||||
*
|
||||
* @param string $name Extension name to update
|
||||
* @param array $data Data to update in the database
|
||||
* @param string $action Action to perform, by default 'update', may be also 'insert' or 'delete'
|
||||
*/
|
||||
protected function update_state($name, $data, $action = 'update')
|
||||
{
|
||||
switch ($action)
|
||||
{
|
||||
case 'insert':
|
||||
$this->extensions[$name] = $data;
|
||||
$this->extensions[$name]['ext_path'] = $this->get_extension_path($name);
|
||||
ksort($this->extensions);
|
||||
$sql = 'INSERT INTO ' . $this->extension_table . ' ' . $this->db->sql_build_array('INSERT', $data);
|
||||
$this->db->sql_query($sql);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
$this->extensions[$name] = array_merge($this->extensions[$name], $data);
|
||||
$sql = 'UPDATE ' . $this->extension_table . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $data) . "
|
||||
WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
unset($this->extensions[$name]);
|
||||
$sql = 'DELETE FROM ' . $this->extension_table . "
|
||||
WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->cache)
|
||||
{
|
||||
$this->cache->purge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a step of the extension enabling process.
|
||||
*
|
||||
* Allows the exentension to enable in a long running script that works
|
||||
* in multiple steps across requests. State is kept for the extension
|
||||
* in the extensions table.
|
||||
*
|
||||
* @param string $name The extension's name
|
||||
* @return bool False if enabling is finished, true otherwise
|
||||
*/
|
||||
public function enable_step($name)
|
||||
{
|
||||
// ignore extensions that are already enabled
|
||||
if ($this->is_enabled($name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false;
|
||||
|
||||
$extension = $this->get_extension($name);
|
||||
|
||||
if (!$extension->is_enableable())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$state = $extension->enable_step($old_state);
|
||||
|
||||
$active = ($state === false);
|
||||
|
||||
$extension_data = array(
|
||||
'ext_name' => $name,
|
||||
'ext_active' => $active,
|
||||
'ext_state' => serialize($state),
|
||||
);
|
||||
|
||||
$this->update_state($name, $extension_data, $this->is_configured($name) ? 'update' : 'insert');
|
||||
|
||||
if ($active)
|
||||
{
|
||||
$this->config->increment('assets_version', 1);
|
||||
}
|
||||
|
||||
return !$active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables an extension
|
||||
*
|
||||
* This method completely enables an extension. But it could be long running
|
||||
* so never call this in a script that has a max_execution time.
|
||||
*
|
||||
* @param string $name The extension's name
|
||||
* @return null
|
||||
*/
|
||||
public function enable($name)
|
||||
{
|
||||
// @codingStandardsIgnoreStart
|
||||
while ($this->enable_step($name));
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables an extension
|
||||
*
|
||||
* Calls the disable method on the extension's meta class to allow it to
|
||||
* process the event.
|
||||
*
|
||||
* @param string $name The extension's name
|
||||
* @return bool False if disabling is finished, true otherwise
|
||||
*/
|
||||
public function disable_step($name)
|
||||
{
|
||||
// ignore extensions that are not enabled
|
||||
if (!$this->is_enabled($name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$old_state = unserialize($this->extensions[$name]['ext_state']);
|
||||
|
||||
$extension = $this->get_extension($name);
|
||||
$state = $extension->disable_step($old_state);
|
||||
$active = ($state !== false);
|
||||
|
||||
$extension_data = array(
|
||||
'ext_active' => $active,
|
||||
'ext_state' => serialize($state),
|
||||
);
|
||||
$this->update_state($name, $extension_data);
|
||||
|
||||
return $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables an extension
|
||||
*
|
||||
* Disables an extension completely at once. This process could run for a
|
||||
* while so never call this in a script that has a max_execution time.
|
||||
*
|
||||
* @param string $name The extension's name
|
||||
* @return null
|
||||
*/
|
||||
public function disable($name)
|
||||
{
|
||||
// @codingStandardsIgnoreStart
|
||||
while ($this->disable_step($name));
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge an extension
|
||||
*
|
||||
* Disables the extension first if active, and then calls purge on the
|
||||
* extension's meta class to delete the extension's database content.
|
||||
*
|
||||
* @param string $name The extension's name
|
||||
* @return bool False if purging is finished, true otherwise
|
||||
*/
|
||||
public function purge_step($name)
|
||||
{
|
||||
// ignore extensions that are not configured
|
||||
if (!$this->is_configured($name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// disable first if necessary
|
||||
if ($this->extensions[$name]['ext_active'])
|
||||
{
|
||||
$this->disable($name);
|
||||
}
|
||||
|
||||
$old_state = unserialize($this->extensions[$name]['ext_state']);
|
||||
|
||||
$extension = $this->get_extension($name);
|
||||
$state = $extension->purge_step($old_state);
|
||||
$purged = ($state === false);
|
||||
|
||||
$extension_data = array(
|
||||
'ext_state' => serialize($state),
|
||||
);
|
||||
|
||||
$this->update_state($name, $extension_data, $purged ? 'delete' : 'update');
|
||||
|
||||
// continue until the state is false
|
||||
return !$purged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge an extension
|
||||
*
|
||||
* Purges an extension completely at once. This process could run for a while
|
||||
* so never call this in a script that has a max_execution time.
|
||||
*
|
||||
* @param string $name The extension's name
|
||||
* @return null
|
||||
*/
|
||||
public function purge($name)
|
||||
{
|
||||
// @codingStandardsIgnoreStart
|
||||
while ($this->purge_step($name));
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of all available extensions on the filesystem
|
||||
*
|
||||
* @return array An array with extension names as keys and paths to the
|
||||
* extension as values
|
||||
*/
|
||||
public function all_available()
|
||||
{
|
||||
$available = array();
|
||||
if (!is_dir($this->phpbb_root_path . 'ext/'))
|
||||
{
|
||||
return $available;
|
||||
}
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \phpbb\recursive_dot_prefix_filter_iterator(
|
||||
new \RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/', \FilesystemIterator::NEW_CURRENT_AND_KEY | \FilesystemIterator::FOLLOW_SYMLINKS)
|
||||
),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
$iterator->setMaxDepth(2);
|
||||
|
||||
foreach ($iterator as $file_info)
|
||||
{
|
||||
if ($file_info->isFile() && $file_info->getFilename() == 'composer.json')
|
||||
{
|
||||
$ext_name = $iterator->getInnerIterator()->getSubPath();
|
||||
$ext_name = str_replace(DIRECTORY_SEPARATOR, '/', $ext_name);
|
||||
if ($this->is_available($ext_name))
|
||||
{
|
||||
$available[$ext_name] = $this->get_extension_path($ext_name, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($available);
|
||||
return $available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all configured extensions.
|
||||
*
|
||||
* All enabled and disabled extensions are considered configured. A purged
|
||||
* extension that is no longer in the database is not configured.
|
||||
*
|
||||
* @param bool $phpbb_relative Whether the path should be relative to phpbb root
|
||||
*
|
||||
* @return array An array with extension names as keys and and the
|
||||
* database stored extension information as values
|
||||
*/
|
||||
public function all_configured($phpbb_relative = true)
|
||||
{
|
||||
$configured = array();
|
||||
foreach ($this->extensions as $name => $data)
|
||||
{
|
||||
if ($this->is_configured($name))
|
||||
{
|
||||
unset($data['metadata']);
|
||||
$data['ext_path'] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
|
||||
$configured[$name] = $data;
|
||||
}
|
||||
}
|
||||
return $configured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all enabled extensions.
|
||||
* @param bool $phpbb_relative Whether the path should be relative to phpbb root
|
||||
*
|
||||
* @return array An array with extension names as keys and and the
|
||||
* database stored extension information as values
|
||||
*/
|
||||
public function all_enabled($phpbb_relative = true)
|
||||
{
|
||||
$enabled = array();
|
||||
foreach ($this->extensions as $name => $data)
|
||||
{
|
||||
if ($this->is_enabled($name))
|
||||
{
|
||||
$enabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
|
||||
}
|
||||
}
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all disabled extensions.
|
||||
*
|
||||
* @param bool $phpbb_relative Whether the path should be relative to phpbb root
|
||||
*
|
||||
* @return array An array with extension names as keys and and the
|
||||
* database stored extension information as values
|
||||
*/
|
||||
public function all_disabled($phpbb_relative = true)
|
||||
{
|
||||
$disabled = array();
|
||||
foreach ($this->extensions as $name => $data)
|
||||
{
|
||||
if ($this->is_disabled($name))
|
||||
{
|
||||
$disabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
|
||||
}
|
||||
}
|
||||
return $disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given extension is available on the filesystem
|
||||
*
|
||||
* @param string $name Extension name to check NOTE: Can be user input
|
||||
* @return bool Depending on whether or not the extension is available
|
||||
*/
|
||||
public function is_available($name)
|
||||
{
|
||||
$md_manager = $this->create_extension_metadata_manager($name);
|
||||
try
|
||||
{
|
||||
return $md_manager->get_metadata('all') && $md_manager->validate_enable();
|
||||
}
|
||||
catch (\phpbb\extension\exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given extension is enabled
|
||||
*
|
||||
* @param string $name Extension name to check
|
||||
* @return bool Depending on whether or not the extension is enabled
|
||||
*/
|
||||
public function is_enabled($name)
|
||||
{
|
||||
return isset($this->extensions[$name]['ext_active']) && $this->extensions[$name]['ext_active'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given extension is disabled
|
||||
*
|
||||
* @param string $name Extension name to check
|
||||
* @return bool Depending on whether or not the extension is disabled
|
||||
*/
|
||||
public function is_disabled($name)
|
||||
{
|
||||
return isset($this->extensions[$name]['ext_active']) && !$this->extensions[$name]['ext_active'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given extension is configured
|
||||
*
|
||||
* All enabled and disabled extensions are considered configured. A purged
|
||||
* extension that is no longer in the database is not configured.
|
||||
*
|
||||
* @param string $name Extension name to check
|
||||
* @return bool Depending on whether or not the extension is configured
|
||||
*/
|
||||
public function is_configured($name)
|
||||
{
|
||||
return isset($this->extensions[$name]['ext_active']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the version and return the available updates (for an extension).
|
||||
*
|
||||
* @param \phpbb\extension\metadata_manager $md_manager The metadata manager for the version to check.
|
||||
* @param bool $force_update Ignores cached data. Defaults to false.
|
||||
* @param bool $force_cache Force the use of the cache. Override $force_update.
|
||||
* @param string $stability Force the stability (null by default).
|
||||
* @return array
|
||||
* @throws runtime_exception
|
||||
*/
|
||||
public function version_check(\phpbb\extension\metadata_manager $md_manager, $force_update = false, $force_cache = false, $stability = null)
|
||||
{
|
||||
$meta = $md_manager->get_metadata('all');
|
||||
|
||||
if (!isset($meta['extra']['version-check']))
|
||||
{
|
||||
throw new runtime_exception('NO_VERSIONCHECK');
|
||||
}
|
||||
|
||||
$version_check = $meta['extra']['version-check'];
|
||||
|
||||
$version_helper = new \phpbb\version_helper($this->cache, $this->config, new file_downloader());
|
||||
$version_helper->set_current_version($meta['version']);
|
||||
$version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename'], isset($version_check['ssl']) ? $version_check['ssl'] : false);
|
||||
$version_helper->force_stability($stability);
|
||||
|
||||
return $version_helper->get_ext_update_on_branch($force_update, $force_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given extension is purged
|
||||
*
|
||||
* An extension is purged if it is available, not enabled and not disabled.
|
||||
*
|
||||
* @param string $name Extension name to check
|
||||
* @return bool Depending on whether or not the extension is purged
|
||||
*/
|
||||
public function is_purged($name)
|
||||
{
|
||||
return $this->is_available($name) && !$this->is_configured($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a \phpbb\finder.
|
||||
*
|
||||
* @param bool $use_all_available Should we load all extensions, or just enabled ones
|
||||
* @return \phpbb\finder An extension finder instance
|
||||
*/
|
||||
public function get_finder($use_all_available = false)
|
||||
{
|
||||
$finder = new \phpbb\finder($this->filesystem, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');
|
||||
if ($use_all_available)
|
||||
{
|
||||
$finder->set_extensions(array_keys($this->all_available()));
|
||||
}
|
||||
else
|
||||
{
|
||||
$finder->set_extensions(array_keys($this->all_enabled()));
|
||||
}
|
||||
return $finder;
|
||||
}
|
||||
}
|
||||
410
install/update/new/phpbb/feed/controller/feed.php
Normal file
410
install/update/new/phpbb/feed/controller/feed.php
Normal file
@@ -0,0 +1,410 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\feed\controller;
|
||||
|
||||
use phpbb\auth\auth;
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use \phpbb\event\dispatcher_interface;
|
||||
use phpbb\exception\http_exception;
|
||||
use phpbb\feed\feed_interface;
|
||||
use phpbb\feed\exception\feed_unavailable_exception;
|
||||
use phpbb\feed\exception\unauthorized_exception;
|
||||
use phpbb\feed\helper as feed_helper;
|
||||
use phpbb\controller\helper as controller_helper;
|
||||
use phpbb\symfony_request;
|
||||
use phpbb\user;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class feed
|
||||
{
|
||||
/**
|
||||
* @var \Twig_Environment
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* @var symfony_request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var controller_helper
|
||||
*/
|
||||
protected $controller_helper;
|
||||
|
||||
/**
|
||||
* @var config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var feed_helper
|
||||
*/
|
||||
protected $feed_helper;
|
||||
|
||||
/**
|
||||
* @var user
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var auth
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* @var dispatcher_interface
|
||||
*/
|
||||
protected $phpbb_dispatcher;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Twig_Environment $twig
|
||||
* @param symfony_request $request
|
||||
* @param controller_helper $controller_helper
|
||||
* @param config $config
|
||||
* @param driver_interface $db
|
||||
* @param ContainerInterface $container
|
||||
* @param feed_helper $feed_helper
|
||||
* @param user $user
|
||||
* @param auth $auth
|
||||
* @param dispatcher_interface $phpbb_dispatcher
|
||||
* @param string $php_ext
|
||||
*/
|
||||
public function __construct(\Twig_Environment $twig, symfony_request $request, controller_helper $controller_helper, config $config, driver_interface $db, ContainerInterface $container, feed_helper $feed_helper, user $user, auth $auth, dispatcher_interface $phpbb_dispatcher, $php_ext)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->controller_helper = $controller_helper;
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->container = $container;
|
||||
$this->feed_helper = $feed_helper;
|
||||
$this->user = $user;
|
||||
$this->auth = $auth;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->template = $twig;
|
||||
$this->phpbb_dispatcher = $phpbb_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/forums route
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function forums()
|
||||
{
|
||||
if (!$this->config['feed_overall_forums'])
|
||||
{
|
||||
$this->send_unavailable();
|
||||
}
|
||||
|
||||
return $this->send_feed($this->container->get('feed.forums'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/news route
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function news()
|
||||
{
|
||||
// Get at least one news forum
|
||||
$sql = 'SELECT forum_id
|
||||
FROM ' . FORUMS_TABLE . '
|
||||
WHERE ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0');
|
||||
$result = $this->db->sql_query_limit($sql, 1, 0, 600);
|
||||
$s_feed_news = (int) $this->db->sql_fetchfield('forum_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$s_feed_news)
|
||||
{
|
||||
$this->send_unavailable();
|
||||
}
|
||||
|
||||
return $this->send_feed($this->container->get('feed.news'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/topics route
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function topics()
|
||||
{
|
||||
if (!$this->config['feed_topics_new'])
|
||||
{
|
||||
$this->send_unavailable();
|
||||
}
|
||||
|
||||
return $this->send_feed($this->container->get('feed.topics'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/topics_new route
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function topics_new()
|
||||
{
|
||||
return $this->topics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/topics_active route
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function topics_active()
|
||||
{
|
||||
if (!$this->config['feed_topics_active'])
|
||||
{
|
||||
$this->send_unavailable();
|
||||
}
|
||||
|
||||
return $this->send_feed($this->container->get('feed.topics_active'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/forum/{forum_id} route
|
||||
*
|
||||
* @param int $forum_id
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function forum($forum_id)
|
||||
{
|
||||
if (!$this->config['feed_forum'])
|
||||
{
|
||||
$this->send_unavailable();
|
||||
}
|
||||
|
||||
return $this->send_feed($this->container->get('feed.forum')->set_forum_id($forum_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/topic/{topic_id} route
|
||||
*
|
||||
* @param int $topic_id
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function topic($topic_id)
|
||||
{
|
||||
if (!$this->config['feed_topic'])
|
||||
{
|
||||
$this->send_unavailable();
|
||||
}
|
||||
|
||||
return $this->send_feed($this->container->get('feed.topic')->set_topic_id($topic_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for /feed/{mode] route
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws http_exception when the feed is disabled
|
||||
*/
|
||||
public function overall()
|
||||
{
|
||||
if (!$this->config['feed_overall'])
|
||||
{
|
||||
$this->send_unavailable();
|
||||
}
|
||||
|
||||
return $this->send_feed($this->container->get('feed.overall'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a given feed
|
||||
*
|
||||
* @param feed_interface $feed
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function send_feed(feed_interface $feed)
|
||||
{
|
||||
try
|
||||
{
|
||||
return $this->send_feed_do($feed);
|
||||
}
|
||||
catch (feed_unavailable_exception $e)
|
||||
{
|
||||
throw new http_exception(Response::HTTP_NOT_FOUND, $e->getMessage(), $e->get_parameters(), $e);
|
||||
}
|
||||
catch (unauthorized_exception $e)
|
||||
{
|
||||
throw new http_exception(Response::HTTP_FORBIDDEN, $e->getMessage(), $e->get_parameters(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Really send the feed
|
||||
*
|
||||
* @param feed_interface $feed
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throw exception\feed_exception
|
||||
*/
|
||||
protected function send_feed_do(feed_interface $feed)
|
||||
{
|
||||
$feed_updated_time = 0;
|
||||
$item_vars = array();
|
||||
|
||||
$board_url = $this->feed_helper->get_board_url();
|
||||
|
||||
// Open Feed
|
||||
$feed->open();
|
||||
|
||||
// Iterate through items
|
||||
while ($row = $feed->get_item())
|
||||
{
|
||||
/**
|
||||
* Event to modify the feed row
|
||||
*
|
||||
* @event core.feed_modify_feed_row
|
||||
* @var feed_interface feed Feed instance
|
||||
* @var array row Array with feed data
|
||||
*
|
||||
* @since 3.1.10-RC1
|
||||
* @changed 3.3.0 Replace forum_id, mode, topic_id with feed instance
|
||||
*/
|
||||
$vars = array('feed', 'row');
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.feed_modify_feed_row', compact($vars)));
|
||||
|
||||
// BBCode options to correctly disable urls, smilies, bbcode...
|
||||
if ($feed->get('options') === null)
|
||||
{
|
||||
// Allow all combinations
|
||||
$options = 7;
|
||||
|
||||
if ($feed->get('enable_bbcode') !== null && $feed->get('enable_smilies') !== null && $feed->get('enable_magic_url') !== null)
|
||||
{
|
||||
$options = (($row[$feed->get('enable_bbcode')]) ? OPTION_FLAG_BBCODE : 0) + (($row[$feed->get('enable_smilies')]) ? OPTION_FLAG_SMILIES : 0) + (($row[$feed->get('enable_magic_url')]) ? OPTION_FLAG_LINKS : 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$options = $row[$feed->get('options')];
|
||||
}
|
||||
|
||||
$title = (isset($row[$feed->get('title')]) && $row[$feed->get('title')] !== '') ? $row[$feed->get('title')] : ((isset($row[$feed->get('title2')])) ? $row[$feed->get('title2')] : '');
|
||||
|
||||
$published = ($feed->get('published') !== null) ? (int) $row[$feed->get('published')] : 0;
|
||||
$updated = ($feed->get('updated') !== null) ? (int) $row[$feed->get('updated')] : 0;
|
||||
|
||||
$display_attachments = ($this->auth->acl_get('u_download') && $this->auth->acl_get('f_download', $row['forum_id']) && isset($row['post_attachment']) && $row['post_attachment']) ? true : false;
|
||||
|
||||
$item_row = array(
|
||||
'author' => ($feed->get('creator') !== null) ? $row[$feed->get('creator')] : '',
|
||||
'published' => ($published > 0) ? $this->feed_helper->format_date($published) : '',
|
||||
'updated' => ($updated > 0) ? $this->feed_helper->format_date($updated) : '',
|
||||
'link' => '',
|
||||
'title' => censor_text($title),
|
||||
'category' => ($this->config['feed_item_statistics'] && !empty($row['forum_id'])) ? $board_url . '/viewforum.' . $this->php_ext . '?f=' . $row['forum_id'] : '',
|
||||
'category_name' => ($this->config['feed_item_statistics'] && isset($row['forum_name'])) ? $row['forum_name'] : '',
|
||||
'description' => censor_text($this->feed_helper->generate_content($row[$feed->get('text')], $row[$feed->get('bbcode_uid')], $row[$feed->get('bitfield')], $options, $row['forum_id'], ($display_attachments ? $feed->get_attachments($row['post_id']) : array()))),
|
||||
'statistics' => '',
|
||||
);
|
||||
|
||||
// Adjust items, fill link, etc.
|
||||
$feed->adjust_item($item_row, $row);
|
||||
|
||||
$item_vars[] = $item_row;
|
||||
|
||||
$feed_updated_time = max($feed_updated_time, $published, $updated);
|
||||
}
|
||||
|
||||
// If we do not have any items at all, sending the current time is better than sending no time.
|
||||
if (!$feed_updated_time)
|
||||
{
|
||||
$feed_updated_time = time();
|
||||
}
|
||||
|
||||
$feed->close();
|
||||
|
||||
$content = $this->template->render('feed.xml.twig', array(
|
||||
// Some default assignments
|
||||
// FEED_IMAGE is not used (atom)
|
||||
'FEED_IMAGE' => '',
|
||||
'SELF_LINK' => $this->controller_helper->route($this->request->attributes->get('_route'), $this->request->attributes->get('_route_params'), true, '', UrlGeneratorInterface::ABSOLUTE_URL),
|
||||
'FEED_LINK' => $board_url . '/index.' . $this->php_ext,
|
||||
'FEED_TITLE' => $this->config['sitename'],
|
||||
'FEED_SUBTITLE' => $this->config['site_desc'],
|
||||
'FEED_UPDATED' => $this->feed_helper->format_date($feed_updated_time),
|
||||
'FEED_LANG' => $this->user->lang['USER_LANG'],
|
||||
'FEED_AUTHOR' => $this->config['sitename'],
|
||||
|
||||
// Feed entries
|
||||
'FEED_ROWS' => $item_vars,
|
||||
));
|
||||
|
||||
$response = new Response($content);
|
||||
$response->headers->set('Content-Type', 'application/atom+xml');
|
||||
$response->setCharset('UTF-8');
|
||||
$response->setLastModified(new \DateTime('@' . $feed_updated_time));
|
||||
|
||||
if (!empty($this->user->data['is_bot']))
|
||||
{
|
||||
// Let reverse proxies know we detected a bot.
|
||||
$response->headers->set('X-PHPBB-IS-BOT', 'yes');
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw and exception saying that the feed isn't available
|
||||
*
|
||||
* @throw http_exception
|
||||
*/
|
||||
protected function send_unavailable()
|
||||
{
|
||||
throw new http_exception(404, 'FEATURE_NOT_AVAILABLE');
|
||||
}
|
||||
}
|
||||
147
install/update/new/phpbb/feed/topics_active.php
Normal file
147
install/update/new/phpbb/feed/topics_active.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\feed;
|
||||
|
||||
/**
|
||||
* Active Topics feed
|
||||
*
|
||||
* This will give you the last {$this->num_items} topics
|
||||
* with replies made within the last {$this->sort_days} days
|
||||
* including the last post.
|
||||
*/
|
||||
class topics_active extends topic_base
|
||||
{
|
||||
protected $sort_days = 7;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set_keys()
|
||||
{
|
||||
parent::set_keys();
|
||||
|
||||
$this->set('author_id', 'topic_last_poster_id');
|
||||
$this->set('creator', 'topic_last_poster_name');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function get_sql()
|
||||
{
|
||||
$forum_ids_read = $this->get_readable_forums();
|
||||
if (empty($forum_ids_read))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$in_fid_ary = array_intersect($forum_ids_read, $this->get_forum_ids());
|
||||
$in_fid_ary = array_diff($in_fid_ary, $this->get_passworded_forums());
|
||||
if (empty($in_fid_ary))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search for topics in last X days
|
||||
$last_post_time_sql = ($this->sort_days) ? ' AND topic_last_post_time > ' . (time() - ($this->sort_days * 24 * 3600)) : '';
|
||||
|
||||
// We really have to get the post ids first!
|
||||
$sql = 'SELECT topic_last_post_id, topic_last_post_time
|
||||
FROM ' . TOPICS_TABLE . '
|
||||
WHERE topic_moved_id = 0
|
||||
AND ' . $this->content_visibility->get_forums_visibility_sql('topic', $in_fid_ary) . '
|
||||
' . $last_post_time_sql . '
|
||||
ORDER BY topic_last_post_time DESC, topic_last_post_id DESC';
|
||||
$result = $this->db->sql_query_limit($sql, $this->num_items);
|
||||
|
||||
$post_ids = array();
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$post_ids[] = (int) $row['topic_last_post_id'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (empty($post_ids))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parent::fetch_attachments($post_ids);
|
||||
|
||||
$this->sql = array(
|
||||
'SELECT' => 'f.forum_id, f.forum_name,
|
||||
t.topic_id, t.topic_title, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views,
|
||||
t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_post_time,
|
||||
p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, t.topic_visibility',
|
||||
'FROM' => array(
|
||||
TOPICS_TABLE => 't',
|
||||
POSTS_TABLE => 'p',
|
||||
),
|
||||
'LEFT_JOIN' => array(
|
||||
array(
|
||||
'FROM' => array(FORUMS_TABLE => 'f'),
|
||||
'ON' => 'p.forum_id = f.forum_id',
|
||||
),
|
||||
),
|
||||
'WHERE' => 'p.topic_id = t.topic_id
|
||||
AND ' . $this->db->sql_in_set('p.post_id', $post_ids),
|
||||
'ORDER_BY' => 'p.post_time DESC, p.post_id DESC',
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ids of the forums not excluded from the active list
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function get_forum_ids()
|
||||
{
|
||||
static $forum_ids;
|
||||
|
||||
$cache_name = 'feed_topic_active_forum_ids';
|
||||
|
||||
if (!isset($forum_ids) && ($forum_ids = $this->cache->get('_' . $cache_name)) === false)
|
||||
{
|
||||
$sql = 'SELECT forum_id
|
||||
FROM ' . FORUMS_TABLE . '
|
||||
WHERE forum_type = ' . FORUM_POST . '
|
||||
AND ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '= 0') . '
|
||||
AND ' . $this->db->sql_bit_and('forum_flags', round(log(FORUM_FLAG_ACTIVE_TOPICS, 2)), '<> 0');
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$forum_ids = array();
|
||||
while ($forum_id = (int) $this->db->sql_fetchfield('forum_id'))
|
||||
{
|
||||
$forum_ids[$forum_id] = $forum_id;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$this->cache->put('_' . $cache_name, $forum_ids, 180);
|
||||
}
|
||||
|
||||
return $forum_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function adjust_item(&$item_row, &$row)
|
||||
{
|
||||
parent::adjust_item($item_row, $row);
|
||||
|
||||
$item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title'];
|
||||
}
|
||||
}
|
||||
584
install/update/new/phpbb/files/filespec.php
Normal file
584
install/update/new/phpbb/files/filespec.php
Normal file
@@ -0,0 +1,584 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\files;
|
||||
|
||||
use phpbb\language\language;
|
||||
|
||||
/**
|
||||
* Responsible for holding all file relevant information, as well as doing file-specific operations.
|
||||
* The {@link fileupload fileupload class} can be used to upload several files, each of them being this object to operate further on.
|
||||
*/
|
||||
class filespec
|
||||
{
|
||||
/** @var string File name */
|
||||
protected $filename = '';
|
||||
|
||||
/** @var string Real name of file */
|
||||
protected $realname = '';
|
||||
|
||||
/** @var string Upload name of file */
|
||||
protected $uploadname = '';
|
||||
|
||||
/** @var string Mimetype of file */
|
||||
protected $mimetype = '';
|
||||
|
||||
/** @var string File extension */
|
||||
protected $extension = '';
|
||||
|
||||
/** @var int File size */
|
||||
protected $filesize = 0;
|
||||
|
||||
/** @var int Width of file */
|
||||
protected $width = 0;
|
||||
|
||||
/** @var int Height of file */
|
||||
protected $height = 0;
|
||||
|
||||
/** @var array Image info including type and size */
|
||||
protected $image_info = array();
|
||||
|
||||
/** @var string Destination file name */
|
||||
protected $destination_file = '';
|
||||
|
||||
/** @var string Destination file path */
|
||||
protected $destination_path = '';
|
||||
|
||||
/** @var bool Whether file was moved */
|
||||
protected $file_moved = false;
|
||||
|
||||
/** @var bool Whether file is local */
|
||||
protected $local = false;
|
||||
|
||||
/** @var bool Class initialization flag */
|
||||
protected $class_initialized = false;
|
||||
|
||||
/** @var array Error array */
|
||||
public $error = array();
|
||||
|
||||
/** @var upload Instance of upload class */
|
||||
public $upload;
|
||||
|
||||
/** @var \phpbb\filesystem\filesystem_interface */
|
||||
protected $filesystem;
|
||||
|
||||
/** @var \bantu\IniGetWrapper\IniGetWrapper ini_get() wrapper class */
|
||||
protected $php_ini;
|
||||
|
||||
/** @var \FastImageSize\FastImageSize */
|
||||
protected $imagesize;
|
||||
|
||||
/** @var language Language class */
|
||||
protected $language;
|
||||
|
||||
/** @var string phpBB root path */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var \phpbb\plupload\plupload The plupload object */
|
||||
protected $plupload;
|
||||
|
||||
/** @var \phpbb\mimetype\guesser phpBB Mimetype guesser */
|
||||
protected $mimetype_guesser;
|
||||
|
||||
/**
|
||||
* File upload class
|
||||
*
|
||||
* @param \phpbb\filesystem\filesystem_interface $phpbb_filesystem Filesystem
|
||||
* @param language $language Language
|
||||
* @param \bantu\IniGetWrapper\IniGetWrapper $php_ini ini_get() wrapper
|
||||
* @param \FastImageSize\FastImageSize $imagesize Imagesize class
|
||||
* @param string $phpbb_root_path phpBB root path
|
||||
* @param \phpbb\mimetype\guesser $mimetype_guesser Mime type guesser
|
||||
* @param \phpbb\plupload\plupload $plupload Plupload
|
||||
*/
|
||||
public function __construct(\phpbb\filesystem\filesystem_interface $phpbb_filesystem, language $language, \bantu\IniGetWrapper\IniGetWrapper $php_ini, \FastImageSize\FastImageSize $imagesize, $phpbb_root_path, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null)
|
||||
{
|
||||
$this->filesystem = $phpbb_filesystem;
|
||||
$this->language = $language;
|
||||
$this->php_ini = $php_ini;
|
||||
$this->imagesize = $imagesize;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->plupload = $plupload;
|
||||
$this->mimetype_guesser = $mimetype_guesser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set upload ary
|
||||
*
|
||||
* @param array $upload_ary Upload ary
|
||||
*
|
||||
* @return filespec This instance of the filespec class
|
||||
*/
|
||||
public function set_upload_ary($upload_ary)
|
||||
{
|
||||
if (!isset($upload_ary) || !count($upload_ary))
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->class_initialized = true;
|
||||
$this->filename = $upload_ary['tmp_name'];
|
||||
$this->filesize = $upload_ary['size'];
|
||||
$name = $upload_ary['name'];
|
||||
$name = trim(utf8_basename($name));
|
||||
$this->realname = $this->uploadname = $name;
|
||||
$this->mimetype = $upload_ary['type'];
|
||||
|
||||
// Opera adds the name to the mime type
|
||||
$this->mimetype = (strpos($this->mimetype, '; name') !== false) ? str_replace(strstr($this->mimetype, '; name'), '', $this->mimetype) : $this->mimetype;
|
||||
|
||||
if (!$this->mimetype)
|
||||
{
|
||||
$this->mimetype = 'application/octet-stream';
|
||||
}
|
||||
|
||||
$this->extension = strtolower(self::get_extension($this->realname));
|
||||
|
||||
// Try to get real filesize from temporary folder (not always working) ;)
|
||||
$this->filesize = ($this->get_filesize($this->filename)) ?: $this->filesize;
|
||||
|
||||
$this->width = $this->height = 0;
|
||||
$this->file_moved = false;
|
||||
|
||||
$this->local = (isset($upload_ary['local_mode'])) ? true : false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upload namespace
|
||||
*
|
||||
* @param upload $namespace Instance of upload class
|
||||
*
|
||||
* @return filespec This instance of the filespec class
|
||||
*/
|
||||
public function set_upload_namespace($namespace)
|
||||
{
|
||||
$this->upload = $namespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if class members were not properly initialised yet
|
||||
*
|
||||
* @return bool True if there was an init error, false if not
|
||||
*/
|
||||
public function init_error()
|
||||
{
|
||||
return !$this->class_initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set error in error array
|
||||
*
|
||||
* @param mixed $error Content for error array
|
||||
*
|
||||
* @return \phpbb\files\filespec This instance of the filespec class
|
||||
*/
|
||||
public function set_error($error)
|
||||
{
|
||||
$this->error[] = $error;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans destination filename
|
||||
*
|
||||
* @param string $mode Either real, unique, or unique_ext. Real creates a
|
||||
* realname, filtering some characters, lowering every
|
||||
* character. Unique creates a unique filename.
|
||||
* @param string $prefix Prefix applied to filename
|
||||
* @param string $user_id The user_id is only needed for when cleaning a user's avatar
|
||||
*/
|
||||
public function clean_filename($mode = 'unique', $prefix = '', $user_id = '')
|
||||
{
|
||||
if ($this->init_error())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($mode)
|
||||
{
|
||||
case 'real':
|
||||
// Remove every extension from filename (to not let the mime bug being exposed)
|
||||
if (strpos($this->realname, '.') !== false)
|
||||
{
|
||||
$this->realname = substr($this->realname, 0, strpos($this->realname, '.'));
|
||||
}
|
||||
|
||||
// Replace any chars which may cause us problems with _
|
||||
$bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|');
|
||||
|
||||
$this->realname = rawurlencode(str_replace($bad_chars, '_', strtolower($this->realname)));
|
||||
$this->realname = preg_replace("/%(\w{2})/", '_', $this->realname);
|
||||
|
||||
$this->realname = $prefix . $this->realname . '.' . $this->extension;
|
||||
break;
|
||||
|
||||
case 'unique':
|
||||
$this->realname = $prefix . md5(unique_id());
|
||||
break;
|
||||
|
||||
case 'avatar':
|
||||
$this->extension = strtolower($this->extension);
|
||||
$this->realname = $prefix . $user_id . '.' . $this->extension;
|
||||
|
||||
break;
|
||||
|
||||
case 'unique_ext':
|
||||
default:
|
||||
$this->realname = $prefix . md5(unique_id()) . '.' . $this->extension;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get property from file object
|
||||
*
|
||||
* @param string $property Name of property
|
||||
*
|
||||
* @return mixed Content of property
|
||||
*/
|
||||
public function get($property)
|
||||
{
|
||||
if ($this->init_error() || !isset($this->$property))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->$property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file is an image (mime type)
|
||||
*
|
||||
* @return bool true if it is an image, false if not
|
||||
*/
|
||||
public function is_image()
|
||||
{
|
||||
return (strpos($this->mimetype, 'image/') === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file got correctly uploaded
|
||||
*
|
||||
* @return bool true if it is a valid upload, false if not
|
||||
*/
|
||||
public function is_uploaded()
|
||||
{
|
||||
$is_plupload = $this->plupload && $this->plupload->is_active();
|
||||
|
||||
if (!$this->local && !$is_plupload && !is_uploaded_file($this->filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (($this->local || $is_plupload) && !file_exists($this->filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove file
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
if ($this->file_moved)
|
||||
{
|
||||
@unlink($this->destination_file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file extension
|
||||
*
|
||||
* @param string $filename Filename that needs to be checked
|
||||
*
|
||||
* @return string Extension of the supplied filename
|
||||
*/
|
||||
static public function get_extension($filename)
|
||||
{
|
||||
$filename = utf8_basename($filename);
|
||||
|
||||
if (strpos($filename, '.') === false)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$filename = explode('.', $filename);
|
||||
return array_pop($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mime type
|
||||
*
|
||||
* @param string $filename Filename that needs to be checked
|
||||
* @return string Mime type of supplied filename
|
||||
*/
|
||||
public function get_mimetype($filename)
|
||||
{
|
||||
if ($this->mimetype_guesser !== null)
|
||||
{
|
||||
$mimetype = $this->mimetype_guesser->guess($filename, $this->uploadname);
|
||||
|
||||
if ($mimetype !== 'application/octet-stream')
|
||||
{
|
||||
$this->mimetype = $mimetype;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->mimetype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file size
|
||||
*
|
||||
* @param string $filename File name of file to check
|
||||
*
|
||||
* @return int File size
|
||||
*/
|
||||
public function get_filesize($filename)
|
||||
{
|
||||
return @filesize($filename);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check the first 256 bytes for forbidden content
|
||||
*
|
||||
* @param array $disallowed_content Array containg disallowed content
|
||||
*
|
||||
* @return bool False if disallowed content found, true if not
|
||||
*/
|
||||
public function check_content($disallowed_content)
|
||||
{
|
||||
if (empty($disallowed_content))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$fp = @fopen($this->filename, 'rb');
|
||||
|
||||
if ($fp !== false)
|
||||
{
|
||||
$ie_mime_relevant = fread($fp, 256);
|
||||
fclose($fp);
|
||||
foreach ($disallowed_content as $forbidden)
|
||||
{
|
||||
if (stripos($ie_mime_relevant, '<' . $forbidden) !== false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move file to destination folder
|
||||
* The phpbb_root_path variable will be applied to the destination path
|
||||
*
|
||||
* @param string $destination Destination path, for example $config['avatar_path']
|
||||
* @param bool $overwrite If set to true, an already existing file will be overwritten
|
||||
* @param bool $skip_image_check If set to true, the check for the file to be a valid image is skipped
|
||||
* @param string|bool $chmod Permission mask for chmodding the file after a successful move.
|
||||
* The mode entered here reflects the mode defined by {@link phpbb_chmod()}
|
||||
*
|
||||
* @return bool True if file was moved, false if not
|
||||
* @access public
|
||||
*/
|
||||
public function move_file($destination, $overwrite = false, $skip_image_check = false, $chmod = false)
|
||||
{
|
||||
if (count($this->error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$chmod = ($chmod === false) ? CHMOD_READ | CHMOD_WRITE : $chmod;
|
||||
|
||||
// We need to trust the admin in specifying valid upload directories and an attacker not being able to overwrite it...
|
||||
$this->destination_path = $this->phpbb_root_path . $destination;
|
||||
|
||||
// Check if the destination path exist...
|
||||
if (!file_exists($this->destination_path))
|
||||
{
|
||||
@unlink($this->filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
$upload_mode = ($this->php_ini->getBool('open_basedir')) ? 'move' : 'copy';
|
||||
$upload_mode = ($this->local) ? 'local' : $upload_mode;
|
||||
$this->destination_file = $this->destination_path . '/' . utf8_basename($this->realname);
|
||||
|
||||
// Check if the file already exist, else there is something wrong...
|
||||
if (file_exists($this->destination_file) && !$overwrite)
|
||||
{
|
||||
@unlink($this->filename);
|
||||
$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
|
||||
$this->file_moved = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file_exists($this->destination_file))
|
||||
{
|
||||
@unlink($this->destination_file);
|
||||
}
|
||||
|
||||
switch ($upload_mode)
|
||||
{
|
||||
case 'copy':
|
||||
|
||||
if (!@copy($this->filename, $this->destination_file))
|
||||
{
|
||||
if (!@move_uploaded_file($this->filename, $this->destination_file))
|
||||
{
|
||||
$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'move':
|
||||
|
||||
if (!@move_uploaded_file($this->filename, $this->destination_file))
|
||||
{
|
||||
if (!@copy($this->filename, $this->destination_file))
|
||||
{
|
||||
$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'local':
|
||||
|
||||
if (!@copy($this->filename, $this->destination_file))
|
||||
{
|
||||
$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove temporary filename
|
||||
@unlink($this->filename);
|
||||
|
||||
if (count($this->error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->filesystem->phpbb_chmod($this->destination_file, $chmod);
|
||||
}
|
||||
catch (\phpbb\filesystem\exception\filesystem_exception $e)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get real filesize from destination folder
|
||||
$this->filesize = ($this->get_filesize($this->destination_file)) ?: $this->filesize;
|
||||
|
||||
// Get mimetype of supplied file
|
||||
$this->mimetype = $this->get_mimetype($this->destination_file);
|
||||
|
||||
if ($this->is_image() && !$skip_image_check)
|
||||
{
|
||||
$this->width = $this->height = 0;
|
||||
|
||||
$this->image_info = $this->imagesize->getImageSize($this->destination_file, $this->mimetype);
|
||||
|
||||
if ($this->image_info !== false)
|
||||
{
|
||||
$this->width = $this->image_info['width'];
|
||||
$this->height = $this->image_info['height'];
|
||||
|
||||
// Check image type
|
||||
$types = upload::image_types();
|
||||
|
||||
if (!isset($types[$this->image_info['type']]) || !in_array($this->extension, $types[$this->image_info['type']]))
|
||||
{
|
||||
if (!isset($types[$this->image_info['type']]))
|
||||
{
|
||||
$this->error[] = $this->language->lang('IMAGE_FILETYPE_INVALID', $this->image_info['type'], $this->mimetype);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->error[] = $this->language->lang('IMAGE_FILETYPE_MISMATCH', $types[$this->image_info['type']][0], $this->extension);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the dimensions match a valid image
|
||||
if (empty($this->width) || empty($this->height))
|
||||
{
|
||||
$this->error[] = $this->language->lang('ATTACHED_IMAGE_NOT_IMAGE');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->error[] = $this->language->lang('UNABLE_GET_IMAGE_SIZE');
|
||||
}
|
||||
}
|
||||
|
||||
$this->file_moved = true;
|
||||
$this->additional_checks();
|
||||
unset($this->upload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performing additional checks
|
||||
*
|
||||
* @return bool False if issue was found, true if not
|
||||
*/
|
||||
public function additional_checks()
|
||||
{
|
||||
if (!$this->file_moved)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filesize is too big or it's 0 if it was larger than the maxsize in the upload form
|
||||
if ($this->upload->max_filesize && ($this->get('filesize') > $this->upload->max_filesize || $this->filesize == 0))
|
||||
{
|
||||
$max_filesize = get_formatted_filesize($this->upload->max_filesize, false);
|
||||
|
||||
$this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->upload->valid_dimensions($this))
|
||||
{
|
||||
$this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_SIZE',
|
||||
$this->language->lang('PIXELS', (int) $this->upload->min_width),
|
||||
$this->language->lang('PIXELS', (int) $this->upload->min_height),
|
||||
$this->language->lang('PIXELS', (int) $this->upload->max_width),
|
||||
$this->language->lang('PIXELS', (int) $this->upload->max_height),
|
||||
$this->language->lang('PIXELS', (int) $this->width),
|
||||
$this->language->lang('PIXELS', (int) $this->height));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
21
install/update/new/phpbb/filesystem.php
Normal file
21
install/update/new/phpbb/filesystem.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb;
|
||||
|
||||
/**
|
||||
* @deprecated 3.2.0-dev (To be removed 4.0.0) use \phpbb\filesystem\filesystem instead
|
||||
*/
|
||||
class filesystem extends \phpbb\filesystem\filesystem
|
||||
{
|
||||
}
|
||||
916
install/update/new/phpbb/filesystem/filesystem.php
Normal file
916
install/update/new/phpbb/filesystem/filesystem.php
Normal file
@@ -0,0 +1,916 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\filesystem;
|
||||
|
||||
use phpbb\filesystem\exception\filesystem_exception;
|
||||
|
||||
/**
|
||||
* A class with various functions that are related to paths, files and the filesystem
|
||||
*/
|
||||
class filesystem implements filesystem_interface
|
||||
{
|
||||
/**
|
||||
* Store some information about file ownership for phpBB's chmod function
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $chmod_info;
|
||||
|
||||
/**
|
||||
* Stores current working directory
|
||||
*
|
||||
* @var string|bool current working directory or false if it cannot be recovered
|
||||
*/
|
||||
protected $working_directory;
|
||||
|
||||
/**
|
||||
* Symfony's Filesystem component
|
||||
*
|
||||
* @var \Symfony\Component\Filesystem\Filesystem
|
||||
*/
|
||||
protected $symfony_filesystem;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->chmod_info = array();
|
||||
$this->symfony_filesystem = new \Symfony\Component\Filesystem\Filesystem();
|
||||
$this->working_directory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function chgrp($files, $group, $recursive = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->chgrp($files, $group, $recursive);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
// Try to recover filename
|
||||
// By the time this is written that is at the end of the message
|
||||
$error = trim($e->getMessage());
|
||||
$file = substr($error, strrpos($error, ' '));
|
||||
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
|
||||
{
|
||||
if (is_null($perms))
|
||||
{
|
||||
// Default to read permission for compatibility reasons
|
||||
$perms = self::CHMOD_READ;
|
||||
}
|
||||
|
||||
// Check if we got a permission flag
|
||||
if ($perms > self::CHMOD_ALL)
|
||||
{
|
||||
$file_perm = $perms;
|
||||
|
||||
// Extract permissions
|
||||
//$owner = ($file_perm >> 6) & 7; // This will be ignored
|
||||
$group = ($file_perm >> 3) & 7;
|
||||
$other = ($file_perm >> 0) & 7;
|
||||
|
||||
// Does any permissions provided? if so we add execute bit for directories
|
||||
$group = ($group !== 0) ? ($group | self::CHMOD_EXECUTE) : $group;
|
||||
$other = ($other !== 0) ? ($other | self::CHMOD_EXECUTE) : $other;
|
||||
|
||||
// Compute directory permissions
|
||||
$dir_perm = (self::CHMOD_ALL << 6) + ($group << 3) + ($other << 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add execute bit to owner if execute bit is among perms
|
||||
$owner_perm = (self::CHMOD_READ | self::CHMOD_WRITE) | ($perms & self::CHMOD_EXECUTE);
|
||||
$file_perm = ($owner_perm << 6) + ($perms << 3) + ($perms << 0);
|
||||
|
||||
// Compute directory permissions
|
||||
$perm = ($perms !== 0) ? ($perms | self::CHMOD_EXECUTE) : $perms;
|
||||
$dir_perm = (($owner_perm | self::CHMOD_EXECUTE) << 6) + ($perm << 3) + ($perm << 0);
|
||||
}
|
||||
|
||||
// Symfony's filesystem component does not support extra execution flags on directories
|
||||
// so we need to implement it again
|
||||
foreach ($this->to_iterator($files) as $file)
|
||||
{
|
||||
if ($recursive && is_dir($file) && !is_link($file))
|
||||
{
|
||||
$this->chmod(new \FilesystemIterator($file), $perms, true);
|
||||
}
|
||||
|
||||
// Don't chmod links as mostly those require 0777 and that cannot be changed
|
||||
if (is_dir($file) || (is_link($file) && $force_chmod_link))
|
||||
{
|
||||
if (true !== @chmod($file, $dir_perm))
|
||||
{
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_PERMISSIONS', $file, array());
|
||||
}
|
||||
}
|
||||
else if (is_file($file))
|
||||
{
|
||||
if (true !== @chmod($file, $file_perm))
|
||||
{
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_PERMISSIONS', $file, array());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function chown($files, $user, $recursive = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->chown($files, $user, $recursive);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
// Try to recover filename
|
||||
// By the time this is written that is at the end of the message
|
||||
$error = trim($e->getMessage());
|
||||
$file = substr($error, strrpos($error, ' '));
|
||||
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clean_path($path)
|
||||
{
|
||||
$exploded = explode('/', $path);
|
||||
$filtered = array();
|
||||
foreach ($exploded as $part)
|
||||
{
|
||||
if ($part === '.' && !empty($filtered))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($part === '..' && !empty($filtered) && $filtered[count($filtered) - 1] !== '.' && $filtered[count($filtered) - 1] !== '..')
|
||||
{
|
||||
array_pop($filtered);
|
||||
}
|
||||
else
|
||||
{
|
||||
$filtered[] = $part;
|
||||
}
|
||||
}
|
||||
$path = implode('/', $filtered);
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function copy($origin_file, $target_file, $override = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->copy($origin_file, $target_file, $override);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_COPY_FILES', '', array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump_file($filename, $content)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->dumpFile($filename, $content);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_DUMP_FILE', $filename, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists($files)
|
||||
{
|
||||
return $this->symfony_filesystem->exists($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_absolute_path($path)
|
||||
{
|
||||
return (isset($path[0]) && $path[0] === '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_readable($files, $recursive = false)
|
||||
{
|
||||
foreach ($this->to_iterator($files) as $file)
|
||||
{
|
||||
if ($recursive && is_dir($file) && !is_link($file))
|
||||
{
|
||||
if (!$this->is_readable(new \FilesystemIterator($file), true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_readable($file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_writable($files, $recursive = false)
|
||||
{
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('is_writable'))
|
||||
{
|
||||
foreach ($this->to_iterator($files) as $file)
|
||||
{
|
||||
if ($recursive && is_dir($file) && !is_link($file))
|
||||
{
|
||||
if (!$this->is_writable(new \FilesystemIterator($file), true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->phpbb_is_writable($file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use built in is_writable
|
||||
foreach ($this->to_iterator($files) as $file)
|
||||
{
|
||||
if ($recursive && is_dir($file) && !is_link($file))
|
||||
{
|
||||
if (!$this->is_writable(new \FilesystemIterator($file), true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_writable($file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function make_path_relative($end_path, $start_path)
|
||||
{
|
||||
return $this->symfony_filesystem->makePathRelative($end_path, $start_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mirror($origin_dir, $target_dir, \Traversable $iterator = null, $options = array())
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->mirror($origin_dir, $target_dir, $iterator, $options);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
$msg = $e->getMessage();
|
||||
$filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
|
||||
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_MIRROR_DIRECTORY', $filename, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mkdir($dirs, $mode = 0777)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->mkdir($dirs, $mode);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
$msg = $e->getMessage();
|
||||
$filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
|
||||
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_CREATE_DIRECTORY', $filename, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function phpbb_chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
|
||||
{
|
||||
if (is_null($perms))
|
||||
{
|
||||
// Default to read permission for compatibility reasons
|
||||
$perms = self::CHMOD_READ;
|
||||
}
|
||||
|
||||
if (empty($this->chmod_info))
|
||||
{
|
||||
if (!function_exists('fileowner') || !function_exists('filegroup'))
|
||||
{
|
||||
$this->chmod_info['process'] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$common_php_owner = @fileowner(__FILE__);
|
||||
$common_php_group = @filegroup(__FILE__);
|
||||
|
||||
// And the owner and the groups PHP is running under.
|
||||
$php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false;
|
||||
$php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
|
||||
|
||||
// If we are unable to get owner/group, then do not try to set them by guessing
|
||||
if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group)
|
||||
{
|
||||
$this->chmod_info['process'] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->chmod_info = array(
|
||||
'process' => true,
|
||||
'common_owner' => $common_php_owner,
|
||||
'common_group' => $common_php_group,
|
||||
'php_uid' => $php_uid,
|
||||
'php_gids' => $php_gids,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->chmod_info['process'])
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach ($this->to_iterator($files) as $file)
|
||||
{
|
||||
$file_uid = @fileowner($file);
|
||||
$file_gid = @filegroup($file);
|
||||
|
||||
// Change owner
|
||||
if ($file_uid !== $this->chmod_info['common_owner'])
|
||||
{
|
||||
$this->chown($file, $this->chmod_info['common_owner'], $recursive);
|
||||
}
|
||||
|
||||
// Change group
|
||||
if ($file_gid !== $this->chmod_info['common_group'])
|
||||
{
|
||||
$this->chgrp($file, $this->chmod_info['common_group'], $recursive);
|
||||
}
|
||||
|
||||
clearstatcache();
|
||||
$file_uid = @fileowner($file);
|
||||
$file_gid = @filegroup($file);
|
||||
}
|
||||
}
|
||||
catch (filesystem_exception $e)
|
||||
{
|
||||
$this->chmod_info['process'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Still able to process?
|
||||
if ($this->chmod_info['process'])
|
||||
{
|
||||
if ($file_uid === $this->chmod_info['php_uid'])
|
||||
{
|
||||
$php = 'owner';
|
||||
}
|
||||
else if (in_array($file_gid, $this->chmod_info['php_gids']))
|
||||
{
|
||||
$php = 'group';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Since we are setting the everyone bit anyway, no need to do expensive operations
|
||||
$this->chmod_info['process'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// We are not able to determine or change something
|
||||
if (!$this->chmod_info['process'])
|
||||
{
|
||||
$php = 'other';
|
||||
}
|
||||
|
||||
switch ($php)
|
||||
{
|
||||
case 'owner':
|
||||
try
|
||||
{
|
||||
$this->chmod($files, $perms, $recursive, $force_chmod_link);
|
||||
clearstatcache();
|
||||
if ($this->is_readable($files) && $this->is_writable($files))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (filesystem_exception $e)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
case 'group':
|
||||
try
|
||||
{
|
||||
$this->chmod($files, $perms, $recursive, $force_chmod_link);
|
||||
clearstatcache();
|
||||
if ((!($perms & self::CHMOD_READ) || $this->is_readable($files, $recursive)) && (!($perms & self::CHMOD_WRITE) || $this->is_writable($files, $recursive)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (filesystem_exception $e)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
case 'other':
|
||||
default:
|
||||
$this->chmod($files, $perms, $recursive, $force_chmod_link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function realpath($path)
|
||||
{
|
||||
if (!function_exists('realpath'))
|
||||
{
|
||||
return $this->phpbb_own_realpath($path);
|
||||
}
|
||||
|
||||
$realpath = realpath($path);
|
||||
|
||||
// Strangely there are provider not disabling realpath but returning strange values. :o
|
||||
// We at least try to cope with them.
|
||||
if ((!$this->is_absolute_path($path) && $realpath === $path) || $realpath === false)
|
||||
{
|
||||
return $this->phpbb_own_realpath($path);
|
||||
}
|
||||
|
||||
// Check for DIRECTORY_SEPARATOR at the end (and remove it!)
|
||||
if (substr($realpath, -1) === DIRECTORY_SEPARATOR)
|
||||
{
|
||||
$realpath = substr($realpath, 0, -1);
|
||||
}
|
||||
|
||||
return $realpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($files)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->remove($files);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
// Try to recover filename
|
||||
// By the time this is written that is at the end of the message
|
||||
$error = trim($e->getMessage());
|
||||
$file = substr($error, strrpos($error, ' '));
|
||||
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_DELETE_FILES', $file, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rename($origin, $target, $overwrite = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->rename($origin, $target, $overwrite);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
$msg = $e->getMessage();
|
||||
$filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
|
||||
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_RENAME_FILE', $filename, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function symlink($origin_dir, $target_dir, $copy_on_windows = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->symlink($origin_dir, $target_dir, $copy_on_windows);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_CREATE_SYMLINK', $origin_dir, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function touch($files, $time = null, $access_time = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->symfony_filesystem->touch($files, $time, $access_time);
|
||||
}
|
||||
catch (\Symfony\Component\Filesystem\Exception\IOException $e)
|
||||
{
|
||||
// Try to recover filename
|
||||
// By the time this is written that is at the end of the message
|
||||
$error = trim($e->getMessage());
|
||||
$file = substr($error, strrpos($error, ' '));
|
||||
|
||||
throw new filesystem_exception('FILESYSTEM_CANNOT_TOUCH_FILES', $file, array(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* phpBB's implementation of is_writable
|
||||
*
|
||||
* @todo Investigate if is_writable is still buggy
|
||||
*
|
||||
* @param string $file file/directory to check if writable
|
||||
*
|
||||
* @return bool true if the given path is writable
|
||||
*/
|
||||
protected function phpbb_is_writable($file)
|
||||
{
|
||||
if (file_exists($file))
|
||||
{
|
||||
// Canonicalise path to absolute path
|
||||
$file = $this->realpath($file);
|
||||
|
||||
if (is_dir($file))
|
||||
{
|
||||
// Test directory by creating a file inside the directory
|
||||
$result = @tempnam($file, 'i_w');
|
||||
|
||||
if (is_string($result) && file_exists($result))
|
||||
{
|
||||
unlink($result);
|
||||
|
||||
// Ensure the file is actually in the directory (returned realpathed)
|
||||
return (strpos($result, $file) === 0) ? true : false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$handle = @fopen($file, 'c');
|
||||
|
||||
if (is_resource($handle))
|
||||
{
|
||||
fclose($handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// file does not exist test if we can write to the directory
|
||||
$dir = dirname($file);
|
||||
|
||||
if (file_exists($dir) && is_dir($dir) && $this->phpbb_is_writable($dir))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to resolve real path when PHP's realpath failes to do so
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function phpbb_own_realpath($path)
|
||||
{
|
||||
// Replace all directory separators with '/'
|
||||
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
|
||||
|
||||
$is_absolute_path = false;
|
||||
$path_prefix = '';
|
||||
|
||||
if ($this->is_absolute_path($path))
|
||||
{
|
||||
$is_absolute_path = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resolve working directory and store it
|
||||
if (is_null($this->working_directory))
|
||||
{
|
||||
if (function_exists('getcwd'))
|
||||
{
|
||||
$this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', getcwd());
|
||||
}
|
||||
|
||||
//
|
||||
// From this point on we really just guessing
|
||||
// If chdir were called we screwed
|
||||
//
|
||||
else if (function_exists('debug_backtrace'))
|
||||
{
|
||||
$call_stack = debug_backtrace(0);
|
||||
$this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', dirname($call_stack[count($call_stack) - 1]['file']));
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Assuming that the working directory is phpBB root
|
||||
// we could use this as a fallback, when phpBB will use controllers
|
||||
// everywhere this will be a safe assumption
|
||||
//
|
||||
//$dir_parts = explode(DIRECTORY_SEPARATOR, __DIR__);
|
||||
//$namespace_parts = explode('\\', trim(__NAMESPACE__, '\\'));
|
||||
|
||||
//$namespace_part_count = count($namespace_parts);
|
||||
|
||||
// Check if we still loading from root
|
||||
//if (array_slice($dir_parts, -$namespace_part_count) === $namespace_parts)
|
||||
//{
|
||||
// $this->working_directory = implode('/', array_slice($dir_parts, 0, -$namespace_part_count));
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// $this->working_directory = false;
|
||||
//}
|
||||
|
||||
$this->working_directory = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->working_directory !== false)
|
||||
{
|
||||
$is_absolute_path = true;
|
||||
$path = $this->working_directory . '/' . $path;
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_absolute_path)
|
||||
{
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR'))
|
||||
{
|
||||
$path_prefix = $path[0] . ':';
|
||||
$path = substr($path, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
$path_prefix = '';
|
||||
}
|
||||
}
|
||||
|
||||
$resolved_path = $this->resolve_path($path, $path_prefix, $is_absolute_path);
|
||||
if ($resolved_path === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!@file_exists($resolved_path) || (!@is_dir($resolved_path . '/') && !is_file($resolved_path)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return OS specific directory separators
|
||||
$resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved_path);
|
||||
|
||||
// Check for DIRECTORY_SEPARATOR at the end (and remove it!)
|
||||
if (substr($resolved, -1) === DIRECTORY_SEPARATOR)
|
||||
{
|
||||
return substr($resolved, 0, -1);
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert file(s) to \Traversable object
|
||||
*
|
||||
* This is the same function as Symfony's toIterator, but that is private
|
||||
* so we cannot use it.
|
||||
*
|
||||
* @param string|array|\Traversable $files filename/list of filenames
|
||||
* @return \Traversable
|
||||
*/
|
||||
protected function to_iterator($files)
|
||||
{
|
||||
if (!$files instanceof \Traversable)
|
||||
{
|
||||
$files = new \ArrayObject(is_array($files) ? $files : array($files));
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to resolve symlinks in path
|
||||
*
|
||||
* @param string $path The path to resolve
|
||||
* @param string $prefix The path prefix (on windows the drive letter)
|
||||
* @param bool $absolute Whether or not the path is absolute
|
||||
* @param bool $return_array Whether or not to return path parts
|
||||
*
|
||||
* @return string|array|bool returns the resolved path or an array of parts of the path if $return_array is true
|
||||
* or false if path cannot be resolved
|
||||
*/
|
||||
protected function resolve_path($path, $prefix = '', $absolute = false, $return_array = false)
|
||||
{
|
||||
if ($return_array)
|
||||
{
|
||||
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
|
||||
}
|
||||
|
||||
trim ($path, '/');
|
||||
$path_parts = explode('/', $path);
|
||||
$resolved = array();
|
||||
$resolved_path = $prefix;
|
||||
$file_found = false;
|
||||
|
||||
foreach ($path_parts as $path_part)
|
||||
{
|
||||
if ($file_found)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($path_part) || ($path_part === '.' && ($absolute || !empty($resolved))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if ($absolute && $path_part === '..')
|
||||
{
|
||||
if (empty($resolved))
|
||||
{
|
||||
// No directories above root
|
||||
return false;
|
||||
}
|
||||
|
||||
array_pop($resolved);
|
||||
$resolved_path = false;
|
||||
}
|
||||
else if ($path_part === '..' && !empty($resolved) && !in_array($resolved[count($resolved) - 1], array('.', '..')))
|
||||
{
|
||||
array_pop($resolved);
|
||||
$resolved_path = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($resolved_path === false)
|
||||
{
|
||||
if (empty($resolved))
|
||||
{
|
||||
$resolved_path = ($absolute) ? $prefix . '/' . $path_part : $path_part;
|
||||
}
|
||||
else
|
||||
{
|
||||
$tmp_array = $resolved;
|
||||
if ($absolute)
|
||||
{
|
||||
array_unshift($tmp_array, $prefix);
|
||||
}
|
||||
|
||||
$resolved_path = implode('/', $tmp_array);
|
||||
}
|
||||
}
|
||||
|
||||
$current_path = $resolved_path . '/' . $path_part;
|
||||
|
||||
// Resolve symlinks
|
||||
if (@is_link($current_path))
|
||||
{
|
||||
if (!function_exists('readlink'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$link = readlink($current_path);
|
||||
|
||||
// Is link has an absolute path in it?
|
||||
if ($this->is_absolute_path($link))
|
||||
{
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR'))
|
||||
{
|
||||
$prefix = $link[0] . ':';
|
||||
$link = substr($link, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
$prefix = '';
|
||||
}
|
||||
|
||||
$resolved = $this->resolve_path($link, $prefix, true, true);
|
||||
$absolute = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$resolved = $this->resolve_path($resolved_path . '/' . $link, $prefix, $absolute, true);
|
||||
}
|
||||
|
||||
if (!$resolved)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$resolved_path = false;
|
||||
}
|
||||
else if (@is_dir($current_path . '/'))
|
||||
{
|
||||
$resolved[] = $path_part;
|
||||
$resolved_path = $current_path;
|
||||
}
|
||||
else if (@is_file($current_path))
|
||||
{
|
||||
$resolved[] = $path_part;
|
||||
$resolved_path = $current_path;
|
||||
$file_found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If at the end of the path there were a .. or .
|
||||
// we need to build the path again.
|
||||
// Only doing this when a string is expected in return
|
||||
if ($resolved_path === false && $return_array === false)
|
||||
{
|
||||
if (empty($resolved))
|
||||
{
|
||||
$resolved_path = ($absolute) ? $prefix . '/' : './';
|
||||
}
|
||||
else
|
||||
{
|
||||
$tmp_array = $resolved;
|
||||
if ($absolute)
|
||||
{
|
||||
array_unshift($tmp_array, $prefix);
|
||||
}
|
||||
|
||||
$resolved_path = implode('/', $tmp_array);
|
||||
}
|
||||
}
|
||||
|
||||
return ($return_array) ? $resolved : $resolved_path;
|
||||
}
|
||||
}
|
||||
284
install/update/new/phpbb/filesystem/filesystem_interface.php
Normal file
284
install/update/new/phpbb/filesystem/filesystem_interface.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\filesystem;
|
||||
|
||||
/**
|
||||
* Interface for phpBB's filesystem service
|
||||
*/
|
||||
interface filesystem_interface
|
||||
{
|
||||
/**
|
||||
* chmod all permissions flag
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CHMOD_ALL = 7;
|
||||
|
||||
/**
|
||||
* chmod read permissions flag
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CHMOD_READ = 4;
|
||||
|
||||
/**
|
||||
* chmod write permissions flag
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CHMOD_WRITE = 2;
|
||||
|
||||
/**
|
||||
* chmod execute permissions flag
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CHMOD_EXECUTE = 1;
|
||||
|
||||
/**
|
||||
* Change owner group of files/directories
|
||||
*
|
||||
* @param string|array|\Traversable $files The file(s)/directorie(s) to change group
|
||||
* @param string $group The group that should own the files/directories
|
||||
* @param bool $recursive If the group should be changed recursively
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
|
||||
* retrieved by filesystem_exception::get_filename()
|
||||
*/
|
||||
public function chgrp($files, $group, $recursive = false);
|
||||
|
||||
/**
|
||||
* Global function for chmodding directories and files for internal use
|
||||
*
|
||||
* The function accepts filesystem_interface::CHMOD_ flags in the permission argument
|
||||
* or the user can specify octal values (or any integer if it makes sense). All directories will have
|
||||
* an execution bit appended, if the user group (owner, group or other) has any bit specified.
|
||||
*
|
||||
* @param string|array|\Traversable $files The file/directory to be chmodded
|
||||
* @param int $perms Permissions to set
|
||||
* @param bool $recursive If the permissions should be changed recursively
|
||||
* @param bool $force_chmod_link Try to apply permissions to symlinks as well
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
|
||||
* retrieved by filesystem_exception::get_filename()
|
||||
*/
|
||||
public function chmod($files, $perms = null, $recursive = false, $force_chmod_link = false);
|
||||
|
||||
/**
|
||||
* Change owner group of files/directories
|
||||
*
|
||||
* @param string|array|\Traversable $files The file(s)/directorie(s) to change group
|
||||
* @param string $user The owner user name
|
||||
* @param bool $recursive Whether change the owner recursively or not
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
|
||||
* retrieved by filesystem_exception::get_filename()
|
||||
*/
|
||||
public function chown($files, $user, $recursive = false);
|
||||
|
||||
/**
|
||||
* Eliminates useless . and .. components from specified path.
|
||||
*
|
||||
* @param string $path Path to clean
|
||||
*
|
||||
* @return string Cleaned path
|
||||
*/
|
||||
public function clean_path($path);
|
||||
|
||||
/**
|
||||
* Copies a file.
|
||||
*
|
||||
* This method only copies the file if the origin file is newer than the target file.
|
||||
*
|
||||
* By default, if the target already exists, it is not overridden.
|
||||
*
|
||||
* @param string $origin_file The original filename
|
||||
* @param string $target_file The target filename
|
||||
* @param bool $override Whether to override an existing file or not
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception When the file cannot be copied
|
||||
*/
|
||||
public function copy($origin_file, $target_file, $override = false);
|
||||
|
||||
/**
|
||||
* Atomically dumps content into a file.
|
||||
*
|
||||
* @param string $filename The file to be written to.
|
||||
* @param string $content The data to write into the file.
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception When the file cannot be written
|
||||
*/
|
||||
public function dump_file($filename, $content);
|
||||
|
||||
/**
|
||||
* Checks the existence of files or directories.
|
||||
*
|
||||
* @param string|array|\Traversable $files files/directories to check
|
||||
*
|
||||
* @return bool Returns true if all files/directories exist, false otherwise
|
||||
*/
|
||||
public function exists($files);
|
||||
|
||||
/**
|
||||
* Checks if a path is absolute or not
|
||||
*
|
||||
* @param string $path Path to check
|
||||
*
|
||||
* @return bool true if the path is absolute, false otherwise
|
||||
*/
|
||||
public function is_absolute_path($path);
|
||||
|
||||
/**
|
||||
* Checks if files/directories are readable
|
||||
*
|
||||
* @param string|array|\Traversable $files files/directories to check
|
||||
* @param bool $recursive Whether or not directories should be checked recursively
|
||||
*
|
||||
* @return bool True when the files/directories are readable, otherwise false.
|
||||
*/
|
||||
public function is_readable($files, $recursive = false);
|
||||
|
||||
/**
|
||||
* Test if a file/directory is writable
|
||||
*
|
||||
* @param string|array|\Traversable $files files/directories to perform write test on
|
||||
* @param bool $recursive Whether or not directories should be checked recursively
|
||||
*
|
||||
* @return bool True when the files/directories are writable, otherwise false.
|
||||
*/
|
||||
public function is_writable($files, $recursive = false);
|
||||
|
||||
/**
|
||||
* Given an existing path, convert it to a path relative to a given starting path
|
||||
*
|
||||
* @param string $end_path Absolute path of target
|
||||
* @param string $start_path Absolute path where traversal begins
|
||||
*
|
||||
* @return string Path of target relative to starting path
|
||||
*/
|
||||
public function make_path_relative($end_path, $start_path);
|
||||
|
||||
/**
|
||||
* Mirrors a directory to another.
|
||||
*
|
||||
* @param string $origin_dir The origin directory
|
||||
* @param string $target_dir The target directory
|
||||
* @param \Traversable $iterator A Traversable instance
|
||||
* @param array $options An array of boolean options
|
||||
* Valid options are:
|
||||
* - $options['override'] Whether to override an existing file on copy or not (see copy())
|
||||
* - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
|
||||
* - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception When the file cannot be copied.
|
||||
* The filename which triggered the error can be
|
||||
* retrieved by filesystem_exception::get_filename()
|
||||
*/
|
||||
public function mirror($origin_dir, $target_dir, \Traversable $iterator = null, $options = array());
|
||||
|
||||
/**
|
||||
* Creates a directory recursively.
|
||||
*
|
||||
* @param string|array|\Traversable $dirs The directory path
|
||||
* @param int $mode The directory mode
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception On any directory creation failure
|
||||
* The filename which triggered the error can be
|
||||
* retrieved by filesystem_exception::get_filename()
|
||||
*/
|
||||
public function mkdir($dirs, $mode = 0777);
|
||||
|
||||
/**
|
||||
* Global function for chmodding directories and files for internal use
|
||||
*
|
||||
* This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions.
|
||||
* The function determines owner and group from common.php file and sets the same to the provided file.
|
||||
* The function uses bit fields to build the permissions.
|
||||
* The function sets the appropriate execute bit on directories.
|
||||
*
|
||||
* Supported constants representing bit fields are:
|
||||
*
|
||||
* filesystem_interface::CHMOD_ALL - all permissions (7)
|
||||
* filesystem_interface::CHMOD_READ - read permission (4)
|
||||
* filesystem_interface::CHMOD_WRITE - write permission (2)
|
||||
* filesystem_interface::CHMOD_EXECUTE - execute permission (1)
|
||||
*
|
||||
* NOTE: The function uses POSIX extension and fileowner()/filegroup() functions. If any of them is disabled, this function tries to build proper permissions, by calling is_readable() and is_writable() functions.
|
||||
*
|
||||
* @param string|array|\Traversable $file The file/directory to be chmodded
|
||||
* @param int $perms Permissions to set
|
||||
* @param bool $recursive If the permissions should be changed recursively
|
||||
* @param bool $force_chmod_link Try to apply permissions to symlinks as well
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
|
||||
* retrieved by filesystem_exception::get_filename()
|
||||
*/
|
||||
public function phpbb_chmod($file, $perms = null, $recursive = false, $force_chmod_link = false);
|
||||
|
||||
/**
|
||||
* A wrapper for PHP's realpath
|
||||
*
|
||||
* Try to resolve realpath when PHP's realpath is not available, or
|
||||
* known to be buggy.
|
||||
*
|
||||
* @param string $path Path to resolve
|
||||
*
|
||||
* @return string Resolved path
|
||||
*/
|
||||
public function realpath($path);
|
||||
|
||||
/**
|
||||
* Removes files or directories.
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception When removal fails.
|
||||
* The filename which triggered the error can be
|
||||
* retrieved by filesystem_exception::get_filename()
|
||||
*/
|
||||
public function remove($files);
|
||||
|
||||
/**
|
||||
* Renames a file or a directory.
|
||||
*
|
||||
* @param string $origin The origin filename or directory
|
||||
* @param string $target The new filename or directory
|
||||
* @param bool $overwrite Whether to overwrite the target if it already exists
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception When target file or directory already exists,
|
||||
* or origin cannot be renamed.
|
||||
*/
|
||||
public function rename($origin, $target, $overwrite = false);
|
||||
|
||||
/**
|
||||
* Creates a symbolic link or copy a directory.
|
||||
*
|
||||
* @param string $origin_dir The origin directory path
|
||||
* @param string $target_dir The symbolic link name
|
||||
* @param bool $copy_on_windows Whether to copy files if on Windows
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception When symlink fails
|
||||
*/
|
||||
public function symlink($origin_dir, $target_dir, $copy_on_windows = false);
|
||||
|
||||
/**
|
||||
* Sets access and modification time of file.
|
||||
*
|
||||
* @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
|
||||
* @param int $time The touch time as a Unix timestamp
|
||||
* @param int $access_time The access time as a Unix timestamp
|
||||
*
|
||||
* @throws \phpbb\filesystem\exception\filesystem_exception When touch fails
|
||||
*/
|
||||
public function touch($files, $time = null, $access_time = null);
|
||||
}
|
||||
547
install/update/new/phpbb/finder.php
Normal file
547
install/update/new/phpbb/finder.php
Normal file
@@ -0,0 +1,547 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb;
|
||||
|
||||
/**
|
||||
* The finder provides a simple way to locate files in the core and a set of extensions
|
||||
*/
|
||||
class finder
|
||||
{
|
||||
protected $extensions;
|
||||
protected $filesystem;
|
||||
protected $phpbb_root_path;
|
||||
protected $cache;
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* The cache variable name used to store $this->cached_queries in $this->cache.
|
||||
*
|
||||
* Allows the use of multiple differently configured finders with the same cache.
|
||||
* @var string
|
||||
*/
|
||||
protected $cache_name;
|
||||
|
||||
/**
|
||||
* An associative array, containing all search parameters set in methods.
|
||||
* @var array
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* A map from md5 hashes of serialized queries to their previously retrieved
|
||||
* results.
|
||||
* @var array
|
||||
*/
|
||||
protected $cached_queries;
|
||||
|
||||
/**
|
||||
* Creates a new finder instance with its dependencies
|
||||
*
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem instance
|
||||
* @param string $phpbb_root_path Path to the phpbb root directory
|
||||
* @param \phpbb\cache\service $cache A cache instance or null
|
||||
* @param string $php_ext php file extension
|
||||
* @param string $cache_name The name of the cache variable, defaults to
|
||||
* _ext_finder
|
||||
*/
|
||||
public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path = '', \phpbb\cache\service $cache = null, $php_ext = 'php', $cache_name = '_ext_finder')
|
||||
{
|
||||
$this->filesystem = $filesystem;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->cache = $cache;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->cache_name = $cache_name;
|
||||
|
||||
$this->query = array(
|
||||
'core_path' => false,
|
||||
'core_suffix' => false,
|
||||
'core_prefix' => false,
|
||||
'core_directory' => false,
|
||||
'extension_suffix' => false,
|
||||
'extension_prefix' => false,
|
||||
'extension_directory' => false,
|
||||
);
|
||||
$this->extensions = array();
|
||||
|
||||
$this->cached_queries = ($this->cache) ? $this->cache->get($this->cache_name) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the array of extensions
|
||||
*
|
||||
* @param array $extensions A list of extensions that should be searched as well
|
||||
* @param bool $replace_list Should the list be emptied before adding the extensions
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function set_extensions(array $extensions, $replace_list = true)
|
||||
{
|
||||
if ($replace_list)
|
||||
{
|
||||
$this->extensions = array();
|
||||
}
|
||||
|
||||
foreach ($extensions as $ext_name)
|
||||
{
|
||||
$this->extensions[$ext_name] = $this->phpbb_root_path . 'ext/' . $ext_name . '/';
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a core path to be searched in addition to extensions
|
||||
*
|
||||
* @param string $core_path The path relative to phpbb_root_path
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function core_path($core_path)
|
||||
{
|
||||
$this->query['core_path'] = $core_path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the suffix all files found in extensions and core must match.
|
||||
*
|
||||
* There is no default file extension, so to find PHP files only, you will
|
||||
* have to specify .php as a suffix. However when using get_classes, the .php
|
||||
* file extension is automatically added to suffixes.
|
||||
*
|
||||
* @param string $suffix A filename suffix
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function suffix($suffix)
|
||||
{
|
||||
$this->core_suffix($suffix);
|
||||
$this->extension_suffix($suffix);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a suffix all files found in extensions must match
|
||||
*
|
||||
* There is no default file extension, so to find PHP files only, you will
|
||||
* have to specify .php as a suffix. However when using get_classes, the .php
|
||||
* file extension is automatically added to suffixes.
|
||||
*
|
||||
* @param string $extension_suffix A filename suffix
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function extension_suffix($extension_suffix)
|
||||
{
|
||||
$this->query['extension_suffix'] = $extension_suffix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a suffix all files found in the core path must match
|
||||
*
|
||||
* There is no default file extension, so to find PHP files only, you will
|
||||
* have to specify .php as a suffix. However when using get_classes, the .php
|
||||
* file extension is automatically added to suffixes.
|
||||
*
|
||||
* @param string $core_suffix A filename suffix
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function core_suffix($core_suffix)
|
||||
{
|
||||
$this->query['core_suffix'] = $core_suffix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix all files found in extensions and core must match
|
||||
*
|
||||
* @param string $prefix A filename prefix
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function prefix($prefix)
|
||||
{
|
||||
$this->core_prefix($prefix);
|
||||
$this->extension_prefix($prefix);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a prefix all files found in extensions must match
|
||||
*
|
||||
* @param string $extension_prefix A filename prefix
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function extension_prefix($extension_prefix)
|
||||
{
|
||||
$this->query['extension_prefix'] = $extension_prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a prefix all files found in the core path must match
|
||||
*
|
||||
* @param string $core_prefix A filename prefix
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function core_prefix($core_prefix)
|
||||
{
|
||||
$this->query['core_prefix'] = $core_prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a directory all files found in extensions and core must be contained in
|
||||
*
|
||||
* Automatically sets the core_directory if its value does not differ from
|
||||
* the current directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function directory($directory)
|
||||
{
|
||||
$this->core_directory($directory);
|
||||
$this->extension_directory($directory);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a directory all files found in extensions must be contained in
|
||||
*
|
||||
* @param string $extension_directory
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function extension_directory($extension_directory)
|
||||
{
|
||||
$this->query['extension_directory'] = $this->sanitise_directory($extension_directory);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a directory all files found in the core path must be contained in
|
||||
*
|
||||
* @param string $core_directory
|
||||
* @return \phpbb\finder This object for chaining calls
|
||||
*/
|
||||
public function core_directory($core_directory)
|
||||
{
|
||||
$this->query['core_directory'] = $this->sanitise_directory($core_directory);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes occurrences of /./ and makes sure path ends without trailing slash
|
||||
*
|
||||
* @param string $directory A directory pattern
|
||||
* @return string A cleaned up directory pattern
|
||||
*/
|
||||
protected function sanitise_directory($directory)
|
||||
{
|
||||
$directory = $this->filesystem->clean_path($directory);
|
||||
$dir_len = strlen($directory);
|
||||
|
||||
if ($dir_len > 1 && $directory[$dir_len - 1] === '/')
|
||||
{
|
||||
$directory = substr($directory, 0, -1);
|
||||
}
|
||||
|
||||
return $directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds classes matching the configured options if they follow phpBB naming rules.
|
||||
*
|
||||
* The php file extension is automatically added to suffixes.
|
||||
*
|
||||
* Note: If a file is matched but contains a class name not following the
|
||||
* phpBB naming rules an incorrect class name will be returned.
|
||||
*
|
||||
* @param bool $cache Whether the result should be cached
|
||||
* @return array An array of found class names
|
||||
*/
|
||||
public function get_classes($cache = true)
|
||||
{
|
||||
$this->query['extension_suffix'] .= '.' . $this->php_ext;
|
||||
$this->query['core_suffix'] .= '.' . $this->php_ext;
|
||||
|
||||
$files = $this->find($cache, false);
|
||||
|
||||
return $this->get_classes_from_files($files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class names from a list of files
|
||||
*
|
||||
* @param array $files Array of files (from find())
|
||||
* @return array Array of class names
|
||||
*/
|
||||
public function get_classes_from_files($files)
|
||||
{
|
||||
$classes = array();
|
||||
foreach ($files as $file => $ext_name)
|
||||
{
|
||||
$class = substr($file, 0, -strlen('.' . $this->php_ext));
|
||||
if ($ext_name === '/' && preg_match('#^includes/#', $file))
|
||||
{
|
||||
$class = preg_replace('#^includes/#', '', $class);
|
||||
$classes[] = 'phpbb_' . str_replace('/', '_', $class);
|
||||
}
|
||||
else
|
||||
{
|
||||
$class = preg_replace('#^ext/#', '', $class);
|
||||
$classes[] = '\\' . str_replace('/', '\\', $class);
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all directories matching the configured options
|
||||
*
|
||||
* @param bool $cache Whether the result should be cached
|
||||
* @param bool $extension_keys Whether the result should have extension name as array key
|
||||
* @return array An array of paths to found directories
|
||||
*/
|
||||
public function get_directories($cache = true, $extension_keys = false)
|
||||
{
|
||||
return $this->find_with_root_path($cache, true, $extension_keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all files matching the configured options.
|
||||
*
|
||||
* @param bool $cache Whether the result should be cached
|
||||
* @return array An array of paths to found files
|
||||
*/
|
||||
public function get_files($cache = true)
|
||||
{
|
||||
return $this->find_with_root_path($cache, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around the general find which prepends a root path to results
|
||||
*
|
||||
* @param bool $cache Whether the result should be cached
|
||||
* @param bool $is_dir Directories will be returned when true, only files
|
||||
* otherwise
|
||||
* @param bool $extension_keys If true, result will be associative array
|
||||
* with extension name as key
|
||||
* @return array An array of paths to found items
|
||||
*/
|
||||
protected function find_with_root_path($cache = true, $is_dir = false, $extension_keys = false)
|
||||
{
|
||||
$items = $this->find($cache, $is_dir);
|
||||
|
||||
$result = array();
|
||||
foreach ($items as $item => $ext_name)
|
||||
{
|
||||
if ($extension_keys)
|
||||
{
|
||||
$result[$ext_name] = $this->phpbb_root_path . $item;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[] = $this->phpbb_root_path . $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all file system entries matching the configured options
|
||||
*
|
||||
* @param bool $cache Whether the result should be cached
|
||||
* @param bool $is_dir Directories will be returned when true, only files
|
||||
* otherwise
|
||||
* @return array An array of paths to found items
|
||||
*/
|
||||
public function find($cache = true, $is_dir = false)
|
||||
{
|
||||
$extensions = $this->extensions;
|
||||
if ($this->query['core_path'])
|
||||
{
|
||||
$extensions['/'] = $this->phpbb_root_path . $this->query['core_path'];
|
||||
}
|
||||
|
||||
$files = array();
|
||||
$file_list = $this->find_from_paths($extensions, $cache, $is_dir);
|
||||
|
||||
foreach ($file_list as $file)
|
||||
{
|
||||
$files[$file['named_path']] = $file['ext_name'];
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all file system entries matching the configured options for one
|
||||
* specific extension
|
||||
*
|
||||
* @param string $extension_name Name of the extension
|
||||
* @param string $extension_path Relative path to the extension root directory
|
||||
* @param bool $cache Whether the result should be cached
|
||||
* @param bool $is_dir Directories will be returned when true, only files
|
||||
* otherwise
|
||||
* @return array An array of paths to found items
|
||||
*/
|
||||
public function find_from_extension($extension_name, $extension_path, $cache = true, $is_dir = false)
|
||||
{
|
||||
$extensions = array(
|
||||
$extension_name => $extension_path,
|
||||
);
|
||||
|
||||
$files = array();
|
||||
$file_list = $this->find_from_paths($extensions, $cache, $is_dir);
|
||||
|
||||
foreach ($file_list as $file)
|
||||
{
|
||||
$files[$file['named_path']] = $file['ext_name'];
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all file system entries matching the configured options from
|
||||
* an array of paths
|
||||
*
|
||||
* @param array $extensions Array of extensions (name => full relative path)
|
||||
* @param bool $cache Whether the result should be cached
|
||||
* @param bool $is_dir Directories will be returned when true, only files
|
||||
* otherwise
|
||||
* @return array An array of paths to found items
|
||||
*/
|
||||
public function find_from_paths($extensions, $cache = true, $is_dir = false)
|
||||
{
|
||||
$this->query['is_dir'] = $is_dir;
|
||||
$query = md5(serialize($this->query) . serialize($extensions));
|
||||
|
||||
if (!defined('DEBUG') && $cache && isset($this->cached_queries[$query]))
|
||||
{
|
||||
return $this->cached_queries[$query];
|
||||
}
|
||||
|
||||
$files = array();
|
||||
|
||||
foreach ($extensions as $name => $path)
|
||||
{
|
||||
$ext_name = $name;
|
||||
|
||||
if (!file_exists($path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($name === '/')
|
||||
{
|
||||
$location = $this->query['core_path'];
|
||||
$name = '';
|
||||
$suffix = $this->query['core_suffix'];
|
||||
$prefix = $this->query['core_prefix'];
|
||||
$directory = $this->query['core_directory'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$location = 'ext/';
|
||||
$name .= '/';
|
||||
$suffix = $this->query['extension_suffix'];
|
||||
$prefix = $this->query['extension_prefix'];
|
||||
$directory = $this->query['extension_directory'];
|
||||
}
|
||||
|
||||
// match only first directory if leading slash is given
|
||||
if ($directory === '/')
|
||||
{
|
||||
$directory_pattern = '^' . preg_quote(DIRECTORY_SEPARATOR, '#');
|
||||
}
|
||||
else if ($directory && $directory[0] === '/')
|
||||
{
|
||||
if (!$is_dir)
|
||||
{
|
||||
$path .= substr($directory, 1);
|
||||
}
|
||||
$directory_pattern = '^' . preg_quote(str_replace('/', DIRECTORY_SEPARATOR, $directory) . DIRECTORY_SEPARATOR, '#');
|
||||
}
|
||||
else
|
||||
{
|
||||
$directory_pattern = preg_quote(DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $directory) . DIRECTORY_SEPARATOR, '#');
|
||||
}
|
||||
if ($is_dir)
|
||||
{
|
||||
$directory_pattern .= '$';
|
||||
}
|
||||
$directory_pattern = '#' . $directory_pattern . '#';
|
||||
|
||||
if (is_dir($path))
|
||||
{
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \phpbb\recursive_dot_prefix_filter_iterator(
|
||||
new \RecursiveDirectoryIterator(
|
||||
$path,
|
||||
\FilesystemIterator::SKIP_DOTS
|
||||
)
|
||||
),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file_info)
|
||||
{
|
||||
$filename = $file_info->getFilename();
|
||||
|
||||
if ($file_info->isDir() == $is_dir)
|
||||
{
|
||||
if ($is_dir)
|
||||
{
|
||||
$relative_path = $iterator->getInnerIterator()->getSubPath() . DIRECTORY_SEPARATOR . basename($filename) . DIRECTORY_SEPARATOR;
|
||||
if ($relative_path[0] !== DIRECTORY_SEPARATOR)
|
||||
{
|
||||
$relative_path = DIRECTORY_SEPARATOR . $relative_path;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$relative_path = $iterator->getInnerIterator()->getSubPathname();
|
||||
if ($directory && $directory[0] === '/')
|
||||
{
|
||||
$relative_path = str_replace('/', DIRECTORY_SEPARATOR, $directory) . DIRECTORY_SEPARATOR . $relative_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
$relative_path = DIRECTORY_SEPARATOR . $relative_path;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!$suffix || substr($relative_path, -strlen($suffix)) === $suffix) &&
|
||||
(!$prefix || substr($filename, 0, strlen($prefix)) === $prefix) &&
|
||||
(!$directory || preg_match($directory_pattern, $relative_path)))
|
||||
{
|
||||
$files[] = array(
|
||||
'named_path' => str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . substr($relative_path, 1)),
|
||||
'ext_name' => $ext_name,
|
||||
'path' => str_replace(array(DIRECTORY_SEPARATOR, $this->phpbb_root_path), array('/', ''), $file_info->getPath()) . '/',
|
||||
'filename' => $filename,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($cache && $this->cache)
|
||||
{
|
||||
$this->cached_queries[$query] = $files;
|
||||
$this->cache->put($this->cache_name, $this->cached_queries);
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
}
|
||||
294
install/update/new/phpbb/group/helper.php
Normal file
294
install/update/new/phpbb/group/helper.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\group;
|
||||
|
||||
use phpbb\auth\auth;
|
||||
use phpbb\cache\service as cache;
|
||||
use phpbb\config\config;
|
||||
use phpbb\language\language;
|
||||
use phpbb\event\dispatcher_interface;
|
||||
use phpbb\path_helper;
|
||||
use phpbb\user;
|
||||
|
||||
class helper
|
||||
{
|
||||
/** @var auth */
|
||||
protected $auth;
|
||||
|
||||
/** @var cache */
|
||||
protected $cache;
|
||||
|
||||
/** @var config */
|
||||
protected $config;
|
||||
|
||||
/** @var language */
|
||||
protected $language;
|
||||
|
||||
/** @var dispatcher_interface */
|
||||
protected $dispatcher;
|
||||
|
||||
/** @var path_helper */
|
||||
protected $path_helper;
|
||||
|
||||
/** @var user */
|
||||
protected $user;
|
||||
|
||||
/** @var string phpBB root path */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var array Return templates for a group name string */
|
||||
protected $name_strings;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param auth $auth Authentication object
|
||||
* @param cache $cache Cache service object
|
||||
* @param config $config Configuration object
|
||||
* @param language $language Language object
|
||||
* @param dispatcher_interface $dispatcher Event dispatcher object
|
||||
* @param path_helper $path_helper Path helper object
|
||||
* @param user $user User object
|
||||
*/
|
||||
public function __construct(auth $auth, cache $cache, config $config, language $language, dispatcher_interface $dispatcher, path_helper $path_helper, user $user)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
$this->language = $language;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->path_helper = $path_helper;
|
||||
$this->user = $user;
|
||||
|
||||
$this->phpbb_root_path = $path_helper->get_phpbb_root_path();
|
||||
|
||||
/** @html Group name spans and links for usage in the template */
|
||||
$this->name_strings = array(
|
||||
'base_url' => "{$path_helper->get_phpbb_root_path()}memberlist.{$path_helper->get_php_ext()}?mode=group&g={GROUP_ID}",
|
||||
'tpl_noprofile' => '<span class="username">{GROUP_NAME}</span>',
|
||||
'tpl_noprofile_colour' => '<span class="username-coloured" style="color: {GROUP_COLOUR};">{GROUP_NAME}</span>',
|
||||
'tpl_profile' => '<a class="username" href="{PROFILE_URL}">{GROUP_NAME}</a>',
|
||||
'tpl_profile_colour' => '<a class="username-coloured" href="{PROFILE_URL}" style="color: {GROUP_COLOUR};">{GROUP_NAME}</a>',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $group_name string The stored group name
|
||||
*
|
||||
* @return string Group name or translated group name if it exists
|
||||
*/
|
||||
public function get_name($group_name)
|
||||
{
|
||||
return $this->language->is_set('G_' . utf8_strtoupper($group_name)) ? $this->language->lang('G_' . utf8_strtoupper($group_name)) : $group_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group name details for placing into templates.
|
||||
*
|
||||
* @html Group name spans and links
|
||||
*
|
||||
* @param string $mode Profile (for getting an url to the profile),
|
||||
* group_name (for obtaining the group name),
|
||||
* colour (for obtaining the group colour),
|
||||
* full (for obtaining a coloured group name link to the group's profile),
|
||||
* no_profile (the same as full but forcing no profile link)
|
||||
* @param int $group_id The group id
|
||||
* @param string $group_name The group name
|
||||
* @param string $group_colour The group colour
|
||||
* @param mixed $custom_profile_url optional parameter to specify a profile url. The group id gets appended to this url as &g={group_id}
|
||||
*
|
||||
* @return string A string consisting of what is wanted based on $mode.
|
||||
*/
|
||||
public function get_name_string($mode, $group_id, $group_name, $group_colour = '', $custom_profile_url = false)
|
||||
{
|
||||
$s_is_bots = ($group_name === 'BOTS');
|
||||
|
||||
// This switch makes sure we only run code required for the mode
|
||||
switch ($mode)
|
||||
{
|
||||
case 'full':
|
||||
case 'no_profile':
|
||||
case 'colour':
|
||||
|
||||
// Build correct group colour
|
||||
$group_colour = $group_colour ? '#' . $group_colour : '';
|
||||
|
||||
// Return colour
|
||||
if ($mode === 'colour')
|
||||
{
|
||||
$group_name_string = $group_colour;
|
||||
break;
|
||||
}
|
||||
|
||||
// no break;
|
||||
|
||||
case 'group_name':
|
||||
|
||||
// Build correct group name
|
||||
$group_name = $this->get_name($group_name);
|
||||
|
||||
// Return group name
|
||||
if ($mode === 'group_name')
|
||||
{
|
||||
$group_name_string = $group_name;
|
||||
break;
|
||||
}
|
||||
|
||||
// no break;
|
||||
|
||||
case 'profile':
|
||||
|
||||
// Build correct profile url - only show if not anonymous and permission to view profile if registered user
|
||||
// For anonymous the link leads to a login page.
|
||||
if ($group_id && !$s_is_bots && ($this->user->data['user_id'] == ANONYMOUS || $this->auth->acl_get('u_viewprofile')))
|
||||
{
|
||||
$profile_url = ($custom_profile_url !== false) ? $custom_profile_url . '&g=' . (int) $group_id : str_replace(array('={GROUP_ID}', '=%7BGROUP_ID%7D'), '=' . (int) $group_id, append_sid($this->name_strings['base_url']));
|
||||
}
|
||||
else
|
||||
{
|
||||
$profile_url = '';
|
||||
}
|
||||
|
||||
// Return profile
|
||||
if ($mode === 'profile')
|
||||
{
|
||||
$group_name_string = $profile_url;
|
||||
break;
|
||||
}
|
||||
|
||||
// no break;
|
||||
}
|
||||
|
||||
if (!isset($group_name_string))
|
||||
{
|
||||
if (($mode === 'full' && empty($profile_url)) || $mode === 'no_profile' || $s_is_bots)
|
||||
{
|
||||
$group_name_string = str_replace(array('{GROUP_COLOUR}', '{GROUP_NAME}'), array($group_colour, $group_name), (!$group_colour) ? $this->name_strings['tpl_noprofile'] : $this->name_strings['tpl_noprofile_colour']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$group_name_string = str_replace(array('{PROFILE_URL}', '{GROUP_COLOUR}', '{GROUP_NAME}'), array($profile_url, $group_colour, $group_name), (!$group_colour) ? $this->name_strings['tpl_profile'] : $this->name_strings['tpl_profile_colour']);
|
||||
}
|
||||
}
|
||||
|
||||
$name_strings = $this->name_strings;
|
||||
|
||||
/**
|
||||
* Use this event to change the output of the group name
|
||||
*
|
||||
* @event core.modify_group_name_string
|
||||
* @var string mode profile|group_name|colour|full|no_profile
|
||||
* @var int group_id The group identifier
|
||||
* @var string group_name The group name
|
||||
* @var string group_colour The group colour
|
||||
* @var string custom_profile_url Optional parameter to specify a profile url.
|
||||
* @var string group_name_string The string that has been generated
|
||||
* @var array name_strings Array of original return templates
|
||||
* @since 3.2.8-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'mode',
|
||||
'group_id',
|
||||
'group_name',
|
||||
'group_colour',
|
||||
'custom_profile_url',
|
||||
'group_name_string',
|
||||
'name_strings',
|
||||
);
|
||||
extract($this->dispatcher->trigger_event('core.modify_group_name_string', compact($vars)));
|
||||
|
||||
return $group_name_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group rank title and image
|
||||
*
|
||||
* @html Group rank image element
|
||||
*
|
||||
* @param array $group_data The current stored group data
|
||||
*
|
||||
* @return array An associative array containing the rank title (title),
|
||||
* the rank image as full img tag (img) and the rank image source (img_src)
|
||||
*/
|
||||
public function get_rank($group_data)
|
||||
{
|
||||
$group_rank_data = array(
|
||||
'title' => null,
|
||||
'img' => null,
|
||||
'img_src' => null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Preparing a group's rank before displaying
|
||||
*
|
||||
* @event core.get_group_rank_before
|
||||
* @var array group_data Array with group's data
|
||||
* @since 3.2.8-RC1
|
||||
*/
|
||||
|
||||
$vars = array('group_data');
|
||||
extract($this->dispatcher->trigger_event('core.get_group_rank_before', compact($vars)));
|
||||
|
||||
if (!empty($group_data['group_rank']))
|
||||
{
|
||||
// Only obtain ranks if group rank is set
|
||||
$ranks = $this->cache->obtain_ranks();
|
||||
|
||||
if (isset($ranks['special'][$group_data['group_rank']]))
|
||||
{
|
||||
$rank = $ranks['special'][$group_data['group_rank']];
|
||||
|
||||
$group_rank_data['title'] = $rank['rank_title'];
|
||||
|
||||
$group_rank_data['img_src'] = (!empty($rank['rank_image'])) ? $this->path_helper->update_web_root_path($this->phpbb_root_path . $this->config['ranks_path'] . '/' . $rank['rank_image']) : '';
|
||||
|
||||
/** @html Group rank image element for usage in the template */
|
||||
$group_rank_data['img'] = (!empty($rank['rank_image'])) ? '<img src="' . $group_rank_data['img_src'] . '" alt="' . $rank['rank_title'] . '" title="' . $rank['rank_title'] . '" />' : '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a group's rank before displaying
|
||||
*
|
||||
* @event core.get_group_rank_after
|
||||
* @var array group_data Array with group's data
|
||||
* @var array group_rank_data Group rank data
|
||||
* @since 3.2.8-RC1
|
||||
*/
|
||||
|
||||
$vars = array(
|
||||
'group_data',
|
||||
'group_rank_data',
|
||||
);
|
||||
extract($this->dispatcher->trigger_event('core.get_group_rank_after', compact($vars)));
|
||||
|
||||
return $group_rank_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group avatar.
|
||||
* Wrapper function for phpbb_get_group_avatar()
|
||||
*
|
||||
* @param array $group_row Row from the groups table
|
||||
* @param string $alt Optional language string for alt tag within image, can be a language key or text
|
||||
* @param bool $ignore_config Ignores the config-setting, to be still able to view the avatar in the UCP
|
||||
* @param bool $lazy If true, will be lazy loaded (requires JS)
|
||||
*
|
||||
* @return string Avatar html
|
||||
*/
|
||||
function get_avatar($group_row, $alt = 'GROUP_AVATAR', $ignore_config = false, $lazy = false)
|
||||
{
|
||||
return phpbb_get_group_avatar($group_row, $alt, $ignore_config, $lazy);
|
||||
}
|
||||
}
|
||||
90
install/update/new/phpbb/help/controller/bbcode.php
Normal file
90
install/update/new/phpbb/help/controller/bbcode.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\help\controller;
|
||||
|
||||
/**
|
||||
* BBCode help page
|
||||
*/
|
||||
class bbcode extends controller
|
||||
{
|
||||
/**
|
||||
* @return string The title of the page
|
||||
*/
|
||||
public function display()
|
||||
{
|
||||
$this->language->add_lang('help/bbcode');
|
||||
|
||||
$this->template->assign_block_vars('navlinks', array(
|
||||
'BREADCRUMB_NAME' => $this->language->lang('BBCODE_GUIDE'),
|
||||
'U_BREADCRUMB' => $this->helper->route('phpbb_help_bbcode_controller'),
|
||||
));
|
||||
|
||||
$this->manager->add_block(
|
||||
'HELP_BBCODE_BLOCK_INTRO',
|
||||
false,
|
||||
array(
|
||||
'HELP_BBCODE_INTRO_BBCODE_QUESTION' => 'HELP_BBCODE_INTRO_BBCODE_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_BBCODE_BLOCK_TEXT',
|
||||
false,
|
||||
array(
|
||||
'HELP_BBCODE_TEXT_BASIC_QUESTION' => 'HELP_BBCODE_TEXT_BASIC_ANSWER',
|
||||
'HELP_BBCODE_TEXT_COLOR_QUESTION' => 'HELP_BBCODE_TEXT_COLOR_ANSWER',
|
||||
'HELP_BBCODE_TEXT_COMBINE_QUESTION' => 'HELP_BBCODE_TEXT_COMBINE_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_BBCODE_BLOCK_QUOTES',
|
||||
false,
|
||||
array(
|
||||
'HELP_BBCODE_QUOTES_TEXT_QUESTION' => 'HELP_BBCODE_QUOTES_TEXT_ANSWER',
|
||||
'HELP_BBCODE_QUOTES_CODE_QUESTION' => 'HELP_BBCODE_QUOTES_CODE_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_BBCODE_BLOCK_LISTS',
|
||||
false,
|
||||
array(
|
||||
'HELP_BBCODE_LISTS_UNORDERER_QUESTION' => 'HELP_BBCODE_LISTS_UNORDERER_ANSWER',
|
||||
'HELP_BBCODE_LISTS_ORDERER_QUESTION' => 'HELP_BBCODE_LISTS_ORDERER_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_BBCODE_BLOCK_LINKS',
|
||||
true,
|
||||
array(
|
||||
'HELP_BBCODE_LINKS_BASIC_QUESTION' => 'HELP_BBCODE_LINKS_BASIC_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_BBCODE_BLOCK_IMAGES',
|
||||
false,
|
||||
array(
|
||||
'HELP_BBCODE_IMAGES_BASIC_QUESTION' => 'HELP_BBCODE_IMAGES_BASIC_ANSWER',
|
||||
'HELP_BBCODE_IMAGES_ATTACHMENT_QUESTION' => 'HELP_BBCODE_IMAGES_ATTACHMENT_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_BBCODE_BLOCK_OTHERS',
|
||||
false,
|
||||
array(
|
||||
'HELP_BBCODE_OTHERS_CUSTOM_QUESTION' => 'HELP_BBCODE_OTHERS_CUSTOM_ANSWER',
|
||||
)
|
||||
);
|
||||
|
||||
return $this->language->lang('BBCODE_GUIDE');
|
||||
}
|
||||
}
|
||||
170
install/update/new/phpbb/help/controller/faq.php
Normal file
170
install/update/new/phpbb/help/controller/faq.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\help\controller;
|
||||
|
||||
/**
|
||||
* FAQ help page
|
||||
*/
|
||||
class faq extends controller
|
||||
{
|
||||
/**
|
||||
* @return string The title of the page
|
||||
*/
|
||||
public function display()
|
||||
{
|
||||
$this->language->add_lang('help/faq');
|
||||
|
||||
$this->template->assign_block_vars('navlinks', array(
|
||||
'BREADCRUMB_NAME' => $this->language->lang('FAQ_EXPLAIN'),
|
||||
'U_BREADCRUMB' => $this->helper->route('phpbb_help_faq_controller'),
|
||||
));
|
||||
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_LOGIN',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_LOGIN_REGISTER_QUESTION' => 'HELP_FAQ_LOGIN_REGISTER_ANSWER',
|
||||
'HELP_FAQ_LOGIN_COPPA_QUESTION' => 'HELP_FAQ_LOGIN_COPPA_ANSWER',
|
||||
'HELP_FAQ_LOGIN_CANNOT_REGISTER_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_REGISTER_ANSWER',
|
||||
'HELP_FAQ_LOGIN_REGISTER_CONFIRM_QUESTION' => 'HELP_FAQ_LOGIN_REGISTER_CONFIRM_ANSWER',
|
||||
'HELP_FAQ_LOGIN_CANNOT_LOGIN_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANSWER',
|
||||
'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANYMORE_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANYMORE_ANSWER',
|
||||
'HELP_FAQ_LOGIN_LOST_PASSWORD_QUESTION' => 'HELP_FAQ_LOGIN_LOST_PASSWORD_ANSWER',
|
||||
'HELP_FAQ_LOGIN_AUTO_LOGOUT_QUESTION' => 'HELP_FAQ_LOGIN_AUTO_LOGOUT_ANSWER',
|
||||
'HELP_FAQ_LOGIN_DELETE_COOKIES_QUESTION' => 'HELP_FAQ_LOGIN_DELETE_COOKIES_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_USERSETTINGS',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_USERSETTINGS_CHANGE_SETTINGS_QUESTION' => 'HELP_FAQ_USERSETTINGS_CHANGE_SETTINGS_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_HIDE_ONLINE_QUESTION' => 'HELP_FAQ_USERSETTINGS_HIDE_ONLINE_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_TIMEZONE_QUESTION' => 'HELP_FAQ_USERSETTINGS_TIMEZONE_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_SERVERTIME_QUESTION' => 'HELP_FAQ_USERSETTINGS_SERVERTIME_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_LANGUAGE_QUESTION' => 'HELP_FAQ_USERSETTINGS_LANGUAGE_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_AVATAR_QUESTION' => 'HELP_FAQ_USERSETTINGS_AVATAR_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_AVATAR_DISPLAY_QUESTION' => 'HELP_FAQ_USERSETTINGS_AVATAR_DISPLAY_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_RANK_QUESTION' => 'HELP_FAQ_USERSETTINGS_RANK_ANSWER',
|
||||
'HELP_FAQ_USERSETTINGS_EMAIL_LOGIN_QUESTION' => 'HELP_FAQ_USERSETTINGS_EMAIL_LOGIN_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_POSTING',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_POSTING_CREATE_QUESTION' => 'HELP_FAQ_POSTING_CREATE_ANSWER',
|
||||
'HELP_FAQ_POSTING_EDIT_DELETE_QUESTION' => 'HELP_FAQ_POSTING_EDIT_DELETE_ANSWER',
|
||||
'HELP_FAQ_POSTING_SIGNATURE_QUESTION' => 'HELP_FAQ_POSTING_SIGNATURE_ANSWER',
|
||||
'HELP_FAQ_POSTING_POLL_CREATE_QUESTION' => 'HELP_FAQ_POSTING_POLL_CREATE_ANSWER',
|
||||
'HELP_FAQ_POSTING_POLL_ADD_QUESTION' => 'HELP_FAQ_POSTING_POLL_ADD_ANSWER',
|
||||
'HELP_FAQ_POSTING_POLL_EDIT_QUESTION' => 'HELP_FAQ_POSTING_POLL_EDIT_ANSWER',
|
||||
'HELP_FAQ_POSTING_FORUM_RESTRICTED_QUESTION' => 'HELP_FAQ_POSTING_FORUM_RESTRICTED_ANSWER',
|
||||
'HELP_FAQ_POSTING_NO_ATTACHMENTS_QUESTION' => 'HELP_FAQ_POSTING_NO_ATTACHMENTS_ANSWER',
|
||||
'HELP_FAQ_POSTING_WARNING_QUESTION' => 'HELP_FAQ_POSTING_WARNING_ANSWER',
|
||||
'HELP_FAQ_POSTING_REPORT_QUESTION' => 'HELP_FAQ_POSTING_REPORT_ANSWER',
|
||||
'HELP_FAQ_POSTING_DRAFT_QUESTION' => 'HELP_FAQ_POSTING_DRAFT_ANSWER',
|
||||
'HELP_FAQ_POSTING_QUEUE_QUESTION' => 'HELP_FAQ_POSTING_QUEUE_ANSWER',
|
||||
'HELP_FAQ_POSTING_BUMP_QUESTION' => 'HELP_FAQ_POSTING_BUMP_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_FORMATTING',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_FORMATTING_BBOCDE_QUESTION' => 'HELP_FAQ_FORMATTING_BBOCDE_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_HTML_QUESTION' => 'HELP_FAQ_FORMATTING_HTML_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_SMILIES_QUESTION' => 'HELP_FAQ_FORMATTING_SMILIES_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_IMAGES_QUESTION' => 'HELP_FAQ_FORMATTING_IMAGES_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_GLOBAL_ANNOUNCE_QUESTION' => 'HELP_FAQ_FORMATTING_GLOBAL_ANNOUNCE_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_ANNOUNCEMENT_QUESTION' => 'HELP_FAQ_FORMATTING_ANNOUNCEMENT_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_STICKIES_QUESTION' => 'HELP_FAQ_FORMATTING_STICKIES_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_LOCKED_QUESTION' => 'HELP_FAQ_FORMATTING_LOCKED_ANSWER',
|
||||
'HELP_FAQ_FORMATTING_ICONS_QUESTION' => 'HELP_FAQ_FORMATTING_ICONS_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_GROUPS',
|
||||
true,
|
||||
array(
|
||||
'HELP_FAQ_GROUPS_ADMINISTRATORS_QUESTION' => 'HELP_FAQ_GROUPS_ADMINISTRATORS_ANSWER',
|
||||
'HELP_FAQ_GROUPS_MODERATORS_QUESTION' => 'HELP_FAQ_GROUPS_MODERATORS_ANSWER',
|
||||
'HELP_FAQ_GROUPS_USERGROUPS_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_ANSWER',
|
||||
'HELP_FAQ_GROUPS_USERGROUPS_JOIN_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_JOIN_ANSWER',
|
||||
'HELP_FAQ_GROUPS_USERGROUPS_LEAD_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_LEAD_ANSWER',
|
||||
'HELP_FAQ_GROUPS_COLORS_QUESTION' => 'HELP_FAQ_GROUPS_COLORS_ANSWER',
|
||||
'HELP_FAQ_GROUPS_DEFAULT_QUESTION' => 'HELP_FAQ_GROUPS_DEFAULT_ANSWER',
|
||||
'HELP_FAQ_GROUPS_TEAM_QUESTION' => 'HELP_FAQ_GROUPS_TEAM_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_PMS',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_PMS_CANNOT_SEND_QUESTION' => 'HELP_FAQ_PMS_CANNOT_SEND_ANSWER',
|
||||
'HELP_FAQ_PMS_UNWANTED_QUESTION' => 'HELP_FAQ_PMS_UNWANTED_ANSWER',
|
||||
'HELP_FAQ_PMS_SPAM_QUESTION' => 'HELP_FAQ_PMS_SPAM_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_FRIENDS',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_FRIENDS_BASIC_QUESTION' => 'HELP_FAQ_FRIENDS_BASIC_ANSWER',
|
||||
'HELP_FAQ_FRIENDS_MANAGE_QUESTION' => 'HELP_FAQ_FRIENDS_MANAGE_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_SEARCH',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_SEARCH_FORUM_QUESTION' => 'HELP_FAQ_SEARCH_FORUM_ANSWER',
|
||||
'HELP_FAQ_SEARCH_NO_RESULT_QUESTION' => 'HELP_FAQ_SEARCH_NO_RESULT_ANSWER',
|
||||
'HELP_FAQ_SEARCH_BLANK_QUESTION' => 'HELP_FAQ_SEARCH_BLANK_ANSWER',
|
||||
'HELP_FAQ_SEARCH_MEMBERS_QUESTION' => 'HELP_FAQ_SEARCH_MEMBERS_ANSWER',
|
||||
'HELP_FAQ_SEARCH_OWN_QUESTION' => 'HELP_FAQ_SEARCH_OWN_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_BOOKMARKS',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_BOOKMARKS_DIFFERENCE_QUESTION' => 'HELP_FAQ_BOOKMARKS_DIFFERENCE_ANSWER',
|
||||
'HELP_FAQ_BOOKMARKS_TOPIC_QUESTION' => 'HELP_FAQ_BOOKMARKS_TOPIC_ANSWER',
|
||||
'HELP_FAQ_BOOKMARKS_FORUM_QUESTION' => 'HELP_FAQ_BOOKMARKS_FORUM_ANSWER',
|
||||
'HELP_FAQ_BOOKMARKS_REMOVE_QUESTION' => 'HELP_FAQ_BOOKMARKS_REMOVE_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_ATTACHMENTS',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_ATTACHMENTS_ALLOWED_QUESTION' => 'HELP_FAQ_ATTACHMENTS_ALLOWED_ANSWER',
|
||||
'HELP_FAQ_ATTACHMENTS_OWN_QUESTION' => 'HELP_FAQ_ATTACHMENTS_OWN_ANSWER',
|
||||
)
|
||||
);
|
||||
$this->manager->add_block(
|
||||
'HELP_FAQ_BLOCK_ISSUES',
|
||||
false,
|
||||
array(
|
||||
'HELP_FAQ_ISSUES_WHOIS_PHPBB_QUESTION' => 'HELP_FAQ_ISSUES_WHOIS_PHPBB_ANSWER',
|
||||
'HELP_FAQ_ISSUES_FEATURE_QUESTION' => 'HELP_FAQ_ISSUES_FEATURE_ANSWER',
|
||||
'HELP_FAQ_ISSUES_LEGAL_QUESTION' => 'HELP_FAQ_ISSUES_LEGAL_ANSWER',
|
||||
'HELP_FAQ_ISSUES_ADMIN_QUESTION' => 'HELP_FAQ_ISSUES_ADMIN_ANSWER',
|
||||
)
|
||||
);
|
||||
|
||||
return $this->language->lang('FAQ_EXPLAIN');
|
||||
}
|
||||
}
|
||||
413
install/update/new/phpbb/install/controller/helper.php
Normal file
413
install/update/new/phpbb/install/controller/helper.php
Normal file
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\controller;
|
||||
|
||||
use phpbb\install\helper\config;
|
||||
use phpbb\install\helper\navigation\navigation_provider;
|
||||
use phpbb\language\language;
|
||||
use phpbb\language\language_file_helper;
|
||||
use phpbb\path_helper;
|
||||
use phpbb\request\request;
|
||||
use phpbb\request\request_interface;
|
||||
use phpbb\routing\router;
|
||||
use phpbb\symfony_request;
|
||||
use phpbb\template\template;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
/**
|
||||
* A duplicate of \phpbb\controller\helper
|
||||
*
|
||||
* This class is necessary because of controller\helper's legacy function calls
|
||||
* to page_header() page_footer() functions which has unavailable dependencies.
|
||||
*/
|
||||
class helper
|
||||
{
|
||||
/**
|
||||
* @var config
|
||||
*/
|
||||
protected $installer_config;
|
||||
|
||||
/**
|
||||
* @var \phpbb\language\language
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* @var bool|string
|
||||
*/
|
||||
protected $language_cookie;
|
||||
|
||||
/**
|
||||
* @var \phpbb\language\language_file_helper
|
||||
*/
|
||||
protected $lang_helper;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\navigation\navigation_provider
|
||||
*/
|
||||
protected $navigation_provider;
|
||||
|
||||
/**
|
||||
* @var \phpbb\template\template
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* @var \phpbb\path_helper
|
||||
*/
|
||||
protected $path_helper;
|
||||
|
||||
/**
|
||||
* @var \phpbb\request\request
|
||||
*/
|
||||
protected $phpbb_request;
|
||||
|
||||
/**
|
||||
* @var \phpbb\symfony_request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var \phpbb\routing\router
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_admin_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param config $config
|
||||
* @param language $language
|
||||
* @param language_file_helper $lang_helper
|
||||
* @param navigation_provider $nav
|
||||
* @param template $template
|
||||
* @param path_helper $path_helper
|
||||
* @param request $phpbb_request
|
||||
* @param symfony_request $request
|
||||
* @param router $router
|
||||
* @param string $phpbb_root_path
|
||||
*/
|
||||
public function __construct(config $config, language $language, language_file_helper $lang_helper, navigation_provider $nav, template $template, path_helper $path_helper, request $phpbb_request, symfony_request $request, router $router, $phpbb_root_path)
|
||||
{
|
||||
$this->installer_config = $config;
|
||||
$this->language = $language;
|
||||
$this->language_cookie = false;
|
||||
$this->lang_helper = $lang_helper;
|
||||
$this->navigation_provider = $nav;
|
||||
$this->template = $template;
|
||||
$this->path_helper = $path_helper;
|
||||
$this->phpbb_request = $phpbb_request;
|
||||
$this->request = $request;
|
||||
$this->router = $router;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->phpbb_admin_path = $phpbb_root_path . 'adm/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Automate setting up the page and creating the response object.
|
||||
*
|
||||
* @param string $template_file The template handle to render
|
||||
* @param string $page_title The title of the page to output
|
||||
* @param bool $selected_language True to enable language selector it, false otherwise
|
||||
* @param int $status_code The status code to be sent to the page header
|
||||
*
|
||||
* @return Response object containing rendered page
|
||||
*/
|
||||
public function render($template_file, $page_title = '', $selected_language = false, $status_code = 200)
|
||||
{
|
||||
$this->page_header($page_title, $selected_language);
|
||||
|
||||
$this->template->set_filenames(array(
|
||||
'body' => $template_file,
|
||||
));
|
||||
|
||||
$response = new Response($this->template->assign_display('body'), $status_code);
|
||||
|
||||
// Set language cookie
|
||||
if ($this->language_cookie !== false)
|
||||
{
|
||||
$cookie = new Cookie('lang', $this->language_cookie, time() + 3600);
|
||||
$response->headers->setCookie($cookie);
|
||||
|
||||
$this->language_cookie = false;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path from route name
|
||||
*
|
||||
* @param string $route_name
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function route($route_name, $parameters = array())
|
||||
{
|
||||
$url = $this->router->generate($route_name, $parameters);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles language selector form
|
||||
*/
|
||||
public function handle_language_select()
|
||||
{
|
||||
$lang = null;
|
||||
|
||||
// Check if language form has been submited
|
||||
$submit = $this->phpbb_request->variable('change_lang', '');
|
||||
if (!empty($submit))
|
||||
{
|
||||
$lang = $this->phpbb_request->variable('language', '');
|
||||
}
|
||||
|
||||
// Retrieve language from cookie
|
||||
$lang_cookie = $this->phpbb_request->variable('lang', '', false, request_interface::COOKIE);
|
||||
if (empty($lang) && !empty($lang_cookie))
|
||||
{
|
||||
$lang = $lang_cookie;
|
||||
}
|
||||
|
||||
$lang = (!empty($lang) && strpos($lang, '/') === false) ? $lang : null;
|
||||
$this->language_cookie = $lang;
|
||||
|
||||
$this->render_language_select($lang);
|
||||
|
||||
if ($lang !== null)
|
||||
{
|
||||
$this->language->set_user_language($lang, true);
|
||||
$this->installer_config->set('user_language', $lang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process navigation data to reflect active/completed stages
|
||||
*
|
||||
* @param \phpbb\install\helper\iohandler\iohandler_interface|null $iohandler
|
||||
*/
|
||||
public function handle_navigation($iohandler = null)
|
||||
{
|
||||
$nav_data = $this->installer_config->get_navigation_data();
|
||||
|
||||
// Set active navigation stage
|
||||
if (isset($nav_data['active']) && is_array($nav_data['active']))
|
||||
{
|
||||
if ($iohandler !== null)
|
||||
{
|
||||
$iohandler->set_active_stage_menu($nav_data['active']);
|
||||
}
|
||||
|
||||
$this->navigation_provider->set_nav_property($nav_data['active'], array(
|
||||
'selected' => true,
|
||||
'completed' => false,
|
||||
));
|
||||
}
|
||||
|
||||
// Set finished navigation stages
|
||||
if (isset($nav_data['finished']) && is_array($nav_data['finished']))
|
||||
{
|
||||
foreach ($nav_data['finished'] as $finished_stage)
|
||||
{
|
||||
if ($iohandler !== null)
|
||||
{
|
||||
$iohandler->set_finished_stage_menu($finished_stage);
|
||||
}
|
||||
|
||||
$this->navigation_provider->set_nav_property($finished_stage, array(
|
||||
'selected' => false,
|
||||
'completed' => true,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default template variables
|
||||
*
|
||||
* @param string $page_title Title of the page
|
||||
* @param bool $selected_language True to enable language selector it, false otherwise
|
||||
*/
|
||||
protected function page_header($page_title, $selected_language = false)
|
||||
{
|
||||
// Path to templates
|
||||
$paths = array($this->phpbb_root_path . 'install/update/new/adm/', $this->phpbb_admin_path);
|
||||
$paths = array_filter($paths, 'is_dir');
|
||||
$path = array_shift($paths);
|
||||
$path = substr($path, strlen($this->phpbb_root_path));
|
||||
|
||||
$this->template->assign_vars(array(
|
||||
'L_CHANGE' => $this->language->lang('CHANGE'),
|
||||
'L_COLON' => $this->language->lang('COLON'),
|
||||
'L_INSTALL_PANEL' => $this->language->lang('INSTALL_PANEL'),
|
||||
'L_SELECT_LANG' => $this->language->lang('SELECT_LANG'),
|
||||
'L_SKIP' => $this->language->lang('SKIP'),
|
||||
'PAGE_TITLE' => $this->language->lang($page_title),
|
||||
'T_IMAGE_PATH' => $this->path_helper->get_web_root_path() . $path . 'images',
|
||||
'T_JQUERY_LINK' => $this->path_helper->get_web_root_path() . $path . '../assets/javascript/jquery-3.4.1.min.js',
|
||||
'T_TEMPLATE_PATH' => $this->path_helper->get_web_root_path() . $path . 'style',
|
||||
'T_ASSETS_PATH' => $this->path_helper->get_web_root_path() . $path . '../assets',
|
||||
|
||||
'S_CONTENT_DIRECTION' => $this->language->lang('DIRECTION'),
|
||||
'S_CONTENT_FLOW_BEGIN' => ($this->language->lang('DIRECTION') === 'ltr') ? 'left' : 'right',
|
||||
'S_CONTENT_FLOW_END' => ($this->language->lang('DIRECTION') === 'ltr') ? 'right' : 'left',
|
||||
'S_CONTENT_ENCODING' => 'UTF-8',
|
||||
'S_LANG_SELECT' => $selected_language,
|
||||
|
||||
'S_USER_LANG' => $this->language->lang('USER_LANG'),
|
||||
));
|
||||
|
||||
$this->render_navigation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render navigation
|
||||
*/
|
||||
protected function render_navigation()
|
||||
{
|
||||
// Get navigation items
|
||||
$nav_array = $this->navigation_provider->get();
|
||||
$nav_array = $this->sort_navigation_level($nav_array);
|
||||
|
||||
$active_main_menu = $this->get_active_main_menu($nav_array);
|
||||
|
||||
// Pass navigation to template
|
||||
foreach ($nav_array as $key => $entry)
|
||||
{
|
||||
$this->template->assign_block_vars('t_block1', array(
|
||||
'L_TITLE' => $this->language->lang($entry['label']),
|
||||
'S_SELECTED' => ($active_main_menu === $key),
|
||||
'U_TITLE' => $this->route($entry['route']),
|
||||
));
|
||||
|
||||
if (is_array($entry[0]) && $active_main_menu === $key)
|
||||
{
|
||||
$entry[0] = $this->sort_navigation_level($entry[0]);
|
||||
|
||||
foreach ($entry[0] as $name => $sub_entry)
|
||||
{
|
||||
if (isset($sub_entry['stage']) && $sub_entry['stage'] === true)
|
||||
{
|
||||
$this->template->assign_block_vars('l_block2', array(
|
||||
'L_TITLE' => $this->language->lang($sub_entry['label']),
|
||||
'S_SELECTED' => (isset($sub_entry['selected']) && $sub_entry['selected'] === true),
|
||||
'S_COMPLETE' => (isset($sub_entry['completed']) && $sub_entry['completed'] === true),
|
||||
'STAGE_NAME' => $name,
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->template->assign_block_vars('l_block1', array(
|
||||
'L_TITLE' => $this->language->lang($sub_entry['label']),
|
||||
'S_SELECTED' => (isset($sub_entry['route']) && $sub_entry['route'] === $this->request->get('_route')),
|
||||
'U_TITLE' => $this->route($sub_entry['route']),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render language select form
|
||||
*
|
||||
* @param string $selected_language
|
||||
*/
|
||||
protected function render_language_select($selected_language = null)
|
||||
{
|
||||
$langs = $this->lang_helper->get_available_languages();
|
||||
foreach ($langs as $lang)
|
||||
{
|
||||
$this->template->assign_block_vars('language_select_item', array(
|
||||
'VALUE' => $lang['iso'],
|
||||
'NAME' => $lang['local_name'],
|
||||
'SELECTED' => ($lang['iso'] === $selected_language),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the active main menu item
|
||||
*
|
||||
* @param array $nav_array
|
||||
*
|
||||
* @return string|bool Returns the name of the active main menu element, if the element not found, returns false
|
||||
*/
|
||||
protected function get_active_main_menu($nav_array)
|
||||
{
|
||||
$active_route = $this->request->get('_route');
|
||||
|
||||
foreach ($nav_array as $nav_name => $nav_options)
|
||||
{
|
||||
$current_menu = $nav_name;
|
||||
|
||||
if (isset($nav_options['route']) && $nav_options['route'] === $active_route)
|
||||
{
|
||||
return $nav_name;
|
||||
}
|
||||
|
||||
if (is_array($nav_options[0]))
|
||||
{
|
||||
foreach ($nav_options[0] as $sub_menus)
|
||||
{
|
||||
if (isset($sub_menus['route']) && $sub_menus['route'] === $active_route)
|
||||
{
|
||||
return $current_menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the top level of navigation array
|
||||
*
|
||||
* @param array $nav_array Navigation array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function sort_navigation_level($nav_array)
|
||||
{
|
||||
$sorted = array();
|
||||
foreach ($nav_array as $key => $nav)
|
||||
{
|
||||
$order = (isset($nav['order'])) ? $nav['order'] : 0;
|
||||
$sorted[$order][$key] = $nav;
|
||||
}
|
||||
|
||||
// Linearization of navigation array
|
||||
$nav_array = array();
|
||||
ksort($sorted);
|
||||
foreach ($sorted as $nav)
|
||||
{
|
||||
$nav_array = array_merge($nav_array, $nav);
|
||||
}
|
||||
|
||||
return $nav_array;
|
||||
}
|
||||
}
|
||||
191
install/update/new/phpbb/install/helper/container_factory.php
Normal file
191
install/update/new/phpbb/install/helper/container_factory.php
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\helper;
|
||||
|
||||
use phpbb\install\exception\cannot_build_container_exception;
|
||||
use phpbb\language\language;
|
||||
use phpbb\request\request;
|
||||
|
||||
class container_factory
|
||||
{
|
||||
/**
|
||||
* @var language
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* @var \phpbb\request\request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var update_helper
|
||||
*/
|
||||
protected $update_helper;
|
||||
|
||||
/**
|
||||
* The full phpBB container
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param language $language Language service
|
||||
* @param request $request Request interface
|
||||
* @param update_helper $update_helper Update helper
|
||||
* @param string $phpbb_root_path Path to phpBB's root
|
||||
* @param string $php_ext Extension of PHP files
|
||||
*/
|
||||
public function __construct(language $language, request $request, update_helper $update_helper, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->language = $language;
|
||||
$this->request = $request;
|
||||
$this->update_helper = $update_helper;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->container = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Container getter
|
||||
*
|
||||
* @param null|string $service_name Name of the service to return
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\ContainerInterface|Object phpBB's dependency injection container
|
||||
* or the service specified in $service_name
|
||||
*
|
||||
* @throws \phpbb\install\exception\cannot_build_container_exception When container cannot be built
|
||||
* @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException If the service is not defined
|
||||
* @throws \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException When a circular reference is detected
|
||||
* @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException When the service is not defined
|
||||
*/
|
||||
public function get($service_name = null)
|
||||
{
|
||||
// Check if container was built, if not try to build it
|
||||
if ($this->container === null)
|
||||
{
|
||||
$this->build_container();
|
||||
}
|
||||
|
||||
return ($service_name === null) ? $this->container : $this->container->get($service_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified parameter from the container
|
||||
*
|
||||
* @param string $param_name
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \phpbb\install\exception\cannot_build_container_exception When container cannot be built
|
||||
*/
|
||||
public function get_parameter($param_name)
|
||||
{
|
||||
// Check if container was built, if not try to build it
|
||||
if ($this->container === null)
|
||||
{
|
||||
$this->build_container();
|
||||
}
|
||||
|
||||
return $this->container->getParameter($param_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build dependency injection container
|
||||
*
|
||||
* @throws \phpbb\install\exception\cannot_build_container_exception When container cannot be built
|
||||
*/
|
||||
protected function build_container()
|
||||
{
|
||||
// If the container has been already built just return.
|
||||
// Although this should never happen
|
||||
if ($this->container instanceof \Symfony\Component\DependencyInjection\ContainerInterface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether container can be built
|
||||
// We need config.php for that so let's check if it has been set up yet
|
||||
if (!filesize($this->phpbb_root_path . 'config.' . $this->php_ext))
|
||||
{
|
||||
throw new cannot_build_container_exception();
|
||||
}
|
||||
|
||||
$phpbb_config_php_file = new \phpbb\config_php_file($this->phpbb_root_path, $this->php_ext);
|
||||
$phpbb_container_builder = new \phpbb\di\container_builder($this->phpbb_root_path, $this->php_ext);
|
||||
|
||||
// For BC with functions that we need during install
|
||||
global $phpbb_container, $table_prefix;
|
||||
|
||||
$disable_super_globals = $this->request->super_globals_disabled();
|
||||
|
||||
// This is needed because container_builder::get_env_parameters() uses $_SERVER
|
||||
if ($disable_super_globals)
|
||||
{
|
||||
$this->request->enable_super_globals();
|
||||
}
|
||||
|
||||
$other_config_path = $this->phpbb_root_path . 'install/update/new/config';
|
||||
$config_path = (is_dir($other_config_path)) ? $other_config_path : $this->phpbb_root_path . 'config';
|
||||
|
||||
$this->container = $phpbb_container_builder
|
||||
->with_environment('production')
|
||||
->with_config($phpbb_config_php_file)
|
||||
->with_config_path($config_path)
|
||||
->without_compiled_container()
|
||||
->get_container();
|
||||
|
||||
// Setting request is required for the compatibility globals as those are generated from
|
||||
// this container
|
||||
if (!$this->container->isFrozen())
|
||||
{
|
||||
$this->container->register('request')->setSynthetic(true);
|
||||
$this->container->register('language')->setSynthetic(true);
|
||||
}
|
||||
|
||||
$this->container->set('request', $this->request);
|
||||
$this->container->set('language', $this->language);
|
||||
|
||||
$this->container->compile();
|
||||
|
||||
$phpbb_container = $this->container;
|
||||
$table_prefix = $phpbb_config_php_file->get('table_prefix');
|
||||
|
||||
// Restore super globals to previous state
|
||||
if ($disable_super_globals)
|
||||
{
|
||||
$this->request->disable_super_globals();
|
||||
}
|
||||
|
||||
// Get compatibility globals and constants
|
||||
$this->update_helper->include_file('includes/compatibility_globals.' . $this->php_ext);
|
||||
|
||||
register_compatibility_globals();
|
||||
|
||||
$this->update_helper->include_file('includes/constants.' . $this->php_ext);
|
||||
}
|
||||
}
|
||||
421
install/update/new/phpbb/install/helper/database.php
Normal file
421
install/update/new/phpbb/install/helper/database.php
Normal file
@@ -0,0 +1,421 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\helper;
|
||||
|
||||
use phpbb\install\exception\invalid_dbms_exception;
|
||||
|
||||
/**
|
||||
* Database related general functionality for installer
|
||||
*/
|
||||
class database
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\filesystem\filesystem_interface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $supported_dbms = array(
|
||||
// Note: php 5.5 alpha 2 deprecated mysql.
|
||||
// Keep mysqli before mysql in this list.
|
||||
'mysqli' => array(
|
||||
'LABEL' => 'MySQL with MySQLi Extension',
|
||||
'SCHEMA' => 'mysql_41',
|
||||
'MODULE' => 'mysqli',
|
||||
'DELIM' => ';',
|
||||
'DRIVER' => 'phpbb\db\driver\mysqli',
|
||||
'AVAILABLE' => true,
|
||||
'2.0.x' => true,
|
||||
),
|
||||
'mssql_odbc'=> array(
|
||||
'LABEL' => 'MS SQL Server [ ODBC ]',
|
||||
'SCHEMA' => 'mssql',
|
||||
'MODULE' => 'odbc',
|
||||
'DELIM' => ';',
|
||||
'DRIVER' => 'phpbb\db\driver\mssql_odbc',
|
||||
'AVAILABLE' => true,
|
||||
'2.0.x' => true,
|
||||
),
|
||||
'mssqlnative' => array(
|
||||
'LABEL' => 'MS SQL Server 2005+ [ Native ]',
|
||||
'SCHEMA' => 'mssql',
|
||||
'MODULE' => 'sqlsrv',
|
||||
'DELIM' => ';',
|
||||
'DRIVER' => 'phpbb\db\driver\mssqlnative',
|
||||
'AVAILABLE' => true,
|
||||
'2.0.x' => false,
|
||||
),
|
||||
'oracle' => array(
|
||||
'LABEL' => 'Oracle',
|
||||
'SCHEMA' => 'oracle',
|
||||
'MODULE' => 'oci8',
|
||||
'DELIM' => ';',
|
||||
'DRIVER' => 'phpbb\db\driver\oracle',
|
||||
'AVAILABLE' => true,
|
||||
'2.0.x' => false,
|
||||
),
|
||||
'postgres' => array(
|
||||
'LABEL' => 'PostgreSQL 8.3+',
|
||||
'SCHEMA' => 'postgres',
|
||||
'MODULE' => 'pgsql',
|
||||
'DELIM' => ';',
|
||||
'DRIVER' => 'phpbb\db\driver\postgres',
|
||||
'AVAILABLE' => true,
|
||||
'2.0.x' => true,
|
||||
),
|
||||
'sqlite3' => array(
|
||||
'LABEL' => 'SQLite3',
|
||||
'SCHEMA' => 'sqlite',
|
||||
'MODULE' => 'sqlite3',
|
||||
'DELIM' => ';',
|
||||
'DRIVER' => 'phpbb\db\driver\sqlite3',
|
||||
'AVAILABLE' => true,
|
||||
'2.0.x' => false,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem interface
|
||||
* @param string $phpbb_root_path Path to phpBB's root
|
||||
*/
|
||||
public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path)
|
||||
{
|
||||
$this->filesystem = $filesystem;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of available DBMS supported by phpBB
|
||||
*
|
||||
* If a DBMS is specified it will only return data for that DBMS
|
||||
* and will load its extension if necessary.
|
||||
*
|
||||
* @param mixed $dbms name of the DBMS that's info is required or false for all DBMS info
|
||||
* @param bool $return_unavailable set it to true if you expect unavailable but supported DBMS
|
||||
* returned as well
|
||||
* @param bool $only_20x_options set it to true if you only want to recover 2.0.x options
|
||||
*
|
||||
* @return array Array of available and supported DBMS
|
||||
*/
|
||||
public function get_available_dbms($dbms = false, $return_unavailable = false, $only_20x_options = false)
|
||||
{
|
||||
$available_dbms = $this->supported_dbms;
|
||||
|
||||
if ($dbms)
|
||||
{
|
||||
if (isset($this->supported_dbms[$dbms]))
|
||||
{
|
||||
$available_dbms = array($dbms => $this->supported_dbms[$dbms]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
$any_dbms_available = false;
|
||||
foreach ($available_dbms as $db_name => $db_array)
|
||||
{
|
||||
if ($only_20x_options && !$db_array['2.0.x'])
|
||||
{
|
||||
if ($return_unavailable)
|
||||
{
|
||||
$available_dbms[$db_name]['AVAILABLE'] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($available_dbms[$db_name]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$dll = $db_array['MODULE'];
|
||||
if (!@extension_loaded($dll))
|
||||
{
|
||||
if ($return_unavailable)
|
||||
{
|
||||
$available_dbms[$db_name]['AVAILABLE'] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($available_dbms[$db_name]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$any_dbms_available = true;
|
||||
}
|
||||
|
||||
if ($return_unavailable)
|
||||
{
|
||||
$available_dbms['ANY_DB_SUPPORT'] = $any_dbms_available;
|
||||
}
|
||||
|
||||
return $available_dbms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes "/* style" as well as "# style" comments from $input.
|
||||
*
|
||||
* @param string $sql_query Input string
|
||||
*
|
||||
* @return string Input string with comments removed
|
||||
*/
|
||||
public function remove_comments($sql_query)
|
||||
{
|
||||
// Remove /* */ comments (http://ostermiller.org/findcomment.html)
|
||||
$sql_query = preg_replace('#/\*(.|[\r\n])*?\*/#', "\n", $sql_query);
|
||||
|
||||
// Remove # style comments
|
||||
$sql_query = preg_replace('/\n{2,}/', "\n", preg_replace('/^#.*$/m', "\n", $sql_query));
|
||||
|
||||
return $sql_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* split_sql_file() will split an uploaded sql file into single sql statements.
|
||||
*
|
||||
* Note: expects trim() to have already been run on $sql.
|
||||
*
|
||||
* @param string $sql SQL statements
|
||||
* @param string $delimiter Delimiter between sql statements
|
||||
*
|
||||
* @return array Array of sql statements
|
||||
*/
|
||||
public function split_sql_file($sql, $delimiter)
|
||||
{
|
||||
$sql = str_replace("\r" , '', $sql);
|
||||
$data = preg_split('/' . preg_quote($delimiter, '/') . '$/m', $sql);
|
||||
|
||||
$data = array_map('trim', $data);
|
||||
|
||||
// The empty case
|
||||
$end_data = end($data);
|
||||
|
||||
if (empty($end_data))
|
||||
{
|
||||
unset($data[key($data)]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates table prefix
|
||||
*
|
||||
* @param string $dbms The selected dbms
|
||||
* @param string $table_prefix The table prefix to validate
|
||||
*
|
||||
* @return bool|array true if table prefix is valid, array of errors otherwise
|
||||
*
|
||||
* @throws \phpbb\install\exception\invalid_dbms_exception When $dbms is not a valid
|
||||
*/
|
||||
public function validate_table_prefix($dbms, $table_prefix)
|
||||
{
|
||||
$errors = array();
|
||||
|
||||
if (!preg_match('#^[a-zA-Z][a-zA-Z0-9_]*$#', $table_prefix))
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' => 'INST_ERR_DB_INVALID_PREFIX',
|
||||
);
|
||||
}
|
||||
|
||||
// Do dbms specific checks
|
||||
$dbms_info = $this->get_available_dbms($dbms);
|
||||
switch ($dbms_info[$dbms]['SCHEMA'])
|
||||
{
|
||||
case 'mysql_41':
|
||||
$prefix_length = 36;
|
||||
break;
|
||||
case 'mssql':
|
||||
$prefix_length = 90;
|
||||
break;
|
||||
case 'oracle':
|
||||
$prefix_length = 6;
|
||||
break;
|
||||
case 'postgres':
|
||||
$prefix_length = 36;
|
||||
break;
|
||||
case 'sqlite':
|
||||
$prefix_length = 200;
|
||||
break;
|
||||
default:
|
||||
throw new invalid_dbms_exception();
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the prefix length to ensure that index names are not too long
|
||||
if (strlen($table_prefix) > $prefix_length)
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' => array('INST_ERR_PREFIX_TOO_LONG', $prefix_length),
|
||||
);
|
||||
}
|
||||
|
||||
return (empty($errors)) ? true : $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user provided database parameters are correct
|
||||
*
|
||||
* This function checks the database connection data and also checks for
|
||||
* any other problems that could cause an error during the installation
|
||||
* such as if there is any database table names conflicting.
|
||||
*
|
||||
* Note: The function assumes that $table_prefix has been already validated
|
||||
* with validate_table_prefix().
|
||||
*
|
||||
* @param string $dbms Selected database type
|
||||
* @param string $dbhost Database host address
|
||||
* @param int $dbport Database port number
|
||||
* @param string $dbuser Database username
|
||||
* @param string $dbpass Database password
|
||||
* @param string $dbname Database name
|
||||
* @param string $table_prefix Database table prefix
|
||||
*
|
||||
* @return array|bool Returns true if test is successful, array of errors otherwise
|
||||
*/
|
||||
public function check_database_connection($dbms, $dbhost, $dbport, $dbuser, $dbpass, $dbname, $table_prefix)
|
||||
{
|
||||
$dbms_info = $this->get_available_dbms($dbms);
|
||||
$dbms_info = $dbms_info[$dbms];
|
||||
$errors = array();
|
||||
|
||||
// Instantiate it and set return on error true
|
||||
/** @var \phpbb\db\driver\driver_interface $db */
|
||||
$db = new $dbms_info['DRIVER'];
|
||||
$db->sql_return_on_error(true);
|
||||
|
||||
// Check that we actually have a database name before going any further
|
||||
if (!in_array($dbms_info['SCHEMA'], array('sqlite', 'oracle'), true) && $dbname === '')
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' => 'INST_ERR_DB_NO_NAME',
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure we don't have a daft user who thinks having the SQLite database in the forum directory is a good idea
|
||||
if ($dbms_info['SCHEMA'] === 'sqlite'
|
||||
&& stripos($this->filesystem->realpath($dbhost), $this->filesystem->realpath($this->phpbb_root_path) === 0))
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' =>'INST_ERR_DB_FORUM_PATH',
|
||||
);
|
||||
}
|
||||
|
||||
// Check if SQLite database is writable
|
||||
if ($dbms_info['SCHEMA'] === 'sqlite'
|
||||
&& (($this->filesystem->exists($dbhost) && !$this->filesystem->is_writable($dbhost)) || !$this->filesystem->is_writable(pathinfo($dbhost, PATHINFO_DIRNAME))))
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' =>'INST_ERR_DB_NO_WRITABLE',
|
||||
);
|
||||
}
|
||||
|
||||
// Try to connect to db
|
||||
if (is_array($db->sql_connect($dbhost, $dbuser, $dbpass, $dbname, $dbport, false, true)))
|
||||
{
|
||||
$db_error = $db->sql_error();
|
||||
$errors[] = array(
|
||||
'title' => 'INST_ERR_DB_CONNECT',
|
||||
'description' => ($db_error['message']) ? utf8_convert_message($db_error['message']) : 'INST_ERR_DB_NO_ERROR',
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if there is any table name collisions
|
||||
$temp_prefix = strtolower($table_prefix);
|
||||
$table_ary = array(
|
||||
$temp_prefix . 'attachments',
|
||||
$temp_prefix . 'config',
|
||||
$temp_prefix . 'sessions',
|
||||
$temp_prefix . 'topics',
|
||||
$temp_prefix . 'users',
|
||||
);
|
||||
|
||||
$db_tools_factory = new \phpbb\db\tools\factory();
|
||||
$db_tools = $db_tools_factory->get($db);
|
||||
$tables = $db_tools->sql_list_tables();
|
||||
$tables = array_map('strtolower', $tables);
|
||||
$table_intersect = array_intersect($tables, $table_ary);
|
||||
|
||||
if (count($table_intersect))
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' => 'INST_ERR_PREFIX',
|
||||
);
|
||||
}
|
||||
|
||||
// Check if database version is supported
|
||||
switch ($dbms)
|
||||
{
|
||||
case 'sqlite3':
|
||||
if (version_compare($db->sql_server_info(true), '3.6.15', '<'))
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' => 'INST_ERR_DB_NO_SQLITE3',
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'oracle':
|
||||
$sql = "SELECT *
|
||||
FROM NLS_DATABASE_PARAMETERS
|
||||
WHERE PARAMETER = 'NLS_RDBMS_VERSION'
|
||||
OR PARAMETER = 'NLS_CHARACTERSET'";
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
while ($row = $db->sql_fetchrow($result))
|
||||
{
|
||||
$stats[$row['parameter']] = $row['value'];
|
||||
}
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
if (version_compare($stats['NLS_RDBMS_VERSION'], '9.2', '<') && $stats['NLS_CHARACTERSET'] !== 'UTF8')
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' => 'INST_ERR_DB_NO_ORACLE',
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'postgres':
|
||||
$sql = "SHOW server_encoding;";
|
||||
$result = $db->sql_query($sql);
|
||||
$row = $db->sql_fetchrow($result);
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
if ($row['server_encoding'] !== 'UNICODE' && $row['server_encoding'] !== 'UTF8')
|
||||
{
|
||||
$errors[] = array(
|
||||
'title' => 'INST_ERR_DB_NO_POSTGRES',
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (empty($errors)) ? true : $errors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\helper\iohandler;
|
||||
|
||||
/**
|
||||
* Input-Output handler interface for the installer
|
||||
*/
|
||||
interface iohandler_interface
|
||||
{
|
||||
/**
|
||||
* Renders or returns response message
|
||||
*
|
||||
* @param bool $no_more_output Whether or not there will be more output in this output unit
|
||||
*/
|
||||
public function send_response($no_more_output = false);
|
||||
|
||||
/**
|
||||
* Returns input variable
|
||||
*
|
||||
* @param string $name Name of the input variable to obtain
|
||||
* @param mixed $default A default value that is returned if the variable was not set.
|
||||
* This function will always return a value of the same type as the default.
|
||||
* @param bool $multibyte If $default is a string this parameter has to be true if the variable may contain any UTF-8 characters
|
||||
* Default is false, causing all bytes outside the ASCII range (0-127) to be replaced with question marks
|
||||
*
|
||||
* @return mixed Value of the input variable
|
||||
*/
|
||||
public function get_input($name, $default, $multibyte = false);
|
||||
|
||||
/**
|
||||
* Returns raw input variable
|
||||
*
|
||||
* @param string $name Name of the input variable to obtain
|
||||
* @param mixed $default A default value that is returned if the variable was not set.
|
||||
* This function will always return a value of the same type as the default.
|
||||
*
|
||||
* @return mixed Value of the raw input variable
|
||||
*/
|
||||
public function get_raw_input($name, $default);
|
||||
|
||||
/**
|
||||
* Returns server variable
|
||||
*
|
||||
* This function should work the same as request_interface::server().
|
||||
*
|
||||
* @param string $name Name of the server variable
|
||||
* @param mixed $default Default value to return when the requested variable does not exist
|
||||
*
|
||||
* @return mixed Value of the server variable
|
||||
*/
|
||||
public function get_server_variable($name, $default = '');
|
||||
|
||||
/**
|
||||
* Wrapper function for request_interface::header()
|
||||
*
|
||||
* @param string $name Name of the request header variable
|
||||
* @param mixed $default Default value to return when the requested variable does not exist
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_header_variable($name, $default = '');
|
||||
|
||||
/**
|
||||
* Returns true if the connection is encrypted
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_secure();
|
||||
|
||||
/**
|
||||
* Adds an error message to the rendering queue
|
||||
*
|
||||
* Note: When an array is passed into the parameters below, it will be
|
||||
* resolved as printf($param[0], $param[1], ...).
|
||||
*
|
||||
* @param string|array $error_title Title of the error message.
|
||||
* @param string|bool|array $error_description Description of the error (and possibly guidelines to resolve it),
|
||||
* or false if the error description is not available.
|
||||
*/
|
||||
public function add_error_message($error_title, $error_description = false);
|
||||
|
||||
/**
|
||||
* Adds a warning message to the rendering queue
|
||||
*
|
||||
* Note: When an array is passed into the parameters below, it will be
|
||||
* resolved as printf($param[0], $param[1], ...).
|
||||
*
|
||||
* @param string|array $warning_title Title of the warning message
|
||||
* @param string|bool|array $warning_description Description of the warning (and possibly guidelines to resolve it),
|
||||
* or false if the warning description is not available
|
||||
*/
|
||||
public function add_warning_message($warning_title, $warning_description = false);
|
||||
|
||||
/**
|
||||
* Adds a log message to the rendering queue
|
||||
*
|
||||
* Note: When an array is passed into the parameters below, it will be
|
||||
* resolved as printf($param[0], $param[1], ...).
|
||||
*
|
||||
* @param string|array $log_title Title of the log message
|
||||
* @param string|bool|array $log_description Description of the log,
|
||||
* or false if the log description is not available
|
||||
*/
|
||||
public function add_log_message($log_title, $log_description = false);
|
||||
|
||||
/**
|
||||
* Adds a success message to the rendering queue
|
||||
*
|
||||
* Note: When an array is passed into the parameters below, it will be
|
||||
* resolved as printf($param[0], $param[1], ...).
|
||||
*
|
||||
* @param string|array $success_title Title of the success message
|
||||
* @param string|bool|array $success_description Description of the success,
|
||||
* or false if the success description is not available
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function add_success_message($success_title, $success_description = false);
|
||||
|
||||
/**
|
||||
* Adds a requested data group to the rendering queue
|
||||
*
|
||||
* @param string $title Language variable with the title of the form
|
||||
* @param array $form An array describing the required data (options etc)
|
||||
*/
|
||||
public function add_user_form_group($title, $form);
|
||||
|
||||
/**
|
||||
* Returns the rendering information for the form
|
||||
*
|
||||
* @param string $title Language variable with the title of the form
|
||||
* @param array $form An array describing the required data (options etc)
|
||||
*
|
||||
* @return string Information to render the form
|
||||
*/
|
||||
public function generate_form_render_data($title, $form);
|
||||
|
||||
/**
|
||||
* Sets the number of tasks belonging to the installer in the current mode.
|
||||
*
|
||||
* @param int $task_count Number of tasks
|
||||
* @param bool $restart Whether or not to restart the progress bar, false by default
|
||||
*/
|
||||
public function set_task_count($task_count, $restart = false);
|
||||
|
||||
/**
|
||||
* Sets the progress information
|
||||
*
|
||||
* @param string $task_lang_key Language key for the name of the task
|
||||
* @param int $task_number Position of the current task in the task queue
|
||||
*/
|
||||
public function set_progress($task_lang_key, $task_number);
|
||||
|
||||
/**
|
||||
* Sends refresh request to the client
|
||||
*/
|
||||
public function request_refresh();
|
||||
|
||||
/**
|
||||
* Marks stage as active in the navigation bar
|
||||
*
|
||||
* @param array $menu_path Array to the navigation elem
|
||||
*/
|
||||
public function set_active_stage_menu($menu_path);
|
||||
|
||||
/**
|
||||
* Marks stage as completed in the navigation bar
|
||||
*
|
||||
* @param array $menu_path Array to the navigation elem
|
||||
*/
|
||||
public function set_finished_stage_menu($menu_path);
|
||||
|
||||
/**
|
||||
* Finish the progress bar
|
||||
*
|
||||
* @param string $message_lang_key Language key for the message
|
||||
*/
|
||||
public function finish_progress($message_lang_key);
|
||||
|
||||
/**
|
||||
* Adds a download link
|
||||
*
|
||||
* @param string $route Route for the link
|
||||
* @param string $title Language key for the title
|
||||
* @param string|null|array $msg Language key for the message
|
||||
*/
|
||||
public function add_download_link($route, $title, $msg = null);
|
||||
|
||||
/**
|
||||
* Redirects the user to a new page
|
||||
*
|
||||
* @param string $url URL to redirect to
|
||||
* @param bool $use_ajax Whether or not to use AJAX redirect
|
||||
*/
|
||||
public function redirect($url, $use_ajax = false);
|
||||
|
||||
/**
|
||||
* Renders the status of update files
|
||||
*
|
||||
* @param array $status_array Array containing files in groups to render
|
||||
*/
|
||||
public function render_update_file_status($status_array);
|
||||
|
||||
/**
|
||||
* Sends and sets cookies
|
||||
*
|
||||
* @param string $cookie_name Name of the cookie to set
|
||||
* @param string $cookie_value Value of the cookie to set
|
||||
*/
|
||||
public function set_cookie($cookie_name, $cookie_value);
|
||||
}
|
||||
146
install/update/new/phpbb/install/installer_configuration.php
Normal file
146
install/update/new/phpbb/install/installer_configuration.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
class installer_configuration implements ConfigurationInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Generates the configuration tree builder.
|
||||
*
|
||||
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
|
||||
*/
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder();
|
||||
$rootNode = $treeBuilder->root('installer');
|
||||
$rootNode
|
||||
->children()
|
||||
->arrayNode('admin')
|
||||
->children()
|
||||
->scalarNode('name')->defaultValue('admin')->cannotBeEmpty()->end()
|
||||
->scalarNode('password')->defaultValue('adminadmin')->cannotBeEmpty()->end()
|
||||
->scalarNode('email')->defaultValue('admin@example.org')->cannotBeEmpty()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('board')
|
||||
->children()
|
||||
->scalarNode('lang')
|
||||
->defaultValue('en')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('name')
|
||||
->defaultValue('My Board')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('description')
|
||||
->defaultValue('My amazing new phpBB board')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('database')
|
||||
->children()
|
||||
->scalarNode('dbms')
|
||||
->defaultValue('sqlite3')
|
||||
->cannotBeEmpty()
|
||||
->isRequired()
|
||||
->end()
|
||||
->scalarNode('dbhost')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('dbport')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('dbuser')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('dbpasswd')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('dbname')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('table_prefix')
|
||||
->defaultValue('phpbb_')
|
||||
->cannotBeEmpty()
|
||||
->isRequired()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('email')
|
||||
->canBeEnabled()
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->booleanNode('smtp_delivery')
|
||||
->defaultValue(false)
|
||||
->treatNullLike(false)
|
||||
->end()
|
||||
->scalarNode('smtp_host')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('smtp_port')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('smtp_auth')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('smtp_user')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->scalarNode('smtp_pass')
|
||||
->defaultValue(null)
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('server')
|
||||
->children()
|
||||
->booleanNode('cookie_secure')
|
||||
->defaultValue(false)
|
||||
->treatNullLike(false)
|
||||
->end()
|
||||
->scalarNode('server_protocol')
|
||||
->defaultValue('http://')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->booleanNode('force_server_vars')
|
||||
->defaultValue(false)
|
||||
->treatNullLike(false)
|
||||
->end()
|
||||
->scalarNode('server_name')
|
||||
->defaultValue('localhost')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->integerNode('server_port')
|
||||
->defaultValue(80)
|
||||
->min(1)
|
||||
->end()
|
||||
->scalarNode('script_path')
|
||||
->defaultValue('/')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('extensions')
|
||||
->prototype('scalar')->end()
|
||||
->defaultValue([])
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\module\install_database\task;
|
||||
|
||||
use phpbb\install\exception\resource_limit_reached_exception;
|
||||
|
||||
/**
|
||||
* Create database schema
|
||||
*/
|
||||
class add_config_settings extends \phpbb\install\task_base
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var \phpbb\filesystem\filesystem_interface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\config
|
||||
*/
|
||||
protected $install_config;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\iohandler\iohandler_interface
|
||||
*/
|
||||
protected $iohandler;
|
||||
|
||||
/**
|
||||
* @var \phpbb\language\language
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* @var \phpbb\passwords\manager
|
||||
*/
|
||||
protected $password_manager;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $config_table;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $user_table;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $topics_table;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $forums_table;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $posts_table;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $moderator_cache_table;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem service
|
||||
* @param \phpbb\install\helper\config $install_config Installer's config helper
|
||||
* @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
|
||||
* @param \phpbb\install\helper\container_factory $container Installer's DI container
|
||||
* @param \phpbb\language\language $language Language service
|
||||
* @param string $phpbb_root_path Path to phpBB's root
|
||||
*/
|
||||
public function __construct(\phpbb\filesystem\filesystem_interface $filesystem,
|
||||
\phpbb\install\helper\config $install_config,
|
||||
\phpbb\install\helper\iohandler\iohandler_interface $iohandler,
|
||||
\phpbb\install\helper\container_factory $container,
|
||||
\phpbb\language\language $language,
|
||||
$phpbb_root_path)
|
||||
{
|
||||
$this->db = $container->get('dbal.conn');
|
||||
$this->filesystem = $filesystem;
|
||||
$this->install_config = $install_config;
|
||||
$this->iohandler = $iohandler;
|
||||
$this->language = $language;
|
||||
$this->password_manager = $container->get('passwords.manager');
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
|
||||
// Table names
|
||||
$this->config_table = $container->get_parameter('tables.config');
|
||||
$this->forums_table = $container->get_parameter('tables.forums');
|
||||
$this->topics_table = $container->get_parameter('tables.topics');
|
||||
$this->user_table = $container->get_parameter('tables.users');
|
||||
$this->moderator_cache_table = $container->get_parameter('tables.moderator_cache');
|
||||
$this->posts_table = $container->get_parameter('tables.posts');
|
||||
|
||||
parent::__construct(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->db->sql_return_on_error(true);
|
||||
|
||||
$server_name = $this->install_config->get('server_name');
|
||||
$current_time = time();
|
||||
$user_ip = phpbb_ip_normalise($this->iohandler->get_server_variable('REMOTE_ADDR'));
|
||||
$user_ip = ($user_ip === false) ? '' : $user_ip;
|
||||
$referer = $this->iohandler->get_server_variable('REFERER');
|
||||
|
||||
// Calculate cookie domain
|
||||
$cookie_domain = $server_name;
|
||||
|
||||
if (strpos($cookie_domain, 'www.') === 0)
|
||||
{
|
||||
$cookie_domain = substr($cookie_domain, 3);
|
||||
}
|
||||
|
||||
// Set default config and post data, this applies to all DB's
|
||||
$sql_ary = array(
|
||||
'INSERT INTO ' . $this->config_table . " (config_name, config_value)
|
||||
VALUES ('board_startdate', '$current_time')",
|
||||
|
||||
'INSERT INTO ' . $this->config_table . " (config_name, config_value)
|
||||
VALUES ('default_lang', '" . $this->db->sql_escape($this->install_config->get('default_lang')) . "')",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('server_name')) . "'
|
||||
WHERE config_name = 'server_name'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('server_port')) . "'
|
||||
WHERE config_name = 'server_port'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_email')) . "'
|
||||
WHERE config_name = 'board_email'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_email')) . "'
|
||||
WHERE config_name = 'board_contact'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($cookie_domain) . "'
|
||||
WHERE config_name = 'cookie_domain'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->language->lang('default_dateformat')) . "'
|
||||
WHERE config_name = 'default_dateformat'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('email_enable')) . "'
|
||||
WHERE config_name = 'email_enable'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_delivery')) . "'
|
||||
WHERE config_name = 'smtp_delivery'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_host')) . "'
|
||||
WHERE config_name = 'smtp_host'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_port')) . "'
|
||||
WHERE config_name = 'smtp_port'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_auth')) . "'
|
||||
WHERE config_name = 'smtp_auth_method'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_user')) . "'
|
||||
WHERE config_name = 'smtp_username'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_pass')) . "'
|
||||
WHERE config_name = 'smtp_password'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('cookie_secure')) . "'
|
||||
WHERE config_name = 'cookie_secure'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('force_server_vars')) . "'
|
||||
WHERE config_name = 'force_server_vars'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('script_path')) . "'
|
||||
WHERE config_name = 'script_path'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('server_protocol')) . "'
|
||||
WHERE config_name = 'server_protocol'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
|
||||
WHERE config_name = 'newest_username'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . md5(mt_rand()) . "'
|
||||
WHERE config_name = 'avatar_salt'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . md5(mt_rand()) . "'
|
||||
WHERE config_name = 'plupload_salt'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_name')) . "'
|
||||
WHERE config_name = 'sitename'",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_description')) . "'
|
||||
WHERE config_name = 'site_desc'",
|
||||
|
||||
'UPDATE ' . $this->user_table . "
|
||||
SET username = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "',
|
||||
user_password='" . $this->password_manager->hash($this->install_config->get('admin_passwd')) . "',
|
||||
user_ip = '" . $this->db->sql_escape($user_ip) . "',
|
||||
user_lang = '" . $this->db->sql_escape($this->install_config->get('user_language', 'en')) . "',
|
||||
user_email='" . $this->db->sql_escape($this->install_config->get('board_email')) . "',
|
||||
user_dateformat='" . $this->db->sql_escape($this->language->lang('default_dateformat')) . "',
|
||||
username_clean = '" . $this->db->sql_escape(utf8_clean_string($this->install_config->get('admin_name'))) . "'
|
||||
WHERE username = 'Admin'",
|
||||
|
||||
'UPDATE ' . $this->moderator_cache_table . "
|
||||
SET username = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
|
||||
WHERE username = 'Admin'",
|
||||
|
||||
'UPDATE ' . $this->forums_table . "
|
||||
SET forum_last_poster_name = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
|
||||
WHERE forum_last_poster_name = 'Admin'",
|
||||
|
||||
'UPDATE ' . $this->topics_table . "
|
||||
SET topic_first_poster_name = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "',
|
||||
topic_last_poster_name = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
|
||||
WHERE topic_first_poster_name = 'Admin'
|
||||
OR topic_last_poster_name = 'Admin'",
|
||||
|
||||
'UPDATE ' . $this->user_table . "
|
||||
SET user_regdate = $current_time",
|
||||
|
||||
'UPDATE ' . $this->posts_table . "
|
||||
SET post_time = $current_time, poster_ip = '" . $this->db->sql_escape($user_ip) . "'",
|
||||
|
||||
'UPDATE ' . $this->topics_table . "
|
||||
SET topic_time = $current_time, topic_last_post_time = $current_time",
|
||||
|
||||
'UPDATE ' . $this->forums_table . "
|
||||
SET forum_last_post_time = $current_time",
|
||||
|
||||
'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($this->db->sql_server_info(true)) . "'
|
||||
WHERE config_name = 'dbms_version'",
|
||||
);
|
||||
|
||||
if (@extension_loaded('gd'))
|
||||
{
|
||||
$sql_ary[] = 'UPDATE ' . $this->config_table . "
|
||||
SET config_value = 'core.captcha.plugins.gd'
|
||||
WHERE config_name = 'captcha_plugin'";
|
||||
|
||||
$sql_ary[] = 'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '1'
|
||||
WHERE config_name = 'captcha_gd'";
|
||||
}
|
||||
|
||||
$ref = substr($referer, strpos($referer, '://') + 3);
|
||||
if (!(stripos($ref, $server_name) === 0))
|
||||
{
|
||||
$sql_ary[] = 'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '0'
|
||||
WHERE config_name = 'referer_validation'";
|
||||
}
|
||||
|
||||
// We set a (semi-)unique cookie name to bypass login issues related to the cookie name.
|
||||
$cookie_name = 'phpbb3_';
|
||||
$rand_str = md5(mt_rand());
|
||||
$rand_str = str_replace('0', 'z', base_convert($rand_str, 16, 35));
|
||||
$rand_str = substr($rand_str, 0, 5);
|
||||
$cookie_name .= strtolower($rand_str);
|
||||
|
||||
$sql_ary[] = 'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '" . $this->db->sql_escape($cookie_name) . "'
|
||||
WHERE config_name = 'cookie_name'";
|
||||
|
||||
// Disable avatars if upload directory is not writable
|
||||
if (!$this->filesystem->is_writable($this->phpbb_root_path . 'images/avatars/upload/'))
|
||||
{
|
||||
$sql_ary[] = 'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '0'
|
||||
WHERE config_name = 'allow_avatar'";
|
||||
|
||||
$sql_ary[] = 'UPDATE ' . $this->config_table . "
|
||||
SET config_value = '0'
|
||||
WHERE config_name = 'allow_avatar_upload'";
|
||||
}
|
||||
|
||||
$i = $this->install_config->get('add_config_settings_index', 0);
|
||||
$total = count($sql_ary);
|
||||
$sql_ary = array_slice($sql_ary, $i);
|
||||
|
||||
foreach ($sql_ary as $sql)
|
||||
{
|
||||
if (!$this->db->sql_query($sql))
|
||||
{
|
||||
$error = $this->db->sql_error($this->db->get_sql_error_sql());
|
||||
$this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
|
||||
}
|
||||
|
||||
$i++;
|
||||
|
||||
// Stop execution if resource limit is reached
|
||||
if ($this->install_config->get_time_remaining() <= 0 || $this->install_config->get_memory_remaining() <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($i < $total)
|
||||
{
|
||||
$this->install_config->set('add_config_settings_index', $i);
|
||||
throw new resource_limit_reached_exception();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function get_step_count()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_task_lang_name()
|
||||
{
|
||||
return 'TASK_ADD_CONFIG_SETTINGS';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\module\install_database\task;
|
||||
|
||||
use phpbb\install\exception\resource_limit_reached_exception;
|
||||
|
||||
/**
|
||||
* Create database schema
|
||||
*/
|
||||
class create_schema extends \phpbb\install\task_base
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\install\helper\config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var \phpbb\db\tools\tools_interface
|
||||
*/
|
||||
protected $db_tools;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\database
|
||||
*/
|
||||
protected $database_helper;
|
||||
|
||||
/**
|
||||
* @var \phpbb\filesystem\filesystem_interface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\iohandler\iohandler_interface
|
||||
*/
|
||||
protected $iohandler;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\install\helper\config $config Installer's config provider
|
||||
* @param \phpbb\install\helper\database $db_helper Installer's database helper
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem service
|
||||
* @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
|
||||
* @param string $phpbb_root_path Path phpBB's root
|
||||
* @param string $php_ext Extension of PHP files
|
||||
*/
|
||||
public function __construct(\phpbb\install\helper\config $config,
|
||||
\phpbb\install\helper\database $db_helper,
|
||||
\phpbb\filesystem\filesystem_interface $filesystem,
|
||||
\phpbb\install\helper\iohandler\iohandler_interface $iohandler,
|
||||
$phpbb_root_path,
|
||||
$php_ext)
|
||||
{
|
||||
$dbms = $db_helper->get_available_dbms($config->get('dbms'));
|
||||
$dbms = $dbms[$config->get('dbms')]['DRIVER'];
|
||||
$factory = new \phpbb\db\tools\factory();
|
||||
|
||||
$this->db = new $dbms();
|
||||
$this->db->sql_connect(
|
||||
$config->get('dbhost'),
|
||||
$config->get('dbuser'),
|
||||
$config->get('dbpasswd'),
|
||||
$config->get('dbname'),
|
||||
$config->get('dbport'),
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
$this->config = $config;
|
||||
$this->db_tools = $factory->get($this->db);
|
||||
$this->database_helper = $db_helper;
|
||||
$this->filesystem = $filesystem;
|
||||
$this->iohandler = $iohandler;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
parent::__construct(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// As this task may take a large amount of time to complete refreshing the page might be necessary for some
|
||||
// server configurations with limited resources
|
||||
if (!$this->config->get('pre_schema_forced_refresh'))
|
||||
{
|
||||
if ($this->config->get_time_remaining() < 5)
|
||||
{
|
||||
$this->config->set('pre_schema_forced_refresh', true);
|
||||
throw new resource_limit_reached_exception();
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->sql_return_on_error(true);
|
||||
|
||||
$dbms = $this->config->get('dbms');
|
||||
$dbms_info = $this->database_helper->get_available_dbms($dbms);
|
||||
$schema_name = $dbms_info[$dbms]['SCHEMA'];
|
||||
$delimiter = $dbms_info[$dbms]['DELIM'];
|
||||
$table_prefix = $this->config->get('table_prefix');
|
||||
|
||||
if ($dbms === 'mysql')
|
||||
{
|
||||
$schema_name .= '_41';
|
||||
}
|
||||
|
||||
$db_schema_path = $this->phpbb_root_path . 'install/schemas/' . $schema_name . '_schema.sql';
|
||||
|
||||
// Load database vendor specific code if there is any
|
||||
if ($this->filesystem->exists($db_schema_path))
|
||||
{
|
||||
$sql_query = @file_get_contents($db_schema_path);
|
||||
$sql_query = preg_replace('#phpbb_#i', $table_prefix, $sql_query);
|
||||
$sql_query = $this->database_helper->remove_comments($sql_query);
|
||||
$sql_query = $this->database_helper->split_sql_file($sql_query, $delimiter);
|
||||
|
||||
foreach ($sql_query as $sql)
|
||||
{
|
||||
if (!$this->db->sql_query($sql))
|
||||
{
|
||||
$error = $this->db->sql_error($this->db->get_sql_error_sql());
|
||||
$this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
|
||||
}
|
||||
}
|
||||
|
||||
unset($sql_query);
|
||||
}
|
||||
|
||||
$change_prefix = false;
|
||||
|
||||
// Generate database schema
|
||||
if ($this->filesystem->exists($this->phpbb_root_path . 'install/schemas/schema.json'))
|
||||
{
|
||||
$db_table_schema = @file_get_contents($this->phpbb_root_path . 'install/schemas/schema.json');
|
||||
$db_table_schema = json_decode($db_table_schema, true);
|
||||
$change_prefix = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
global $table_prefix;
|
||||
|
||||
$table_prefix = $this->config->get('table_prefix');
|
||||
|
||||
if (!defined('CONFIG_TABLE'))
|
||||
{
|
||||
// We need to include the constants file for the table constants
|
||||
// when we generate the schema from the migration files.
|
||||
include ($this->phpbb_root_path . 'includes/constants.' . $this->php_ext);
|
||||
}
|
||||
|
||||
$finder = new \phpbb\finder($this->filesystem, $this->phpbb_root_path, null, $this->php_ext);
|
||||
$migrator_classes = $finder->core_path('phpbb/db/migration/data/')->get_classes();
|
||||
$factory = new \phpbb\db\tools\factory();
|
||||
$db_tools = $factory->get($this->db, true);
|
||||
$schema_generator = new \phpbb\db\migration\schema_generator(
|
||||
$migrator_classes,
|
||||
new \phpbb\config\config(array()),
|
||||
$this->db,
|
||||
$db_tools,
|
||||
$this->phpbb_root_path,
|
||||
$this->php_ext,
|
||||
$table_prefix
|
||||
);
|
||||
$db_table_schema = $schema_generator->get_schema();
|
||||
}
|
||||
|
||||
if (!defined('CONFIG_TABLE'))
|
||||
{
|
||||
// CONFIG_TABLE is required by sql_create_index() to check the
|
||||
// length of index names. However table_prefix is not defined
|
||||
// here yet, so we need to create the constant ourselves.
|
||||
define('CONFIG_TABLE', $table_prefix . 'config');
|
||||
}
|
||||
|
||||
foreach ($db_table_schema as $table_name => $table_data)
|
||||
{
|
||||
$this->db_tools->sql_create_table(
|
||||
( ($change_prefix) ? ($table_prefix . substr($table_name, 6)) : $table_name ),
|
||||
$table_data
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function get_step_count()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_task_lang_name()
|
||||
{
|
||||
return 'TASK_CREATE_DATABASE_SCHEMA';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\module\install_database\task;
|
||||
|
||||
/**
|
||||
* Set up database for table generation
|
||||
*/
|
||||
class set_up_database extends \phpbb\install\task_base
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\install\helper\config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\database
|
||||
*/
|
||||
protected $database_helper;
|
||||
|
||||
/**
|
||||
* @var \phpbb\filesystem\filesystem_interface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\iohandler\iohandler_interface
|
||||
*/
|
||||
protected $iohandler;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $schema_file_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\install\helper\config $config
|
||||
* @param \phpbb\install\helper\database $db_helper
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem
|
||||
* @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler
|
||||
* @param string $phpbb_root_path
|
||||
*/
|
||||
public function __construct(\phpbb\install\helper\config $config,
|
||||
\phpbb\install\helper\database $db_helper,
|
||||
\phpbb\filesystem\filesystem_interface $filesystem,
|
||||
\phpbb\install\helper\iohandler\iohandler_interface $iohandler,
|
||||
$phpbb_root_path)
|
||||
{
|
||||
$dbms = $db_helper->get_available_dbms($config->get('dbms'));
|
||||
$dbms = $dbms[$config->get('dbms')]['DRIVER'];
|
||||
|
||||
$this->db = new $dbms();
|
||||
$this->db->sql_connect(
|
||||
$config->get('dbhost'),
|
||||
$config->get('dbuser'),
|
||||
$config->get('dbpasswd'),
|
||||
$config->get('dbname'),
|
||||
$config->get('dbport'),
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
$this->config = $config;
|
||||
$this->database_helper = $db_helper;
|
||||
$this->filesystem = $filesystem;
|
||||
$this->iohandler = $iohandler;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
|
||||
parent::__construct(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function check_requirements()
|
||||
{
|
||||
$dbms = $this->config->get('dbms');
|
||||
$dbms_info = $this->database_helper->get_available_dbms($dbms);
|
||||
$schema_name = $dbms_info[$dbms]['SCHEMA'];
|
||||
|
||||
if ($dbms === 'mysql')
|
||||
{
|
||||
$schema_name .= '_41';
|
||||
}
|
||||
|
||||
$this->schema_file_path = $this->phpbb_root_path . 'install/schemas/' . $schema_name . '_schema.sql';
|
||||
|
||||
return $this->filesystem->exists($this->schema_file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->db->sql_return_on_error(true);
|
||||
|
||||
$dbms = $this->config->get('dbms');
|
||||
$dbms_info = $this->database_helper->get_available_dbms($dbms);
|
||||
$delimiter = $dbms_info[$dbms]['DELIM'];
|
||||
$table_prefix = $this->config->get('table_prefix');
|
||||
|
||||
$sql_query = @file_get_contents($this->schema_file_path);
|
||||
$sql_query = preg_replace('#phpbb_#i', $table_prefix, $sql_query);
|
||||
$sql_query = $this->database_helper->remove_comments($sql_query);
|
||||
$sql_query = $this->database_helper->split_sql_file($sql_query, $delimiter);
|
||||
|
||||
foreach ($sql_query as $sql)
|
||||
{
|
||||
if (!$this->db->sql_query($sql))
|
||||
{
|
||||
$error = $this->db->sql_error($this->db->get_sql_error_sql());
|
||||
$this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
|
||||
}
|
||||
}
|
||||
|
||||
unset($sql_query);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function get_step_count()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_task_lang_name()
|
||||
{
|
||||
return 'TASK_SETUP_DATABASE';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\module\install_filesystem\task;
|
||||
|
||||
use phpbb\install\exception\user_interaction_required_exception;
|
||||
|
||||
/**
|
||||
* Dumps config file
|
||||
*/
|
||||
class create_config_file extends \phpbb\install\task_base
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\filesystem\filesystem_interface
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\database
|
||||
*/
|
||||
protected $db_helper;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\config
|
||||
*/
|
||||
protected $install_config;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\iohandler\iohandler_interface
|
||||
*/
|
||||
protected $iohandler;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\filesystem\filesystem_interface $filesystem
|
||||
* @param \phpbb\install\helper\config $install_config
|
||||
* @param \phpbb\install\helper\database $db_helper
|
||||
* @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler
|
||||
* @param string $phpbb_root_path
|
||||
* @param string $php_ext
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(\phpbb\filesystem\filesystem_interface $filesystem,
|
||||
\phpbb\install\helper\config $install_config,
|
||||
\phpbb\install\helper\database $db_helper,
|
||||
\phpbb\install\helper\iohandler\iohandler_interface $iohandler,
|
||||
$phpbb_root_path,
|
||||
$php_ext,
|
||||
$options = array())
|
||||
{
|
||||
$this->install_config = $install_config;
|
||||
$this->db_helper = $db_helper;
|
||||
$this->filesystem = $filesystem;
|
||||
$this->iohandler = $iohandler;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->options = array_merge(array(
|
||||
'debug' => false,
|
||||
'debug_container' => false,
|
||||
'environment' => null,
|
||||
), $options);
|
||||
|
||||
parent::__construct(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$config_written = true;
|
||||
|
||||
// Create config.php
|
||||
$path_to_config = $this->phpbb_root_path . 'config.' . $this->php_ext;
|
||||
|
||||
$fp = @fopen($path_to_config, 'w');
|
||||
if (!$fp)
|
||||
{
|
||||
$config_written = false;
|
||||
}
|
||||
|
||||
$config_content = $this->get_config_data($this->options['debug'], $this->options['debug_container'], $this->options['environment']);
|
||||
|
||||
if (!@fwrite($fp, $config_content))
|
||||
{
|
||||
$config_written = false;
|
||||
}
|
||||
|
||||
@fclose($fp);
|
||||
|
||||
// chmod config.php to be only readable
|
||||
if ($config_written)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->filesystem->phpbb_chmod($path_to_config, \phpbb\filesystem\filesystem_interface::CHMOD_READ);
|
||||
}
|
||||
catch (\phpbb\filesystem\exception\filesystem_exception $e)
|
||||
{
|
||||
// Do nothing, the user will get a notice later
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->iohandler->add_error_message('UNABLE_TO_WRITE_CONFIG_FILE');
|
||||
throw new user_interaction_required_exception();
|
||||
}
|
||||
|
||||
// Create a lock file to indicate that there is an install in progress
|
||||
$fp = @fopen($this->phpbb_root_path . 'cache/install_lock', 'wb');
|
||||
if ($fp === false)
|
||||
{
|
||||
// We were unable to create the lock file - abort
|
||||
$this->iohandler->add_error_message('UNABLE_TO_WRITE_LOCK');
|
||||
throw new user_interaction_required_exception();
|
||||
}
|
||||
@fclose($fp);
|
||||
|
||||
try
|
||||
{
|
||||
$this->filesystem->phpbb_chmod($this->phpbb_root_path . 'cache/install_lock', 0777);
|
||||
}
|
||||
catch (\phpbb\filesystem\exception\filesystem_exception $e)
|
||||
{
|
||||
// Do nothing, the user will get a notice later
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content which should be dumped to config.php
|
||||
*
|
||||
* @param bool $debug If the debug constants should be enabled by default or not
|
||||
* @param bool $debug_container If the container should be compiled on
|
||||
* every page load or not
|
||||
* @param string $environment The environment to use
|
||||
*
|
||||
* @return string content to be written to the config file
|
||||
*/
|
||||
protected function get_config_data($debug = false, $debug_container = false, $environment = null)
|
||||
{
|
||||
$config_content = "<?php\n";
|
||||
$config_content .= "// phpBB 3.3.x auto-generated configuration file\n// Do not change anything in this file!\n";
|
||||
|
||||
$dbms = $this->install_config->get('dbms');
|
||||
$db_driver = $this->db_helper->get_available_dbms($dbms);
|
||||
$db_driver = $db_driver[$dbms]['DRIVER'];
|
||||
|
||||
$config_data_array = array(
|
||||
'dbms' => $db_driver,
|
||||
'dbhost' => $this->install_config->get('dbhost'),
|
||||
'dbport' => $this->install_config->get('dbport'),
|
||||
'dbname' => $this->install_config->get('dbname'),
|
||||
'dbuser' => $this->install_config->get('dbuser'),
|
||||
'dbpasswd' => $this->install_config->get('dbpasswd'),
|
||||
'table_prefix' => $this->install_config->get('table_prefix'),
|
||||
|
||||
'phpbb_adm_relative_path' => 'adm/',
|
||||
|
||||
'acm_type' => 'phpbb\cache\driver\file',
|
||||
);
|
||||
|
||||
foreach ($config_data_array as $key => $value)
|
||||
{
|
||||
$config_content .= "\${$key} = '" . str_replace("'", "\\'", str_replace('\\', '\\\\', $value)) . "';\n";
|
||||
}
|
||||
|
||||
$config_content .= "\n@define('PHPBB_INSTALLED', true);\n";
|
||||
|
||||
if ($environment)
|
||||
{
|
||||
$config_content .= "@define('PHPBB_ENVIRONMENT', 'test');\n";
|
||||
}
|
||||
else if ($debug)
|
||||
{
|
||||
$config_content .= "@define('PHPBB_ENVIRONMENT', 'development');\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$config_content .= "@define('PHPBB_ENVIRONMENT', 'production');\n";
|
||||
}
|
||||
|
||||
if ($debug_container)
|
||||
{
|
||||
$config_content .= "@define('DEBUG_CONTAINER', true);\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$config_content .= "// @define('DEBUG_CONTAINER', true);\n";
|
||||
}
|
||||
|
||||
if ($environment === 'test')
|
||||
{
|
||||
$config_content .= "@define('DEBUG_TEST', true);\n";
|
||||
|
||||
// Mandatory for the functional tests, will be removed by PHPBB3-12623
|
||||
$config_content .= "@define('DEBUG', true);\n";
|
||||
}
|
||||
|
||||
return $config_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function get_step_count()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_task_lang_name()
|
||||
{
|
||||
return 'TASK_CREATE_CONFIG_FILE';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install\module\requirements\task;
|
||||
|
||||
/**
|
||||
* Installer task that checks if the server meats phpBB requirements
|
||||
*/
|
||||
class check_server_environment extends \phpbb\install\task_base
|
||||
{
|
||||
/**
|
||||
* @var \phpbb\install\helper\database
|
||||
*/
|
||||
protected $database_helper;
|
||||
|
||||
/**
|
||||
* @var \phpbb\install\helper\iohandler\iohandler_interface
|
||||
*/
|
||||
protected $response_helper;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $tests_passed;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \phpbb\install\helper\database $database_helper
|
||||
* @param \phpbb\install\helper\iohandler\iohandler_interface $response
|
||||
*/
|
||||
public function __construct(\phpbb\install\helper\database $database_helper,
|
||||
\phpbb\install\helper\iohandler\iohandler_interface $response)
|
||||
{
|
||||
$this->database_helper = $database_helper;
|
||||
$this->response_helper = $response;
|
||||
$this->tests_passed = true;
|
||||
|
||||
parent::__construct(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
//
|
||||
// Check requirements
|
||||
// The error messages should be set in the check_ functions
|
||||
//
|
||||
|
||||
// Check PHP version
|
||||
$this->check_php_version();
|
||||
|
||||
// Check for getimagesize()
|
||||
$this->check_image_size();
|
||||
|
||||
// Check for PCRE support
|
||||
$this->check_pcre();
|
||||
|
||||
// Check for JSON support
|
||||
$this->check_json();
|
||||
|
||||
// XML extension support check
|
||||
$this->check_xml();
|
||||
|
||||
// Check for dbms support
|
||||
$this->check_available_dbms();
|
||||
|
||||
return $this->tests_passed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets $this->tests_passed
|
||||
*
|
||||
* @param bool $is_passed
|
||||
*/
|
||||
protected function set_test_passed($is_passed)
|
||||
{
|
||||
// If one test failed, tests_passed should be false
|
||||
$this->tests_passed = (!$this->tests_passed) ? false : $is_passed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the requirements for PHP version is met
|
||||
*/
|
||||
protected function check_php_version()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '7.1.3', '<'))
|
||||
{
|
||||
$this->response_helper->add_error_message('PHP_VERSION_REQD', 'PHP_VERSION_REQD_EXPLAIN');
|
||||
|
||||
$this->set_test_passed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_test_passed(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the installed PHP has getimagesize() available
|
||||
*/
|
||||
protected function check_image_size()
|
||||
{
|
||||
if (!@function_exists('getimagesize'))
|
||||
{
|
||||
$this->response_helper->add_error_message('PHP_GETIMAGESIZE_SUPPORT', 'PHP_GETIMAGESIZE_SUPPORT_EXPLAIN');
|
||||
|
||||
$this->set_test_passed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_test_passed(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the installed PHP supports PCRE
|
||||
*/
|
||||
protected function check_pcre()
|
||||
{
|
||||
if (@preg_match('//u', ''))
|
||||
{
|
||||
$this->set_test_passed(true);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response_helper->add_error_message('PCRE_UTF_SUPPORT', 'PCRE_UTF_SUPPORT_EXPLAIN');
|
||||
|
||||
$this->set_test_passed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether PHP's JSON extension is available or not
|
||||
*/
|
||||
protected function check_json()
|
||||
{
|
||||
if (@extension_loaded('json'))
|
||||
{
|
||||
$this->set_test_passed(true);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response_helper->add_error_message('PHP_JSON_SUPPORT', 'PHP_JSON_SUPPORT_EXPLAIN');
|
||||
|
||||
$this->set_test_passed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the XML PHP extension is available (Required by the text formatter)
|
||||
*/
|
||||
protected function check_xml()
|
||||
{
|
||||
if (class_exists('DOMDocument'))
|
||||
{
|
||||
$this->set_test_passed(true);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response_helper->add_error_message('PHP_XML_SUPPORT', 'PHP_XML_SUPPORT_EXPLAIN');
|
||||
|
||||
$this->set_test_passed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any supported DBMS is available
|
||||
*/
|
||||
protected function check_available_dbms()
|
||||
{
|
||||
$available_dbms = $this->database_helper->get_available_dbms(false, true);
|
||||
|
||||
if ($available_dbms['ANY_DB_SUPPORT'])
|
||||
{
|
||||
$this->set_test_passed(true);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response_helper->add_error_message('PHP_SUPPORTED_DB', 'PHP_SUPPORTED_DB_EXPLAIN');
|
||||
|
||||
$this->set_test_passed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function get_step_count()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_task_lang_name()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
213
install/update/new/phpbb/install/module_base.php
Normal file
213
install/update/new/phpbb/install/module_base.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\install;
|
||||
|
||||
use phpbb\di\ordered_service_collection;
|
||||
use phpbb\install\exception\resource_limit_reached_exception;
|
||||
use phpbb\install\helper\config;
|
||||
use phpbb\install\helper\iohandler\iohandler_interface;
|
||||
|
||||
/**
|
||||
* Base class for installer module
|
||||
*/
|
||||
abstract class module_base implements module_interface
|
||||
{
|
||||
/**
|
||||
* @var config
|
||||
*/
|
||||
protected $install_config;
|
||||
|
||||
/**
|
||||
* @var iohandler_interface
|
||||
*/
|
||||
protected $iohandler;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_essential;
|
||||
|
||||
/**
|
||||
* Array of tasks for installer module
|
||||
*
|
||||
* @var ordered_service_collection
|
||||
*/
|
||||
protected $task_collection;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $task_step_count;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $allow_progress_bar;
|
||||
|
||||
/**
|
||||
* Installer module constructor
|
||||
*
|
||||
* @param ordered_service_collection $tasks array of installer tasks for installer module
|
||||
* @param bool $essential flag indicating whether the module is essential or not
|
||||
* @param bool $allow_progress_bar flag indicating whether or not to send progress information from within the module
|
||||
*/
|
||||
public function __construct(ordered_service_collection $tasks, $essential = true, $allow_progress_bar = true)
|
||||
{
|
||||
$this->task_collection = $tasks;
|
||||
$this->is_essential = $essential;
|
||||
$this->allow_progress_bar = $allow_progress_bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependency getter
|
||||
*
|
||||
* @param config $config
|
||||
* @param iohandler_interface $iohandler
|
||||
*/
|
||||
public function setup(config $config, iohandler_interface $iohandler)
|
||||
{
|
||||
$this->install_config = $config;
|
||||
$this->iohandler = $iohandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_essential()
|
||||
{
|
||||
return $this->is_essential;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Overwrite this method if your task is non-essential!
|
||||
*/
|
||||
public function check_requirements()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// Recover install progress
|
||||
$task_index = $this->recover_progress();
|
||||
$iterator = $this->task_collection->getIterator();
|
||||
|
||||
if ($task_index < $iterator->count())
|
||||
{
|
||||
$iterator->seek($task_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->install_config->set_finished_task(0);
|
||||
return;
|
||||
}
|
||||
|
||||
while ($iterator->valid())
|
||||
{
|
||||
$task = $iterator->current();
|
||||
$name = $iterator->key();
|
||||
|
||||
// Check if we can run the task
|
||||
if (!$task->is_essential() && !$task->check_requirements())
|
||||
{
|
||||
$this->iohandler->add_log_message(array(
|
||||
'SKIP_TASK',
|
||||
$name,
|
||||
));
|
||||
|
||||
$this->install_config->increment_current_task_progress($this->task_step_count[$name] ?? false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send progress information
|
||||
if ($this->allow_progress_bar)
|
||||
{
|
||||
$this->iohandler->set_progress(
|
||||
$task->get_task_lang_name(),
|
||||
$this->install_config->get_current_task_progress()
|
||||
);
|
||||
|
||||
$this->iohandler->send_response();
|
||||
}
|
||||
|
||||
$task->run();
|
||||
|
||||
if ($this->allow_progress_bar)
|
||||
{
|
||||
// Only increment progress by one, as if a task has more than one steps
|
||||
// then that should be incremented in the task itself
|
||||
$this->install_config->increment_current_task_progress();
|
||||
}
|
||||
}
|
||||
|
||||
$task_index++;
|
||||
$this->install_config->set_finished_task($task_index);
|
||||
$iterator->next();
|
||||
|
||||
// Send progress information
|
||||
if ($this->allow_progress_bar)
|
||||
{
|
||||
$this->iohandler->set_progress(
|
||||
$task->get_task_lang_name(),
|
||||
$this->install_config->get_current_task_progress()
|
||||
);
|
||||
}
|
||||
|
||||
$this->iohandler->send_response();
|
||||
|
||||
// Stop execution if resource limit is reached
|
||||
if ($iterator->valid() && ($this->install_config->get_time_remaining() <= 0 || $this->install_config->get_memory_remaining() <= 0))
|
||||
{
|
||||
throw new resource_limit_reached_exception();
|
||||
}
|
||||
}
|
||||
|
||||
// Module finished, so clear task progress
|
||||
$this->install_config->set_finished_task(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next task's name
|
||||
*
|
||||
* @return string Index of the array element of the next task
|
||||
*/
|
||||
protected function recover_progress()
|
||||
{
|
||||
$progress_array = $this->install_config->get_progress_data();
|
||||
return $progress_array['last_task_index'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_step_count()
|
||||
{
|
||||
$task_step_count = 0;
|
||||
$task_class_names = $this->task_collection->get_service_classes();
|
||||
|
||||
foreach ($task_class_names as $name => $task_class)
|
||||
{
|
||||
$step_count = $task_class::get_step_count();
|
||||
$task_step_count += $step_count;
|
||||
$this->task_step_count[$name] = $step_count;
|
||||
}
|
||||
|
||||
return $task_step_count;
|
||||
}
|
||||
}
|
||||
207
install/update/new/phpbb/language/language_file_loader.php
Normal file
207
install/update/new/phpbb/language/language_file_loader.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\language;
|
||||
|
||||
use \phpbb\language\exception\language_file_not_found;
|
||||
|
||||
/**
|
||||
* Language file loader
|
||||
*/
|
||||
class language_file_loader
|
||||
{
|
||||
/**
|
||||
* @var string Path to phpBB's root
|
||||
*/
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/**
|
||||
* @var string Extension of PHP files
|
||||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* @var \phpbb\extension\manager Extension manager
|
||||
*/
|
||||
protected $extension_manager;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $phpbb_root_path Path to phpBB's root
|
||||
* @param string $php_ext Extension of PHP files
|
||||
*/
|
||||
public function __construct($phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
$this->extension_manager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension manager setter
|
||||
*
|
||||
* @param \phpbb\extension\manager $extension_manager Extension manager
|
||||
*/
|
||||
public function set_extension_manager(\phpbb\extension\manager $extension_manager)
|
||||
{
|
||||
$this->extension_manager = $extension_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads language array for the given component
|
||||
*
|
||||
* @param string $component Name of the language component
|
||||
* @param string|array $locale ISO code of the language to load, or array of ISO codes if you want to
|
||||
* specify additional language fallback steps
|
||||
* @param array $lang Array reference containing language strings
|
||||
*/
|
||||
public function load($component, $locale, &$lang)
|
||||
{
|
||||
$locale = (array) $locale;
|
||||
|
||||
// Determine path to language directory
|
||||
$path = $this->phpbb_root_path . 'language/';
|
||||
|
||||
$this->load_file($path, $component, $locale, $lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads language array for the given extension component
|
||||
*
|
||||
* @param string $extension Name of the extension
|
||||
* @param string $component Name of the language component
|
||||
* @param string|array $locale ISO code of the language to load, or array of ISO codes if you want to
|
||||
* specify additional language fallback steps
|
||||
* @param array $lang Array reference containing language strings
|
||||
*/
|
||||
public function load_extension($extension, $component, $locale, &$lang)
|
||||
{
|
||||
// Check if extension manager was loaded
|
||||
if ($this->extension_manager === null)
|
||||
{
|
||||
// If not, let's return
|
||||
return;
|
||||
}
|
||||
|
||||
$locale = (array) $locale;
|
||||
|
||||
// Determine path to language directory
|
||||
$path = $this->extension_manager->get_extension_path($extension, true) . 'language/';
|
||||
|
||||
$this->load_file($path, $component, $locale, $lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares language file loading
|
||||
*
|
||||
* @param string $path Path to search for file in
|
||||
* @param string $component Name of the language component
|
||||
* @param array $locale Array containing language fallback options
|
||||
* @param array $lang Array reference of language strings
|
||||
*/
|
||||
protected function load_file($path, $component, $locale, &$lang)
|
||||
{
|
||||
// This is BC stuff and not the best idea as it makes language fallback
|
||||
// implementation quite hard like below.
|
||||
if (strpos($this->phpbb_root_path . $component, $path) === 0)
|
||||
{
|
||||
// Filter out the path
|
||||
$path_diff = str_replace($path, '', dirname($this->phpbb_root_path . $component));
|
||||
$language_file = basename($component, '.' . $this->php_ext);
|
||||
$component = '';
|
||||
|
||||
// This step is needed to resolve language/en/subdir style $component
|
||||
// $path already points to the language base directory so we need to eliminate
|
||||
// the first directory from the path (that should be the language directory)
|
||||
$path_diff_parts = explode('/', $path_diff);
|
||||
|
||||
if (count($path_diff_parts) > 1)
|
||||
{
|
||||
array_shift($path_diff_parts);
|
||||
$component = implode('/', $path_diff_parts) . '/';
|
||||
}
|
||||
|
||||
$component .= $language_file;
|
||||
}
|
||||
|
||||
// Determine filename
|
||||
$filename = $component . '.' . $this->php_ext;
|
||||
|
||||
// Determine path to file
|
||||
$file_path = $this->get_language_file_path($path, $filename, $locale);
|
||||
|
||||
// Load language array
|
||||
$this->load_language_file($file_path, $lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function implements language fallback logic
|
||||
*
|
||||
* @param string $path Path to language directory
|
||||
* @param string $filename Filename to load language strings from
|
||||
* @param array $locales Array containing language fallback options
|
||||
*
|
||||
* @return string Relative path to language file
|
||||
*
|
||||
* @throws language_file_not_found When the path to the file cannot be resolved
|
||||
*/
|
||||
protected function get_language_file_path($path, $filename, $locales)
|
||||
{
|
||||
$language_file_path = $filename;
|
||||
|
||||
// Language fallback logic
|
||||
foreach ($locales as $locale)
|
||||
{
|
||||
$language_file_path = $path . $locale . '/' . $filename;
|
||||
|
||||
// If we are in install, try to use the updated version, when available
|
||||
if (defined('IN_INSTALL'))
|
||||
{
|
||||
$install_language_path = str_replace('language/', 'install/update/new/language/', $language_file_path);
|
||||
if (file_exists($install_language_path))
|
||||
{
|
||||
return $install_language_path;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($language_file_path))
|
||||
{
|
||||
return $language_file_path;
|
||||
}
|
||||
}
|
||||
|
||||
// The language file is not exist
|
||||
throw new language_file_not_found('Language file ' . $language_file_path . ' couldn\'t be opened.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads language file
|
||||
*
|
||||
* @param string $path Path to language file to load
|
||||
* @param array $lang Reference of the array of language strings
|
||||
*/
|
||||
protected function load_language_file($path, &$lang)
|
||||
{
|
||||
// Do not suppress error if in DEBUG mode
|
||||
if (defined('DEBUG'))
|
||||
{
|
||||
include $path;
|
||||
}
|
||||
else
|
||||
{
|
||||
@include $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
153
install/update/new/phpbb/lock/db.php
Normal file
153
install/update/new/phpbb/lock/db.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\lock;
|
||||
|
||||
/**
|
||||
* Database locking class
|
||||
*/
|
||||
class db
|
||||
{
|
||||
/**
|
||||
* Name of the config variable this lock uses
|
||||
* @var string
|
||||
*/
|
||||
private $config_name;
|
||||
|
||||
/**
|
||||
* Unique identifier for this lock.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $unique_id;
|
||||
|
||||
/**
|
||||
* Stores the state of this lock
|
||||
* @var bool
|
||||
*/
|
||||
private $locked;
|
||||
|
||||
/**
|
||||
* The phpBB configuration
|
||||
* @var \phpbb\config\config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* A database connection
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Creates a named released instance of the lock.
|
||||
*
|
||||
* You have to call acquire() to actually create the lock.
|
||||
*
|
||||
* @param string $config_name A config variable to be used for locking
|
||||
* @param \phpbb\config\config $config The phpBB configuration
|
||||
* @param \phpbb\db\driver\driver_interface $db A database connection
|
||||
*/
|
||||
public function __construct($config_name, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db)
|
||||
{
|
||||
$this->config_name = $config_name;
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to acquire the lock by updating
|
||||
* the configuration variable in the database.
|
||||
*
|
||||
* As a lock may only be held by one process at a time, lock
|
||||
* acquisition may fail if another process is holding the lock
|
||||
* or if another process obtained the lock but never released it.
|
||||
* Locks are forcibly released after a timeout of 1 hour.
|
||||
*
|
||||
* @return bool true if lock was acquired
|
||||
* false otherwise
|
||||
*/
|
||||
public function acquire()
|
||||
{
|
||||
if ($this->locked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($this->config[$this->config_name]))
|
||||
{
|
||||
$this->config->set($this->config_name, '0', false);
|
||||
}
|
||||
$lock_value = $this->config[$this->config_name];
|
||||
|
||||
// make sure lock cannot be acquired by multiple processes
|
||||
if ($lock_value)
|
||||
{
|
||||
// if the other process is running more than an hour already we have to assume it
|
||||
// aborted without cleaning the lock
|
||||
$time = explode(' ', $lock_value);
|
||||
$time = $time[0];
|
||||
|
||||
if ($time + 3600 >= time())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->unique_id = time() . ' ' . unique_id();
|
||||
|
||||
// try to update the config value, if it was already modified by another
|
||||
// process we failed to acquire the lock.
|
||||
$this->locked = $this->config->set_atomic($this->config_name, $lock_value, $this->unique_id, false);
|
||||
|
||||
if ($this->locked == true)
|
||||
{
|
||||
if ($this->config->ensure_lock($this->config_name, $this->unique_id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this process own the lock?
|
||||
*
|
||||
* @return bool true if lock is owned
|
||||
* false otherwise
|
||||
*/
|
||||
public function owns_lock()
|
||||
{
|
||||
return (bool) $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the lock.
|
||||
*
|
||||
* The lock must have been previously obtained, that is, acquire() call
|
||||
* was issued and returned true.
|
||||
*
|
||||
* Note: Attempting to release a lock that is already released,
|
||||
* that is, calling release() multiple times, is harmless.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function release()
|
||||
{
|
||||
if ($this->locked)
|
||||
{
|
||||
$this->config->set_atomic($this->config_name, $this->unique_id, '0', false);
|
||||
$this->locked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
144
install/update/new/phpbb/lock/flock.php
Normal file
144
install/update/new/phpbb/lock/flock.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\lock;
|
||||
|
||||
/**
|
||||
* File locking class
|
||||
*/
|
||||
class flock
|
||||
{
|
||||
/**
|
||||
* Path to the file to which access is controlled
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* File pointer for the lock file
|
||||
* @var string
|
||||
*/
|
||||
private $lock_fp;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* You have to call acquire() to actually acquire the lock.
|
||||
*
|
||||
* @param string $path Path to the file to which access is controlled
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->lock_fp = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to acquire the lock.
|
||||
*
|
||||
* If the lock is already held by another process, this call will block
|
||||
* until the other process releases the lock. If a lock is acquired and
|
||||
* is not released before script finishes but the process continues to
|
||||
* live (apache/fastcgi) then subsequent processes trying to acquire
|
||||
* the same lock will be blocked forever.
|
||||
*
|
||||
* If the lock is already held by the same process via another instance
|
||||
* of this class, this call will block forever.
|
||||
*
|
||||
* If flock function is disabled in php or fails to work, lock
|
||||
* acquisition will fail and false will be returned.
|
||||
*
|
||||
* @return bool true if lock was acquired
|
||||
* false otherwise
|
||||
*/
|
||||
public function acquire()
|
||||
{
|
||||
if ($this->lock_fp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For systems that can't have two processes opening
|
||||
// one file for writing simultaneously
|
||||
if (file_exists($this->path . '.lock'))
|
||||
{
|
||||
$mode = 'rb';
|
||||
}
|
||||
else
|
||||
{
|
||||
$mode = 'wb';
|
||||
}
|
||||
|
||||
$this->lock_fp = @fopen($this->path . '.lock', $mode);
|
||||
|
||||
if ($mode == 'wb')
|
||||
{
|
||||
if (!$this->lock_fp)
|
||||
{
|
||||
// Two processes may attempt to create lock file at the same time.
|
||||
// Have the losing process try opening the lock file again for reading
|
||||
// on the assumption that the winning process created it
|
||||
$mode = 'rb';
|
||||
$this->lock_fp = @fopen($this->path . '.lock', $mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only need to set mode when the lock file is written
|
||||
@chmod($this->path . '.lock', 0666);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->lock_fp)
|
||||
{
|
||||
if (!@flock($this->lock_fp, LOCK_EX))
|
||||
{
|
||||
throw new \phpbb\exception\http_exception(500, 'Failure while aqcuiring locks.');
|
||||
}
|
||||
}
|
||||
|
||||
return (bool) $this->lock_fp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this process own the lock?
|
||||
*
|
||||
* @return bool true if lock is owned
|
||||
* false otherwise
|
||||
*/
|
||||
public function owns_lock()
|
||||
{
|
||||
return (bool) $this->lock_fp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the lock.
|
||||
*
|
||||
* The lock must have been previously obtained, that is, acquire() call
|
||||
* was issued and returned true.
|
||||
*
|
||||
* Note: Attempting to release a lock that is already released,
|
||||
* that is, calling release() multiple times, is harmless.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function release()
|
||||
{
|
||||
if ($this->lock_fp)
|
||||
{
|
||||
@flock($this->lock_fp, LOCK_UN);
|
||||
fclose($this->lock_fp);
|
||||
$this->lock_fp = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user