summaryrefslogtreecommitdiff
path: root/db_version
diff options
context:
space:
mode:
Diffstat (limited to 'db_version')
-rw-r--r--db_version/CHANGELOG21
-rw-r--r--db_version/LICENSE84
-rw-r--r--db_version/README8
-rw-r--r--db_version/SQL/mssql.initial.sql5
-rw-r--r--db_version/SQL/mysql.initial.sql13
-rw-r--r--db_version/SQL/pgsql.initial.sql12
-rw-r--r--db_version/SQL/sqlite.initial.sql12
-rw-r--r--db_version/db_version.php474
-rw-r--r--db_version/package.xml22
-rw-r--r--db_version/skins/classic/sql.html31
-rw-r--r--db_version/skins/larry/sql.html48
11 files changed, 730 insertions, 0 deletions
diff --git a/db_version/CHANGELOG b/db_version/CHANGELOG
new file mode 100644
index 0000000..a93f7c2
--- /dev/null
+++ b/db_version/CHANGELOG
@@ -0,0 +1,21 @@
+VERSION COMMENT
+-----------------------------------------------------------------------------------------------------------------
+1.0 - Initial release
+2.0 - Completely recoded from scratch
+2.0.1 - 2.0.2 - GUI improvements
+2.0.3 - 2.0.5 - Fix for *NIX OS (workaround for case sensitivity of SQL folder)
+2.0.6 - Added sqlite3 to database map
+2.0.7 - Fixed Roundcube version detection
+2.0.8 - 2.0.10 - Prepare for Roundcube 1.0
+3.0 - Support for sqlite (https://code.google.com/p/myroundcube/issues/detail?id=624);
+3.1 - Better support for database prefixes
+3.1.1 - Implement rcube_db::factory
+3.1.2 - Implement rcube_db::limitquery
+3.1.3 - 3.1.4 - Don't load plugin if database access to system table fails (should avoid to display
+ database update prompt to "normal" users in case of database connection failures)
+3.1.5 - Don't exit for plugin.plugin_manager_update_notifications action
+3.1.6 - Make sure that db_config plugin is always loaded
+3.1.7 - Do not load plugin on Plugin Manager Update request
+3.1.8 - Always load plugins on about request
+3.1.9 - Remove reward logging (MSSQL will be deprecated)
+3.1.10 - Better RegEx for custom table names replacement \ No newline at end of file
diff --git a/db_version/LICENSE b/db_version/LICENSE
new file mode 100644
index 0000000..11b1f5e
--- /dev/null
+++ b/db_version/LICENSE
@@ -0,0 +1,84 @@
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+dev-team [at] myroundcube [dot] com
+http://myroundcube.com
+
+--- LICENSE DETAILS ---
+
+
+
+
+TERMS AND CONDITIONS
+--------------------
+
+User Agreement
+
+MyRoundcube Developers provides MyRoundcube Plugins (code) shall provide ("MyRoundcube Plugins") to
+you ("User") under the terms and conditions of this User Agreement ("the Agreement"). USER UNDERSTANDS
+AND ACKNOWLEDGES THAT USER IS ENTERING INTO AN AGREEMENT WITH MyRoundcube Developers AND NOT THE WEB
+SITE WITH WHOM ANYTHING HAS ASSOCIATED TO BRING YOU THIS CODE. BY USING ("MyRoundcube Plugins") OR ANY
+PART OF ITS CODE YOU ARE AGREEING TO BECOME A PARTY TO THIS AGREEMENT WITH MyRoundcube Developers AND
+TO THE TERMS AND CONDITIONS HEREIN AND ACKNOWLEDGE THAT YOU HAVE READ AND UNDERSTAND ANY APPLICABLE
+ASSOCIATE STATEMENT IN THIS DOCUMENT. ALL MyRoundcube Developers SERVICES ARE PROVIDED ONLINE. PERSONS
+UNDER 13 MAY NOT BE ELEGIBLE TO ENTERING INTO AN AGREEMENT WITH MyRoundcube Developers OR PURCHASE OUR
+SERVICES AND CODE DOWNLOADS.
+
+Acceptable Use Policy
+
+The following policy governs the use of the MyRoundcube Developers code. User will comply with the terms
+and spirit of the Agreement.
+
+(a) User shall not use MyRoundcube Plugins (code) in a manner that violates any city, state, national
+or international law or regulation, or which fails to comply with accepted Internet protocol. User
+shall not attempt to interfere in any way with MyRoundcube Plugins networks or network security, or
+attempt to use the MyRoundcube Plugins code to gain unauthorized access to any other computer system.
+(b) User shall at all times provide MyRoundcube Developers with accurate information. User shall not
+interfere in any way with another User's use of, or MyRoundcube Developers provision of the MyRoundcube
+Plugins. User shall not resell, rent, lease, grant a security interest in, or make commercial use of
+the MyRoundcube Plugins without the express written consent of MyRoundcube Developers.
+(c) User agrees not to transfer MyRoundcube Plugins (code) for gain or otherwise. Transfer of such code
+will result in termination of contract with end user.
+
+Limitation of Liability
+
+UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, TORT, CONTRACT, OR OTHERWISE, SHALL MyRoundcube
+Developers OR ITS LICENSORS OR RESELLERS BE LIABLE TO USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL,
+INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES RESULTING
+FROM THE USE OF OR THE INABILITY TO USE THE MyRoundcube Plugins (code), THE PERFORMANCE OF MyRoundcube
+Plugins (code) SERVICE, OR DAMAGES FOR LOSS OF GOODWILL, BUSINESS PROFIT, BUSINESS STOPPAGE, LOSS OF
+DATA OR BUSINESS INFORMATION, COMPUTER DAMAGE, OR DAMAGES RESULTING FROM UNAUTHORIZED ACCESS TO OR
+CHANGES MADE TO USER'S TRANSMISSIONS OR DATA, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES.
+IN NO EVENT WILL MyRoundcube Developers BE LIABLE FOR ANY DAMAGES IN EXCESS OF WHAT ANYTHING MyRoundcube
+Developers RECEIVED FROM USER FOR THE MyRoundcube Plugins (code).
+
+Terms
+
+You (the User) are permitted to use the code on unlimited servers you may own, rented or leased, as
+long as you own, rent or lease the server in which MyRoundcube plugins code is hosted.
+Exclusive: You (the User) agree to use the code in one server at a time. Multiple server deployments
+(multiserver setup), clusters or any other form of deployment that simultaneously executes MyRoundcube
+plugins in a live environment must purchase a separate download per server or installation thereof.
+
+Warranties
+
+Our code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+Terms Modifications
+
+We reserve the right to change or modify our Terms and Conditions at any time without prior notice.
+For questions, please contact our team at dev-team [at] myroundcube [dot] com.
+
+Copyright (c) 2012 - 2014
+MyRoundcube.com - A Division of Informative Computing Consultants, LLC.
+All rights reserved
+
+Informative Computing Consultants, LLC.
+21741 NW 8th CT
+Pembroke Pines
+Florida, 33029
+dev-team [at] myroundcube [dot] com
+http://myroundcube.com
diff --git a/db_version/README b/db_version/README
new file mode 100644
index 0000000..1530f52
--- /dev/null
+++ b/db_version/README
@@ -0,0 +1,8 @@
+db_version
+----------
+Documentation:
+http://myroundcube.com/myroundcube-plugins/
+
+
+MyRoundcube Dev Team
+www.myroundcube.com \ No newline at end of file
diff --git a/db_version/SQL/mssql.initial.sql b/db_version/SQL/mssql.initial.sql
new file mode 100644
index 0000000..80261cf
--- /dev/null
+++ b/db_version/SQL/mssql.initial.sql
@@ -0,0 +1,5 @@
+CREATE TABLE [dbo].[system] (
+ [name] [varchar] (64) COLLATE Latin1_General_CI_AI NOT NULL ,
+ [value] [text] COLLATE Latin1_General_CI_AI NOT NULL
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
+GO
diff --git a/db_version/SQL/mysql.initial.sql b/db_version/SQL/mysql.initial.sql
new file mode 100644
index 0000000..bb10b47
--- /dev/null
+++ b/db_version/SQL/mysql.initial.sql
@@ -0,0 +1,13 @@
+CREATE TABLE IF NOT EXISTS `myrc` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `user_id` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `user_id` (`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+ALTER TABLE `myrc`
+ ADD CONSTRAINT `myrc_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE;
+
+CREATE INDEX myrc_idx ON myrc (`user_id`);
+
+DROP TABLE `myrc`; \ No newline at end of file
diff --git a/db_version/SQL/pgsql.initial.sql b/db_version/SQL/pgsql.initial.sql
new file mode 100644
index 0000000..d7b974e
--- /dev/null
+++ b/db_version/SQL/pgsql.initial.sql
@@ -0,0 +1,12 @@
+CREATE TABLE IF NOT EXISTS myrc (
+ id serial NOT NULL,
+ user_id integer NOT NULL,
+ PRIMARY KEY (id)
+);
+
+ALTER TABLE myrc
+ ADD CONSTRAINT myrc_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE;
+
+CREATE INDEX myrc_idx ON myrc (user_id);
+
+DROP TABLE myrc; \ No newline at end of file
diff --git a/db_version/SQL/sqlite.initial.sql b/db_version/SQL/sqlite.initial.sql
new file mode 100644
index 0000000..776c0e4
--- /dev/null
+++ b/db_version/SQL/sqlite.initial.sql
@@ -0,0 +1,12 @@
+PRAGMA foreign_keys = ON;
+
+CREATE TABLE IF NOT EXISTS myrc (
+ id serial NOT NULL,
+ user_id integer NOT NULL,
+ PRIMARY KEY (id),
+ FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE INDEX myrc_idx ON myrc (user_id);
+
+DROP TABLE myrc; \ No newline at end of file
diff --git a/db_version/db_version.php b/db_version/db_version.php
new file mode 100644
index 0000000..6b43e98
--- /dev/null
+++ b/db_version/db_version.php
@@ -0,0 +1,474 @@
+<?php
+#
+# This file is part of MyRoundcube "db_version" plugin.
+#
+# This file is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Copyright (c) 2012 - 2015 Roland 'Rosali' Liebl
+# dev-team [at] myroundcube [dot] com
+# http://myroundcube.com
+#
+class db_version extends rcube_plugin{
+
+ static private $done = false;
+ static private $map = array('mysql' => 'MySQL', 'pgsql' => 'PostGreSQL', 'sqlite' => 'Sqlite v.3', 'mssql' => 'MSSQL');
+
+ /* unified plugin properties */
+ static private $plugin = 'db_version';
+ static private $author = 'myroundcube@mail4us.net';
+ static private $authors_comments = '<a href="http://myroundcube.com/myroundcube-plugins/helper-plugin?db_version" target="_blank">Documentation</a>';
+ static private $download = 'http://myroundcube.googlecode.com';
+ static private $version = '3.1.10';
+ static private $date = '21-01-2015';
+ static private $licence = 'All Rights reserved';
+ static private $requirements = array(
+ 'Roundcube' => '1.0',
+ 'PHP' => '5.3',
+ 'required_plugins' => array(
+ 'codemirror_ui' => 'require_plugin',
+ ),
+ );
+ static private $prefs = array(
+ );
+ static private $config_dist = null;
+ static private $db_map = array(
+ 'sqlite' => 'sqlite',
+ 'sqlite2' => 'sqlite',
+ 'sqlite3' => 'sqlite',
+ 'sybase' => 'mssql',
+ 'dblib' => 'mssql',
+ 'sqlsrv' => 'mssql',
+ 'mssql' => 'mssql',
+ 'mysql' => 'mysql',
+ 'mysqli' => 'mysql',
+ 'pgsql' => 'pgsql',
+ 'postgresql' => 'pgsql',
+ );
+ static private $default_tables = array(
+ 'users',
+ 'attachments',
+ 'cache',
+ 'cache_index',
+ 'cache_thread',
+ 'cache_tables',
+ 'cache_messages',
+ 'cache_shared',
+ 'contactgroupmembers',
+ 'contactgroups',
+ 'contacts',
+ 'dictionary',
+ 'identities',
+ 'searches',
+ 'system'
+ );
+ static private $f;
+
+ function init(){
+ self::$f = $this;
+ }
+
+ static public function about($keys = false){
+ $requirements = self::$requirements;
+ foreach(array('required_', 'recommended_') as $prefix){
+ if(is_array($requirements[$prefix.'plugins'])){
+ foreach($requirements[$prefix.'plugins'] as $plugin => $method){
+ if(class_exists($plugin) && method_exists($plugin, 'about')){
+ /* PHP 5.2.x workaround for $plugin::about() */
+ $class = new $plugin(false);
+ $requirements[$prefix.'plugins'][$plugin] = array(
+ 'method' => $method,
+ 'plugin' => $class->about($keys),
+ );
+ }
+ else{
+ $requirements[$prefix.'plugins'][$plugin] = array(
+ 'method' => $method,
+ 'plugin' => $plugin,
+ );
+ }
+ }
+ }
+ }
+ return array(
+ 'plugin' => self::$plugin,
+ 'version' => self::$version,
+ 'date' => self::$date,
+ 'author' => self::$author,
+ 'comments' => self::$authors_comments,
+ 'licence' => self::$licence,
+ 'requirements' => $requirements,
+ );
+ }
+
+ function form($p){
+ return self::$out;
+ }
+
+ static public function exec($plugin, $tables, $dbversion){
+ $rcmail = rcmail::get_instance();
+ /*
+ if($rcmail->action == 'plugin.plugin_manager_update' || $rcmail->action == 'about' || $rcmail->action == 'alive' || $rcmail->action == 'refresh'){
+ return true;
+ }
+ */
+ if($rcmail->action == 'about'){ // Do not force database adjustments on about request
+ return true;
+ }
+ if(get_input_value('_load_pm_settings', RCUBE_INPUT_GET)){
+ if($plugin = get_input_value('_plugin', RCUBE_INPUT_GET)){
+ $append = '&_plugin=' . $plugin;
+ }
+ else{
+ $append = '';
+ }
+ if(!self::$done){
+ self::$done = true;
+ if($rcmail->config->get('skin', 'classic') == 'classic'){
+ $frame = 'prefs-frame';
+ }
+ else{
+ $frame = 'preferences-frame';
+ }
+ $rcmail->output->add_script('$("#'. $frame . '").attr("src", "./?_task=settings&_action=edit-prefs&_section=plugin_manager_settings&_framed=1' . $append . '");', 'docready');
+ }
+ return false;
+ }
+ $return = false;
+ $dbversion = implode('|', $dbversion);
+ $query = 'SELECT * FROM ' . get_table_name('system') . ' WHERE ' . $rcmail->db->quoteIdentifier('name') . '=?';
+ $res = $rcmail->db->limitquery($query, 0, 1, 'myrc_' . $plugin);
+ $version = $rcmail->db->fetch_assoc($res);
+ if($error = $rcmail->db->is_error()){
+ write_log('db_version', $error);
+ return false;
+ }
+ if($version['value'] == $dbversion){
+ $return = true;
+ }
+ else if($plugin == 'plugin_manager' || $rcmail->config->get('plugin_manager_hash')){
+ $hash = $rcmail->config->get('plugin_manager_hash');
+ if(($plugin == 'plugin_manager' && !is_array($version)) || ($rcmail->task != 'logout' && file_exists(INSTALL_PATH . $hash . '.myrc'))){
+ if(get_input_value('_framed', RCUBE_INPUT_GPC)){
+ return false;
+ }
+ $type = current(explode(':', $rcmail->config->get('db_dsnw'), 2));
+ $type = self::$db_map[$type];
+ if(!$type){
+ if(!file_exists(slashify($rcmail->config->get('log_dir', 'logs/')) . 'dbtypefailure')){
+ write_log('dbtypefailure', 'MyRoundcube Fatal Error');
+ write_log('dbtypefailure', '-----------------------');
+ write_log('dbtypefailure', 'Failed to detect database type.');
+ write_log('dbtypefailure', 'Please contact MyRoundcube Dev Team (dev-team@myroundcube.com) and mail the following string (don\'t miss to mask password and to clear your Browser cache):');
+ write_log('dbtypefailure', ' ');
+ write_log('dbtypefailure', $rcmail->config->get('db_dsnw'));
+ }
+ $content = file_get_contents(slashify($rcmail->config->get('log_dir', 'logs/')) . 'dbtypefailure');
+ $content = explode("\n", $content);
+ $out = '';
+ foreach($content as $nb => $line){
+ $line = explode("]: ", $line);
+ $out .= $line[1] . "\n";
+ }
+ $rcmail->output->nocacheing_headers();
+ die('<html><head><title>MyRoundcube Fatal Error</title></head><body><pre>' . $out . '</pre></body></html>');
+ }
+ if(get_input_value('_remote', RCUBE_INPUT_GPC) && get_input_value('_action', RCUBE_INPUT_GPC) != 'plugin.plugin_manager_update_notifier' && !$_SESSION['db_version_lock']){
+ $db_version = explode('|', $dbversion);
+ $redirect = true;
+ $reward = false;
+ $missing = '';
+ foreach($db_version as $script){
+ if(!file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql') && !file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){
+ $rcmail->output->show_message('Plugin "' . $plugin . '": Can\'t load plugin. ' . self::$map[strtolower($type)] . ' database scripts are incomplete.', 'error');
+ $redirect = false;
+ $missing .= $plugin . ': plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql does not exist!';
+ //$reward = '--> REWARD MyRC$ 5: Create missing scripts and mail it to dev-team@myroundcube.com (include your customer ID)!';
+ }
+ }
+ if($reward && !$_SESSION['db_version_missing_logged_' . $plugin]){
+ $_SESSION['db_version_missing_logged_' . $plugin] = true;
+ write_log('errors', $missing);
+ //write_log('errors', $reward);
+ }
+ if($redirect){
+ echo json_encode(array('exec' => 'document.location.href="./"'));
+ exit;
+ }
+ }
+ $return = db_version::db_versioning($plugin, array_merge(array('system'), $tables));
+ }
+ }
+ else if($plugin == 'db_config'){
+ $return = true;
+ }
+ return $return;
+ }
+
+ static public function test_permissions($type, $db){
+ if($type == 'sqlite'){
+ return true;
+ }
+ $sql = @file_get_contents(INSTALL_PATH . 'plugins/db_version/SQL/' . $type . '.initial.sql');
+ if(!$sql){
+ $sql = @file_get_contents(INSTALL_PATH . 'plugins/db_version/sql/' . $type . '.initial.sql');
+ }
+ if($sql){
+ $sql = trim($sql) . "\r\n";
+ $sql = self::fix_table_names($sql, array('myrc'));
+ $sql = explode(';', $sql);
+ foreach($sql as $line){
+ if(trim($line)){
+ if(!$db->query($line)){
+ $command = explode(' ', $line);
+ return $command[0];
+ }
+ }
+ }
+ return true;
+ }
+ else{
+ return $type;
+ }
+ }
+
+ static public function db_versioning($plugin, $tables){
+ $rcmail = rcmail::get_instance();
+ /* PHP 5.2.x workaround for $plugin::about() */
+ $class = new $plugin(false);
+ $db_version = $class->about(array('db_version'));
+ $db_version = $db_version['db_version'];
+ if(is_array($db_version)){
+ $database = parse_url($rcmail->config->get('db_dsnw'));
+ if(!$database){
+ $database = parse_url(end(explode(':', $rcmail->config->get('db_dsnw'), 2)));
+ $database['scheme'] = current(explode(':', $rcmail->config->get('db_dsnw'), 2));
+ }
+ $type = self::$db_map[$database['scheme']];
+ $sql = 'SELECT * FROM ' . get_table_name('system') . ' WHERE ' . $rcmail->db->quote_identifier('name') . '=?';
+ $res = $rcmail->db->limitquery($sql, 0, 1, 'myrc_' . $plugin);
+ $system = $rcmail->db->fetch_assoc($res);
+ if($system){
+ $versions = explode('|', $system['value']);
+ }
+ if(implode('|', $db_version) == $system['value']){
+ return true;
+ }
+ if(count($_POST) == 0){
+ $sql = '';
+ foreach($db_version as $script){
+ if(!is_array($versions) || !in_array($script, $versions)){
+ if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')){
+ $sql .= trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')) . "\r\n";
+ }
+ else if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){
+ $sql .= trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')) . "\r\n";
+ }
+ else{
+ return false;
+ }
+ }
+ }
+ if($sql){
+ $disable = '';
+ if($plugin != 'plugin_manager'){
+ $res = $rcmail->db->limitquery('SELECT * FROM ' . get_table_name('plugin_manager') . ' WHERE ' . $rcmail->db->quote_identifier('conf') . '=?', 0, 1, '_plugin_manager_file_based_config');
+ $fb = $rcmail->db->fetch_assoc($res);
+ if($fb['value'] == 0){
+ $disable = '&nbsp;' . html::tag('input', array('onclick' => 'document.location.href="./?_task=settings&_load_pm_settings=1&_plugin=' . $plugin . '"', 'type' => 'button', 'class' => 'button', 'value' => 'Disable Plugin'));
+ }
+ }
+ if($database['user']){
+ $dsnw = $database['scheme'] . '://' . $database['user'] . ':***masked***@' . $database['host'] . $database['path'];
+ }
+ else{
+ $dsnw = $rcmail->config->get('db_dsnw');
+ }
+ $v = explode('-', RCMAIL_VERSION);
+ $v = trim($v[0]);
+ if(version_compare($v, '0.9', '>=')){
+ $out =
+ html::tag('div', array('style' => 'font-size: 12px; text-align: justify; position: absolute; margin-left: auto; left: 50%; margin-left: -400px; width: 800px;'),
+ html::tag('h3', null, 'Plugin Manager database versioning: Plugin "' . $plugin . '"') .
+ 'Plugin Manager has detected required missing adjustments in your database. The following commands will be executed against your database ' .
+ html::tag('b', null, $dsnw . ':') . html::tag('br') . html::tag('br') .
+ html::tag('textarea', array('readonly' => true, 'rows' => '18', 'cols' => '95', 'id' => 'code'), self::fix_table_names($sql, $tables)) .
+ html::tag('table', null,
+ html::tag('tr', null, html::tag('td', null, html::tag('input', array('type' => 'radio', 'name' => '_dbadjust_agreed', 'checked' => true, 'value' => 1, 'onclick' => '$("#submitbutton").show();$("#donebox").hide()')) .
+ html::tag('td', null, 'I have a recent database backup. I understand the code and I agree to execute it. '))) .
+ html::tag('tr', null, html::tag('td', null, html::tag('input', array('type' => 'radio', 'name' => '_dbadjust_agreed', 'value' => 0, 'onclick' => '$("#submitbutton").hide();$("#donebox").show()')) .
+ html::tag('td', null, 'I will take care of the necessary ' .
+ html::tag('a', array('href' => 'http://myroundcube.com/myroundcube-plugins/faqs/myroundcube-plugins-database-versioning-support', 'target' => '_new'), 'database adjustments') . ' by myself. ' .
+ 'I\'m aware that MyRoundcube "' . $plugin . '" plugin does not work without these database adjustments. ')))
+ ) .
+ html::tag('center', null, html::tag('input', array('id' => 'submitbutton', 'type' => 'submit', 'value' => 'Submit')) . $disable) . html::tag('br') .
+ html::tag('center', array('style' => 'display: none;', 'id' => 'donebox'), html::tag('input', array('type' => 'checkbox', 'onclick' => '$("#submitbutton").show();$("#donebox").hide();$(this).prop("checked", false)')) . html::tag('span', null, '&nbsp;' .
+ html::tag('b', null, 'I have unregistered this plugin or I have already applied database adjustments manually.')))
+ );
+ $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $rcmail->get_request_token()));
+ $out .= "\n" . $hidden->show();
+ $out .= html::tag('script', array('type' => 'text/javascript'), '$("#taskbar.topright").hide();');
+ $section = '';
+ if($section = get_input_value('_plugin_manager_settings_section', RCUBE_INPUT_GET)){
+ $section = '&_plugin_manager_settings_section=' . $section . '&_expand=' . $plugin;
+ }
+ $form = html::tag('form', array('method' => 'post', 'action' => './?_task=' . $rcmail->task . $section), $out);
+ }
+ else{
+ $out =
+ html::tag('div', array('style' => 'font-size: 12px; text-align: justify; position: absolute; margin-left: auto; left: 50%; margin-left: -400px; width: 800px;'),
+ html::tag('h3', null, 'Plugin Manager database versioning: Plugin "' . $plugin . '"') .
+ 'Plugin Manager has detected required missing adjustments in your database. Please execute the following commands against your database ' .
+ html::tag('b', null, $database['scheme'] . '://' . $database['user'] . ':***masked***@' . $database['host'] . $database['path'] . ':') . html::tag('br') . html::tag('br') .
+ html::tag('textarea', array('readonly' => true, 'rows' => '18', 'cols' => '95', 'id' => 'code'), self::fix_table_names($sql, $tables)) .
+ html::tag('table', null,
+ html::tag('tr', null, html::tag('td', null,
+ html::tag('td', null, 'Please backup your database. ' .
+ 'Please note, that MyRoundcube "' . $plugin . '" plugin does not work without these database adjustments.')))
+ ) .
+ html::tag('center', null, html::tag('input', array('id' => 'submitbutton', 'type' => 'submit', 'value' => 'Database has been backed up and commands have been executed.')))
+ );
+ $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $rcmail->get_request_token()));
+ $out .= "\n" . $hidden->show();
+ $out .= html::tag('script', array('type' => 'text/javascript'), '$("#taskbar.topright").hide();');
+ $section = '';
+ if($section = get_input_value('_plugin_manager_settings_section', RCUBE_INPUT_GET)){
+ $section = '&_plugin_manager_settings_section=' . $section . '&_expand=' . $plugin;
+ }
+ $form = html::tag('form', array('method' => 'post', 'action' => './?_task=' . $rcmail->task . $section), $out);
+ }
+ $out = str_replace('##FORM##', $form, @file_get_contents(INSTALL_PATH . 'plugins/db_version/skins/' . $rcmail->config->get('skin', 'classic') . '/sql.html'));
+ if(method_exists($rcmail->output, 'just_parse')){
+ send_nocacheing_headers();
+ header('Content-Type: text/html; charset=' . RCMAIL_CHARSET);
+ echo $rcmail->output->just_parse($out);
+ exit;
+ }
+ }
+ }
+ else{
+ if(get_input_value('_dbadjust_agreed', RCUBE_INPUT_POST)){
+ unset($_POST);
+ $rcmail->session->remove('db_version_lock');
+ if($dsn = $rcmail->config->get('db_dsnw_superadmin')){
+ $rcmail->db = rcube_db::factory($dsn . '?new_link=true', '', false);
+ $rcmail->db = $rcmail->db->factory($dsn . '?new_link=true');
+ $rcmail->db->set_debug((bool)$rcmail->config->get('sql_debug'));
+ $rcmail->db->db_connect('r');
+ }
+ $ret = self::test_permissions($type, $rcmail->db);
+ $sversions = is_array($system) ? $system['value'] : '';
+ $sversions = explode('|', $sversions);
+ if($ret !== true){
+ $sql = '';
+ foreach($db_version as $script){
+ if(!in_array($script, $sversions)){
+ if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')){
+ $sql .= self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')) . "\r\n", $tables);
+ }
+ else if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){
+ $sql .= self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')) . "\r\n", $tables);
+ }
+ }
+ }
+ if($ret === $type){
+ $rcmail->output->show_message('Plugin "db_version": ' . self::$map[strtolower($type)] . ' database is not supported!', 'error');
+ return false;
+ }
+ self::html_output($sql, $ret);
+ }
+ foreach($db_version as $script){
+ if(!in_array($script, $sversions)){
+ if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')){
+ $sql = self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')) . "\r\n", $tables);
+ $lines = explode(';', $sql);
+ foreach($lines as $line){
+ if(trim($line)){
+ $rcmail->db->query(trim($line));
+ }
+ }
+ }
+ else if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){
+ $sql = self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')) . "\r\n", $tables);
+ $lines = explode(';', $sql);
+ foreach($lines as $line){
+ if(trim($line)){
+ $rcmail->db->query(trim($line));
+ }
+ }
+ }
+ }
+ }
+ $sql = 'DELETE from ' . get_table_name('system') . ' WHERE ' . $rcmail->db->quoteIdentifier('name') . '=?';
+ $rcmail->db->query($sql, 'myrc_' . $plugin);
+ $sql = 'INSERT INTO ' . get_table_name('system') . ' (' . $rcmail->db->quote_identifier('name') . ', ' . $rcmail->db->quote_identifier('value') . ') VALUES(?, ?)';
+ $res = $rcmail->db->query($sql, 'myrc_' . $plugin, implode('|', $db_version));
+ if($rcmail->db->affected_rows($res)){
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ static public function html_output($script, $command = false){
+ $rcmail = rcmail::get_instance();
+ $config_var = '$config';
+ $config_file = 'config.inc.php';
+ $v = current(explode('-', RCMAIL_VERSION));
+ if(version_compare($v, '1.0', '<')){
+ $config_var = '$rcmail_config';
+ $config_file = 'db.inc.php';
+ }
+ $dbconfig = parse_url($rcmail->config->get('db_dsnw'));
+ $out = html::tag('div', array('style' => 'font-size: 12px; text-align: justify; position: absolute; margin-left: auto; left: 50%; margin-left: -400px; width: 800px;'),
+ html::tag('h3', null, sprintf('Database adjustment failed (Command: %s)', $command)) .
+ html::tag('span', null, 'The user ' .
+ html::tag('b', null, '"' . ($dbconfig['user'] ? $dbconfig['user'] : get_current_user()) . '"') .' does not seem to have sufficient permissions on the database ' .
+ html::tag('b', null, '"' . ($dbconfig['path'] ? substr($dbconfig['path'], 1) : $rcmail->config->get('db_dsnw')) . '"') . ':' . html::tag('br') . html::tag('br') .
+ ' ' . html::tag('center', null, html::tag('i', null, 'Required permissions: SELECT / INSERT / UPDATE / DELETE / CREATE / ALTER / INDEX / DROP')) .
+ html::tag('br') .
+ 'Please, grant the user sufficient permissions on the database or execute the following database script manually:') . html::tag('br') . html::tag('br') .
+ html::tag('textarea', array('readonly' => true, 'rows' => '10', 'cols' => '95', 'id' => 'code'), self::fix_table_names($script, array('plugin_manager'))) .
+ html::tag('br') . html::tag('br') .
+ html::tag('div', array('style' => $display_superadmin ? 'block' : 'none'),
+ $dbconfig['user'] ?
+ (
+ html::tag('left', null, html::tag('b', null, 'Alternatively') . ' add a different database user with sufficient permissions to your configuration in ' .
+ html::tag('b', null, '"./config/' . $config_file . '"') . ':' . html::tag('br') . html::tag('br') .
+ html::tag('textarea', array('readonly' => true, 'cols' => '95', 'rows' => '2'),
+ $config_var . '[\'db_dsnw_superadmin\'] = \'' . $dbconfig['scheme'] . '://root:password@' . $dbconfig['host'] . $dbconfig['path'] . '\';')) .
+ html::tag('br') . html::tag('br')
+ ) : ''
+ ) .
+ html::tag('div', array('style' => 'display: inline; float: left'),
+ html::tag('a', array('href' => 'javascript:void(0)', 'onclick' => 'document.location.href=\'./\''), 'Done')
+ )
+ );
+ $out .= html::tag('script', array('type' => 'text/javascript'), '$("#taskbar.topright").hide();');
+ $out = str_replace('##FORM##', $out, @file_get_contents(INSTALL_PATH . 'plugins/db_version/skins/' . $rcmail->config->get('skin', 'classic') . '/sql.html'));
+ send_nocacheing_headers();
+ header('Content-Type: text/html; charset=' . RCMAIL_CHARSET);
+ echo $rcmail->output->just_parse($out);
+ exit;
+ }
+
+ static public function fix_table_names($sql, $tables){
+ $tables = array_merge(self::$default_tables, $tables);
+ $DB = rcmail::get_instance()->db;
+ if(is_array($tables)){
+ foreach($tables as $table){
+ $real_table = $DB->table_name($table);
+ if($real_table != $table){
+ $sql = preg_replace("/([^a-z0-9_'])$table([^a-z0-9_'])/i", "\\1$real_table\\2", $sql);
+ }
+ }
+ }
+ return $sql;
+ }
+}
+?> \ No newline at end of file
diff --git a/db_version/package.xml b/db_version/package.xml
new file mode 100644
index 0000000..3710a27
--- /dev/null
+++ b/db_version/package.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+ http://pear.php.net/dtd/tasks-1.0.xsd
+ http://pear.php.net/dtd/package-2.0
+ http://pear.php.net/dtd/package-2.0.xsd">
+ <name>db_version</name>
+ <lead>
+ <name>Myroundcube Dev Team</name>
+ <user>rosali</user>
+ <email>dev-team@myroundcube.com</email>
+ <active>yes</active>
+ </lead><date>2014-07-05</date>
+ <version>
+ <release>3.1.4</release>
+ <api>3.1.4</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="http://dev.myroundcube.com/index.php?_action=plugin.plugin_server_license&amp;_plugin=db_version">All Rights reserved</license>
+</package>
diff --git a/db_version/skins/classic/sql.html b/db_version/skins/classic/sql.html
new file mode 100644
index 0000000..36a73e4
--- /dev/null
+++ b/db_version/skins/classic/sql.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<script src="program/js/jquery.min.js" type="text/javascript"></script>
+<script src="program/js/common.js" type="text/javascript"></script>
+<script src="program/js/app.js" type="text/javascript"></script>
+<script type="text/javascript">
+var rcmail = new rcube_webmail();
+</script>
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+<roundcube:if condition="env:task == 'settings'" />
+ <roundcube:include file="/includes/settingstabs.html" />
+<roundcube:endif />
+
+<div id="mainscreen">
+##FORM##
+</div>
+<script type="text/javascript">
+$(document).ready(function(){
+rcmail.init();
+$('.button-logout').attr('onclick', '');
+});
+</script>
+</body>
+</html>
diff --git a/db_version/skins/larry/sql.html b/db_version/skins/larry/sql.html
new file mode 100644
index 0000000..99cd78a
--- /dev/null
+++ b/db_version/skins/larry/sql.html
@@ -0,0 +1,48 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="plugins/codemirror_ui/lib/CodeMirror-2.3/lib/codemirror.css">
+<style type="text/css">.CodeMirror {height: 93%; border-top: 1px solid black; border-bottom: 1px solid black; .CodeMirror-scroll {height: 100%} </style>
+<script src="program/js/jquery.min.js" type="text/javascript"></script>
+<script src="program/js/common.js" type="text/javascript"></script>
+<script src="program/js/app.js" type="text/javascript"></script>
+<script type="text/javascript" src="plugins/codemirror_ui/lib/CodeMirror-2.3/lib/codemirror.js"></script>
+<script type="text/javascript" src="plugins/codemirror_ui/lib/CodeMirror-2.3/lib/util/searchcursor.js"></script>
+<script type="text/javascript" src="plugins/codemirror_ui/lib/CodeMirror-2.3/mode/mysql/mysql.js"></script>
+<script type="text/javascript">
+var rcmail = new rcube_webmail();
+</script>
+</head>
+<body class="noscroll">
+
+<roundcube:include file="/includes/header.html" />
+
+<div id="mainscreen" class="offset">
+
+<roundcube:if condition="env:task == 'settings'" />
+ <roundcube:include file="/includes/settingstabs.html" />
+<roundcube:endif />
+
+<div id="pluginbody" class="uibox contentbox">
+##FORM##
+<roundcube:object name="message" id="message" class="statusbar" />
+</div>
+</div>
+<script type="text/javascript">
+$(document).ready(function(){
+rcmail.init();
+$('.button-logout').attr('onclick', '');
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+ lineNumbers: true,
+ readOnly: true,
+ mode: "text/x-mysql",
+ tabMode: "indent",
+ matchBrackets: true
+ });
+});
+</script>
+<roundcube:include file="/includes/footer.html" />
+</body>
+</html>