From 5507db9b15034c73d9a121596da4bf440206f173 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 3 Feb 2012 20:32:06 +0000 Subject: [PATCH 001/133] Initial migration code, and basic export for bookmarks --- apps/admin_export/settings.php | 7 +- apps/bookmarks/lib/migrate.php | 18 +++++ apps/user_migrate/appinfo/app.php | 26 +++++++ apps/user_migrate/appinfo/info.xml | 11 +++ apps/user_migrate/settings.php | 95 ++++++++++++++++++++++++ apps/user_migrate/templates/settings.php | 12 +++ lib/migrate.php | 66 ++++++++++++++++ lib/migrate/provider.php | 23 ++++++ 8 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 apps/bookmarks/lib/migrate.php create mode 100644 apps/user_migrate/appinfo/app.php create mode 100644 apps/user_migrate/appinfo/info.xml create mode 100644 apps/user_migrate/settings.php create mode 100644 apps/user_migrate/templates/settings.php create mode 100644 lib/migrate.php create mode 100644 lib/migrate/provider.php diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index a33c872ccf..cafd35570c 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -5,6 +5,8 @@ * * @author Thomas Schmidt * @copyright 2011 Thomas Schmidt tom@opensuse.org + * @author Tom Needham + * @copyright 2012 Tom Needham tom@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -22,13 +24,15 @@ */ OC_Util::checkAdminUser(); OC_Util::checkAppEnabled('admin_export'); + + if (isset($_POST['admin_export'])) { $root = OC::$SERVERROOT . "/"; $zip = new ZipArchive(); $filename = get_temp_dir() . "/owncloud_export_" . date("y-m-d_H-i-s") . ".zip"; OC_Log::write('admin_export',"Creating export file at: " . $filename,OC_Log::INFO); if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) { - exit("Cannot open <$filename>\n"); + exit("Cannot open <$filename>\n"); } if (isset($_POST['owncloud_system'])) { @@ -49,6 +53,7 @@ if (isset($_POST['admin_export'])) { } if (isset($_POST['user_files'])) { + // needs to handle data outside of the default data dir. // adding user files $zip->addFile($root . '/data/.htaccess', basename($root) . "/data/.htaccess"); $zip->addFile($root . '/data/index.html', basename($root) . "/data/index.html"); diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php new file mode 100644 index 0000000000..2e6581cd9f --- /dev/null +++ b/apps/bookmarks/lib/migrate.php @@ -0,0 +1,18 @@ +appid = 'bookmarks'; + // Create the xml for the user supplied + function export($uid){ + $xml = ''; + $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmakrs.user_id = ?"); + $bookmarks = $query->execute($uid); + OC_Log::write('user_migrate',print_r($bookmarks)); + foreach($bookmarks as $bookmark){ + $xml .= ''; + $xml .='DATA WILL BE HERE'; + $xml .= ''; + } + return $xml; + } +} diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php new file mode 100644 index 0000000000..4a795a5474 --- /dev/null +++ b/apps/user_migrate/appinfo/app.php @@ -0,0 +1,26 @@ +. +* +*/ + +OC_APP::registerPersonal('user_migrate','settings'); + +?> \ No newline at end of file diff --git a/apps/user_migrate/appinfo/info.xml b/apps/user_migrate/appinfo/info.xml new file mode 100644 index 0000000000..6abcb4af92 --- /dev/null +++ b/apps/user_migrate/appinfo/info.xml @@ -0,0 +1,11 @@ + + + user_migrate + User Account Migration + Migrate your user accounts + 0.1 + AGPL + Tom Needham + 2 + + diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php new file mode 100644 index 0000000000..fbf190e37d --- /dev/null +++ b/apps/user_migrate/settings.php @@ -0,0 +1,95 @@ +. + * + */ +OC_Util::checkAppEnabled('user_migrate'); + + +if (isset($_POST['user_migrate'])) { + // Looks like they want to migrate + $errors = array(); + $root = OC::$SERVERROOT . "/"; + $user = OC_User::getUser(); + $zip = new ZipArchive(); + $tempdir = get_temp_dir(); + $filename = $tempdir . "/" . $user . "_export_" . date("y-m-d_H-i-s") . ".zip"; + OC_Log::write('user_migrate',"Creating user export file at: " . $filename,OC_Log::INFO); + if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) { + exit("Cannot open <$filename>\n"); + } + + // Does the user want to include their files? + if (isset($_POST['user_files'])) { + // needs to handle data outside of the default data dir. + // adding user files + OC_Log::write('user_migrate',"Adding owncloud user files of $user to export",OC_Log::INFO); + zipAddDir($root . "data/" . $user, $zip, true, "files/"); + } + + // Does the user want their app data? + if (isset($_POST['user_appdata'])) { + // adding owncloud system files + OC_Log::write('user_migrate',"Adding app data to user export",OC_Log::INFO); + // Call to OC_Migrate for the xml file. + $appdatafile = $tempdir . "/appdata.xml"; + $fh = fopen($appdatafile, 'w'); + $appdata = OC_Migrate::export(OC_User::getUser()); + fwrite($fh, $appdata); + $zip->addFile($appdatafile, "appdata.xml"); + fclose($fh); + } + + $zip->close(); + + header("Content-Type: application/zip"); + header("Content-Disposition: attachment; filename=" . basename($filename)); + header("Content-Length: " . filesize($filename)); + @ob_end_clean(); + readfile($filename); + unlink($filename); +} else { +// fill template + $tmpl = new OC_Template('user_migrate', 'settings'); + return $tmpl->fetchPage(); +} + +function zipAddDir($dir, $zip, $recursive=true, $internalDir='') { + $dirname = basename($dir); + $zip->addEmptyDir($internalDir . $dirname); + $internalDir.=$dirname.='/'; + + if ($dirhandle = opendir($dir)) { + while (false !== ( $file = readdir($dirhandle))) { + + if (( $file != '.' ) && ( $file != '..' )) { + + if (is_dir($dir . '/' . $file) && $recursive) { + zipAddDir($dir . '/' . $file, $zip, $recursive, $internalDir); + } elseif (is_file($dir . '/' . $file)) { + $zip->addFile($dir . '/' . $file, $internalDir . $file); + } + } + } + closedir($dirhandle); + } else { + OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); + } +} diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php new file mode 100644 index 0000000000..ece8f70e06 --- /dev/null +++ b/apps/user_migrate/templates/settings.php @@ -0,0 +1,12 @@ +
+
+ t('Export your user account');?> +

t('This will create a compressed file that contains the data of owncloud account. + Please choose which components should be included:');?> +

+


+
+

+ +
+
diff --git a/lib/migrate.php b/lib/migrate.php new file mode 100644 index 0000000000..8f13d07f46 --- /dev/null +++ b/lib/migrate.php @@ -0,0 +1,66 @@ +. + * + */ + + +/** + * provides an interface to all search providers + */ +class OC_Migrate{ + static public $providers=array(); + + /** + * register a new migration provider + * @param OC_Migrate_Provider $provider + */ + public static function registerProvider($provider){ + self::$providers[]=$provider; + } + + /** + * export app data for a user + * @param string userid + * @return string xml of app data + */ + public static function export($uid){ + $xml = ''; + foreach(self::$providers as $provider){ + $xml .= ''; + $xml .= self::appInfoXML($provider->$appid); + $xml .= $provider->export($uid)); + $xml .= ''; + } + return $xml; + } + + /** + * generates the app info xml + * @param string appid + * @return string xml app info + */ + public static function appInfoXML($appid){ + $info = OC_App::getAppInfo($appid); + $xml = ''; + $zml .= 'INFO HERE'; + $xml .= ''; + return $xml; + } +} diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php new file mode 100644 index 0000000000..920fde7db3 --- /dev/null +++ b/lib/migrate/provider.php @@ -0,0 +1,23 @@ + Date: Fri, 3 Feb 2012 20:48:32 +0000 Subject: [PATCH 002/133] fix syntax, add logging, debug xml output instead of zip --- apps/user_migrate/settings.php | 21 +++++++++++---------- lib/migrate.php | 4 +++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index fbf190e37d..5831bf54cb 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -49,21 +49,22 @@ if (isset($_POST['user_migrate'])) { // adding owncloud system files OC_Log::write('user_migrate',"Adding app data to user export",OC_Log::INFO); // Call to OC_Migrate for the xml file. - $appdatafile = $tempdir . "/appdata.xml"; - $fh = fopen($appdatafile, 'w'); + //$appdatafile = $tempdir . "/appdata.xml"; + //$fh = fopen($appdatafile, 'w'); $appdata = OC_Migrate::export(OC_User::getUser()); - fwrite($fh, $appdata); - $zip->addFile($appdatafile, "appdata.xml"); - fclose($fh); + //fwrite($fh, $appdata); + //$zip->addFile($appdatafile, "appdata.xml"); + //fclose($fh); } $zip->close(); - header("Content-Type: application/zip"); - header("Content-Disposition: attachment; filename=" . basename($filename)); - header("Content-Length: " . filesize($filename)); - @ob_end_clean(); - readfile($filename); + //header("Content-Type: application/zip"); + //header("Content-Disposition: attachment; filename=" . basename($filename)); + //header("Content-Length: " . filesize($filename)); + //@ob_end_clean(); + echo $appdata; + //readfile($filename); unlink($filename); } else { // fill template diff --git a/lib/migrate.php b/lib/migrate.php index 8f13d07f46..b3345ccd1a 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -41,11 +41,13 @@ class OC_Migrate{ * @return string xml of app data */ public static function export($uid){ + OC_Log::write('user_migrate','Starting user appdata export for: '.$uid,OC_Log::INFO); $xml = ''; foreach(self::$providers as $provider){ + OC_Log::write('user_migrate','Getting app data for app:'.$provider->appid,OC_Log::INFO); $xml .= ''; $xml .= self::appInfoXML($provider->$appid); - $xml .= $provider->export($uid)); + $xml .= $provider->export($uid); $xml .= ''; } return $xml; From ee88ded463b24a9b9cd8ba78a5df8657159eeef8 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 3 Feb 2012 21:00:12 +0000 Subject: [PATCH 003/133] more commenting out for debugging, added logging commands --- apps/bookmarks/appinfo/app.php | 4 ++++ apps/bookmarks/lib/migrate.php | 19 ++++++++----------- apps/user_migrate/settings.php | 2 +- lib/migrate.php | 5 +++-- lib/migrate/provider.php | 4 ++-- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php index 479d8ed476..5481218841 100644 --- a/apps/bookmarks/appinfo/app.php +++ b/apps/bookmarks/appinfo/app.php @@ -17,3 +17,7 @@ OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'hr OC_App::registerPersonal('bookmarks', 'settings'); require_once('apps/bookmarks/lib/search.php'); OC_Util::addScript('bookmarks','bookmarksearch'); + +// Include the migration provider + +require_once('apps/bookmarks/lib/migrate.php'); \ No newline at end of file diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 2e6581cd9f..d5a6a75ca8 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -1,18 +1,15 @@ appid = 'bookmarks'; // Create the xml for the user supplied function export($uid){ - $xml = ''; - $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmakrs.user_id = ?"); - $bookmarks = $query->execute($uid); - OC_Log::write('user_migrate',print_r($bookmarks)); - foreach($bookmarks as $bookmark){ - $xml .= ''; - $xml .='DATA WILL BE HERE'; - $xml .= ''; - } + $xml = 'debugfrombookmarks'; + //$query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmakrs.user_id = ?"); + //$bookmarks = $query->execute($uid); + //foreach($bookmarks as $bookmark){ + // $xml .= ''; + // $xml .='DATA WILL BE HERE'; + // $xml .= ''; + //} return $xml; } } diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 5831bf54cb..4fa4d23a6f 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -47,7 +47,7 @@ if (isset($_POST['user_migrate'])) { // Does the user want their app data? if (isset($_POST['user_appdata'])) { // adding owncloud system files - OC_Log::write('user_migrate',"Adding app data to user export",OC_Log::INFO); + OC_Log::write('user_migrate',"Adding app data to user export file",OC_Log::INFO); // Call to OC_Migrate for the xml file. //$appdatafile = $tempdir . "/appdata.xml"; //$fh = fopen($appdatafile, 'w'); diff --git a/lib/migrate.php b/lib/migrate.php index b3345ccd1a..e3394c5a67 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -25,7 +25,7 @@ * provides an interface to all search providers */ class OC_Migrate{ - static public $providers=array(); + static private $providers=array(); /** * register a new migration provider @@ -33,6 +33,7 @@ class OC_Migrate{ */ public static function registerProvider($provider){ self::$providers[]=$provider; + OC_Log::write('user_migrate','Provider registered',OC_Log::INFO); } /** @@ -46,7 +47,7 @@ class OC_Migrate{ foreach(self::$providers as $provider){ OC_Log::write('user_migrate','Getting app data for app:'.$provider->appid,OC_Log::INFO); $xml .= ''; - $xml .= self::appInfoXML($provider->$appid); + //$xml .= self::appInfoXML($provider->$appid); $xml .= $provider->export($uid); $xml .= ''; } diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php index 920fde7db3..b604a01072 100644 --- a/lib/migrate/provider.php +++ b/lib/migrate/provider.php @@ -6,7 +6,7 @@ abstract class OC_Migrate_Provider{ public function __construct(){ OC_Migrate::registerProvider($this); } - public static $appid; + //public static $appid; /** * exports data for apps * @param string $uid @@ -19,5 +19,5 @@ abstract class OC_Migrate_Provider{ * @param string $query * @return array An array of OC_Search_Result's */ - abstract function import($data); + //abstract function import($data); } From 1133eaa679558a3f88a4678de977734533c29507 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 3 Feb 2012 21:28:58 +0000 Subject: [PATCH 004/133] load bookmarks provider class --- apps/bookmarks/lib/migrate.php | 1 + apps/user_migrate/settings.php | 2 +- lib/migrate.php | 7 +++---- lib/migrate/provider.php | 5 ++++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index d5a6a75ca8..bfbe3fc583 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -13,3 +13,4 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ return $xml; } } +new OC_Migrate_Provider_Bookmarks('bookmarks'); \ No newline at end of file diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 4fa4d23a6f..7e3510d97e 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -63,7 +63,7 @@ if (isset($_POST['user_migrate'])) { //header("Content-Disposition: attachment; filename=" . basename($filename)); //header("Content-Length: " . filesize($filename)); //@ob_end_clean(); - echo $appdata; + echo htmlspecialchars($appdata); //readfile($filename); unlink($filename); } else { diff --git a/lib/migrate.php b/lib/migrate.php index e3394c5a67..a1b4c5019b 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -33,7 +33,6 @@ class OC_Migrate{ */ public static function registerProvider($provider){ self::$providers[]=$provider; - OC_Log::write('user_migrate','Provider registered',OC_Log::INFO); } /** @@ -42,12 +41,12 @@ class OC_Migrate{ * @return string xml of app data */ public static function export($uid){ - OC_Log::write('user_migrate','Starting user appdata export for: '.$uid,OC_Log::INFO); + OC_Log::write('user_migrate','App data export started for user: '.$uid,OC_Log::INFO); $xml = ''; foreach(self::$providers as $provider){ OC_Log::write('user_migrate','Getting app data for app:'.$provider->appid,OC_Log::INFO); $xml .= ''; - //$xml .= self::appInfoXML($provider->$appid); + $xml .= self::appInfoXML($provider->appid); $xml .= $provider->export($uid); $xml .= ''; } @@ -62,7 +61,7 @@ class OC_Migrate{ public static function appInfoXML($appid){ $info = OC_App::getAppInfo($appid); $xml = ''; - $zml .= 'INFO HERE'; + $xml .= 'INFO HERE'; $xml .= ''; return $xml; } diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php index b604a01072..9dd1b2f389 100644 --- a/lib/migrate/provider.php +++ b/lib/migrate/provider.php @@ -3,7 +3,10 @@ * provides search functionalty */ abstract class OC_Migrate_Provider{ - public function __construct(){ + public $appid; + + public function __construct($appid){ + $this->appid = $appid; OC_Migrate::registerProvider($this); } //public static $appid; From 24c79c5bceaa9381e892c2ed9d0eca512fec20ed Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 3 Feb 2012 22:41:39 +0000 Subject: [PATCH 005/133] Updated Migrate_provider bookmakr implementation --- apps/bookmarks/lib/migrate.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index bfbe3fc583..af76a4ef4d 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -2,14 +2,12 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ // Create the xml for the user supplied function export($uid){ - $xml = 'debugfrombookmarks'; - //$query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmakrs.user_id = ?"); - //$bookmarks = $query->execute($uid); - //foreach($bookmarks as $bookmark){ - // $xml .= ''; - // $xml .='DATA WILL BE HERE'; - // $xml .= ''; - //} + $xml = 'test'; + $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmarks.user_id = ?"); + $bookmarks =& $query->execute(array($uid)); + while ($row = $bookmarks->fetchRow()) { + $xml .= $row[0] . "\n"; + } return $xml; } } From 960dd750c95e116b76e17de728936a17556f2f93 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 5 Feb 2012 23:00:38 +0000 Subject: [PATCH 006/133] Added dbexport to export output. Initial import code. --- apps/admin_export/appinfo/app.php | 2 + apps/admin_export/appinfo/info.xml | 2 +- apps/admin_export/settings.php | 129 +++++++++++++++++++++-- apps/admin_export/templates/settings.php | 12 ++- 4 files changed, 134 insertions(+), 11 deletions(-) diff --git a/apps/admin_export/appinfo/app.php b/apps/admin_export/appinfo/app.php index beebb4864e..4e896abd6a 100644 --- a/apps/admin_export/appinfo/app.php +++ b/apps/admin_export/appinfo/app.php @@ -5,6 +5,8 @@ * * @author Dominik Schmidt * @copyright 2011 Dominik Schmidt dev@dominik-schmidt.de +* @author Tom Needham +* @copyright 2012 Tom Needham tom@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/apps/admin_export/appinfo/info.xml b/apps/admin_export/appinfo/info.xml index df8a07c2f5..e434705c9a 100644 --- a/apps/admin_export/appinfo/info.xml +++ b/apps/admin_export/appinfo/info.xml @@ -5,7 +5,7 @@ Import/Export your owncloud data 0.1 AGPL - Thomas Schmidt + Thomas Schmidt and Tom Needham 2 diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index cafd35570c..62b4b68ca0 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -25,11 +25,15 @@ OC_Util::checkAdminUser(); OC_Util::checkAppEnabled('admin_export'); +define('DS', '/'); + + if (isset($_POST['admin_export'])) { $root = OC::$SERVERROOT . "/"; $zip = new ZipArchive(); - $filename = get_temp_dir() . "/owncloud_export_" . date("y-m-d_H-i-s") . ".zip"; + $tempdir = get_temp_dir(); + $filename = $tempdir . "/owncloud_export_" . date("y-m-d_H-i-s") . ".zip"; OC_Log::write('admin_export',"Creating export file at: " . $filename,OC_Log::INFO); if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) { exit("Cannot open <$filename>\n"); @@ -40,37 +44,79 @@ if (isset($_POST['admin_export'])) { OC_Log::write('admin_export',"Adding owncloud system files to export",OC_Log::INFO); zipAddDir($root, $zip, false); foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dirname) { - zipAddDir($root . $dirname, $zip, true, basename($root) . "/"); + zipAddDir($root . $dirname, $zip, true, "/"); } } if (isset($_POST['owncloud_config'])) { // adding owncloud config // todo: add database export + $dbfile = $tempdir . "/dbexport.xml"; + OC_DB::getDbStructure( $file, 'MDB2_SCHEMA_DUMP_ALL'); + $zip->addFile($dbfile, "dbexport.xml"); + OC_Log::write('admin_export',"Adding owncloud config to export",OC_Log::INFO); - zipAddDir($root . "config/", $zip, true, basename($root) . "/"); - $zip->addFile($root . '/data/.htaccess', basename($root) . "/data/owncloud.db"); + zipAddDir($root . "config/", $zip, true, "/"); + $zip->addFile($root . '/data/.htaccess', "data/owncloud.db"); } if (isset($_POST['user_files'])) { // needs to handle data outside of the default data dir. // adding user files - $zip->addFile($root . '/data/.htaccess', basename($root) . "/data/.htaccess"); - $zip->addFile($root . '/data/index.html', basename($root) . "/data/index.html"); + $zip->addFile($root . '/data/.htaccess', "data/.htaccess"); + $zip->addFile($root . '/data/index.html', "data/index.html"); foreach (OC_User::getUsers() as $i) { OC_Log::write('admin_export',"Adding owncloud user files of $i to export",OC_Log::INFO); - zipAddDir($root . "data/" . $i, $zip, true, basename($root) . "/data/"); + zipAddDir($root . "data/" . $i, $zip, true, "/data/"); } } - $zip->close(); - header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=" . basename($filename)); header("Content-Length: " . filesize($filename)); @ob_end_clean(); readfile($filename); unlink($filename); + unlink($dbfile); +} else if( isset($_POST['admin_import']) ){ + + $root = OC::$SERVERROOT . "/"; + $importname = "owncloud_import_" . date("y-m-d_H-i-s"); + + // Save data dir for later + $datadir = OC_Config::getValue( 'datadirectory' ); + + // Copy the uploaded file + $from = $_FILES['owncloud_import']['tmp_name']; + $to = get_temp_dir().'/'.$importname.'.zip'; + if( !move_uploaded_file( $from, $to ) ){ + OC_Log::write('admin_export',"Failed to copy the uploaded file",OC_Log::INFO); + exit(); + } + + // Extract zip + $zip = new ZipArchive(); + if ($zip->open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { + OC_Log::write('admin_export',"Failed to open zip file",OC_Log::INFO); + exit(); + } + $zip->extractTo(get_temp_dir().'/'.$importname.'/'); + $zip->close(); + + // Delete uploaded file + unlink( get_temp_dir() . '/' . $importname . '.zip' ); + + // Delete current data folder. + OC_Log::write('admin_export',"Deleting current data dir",OC_Log::INFO); + unlinkRecursive( $datadir, false ); + + // Copy over data + if( !copy_r( get_temp_dir() . '/' . $importname . '/data', $datadir ) ){ + OC_Log::write('admin_export',"Failed to copy over data directory",OC_Log::INFO); + exit(); + } + + // TODO: Import db } else { // fill template $tmpl = new OC_Template('admin_export', 'settings'); @@ -99,3 +145,68 @@ function zipAddDir($dir, $zip, $recursive=true, $internalDir='') { OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); } } + +function unlinkRecursive($dir, $deleteRootToo) +{ + if(!$dh = @opendir($dir)) + { + return; + } + while (false !== ($obj = readdir($dh))) + { + if($obj == '.' || $obj == '..') + { + continue; + } + + if (!@unlink($dir . '/' . $obj)) + { + unlinkRecursive($dir.'/'.$obj, true); + } + } + + closedir($dh); + + if ($deleteRootToo) + { + @rmdir($dir); + } + + return; +} + + + function copy_r( $path, $dest ) + { + if( is_dir($path) ) + { + @mkdir( $dest ); + $objects = scandir($path); + if( sizeof($objects) > 0 ) + { + foreach( $objects as $file ) + { + if( $file == "." || $file == ".." ) + continue; + // go on + if( is_dir( $path.DS.$file ) ) + { + copy_r( $path.DS.$file, $dest.DS.$file ); + } + else + { + copy( $path.DS.$file, $dest.DS.$file ); + } + } + } + return true; + } + elseif( is_file($path) ) + { + return copy($path, $dest); + } + else + { + return false; + } + } diff --git a/apps/admin_export/templates/settings.php b/apps/admin_export/templates/settings.php index 47689facbb..9f0845bf55 100644 --- a/apps/admin_export/templates/settings.php +++ b/apps/admin_export/templates/settings.php @@ -8,6 +8,16 @@

- + + + +
+
+ t('Import an ownCloud instance THIS WILL DELETE ALL CURRENT OWNCLOUD DATA');?> +

t('All current ownCloud data will be replaced by the ownCloud instance that is uploaded.');?> +

+

+

+
From 20b96787899906c596d4132fe98955f1639a9ff2 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 10 Feb 2012 20:23:28 +0000 Subject: [PATCH 007/133] replace database name and table prefix with default values --- apps/admin_export/settings.php | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 62b4b68ca0..851388c0b5 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -40,19 +40,32 @@ if (isset($_POST['admin_export'])) { } if (isset($_POST['owncloud_system'])) { - // adding owncloud system files - OC_Log::write('admin_export',"Adding owncloud system files to export",OC_Log::INFO); - zipAddDir($root, $zip, false); - foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dirname) { - zipAddDir($root . $dirname, $zip, true, "/"); - } + // adding owncloud system files + OC_Log::write('admin_export',"Adding owncloud system files to export",OC_Log::INFO); + zipAddDir($root, $zip, false); + foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dirname) { + zipAddDir($root . $dirname, $zip, true, "/"); + } } if (isset($_POST['owncloud_config'])) { // adding owncloud config // todo: add database export $dbfile = $tempdir . "/dbexport.xml"; - OC_DB::getDbStructure( $file, 'MDB2_SCHEMA_DUMP_ALL'); + OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL'); + + // Now add in *dbname* and *dbtableprefix* + $dbexport = file_get_contents( $dbfile ); + + $dbnamestring = "\n\n " . OC_Config::getValue( "dbname", "owncloud" ); + $dbtableprefixstring = "\n\n " . OC_Config::getValue( "dbtableprefix", "_oc" ); + + $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); + $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbtableprefix*", $dbexport ); + + // Write the new db export file + file_put_contents( $dbfile, $dbexport ); + $zip->addFile($dbfile, "dbexport.xml"); OC_Log::write('admin_export',"Adding owncloud config to export",OC_Log::INFO); @@ -106,6 +119,9 @@ if (isset($_POST['admin_export'])) { // Delete uploaded file unlink( get_temp_dir() . '/' . $importname . '.zip' ); + // Now we need to check if everything is present. Data and dbexport.xml + + // Delete current data folder. OC_Log::write('admin_export',"Deleting current data dir",OC_Log::INFO); unlinkRecursive( $datadir, false ); From 33c5b3a2efa8559a3e960e6d8256576aab604869 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Thu, 1 Mar 2012 19:41:14 +0000 Subject: [PATCH 008/133] Added replaceDB method in lib/db.php --- lib/db.php | 83 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/lib/db.php b/lib/db.php index 9d3c20e014..f43f00a397 100644 --- a/lib/db.php +++ b/lib/db.php @@ -316,8 +316,11 @@ class OC_DB { // read file $content = file_get_contents( $file ); - // Make changes and save them to a temporary file - $file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' ); + // Make changes and save them to an in-memory file + $file2 = 'static://db_scheme'; + if($file2 == ''){ + die('could not create tempfile in get_temp_dir() - aborting'); + } $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content ); $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't @@ -328,7 +331,7 @@ class OC_DB { // Try to create tables $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - // Delete our temporary file + //clean up memory unlink( $file2 ); // Die in case something went wrong @@ -368,8 +371,8 @@ class OC_DB { return false; } - // Make changes and save them to a temporary file - $file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' ); + // Make changes and save them to an in-memory file + $file2 = 'static://db_scheme'; $content = str_replace( '*dbname*', $previousSchema['name'], $content ); $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't @@ -378,7 +381,7 @@ class OC_DB { file_put_contents( $file2, $content ); $op = self::$schema->updateDatabase($file2, $previousSchema, array(), false); - // Delete our temporary file + //clean up memory unlink( $file2 ); if (PEAR::isError($op)) { @@ -389,6 +392,27 @@ class OC_DB { return true; } + /** + * @breif replaces the owncloud tables with a new set + */ + public static function replaceDB( $file ){ + + // Delete the old tables + self::removeDBStructure( '/home/tom/sites/secure.tomneedham.com/public_html/migration/db_structure.xml' ); + + $apps = OC_App::getAllApps(); + foreach($apps as $app){ + $path = '/apps/'.$app.'/appinfo/database.xml'; + if(file_exists($path)){ + self::removeDBStructure( $path ); + } + } + + // Create new tables + self::createDBFromStructure( $file ); + + } + /** * @brief connects to a MDB2 database scheme * @returns true/false @@ -482,6 +506,27 @@ class OC_DB { } } + /** + * @breif replaces the owncloud tables with a new set + */ + public static function replaceDB( $file ){ + + // Delete the old tables + self::removeDBStructure( OC::$DOCUMENTROOT . 'db_structure.xml' ); + + $apps = OC_App::getAllApps(); + foreach($apps as $app){ + $path = '/apps/'.$app.'/appinfo/database.xml'; + if(file_exists($path)){ + self::removeDBStructure( $path ); + } + } + + // Create new tables + self::createDBFromStructure( $file ); + + } + /** * Start a transaction */ @@ -505,6 +550,21 @@ class OC_DB { self::$connection->commit(); self::$inTransaction=false; } + + /** + * check if a result is an error, works with MDB2 and PDOException + * @param mixed $result + * @return bool + */ + public static function isError($result){ + if(!$result){ + return true; + }elseif(self::$backend==self::BACKEND_MDB2 and PEAR::isError($result)){ + return true; + }else{ + return false; + } + } } /** @@ -524,11 +584,15 @@ class PDOStatementWrapper{ public function execute($input=array()){ $this->lastArguments=$input; if(count($input)>0){ - $this->statement->execute($input); + $result=$this->statement->execute($input); }else{ - $this->statement->execute(); + $result=$this->statement->execute(); + } + if($result){ + return $this; + }else{ + return false; } - return $this; } /** @@ -567,3 +631,4 @@ class PDOStatementWrapper{ return $this->statement->fetchColumn($colnum); } } + From 9c79de4aa01697e6b2eece450a83ca2019eb9b1c Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 2 Mar 2012 21:47:20 +0000 Subject: [PATCH 009/133] removed duplicate function --- lib/db.php | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/lib/db.php b/lib/db.php index f43f00a397..2348da908e 100644 --- a/lib/db.php +++ b/lib/db.php @@ -392,27 +392,6 @@ class OC_DB { return true; } - /** - * @breif replaces the owncloud tables with a new set - */ - public static function replaceDB( $file ){ - - // Delete the old tables - self::removeDBStructure( '/home/tom/sites/secure.tomneedham.com/public_html/migration/db_structure.xml' ); - - $apps = OC_App::getAllApps(); - foreach($apps as $app){ - $path = '/apps/'.$app.'/appinfo/database.xml'; - if(file_exists($path)){ - self::removeDBStructure( $path ); - } - } - - // Create new tables - self::createDBFromStructure( $file ); - - } - /** * @brief connects to a MDB2 database scheme * @returns true/false @@ -511,10 +490,11 @@ class OC_DB { */ public static function replaceDB( $file ){ + $apps = OC_App::getAllApps(); + // Delete the old tables self::removeDBStructure( OC::$DOCUMENTROOT . 'db_structure.xml' ); - $apps = OC_App::getAllApps(); foreach($apps as $app){ $path = '/apps/'.$app.'/appinfo/database.xml'; if(file_exists($path)){ From 8188a9f51bdf0845dcbc24ffe654118b695febc6 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 2 Mar 2012 21:54:44 +0000 Subject: [PATCH 010/133] change value in db export --- apps/admin_export/settings.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 851388c0b5..5eecd4f3a2 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -59,9 +59,11 @@ if (isset($_POST['admin_export'])) { $dbnamestring = "\n\n " . OC_Config::getValue( "dbname", "owncloud" ); $dbtableprefixstring = "
\n\n " . OC_Config::getValue( "dbtableprefix", "_oc" ); + $createstring = "\n\n *dbname*\n true"; $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbtableprefix*", $dbexport ); + $dbexport = str_replace( $createstring, "\n\n *dbname*\n false Date: Fri, 2 Mar 2012 22:19:06 +0000 Subject: [PATCH 011/133] Update database.xml locations. Fix dbexport.xml. --- apps/admin_export/settings.php | 7 ++++--- lib/db.php | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 5eecd4f3a2..5ba6f716a2 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -62,8 +62,8 @@ if (isset($_POST['admin_export'])) { $createstring = "\n\n *dbname*\n true"; $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); - $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbtableprefix*", $dbexport ); - $dbexport = str_replace( $createstring, "\n\n *dbname*\n false\n\n *dbprefix*", $dbexport ); + $dbexport = str_replace( $createstring, "\n\n *dbname*\n false", $dbexport ); // Write the new db export file file_put_contents( $dbfile, $dbexport ); @@ -134,7 +134,8 @@ if (isset($_POST['admin_export'])) { exit(); } - // TODO: Import db + // TODO: Import db + OC_DB::replaceDB( get_temp_dir() . '/' . $importname . '/dbexport.xml' ); } else { // fill template $tmpl = new OC_Template('admin_export', 'settings'); diff --git a/lib/db.php b/lib/db.php index 2348da908e..07e5859096 100644 --- a/lib/db.php +++ b/lib/db.php @@ -493,10 +493,10 @@ class OC_DB { $apps = OC_App::getAllApps(); // Delete the old tables - self::removeDBStructure( OC::$DOCUMENTROOT . 'db_structure.xml' ); + self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' ); foreach($apps as $app){ - $path = '/apps/'.$app.'/appinfo/database.xml'; + $path = OC::$SERVERROOT.'/apps/'.$app.'/appinfo/database.xml'; if(file_exists($path)){ self::removeDBStructure( $path ); } From 6906988b4ec795d5d33367cd192b47d061f1c906 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 3 Mar 2012 13:22:45 +0000 Subject: [PATCH 012/133] Update bookmarks migration provider. --- apps/bookmarks/lib/migrate.php | 68 +++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index af76a4ef4d..86a5b95725 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -1,14 +1,72 @@ formatOutput = true; + $bookmarks = $doc->createElement('bookmarks'); + $bookmarks = $doc->appendChild($bookmarks); + $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmarks.user_id = ?"); - $bookmarks =& $query->execute(array($uid)); - while ($row = $bookmarks->fetchRow()) { - $xml .= $row[0] . "\n"; + $bookmarksdata =& $query->execute(array($uid)); + + + // Foreach bookmark + while ($row = $bookmarksdata->fetchRow()) { + $bookmark = $doc->createElement('bookmark'); + $bookmark = $bookmarks->appendChild($bookmark); + + $attr = $doc->createElement('title'); + $attr = $bookmark->appendChild($attr); + $value = $doc->createTextNode($row['title']); + $attr->appendChild($value); + + $attr = $doc->createElement('url'); + $attr = $bookmark->appendChild($attr); + $value = $doc->createTextNode($row['url']); + $attr->appendChild($value); + + $attr = $doc->createElement('added'); + $attr = $bookmark->appendChild($attr); + $value = $doc->createTextNode($row['added']); + $attr->appendChild($value); + + $attr = $doc->createElement('lastmodified'); + $attr = $bookmark->appendChild($attr); + $value = $doc->createTextNode($row['lastmodified']); + $attr->appendChild($value); + + $attr = $doc->createElement('public'); + $attr = $bookmark->appendChild($attr); + $value = $doc->createTextNode($row['public']); + $attr->appendChild($value); + + $attr = $doc->createElement('clickcount'); + $attr = $bookmark->appendChild($attr); + $value = $doc->createTextNode($row['clickcount']); + $attr->appendChild($value); + + $attr = $doc->createElement('tags'); + $tags = $bookmark->appendChild($attr); + + $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks_tags WHERE *PREFIX*bookmarks_tags.bookmark_id = ?"); + $tagsdata =& $query->execute(array($row['id'])); + + // Foreach tag + while ($row = $tagsdata->fetchRow()) { + $attr = $doc->createElement('tag'); + $attr = $tags->appendChild($attr); + $value = $doc->createTextNode($row['tag']); + $attr->appendChild($value); + } } - return $xml; + + return $bookmarks; + } + } + new OC_Migrate_Provider_Bookmarks('bookmarks'); \ No newline at end of file From 188a304625b8ddb597505f69bc34487806b5b18e Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 3 Mar 2012 13:26:01 +0000 Subject: [PATCH 013/133] Replace db on import. Update user_migration export function. --- apps/admin_export/settings.php | 3 --- apps/user_migrate/settings.php | 21 ++++++++++----------- lib/migrate.php | 34 +++++++++++++++++++++++----------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 5ba6f716a2..5584181fbb 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -59,11 +59,9 @@ if (isset($_POST['admin_export'])) { $dbnamestring = "\n\n " . OC_Config::getValue( "dbname", "owncloud" ); $dbtableprefixstring = "
\n\n " . OC_Config::getValue( "dbtableprefix", "_oc" ); - $createstring = "\n\n *dbname*\n true"; $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbprefix*", $dbexport ); - $dbexport = str_replace( $createstring, "\n\n *dbname*\n false", $dbexport ); // Write the new db export file file_put_contents( $dbfile, $dbexport ); @@ -134,7 +132,6 @@ if (isset($_POST['admin_export'])) { exit(); } - // TODO: Import db OC_DB::replaceDB( get_temp_dir() . '/' . $importname . '/dbexport.xml' ); } else { // fill template diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 7e3510d97e..7c00dace3c 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -49,23 +49,22 @@ if (isset($_POST['user_migrate'])) { // adding owncloud system files OC_Log::write('user_migrate',"Adding app data to user export file",OC_Log::INFO); // Call to OC_Migrate for the xml file. - //$appdatafile = $tempdir . "/appdata.xml"; - //$fh = fopen($appdatafile, 'w'); + $appdatafile = $tempdir . "/appdata.xml"; + $appdata = OC_Migrate::export(OC_User::getUser()); - //fwrite($fh, $appdata); - //$zip->addFile($appdatafile, "appdata.xml"); - //fclose($fh); + file_put_contents($appdatafile, $appdata); + $zip->addFile($appdatafile, "appdata.xml"); + } $zip->close(); - //header("Content-Type: application/zip"); - //header("Content-Disposition: attachment; filename=" . basename($filename)); - //header("Content-Length: " . filesize($filename)); - //@ob_end_clean(); - echo htmlspecialchars($appdata); - //readfile($filename); + header("Content-Type: application/zip"); + header("Content-Disposition: attachment; filename=" . basename($filename)); + header("Content-Length: " . filesize($filename)); + readfile($filename); unlink($filename); + } else { // fill template $tmpl = new OC_Template('user_migrate', 'settings'); diff --git a/lib/migrate.php b/lib/migrate.php index a1b4c5019b..c5def5b583 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -41,16 +41,26 @@ class OC_Migrate{ * @return string xml of app data */ public static function export($uid){ + + $doc = new DOMDocument(); + $doc->formatOutput = true; + OC_Log::write('user_migrate','App data export started for user: '.$uid,OC_Log::INFO); - $xml = ''; + foreach(self::$providers as $provider){ + OC_Log::write('user_migrate','Getting app data for app:'.$provider->appid,OC_Log::INFO); - $xml .= ''; - $xml .= self::appInfoXML($provider->appid); - $xml .= $provider->export($uid); - $xml .= ''; + $app = $doc->createElement('app'); + $doc->appendChild($app); + // Append app info + $app = $doc->appendChild( self::appInfoXML( $provider->appid ) ); + + // Add the app data + $app->appendChild($provider->export($uid)); + } - return $xml; + + return $doc->saveXML(); } /** @@ -59,10 +69,12 @@ class OC_Migrate{ * @return string xml app info */ public static function appInfoXML($appid){ - $info = OC_App::getAppInfo($appid); - $xml = ''; - $xml .= 'INFO HERE'; - $xml .= ''; - return $xml; + $doc = new DOMDocument(); + $appinfo = $doc->createElement('appinfo'); + $appinfo = $doc->appendChild($appinfo); + $data = $doc->createTextNode($appid); + $appinfo->appendChild($data); + + return $appinfo; } } From 34f05ba180792afb40d953b6cf8595a4513eb972 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 3 Mar 2012 14:35:17 +0000 Subject: [PATCH 014/133] Udpdate bookmarks migration provider. App version included in export. --- apps/bookmarks/lib/migrate.php | 2 +- lib/migrate.php | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 86a5b95725..c655154d41 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -63,7 +63,7 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } } - return $bookmarks; + return $doc; } diff --git a/lib/migrate.php b/lib/migrate.php index c5def5b583..5179e43199 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -51,12 +51,17 @@ class OC_Migrate{ OC_Log::write('user_migrate','Getting app data for app:'.$provider->appid,OC_Log::INFO); $app = $doc->createElement('app'); - $doc->appendChild($app); + $app = $doc->appendChild($app); + $app->setAttribute('id',$provider->appid); // Append app info - $app = $doc->appendChild( self::appInfoXML( $provider->appid ) ); + $appinfo = $doc->importNode( self::appInfoXML( $provider->appid )->documentElement, true ); + $app->appendChild( $appinfo ); + $appdata = $doc->createElement('appdata'); + $appdata = $app->appendChild($appdata); // Add the app data - $app->appendChild($provider->export($uid)); + $appdatanode = $doc->importNode( $provider->export($uid)->documentElement, true ); + $appdata->appendChild( $appdatanode ); } @@ -69,12 +74,17 @@ class OC_Migrate{ * @return string xml app info */ public static function appInfoXML($appid){ + + $info = OC_App::getAppInfo($appid); + $doc = new DOMDocument(); $appinfo = $doc->createElement('appinfo'); $appinfo = $doc->appendChild($appinfo); - $data = $doc->createTextNode($appid); - $appinfo->appendChild($data); + $version = $doc->createElement('version'); + $appinfo->appendChild($version); + $versionval = $doc->createTextNode($info['version']); + $version->appendChild($versionval); - return $appinfo; + return $doc; } } From 691103acd5aad2673b6375726ba24fb56e88451b Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 3 Mar 2012 17:30:21 +0000 Subject: [PATCH 015/133] Use json for migration data --- apps/bookmarks/lib/migrate.php | 98 ++++++++++---------- apps/user_migrate/settings.php | 4 +- lib/migrate.php | 157 +++++++++++++++++++++++++-------- lib/migrate/provider.php | 9 +- 4 files changed, 178 insertions(+), 90 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index c655154d41..f50a8c4633 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -4,69 +4,71 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ // Create the xml for the user supplied function export($uid){ - $doc = new DOMDocument(); - $doc->formatOutput = true; - $bookmarks = $doc->createElement('bookmarks'); - $bookmarks = $doc->appendChild($bookmarks); - + $bookmarks = array(); + $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmarks.user_id = ?"); $bookmarksdata =& $query->execute(array($uid)); - - // Foreach bookmark while ($row = $bookmarksdata->fetchRow()) { - $bookmark = $doc->createElement('bookmark'); - $bookmark = $bookmarks->appendChild($bookmark); - - $attr = $doc->createElement('title'); - $attr = $bookmark->appendChild($attr); - $value = $doc->createTextNode($row['title']); - $attr->appendChild($value); - - $attr = $doc->createElement('url'); - $attr = $bookmark->appendChild($attr); - $value = $doc->createTextNode($row['url']); - $attr->appendChild($value); - - $attr = $doc->createElement('added'); - $attr = $bookmark->appendChild($attr); - $value = $doc->createTextNode($row['added']); - $attr->appendChild($value); - - $attr = $doc->createElement('lastmodified'); - $attr = $bookmark->appendChild($attr); - $value = $doc->createTextNode($row['lastmodified']); - $attr->appendChild($value); - - $attr = $doc->createElement('public'); - $attr = $bookmark->appendChild($attr); - $value = $doc->createTextNode($row['public']); - $attr->appendChild($value); - - $attr = $doc->createElement('clickcount'); - $attr = $bookmark->appendChild($attr); - $value = $doc->createTextNode($row['clickcount']); - $attr->appendChild($value); - - $attr = $doc->createElement('tags'); - $tags = $bookmark->appendChild($attr); + // Get the tags $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks_tags WHERE *PREFIX*bookmarks_tags.bookmark_id = ?"); $tagsdata =& $query->execute(array($row['id'])); + $tags = array(); // Foreach tag while ($row = $tagsdata->fetchRow()) { - $attr = $doc->createElement('tag'); - $attr = $tags->appendChild($attr); - $value = $doc->createTextNode($row['tag']); - $attr->appendChild($value); - } + $tags[] = $row['tag']; + } + + $bookmarks[] = array( + 'url' => $row['url'], + 'title' => $row['title'], + 'public' => $row['public'], + 'added' => $row['added'], + 'lastmodified' => $row['lastmodified'], + 'clickcount' => $row['clickcount'], + 'tags' => $tags + ); + } - return $doc; + return array('bookmarks' => $bookmarks); } + // Import function for bookmarks + function import($data,$uid){ + + // Different import code for different versions of the app + switch($data['info']['version']){ + default: + // Foreach bookmark + foreach($data['data']['bookmarks'] as $bookmark){ + + $query = OC_DB::prepare( "INSERT INTO `*PREFIX*bookmarks` ( `url`, `title`, `user_id`, `public`, `added`, `lastmodified`, `clickcount` ) VALUES( ?, ?, ?, ?, ?, ?, ? )" ); + $result = $query->execute( array( + $bookmark['url'], + $bookmark['title'], + $uid, + $bookmark['public'], + $bookmark['added'], + $bookmark['lastmodified'], + $bookmark['clickcount'] + ) ); + // Now add the tags + $id = OC_DB::insertid(); + foreach($bookmark['tags'] as $tag){ + $query = OC_DB::prepare( "INSERT INTO `*PREFIX*bookmarks_tags` ( `id`, `tag` ) VALUES( ?, ? )" ); + $result = $query->execute( array( $id, $tag)); + } + + } + break; + } + // Finished import + } + } new OC_Migrate_Provider_Bookmarks('bookmarks'); \ No newline at end of file diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 7c00dace3c..ed08abe79d 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -49,11 +49,11 @@ if (isset($_POST['user_migrate'])) { // adding owncloud system files OC_Log::write('user_migrate',"Adding app data to user export file",OC_Log::INFO); // Call to OC_Migrate for the xml file. - $appdatafile = $tempdir . "/appdata.xml"; + $appdatafile = $tempdir . "/userexport.json"; $appdata = OC_Migrate::export(OC_User::getUser()); file_put_contents($appdatafile, $appdata); - $zip->addFile($appdatafile, "appdata.xml"); + $zip->addFile($appdatafile, "userexport.json"); } diff --git a/lib/migrate.php b/lib/migrate.php index 5179e43199..f3dd3080d3 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -42,49 +42,134 @@ class OC_Migrate{ */ public static function export($uid){ - $doc = new DOMDocument(); - $doc->formatOutput = true; - - OC_Log::write('user_migrate','App data export started for user: '.$uid,OC_Log::INFO); - - foreach(self::$providers as $provider){ + // Only export database users, otherwise we get chaos + if(OC_User_Database::userExists($uid)){ + + $data = array(); + $data['userid'] = OC_User::getUser(); - OC_Log::write('user_migrate','Getting app data for app:'.$provider->appid,OC_Log::INFO); - $app = $doc->createElement('app'); - $app = $doc->appendChild($app); - $app->setAttribute('id',$provider->appid); - // Append app info - $appinfo = $doc->importNode( self::appInfoXML( $provider->appid )->documentElement, true ); - $app->appendChild( $appinfo ); + $query = OC_DB::prepare( "SELECT uid, password FROM *PREFIX*users WHERE uid LIKE ?" ); + $result = $query->execute( array( $uid)); + + $row = $result->fetchRow(); + if($row){ + $data['hash'] = $row['password']; + } else { + return false; + exit(); + } - $appdata = $doc->createElement('appdata'); - $appdata = $app->appendChild($appdata); - // Add the app data - $appdatanode = $doc->importNode( $provider->export($uid)->documentElement, true ); - $appdata->appendChild( $appdatanode ); - + foreach(self::$providers as $provider){ + + $data['apps'][$prodider->appid]['info'] = OC_App::getAppInfo($provider->appid); + $data['apps'][$provider->appid]['data'] = $provider->export($uid); + + } + + return self::indent(json_encode($data)); + + } else { + return false; } - - return $doc->saveXML(); + } /** - * generates the app info xml - * @param string appid - * @return string xml app info - */ - public static function appInfoXML($appid){ + * @breif imports a new user + * @param $data json data for the user + * @param $uid optional uid to use + * @return json reply + */ + public function import($data,$uid=null){ + + // Import the data + $data = json_decode($data); + if(is_null($data)){ + // TODO LOG + return false; + exit(); + } + + // Specified user or use original + $uid = !is_null($uid) ? $uid : $data['userid']; + + // Check if userid exists + if(OC_User::userExists($uid)){ + // TODO LOG + return false; + exit(); + } + + // Create the user + $query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" ); + $result = $query->execute( array( $uid, $data['hash'])); + if(!$result){ + // TODO LOG + return false; + exit(); + } + + foreach($data['app'] as $app){ + // Check if supports migration and is enabled + if(in_array($app, self::$providers)){ + if(OC_App::isEnabled($app)){ + $provider->import($data['app'][$app],$uid); + } + } + + } + + } + + private static function indent($json){ + + $result = ''; + $pos = 0; + $strLen = strlen($json); + $indentStr = ' '; + $newLine = "\n"; + $prevChar = ''; + $outOfQuotes = true; - $info = OC_App::getAppInfo($appid); + for ($i=0; $i<=$strLen; $i++) { - $doc = new DOMDocument(); - $appinfo = $doc->createElement('appinfo'); - $appinfo = $doc->appendChild($appinfo); - $version = $doc->createElement('version'); - $appinfo->appendChild($version); - $versionval = $doc->createTextNode($info['version']); - $version->appendChild($versionval); + // Grab the next character in the string. + $char = substr($json, $i, 1); - return $doc; - } + // Are we inside a quoted string? + if ($char == '"' && $prevChar != '\\') { + $outOfQuotes = !$outOfQuotes; + + // If this character is the end of an element, + // output a new line and indent the next line. + } else if(($char == '}' || $char == ']') && $outOfQuotes) { + $result .= $newLine; + $pos --; + for ($j=0; $j<$pos; $j++) { + $result .= $indentStr; + } + } + + // Add the character to the result string. + $result .= $char; + + // If the last character was the beginning of an element, + // output a new line and indent the next line. + if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { + $result .= $newLine; + if ($char == '{' || $char == '[') { + $pos ++; + } + + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + + $prevChar = $char; + } + + return $result; + } + } diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php index 9dd1b2f389..63b804b620 100644 --- a/lib/migrate/provider.php +++ b/lib/migrate/provider.php @@ -13,14 +13,15 @@ abstract class OC_Migrate_Provider{ /** * exports data for apps * @param string $uid - * @return string xml data for that app + * @return array appdata to be exported */ abstract function export($uid); /** * imports data for the app - * @param string $query - * @return array An array of OC_Search_Result's + * @param $data array of data. eg: array('info'=> APPINFO, 'data'=>APPDATA ARRAY) + * @param $info array of info of the source install + * @return void */ - //abstract function import($data); + abstract function import($data,$uid); } From c3dfcc5b21620476b6e5bf356b42aee9f0da5874 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 9 Mar 2012 23:33:11 +0000 Subject: [PATCH 016/133] First basic implementation of migration.db. --- lib/migrate.php | 326 +++++++++++++++++++++++++-------------- lib/migrate/provider.php | 15 +- 2 files changed, 215 insertions(+), 126 deletions(-) diff --git a/lib/migrate.php b/lib/migrate.php index f3dd3080d3..da7a5ea34f 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -25,7 +25,10 @@ * provides an interface to all search providers */ class OC_Migrate{ + + static private $MDB2=false; static private $providers=array(); + static private $schema=false; /** * register a new migration provider @@ -36,140 +39,225 @@ class OC_Migrate{ } /** - * export app data for a user - * @param string userid - * @return string xml of app data + * @breif creates a migration.db in the users data dir with their app data in + * @param @uid string userid of the user to export for + * @return bool whether operation was successfull */ - public static function export($uid){ + public static function export( $uid ){ // Only export database users, otherwise we get chaos - if(OC_User_Database::userExists($uid)){ - - $data = array(); - $data['userid'] = OC_User::getUser(); - - $query = OC_DB::prepare( "SELECT uid, password FROM *PREFIX*users WHERE uid LIKE ?" ); - $result = $query->execute( array( $uid)); - - $row = $result->fetchRow(); - if($row){ - $data['hash'] = $row['password']; - } else { - return false; - exit(); - } - - foreach(self::$providers as $provider){ - - $data['apps'][$prodider->appid]['info'] = OC_App::getAppInfo($provider->appid); - $data['apps'][$provider->appid]['data'] = $provider->export($uid); - - } - - return self::indent(json_encode($data)); - - } else { + if(!OC_User_Database::userExists( $uid )){ return false; } + + // Foreach provider + foreach( $providers as $provider ){ + + self::createAppTables( $provider->id ); + // Run the export function + $provider->export( $uid ); + + } + + return true; } /** - * @breif imports a new user - * @param $data json data for the user - * @param $uid optional uid to use - * @return json reply - */ - public function import($data,$uid=null){ - - // Import the data - $data = json_decode($data); - if(is_null($data)){ - // TODO LOG - return false; - exit(); - } - - // Specified user or use original - $uid = !is_null($uid) ? $uid : $data['userid']; - - // Check if userid exists - if(OC_User::userExists($uid)){ - // TODO LOG - return false; - exit(); - } - - // Create the user - $query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" ); - $result = $query->execute( array( $uid, $data['hash'])); - if(!$result){ - // TODO LOG + * @breif imports a new user + * @param $uid optional uid to use + * @return bool if the import succedded + */ + public static function import( $uid=null ){ + + self::$uid = $uid; + + // Connect to the db + if(!self::connectDB()){ + return false; + } + + // Create the user + if(!self::createUser($uid, $hash)){ + return false; + } + + // Now get the list of apps to import from migration.db + // Then check for migrate.php for these apps + // If present, run the import function for them. + + return treu; + + } + + // @breif connects to migration.db, or creates if not found + // @return bool whether the operation was successful + private static function connectDB(){ + + // Already connected + if(!self::$MDB2){ + require_once('MDB2.php'); + + $datadir = OC_Config::getValue( "datadirectory", "$SERVERROOT/data" ); + + // Prepare options array + $options = array( + 'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE), + 'log_line_break' => '
', + 'idxname_format' => '%s', + 'debug' => true, + 'quote_identifier' => true + ); + $dsn = array( + 'phptype' => 'sqlite', + 'database' => $datadir.'/'.self::$uid.'/migration.db', + 'mode' => '0644' + ); + + // Try to establish connection + self::$MDB2 = MDB2::factory( $dsn, $options ); + + // Die if we could not connect + if( PEAR::isError( self::$MDB2 )){ + OC_Log::write('migration', 'Failed to create migration.db',OC_Log::FATAL); + OC_Log::write('migration',self::$MDB2->getUserInfo(),OC_Log::FATAL); + OC_Log::write('migration',self::$MDB2->getMessage(),OC_Log::FATAL); + return false; + } + + // We always, really always want associative arrays + self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); + } + return true; + + } + + // @breif prepares the db + // @param $query the sql query to prepare + public static function prepareDB( $query ){ + + // Optimize the query + $query = self::processQuery( $query ); + + // Optimize the query + $query = self::$MDB2->prepare( $query ); + + // Die if we have an error (error means: bad query, not 0 results!) + if( PEAR::isError( $query )) { + $entry = 'DB Error: "'.$result->getMessage().'"
'; + $entry .= 'Offending command was: '.$query.'
'; + OC_Log::write('migration',$entry,OC_Log::FATAL); return false; - exit(); + } else { + return true; } - - foreach($data['app'] as $app){ - // Check if supports migration and is enabled - if(in_array($app, self::$providers)){ - if(OC_App::isEnabled($app)){ - $provider->import($data['app'][$app],$uid); - } - } - - } - - } - - private static function indent($json){ + + } + + // @breif processes the db query + // @param $query the query to process + // @return string of processed query + private static function processQuery( $query ){ + + self::connectDB(); + $type = 'sqlite'; + $prefix = ''; + + $query = str_replace( '`', '\'', $query ); + $query = str_replace( 'NOW()', 'datetime(\'now\')', $query ); + $query = str_replace( 'now()', 'datetime(\'now\')', $query ); - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = ' '; - $newLine = "\n"; - $prevChar = ''; - $outOfQuotes = true; + // replace table name prefix + $query = str_replace( '*PREFIX*', $prefix, $query ); + + return $query; - for ($i=0; $i<=$strLen; $i++) { + } + + // @breif creates the tables in migration.db from an apps database.xml + // @param $appid string id of the app + // @return bool whether the operation was successful + private static function createAppTables( $appid ){ + $file = OC::$SERVERROOT.'/apps/'.$appid.'appinfo/database.xml'; + if(file_exists( $file )){ + // There is a database.xml file + $content = file_get_contents( $file ); + + $file2 = 'static://db_scheme'; + $content = str_replace( '*dbname*', 'migration', $content ); + $content = str_replace( '*dbprefix*', '', $content ); + + file_put_contents( $file2, $content ); + + // Try to create tables + $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); + + unlink( $file2 ); + + // Die in case something went wrong + if( $definition instanceof MDB2_Schema_Error ){ + OC_Log::write('migration','Failed to parse database.xml for: '.$appid,OC_Log::FATAL); + OC_Log::write('migration',$definition->getMessage().': '.$definition->getUserInfo(),OC_Log::FATAL); + return false; + } + + $definition['overwrite'] = true; + + $ret = self::$schema->createDatabase( $definition ); + // Die in case something went wrong + + if( $ret instanceof MDB2_Error ){ + OC_Log::write('migration','Failed to create tables for: '.$appid,OC_Log::FATAL); + OC_Log::write('migration',$ret->getMessage().': '.$ret->getUserInfo(),OC_Log::FATAL); + return false; + } + return true; + + } else { + // No database.xml + return false; + } + } + + + /** + * @brief connects to a MDB2 database scheme + * @returns true/false + * + * Connects to a MDB2 database scheme + */ + private static function connectScheme(){ + // We need a mdb2 database connection + self::connectDB(); + self::$MDB2->loadModule( 'Manager' ); + self::$MDB2->loadModule( 'Reverse' ); + + // Connect if this did not happen before + if( !self::$schema ){ + require_once('MDB2/Schema.php'); + self::$schema=MDB2_Schema::factory( self::$MDB2 ); + } + + return true; + } + + // @breif creates a new user in the database + // @param $uid string user_id of the user to be created + // @param $hash string hash of the user to be created + // @return bool result of user creation + private static function createUser( $uid, $hash ){ - // Grab the next character in the string. - $char = substr($json, $i, 1); - - // Are we inside a quoted string? - if ($char == '"' && $prevChar != '\\') { - $outOfQuotes = !$outOfQuotes; - - // If this character is the end of an element, - // output a new line and indent the next line. - } else if(($char == '}' || $char == ']') && $outOfQuotes) { - $result .= $newLine; - $pos --; - for ($j=0; $j<$pos; $j++) { - $result .= $indentStr; - } - } - - // Add the character to the result string. - $result .= $char; - - // If the last character was the beginning of an element, - // output a new line and indent the next line. - if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { - $result .= $newLine; - if ($char == '{' || $char == '[') { - $pos ++; - } - - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - - $prevChar = $char; + // Check if userid exists + if(OC_User::userExists( $uid )){ + return false; } - return $result; - } + // Create the user + $query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" ); + $result = $query->execute( array( $uid, $data['hash'])); + + return $result ? true : false; + + } } diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php index 63b804b620..9c03639b7c 100644 --- a/lib/migrate/provider.php +++ b/lib/migrate/provider.php @@ -3,22 +3,23 @@ * provides search functionalty */ abstract class OC_Migrate_Provider{ - public $appid; - public function __construct($appid){ - $this->appid = $appid; - OC_Migrate::registerProvider($this); + public $id; + + public function __construct( $appid ){ + $this->id = $appid; + OC_Migrate::registerProvider( $this ); } - //public static $appid; + /** - * exports data for apps + * @breif exports data for apps * @param string $uid * @return array appdata to be exported */ abstract function export($uid); /** - * imports data for the app + * @breif imports data for the app * @param $data array of data. eg: array('info'=> APPINFO, 'data'=>APPDATA ARRAY) * @param $info array of info of the source install * @return void From 3ca76d24a9b27101661c454be84c7126315315d6 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 10 Mar 2012 15:52:38 +0000 Subject: [PATCH 017/133] Add OC_Migrate::copyRows() method --- apps/bookmarks/lib/migrate.php | 89 ++++++++++++---------------------- lib/migrate.php | 59 +++++++++++++++++++++- 2 files changed, 90 insertions(+), 58 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index f50a8c4633..7d8ad8bfc5 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -2,73 +2,48 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ // Create the xml for the user supplied - function export($uid){ + function export( $uid ){ - $bookmarks = array(); - - $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks WHERE *PREFIX*bookmarks.user_id = ?"); - $bookmarksdata =& $query->execute(array($uid)); - // Foreach bookmark - while ($row = $bookmarksdata->fetchRow()) { - - // Get the tags - $query = OC_DB::prepare("SELECT * FROM *PREFIX*bookmarks_tags WHERE *PREFIX*bookmarks_tags.bookmark_id = ?"); - $tagsdata =& $query->execute(array($row['id'])); + $options = array( + 'table'=>'bookmarks', + 'matchcol'=>'user_id', + 'matchval'=>$uid, + 'idcol'=>'id' + ); + $ids = OC_Migrate::copyRows( $options ); - $tags = array(); - // Foreach tag - while ($row = $tagsdata->fetchRow()) { - $tags[] = $row['tag']; - } - - $bookmarks[] = array( - 'url' => $row['url'], - 'title' => $row['title'], - 'public' => $row['public'], - 'added' => $row['added'], - 'lastmodified' => $row['lastmodified'], - 'clickcount' => $row['clickcount'], - 'tags' => $tags - ); - - } + $options = array( + 'table'=>'bookmarks_tags', + 'matchcol'=>'id', + 'matchval'=>$ids + ); - return array('bookmarks' => $bookmarks); + // Export tags + OC_Migrate::copyRows( $options ); } // Import function for bookmarks - function import($data,$uid){ + function import( $data, $uid ){ - // Different import code for different versions of the app - switch($data['info']['version']){ - default: - // Foreach bookmark - foreach($data['data']['bookmarks'] as $bookmark){ - - $query = OC_DB::prepare( "INSERT INTO `*PREFIX*bookmarks` ( `url`, `title`, `user_id`, `public`, `added`, `lastmodified`, `clickcount` ) VALUES( ?, ?, ?, ?, ?, ?, ? )" ); - $result = $query->execute( array( - $bookmark['url'], - $bookmark['title'], - $uid, - $bookmark['public'], - $bookmark['added'], - $bookmark['lastmodified'], - $bookmark['clickcount'] - ) ); - // Now add the tags - $id = OC_DB::insertid(); - foreach($bookmark['tags'] as $tag){ - $query = OC_DB::prepare( "INSERT INTO `*PREFIX*bookmarks_tags` ( `id`, `tag` ) VALUES( ?, ? )" ); - $result = $query->execute( array( $id, $tag)); - } - - } - break; + // new id mapping + $newids = array(); + + // Import bookmarks + foreach($data['bookmarks'] as $bookmark){ + $bookmark['user_id'] = $uid; + // import to the db now + $newids[$bookmark['id']] = OC_DB::insertid(); + } + + // Import tags + foreach($data['bookmarks_tags'] as $tag){ + // Map the new ids + $tag['id'] = $newids[$tag['id']]; + // Import to the db now using OC_DB } - // Finished import } } -new OC_Migrate_Provider_Bookmarks('bookmarks'); \ No newline at end of file +new OC_Migrate_Provider_Bookmarks( 'bookmarks' ); \ No newline at end of file diff --git a/lib/migrate.php b/lib/migrate.php index da7a5ea34f..b09626d11b 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -134,7 +134,7 @@ class OC_Migrate{ // @breif prepares the db // @param $query the sql query to prepare - public static function prepareDB( $query ){ + public static function prepare( $query ){ // Optimize the query $query = self::processQuery( $query ); @@ -174,12 +174,69 @@ class OC_Migrate{ } + // @brief copys rows to migration.db from the main database + // @param $options array of options. + // @return bool + public static function copyRows( $options ){ + if( !array_key_exists( 'table', $options ) ){ + return false; + } + + // Need to include 'where' in the query? + if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){ + foreach( $options['matchval'] as $matchval ){ + // Run the query for this match value (where x = y value) + $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); + $results = $query->execute( array( $matchval ) ); + self::insertData( $results, $options ); + + } + + } else { + // Just get everything + $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] ); + $results = $query->execute(); + self::insertData( $results, $options ); + + } + + return true; + + } + + // @breif saves a sql data set into migration.db + // @param $data a sql data set returned from self::prepare()->query() + // @param $options array of copyRows options + // @return void + private static function insertData( $data, $options ){ + while( $data = $result->fetchRow() ){ + // Now save all this to the migration.db + foreach($row as $field=>$value){ + $fields[] = $field; + $values[] = $value; + } + + // Generate some sql + $sql = "INSERT INTO `*PREFIX*" . $options['table'] . '` ( `'; + $fieldssql = implode( '`, `', $fields ); + $sql .= $fieldssql . "` ) VALUES( "; + $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-1 ); + $sql .= $valuessql . " )"; + // Make the query + $query = self::prepare( $sql ); + $query->execute( $values ); + } + } + // @breif creates the tables in migration.db from an apps database.xml // @param $appid string id of the app // @return bool whether the operation was successful private static function createAppTables( $appid ){ $file = OC::$SERVERROOT.'/apps/'.$appid.'appinfo/database.xml'; if(file_exists( $file )){ + + self::connectScheme(); + // There is a database.xml file $content = file_get_contents( $file ); From d712d7f52c667e70edefcbd64c036afada678863 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 10 Mar 2012 18:18:58 +0000 Subject: [PATCH 018/133] Lots of fixes, improve copyRows() method and update settings page. --- apps/bookmarks/lib/migrate.php | 4 +-- apps/user_migrate/settings.php | 19 +++++----- lib/migrate.php | 64 +++++++++++++++++++++++----------- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 7d8ad8bfc5..9493f2cae8 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -11,10 +11,10 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ 'idcol'=>'id' ); $ids = OC_Migrate::copyRows( $options ); - + $ids = array('1'); $options = array( 'table'=>'bookmarks_tags', - 'matchcol'=>'id', + 'matchcol'=>'bookmark_id', 'matchval'=>$ids ); diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index ed08abe79d..c35d46b351 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -25,7 +25,6 @@ OC_Util::checkAppEnabled('user_migrate'); if (isset($_POST['user_migrate'])) { // Looks like they want to migrate - $errors = array(); $root = OC::$SERVERROOT . "/"; $user = OC_User::getUser(); $zip = new ZipArchive(); @@ -49,21 +48,21 @@ if (isset($_POST['user_migrate'])) { // adding owncloud system files OC_Log::write('user_migrate',"Adding app data to user export file",OC_Log::INFO); // Call to OC_Migrate for the xml file. - $appdatafile = $tempdir . "/userexport.json"; - $appdata = OC_Migrate::export(OC_User::getUser()); - file_put_contents($appdatafile, $appdata); - $zip->addFile($appdatafile, "userexport.json"); + // Create migration.db + var_dump(OC_Migrate::export(OC_User::getUser())); + // Add export db to zip + $zip->addFile($root.'data/'.$user.'/migration.db', "migration.db"); } $zip->close(); - header("Content-Type: application/zip"); - header("Content-Disposition: attachment; filename=" . basename($filename)); - header("Content-Length: " . filesize($filename)); - readfile($filename); - unlink($filename); + //header("Content-Type: application/zip"); + //header("Content-Disposition: attachment; filename=" . basename($filename)); + //header("Content-Length: " . filesize($filename)); + //readfile($filename); + //unlink($filename); } else { // fill template diff --git a/lib/migrate.php b/lib/migrate.php index b09626d11b..a0d702b4e2 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -29,6 +29,7 @@ class OC_Migrate{ static private $MDB2=false; static private $providers=array(); static private $schema=false; + static private $uid=false; /** * register a new migration provider @@ -49,17 +50,27 @@ class OC_Migrate{ if(!OC_User_Database::userExists( $uid )){ return false; } - + + self::$uid = $uid; + self::connectDB(); + $ok = true; + $return = array(); + // Foreach provider - foreach( $providers as $provider ){ - - self::createAppTables( $provider->id ); - // Run the export function - $provider->export( $uid ); - + foreach( self::$providers as $provider ){ + // Check for database.xml + if(file_exists(OC::$SERVERROOT.'/apps/'.$provider->id.'/appinfo/database.xml')){ + if(!self::createAppTables( $provider->id )){ + $ok = false; + OC_Log::write('migration','failed to create migration tables for: '.$provider->id,OC_Log::INFO); + } + } + if($ok){ + $return[$provider->id]['success'] = $provider->export( $uid ); + } } - return true; + return $return; } @@ -86,7 +97,7 @@ class OC_Migrate{ // Then check for migrate.php for these apps // If present, run the import function for them. - return treu; + return true; } @@ -98,7 +109,7 @@ class OC_Migrate{ if(!self::$MDB2){ require_once('MDB2.php'); - $datadir = OC_Config::getValue( "datadirectory", "$SERVERROOT/data" ); + $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); // Prepare options array $options = array( @@ -109,22 +120,22 @@ class OC_Migrate{ 'quote_identifier' => true ); $dsn = array( - 'phptype' => 'sqlite', + 'phptype' => 'sqlite3', 'database' => $datadir.'/'.self::$uid.'/migration.db', 'mode' => '0644' ); // Try to establish connection self::$MDB2 = MDB2::factory( $dsn, $options ); - // Die if we could not connect if( PEAR::isError( self::$MDB2 )){ + die(self::$MDB2->getMessage()); OC_Log::write('migration', 'Failed to create migration.db',OC_Log::FATAL); OC_Log::write('migration',self::$MDB2->getUserInfo(),OC_Log::FATAL); OC_Log::write('migration',self::$MDB2->getMessage(),OC_Log::FATAL); return false; + } else { } - // We always, really always want associative arrays self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); } @@ -149,7 +160,7 @@ class OC_Migrate{ OC_Log::write('migration',$entry,OC_Log::FATAL); return false; } else { - return true; + return $query; } } @@ -160,7 +171,6 @@ class OC_Migrate{ private static function processQuery( $query ){ self::connectDB(); - $type = 'sqlite'; $prefix = ''; $query = str_replace( '`', '\'', $query ); @@ -184,6 +194,12 @@ class OC_Migrate{ // Need to include 'where' in the query? if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){ + + // If only one matchval, create an array + if(!is_array($options['matchval'])){ + $options['matchval'] = array( $options['matchval'] ); + } + foreach( $options['matchval'] as $matchval ){ // Run the query for this match value (where x = y value) $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); @@ -209,7 +225,7 @@ class OC_Migrate{ // @param $options array of copyRows options // @return void private static function insertData( $data, $options ){ - while( $data = $result->fetchRow() ){ + while( $row = $data->fetchRow() ){ // Now save all this to the migration.db foreach($row as $field=>$value){ $fields[] = $field; @@ -217,14 +233,18 @@ class OC_Migrate{ } // Generate some sql - $sql = "INSERT INTO `*PREFIX*" . $options['table'] . '` ( `'; + $sql = "INSERT INTO `" . $options['table'] . '` ( `'; $fieldssql = implode( '`, `', $fields ); $sql .= $fieldssql . "` ) VALUES( "; - $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-1 ); + $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-2 ); $sql .= $valuessql . " )"; // Make the query $query = self::prepare( $sql ); - $query->execute( $values ); + if(!$query){ + OC_Log::write('migration','Invalid sql produced: '.$sql,OC_Log::FATAL); + } else { + $query->execute( $values ); + } } } @@ -232,10 +252,12 @@ class OC_Migrate{ // @param $appid string id of the app // @return bool whether the operation was successful private static function createAppTables( $appid ){ - $file = OC::$SERVERROOT.'/apps/'.$appid.'appinfo/database.xml'; + $file = OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/database.xml'; if(file_exists( $file )){ - self::connectScheme(); + if(!self::connectScheme()){ + return false; + } // There is a database.xml file $content = file_get_contents( $file ); From fa5a5649c6f7a08b319b15bd537d898e5e77ee98 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 11 Mar 2012 22:09:16 +0000 Subject: [PATCH 019/133] Fix copyRows() and sqlite connection --- apps/bookmarks/lib/migrate.php | 4 +-- lib/migrate.php | 65 ++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 9493f2cae8..6e0b5c4cc4 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -3,7 +3,7 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ // Create the xml for the user supplied function export( $uid ){ - + OC_Log::write('migration','starting export for bookmarks',OC_Log::INFO); $options = array( 'table'=>'bookmarks', 'matchcol'=>'user_id', @@ -11,7 +11,7 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ 'idcol'=>'id' ); $ids = OC_Migrate::copyRows( $options ); - $ids = array('1'); + $options = array( 'table'=>'bookmarks_tags', 'matchcol'=>'bookmark_id', diff --git a/lib/migrate.php b/lib/migrate.php index a0d702b4e2..28329a8170 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -45,13 +45,20 @@ class OC_Migrate{ * @return bool whether operation was successfull */ public static function export( $uid ){ - + // Only export database users, otherwise we get chaos if(!OC_User_Database::userExists( $uid )){ return false; } self::$uid = $uid; + + if(empty(self::$uid)){ + OC_Log::write('migration','Invalid uid passed',OC_Log::FATAL); + return false; + exit(); + } + self::connectDB(); $ok = true; $return = array(); @@ -60,13 +67,16 @@ class OC_Migrate{ foreach( self::$providers as $provider ){ // Check for database.xml if(file_exists(OC::$SERVERROOT.'/apps/'.$provider->id.'/appinfo/database.xml')){ - if(!self::createAppTables( $provider->id )){ - $ok = false; - OC_Log::write('migration','failed to create migration tables for: '.$provider->id,OC_Log::INFO); - } + $ok = self::createAppTables( $provider->id ); } if($ok){ + // Run the export function provided by the providor $return[$provider->id]['success'] = $provider->export( $uid ); + } else { + // Log the error + OC_Log::write('migration','failed to create migration tables for: '.$provider->id,OC_Log::INFO); + $return[$provider->id]['success'] = 'false'; + $return[$provider->id]['message'] = 'failed to create the app tables'; } } @@ -79,10 +89,16 @@ class OC_Migrate{ * @param $uid optional uid to use * @return bool if the import succedded */ - public static function import( $uid=null ){ + public static function import( $uid=false ){ self::$uid = $uid; + if(!self::$uid){ + OC_Log::write('migration','Tried to import without passing a uid',OC_Log::FATAL); + return false; + exit(); + } + // Connect to the db if(!self::connectDB()){ return false; @@ -104,7 +120,12 @@ class OC_Migrate{ // @breif connects to migration.db, or creates if not found // @return bool whether the operation was successful private static function connectDB(){ - + OC_Log::write('migration','connecting to migration.db for user: '.self::$uid,OC_Log::INFO); + // Fail if no user is set + if(!self::$uid){ + OC_Log::write('migration','connectDB() called without self::$uid being set',OC_Log::INFO); + return false; + } // Already connected if(!self::$MDB2){ require_once('MDB2.php'); @@ -130,7 +151,7 @@ class OC_Migrate{ // Die if we could not connect if( PEAR::isError( self::$MDB2 )){ die(self::$MDB2->getMessage()); - OC_Log::write('migration', 'Failed to create migration.db',OC_Log::FATAL); + OC_Log::write('migration', 'Failed to create/connect to migration.db',OC_Log::FATAL); OC_Log::write('migration',self::$MDB2->getUserInfo(),OC_Log::FATAL); OC_Log::write('migration',self::$MDB2->getMessage(),OC_Log::FATAL); return false; @@ -191,7 +212,9 @@ class OC_Migrate{ if( !array_key_exists( 'table', $options ) ){ return false; } - + + $return = array(); + // Need to include 'where' in the query? if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){ @@ -204,19 +227,19 @@ class OC_Migrate{ // Run the query for this match value (where x = y value) $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); $results = $query->execute( array( $matchval ) ); - self::insertData( $results, $options ); - + $return = self::insertData( $results, $options ); + //$return = array_merge( $return, $newreturns ); } } else { // Just get everything $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] ); $results = $query->execute(); - self::insertData( $results, $options ); + $return = self::insertData( $results, $options ); } - return true; + return $return; } @@ -225,8 +248,11 @@ class OC_Migrate{ // @param $options array of copyRows options // @return void private static function insertData( $data, $options ){ + $return = array(); while( $row = $data->fetchRow() ){ // Now save all this to the migration.db + $fields = array(); + $values = array(); foreach($row as $field=>$value){ $fields[] = $field; $values[] = $value; @@ -242,10 +268,21 @@ class OC_Migrate{ $query = self::prepare( $sql ); if(!$query){ OC_Log::write('migration','Invalid sql produced: '.$sql,OC_Log::FATAL); + return false; + exit(); } else { $query->execute( $values ); + // Do we need to return some values? + if( array_key_exists( 'idcol', $options ) ){ + // Yes we do + $return[] = $row[$options['idcol']]; + } else { + // Take a guess and return the first field :) + $return[] = reset($row); + } } } + return $return; } // @breif creates the tables in migration.db from an apps database.xml @@ -263,7 +300,7 @@ class OC_Migrate{ $content = file_get_contents( $file ); $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', 'migration', $content ); + $content = str_replace( '*dbname*', self::$uid.'/migration', $content ); $content = str_replace( '*dbprefix*', '', $content ); file_put_contents( $file2, $content ); From a2d7e9c6e8b951848c53dc2b159e1ff4b92642c5 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 11 Mar 2012 22:13:50 +0000 Subject: [PATCH 020/133] Merge returns from insertData() --- lib/migrate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/migrate.php b/lib/migrate.php index 28329a8170..b8dbcc475e 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -227,8 +227,8 @@ class OC_Migrate{ // Run the query for this match value (where x = y value) $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); $results = $query->execute( array( $matchval ) ); - $return = self::insertData( $results, $options ); - //$return = array_merge( $return, $newreturns ); + $newreturns = self::insertData( $results, $options ); + $return = array_merge( $return, $newreturns ); } } else { From 9c032ecc33d52438280bdf7d53fd904883ba664f Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 11 Mar 2012 22:20:01 +0000 Subject: [PATCH 021/133] Fix return value of export --- apps/bookmarks/lib/migrate.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 6e0b5c4cc4..451699ec15 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -19,7 +19,15 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ ); // Export tags - OC_Migrate::copyRows( $options ); + $ids2 = OC_Migrate::copyRows( $options ); + + // If both returned some ids then they worked + if( is_array( $ids ) && is_array( $ids2 ) ) + { + return true; + } else { + return false; + } } From 1cdb4396a4bc71ee564e73144bfdcd74a1a7493b Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Mon, 12 Mar 2012 18:40:14 +0000 Subject: [PATCH 022/133] Fix copyRows() return value. Generate app info and oc info on return --- apps/user_migrate/settings.php | 2 +- lib/migrate.php | 121 +++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index c35d46b351..c1121f2ddf 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -50,7 +50,7 @@ if (isset($_POST['user_migrate'])) { // Call to OC_Migrate for the xml file. // Create migration.db - var_dump(OC_Migrate::export(OC_User::getUser())); + OC_Migrate::export(OC_User::getUser()); // Add export db to zip $zip->addFile($root.'data/'.$user.'/migration.db', "migration.db"); diff --git a/lib/migrate.php b/lib/migrate.php index b8dbcc475e..95da4514fc 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -65,20 +65,42 @@ class OC_Migrate{ // Foreach provider foreach( self::$providers as $provider ){ - // Check for database.xml + + $failed = false; + + // Does this app use the database? if(file_exists(OC::$SERVERROOT.'/apps/'.$provider->id.'/appinfo/database.xml')){ - $ok = self::createAppTables( $provider->id ); + // Create some app tables + $tables = self::createAppTables( $provider->id ); + if( is_array( $tables ) ){ + // Save the table names + foreach($tables as $table){ + $return['app'][$provider->id]['tables'][] = $table; + } + } else { + // It failed to create the tables + $failed = true; + } } - if($ok){ - // Run the export function provided by the providor - $return[$provider->id]['success'] = $provider->export( $uid ); + + // Run the import function? + if( !$failed ){ + $return['app'][$provider->id]['success'] = $provider->export( $uid ); } else { - // Log the error - OC_Log::write('migration','failed to create migration tables for: '.$provider->id,OC_Log::INFO); - $return[$provider->id]['success'] = 'false'; - $return[$provider->id]['message'] = 'failed to create the app tables'; + $return['app'][$provider->id]['success'] = false; + $return['app'][$provider->id]['message'] = 'failed to create the app tables'; } + + // Now add some app info the the return array + $appinfo = OC_App::getAppInfo( $provider->id ); + $return['app'][$provider->id]['version'] = $appinfo['version']; + } + + + // Add some general info to the return array + $return['migrateinfo']['uid'] = $uid; + $return['migrateinfo']['ocversion'] = OC_Util::getVersionString(); return $return; @@ -289,50 +311,49 @@ class OC_Migrate{ // @param $appid string id of the app // @return bool whether the operation was successful private static function createAppTables( $appid ){ - $file = OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/database.xml'; - if(file_exists( $file )){ - if(!self::connectScheme()){ - return false; - } - - // There is a database.xml file - $content = file_get_contents( $file ); - - $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', self::$uid.'/migration', $content ); - $content = str_replace( '*dbprefix*', '', $content ); - - file_put_contents( $file2, $content ); - - // Try to create tables - $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - - unlink( $file2 ); - - // Die in case something went wrong - if( $definition instanceof MDB2_Schema_Error ){ - OC_Log::write('migration','Failed to parse database.xml for: '.$appid,OC_Log::FATAL); - OC_Log::write('migration',$definition->getMessage().': '.$definition->getUserInfo(),OC_Log::FATAL); - return false; - } - - $definition['overwrite'] = true; - - $ret = self::$schema->createDatabase( $definition ); - // Die in case something went wrong - - if( $ret instanceof MDB2_Error ){ - OC_Log::write('migration','Failed to create tables for: '.$appid,OC_Log::FATAL); - OC_Log::write('migration',$ret->getMessage().': '.$ret->getUserInfo(),OC_Log::FATAL); - return false; - } - return true; - - } else { - // No database.xml + if(!self::connectScheme()){ return false; } + + // There is a database.xml file + $content = file_get_contents( OC::$SERVERROOT . '/apps/' . $appid . '/appinfo/database.xml' ); + + $file2 = 'static://db_scheme'; + $content = str_replace( '*dbname*', self::$uid.'/migration', $content ); + $content = str_replace( '*dbprefix*', '', $content ); + + $xml = new SimpleXMLElement($content); + foreach($xml->table as $table){ + $tables[] = (string)$table->name; + } + + file_put_contents( $file2, $content ); + + // Try to create tables + $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); + + unlink( $file2 ); + + // Die in case something went wrong + if( $definition instanceof MDB2_Schema_Error ){ + OC_Log::write('migration','Failed to parse database.xml for: '.$appid,OC_Log::FATAL); + OC_Log::write('migration',$definition->getMessage().': '.$definition->getUserInfo(),OC_Log::FATAL); + return false; + } + + $definition['overwrite'] = true; + + $ret = self::$schema->createDatabase( $definition ); + // Die in case something went wrong + + if( $ret instanceof MDB2_Error ){ + OC_Log::write('migration','Failed to create tables for: '.$appid,OC_Log::FATAL); + OC_Log::write('migration',$ret->getMessage().': '.$ret->getUserInfo(),OC_Log::FATAL); + return false; + } + return $tables; + } From d108bdc7c7940d23355c9a16f3f355387bbb66ef Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Mon, 12 Mar 2012 21:41:32 +0000 Subject: [PATCH 023/133] Improved import function. Added param to connectDB() to load the db from the import --- lib/migrate.php | 66 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/lib/migrate.php b/lib/migrate.php index 95da4514fc..075358c72b 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -30,6 +30,7 @@ class OC_Migrate{ static private $providers=array(); static private $schema=false; static private $uid=false; + static private $database=false; /** * register a new migration provider @@ -108,40 +109,75 @@ class OC_Migrate{ /** * @breif imports a new user + * @param $db string path to migration.db + * @param $migrateinfo string path to the migration info json file * @param $uid optional uid to use * @return bool if the import succedded */ - public static function import( $uid=false ){ - - self::$uid = $uid; - + public static function import( $db, $migrateinfo, $uid=false ){ + if(!self::$uid){ OC_Log::write('migration','Tried to import without passing a uid',OC_Log::FATAL); return false; exit(); } - // Connect to the db - if(!self::connectDB()){ - return false; + // Check if the db exists + if( file_exists( $db ) ){ + // Connect to the db + if(!self::connectDB( $db )){ + return false; + exit(); + } + } else { + OC_Log::write('migration','Migration.db not found at: '.$db, OC_Log::FATAL ); + return false; + exit(); } + // Load the json info + if( file_exists( $migrateinfo ) ){ + + } else { + OC_Log::write( 'migration', 'Migration information file not found at: '.$migrateinfo, OC_Log::FATAL ); + return false; + exit(); + } + + // Process migration info + $info = file_get_contents( $migrateinfo ); + $info = json_decode( $info ); + + // Set the user id + self::$uid = !$uid : $info['migrateinfo']['uid'] ? $uid; + // Create the user if(!self::createUser($uid, $hash)){ return false; + exit(); } + + $apps = $info['apps']; - // Now get the list of apps to import from migration.db - // Then check for migrate.php for these apps - // If present, run the import function for them. + foreach( self::$providers as $provider){ + // Is the app in the export? + if( array_key_exists( $provider->id, $apps ) ){ + // Did it succeed? + if( $app[$provider->id] ){ + // Then do the import + $provider->import(); + } + } + } return true; } // @breif connects to migration.db, or creates if not found + // @param $db optional path to migration.db, defaults to user data dir // @return bool whether the operation was successful - private static function connectDB(){ + private static function connectDB( $db=null ){ OC_Log::write('migration','connecting to migration.db for user: '.self::$uid,OC_Log::INFO); // Fail if no user is set if(!self::$uid){ @@ -152,6 +188,8 @@ class OC_Migrate{ if(!self::$MDB2){ require_once('MDB2.php'); + self::$database = !is_null( $db ) ? $db : $datadir.'/'.self::$uid.'/migration.db'; + $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); // Prepare options array @@ -164,7 +202,7 @@ class OC_Migrate{ ); $dsn = array( 'phptype' => 'sqlite3', - 'database' => $datadir.'/'.self::$uid.'/migration.db', + 'database' => self::$database, 'mode' => '0644' ); @@ -392,7 +430,9 @@ class OC_Migrate{ // Create the user $query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" ); $result = $query->execute( array( $uid, $data['hash'])); - + if( !$result ){ + OC_Log::write('migration', 'Failed to create the new user "'.$uid.""); + } return $result ? true : false; } From 4d5646a59f813e09455c78d840d0f62397ec60ad Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 16:21:17 +0000 Subject: [PATCH 024/133] Find migrate.php even for disabled apps. Improve ui for user and admin migrations --- apps/admin_export/settings.php | 17 ++--- apps/bookmarks/appinfo/app.php | 4 -- apps/bookmarks/lib/migrate.php | 1 + apps/user_migrate/settings.php | 91 ++++++++++++++---------- apps/user_migrate/templates/settings.php | 17 +++-- lib/migrate.php | 49 ++++++------- 6 files changed, 100 insertions(+), 79 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 5584181fbb..b60557f350 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -74,15 +74,16 @@ if (isset($_POST['admin_export'])) { } if (isset($_POST['user_files'])) { - // needs to handle data outside of the default data dir. - // adding user files - $zip->addFile($root . '/data/.htaccess', "data/.htaccess"); - $zip->addFile($root . '/data/index.html', "data/index.html"); - foreach (OC_User::getUsers() as $i) { - OC_Log::write('admin_export',"Adding owncloud user files of $i to export",OC_Log::INFO); - zipAddDir($root . "data/" . $i, $zip, true, "/data/"); - } + // needs to handle data outside of the default data dir. + // adding user files + $zip->addFile($root . '/data/.htaccess', "data/.htaccess"); + $zip->addFile($root . '/data/index.html', "data/index.html"); + foreach (OC_User::getUsers() as $i) { + OC_Log::write('admin_export',"Adding owncloud user files of $i to export",OC_Log::INFO); + zipAddDir($root . "data/" . $i, $zip, true, "/data/"); + } } + $zip->close(); header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=" . basename($filename)); diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php index 13d76e0817..b9c308ca05 100644 --- a/apps/bookmarks/appinfo/app.php +++ b/apps/bookmarks/appinfo/app.php @@ -18,8 +18,4 @@ OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'hr OC_App::registerPersonal('bookmarks', 'settings'); OC_Util::addScript('bookmarks','bookmarksearch'); -// Include the migration provider - -require_once('apps/bookmarks/lib/migrate.php'); - OC_Search::registerProvider('OC_Search_Provider_Bookmarks'); diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 451699ec15..8387f70603 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -54,4 +54,5 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } +// Load the provider new OC_Migrate_Provider_Bookmarks( 'bookmarks' ); \ No newline at end of file diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index c1121f2ddf..3b82e148b5 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -3,6 +3,8 @@ /** * ownCloud - user_migrate * + * @author Thomas Schmidt + * @copyright 2011 Thomas Schmidt tom@opensuse.org * @author Tom Needham * @copyright 2012 Tom Needham tom@owncloud.com * @@ -22,52 +24,67 @@ */ OC_Util::checkAppEnabled('user_migrate'); +define('DS', '/'); -if (isset($_POST['user_migrate'])) { - // Looks like they want to migrate - $root = OC::$SERVERROOT . "/"; - $user = OC_User::getUser(); +if (isset($_POST['user_export'])) { + + // Setup the export $zip = new ZipArchive(); - $tempdir = get_temp_dir(); - $filename = $tempdir . "/" . $user . "_export_" . date("y-m-d_H-i-s") . ".zip"; - OC_Log::write('user_migrate',"Creating user export file at: " . $filename,OC_Log::INFO); + $tmp = get_temp_dir(); + $user = OC_User::getUser(); + // Create owncoud dir + if( !file_exists( $tmp . '/owncloud' ) ){ + if( !mkdir( $tmp . '/owncloud' ) ){ + die('Failed to create the owncloud tmp directory'); + } + } + // Create the export dir + $exportdir = $tmp . '/owncloud' . '/export_' . $user . '_' . date("y-m-d_H-i-s"); + if( !file_exists( $exportdir ) ){ + if( !mkdir( $exportdir ) ){ + die('Failed to create the owncloud export directory'); + } + } + $filename = $exportdir . '/owncloud_export_' . $user . '_' . date("y-m-d_H-i-s") . ".zip"; + OC_Log::write('user_migrate',"Creating export file at: " . $filename,OC_Log::INFO); if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) { exit("Cannot open <$filename>\n"); } + + // Migrate the app info + $info = OC_Migrate::export( $user ); + $infofile = $exportdir . '/exportinfo.json'; + if( !file_put_contents( $infofile, $info ) ){ + die('Failed to save the export info'); + } + $zip->addFile( $infofile, "exportinfo.json"); + $zip->addFile(OC::$SERVERROOT . '/data/' . $user . '/migration.db', "migration.db"); - // Does the user want to include their files? - if (isset($_POST['user_files'])) { - // needs to handle data outside of the default data dir. - // adding user files - OC_Log::write('user_migrate',"Adding owncloud user files of $user to export",OC_Log::INFO); - zipAddDir($root . "data/" . $user, $zip, true, "files/"); - } - - // Does the user want their app data? - if (isset($_POST['user_appdata'])) { - // adding owncloud system files - OC_Log::write('user_migrate',"Adding app data to user export file",OC_Log::INFO); - // Call to OC_Migrate for the xml file. - - // Create migration.db - OC_Migrate::export(OC_User::getUser()); - // Add export db to zip - $zip->addFile($root.'data/'.$user.'/migration.db', "migration.db"); - - } - + // Add the data dir + zipAddDir(OC::$SERVERROOT . "/data/" . $user, $zip, true, "files/"); + + // Save the zip $zip->close(); - - //header("Content-Type: application/zip"); - //header("Content-Disposition: attachment; filename=" . basename($filename)); - //header("Content-Length: " . filesize($filename)); - //readfile($filename); - //unlink($filename); -} else { -// fill template + // Send the zip + header("Content-Type: application/zip"); + header("Content-Disposition: attachment; filename=" . basename($filename)); + header("Content-Length: " . filesize($filename)); + @ob_end_clean(); + readfile($filename); + // Cleanup + unlink($filename); + unlink($infofile); + rmdir($exportdir); + +} if( isset( $_POST['user_import'] ) ){ + // TODO +}else { + + // fill template $tmpl = new OC_Template('user_migrate', 'settings'); return $tmpl->fetchPage(); + } function zipAddDir($dir, $zip, $recursive=true, $internalDir='') { @@ -89,6 +106,6 @@ function zipAddDir($dir, $zip, $recursive=true, $internalDir='') { } closedir($dirhandle); } else { - OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); + OC_Log::write('user_migrate',"Was not able to open directory: " . $dir,OC_Log::ERROR); } } diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php index ece8f70e06..59a27a926d 100644 --- a/apps/user_migrate/templates/settings.php +++ b/apps/user_migrate/templates/settings.php @@ -1,12 +1,17 @@
t('Export your user account');?> -

t('This will create a compressed file that contains the data of owncloud account. - Please choose which components should be included:');?> +

t('This will create a compressed file that contains your ownCloud account.');?>

-


-
-

- + +
+ +
+
+ t('Import user account');?> +

+

+

+
diff --git a/lib/migrate.php b/lib/migrate.php index 075358c72b..88c0e7cfc2 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -26,11 +26,16 @@ */ class OC_Migrate{ + // Holds the db object static private $MDB2=false; + // Array of OC_Migration_Provider objects static private $providers=array(); + // Schema db object static private $schema=false; + // User id of the user to import/export static private $uid=false; - static private $database=false; + // Path to the sqlite db + static private $dbpath=false; /** * register a new migration provider @@ -64,9 +69,18 @@ class OC_Migrate{ $ok = true; $return = array(); + // Find the providers + $apps = OC_App::getAllApps(); + + foreach($apps as $app){ + $path = OC::$SERVERROOT . '/apps/' . $app . '/lib/migrate.php'; + if( file_exists( $path ) ){ + include( $path ); + } + } + // Foreach provider foreach( self::$providers as $provider ){ - $failed = false; // Does this app use the database? @@ -110,7 +124,7 @@ class OC_Migrate{ /** * @breif imports a new user * @param $db string path to migration.db - * @param $migrateinfo string path to the migration info json file + * @param $migrateinfo array of migration ino * @param $uid optional uid to use * @return bool if the import succedded */ @@ -135,27 +149,14 @@ class OC_Migrate{ exit(); } - // Load the json info - if( file_exists( $migrateinfo ) ){ - - } else { - OC_Log::write( 'migration', 'Migration information file not found at: '.$migrateinfo, OC_Log::FATAL ); + if( !is_array( $migrateinfo ) ){ + OC_Log::write('migration','$migrateinfo is not an array', OC_Log::FATAL); return false; exit(); } - // Process migration info - $info = file_get_contents( $migrateinfo ); - $info = json_decode( $info ); - // Set the user id - self::$uid = !$uid : $info['migrateinfo']['uid'] ? $uid; - - // Create the user - if(!self::createUser($uid, $hash)){ - return false; - exit(); - } + self::$uid = $info['migrateinfo']['uid']; $apps = $info['apps']; @@ -177,7 +178,7 @@ class OC_Migrate{ // @breif connects to migration.db, or creates if not found // @param $db optional path to migration.db, defaults to user data dir // @return bool whether the operation was successful - private static function connectDB( $db=null ){ + private static function connectDB( $dbpath=null ){ OC_Log::write('migration','connecting to migration.db for user: '.self::$uid,OC_Log::INFO); // Fail if no user is set if(!self::$uid){ @@ -188,10 +189,10 @@ class OC_Migrate{ if(!self::$MDB2){ require_once('MDB2.php'); - self::$database = !is_null( $db ) ? $db : $datadir.'/'.self::$uid.'/migration.db'; - $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); + self::$dbpath = $datadir.'/'.self::$uid.'/migration.db';//!is_null( $dbpath ) ? $dbpath : $datadir.'/'.self::$uid.'/migration.db'; + // Prepare options array $options = array( 'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE), @@ -202,10 +203,10 @@ class OC_Migrate{ ); $dsn = array( 'phptype' => 'sqlite3', - 'database' => self::$database, + 'database' => self::$dbpath, 'mode' => '0644' ); - + // Try to establish connection self::$MDB2 = MDB2::factory( $dsn, $options ); // Die if we could not connect From 111af7fed5a03d45793536c23ecc5c25bfc1d36f Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 17:04:49 +0000 Subject: [PATCH 025/133] json encode the output --- apps/user_migrate/settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 3b82e148b5..edad7357a2 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -52,7 +52,7 @@ if (isset($_POST['user_export'])) { } // Migrate the app info - $info = OC_Migrate::export( $user ); + $info = json_encode( OC_Migrate::export( $user ) ); $infofile = $exportdir . '/exportinfo.json'; if( !file_put_contents( $infofile, $info ) ){ die('Failed to save the export info'); From 7e3b35a57c53b6e90cb8560d7b4c90e3aba2a3b5 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 17:08:20 +0000 Subject: [PATCH 026/133] fix structure of user export zip --- apps/user_migrate/settings.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index edad7357a2..00f46660cf 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -53,15 +53,11 @@ if (isset($_POST['user_export'])) { // Migrate the app info $info = json_encode( OC_Migrate::export( $user ) ); - $infofile = $exportdir . '/exportinfo.json'; - if( !file_put_contents( $infofile, $info ) ){ - die('Failed to save the export info'); - } - $zip->addFile( $infofile, "exportinfo.json"); - $zip->addFile(OC::$SERVERROOT . '/data/' . $user . '/migration.db', "migration.db"); + $infofile = OC::$SERVERROOT . '/data/' . $user . '/exportinfo.json'; + file_put_contents( $infofile, $info ); - // Add the data dir - zipAddDir(OC::$SERVERROOT . "/data/" . $user, $zip, true, "files/"); + // Add the data dir (which includes migration.db and exportinfo.json) + zipAddDir(OC::$SERVERROOT . "/data/" . $user, $zip, true, "/"); // Save the zip $zip->close(); From 0f3eebbbd95b75e3dfe8f9322d8fd486925ac54a Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 17:18:42 +0000 Subject: [PATCH 027/133] added cleanUp() method to OC_Migrate --- apps/user_migrate/settings.php | 2 +- lib/migrate.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 00f46660cf..c017ba226c 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -70,8 +70,8 @@ if (isset($_POST['user_export'])) { readfile($filename); // Cleanup unlink($filename); - unlink($infofile); rmdir($exportdir); + OC_Migrate::cleanUp(); } if( isset( $_POST['user_import'] ) ){ // TODO diff --git a/lib/migrate.php b/lib/migrate.php index 88c0e7cfc2..1be229d8fd 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -438,4 +438,19 @@ class OC_Migrate{ } + /** + * @breif removes migration.db and exportinfo.json from the users data dir + * @return void + */ + static public function cleanUp(){ + if( !self::$uid ){ + OC_Log::write('migration', 'Failed to cleanup after migration', OC_Log::ERROR); + return false; + } + // Remove migration.db + unlink( OC::$SERVERROOT . '/data/' . self::$uid . '/migration.db' ); + // Remove exportinfo.json + unlink( OC::$SERVERROOT . '/data/' . self::$uid . '/exportinfo.json' ); + return true; + } } From cd2f75fdad6629f850bb07f07cd45e04d75de97d Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 17:27:47 +0000 Subject: [PATCH 028/133] Use data dir from config.php --- apps/admin_export/settings.php | 4 ++-- apps/user_migrate/settings.php | 22 +++++----------------- lib/migrate.php | 5 +++-- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index b60557f350..73a4209d3f 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -31,6 +31,7 @@ define('DS', '/'); if (isset($_POST['admin_export'])) { $root = OC::$SERVERROOT . "/"; + $datadir = OC_Config::getValue( 'datadirectory' ); $zip = new ZipArchive(); $tempdir = get_temp_dir(); $filename = $tempdir . "/owncloud_export_" . date("y-m-d_H-i-s") . ".zip"; @@ -70,7 +71,6 @@ if (isset($_POST['admin_export'])) { OC_Log::write('admin_export',"Adding owncloud config to export",OC_Log::INFO); zipAddDir($root . "config/", $zip, true, "/"); - $zip->addFile($root . '/data/.htaccess', "data/owncloud.db"); } if (isset($_POST['user_files'])) { @@ -80,7 +80,7 @@ if (isset($_POST['admin_export'])) { $zip->addFile($root . '/data/index.html', "data/index.html"); foreach (OC_User::getUsers() as $i) { OC_Log::write('admin_export',"Adding owncloud user files of $i to export",OC_Log::INFO); - zipAddDir($root . "data/" . $i, $zip, true, "/data/"); + zipAddDir($datadir . '/' . $i, $zip, true, "/data/"); } } diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index c017ba226c..5e8ac9c21d 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -32,20 +32,9 @@ if (isset($_POST['user_export'])) { $zip = new ZipArchive(); $tmp = get_temp_dir(); $user = OC_User::getUser(); - // Create owncoud dir - if( !file_exists( $tmp . '/owncloud' ) ){ - if( !mkdir( $tmp . '/owncloud' ) ){ - die('Failed to create the owncloud tmp directory'); - } - } - // Create the export dir - $exportdir = $tmp . '/owncloud' . '/export_' . $user . '_' . date("y-m-d_H-i-s"); - if( !file_exists( $exportdir ) ){ - if( !mkdir( $exportdir ) ){ - die('Failed to create the owncloud export directory'); - } - } - $filename = $exportdir . '/owncloud_export_' . $user . '_' . date("y-m-d_H-i-s") . ".zip"; + + $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . $user; + $filename = $userdatadir . '/owncloud_export_' . $user . '_' . date("y-m-d_H-i-s") . ".zip"; OC_Log::write('user_migrate',"Creating export file at: " . $filename,OC_Log::INFO); if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) { exit("Cannot open <$filename>\n"); @@ -53,11 +42,11 @@ if (isset($_POST['user_export'])) { // Migrate the app info $info = json_encode( OC_Migrate::export( $user ) ); - $infofile = OC::$SERVERROOT . '/data/' . $user . '/exportinfo.json'; + $infofile = $userdatadir . '/exportinfo.json'; file_put_contents( $infofile, $info ); // Add the data dir (which includes migration.db and exportinfo.json) - zipAddDir(OC::$SERVERROOT . "/data/" . $user, $zip, true, "/"); + zipAddDir( $userdatadir, $zip, true, "/" ); // Save the zip $zip->close(); @@ -70,7 +59,6 @@ if (isset($_POST['user_export'])) { readfile($filename); // Cleanup unlink($filename); - rmdir($exportdir); OC_Migrate::cleanUp(); } if( isset( $_POST['user_import'] ) ){ diff --git a/lib/migrate.php b/lib/migrate.php index 1be229d8fd..a6a6432d0d 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -447,10 +447,11 @@ class OC_Migrate{ OC_Log::write('migration', 'Failed to cleanup after migration', OC_Log::ERROR); return false; } + $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . self::$uid; // Remove migration.db - unlink( OC::$SERVERROOT . '/data/' . self::$uid . '/migration.db' ); + unlink( $userdatadir . '/migration.db' ); // Remove exportinfo.json - unlink( OC::$SERVERROOT . '/data/' . self::$uid . '/exportinfo.json' ); + unlink( $userdatadir . '/exportinfo.json' ); return true; } } From a919a136c418f99bded289034090b4e8d4e23533 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 21:24:07 +0000 Subject: [PATCH 029/133] Finish import function for bookmarks --- apps/bookmarks/lib/migrate.php | 42 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 8387f70603..4f11bc5bde 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -32,24 +32,36 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } // Import function for bookmarks - function import( $data, $uid ){ + function import( $info ){ - // new id mapping - $newids = array(); - - // Import bookmarks - foreach($data['bookmarks'] as $bookmark){ - $bookmark['user_id'] = $uid; - // import to the db now - $newids[$bookmark['id']] = OC_DB::insertid(); + switch( $info['appversion'] ){ + default: + // All versions of the app have had the same db structure, so all can use the same import function + $query = OC_Migrate::prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" ); + $results = $query->execute( array( $info['olduid'] ) ); + $idmap = array(); + while( $row = $data->fetchRow() ){ + // Import each bookmark, saving its id into the map + $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" ); + $query->execute( array( $row['url'], $row['title'], $info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); + // Map the id + $idmap[$row['id']] = OC_DB::insertid(); + } + // Now tags + foreach($idmap as $oldid => $newid){ + $query = OC_Migrate::prepare( "SELECT * FROM bookmarks_tags WHERE user_id LIKE ?" ); + $results = $query->execute( array( $oldid ) ); + while( $row = $data->fetchRow() ){ + // Import the tags for this bookmark, using the new bookmark id + $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks_tags(bookmark_id, tag) VALUES (?, ?)" ); + $query->execute( array( $newid, $row['tag'] ) ); + } + } + // All done! + break; } - // Import tags - foreach($data['bookmarks_tags'] as $tag){ - // Map the new ids - $tag['id'] = $newids[$tag['id']]; - // Import to the db now using OC_DB - } + return true; } } From fa8b66ca4f266ebc72ca204e85cf0e69c4c4aa25 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 21:28:53 +0000 Subject: [PATCH 030/133] Add getApps() method to return apps supporting migration --- lib/migrate.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/migrate.php b/lib/migrate.php index a6a6432d0d..863cf261f4 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -121,6 +121,21 @@ class OC_Migrate{ } + /** + * @breif returns an array of apps that support migration + * @return array + */ + static public function getApps(){ + $allapps = OC_App::getAllApps(); + foreach($allapps as $app){ + $path = OC::$SERVERROOT . '/apps/' . $app . '/lib/migrate.php'; + if( file_exists( $path ) ){ + $supportsmigration[] = $app; + } + } + reutrn $supportsmigration; + } + /** * @breif imports a new user * @param $db string path to migration.db From 5a50144a16fa9b5d8caf9ee261e3c4a39eaa04bc Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 21:29:31 +0000 Subject: [PATCH 031/133] typo :/ --- lib/migrate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/migrate.php b/lib/migrate.php index 863cf261f4..acc01ec7bb 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -133,7 +133,7 @@ class OC_Migrate{ $supportsmigration[] = $app; } } - reutrn $supportsmigration; + return $supportsmigration; } /** From a310a81053c31205abd6d62491304705b1f565e2 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 13 Mar 2012 23:09:43 +0000 Subject: [PATCH 032/133] move zip creation inside OC_Migrate --- apps/user_migrate/settings.php | 77 +++++------------------ lib/migrate.php | 112 +++++++++++++++++++++++++++------ lib/migrate/provider.php | 5 +- 3 files changed, 111 insertions(+), 83 deletions(-) diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 5e8ac9c21d..9fbb4da9e5 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -24,72 +24,27 @@ */ OC_Util::checkAppEnabled('user_migrate'); -define('DS', '/'); - if (isset($_POST['user_export'])) { - - // Setup the export - $zip = new ZipArchive(); - $tmp = get_temp_dir(); - $user = OC_User::getUser(); - - $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . $user; - $filename = $userdatadir . '/owncloud_export_' . $user . '_' . date("y-m-d_H-i-s") . ".zip"; - OC_Log::write('user_migrate',"Creating export file at: " . $filename,OC_Log::INFO); - if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) { - exit("Cannot open <$filename>\n"); - } - - // Migrate the app info - $info = json_encode( OC_Migrate::export( $user ) ); - $infofile = $userdatadir . '/exportinfo.json'; - file_put_contents( $infofile, $info ); - - // Add the data dir (which includes migration.db and exportinfo.json) - zipAddDir( $userdatadir, $zip, true, "/" ); - - // Save the zip - $zip->close(); - - // Send the zip - header("Content-Type: application/zip"); - header("Content-Disposition: attachment; filename=" . basename($filename)); - header("Content-Length: " . filesize($filename)); - @ob_end_clean(); - readfile($filename); - // Cleanup - unlink($filename); - OC_Migrate::cleanUp(); - + // Create the export zip + $user = OC_User::getUser(); + $path = OC_Config::getValue( 'datadirectory' ) . '/' . OC_User::getUser() . '/'; + if( OC_Migrate::createExportFile( $user, $path ) ){ + // Download it then + header("Content-Type: application/zip"); + header("Content-Disposition: attachment; filename=" . basename($path)); + header("Content-Length: " . filesize($path)); + @ob_end_clean(); + readfile($path); + OC_Migrate::cleanUp(); + } else { + die('error'); + } } if( isset( $_POST['user_import'] ) ){ // TODO }else { - // fill template - $tmpl = new OC_Template('user_migrate', 'settings'); - return $tmpl->fetchPage(); - + $tmpl = new OC_Template('user_migrate', 'settings'); + return $tmpl->fetchPage(); } -function zipAddDir($dir, $zip, $recursive=true, $internalDir='') { - $dirname = basename($dir); - $zip->addEmptyDir($internalDir . $dirname); - $internalDir.=$dirname.='/'; - if ($dirhandle = opendir($dir)) { - while (false !== ( $file = readdir($dirhandle))) { - - if (( $file != '.' ) && ( $file != '..' )) { - - if (is_dir($dir . '/' . $file) && $recursive) { - zipAddDir($dir . '/' . $file, $zip, $recursive, $internalDir); - } elseif (is_file($dir . '/' . $file)) { - $zip->addFile($dir . '/' . $file, $internalDir . $file); - } - } - } - closedir($dirhandle); - } else { - OC_Log::write('user_migrate',"Was not able to open directory: " . $dir,OC_Log::ERROR); - } -} diff --git a/lib/migrate.php b/lib/migrate.php index acc01ec7bb..b4c4d635ff 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -36,6 +36,10 @@ class OC_Migrate{ static private $uid=false; // Path to the sqlite db static private $dbpath=false; + // Holds the ZipArchive object + static private $zip=false; + // String path to export + static private $zippath=false; /** * register a new migration provider @@ -47,24 +51,10 @@ class OC_Migrate{ /** * @breif creates a migration.db in the users data dir with their app data in - * @param @uid string userid of the user to export for * @return bool whether operation was successfull */ - public static function export( $uid ){ - - // Only export database users, otherwise we get chaos - if(!OC_User_Database::userExists( $uid )){ - return false; - } - - self::$uid = $uid; - - if(empty(self::$uid)){ - OC_Log::write('migration','Invalid uid passed',OC_Log::FATAL); - return false; - exit(); - } - + private static function exportAppData( ){ + self::connectDB(); $ok = true; $return = array(); @@ -100,7 +90,7 @@ class OC_Migrate{ // Run the import function? if( !$failed ){ - $return['app'][$provider->id]['success'] = $provider->export( $uid ); + $return['app'][$provider->id]['success'] = $provider->export( self::$uid ); } else { $return['app'][$provider->id]['success'] = false; $return['app'][$provider->id]['message'] = 'failed to create the app tables'; @@ -114,13 +104,95 @@ class OC_Migrate{ // Add some general info to the return array - $return['migrateinfo']['uid'] = $uid; + $return['migrateinfo']['uid'] = self::$uid; $return['migrateinfo']['ocversion'] = OC_Util::getVersionString(); return $return; } + /** + * @breif creates a zip user export + * @param $uid string user id of the user to export + * @param $path string path to folder to create file in (with trailing slash) + * @return bool success + */ + static public function createExportFile( $uid, $path ){ + // Is a directory + if( !is_dir( $path ) ){ + OC_Log::write('migration', 'Path supplied to createExportFile() is not a directory', OC_Log::ERROR); + return false; + exit(); + } + // Is writeable + if( !is_writeable( $path ) ){ + OC_Log::write('migration', 'Path supplied to createExportFile() is not writeable', OC_Log::ERROR); + return false; + exit(); + } + // Is a database user? + if( !OC_User_Database::userExists( $uid ) ){ + OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); + return false; + exit(); + } + + self::$uid = $uid; + self::$zip = new ZipArchive; + + // Get some info + $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . self::$uid; + self::$zippath = $path . 'owncloud_export_' . self::$uid . '_' . date("y-m-d_H-i-s") . ".zip"; + if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE ) !== TRUE ) { + OC_Log::write('migration','Cannot create a zip file at: '.self::$zippath, OC_Log::ERROR); + return false; + exit(); + } + + // Export the app info + $info = json_encode( self::exportAppData() ); + file_put_contents( $userdatadir . '/exportinfo.json', $info ); + + // Add the data dir (which includes migration.db and exportinfo.json) + self::addDirToZip( $userdatadir, '/' ); + + // All done! + if( !self::$zip->close() ){ + OC_Log::write('migration', 'Failed to save the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); + return false; + exit(); + } else { + OC_Log::write('migration', 'Created export file for: '.self::$uid, OC_Log::INFO); + return true; + } + + + } + + /** + * @breif adds a directory to the zip object + * @return void + */ + static private function addDirToZip( $dir, $recursive=true, $internalDir='' ){ + $dirname = basename($dir); + self::$zip->addEmptyDir($internalDir . $dirname); + $internalDir.=$dirname.='/'; + if ($dirhandle = opendir($dir)) { + while (false !== ( $file = readdir($dirhandle))) { + if (( $file != '.' ) && ( $file != '..' )) { + if (is_dir($dir . '/' . $file) && $recursive) { + self::addDirToZip($dir . '/' . $file, $recursive, $internalDir); + } elseif (is_file($dir . '/' . $file)) { + self::$zip->addFile($dir . '/' . $file, $internalDir . $file); + } + } + } + closedir($dirhandle); + } else { + OC_Log::write('migration',"Was not able to open directory: " . $dir,OC_Log::ERROR); + } + } + /** * @breif returns an array of apps that support migration * @return array @@ -143,7 +215,7 @@ class OC_Migrate{ * @param $uid optional uid to use * @return bool if the import succedded */ - public static function import( $db, $migrateinfo, $uid=false ){ + public static function importAppData( $db, $migrateinfo, $uid=false ){ if(!self::$uid){ OC_Log::write('migration','Tried to import without passing a uid',OC_Log::FATAL); @@ -467,6 +539,8 @@ class OC_Migrate{ unlink( $userdatadir . '/migration.db' ); // Remove exportinfo.json unlink( $userdatadir . '/exportinfo.json' ); + // Remove the zip + unlink(self::$zippath); return true; } } diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php index 9c03639b7c..e2e01b3b5a 100644 --- a/lib/migrate/provider.php +++ b/lib/migrate/provider.php @@ -20,9 +20,8 @@ abstract class OC_Migrate_Provider{ /** * @breif imports data for the app - * @param $data array of data. eg: array('info'=> APPINFO, 'data'=>APPDATA ARRAY) - * @param $info array of info of the source install + * @param $info array of info including exportinfo.json * @return void */ - abstract function import($data,$uid); + abstract function import( $info ); } From c9be325af2707b256f83cafbda3f7e3713f97876 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Wed, 14 Mar 2012 16:43:06 +0000 Subject: [PATCH 033/133] Fix zip creation. Add param to cleanUp() method. Add defaults to createExportFile() method. --- apps/user_migrate/settings.php | 15 ++-- lib/migrate.php | 122 ++++++++++++++++++--------------- 2 files changed, 75 insertions(+), 62 deletions(-) diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 9fbb4da9e5..04aca51f51 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -26,22 +26,21 @@ OC_Util::checkAppEnabled('user_migrate'); if (isset($_POST['user_export'])) { // Create the export zip - $user = OC_User::getUser(); - $path = OC_Config::getValue( 'datadirectory' ) . '/' . OC_User::getUser() . '/'; - if( OC_Migrate::createExportFile( $user, $path ) ){ - // Download it then + if( !$path = OC_Migrate::createExportFile() ){ + // Error + die('error'); + } else { + // Download it header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=" . basename($path)); header("Content-Length: " . filesize($path)); @ob_end_clean(); readfile($path); - OC_Migrate::cleanUp(); - } else { - die('error'); + OC_Migrate::cleanUp( $path ); } } if( isset( $_POST['user_import'] ) ){ // TODO -}else { +} else { // fill template $tmpl = new OC_Template('user_migrate', 'settings'); return $tmpl->fetchPage(); diff --git a/lib/migrate.php b/lib/migrate.php index b4c4d635ff..728f15e1f6 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -113,85 +113,100 @@ class OC_Migrate{ /** * @breif creates a zip user export - * @param $uid string user id of the user to export - * @param $path string path to folder to create file in (with trailing slash) - * @return bool success + * @param optional $uid string user id of the user to export (defaults to current) + * @param optional $path string path to folder to create file in (with trailing slash) (defaults to current user's data dir) + * @return false on failure | string path on success */ - static public function createExportFile( $uid, $path ){ - // Is a directory - if( !is_dir( $path ) ){ - OC_Log::write('migration', 'Path supplied to createExportFile() is not a directory', OC_Log::ERROR); - return false; - exit(); - } - // Is writeable - if( !is_writeable( $path ) ){ - OC_Log::write('migration', 'Path supplied to createExportFile() is not writeable', OC_Log::ERROR); - return false; - exit(); - } + static public function createExportFile( $uid=null, $path=null ){ + // User passed? + $uid = is_null( $uid ) ? OC_User::getUser() : $uid ; // Is a database user? if( !OC_User_Database::userExists( $uid ) ){ OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); return false; exit(); } - + // Set the uid self::$uid = $uid; + // Create the zip object self::$zip = new ZipArchive; - - // Get some info - $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . self::$uid; - self::$zippath = $path . 'owncloud_export_' . self::$uid . '_' . date("y-m-d_H-i-s") . ".zip"; - if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE ) !== TRUE ) { - OC_Log::write('migration','Cannot create a zip file at: '.self::$zippath, OC_Log::ERROR); - return false; - exit(); + // Calculate users data dir + $user = OC_User::getUser(); + $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . $user . '/'; + // Calculate zip name + $zipname = "owncloud_userexport_" . $user . '_' . date("y-m-d_H-i-s") . ".zip"; + // Calculate destination + if( !is_null( $path ) ){ + // Path given + // Is a directory? + if( !is_dir( $path ) ){ + OC_Log::write('migration', 'Path supplied to createExportFile() is not a directory', OC_Log::ERROR); + return false; + exit(); + } + // Is writeable + if( !is_writeable( $path ) ){ + OC_Log::write('migration', 'Path supplied to createExportFile() is not writeable', OC_Log::ERROR); + return false; + exit(); + } + self::$zippath = $path . $zipname; + } else { + // Save in users data dir + self::$zippath = $userdatadir . $zipname; } - - // Export the app info + if (self::$zip->open(self::$zippath, ZIPARCHIVE::CREATE) !== TRUE) { + // TODO ADD LOGGING + exit("Cannot open <$filename>\n"); + } + // Export the app info $info = json_encode( self::exportAppData() ); file_put_contents( $userdatadir . '/exportinfo.json', $info ); - - // Add the data dir (which includes migration.db and exportinfo.json) - self::addDirToZip( $userdatadir, '/' ); - - // All done! + // Add the data dir to the zip + self::addDirToZip( $userdatadir ); + // All done! if( !self::$zip->close() ){ OC_Log::write('migration', 'Failed to save the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); return false; exit(); } else { OC_Log::write('migration', 'Created export file for: '.self::$uid, OC_Log::INFO); - return true; + //return true; } - - + return self::$zippath; } /** * @breif adds a directory to the zip object - * @return void + * @param $dir string path of the directory to add + * @param $recursive bool + * @param $internaldir string path of folder to add dir to in zip + * @return bool */ - static private function addDirToZip( $dir, $recursive=true, $internalDir='' ){ - $dirname = basename($dir); - self::$zip->addEmptyDir($internalDir . $dirname); - $internalDir.=$dirname.='/'; - if ($dirhandle = opendir($dir)) { + static private function addDirToZip($dir, $recursive=true, $internaldir='') { + $dirname = basename($dir); + self::$zip->addEmptyDir($internaldir . $dirname); + $internaldir.=$dirname.='/'; + + if ($dirhandle = opendir($dir)) { while (false !== ( $file = readdir($dirhandle))) { + if (( $file != '.' ) && ( $file != '..' )) { + if (is_dir($dir . '/' . $file) && $recursive) { - self::addDirToZip($dir . '/' . $file, $recursive, $internalDir); + self::addDirToZip($dir . '/' . $file, $recursive, $internaldir); } elseif (is_file($dir . '/' . $file)) { - self::$zip->addFile($dir . '/' . $file, $internalDir . $file); + self::$zip->addFile($dir . '/' . $file, $internaldir . $file); } } } closedir($dirhandle); - } else { - OC_Log::write('migration',"Was not able to open directory: " . $dir,OC_Log::ERROR); - } - } + } else { + OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); + return false; + } + return true; + } /** * @breif returns an array of apps that support migration @@ -483,7 +498,7 @@ class OC_Migrate{ } - /** + /** * @brief connects to a MDB2 database scheme * @returns true/false * @@ -527,20 +542,19 @@ class OC_Migrate{ /** * @breif removes migration.db and exportinfo.json from the users data dir + * @param optional $path string path to the export zip to delete * @return void */ - static public function cleanUp(){ - if( !self::$uid ){ - OC_Log::write('migration', 'Failed to cleanup after migration', OC_Log::ERROR); - return false; - } + static public function cleanUp( $path=null ){ $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . self::$uid; // Remove migration.db unlink( $userdatadir . '/migration.db' ); // Remove exportinfo.json unlink( $userdatadir . '/exportinfo.json' ); // Remove the zip - unlink(self::$zippath); + if( !is_null( $path ) ){ + unlink( $path ); + } return true; } } From 50233d075c47c86a5a26d4f946f8aa09f703cb15 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Thu, 15 Mar 2012 20:52:43 +0000 Subject: [PATCH 034/133] Improve admin_export ui and move system export cde to OC_Migrate --- apps/admin_export/settings.php | 78 +++--------- apps/admin_export/templates/settings.php | 11 +- apps/user_migrate/settings.php | 2 +- lib/migrate.php | 150 +++++++++++++++++++---- 4 files changed, 151 insertions(+), 90 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 73a4209d3f..9db1d75db9 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -28,70 +28,22 @@ OC_Util::checkAppEnabled('admin_export'); define('DS', '/'); - +// Export? if (isset($_POST['admin_export'])) { - $root = OC::$SERVERROOT . "/"; - $datadir = OC_Config::getValue( 'datadirectory' ); - $zip = new ZipArchive(); - $tempdir = get_temp_dir(); - $filename = $tempdir . "/owncloud_export_" . date("y-m-d_H-i-s") . ".zip"; - OC_Log::write('admin_export',"Creating export file at: " . $filename,OC_Log::INFO); - if ($zip->open($filename, ZIPARCHIVE::CREATE) !== TRUE) { - exit("Cannot open <$filename>\n"); - } - - if (isset($_POST['owncloud_system'])) { - // adding owncloud system files - OC_Log::write('admin_export',"Adding owncloud system files to export",OC_Log::INFO); - zipAddDir($root, $zip, false); - foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dirname) { - zipAddDir($root . $dirname, $zip, true, "/"); - } - } - - if (isset($_POST['owncloud_config'])) { - // adding owncloud config - // todo: add database export - $dbfile = $tempdir . "/dbexport.xml"; - OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL'); - - // Now add in *dbname* and *dbtableprefix* - $dbexport = file_get_contents( $dbfile ); - - $dbnamestring = "\n\n " . OC_Config::getValue( "dbname", "owncloud" ); - $dbtableprefixstring = "
\n\n " . OC_Config::getValue( "dbtableprefix", "_oc" ); - - $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); - $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbprefix*", $dbexport ); - - // Write the new db export file - file_put_contents( $dbfile, $dbexport ); - - $zip->addFile($dbfile, "dbexport.xml"); - - OC_Log::write('admin_export',"Adding owncloud config to export",OC_Log::INFO); - zipAddDir($root . "config/", $zip, true, "/"); - } - - if (isset($_POST['user_files'])) { - // needs to handle data outside of the default data dir. - // adding user files - $zip->addFile($root . '/data/.htaccess', "data/.htaccess"); - $zip->addFile($root . '/data/index.html', "data/index.html"); - foreach (OC_User::getUsers() as $i) { - OC_Log::write('admin_export',"Adding owncloud user files of $i to export",OC_Log::INFO); - zipAddDir($datadir . '/' . $i, $zip, true, "/data/"); - } - } - - $zip->close(); - header("Content-Type: application/zip"); - header("Content-Disposition: attachment; filename=" . basename($filename)); - header("Content-Length: " . filesize($filename)); - @ob_end_clean(); - readfile($filename); - unlink($filename); - unlink($dbfile); + // Create the export zip + if( !$path = OC_Migrate::createSysExportFile( $_POST['export_type'] ) ){ + // Error + die('error'); + } else { + // Download it + header("Content-Type: application/zip"); + header("Content-Disposition: attachment; filename=" . basename($path)); + header("Content-Length: " . filesize($path)); + @ob_end_clean(); + readfile($path); + OC_Migrate::cleanUp( $path ); + } +// Import? } else if( isset($_POST['admin_import']) ){ $root = OC::$SERVERROOT . "/"; diff --git a/apps/admin_export/templates/settings.php b/apps/admin_export/templates/settings.php index 9f0845bf55..15a19b7c63 100644 --- a/apps/admin_export/templates/settings.php +++ b/apps/admin_export/templates/settings.php @@ -2,12 +2,13 @@
t('Export this ownCloud instance');?>

t('This will create a compressed file that contains the data of this owncloud instance. - Please choose which components should be included:');?> -

-


-
- + Please choose the export type:');?>

+

What would you like to export?

+

+ ownCloud instance ( suitable for import )
+ ownCloud system files
+ Just user files

diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 04aca51f51..d862ac5a82 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -26,7 +26,7 @@ OC_Util::checkAppEnabled('user_migrate'); if (isset($_POST['user_export'])) { // Create the export zip - if( !$path = OC_Migrate::createExportFile() ){ + if( !$path = OC_Migrate::createUserExportFile() ){ // Error die('error'); } else { diff --git a/lib/migrate.php b/lib/migrate.php index 728f15e1f6..8f26ea7ae6 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -111,20 +111,120 @@ class OC_Migrate{ } + /** + * @breif creates an export file for the whole system + * @param optional $exporttype string export type ('instance','system' or 'userfiles') + * @param optional $path string path to zip destination (with trailing slash) + * @return path to the zip or false if there was a problem + */ + static public function createSysExportFile( $exporttype='instance', $path=null ){ + // Calculate zip name + $zipname = "owncloud_export_" . date("y-m-d_H-i-s") . ".zip"; + // Get the data dir + $datadir = OC_Config::getValue( 'datadirectory' ); + // Calculate destination + if( !is_null( $path ) ){ + // Path given + // Is a directory? + if( !is_dir( $path ) ){ + OC_Log::write('migration', 'Path supplied to createSysExportFile() is not a directory', OC_Log::ERROR); + return false; + } + // Is writeable + if( !is_writeable( $path ) ){ + OC_Log::write('migration', 'Path supplied to createSysExportFile() is not writeable', OC_Log::ERROR); + return false; + } + self::$zippath = $path . $zipname; + } else { + // Save in tmp dir + $structure = sys_get_temp_dir() . '/owncloudexports/'; + if( !file_exists( $structure ) ){ + if ( !mkdir( $structure ) ) { + OC_Log::write('migration', 'Could not create the temporary export at: '.$structure, OC_Log::ERROR); + return false; + } + } + self::$zippath = $structure . $zipname; + } + // Create the zip object + self::$zip = new ZipArchive; + // Try to create the zip + if( !self::createZip() ){ + return false; + } + // Handle export types + if( $exporttype == 'instance' ){ + // Creates a zip that is compatable with the import function + /* + $dbfile = self:: . "/dbexport.xml"; + OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL'); + + // Now add in *dbname* and *dbtableprefix* + $dbexport = file_get_contents( $dbfile ); + + $dbnamestring = "\n\n " . OC_Config::getValue( "dbname", "owncloud" ); + $dbtableprefixstring = "
\n\n " . OC_Config::getValue( "dbtableprefix", "_oc" ); + + $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); + $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbprefix*", $dbexport ); + + // Write the new db export file + file_put_contents( $dbfile, $dbexport ); + + $zip->addFile($dbfile, "dbexport.xml"); + */ + } else if( $exporttype == 'system' ){ + // Creates a zip with the owncloud system files + self::addDirToZip( OC::$SERVERROOT . '/', false); + foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dir) { + self::addDirToZip( OC::$SERVERROOT . '/' . $dir, true, "/"); + } + } else if ( $exporttype == 'userfiles' ){ + // Creates a zip with all of the users files + foreach(OC_User::getUsers() as $user){ + self::addDirToZip( $datadir . '/' . $user . '/', true, "/" . $user); + } + } else { + // Invalid export type supplied + OC_Log::write('migration', 'Invalid export type supplied to createSysExportFile() "'.$exporttype.'"', OC_Log::ERROR); + return false; + } + // Close the zip + if( !self::closeZip() ){ + return false; + } + return self::$zippath; + + } + + /** + * @breif tried to finalise the zip + * @return bool + */ + static private function closeZip(){ + if( !self::$zip->close() ){ + OC_Log::write('migration', 'Failed to save the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); + return false; + } else { + OC_Log::write('migration', 'Created export file for: '.self::$uid, OC_Log::INFO); + return true; + } + } + /** * @breif creates a zip user export * @param optional $uid string user id of the user to export (defaults to current) * @param optional $path string path to folder to create file in (with trailing slash) (defaults to current user's data dir) * @return false on failure | string path on success */ - static public function createExportFile( $uid=null, $path=null ){ + static public function createUserExportFile( $uid=null, $path=null ){ // User passed? $uid = is_null( $uid ) ? OC_User::getUser() : $uid ; // Is a database user? if( !OC_User_Database::userExists( $uid ) ){ OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); return false; - exit(); } // Set the uid self::$uid = $uid; @@ -140,41 +240,53 @@ class OC_Migrate{ // Path given // Is a directory? if( !is_dir( $path ) ){ - OC_Log::write('migration', 'Path supplied to createExportFile() is not a directory', OC_Log::ERROR); + OC_Log::write('migration', 'Path supplied to createUserExportFile() is not a directory', OC_Log::ERROR); return false; - exit(); } // Is writeable if( !is_writeable( $path ) ){ - OC_Log::write('migration', 'Path supplied to createExportFile() is not writeable', OC_Log::ERROR); + OC_Log::write('migration', 'Path supplied to createUserExportFile() is not writeable', OC_Log::ERROR); return false; - exit(); } self::$zippath = $path . $zipname; } else { // Save in users data dir self::$zippath = $userdatadir . $zipname; } - if (self::$zip->open(self::$zippath, ZIPARCHIVE::CREATE) !== TRUE) { - // TODO ADD LOGGING - exit("Cannot open <$filename>\n"); + // Try to create the zip + if( !self::createZip() ){ + return false; } // Export the app info $info = json_encode( self::exportAppData() ); file_put_contents( $userdatadir . '/exportinfo.json', $info ); // Add the data dir to the zip self::addDirToZip( $userdatadir ); - // All done! - if( !self::$zip->close() ){ - OC_Log::write('migration', 'Failed to save the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); - return false; - exit(); - } else { - OC_Log::write('migration', 'Created export file for: '.self::$uid, OC_Log::INFO); - //return true; + // Close the zip + if( !self::closeZip() ){ + return false; } + // All good return self::$zippath; } + + /** + * @breif tries to create the zip + * @return bool + */ + static private function createZip(){ + // Check if properties are set + if( !self::$zip || !self::$zippath ){ + OC_Log::write('migration', 'createZip() called but $zip and/or $zippath have not been set', OC_Log::ERROR); + return false; + } + if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE ) !== TRUE ) { + OC_Log::write('migration', 'Failed to create the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); + return false; + } else { + return true; + } + } /** * @breif adds a directory to the zip object @@ -235,7 +347,6 @@ class OC_Migrate{ if(!self::$uid){ OC_Log::write('migration','Tried to import without passing a uid',OC_Log::FATAL); return false; - exit(); } // Check if the db exists @@ -243,18 +354,15 @@ class OC_Migrate{ // Connect to the db if(!self::connectDB( $db )){ return false; - exit(); } } else { OC_Log::write('migration','Migration.db not found at: '.$db, OC_Log::FATAL ); return false; - exit(); } if( !is_array( $migrateinfo ) ){ OC_Log::write('migration','$migrateinfo is not an array', OC_Log::FATAL); return false; - exit(); } // Set the user id From c442a06a0217afa8ff284333b0560aeb87db7a55 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 16 Mar 2012 21:09:36 +0000 Subject: [PATCH 035/133] Fix export for admin and users. Added 3 admin export types --- apps/admin_export/settings.php | 9 +- apps/admin_export/templates/settings.php | 2 +- apps/user_migrate/settings.php | 2 +- lib/migrate.php | 144 +++++++++++++---------- 4 files changed, 90 insertions(+), 67 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 9db1d75db9..33fca26630 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -40,12 +40,12 @@ if (isset($_POST['admin_export'])) { header("Content-Disposition: attachment; filename=" . basename($path)); header("Content-Length: " . filesize($path)); @ob_end_clean(); - readfile($path); - OC_Migrate::cleanUp( $path ); + readfile( $path ); + unlink( $path ); } // Import? } else if( isset($_POST['admin_import']) ){ - + /* $root = OC::$SERVERROOT . "/"; $importname = "owncloud_import_" . date("y-m-d_H-i-s"); @@ -85,7 +85,8 @@ if (isset($_POST['admin_export'])) { exit(); } - OC_DB::replaceDB( get_temp_dir() . '/' . $importname . '/dbexport.xml' ); + OC_DB::replaceDB( get_temp_dir() . '/' . $importname . '/dbexport.xml' ); + */ } else { // fill template $tmpl = new OC_Template('admin_export', 'settings'); diff --git a/apps/admin_export/templates/settings.php b/apps/admin_export/templates/settings.php index 15a19b7c63..6d848048c4 100644 --- a/apps/admin_export/templates/settings.php +++ b/apps/admin_export/templates/settings.php @@ -8,7 +8,7 @@

ownCloud instance ( suitable for import )
ownCloud system files
- Just user files + Just user files
diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index d862ac5a82..2d200c0f76 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -36,7 +36,7 @@ if (isset($_POST['user_export'])) { header("Content-Length: " . filesize($path)); @ob_end_clean(); readfile($path); - OC_Migrate::cleanUp( $path ); + unlink( $path ); } } if( isset( $_POST['user_import'] ) ){ // TODO diff --git a/lib/migrate.php b/lib/migrate.php index 8f26ea7ae6..522d8da843 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -40,6 +40,10 @@ class OC_Migrate{ static private $zip=false; // String path to export static private $zippath=false; + // Stores the type of export + static private $exporttype=false; + // Array of temp files to be deleted after zip creation + static private $tmpfiles=array(); /** * register a new migration provider @@ -138,14 +142,7 @@ class OC_Migrate{ self::$zippath = $path . $zipname; } else { // Save in tmp dir - $structure = sys_get_temp_dir() . '/owncloudexports/'; - if( !file_exists( $structure ) ){ - if ( !mkdir( $structure ) ) { - OC_Log::write('migration', 'Could not create the temporary export at: '.$structure, OC_Log::ERROR); - return false; - } - } - self::$zippath = $structure . $zipname; + self::$zippath = sys_get_temp_dir() . '/' . $zipname; } // Create the zip object self::$zip = new ZipArchive; @@ -154,42 +151,48 @@ class OC_Migrate{ return false; } // Handle export types - if( $exporttype == 'instance' ){ - // Creates a zip that is compatable with the import function - /* - $dbfile = self:: . "/dbexport.xml"; - OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL'); - - // Now add in *dbname* and *dbtableprefix* - $dbexport = file_get_contents( $dbfile ); - - $dbnamestring = "\n\n " . OC_Config::getValue( "dbname", "owncloud" ); - $dbtableprefixstring = "

\n\n " . OC_Config::getValue( "dbtableprefix", "_oc" ); - - $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); - $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbprefix*", $dbexport ); - - // Write the new db export file - file_put_contents( $dbfile, $dbexport ); - - $zip->addFile($dbfile, "dbexport.xml"); - */ - } else if( $exporttype == 'system' ){ - // Creates a zip with the owncloud system files - self::addDirToZip( OC::$SERVERROOT . '/', false); - foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dir) { - self::addDirToZip( OC::$SERVERROOT . '/' . $dir, true, "/"); - } - } else if ( $exporttype == 'userfiles' ){ - // Creates a zip with all of the users files - foreach(OC_User::getUsers() as $user){ - self::addDirToZip( $datadir . '/' . $user . '/', true, "/" . $user); - } - } else { - // Invalid export type supplied - OC_Log::write('migration', 'Invalid export type supplied to createSysExportFile() "'.$exporttype.'"', OC_Log::ERROR); + $exporttypes = array( 'userfiles', 'instance', 'system' ); + self::$exporttype = in_array( $exporttype, $exporttypes ) ? $exporttype : false; + if( !self::$exporttype ){ + OC_Log::write( 'migration', 'Export type: '.$exporttype.' is not supported.', OC_Log::ERROR); return false; } + switch( self::$exporttype ){ + case 'instance': + // Creates a zip that is compatable with the import function + $dbfile = tempnam( "/tmp", "owncloud_export_data_" ); + OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL'); + + // Now add in *dbname* and *dbprefix* + $dbexport = file_get_contents( $dbfile ); + $dbnamestring = "\n\n " . OC_Config::getValue( "dbname", "owncloud" ); + $dbtableprefixstring = "
\n\n " . OC_Config::getValue( "dbtableprefix", "oc_" ); + $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); + $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbprefix*", $dbexport ); + // Write the new db export file + file_put_contents( $dbfile, $dbexport ); + self::$zip->addFile( $dbfile, "dbexport.xml" ); + // Add user data + foreach(OC_User::getUsers() as $user){ + self::addDirToZip( $datadir . '/' . $user . '/', true, "/userdata/" ); + } + break; + case 'userfiles': + // Creates a zip with all of the users files + foreach(OC_User::getUsers() as $user){ + self::addDirToZip( $datadir . '/' . $user . '/', true, "/" ); + } + break; + case 'system': + // Creates a zip with the owncloud system files + self::addDirToZip( OC::$SERVERROOT . '/', false, '/'); + foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dir) { + self::addDirToZip( OC::$SERVERROOT . '/' . $dir, true, "/"); + } + break; + } + // Add export info + self::addExportInfo(); // Close the zip if( !self::closeZip() ){ return false; @@ -198,6 +201,30 @@ class OC_Migrate{ } + /** + * @breif adds a json file with infomation on the export to the zips root (used on import) + * @return bool + */ + static private function addExportInfo(){ + $info = array( + 'ocversion' => OC_Util::getVersion(), + 'exporttime' => time(), + 'exportedby' => OC_User::getUser(), + 'exporttype' => self::$exporttype + ); + // Create json + $json = json_encode( $info ); + $tmpfile = tempnam("/tmp", "oc_export_info_"); + self::$tmpfiles[] = $tmpfile; + if( !file_put_contents( $tmpfile, $json ) ){ + return false; + } else { + self::$zip->addFile( $tmpfile, "export_info.json" ); + return true; + } + } + + /** * @breif tried to finalise the zip * @return bool @@ -205,13 +232,25 @@ class OC_Migrate{ static private function closeZip(){ if( !self::$zip->close() ){ OC_Log::write('migration', 'Failed to save the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); + self::cleanup(); return false; } else { - OC_Log::write('migration', 'Created export file for: '.self::$uid, OC_Log::INFO); + OC_Log::write('migration', 'Export zip created ok', OC_Log::INFO); + self::cleanup(); return true; } } + /** + * @breif cleans up after the zip + */ + static private function cleanup(){ + // Delete tmp files + foreach(self::$tmpfiles as $i){ + unlink( $i ); + } + } + /** * @breif creates a zip user export * @param optional $uid string user id of the user to export (defaults to current) @@ -647,22 +686,5 @@ class OC_Migrate{ return $result ? true : false; } - - /** - * @breif removes migration.db and exportinfo.json from the users data dir - * @param optional $path string path to the export zip to delete - * @return void - */ - static public function cleanUp( $path=null ){ - $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . self::$uid; - // Remove migration.db - unlink( $userdatadir . '/migration.db' ); - // Remove exportinfo.json - unlink( $userdatadir . '/exportinfo.json' ); - // Remove the zip - if( !is_null( $path ) ){ - unlink( $path ); - } - return true; - } + } From 5332c319a2140563478d83047d9f717c0d3e179f Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 16 Mar 2012 22:50:35 +0000 Subject: [PATCH 036/133] Migration info is an object. Other fixes --- apps/bookmarks/lib/migrate.php | 6 +++--- lib/migrate.php | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 4f11bc5bde..46b68ad5ba 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -34,16 +34,16 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ // Import function for bookmarks function import( $info ){ - switch( $info['appversion'] ){ + switch( $info->appversion ){ default: // All versions of the app have had the same db structure, so all can use the same import function $query = OC_Migrate::prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" ); - $results = $query->execute( array( $info['olduid'] ) ); + $results = $query->execute( array( $info->olduid ) ); $idmap = array(); while( $row = $data->fetchRow() ){ // Import each bookmark, saving its id into the map $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" ); - $query->execute( array( $row['url'], $row['title'], $info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); + $query->execute( array( $row['url'], $row['title'], $info->newuid, $row['public'], $row['added'], $row['lastmodified'] ) ); // Map the id $idmap[$row['id']] = OC_DB::insertid(); } diff --git a/lib/migrate.php b/lib/migrate.php index 522d8da843..28c36e9616 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -377,11 +377,11 @@ class OC_Migrate{ /** * @breif imports a new user * @param $db string path to migration.db - * @param $migrateinfo array of migration ino + * @param $info array of migration ino * @param $uid optional uid to use * @return bool if the import succedded */ - public static function importAppData( $db, $migrateinfo, $uid=false ){ + public static function importAppData( $db, $info, $uid=false ){ if(!self::$uid){ OC_Log::write('migration','Tried to import without passing a uid',OC_Log::FATAL); @@ -399,15 +399,15 @@ class OC_Migrate{ return false; } - if( !is_array( $migrateinfo ) ){ + if( !is_array( $info ) ){ OC_Log::write('migration','$migrateinfo is not an array', OC_Log::FATAL); return false; } // Set the user id - self::$uid = $info['migrateinfo']['uid']; + self::$uid = $info->migrateinfo->uid; - $apps = $info['apps']; + $apps = $info->app; foreach( self::$providers as $provider){ // Is the app in the export? @@ -415,7 +415,7 @@ class OC_Migrate{ // Did it succeed? if( $app[$provider->id] ){ // Then do the import - $provider->import(); + $provider->import( $info ); } } } From 222bb2303fb0825b4e279024ff723db84e23153d Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 16 Mar 2012 22:54:37 +0000 Subject: [PATCH 037/133] Added prototype of user import --- apps/admin_export/settings.php | 4 +- apps/admin_export/templates/settings.php | 2 +- apps/user_migrate/settings.php | 117 +++++++++++++++++++++++ 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 33fca26630..e7de74f758 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -45,7 +45,7 @@ if (isset($_POST['admin_export'])) { } // Import? } else if( isset($_POST['admin_import']) ){ - /* + $root = OC::$SERVERROOT . "/"; $importname = "owncloud_import_" . date("y-m-d_H-i-s"); @@ -86,7 +86,7 @@ if (isset($_POST['admin_export'])) { } OC_DB::replaceDB( get_temp_dir() . '/' . $importname . '/dbexport.xml' ); - */ + } else { // fill template $tmpl = new OC_Template('admin_export', 'settings'); diff --git a/apps/admin_export/templates/settings.php b/apps/admin_export/templates/settings.php index 6d848048c4..36eec84d48 100644 --- a/apps/admin_export/templates/settings.php +++ b/apps/admin_export/templates/settings.php @@ -14,7 +14,7 @@
- t('Import an ownCloud instance THIS WILL DELETE ALL CURRENT OWNCLOUD DATA');?> + t('Import an ownCloud instance. THIS WILL DELETE ALL CURRENT OWNCLOUD DATA');?>

t('All current ownCloud data will be replaced by the ownCloud instance that is uploaded.');?>

diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 2d200c0f76..3efe9228a1 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -40,10 +40,127 @@ if (isset($_POST['user_export'])) { } } if( isset( $_POST['user_import'] ) ){ // TODO + $root = OC::$SERVERROOT . "/"; + $importname = "owncloud_import_" . date("y-m-d_H-i-s"); + + // Save data dir for later + $datadir = OC_Config::getValue( 'datadirectory' ); + + // Copy the uploaded file + $from = $_FILES['owncloud_import']['tmp_name']; + $to = get_temp_dir().'/'.$importname.'.zip'; + if( !move_uploaded_file( $from, $to ) ){ + OC_Log::write('admin_export',"Failed to copy the uploaded file",OC_Log::INFO); + exit(); + } + + // Extract zip + $zip = new ZipArchive(); + if ($zip->open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { + OC_Log::write('admin_export',"Failed to open zip file",OC_Log::INFO); + exit(); + } + $zip->extractTo(get_temp_dir().'/'.$importname.'/'); + $zip->close(); + + $importdir = get_temp_dir() . '/' . $importname; + + // Delete uploaded file + unlink( $importdir . '.zip' ); + + // Find folder + $files = scandir( $importdir ); + unset($files[0]); + unset($files[1]); + + // Get the user + if( count($files) != 1 ){ + OC_Log::write('migration', 'Invalid import file', OC_Log::ERROR); + die('invalid import'); + } + + $user = reset($files); + + // Check for dbexport.xml and export info and data dir + $files = scandir( $importdir . '/' . $user ); + $required = array( 'migration.db', 'exportinfo.json', 'files'); + foreach($required as $require){ + if( !in_array( $require, $files) ){ + OC_Log::write('migration', 'Invlaid import file', OC_Log::ERROR); + die('invalid import'); + } + } + + $migrateinfo = $importdir . '/' . $user . '/exportinfo.json'; + $migrateinfo = json_decode( file_get_contents( $migrateinfo ) ); + $olduid = $migrateinfo->migrateinfo->uid; + + // Check if uid is available + if( OC_User::UserExists( $olduid ) ){ + OC_Log::write('migration','Username exists', OC_Log::ERROR); + die('user exists'); + } + + // Create the user + if( !OC_Migrate::createUser( $olduid, $migrateinfo->migrateinfo->hash ) ){ + OC_Log::write('migration', 'Failed to create the new user', OC_Log::ERROR); + die('coundlt create new user'); + } + + $datadir = OC_Config::getValue( 'datadirectory' ); + // Copy data + if( !copy_r( $importdir . '/files', $datadir . '/' ) ){ + OC_Log::write('migration','Failed to copy user files to destination', OC_Log::ERROR); + die('failed to copy user files'); + } + + // Import user data + if( !OC_Migrate::importUser( $importdir . '/migration.db', $migrateinfo ) ){ + OC_Log::write('migration','Failed to import user data', OC_Log::ERROR); + die('failed to import user data'); + } + + // All done! + die('done'); + } else { // fill template $tmpl = new OC_Template('user_migrate', 'settings'); return $tmpl->fetchPage(); } +function copy_r( $path, $dest ) + { + if( is_dir($path) ) + { + @mkdir( $dest ); + $objects = scandir($path); + if( sizeof($objects) > 0 ) + { + foreach( $objects as $file ) + { + if( $file == "." || $file == ".." ) + continue; + // go on + if( is_dir( $path.DS.$file ) ) + { + copy_r( $path.DS.$file, $dest.DS.$file ); + } + else + { + copy( $path.DS.$file, $dest.DS.$file ); + } + } + } + return true; + } + elseif( is_file($path) ) + { + return copy($path, $dest); + } + else + { + return false; + } + } From 27bf34f7be0e7c3c1516cdf6c44877ee77962dca Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 17 Mar 2012 13:30:58 +0000 Subject: [PATCH 038/133] Move user import to the admin --- apps/user_migrate/admin.php | 118 ++++++++++++++++++++++ apps/user_migrate/appinfo/app.php | 8 ++ apps/user_migrate/settings.php | 123 +---------------------- apps/user_migrate/templates/admin.php | 9 ++ apps/user_migrate/templates/settings.php | 11 +- 5 files changed, 137 insertions(+), 132 deletions(-) create mode 100644 apps/user_migrate/admin.php create mode 100644 apps/user_migrate/templates/admin.php diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php new file mode 100644 index 0000000000..56fe887514 --- /dev/null +++ b/apps/user_migrate/admin.php @@ -0,0 +1,118 @@ +. + * + */ +OC_Util::checkAdminUser(); +OC_Util::checkAppEnabled('user_migrate'); + +// Import? +if (isset($_POST['userimport'])) { + + $root = OC::$SERVERROOT . "/"; + $importname = "owncloud_import_" . date("y-m-d_H-i-s"); + + // Save data dir for later + $datadir = OC_Config::getValue( 'datadirectory' ); + + // Copy the uploaded file + $from = $_FILES['owncloud_import']['tmp_name']; + $to = get_temp_dir().'/'.$importname.'.zip'; + if( !move_uploaded_file( $from, $to ) ){ + OC_Log::write('admin_export',"Failed to copy the uploaded file",OC_Log::INFO); + exit(); + } + + // Extract zip + $zip = new ZipArchive(); + if ($zip->open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { + OC_Log::write('admin_export',"Failed to open zip file",OC_Log::INFO); + exit(); + } + $zip->extractTo(get_temp_dir().'/'.$importname.'/'); + $zip->close(); + + $importdir = get_temp_dir() . '/' . $importname; + + // Delete uploaded file + unlink( $importdir . '.zip' ); + + // Find folder + $files = scandir( $importdir ); + unset($files[0]); + unset($files[1]); + + // Get the user + if( count($files) != 1 ){ + OC_Log::write('migration', 'Invalid import file', OC_Log::ERROR); + die('invalid import'); + } + + $user = reset($files); + + // Check for dbexport.xml and export info and data dir + $files = scandir( $importdir . '/' . $user ); + $required = array( 'migration.db', 'exportinfo.json', 'files'); + foreach($required as $require){ + if( !in_array( $require, $files) ){ + OC_Log::write('migration', 'Invlaid import file', OC_Log::ERROR); + die('invalid import'); + } + } + + $migrateinfo = $importdir . '/' . $user . '/exportinfo.json'; + $migrateinfo = json_decode( file_get_contents( $migrateinfo ) ); + $olduid = $migrateinfo->migrateinfo->uid; + + // Check if uid is available + if( OC_User::UserExists( $olduid ) ){ + OC_Log::write('migration','Username exists', OC_Log::ERROR); + die('user exists'); + } + + // Create the user + if( !OC_Migrate::createUser( $olduid, $migrateinfo->migrateinfo->hash ) ){ + OC_Log::write('migration', 'Failed to create the new user', OC_Log::ERROR); + die('coundlt create new user'); + } + + $datadir = OC_Config::getValue( 'datadirectory' ); + // Copy data + if( !copy_r( $importdir . '/files', $datadir . '/' ) ){ + OC_Log::write('migration','Failed to copy user files to destination', OC_Log::ERROR); + die('failed to copy user files'); + } + + // Import user data + if( !OC_Migrate::importUser( $importdir . '/migration.db', $migrateinfo ) ){ + OC_Log::write('migration','Failed to import user data', OC_Log::ERROR); + die('failed to import user data'); + } + + // All done! + die('done'); + +} else { +// fill template + $tmpl = new OC_Template('user_migrate', 'admin'); + return $tmpl->fetchPage(); +} \ No newline at end of file diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php index 4a795a5474..18b97b93df 100644 --- a/apps/user_migrate/appinfo/app.php +++ b/apps/user_migrate/appinfo/app.php @@ -22,5 +22,13 @@ */ OC_APP::registerPersonal('user_migrate','settings'); +OC_APP::registerAdmin('user_migrate','admin'); +// add settings page to navigation +$entry = array( + 'id' => "user_migrate_settings", + 'order'=>1, + 'href' => OC_Helper::linkTo( "user_migrate", "admin.php" ), + 'name' => 'Import' +); ?> \ No newline at end of file diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 3efe9228a1..62f5e3f20d 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -38,129 +38,8 @@ if (isset($_POST['user_export'])) { readfile($path); unlink( $path ); } -} if( isset( $_POST['user_import'] ) ){ - // TODO - $root = OC::$SERVERROOT . "/"; - $importname = "owncloud_import_" . date("y-m-d_H-i-s"); - - // Save data dir for later - $datadir = OC_Config::getValue( 'datadirectory' ); - - // Copy the uploaded file - $from = $_FILES['owncloud_import']['tmp_name']; - $to = get_temp_dir().'/'.$importname.'.zip'; - if( !move_uploaded_file( $from, $to ) ){ - OC_Log::write('admin_export',"Failed to copy the uploaded file",OC_Log::INFO); - exit(); - } - - // Extract zip - $zip = new ZipArchive(); - if ($zip->open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { - OC_Log::write('admin_export',"Failed to open zip file",OC_Log::INFO); - exit(); - } - $zip->extractTo(get_temp_dir().'/'.$importname.'/'); - $zip->close(); - - $importdir = get_temp_dir() . '/' . $importname; - - // Delete uploaded file - unlink( $importdir . '.zip' ); - - // Find folder - $files = scandir( $importdir ); - unset($files[0]); - unset($files[1]); - - // Get the user - if( count($files) != 1 ){ - OC_Log::write('migration', 'Invalid import file', OC_Log::ERROR); - die('invalid import'); - } - - $user = reset($files); - - // Check for dbexport.xml and export info and data dir - $files = scandir( $importdir . '/' . $user ); - $required = array( 'migration.db', 'exportinfo.json', 'files'); - foreach($required as $require){ - if( !in_array( $require, $files) ){ - OC_Log::write('migration', 'Invlaid import file', OC_Log::ERROR); - die('invalid import'); - } - } - - $migrateinfo = $importdir . '/' . $user . '/exportinfo.json'; - $migrateinfo = json_decode( file_get_contents( $migrateinfo ) ); - $olduid = $migrateinfo->migrateinfo->uid; - - // Check if uid is available - if( OC_User::UserExists( $olduid ) ){ - OC_Log::write('migration','Username exists', OC_Log::ERROR); - die('user exists'); - } - - // Create the user - if( !OC_Migrate::createUser( $olduid, $migrateinfo->migrateinfo->hash ) ){ - OC_Log::write('migration', 'Failed to create the new user', OC_Log::ERROR); - die('coundlt create new user'); - } - - $datadir = OC_Config::getValue( 'datadirectory' ); - // Copy data - if( !copy_r( $importdir . '/files', $datadir . '/' ) ){ - OC_Log::write('migration','Failed to copy user files to destination', OC_Log::ERROR); - die('failed to copy user files'); - } - - // Import user data - if( !OC_Migrate::importUser( $importdir . '/migration.db', $migrateinfo ) ){ - OC_Log::write('migration','Failed to import user data', OC_Log::ERROR); - die('failed to import user data'); - } - - // All done! - die('done'); - } else { // fill template $tmpl = new OC_Template('user_migrate', 'settings'); return $tmpl->fetchPage(); -} - -function copy_r( $path, $dest ) - { - if( is_dir($path) ) - { - @mkdir( $dest ); - $objects = scandir($path); - if( sizeof($objects) > 0 ) - { - foreach( $objects as $file ) - { - if( $file == "." || $file == ".." ) - continue; - // go on - if( is_dir( $path.DS.$file ) ) - { - copy_r( $path.DS.$file, $dest.DS.$file ); - } - else - { - copy( $path.DS.$file, $dest.DS.$file ); - } - } - } - return true; - } - elseif( is_file($path) ) - { - return copy($path, $dest); - } - else - { - return false; - } - } - +} \ No newline at end of file diff --git a/apps/user_migrate/templates/admin.php b/apps/user_migrate/templates/admin.php new file mode 100644 index 0000000000..b5a9951841 --- /dev/null +++ b/apps/user_migrate/templates/admin.php @@ -0,0 +1,9 @@ + +

+ t('Import user account');?> +

+

+

+ +
+ diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php index 59a27a926d..389de563a6 100644 --- a/apps/user_migrate/templates/settings.php +++ b/apps/user_migrate/templates/settings.php @@ -5,13 +5,4 @@

- -
-
- t('Import user account');?> -

-

-

- -
- + \ No newline at end of file From 5234e66bab0ebc6fd7eeef8170cf9f61f035124d Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 17 Mar 2012 13:53:00 +0000 Subject: [PATCH 039/133] Add exportinfo to user exports. --- apps/user_migrate/admin.php | 10 ++++------ lib/migrate.php | 37 +++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index 56fe887514..da2e53d2a1 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -1,10 +1,8 @@ open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { - OC_Log::write('admin_export',"Failed to open zip file",OC_Log::INFO); + OC_Log::write('migration',"Failed to open zip file",OC_Log::INFO); exit(); } $zip->extractTo(get_temp_dir().'/'.$importname.'/'); diff --git a/lib/migrate.php b/lib/migrate.php index 28c36e9616..f5fb808f17 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -84,7 +84,7 @@ class OC_Migrate{ if( is_array( $tables ) ){ // Save the table names foreach($tables as $table){ - $return['app'][$provider->id]['tables'][] = $table; + $return['apps'][$provider->id]['tables'][] = $table; } } else { // It failed to create the tables @@ -94,22 +94,17 @@ class OC_Migrate{ // Run the import function? if( !$failed ){ - $return['app'][$provider->id]['success'] = $provider->export( self::$uid ); + $return['apps'][$provider->id]['success'] = $provider->export( self::$uid ); } else { - $return['app'][$provider->id]['success'] = false; - $return['app'][$provider->id]['message'] = 'failed to create the app tables'; + $return['apps'][$provider->id]['success'] = false; + $return['apps'][$provider->id]['message'] = 'failed to create the app tables'; } // Now add some app info the the return array $appinfo = OC_App::getAppInfo( $provider->id ); - $return['app'][$provider->id]['version'] = $appinfo['version']; + $return['apps'][$provider->id]['version'] = $appinfo['version']; } - - - // Add some general info to the return array - $return['migrateinfo']['uid'] = self::$uid; - $return['migrateinfo']['ocversion'] = OC_Util::getVersionString(); return $return; @@ -205,13 +200,27 @@ class OC_Migrate{ * @breif adds a json file with infomation on the export to the zips root (used on import) * @return bool */ - static private function addExportInfo(){ + static private function addExportInfo( $array=array() ){ $info = array( 'ocversion' => OC_Util::getVersion(), 'exporttime' => time(), 'exportedby' => OC_User::getUser(), 'exporttype' => self::$exporttype ); + // Add hash if user export + if( self::$exporttype = 'user' ){ + $query = OC_DB::prepare( "SELECT password FROM *PREFIX*users WHERE uid LIKE ?" ); + $result = $query->execute( array( self::$uid ) ); + $row = $result->fetchRow(); + $hash = $row ? $row['password'] : false; + if( !$hash ){ + OC_Log::write( 'migration', 'Failed to get the users password hash', OC_log::ERROR); + return false; + } + $info['hash'] = $hash; + } + // Merge in other data + $info = array_merge( $info, $array ); // Create json $json = json_encode( $info ); $tmpfile = tempnam("/tmp", "oc_export_info_"); @@ -297,8 +306,8 @@ class OC_Migrate{ return false; } // Export the app info - $info = json_encode( self::exportAppData() ); - file_put_contents( $userdatadir . '/exportinfo.json', $info ); + $exportinfo = json_encode( self::addExportInfo( self::exportAppData() ) ); + file_put_contents( $userdatadir . '/exportinfo.json', $exportinfo ); // Add the data dir to the zip self::addDirToZip( $userdatadir ); // Close the zip @@ -670,7 +679,7 @@ class OC_Migrate{ // @param $uid string user_id of the user to be created // @param $hash string hash of the user to be created // @return bool result of user creation - private static function createUser( $uid, $hash ){ + public static function createUser( $uid, $hash ){ // Check if userid exists if(OC_User::userExists( $uid )){ From 247b25e7a97fcbe8386c63b1318537e669d40480 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 17 Mar 2012 15:01:08 +0000 Subject: [PATCH 040/133] Fix structure of export zip --- lib/migrate.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/migrate.php b/lib/migrate.php index f5fb808f17..44d28297d4 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -228,7 +228,7 @@ class OC_Migrate{ if( !file_put_contents( $tmpfile, $json ) ){ return false; } else { - self::$zip->addFile( $tmpfile, "export_info.json" ); + self::$zip->addFile( $tmpfile, "/" . self::$uid . "/export_info.json" ); return true; } } @@ -278,6 +278,8 @@ class OC_Migrate{ self::$uid = $uid; // Create the zip object self::$zip = new ZipArchive; + // Set export type + self::$exporttype = 'user'; // Calculate users data dir $user = OC_User::getUser(); $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . $user . '/'; @@ -306,8 +308,9 @@ class OC_Migrate{ return false; } // Export the app info - $exportinfo = json_encode( self::addExportInfo( self::exportAppData() ) ); - file_put_contents( $userdatadir . '/exportinfo.json', $exportinfo ); + $appinfo = self::exportAppData(); + // Save the migration results + self::addExportInfo( $appinfo ); // Add the data dir to the zip self::addDirToZip( $userdatadir ); // Close the zip From bc085c3ff40bd7980bb28a20238bf18a754ffba2 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 17 Mar 2012 16:25:14 +0000 Subject: [PATCH 041/133] Create new user, create new data dir, copy files, import app data --- apps/admin_export/settings.php | 1 - apps/bookmarks/lib/migrate.php | 9 +++-- apps/user_migrate/admin.php | 27 ++++++++------ lib/migrate.php | 65 +++++++++++++++++++--------------- lib/migrate/provider.php | 3 +- 5 files changed, 59 insertions(+), 46 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index e7de74f758..1c98bb552f 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -144,7 +144,6 @@ function unlinkRecursive($dir, $deleteRootToo) return; } - function copy_r( $path, $dest ) { diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 46b68ad5ba..ffc5e9f838 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -32,18 +32,17 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } // Import function for bookmarks - function import( $info ){ - - switch( $info->appversion ){ + function import( $app, $info ){ + switch( $app->version ){ default: // All versions of the app have had the same db structure, so all can use the same import function $query = OC_Migrate::prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" ); - $results = $query->execute( array( $info->olduid ) ); + $results = $query->execute( array( $info['olduid'] ) ); $idmap = array(); while( $row = $data->fetchRow() ){ // Import each bookmark, saving its id into the map $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" ); - $query->execute( array( $row['url'], $row['title'], $info->newuid, $row['public'], $row['added'], $row['lastmodified'] ) ); + $query->execute( array( $row['url'], $row['title'], $info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); // Map the id $idmap[$row['id']] = OC_DB::insertid(); } diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index da2e53d2a1..6f3565788e 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -62,14 +62,15 @@ if (isset($_POST['user_import'])) { // Get the user if( count($files) != 1 ){ OC_Log::write('migration', 'Invalid import file', OC_Log::ERROR); - die('invalid import'); + die('invalid import, no user included'); } - $user = reset($files); + $olduser = reset($files); // Check for dbexport.xml and export info and data dir - $files = scandir( $importdir . '/' . $user ); - $required = array( 'migration.db', 'exportinfo.json', 'files'); + $files = scandir( $importdir . '/' . $olduser ); + + $required = array( 'migration.db', 'export_info.json', 'files'); foreach($required as $require){ if( !in_array( $require, $files) ){ OC_Log::write('migration', 'Invlaid import file', OC_Log::ERROR); @@ -77,31 +78,37 @@ if (isset($_POST['user_import'])) { } } - $migrateinfo = $importdir . '/' . $user . '/exportinfo.json'; + $migrateinfo = $importdir . '/' . $olduser . '/export_info.json'; $migrateinfo = json_decode( file_get_contents( $migrateinfo ) ); - $olduid = $migrateinfo->migrateinfo->uid; // Check if uid is available - if( OC_User::UserExists( $olduid ) ){ + if( OC_User::UserExists( $olduser ) ){ OC_Log::write('migration','Username exists', OC_Log::ERROR); die('user exists'); } // Create the user - if( !OC_Migrate::createUser( $olduid, $migrateinfo->migrateinfo->hash ) ){ + if( !OC_Migrate::createUser( $olduser, $migrateinfo->hash ) ){ OC_Log::write('migration', 'Failed to create the new user', OC_Log::ERROR); die('coundlt create new user'); } $datadir = OC_Config::getValue( 'datadirectory' ); + // Make the new users data dir + $path = $datadir . '/' . $olduser . '/files/'; + if( !mkdir( $path, 0755, true ) ){ + OC_Log::write('migration','Failed to create users data dir: '.$path, OC_Log::ERROR); + die('failed to create users data dir'); + } + // Copy data - if( !copy_r( $importdir . '/files', $datadir . '/' ) ){ + if( !copy_r( $importdir . '/' . $olduser . '/files', $datadir . '/' . $olduser . '/files' ) ){ OC_Log::write('migration','Failed to copy user files to destination', OC_Log::ERROR); die('failed to copy user files'); } // Import user data - if( !OC_Migrate::importUser( $importdir . '/migration.db', $migrateinfo ) ){ + if( !OC_Migrate::importAppData( $importdir . '/' . $olduser . '/migration.db', $migrateinfo ) ){ OC_Log::write('migration','Failed to import user data', OC_Log::ERROR); die('failed to import user data'); } diff --git a/lib/migrate.php b/lib/migrate.php index 44d28297d4..415c33e5be 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -53,6 +53,21 @@ class OC_Migrate{ self::$providers[]=$provider; } + /** + * @breif finds and loads the providers + */ + static private function findProviders(){ + // Find the providers + $apps = OC_App::getAllApps(); + + foreach($apps as $app){ + $path = OC::$SERVERROOT . '/apps/' . $app . '/lib/migrate.php'; + if( file_exists( $path ) ){ + include( $path ); + } + } + } + /** * @breif creates a migration.db in the users data dir with their app data in * @return bool whether operation was successfull @@ -64,14 +79,7 @@ class OC_Migrate{ $return = array(); // Find the providers - $apps = OC_App::getAllApps(); - - foreach($apps as $app){ - $path = OC::$SERVERROOT . '/apps/' . $app . '/lib/migrate.php'; - if( file_exists( $path ) ){ - include( $path ); - } - } + self::findProviders(); // Foreach provider foreach( self::$providers as $provider ){ @@ -217,7 +225,8 @@ class OC_Migrate{ OC_Log::write( 'migration', 'Failed to get the users password hash', OC_log::ERROR); return false; } - $info['hash'] = $hash; + $info['hash'] = $hash; + $info['exporteduser'] = self::$uid; } // Merge in other data $info = array_merge( $info, $array ); @@ -393,17 +402,15 @@ class OC_Migrate{ * @param $uid optional uid to use * @return bool if the import succedded */ - public static function importAppData( $db, $info, $uid=false ){ + public static function importAppData( $db, $info, $uid=null ){ - if(!self::$uid){ - OC_Log::write('migration','Tried to import without passing a uid',OC_Log::FATAL); - return false; - } + self::$uid = !is_null( $uid ) ? $uid : $info->exporteduser; // Check if the db exists if( file_exists( $db ) ){ // Connect to the db if(!self::connectDB( $db )){ + OC_Log::write('migration','Failed to connect to migration.db',OC_Log::ERROR); return false; } } else { @@ -411,25 +418,25 @@ class OC_Migrate{ return false; } - if( !is_array( $info ) ){ - OC_Log::write('migration','$migrateinfo is not an array', OC_Log::FATAL); - return false; - } - - // Set the user id - self::$uid = $info->migrateinfo->uid; - - $apps = $info->app; - + // Find providers + self::findProviders(); + + // Generate importinfo array + $importinfo = array( + 'olduid' => $info->exporteduser, + 'newuid' => self::$uid + ); + foreach( self::$providers as $provider){ // Is the app in the export? - if( array_key_exists( $provider->id, $apps ) ){ + $id = $provider->id; + if( isset( $info->apps->$id ) ){ // Did it succeed? - if( $app[$provider->id] ){ + if( $info->apps->$id->success ){ // Then do the import - $provider->import( $info ); + $provider->import( $info->apps->$id, $importinfo ); } - } + } } return true; @@ -691,7 +698,7 @@ class OC_Migrate{ // Create the user $query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" ); - $result = $query->execute( array( $uid, $data['hash'])); + $result = $query->execute( array( $uid, $hash)); if( !$result ){ OC_Log::write('migration', 'Failed to create the new user "'.$uid.""); } diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php index e2e01b3b5a..7ac3cf97ca 100644 --- a/lib/migrate/provider.php +++ b/lib/migrate/provider.php @@ -20,8 +20,9 @@ abstract class OC_Migrate_Provider{ /** * @breif imports data for the app + * @param $appinfo object with the data that the app exported * @param $info array of info including exportinfo.json * @return void */ - abstract function import( $info ); + abstract function import( $appinfo, $info ); } From 77f6872ea4859e13637efbc6d051072a5085394f Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 17 Mar 2012 17:45:39 +0000 Subject: [PATCH 042/133] Shorten export zip names --- lib/migrate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/migrate.php b/lib/migrate.php index 415c33e5be..84eafcd4cd 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -126,7 +126,7 @@ class OC_Migrate{ */ static public function createSysExportFile( $exporttype='instance', $path=null ){ // Calculate zip name - $zipname = "owncloud_export_" . date("y-m-d_H-i-s") . ".zip"; + $zipname = "oc_export_" . date("y-m-d_H-i-s") . ".zip"; // Get the data dir $datadir = OC_Config::getValue( 'datadirectory' ); // Calculate destination @@ -293,7 +293,7 @@ class OC_Migrate{ $user = OC_User::getUser(); $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . $user . '/'; // Calculate zip name - $zipname = "owncloud_userexport_" . $user . '_' . date("y-m-d_H-i-s") . ".zip"; + $zipname = "oc_userexport_" . $user . '_' . date("y-m-d_H-i-s") . ".zip"; // Calculate destination if( !is_null( $path ) ){ // Path given From 145d6f35660669397eaee08988ffbad1b65daff0 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Mon, 19 Mar 2012 20:44:20 +0000 Subject: [PATCH 043/133] Add OC_Migration_Content class to help app devs. Restructure OC_Migrate. --- apps/admin_export/settings.php | 134 +----- apps/bookmarks/lib/migrate.php | 20 +- apps/user_migrate/settings.php | 2 +- lib/migrate.php | 781 ++++++++++++--------------------- lib/migrate/provider.php | 28 -- lib/migration/content.php | 239 ++++++++++ lib/migration/provider.php | 49 +++ 7 files changed, 593 insertions(+), 660 deletions(-) delete mode 100644 lib/migrate/provider.php create mode 100644 lib/migration/content.php create mode 100644 lib/migration/provider.php diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 1c98bb552f..af8dd0dbf5 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -27,11 +27,10 @@ OC_Util::checkAppEnabled('admin_export'); define('DS', '/'); - // Export? if (isset($_POST['admin_export'])) { // Create the export zip - if( !$path = OC_Migrate::createSysExportFile( $_POST['export_type'] ) ){ + if( !$path = OC_Migrate::export( $_POST['export_type'] ) ){ // Error die('error'); } else { @@ -46,136 +45,11 @@ if (isset($_POST['admin_export'])) { // Import? } else if( isset($_POST['admin_import']) ){ - $root = OC::$SERVERROOT . "/"; - $importname = "owncloud_import_" . date("y-m-d_H-i-s"); - - // Save data dir for later - $datadir = OC_Config::getValue( 'datadirectory' ); - - // Copy the uploaded file - $from = $_FILES['owncloud_import']['tmp_name']; - $to = get_temp_dir().'/'.$importname.'.zip'; - if( !move_uploaded_file( $from, $to ) ){ - OC_Log::write('admin_export',"Failed to copy the uploaded file",OC_Log::INFO); - exit(); - } - - // Extract zip - $zip = new ZipArchive(); - if ($zip->open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { - OC_Log::write('admin_export',"Failed to open zip file",OC_Log::INFO); - exit(); - } - $zip->extractTo(get_temp_dir().'/'.$importname.'/'); - $zip->close(); - - // Delete uploaded file - unlink( get_temp_dir() . '/' . $importname . '.zip' ); - - // Now we need to check if everything is present. Data and dbexport.xml - - - // Delete current data folder. - OC_Log::write('admin_export',"Deleting current data dir",OC_Log::INFO); - unlinkRecursive( $datadir, false ); - - // Copy over data - if( !copy_r( get_temp_dir() . '/' . $importname . '/data', $datadir ) ){ - OC_Log::write('admin_export',"Failed to copy over data directory",OC_Log::INFO); - exit(); - } - - OC_DB::replaceDB( get_temp_dir() . '/' . $importname . '/dbexport.xml' ); + // TODO + // OC_Migrate::import( $pathtozipfile ); } else { // fill template $tmpl = new OC_Template('admin_export', 'settings'); return $tmpl->fetchPage(); -} - -function zipAddDir($dir, $zip, $recursive=true, $internalDir='') { - $dirname = basename($dir); - $zip->addEmptyDir($internalDir . $dirname); - $internalDir.=$dirname.='/'; - - if ($dirhandle = opendir($dir)) { - while (false !== ( $file = readdir($dirhandle))) { - - if (( $file != '.' ) && ( $file != '..' )) { - - if (is_dir($dir . '/' . $file) && $recursive) { - zipAddDir($dir . '/' . $file, $zip, $recursive, $internalDir); - } elseif (is_file($dir . '/' . $file)) { - $zip->addFile($dir . '/' . $file, $internalDir . $file); - } - } - } - closedir($dirhandle); - } else { - OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); - } -} - -function unlinkRecursive($dir, $deleteRootToo) -{ - if(!$dh = @opendir($dir)) - { - return; - } - while (false !== ($obj = readdir($dh))) - { - if($obj == '.' || $obj == '..') - { - continue; - } - - if (!@unlink($dir . '/' . $obj)) - { - unlinkRecursive($dir.'/'.$obj, true); - } - } - - closedir($dh); - - if ($deleteRootToo) - { - @rmdir($dir); - } - - return; -} - - function copy_r( $path, $dest ) - { - if( is_dir($path) ) - { - @mkdir( $dest ); - $objects = scandir($path); - if( sizeof($objects) > 0 ) - { - foreach( $objects as $file ) - { - if( $file == "." || $file == ".." ) - continue; - // go on - if( is_dir( $path.DS.$file ) ) - { - copy_r( $path.DS.$file, $dest.DS.$file ); - } - else - { - copy( $path.DS.$file, $dest.DS.$file ); - } - } - } - return true; - } - elseif( is_file($path) ) - { - return copy($path, $dest); - } - else - { - return false; - } - } +} \ No newline at end of file diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index ffc5e9f838..36a08c0cf4 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -1,16 +1,16 @@ 'bookmarks', 'matchcol'=>'user_id', - 'matchval'=>$uid, + 'matchval'=>$this->uid, 'idcol'=>'id' ); - $ids = OC_Migrate::copyRows( $options ); + $ids = $this->content->copyRows( $options ); $options = array( 'table'=>'bookmarks_tags', @@ -19,7 +19,7 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ ); // Export tags - $ids2 = OC_Migrate::copyRows( $options ); + $ids2 = $this->content->copyRows( $options ); // If both returned some ids then they worked if( is_array( $ids ) && is_array( $ids2 ) ) @@ -32,17 +32,17 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } // Import function for bookmarks - function import( $app, $info ){ - switch( $app->version ){ + function import( ){ + switch( $this->appinfo->version ){ default: // All versions of the app have had the same db structure, so all can use the same import function $query = OC_Migrate::prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" ); - $results = $query->execute( array( $info['olduid'] ) ); + $results = $query->execute( array( $this->info['olduid'] ) ); $idmap = array(); while( $row = $data->fetchRow() ){ // Import each bookmark, saving its id into the map $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" ); - $query->execute( array( $row['url'], $row['title'], $info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); + $query->execute( array( $row['url'], $row['title'], $this->info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); // Map the id $idmap[$row['id']] = OC_DB::insertid(); } @@ -66,4 +66,4 @@ class OC_Migrate_Provider_Bookmarks extends OC_Migrate_Provider{ } // Load the provider -new OC_Migrate_Provider_Bookmarks( 'bookmarks' ); \ No newline at end of file +new OC_Migration_Provider_Bookmarks( 'bookmarks' ); \ No newline at end of file diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 62f5e3f20d..38eee990b4 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -26,7 +26,7 @@ OC_Util::checkAppEnabled('user_migrate'); if (isset($_POST['user_export'])) { // Create the export zip - if( !$path = OC_Migrate::createUserExportFile() ){ + if( !$path = OC_Migrate::export() ){ // Error die('error'); } else { diff --git a/lib/migrate.php b/lib/migrate.php index 84eafcd4cd..338d091af8 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -22,28 +22,31 @@ /** - * provides an interface to all search providers + * provides an interface to migrate users and whole ownclouds */ class OC_Migrate{ - // Holds the db object - static private $MDB2=false; + // Array of OC_Migration_Provider objects static private $providers=array(); - // Schema db object - static private $schema=false; // User id of the user to import/export static private $uid=false; - // Path to the sqlite db - static private $dbpath=false; // Holds the ZipArchive object static private $zip=false; - // String path to export - static private $zippath=false; // Stores the type of export static private $exporttype=false; // Array of temp files to be deleted after zip creation static private $tmpfiles=array(); + // Holds the db object + static private $MDB2=false; + // Schema db object + static private $schema=false; + // Path to the sqlite db + static private $dbpath=false; + // Holds the path to the zip file + static private $zippath=false; + // Holds the OC_Migration_Content object + static private $content=false; /** * register a new migration provider @@ -69,99 +72,78 @@ class OC_Migrate{ } /** - * @breif creates a migration.db in the users data dir with their app data in - * @return bool whether operation was successfull + * @breif exports a user, or owncloud instance + * @param ootional $type string type of export, defualts to user + * @param otional $path string path to zip output folder + * @param optional $uid string user id of user to export if export type is user, defaults to current */ - private static function exportAppData( ){ - - self::connectDB(); - $ok = true; - $return = array(); - - // Find the providers - self::findProviders(); - - // Foreach provider - foreach( self::$providers as $provider ){ - $failed = false; - - // Does this app use the database? - if(file_exists(OC::$SERVERROOT.'/apps/'.$provider->id.'/appinfo/database.xml')){ - // Create some app tables - $tables = self::createAppTables( $provider->id ); - if( is_array( $tables ) ){ - // Save the table names - foreach($tables as $table){ - $return['apps'][$provider->id]['tables'][] = $table; - } - } else { - // It failed to create the tables - $failed = true; - } - } - - // Run the import function? - if( !$failed ){ - $return['apps'][$provider->id]['success'] = $provider->export( self::$uid ); - } else { - $return['apps'][$provider->id]['success'] = false; - $return['apps'][$provider->id]['message'] = 'failed to create the app tables'; - } - - // Now add some app info the the return array - $appinfo = OC_App::getAppInfo( $provider->id ); - $return['apps'][$provider->id]['version'] = $appinfo['version']; - - } - - return $return; - - } - - /** - * @breif creates an export file for the whole system - * @param optional $exporttype string export type ('instance','system' or 'userfiles') - * @param optional $path string path to zip destination (with trailing slash) - * @return path to the zip or false if there was a problem - */ - static public function createSysExportFile( $exporttype='instance', $path=null ){ - // Calculate zip name - $zipname = "oc_export_" . date("y-m-d_H-i-s") . ".zip"; - // Get the data dir + public static function export( $type='user', $path=null, $uid=null ){ $datadir = OC_Config::getValue( 'datadirectory' ); - // Calculate destination - if( !is_null( $path ) ){ - // Path given - // Is a directory? - if( !is_dir( $path ) ){ - OC_Log::write('migration', 'Path supplied to createSysExportFile() is not a directory', OC_Log::ERROR); - return false; - } - // Is writeable - if( !is_writeable( $path ) ){ - OC_Log::write('migration', 'Path supplied to createSysExportFile() is not writeable', OC_Log::ERROR); - return false; - } - self::$zippath = $path . $zipname; - } else { - // Save in tmp dir - self::$zippath = sys_get_temp_dir() . '/' . $zipname; - } - // Create the zip object - self::$zip = new ZipArchive; - // Try to create the zip - if( !self::createZip() ){ - return false; - } - // Handle export types - $exporttypes = array( 'userfiles', 'instance', 'system' ); - self::$exporttype = in_array( $exporttype, $exporttypes ) ? $exporttype : false; - if( !self::$exporttype ){ - OC_Log::write( 'migration', 'Export type: '.$exporttype.' is not supported.', OC_Log::ERROR); - return false; - } - switch( self::$exporttype ){ - case 'instance': + // Validate export type + $types = array( 'user', 'instance', 'system', 'userfiles' ); + if( !in_array( $type, $types ) ){ + OC_Log::write( 'migration', 'Invalid export type', OC_Log::ERROR ); + return false; + } + self::$exporttype = $type; + // Userid? + if( self::$exporttype == 'user' ){ + // Check user exists + if( !is_null($uid) ){ + if( !OC_User_Database::userExists( $uid ) ){ + OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); + return false; + } + self::$uid = $uid; + } else { + self::$uid = OC_User::getUser(); + } + } + // Calculate zipname + if( self::$exporttype == 'user' ){ + $zipname = 'oc_export_' . self::$uid . '_' . date("y-m-d_H-i-s") . '.zip'; + } else { + $zipname = 'oc_export_' . self::$exporttype . '_' . date("y-m-d_H-i-s") . '.zip'; + } + // Calculate path + if( self::$exporttype == 'user' ){ + self::$zippath = $datadir . '/' . self::$uid . '/' . $zipname; + } else { + if( !is_null( $path ) ){ + // Validate custom path + if( !file_exists( $path ) || !is_writeable( $path ) ){ + OC_Log::write( 'migration', 'Path supplied is invalid.', OC_Log::ERROR ); + return false; + } + self::$zippath = $path . $zipname; + } else { + // Default path + self::$zippath = get_temp_dir() . '/' . $zipname; + } + } + // Create the zip object + self::$zip = new ZipArchive; + if( !self::createZip() ){ + return false; + } + // Do the export + self::findProviders(); + $exportdata = array(); + switch( self::$exporttype ){ + case 'user': + // Connect to the db + self::$dbpath = $datadir . '/' . self::$uid . '/migration.db'; + if( !self::connectDB() ){ + return false; + } + self::$content = new OC_Migration_Content( self::$zip, self::$MDB2 ); + // Export the app info + $exportdata = self::exportAppData(); + // Add the data dir to the zip + self::$content->addDir( $datadir . '/' . self::$uid, true, '/' ); + break; + case 'instance': + self::$content = new OC_Migration_Content( self::$zip ); // Creates a zip that is compatable with the import function $dbfile = tempnam( "/tmp", "owncloud_export_data_" ); OC_DB::getDbStructure( $dbfile, 'MDB2_SCHEMA_DUMP_ALL'); @@ -172,43 +154,112 @@ class OC_Migrate{ $dbtableprefixstring = "
\n\n " . OC_Config::getValue( "dbtableprefix", "oc_" ); $dbexport = str_replace( $dbnamestring, "\n\n *dbname*", $dbexport ); $dbexport = str_replace( $dbtableprefixstring, "
\n\n *dbprefix*", $dbexport ); - // Write the new db export file - file_put_contents( $dbfile, $dbexport ); - self::$zip->addFile( $dbfile, "dbexport.xml" ); + // Add the export to the zip + self::$content->addFromString( $dbexport, "dbexport.xml" ); // Add user data foreach(OC_User::getUsers() as $user){ - self::addDirToZip( $datadir . '/' . $user . '/', true, "/userdata/" ); + self::$content->addDir( $datadir . '/' . $user . '/', true, "/userdata/" ); } break; case 'userfiles': + self::$content = new OC_Migration_Content( self::$zip ); // Creates a zip with all of the users files foreach(OC_User::getUsers() as $user){ - self::addDirToZip( $datadir . '/' . $user . '/', true, "/" ); + self::$content->addDir( $datadir . '/' . $user . '/', true, "/" ); } break; case 'system': + self::$content = new OC_Migration_Content( self::$zip ); // Creates a zip with the owncloud system files - self::addDirToZip( OC::$SERVERROOT . '/', false, '/'); + self::$content->addDir( OC::$SERVERROOT . '/', false, '/'); foreach (array(".git", "3rdparty", "apps", "core", "files", "l10n", "lib", "ocs", "search", "settings", "tests") as $dir) { - self::addDirToZip( OC::$SERVERROOT . '/' . $dir, true, "/"); + self::$content->addDir( OC::$SERVERROOT . '/' . $dir, true, "/"); } - break; + break; + } + if( !$info = self::getExportInfo( $exportdata ) ){ + return false; + } + // Add the export info json to the export zip + self::$content->addFromString( $info, 'export_info.json' ); + if( !self::$content->finish() ){ + return false; + } + return self::$zippath; + } + + /** + * @brief connects to a MDB2 database scheme + * @returns bool + */ + static private function connectScheme(){ + // We need a mdb2 database connection + self::$MDB2->loadModule( 'Manager' ); + self::$MDB2->loadModule( 'Reverse' ); + + // Connect if this did not happen before + if( !self::$schema ){ + require_once('MDB2/Schema.php'); + self::$schema=MDB2_Schema::factory( self::$MDB2 ); } - // Add export info - self::addExportInfo(); - // Close the zip - if( !self::closeZip() ){ - return false; - } - return self::$zippath; - + + return true; } /** - * @breif adds a json file with infomation on the export to the zips root (used on import) - * @return bool - */ - static private function addExportInfo( $array=array() ){ + * @breif creates a migration.db in the users data dir with their app data in + * @return bool whether operation was successfull + */ + private static function exportAppData( ){ + + $success = true; + $return = array(); + + // Foreach provider + foreach( self::$providers as $provider ){ + $success = true; + // Does this app use the database? + if( file_exists( OC::$SERVERROOT.'/apps/'.$provider->getID().'/appinfo/database.xml' ) ){ + // Create some app tables + $tables = self::createAppTables( $provider->getID() ); + if( is_array( $tables ) ){ + // Save the table names + foreach($tables as $table){ + $return['apps'][$provider->getID()]['tables'][] = $table; + } + } else { + // It failed to create the tables + $success = false; + } + } + + // Run the export function? + if( $success ){ + // Set the provider properties + $provider->setData( self::$uid, self::$content ); + $return['apps'][$provider->getID()]['success'] = $provider->export(); + } else { + $return['apps'][$provider->getID()]['success'] = false; + $return['apps'][$provider->getID()]['message'] = 'failed to create the app tables'; + } + + // Now add some app info the the return array + $appinfo = OC_App::getAppInfo( $provider->getID() ); + $return['apps'][$provider->getID()]['version'] = $appinfo['version']; + + } + + return $return; + + } + + + /** + * @breif generates json containing export info, and merges any data supplied + * @param optional $array array of data to include in the returned json + * @return bool + */ + static private function getExportInfo( $array=array() ){ $info = array( 'ocversion' => OC_Util::getVersion(), 'exporttime' => time(), @@ -216,11 +267,12 @@ class OC_Migrate{ 'exporttype' => self::$exporttype ); // Add hash if user export - if( self::$exporttype = 'user' ){ + if( self::$exporttype == 'user' ){ $query = OC_DB::prepare( "SELECT password FROM *PREFIX*users WHERE uid LIKE ?" ); $result = $query->execute( array( self::$uid ) ); $row = $result->fetchRow(); $hash = $row ? $row['password'] : false; + die(var_dump($hash)); if( !$hash ){ OC_Log::write( 'migration', 'Failed to get the users password hash', OC_log::ERROR); return false; @@ -228,110 +280,122 @@ class OC_Migrate{ $info['hash'] = $hash; $info['exporteduser'] = self::$uid; } + if( !is_array( $array ) ){ + OC_Log::write( 'migration', 'Supplied $array was not an array in getExportInfo()', OC_Log::ERROR ); + } // Merge in other data - $info = array_merge( $info, $array ); + $info = array_merge( $info, (array)$array ); // Create json $json = json_encode( $info ); - $tmpfile = tempnam("/tmp", "oc_export_info_"); - self::$tmpfiles[] = $tmpfile; - if( !file_put_contents( $tmpfile, $json ) ){ - return false; - } else { - self::$zip->addFile( $tmpfile, "/" . self::$uid . "/export_info.json" ); - return true; - } - } - - - /** - * @breif tried to finalise the zip - * @return bool - */ - static private function closeZip(){ - if( !self::$zip->close() ){ - OC_Log::write('migration', 'Failed to save the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); - self::cleanup(); - return false; - } else { - OC_Log::write('migration', 'Export zip created ok', OC_Log::INFO); - self::cleanup(); - return true; - } + return true; } /** - * @breif cleans up after the zip - */ - static private function cleanup(){ - // Delete tmp files - foreach(self::$tmpfiles as $i){ - unlink( $i ); - } - } - - /** - * @breif creates a zip user export - * @param optional $uid string user id of the user to export (defaults to current) - * @param optional $path string path to folder to create file in (with trailing slash) (defaults to current user's data dir) - * @return false on failure | string path on success - */ - static public function createUserExportFile( $uid=null, $path=null ){ - // User passed? - $uid = is_null( $uid ) ? OC_User::getUser() : $uid ; - // Is a database user? - if( !OC_User_Database::userExists( $uid ) ){ - OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); + * @breif connects to migration.db, or creates if not found + * @param $db optional path to migration.db, defaults to user data dir + * @return bool whether the operation was successful + */ + static private function connectDB( $path=null ){ + // Has the dbpath been set? + self::$dbpath = !is_null( $path ) ? $path : self::$dbpath; + if( !self::$dbpath ){ + OC_Log::write( 'migration', 'connectDB() was called without dbpath being set', OC_Log::ERROR ); return false; } - // Set the uid - self::$uid = $uid; - // Create the zip object - self::$zip = new ZipArchive; - // Set export type - self::$exporttype = 'user'; - // Calculate users data dir - $user = OC_User::getUser(); - $userdatadir = OC_Config::getValue( 'datadirectory' ) . '/' . $user . '/'; - // Calculate zip name - $zipname = "oc_userexport_" . $user . '_' . date("y-m-d_H-i-s") . ".zip"; - // Calculate destination - if( !is_null( $path ) ){ - // Path given - // Is a directory? - if( !is_dir( $path ) ){ - OC_Log::write('migration', 'Path supplied to createUserExportFile() is not a directory', OC_Log::ERROR); - return false; - } - // Is writeable - if( !is_writeable( $path ) ){ - OC_Log::write('migration', 'Path supplied to createUserExportFile() is not writeable', OC_Log::ERROR); + // Already connected + if(!self::$MDB2){ + require_once('MDB2.php'); + + $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); + + // Prepare options array + $options = array( + 'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE), + 'log_line_break' => '
', + 'idxname_format' => '%s', + 'debug' => true, + 'quote_identifier' => true + ); + $dsn = array( + 'phptype' => 'sqlite3', + 'database' => self::$dbpath, + 'mode' => '0644' + ); + + // Try to establish connection + self::$MDB2 = MDB2::factory( $dsn, $options ); + // Die if we could not connect + if( PEAR::isError( self::$MDB2 ) ){ + die( self::$MDB2->getMessage() ); + OC_Log::write( 'migration', 'Failed to create/connect to migration.db', OC_Log::FATAL ); + OC_Log::write( 'migration', self::$MDB2->getUserInfo(), OC_Log::FATAL ); + OC_Log::write( 'migration', self::$MDB2->getMessage(), OC_Log::FATAL ); return false; } - self::$zippath = $path . $zipname; - } else { - // Save in users data dir - self::$zippath = $userdatadir . $zipname; + // We always, really always want associative arrays + self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); } - // Try to create the zip - if( !self::createZip() ){ - return false; - } - // Export the app info - $appinfo = self::exportAppData(); - // Save the migration results - self::addExportInfo( $appinfo ); - // Add the data dir to the zip - self::addDirToZip( $userdatadir ); - // Close the zip - if( !self::closeZip() ){ - return false; - } - // All good - return self::$zippath; - } + return true; + + } + /** + * @breif creates the tables in migration.db from an apps database.xml + * @param $appid string id of the app + * @return bool whether the operation was successful + */ + static private function createAppTables( $appid ){ + + if( !self::connectScheme() ){ + return false; + } + + // There is a database.xml file + $content = file_get_contents( OC::$SERVERROOT . '/apps/' . $appid . '/appinfo/database.xml' ); + + $file2 = 'static://db_scheme'; + // TODO get the relative path to migration.db from the data dir + // For now just cheat + $path = pathinfo( self::$dbpath ); + $content = str_replace( '*dbname*', self::$uid.'/migration', $content ); + $content = str_replace( '*dbprefix*', '', $content ); + + $xml = new SimpleXMLElement($content); + foreach($xml->table as $table){ + $tables[] = (string)$table->name; + } + + file_put_contents( $file2, $content ); + + // Try to create tables + $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); + + unlink( $file2 ); + + // Die in case something went wrong + if( $definition instanceof MDB2_Schema_Error ){ + OC_Log::write( 'migration', 'Failed to parse database.xml for: '.$appid, OC_Log::FATAL ); + OC_Log::write( 'migration', $definition->getMessage().': '.$definition->getUserInfo(), OC_Log::FATAL ); + return false; + } + + $definition['overwrite'] = true; + + $ret = self::$schema->createDatabase( $definition ); + + // Die in case something went wrong + if( $ret instanceof MDB2_Error ){ + OC_Log::write( 'migration', 'Failed to create tables for: '.$appid, OC_Log::FATAL ); + OC_Log::write( 'migration', $ret->getMessage().': '.$ret->getUserInfo(), OC_Log::FATAL ); + return false; + } + return $tables; + + } + /** * @breif tries to create the zip + * @param $path string path to zip destination * @return bool */ static private function createZip(){ @@ -340,45 +404,13 @@ class OC_Migrate{ OC_Log::write('migration', 'createZip() called but $zip and/or $zippath have not been set', OC_Log::ERROR); return false; } - if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE ) !== TRUE ) { + if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE ) !== TRUE ) { OC_Log::write('migration', 'Failed to create the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); return false; } else { return true; } } - - /** - * @breif adds a directory to the zip object - * @param $dir string path of the directory to add - * @param $recursive bool - * @param $internaldir string path of folder to add dir to in zip - * @return bool - */ - static private function addDirToZip($dir, $recursive=true, $internaldir='') { - $dirname = basename($dir); - self::$zip->addEmptyDir($internaldir . $dirname); - $internaldir.=$dirname.='/'; - - if ($dirhandle = opendir($dir)) { - while (false !== ( $file = readdir($dirhandle))) { - - if (( $file != '.' ) && ( $file != '..' )) { - - if (is_dir($dir . '/' . $file) && $recursive) { - self::addDirToZip($dir . '/' . $file, $recursive, $internaldir); - } elseif (is_file($dir . '/' . $file)) { - self::$zip->addFile($dir . '/' . $file, $internaldir . $file); - } - } - } - closedir($dirhandle); - } else { - OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); - return false; - } - return true; - } /** * @breif returns an array of apps that support migration @@ -429,10 +461,17 @@ class OC_Migrate{ foreach( self::$providers as $provider){ // Is the app in the export? - $id = $provider->id; + $id = $provider->getID(); if( isset( $info->apps->$id ) ){ // Did it succeed? if( $info->apps->$id->success ){ + // Give the provider the content object + // TODO PASS THE PATH TO MIGRATION.DB + if( !self::connectDB() ){ + return false; + } + $content = new OC_Migration_Content( self::$zip, self::$db ); + $provider->setObject( $content ); // Then do the import $provider->import( $info->apps->$id, $importinfo ); } @@ -443,252 +482,12 @@ class OC_Migrate{ } - // @breif connects to migration.db, or creates if not found - // @param $db optional path to migration.db, defaults to user data dir - // @return bool whether the operation was successful - private static function connectDB( $dbpath=null ){ - OC_Log::write('migration','connecting to migration.db for user: '.self::$uid,OC_Log::INFO); - // Fail if no user is set - if(!self::$uid){ - OC_Log::write('migration','connectDB() called without self::$uid being set',OC_Log::INFO); - return false; - } - // Already connected - if(!self::$MDB2){ - require_once('MDB2.php'); - - $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - - self::$dbpath = $datadir.'/'.self::$uid.'/migration.db';//!is_null( $dbpath ) ? $dbpath : $datadir.'/'.self::$uid.'/migration.db'; - - // Prepare options array - $options = array( - 'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE), - 'log_line_break' => '
', - 'idxname_format' => '%s', - 'debug' => true, - 'quote_identifier' => true - ); - $dsn = array( - 'phptype' => 'sqlite3', - 'database' => self::$dbpath, - 'mode' => '0644' - ); - - // Try to establish connection - self::$MDB2 = MDB2::factory( $dsn, $options ); - // Die if we could not connect - if( PEAR::isError( self::$MDB2 )){ - die(self::$MDB2->getMessage()); - OC_Log::write('migration', 'Failed to create/connect to migration.db',OC_Log::FATAL); - OC_Log::write('migration',self::$MDB2->getUserInfo(),OC_Log::FATAL); - OC_Log::write('migration',self::$MDB2->getMessage(),OC_Log::FATAL); - return false; - } else { - } - // We always, really always want associative arrays - self::$MDB2->setFetchMode(MDB2_FETCHMODE_ASSOC); - } - return true; - - } - - // @breif prepares the db - // @param $query the sql query to prepare - public static function prepare( $query ){ - - // Optimize the query - $query = self::processQuery( $query ); - - // Optimize the query - $query = self::$MDB2->prepare( $query ); - - // Die if we have an error (error means: bad query, not 0 results!) - if( PEAR::isError( $query )) { - $entry = 'DB Error: "'.$result->getMessage().'"
'; - $entry .= 'Offending command was: '.$query.'
'; - OC_Log::write('migration',$entry,OC_Log::FATAL); - return false; - } else { - return $query; - } - - } - - // @breif processes the db query - // @param $query the query to process - // @return string of processed query - private static function processQuery( $query ){ - - self::connectDB(); - $prefix = ''; - - $query = str_replace( '`', '\'', $query ); - $query = str_replace( 'NOW()', 'datetime(\'now\')', $query ); - $query = str_replace( 'now()', 'datetime(\'now\')', $query ); - - // replace table name prefix - $query = str_replace( '*PREFIX*', $prefix, $query ); - - return $query; - - } - - // @brief copys rows to migration.db from the main database - // @param $options array of options. - // @return bool - public static function copyRows( $options ){ - if( !array_key_exists( 'table', $options ) ){ - return false; - } - - $return = array(); - - // Need to include 'where' in the query? - if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){ - - // If only one matchval, create an array - if(!is_array($options['matchval'])){ - $options['matchval'] = array( $options['matchval'] ); - } - - foreach( $options['matchval'] as $matchval ){ - // Run the query for this match value (where x = y value) - $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); - $results = $query->execute( array( $matchval ) ); - $newreturns = self::insertData( $results, $options ); - $return = array_merge( $return, $newreturns ); - } - - } else { - // Just get everything - $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] ); - $results = $query->execute(); - $return = self::insertData( $results, $options ); - - } - - return $return; - - } - - // @breif saves a sql data set into migration.db - // @param $data a sql data set returned from self::prepare()->query() - // @param $options array of copyRows options - // @return void - private static function insertData( $data, $options ){ - $return = array(); - while( $row = $data->fetchRow() ){ - // Now save all this to the migration.db - $fields = array(); - $values = array(); - foreach($row as $field=>$value){ - $fields[] = $field; - $values[] = $value; - } - - // Generate some sql - $sql = "INSERT INTO `" . $options['table'] . '` ( `'; - $fieldssql = implode( '`, `', $fields ); - $sql .= $fieldssql . "` ) VALUES( "; - $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-2 ); - $sql .= $valuessql . " )"; - // Make the query - $query = self::prepare( $sql ); - if(!$query){ - OC_Log::write('migration','Invalid sql produced: '.$sql,OC_Log::FATAL); - return false; - exit(); - } else { - $query->execute( $values ); - // Do we need to return some values? - if( array_key_exists( 'idcol', $options ) ){ - // Yes we do - $return[] = $row[$options['idcol']]; - } else { - // Take a guess and return the first field :) - $return[] = reset($row); - } - } - } - return $return; - } - - // @breif creates the tables in migration.db from an apps database.xml - // @param $appid string id of the app - // @return bool whether the operation was successful - private static function createAppTables( $appid ){ - - if(!self::connectScheme()){ - return false; - } - - // There is a database.xml file - $content = file_get_contents( OC::$SERVERROOT . '/apps/' . $appid . '/appinfo/database.xml' ); - - $file2 = 'static://db_scheme'; - $content = str_replace( '*dbname*', self::$uid.'/migration', $content ); - $content = str_replace( '*dbprefix*', '', $content ); - - $xml = new SimpleXMLElement($content); - foreach($xml->table as $table){ - $tables[] = (string)$table->name; - } - - file_put_contents( $file2, $content ); - - // Try to create tables - $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - - unlink( $file2 ); - - // Die in case something went wrong - if( $definition instanceof MDB2_Schema_Error ){ - OC_Log::write('migration','Failed to parse database.xml for: '.$appid,OC_Log::FATAL); - OC_Log::write('migration',$definition->getMessage().': '.$definition->getUserInfo(),OC_Log::FATAL); - return false; - } - - $definition['overwrite'] = true; - - $ret = self::$schema->createDatabase( $definition ); - // Die in case something went wrong - - if( $ret instanceof MDB2_Error ){ - OC_Log::write('migration','Failed to create tables for: '.$appid,OC_Log::FATAL); - OC_Log::write('migration',$ret->getMessage().': '.$ret->getUserInfo(),OC_Log::FATAL); - return false; - } - return $tables; - - } - - - /** - * @brief connects to a MDB2 database scheme - * @returns true/false - * - * Connects to a MDB2 database scheme - */ - private static function connectScheme(){ - // We need a mdb2 database connection - self::connectDB(); - self::$MDB2->loadModule( 'Manager' ); - self::$MDB2->loadModule( 'Reverse' ); - - // Connect if this did not happen before - if( !self::$schema ){ - require_once('MDB2/Schema.php'); - self::$schema=MDB2_Schema::factory( self::$MDB2 ); - } - - return true; - } - - // @breif creates a new user in the database - // @param $uid string user_id of the user to be created - // @param $hash string hash of the user to be created - // @return bool result of user creation + /* + * @breif creates a new user in the database + * @param $uid string user_id of the user to be created + * @param $hash string hash of the user to be created + * @return bool result of user creation + */ public static function createUser( $uid, $hash ){ // Check if userid exists diff --git a/lib/migrate/provider.php b/lib/migrate/provider.php deleted file mode 100644 index 7ac3cf97ca..0000000000 --- a/lib/migrate/provider.php +++ /dev/null @@ -1,28 +0,0 @@ -id = $appid; - OC_Migrate::registerProvider( $this ); - } - - /** - * @breif exports data for apps - * @param string $uid - * @return array appdata to be exported - */ - abstract function export($uid); - - /** - * @breif imports data for the app - * @param $appinfo object with the data that the app exported - * @param $info array of info including exportinfo.json - * @return void - */ - abstract function import( $appinfo, $info ); -} diff --git a/lib/migration/content.php b/lib/migration/content.php new file mode 100644 index 0000000000..fe8a21a45b --- /dev/null +++ b/lib/migration/content.php @@ -0,0 +1,239 @@ +. + * + */ + + +/** + * provides methods to add and access data from the migration + */ +class OC_Migration_Content{ + + private $zip=false; + // Holds the MDB2 object + private $db=false; + // Holds an array of tmpfiles to delete after zip creation + private $tmpfiles=false; + + /** + * @breif sets up the + * @param $zip ZipArchive object + * @param optional $db a MDB2 database object (required for exporttype user) + * @return bool + */ + public function __construct( $zip, $db=false ){ + + $this->zip = $zip; + $this->db = $db; + + } + + // @breif prepares the db + // @param $query the sql query to prepare + public function prepare( $query ){ + + // Optimize the query + $query = $this->processQuery( $query ); + + // Optimize the query + $query = $this->MDB2->prepare( $query ); + + // Die if we have an error (error means: bad query, not 0 results!) + if( PEAR::isError( $query ) ) { + $entry = 'DB Error: "'.$result->getMessage().'"
'; + $entry .= 'Offending command was: '.$query.'
'; + OC_Log::write( 'migration', $entry, OC_Log::FATAL ); + return false; + } else { + return $query; + } + + } + + /** + * @breif processes the db query + * @param $query the query to process + * @return string of processed query + */ + private function processQuery( $query ){ + $query = str_replace( '`', '\'', $query ); + $query = str_replace( 'NOW()', 'datetime(\'now\')', $query ); + $query = str_replace( 'now()', 'datetime(\'now\')', $query ); + // remove table prefixes + $query = str_replace( '*PREFIX*', '', $query ); + return $query; + } + + /** + * @brief copys rows to migration.db from the main database + * @param $options array of options. + * @return bool + */ + public function copyRows( $options ){ + if( !array_key_exists( 'table', $options ) ){ + return false; + } + + $return = array(); + + // Need to include 'where' in the query? + if( array_key_exists( 'matchval', $options ) && array_key_exists( 'matchcol', $options ) ){ + + // If only one matchval, create an array + if(!is_array($options['matchval'])){ + $options['matchval'] = array( $options['matchval'] ); + } + + foreach( $options['matchval'] as $matchval ){ + // Run the query for this match value (where x = y value) + $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); + $results = $query->execute( array( $matchval ) ); + $newreturns = $this->insertData( $results, $options ); + $return = array_merge( $return, $newreturns ); + } + + } else { + // Just get everything + $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] ); + $results = $query->execute(); + $return = $this->insertData( $results, $options ); + + } + + return $return; + + } + + /** + * @breif saves a sql data set into migration.db + * @param $data a sql data set returned from self::prepare()->query() + * @param $options array of copyRows options + * @return void + */ + private function insertData( $data, $options ){ + $return = array(); + while( $row = $data->fetchRow() ){ + // Now save all this to the migration.db + foreach($row as $field=>$value){ + $fields[] = $field; + $values[] = $value; + } + + // Generate some sql + $sql = "INSERT INTO `" . $options['table'] . '` ( `'; + $fieldssql = implode( '`, `', $fields ); + $sql .= $fieldssql . "` ) VALUES( "; + $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-2 ); + $sql .= $valuessql . " )"; + // Make the query + $query = $this->prepare( $sql ); + if( !$query ){ + OC_Log::write( 'migration', 'Invalid sql produced: '.$sql, OC_Log::FATAL ); + return false; + exit(); + } else { + $query->execute( $values ); + // Do we need to return some values? + if( array_key_exists( 'idcol', $options ) ){ + // Yes we do + $return[] = $row[$options['idcol']]; + } else { + // Take a guess and return the first field :) + $return[] = reset($row); + } + } + } + return $return; + } + + /** + * @breif adds a directory to the zip object + * @param $dir string path of the directory to add + * @param $recursive bool + * @param $internaldir string path of folder to add dir to in zip + * @return bool + */ + public function addDir( $dir, $recursive=true, $internaldir='' ) { + $dirname = basename($dir); + $this->zip->addEmptyDir($internaldir . $dirname); + $internaldir.=$dirname.='/'; + + if ($dirhandle = opendir($dir)) { + while (false !== ( $file = readdir($dirhandle))) { + + if (( $file != '.' ) && ( $file != '..' )) { + + if (is_dir($dir . '/' . $file) && $recursive) { + $this->addDir($dir . '/' . $file, $recursive, $internaldir); + } elseif (is_file($dir . '/' . $file)) { + $this->zip->addFile($dir . '/' . $file, $internaldir . $file); + } + } + } + closedir($dirhandle); + } else { + OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); + return false; + } + return true; + } + + /** + * @breif adds a file to the zip from a given string + * @param $data string of data to add + * @param $path the relative path inside of the zip to save the file to + * @return bool + */ + public function addFromString( $data, $path ){ + // Create a temp file + $file = tempnam( get_temp_dir(). '/', 'oc_export_tmp_' ); + $this->tmpfiles[] = $file; + if( !file_put_contents( $file, $data ) ){ + OC_Log::write( 'migation', 'Failed to save data to a temporary file', OC_Log::ERROR ); + return false; + } + // Add file to the zip + $this->zip->addFile( $file, $path ); + return true; + } + + /** + * @breif closes the zip, removes temp files + * @return bool + */ + public function finish(){ + if( !$this->zip->close() ){ + OC_Log::write( 'migration', 'Failed to write the zip file with error: '.$this->zip->getStatusString(), OC_Log::ERROR ); + return false; + } + $this->cleanup(); + return true; + } + + /** + * @breif cleans up after the zip + */ + private function cleanup(){ + // Delete tmp files + foreach($this->tmpfiles as $i){ + unlink( $i ); + } + } +} \ No newline at end of file diff --git a/lib/migration/provider.php b/lib/migration/provider.php new file mode 100644 index 0000000000..b9e2c47620 --- /dev/null +++ b/lib/migration/provider.php @@ -0,0 +1,49 @@ +id = $appid; + OC_Migrate::registerProvider( $this ); + } + + /** + * @breif exports data for apps + * @return array appdata to be exported + */ + abstract function export( ); + + /** + * @breif imports data for the app + * @return void + */ + abstract function import( ); + + /** + * @breif sets the OC_Migration_Content object to $this->content + * @param $content a OC_Migration_Content object + */ + public function setData( $uid, $content, $info=false, $appinfo=false ){ + $this->content = $content; + $this->uid = $uid; + $this->info = $info; + $this->appinfo = $appinfo; + } + + /** + * @breif returns the appid of the provider + * @return string + */ + public function getID(){ + return $this->id; + } +} From 514c9ad8e7df1d7882adc33c42eb32a209537273 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 20 Mar 2012 20:19:21 +0000 Subject: [PATCH 044/133] Added unified import method. --- apps/admin_export/settings.php | 10 +- apps/bookmarks/lib/migrate.php | 10 +- apps/user_migrate/admin.php | 18 +--- lib/db.php | 7 +- lib/migrate.php | 188 +++++++++++++++++++++++++++++++-- lib/migration/content.php | 6 +- lib/migration/provider.php | 10 +- 7 files changed, 206 insertions(+), 43 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index af8dd0dbf5..cd85bedcd7 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -25,8 +25,6 @@ OC_Util::checkAdminUser(); OC_Util::checkAppEnabled('admin_export'); -define('DS', '/'); - // Export? if (isset($_POST['admin_export'])) { // Create the export zip @@ -44,9 +42,11 @@ if (isset($_POST['admin_export'])) { } // Import? } else if( isset($_POST['admin_import']) ){ - - // TODO - // OC_Migrate::import( $pathtozipfile ); + $from = $_FILES['owncloud_import']['tmp_name']; + + if( !OC_Migrate::import( $from ) ){ + die('failed'); + } } else { // fill template diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/lib/migrate.php index 36a08c0cf4..02c96e5963 100644 --- a/apps/bookmarks/lib/migrate.php +++ b/apps/bookmarks/lib/migrate.php @@ -36,19 +36,19 @@ class OC_Migration_Provider_Bookmarks extends OC_Migration_Provider{ switch( $this->appinfo->version ){ default: // All versions of the app have had the same db structure, so all can use the same import function - $query = OC_Migrate::prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" ); - $results = $query->execute( array( $this->info['olduid'] ) ); + $query = $this->content->prepare( "SELECT * FROM bookmarks WHERE user_id LIKE ?" ); + $results = $query->execute( array( $this->olduid ) ); $idmap = array(); - while( $row = $data->fetchRow() ){ + while( $row = $results->fetchRow() ){ // Import each bookmark, saving its id into the map $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" ); - $query->execute( array( $row['url'], $row['title'], $this->info['newuid'], $row['public'], $row['added'], $row['lastmodified'] ) ); + $query->execute( array( $row['url'], $row['title'], $this->uid, $row['public'], $row['added'], $row['lastmodified'] ) ); // Map the id $idmap[$row['id']] = OC_DB::insertid(); } // Now tags foreach($idmap as $oldid => $newid){ - $query = OC_Migrate::prepare( "SELECT * FROM bookmarks_tags WHERE user_id LIKE ?" ); + $query = $this->content->prepare( "SELECT * FROM bookmarks_tags WHERE user_id LIKE ?" ); $results = $query->execute( array( $oldid ) ); while( $row = $data->fetchRow() ){ // Import the tags for this bookmark, using the new bookmark id diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index 6f3565788e..d54bd6965b 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -36,24 +36,12 @@ if (isset($_POST['user_import'])) { $from = $_FILES['owncloud_import']['tmp_name']; $to = get_temp_dir().'/'.$importname.'.zip'; if( !move_uploaded_file( $from, $to ) ){ - OC_Log::write('migration',"Failed to copy the uploaded file",OC_Log::INFO); + OC_Log::write( 'user_migrate', "Failed to copy the uploaded file", OC_Log::ERROR ); exit(); } - // Extract zip - $zip = new ZipArchive(); - if ($zip->open(get_temp_dir().'/'.$importname.'.zip') != TRUE) { - OC_Log::write('migration',"Failed to open zip file",OC_Log::INFO); - exit(); - } - $zip->extractTo(get_temp_dir().'/'.$importname.'/'); - $zip->close(); - - $importdir = get_temp_dir() . '/' . $importname; - - // Delete uploaded file - unlink( $importdir . '.zip' ); - + OC_Migrate::import( $to, 'user', 'newuser' ); + die(); // Find folder $files = scandir( $importdir ); unset($files[0]); diff --git a/lib/db.php b/lib/db.php index 07e5859096..bfcff67869 100644 --- a/lib/db.php +++ b/lib/db.php @@ -487,6 +487,7 @@ class OC_DB { /** * @breif replaces the owncloud tables with a new set + * @param $file string path to the MDB2 xml db export file */ public static function replaceDB( $file ){ @@ -503,7 +504,11 @@ class OC_DB { } // Create new tables - self::createDBFromStructure( $file ); + if( self::createDBFromStructure( $file ) ){ + return true; + } else { + return false; + } } diff --git a/lib/migrate.php b/lib/migrate.php index 338d091af8..0058de7391 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -122,7 +122,6 @@ class OC_Migrate{ } } // Create the zip object - self::$zip = new ZipArchive; if( !self::createZip() ){ return false; } @@ -188,6 +187,177 @@ class OC_Migrate{ return self::$zippath; } + /** + * @breif imports a user, or owncloud instance + * @param $path string path to zip + * @param optional $uid userid of new user + */ + public static function import( $path, $uid=null ){ + OC_Util::checkAdminUser(); + $datadir = OC_Config::getValue( 'datadirectory' ); + // Extract the zip + if( !$extractpath = self::extractZip( $path ) ){ + return false; + } + // Get export_info.json + $scan = scandir( $extractpath ); + // Check for export_info.json + if( !in_array( 'export_info.json', $scan ) ){ + OC_Log::write( 'migration', 'Invalid import file, export_info.json note found', OC_Log::ERROR ); + return false; + } + $json = json_decode( file_get_contents( $extractpath . 'export_info.json' ) ); + self::$exporttype = $json->exporttype; + + // Have we got a user if type is user + if( self::$exporttype == 'user' ){ + if( !$uid ){ + self::$uid = $json->exporteduser; + } else { + self::$uid = $uid; + } + } + + // Handle export types + switch( self::$exporttype ){ + case 'user': + // Check user availability + if( OC_User::userExists( self::$uid ) ){ + OC_Log::write( 'migration', 'User already exists', OC_Log::ERROR ); + return false; + } + // Create the user + if( !self::createUser( self::$uid, $json->hash ) ){ + return false; + } + // Make the new users data dir + $path = $datadir . '/' . self::$uid . '/files/'; + if( !mkdir( $path, 0755, true ) ){ + OC_Log::write( 'migration', 'Failed to create users data dir: '.$path, OC_Log::ERROR ); + return false; + } + // Copy data + if( !self::copy_r( $extractpath . $json->exporteduser . '/files', $datadir . '/' . self::$uid . '/files' ) ){ + return false; + } + // Import user app data + if( !self::importAppData( $extractpath . $json->exporteduser . '/migration.db', $json, self::$uid ) ){ + return false; + } + // All done! + if( !self::unlink_r( $extractpath ) ){ + OC_Log::write( 'migration', 'Failed to delete the extracted zip', OC_Log::ERROR ); + } + return true; + break; + case 'instance': + // Check for new data dir and dbexport before doing anything + // TODO + /* + // Delete current data folder. + OC_Log::write( 'migration', "Deleting current data dir", OC_Log::INFO ); + if( self::unlink_r( $datadir, false ) ){ + OC_Log::write( 'migration', 'Failed to delete the current data dir', OC_Log::ERROR ); + return false; + } + + // Copy over data + if( !self::copy_r( $extractname . 'data', $datadir ) ){ + OC_Log::write( 'migration', 'Failed to copy over data directory', OC_Log::ERROR ); + return false; + } + */ + // Import the db + if( !OC_DB::replaceDB( $extractpath . 'dbexport.xml' ) ){ + return false; + } + // Done + return true; + break; + } + + } + + /** + * @breif recursively deletes a directory + * @param $dir string path of dir to delete + * $param optional $deleteRootToo bool delete the root directory + * @return bool + */ + private static function unlink_r( $dir, $deleteRootToo=true ){ + if( !$dh = @opendir( $dir ) ){ + return false; + } + while (false !== ($obj = readdir($dh))){ + if($obj == '.' || $obj == '..') { + continue; + } + if (!@unlink($dir . '/' . $obj)){ + self::unlink_r($dir.'/'.$obj, true); + } + } + closedir($dh); + if ( $deleteRootToo ) { + @rmdir($dir); + } + return true; + } + + /** + * @breif copies recursively + * @param $path string path to source folder + * @param $dest string path to destination + * @return bool + */ + private static function copy_r( $path, $dest ){ + if( is_dir($path) ){ + @mkdir( $dest ); + $objects = scandir( $path ); + if( sizeof( $objects ) > 0 ){ + foreach( $objects as $file ){ + if( $file == "." || $file == ".." ) + continue; + // go on + if( is_dir( $path . '/' . $file ) ){ + self::copy_r( $path .'/' . $file, $dest . '/' . $file ); + } else { + copy( $path . '/' . $file, $dest . '/' . $file ); + } + } + } + return true; + } + elseif( is_file( $path ) ){ + return copy( $path, $dest ); + } else { + return false; + } + } + + /** + * @breif tries to extract the import zip + * @param $path string path to the zip + * @return string path to extract location (with a trailing slash) or false on failure + */ + static private function extractZip( $path ){ + self::$zip = new ZipArchive; + // Validate path + if( !file_exists( $path ) ){ + OC_Log::write( 'migration', 'Zip not found', OC_Log::ERROR ); + return false; + } + if ( self::$zip->open( $path ) != TRUE ) { + OC_Log::write( 'migration', "Failed to open zip file", OC_Log::ERROR ); + return false; + } + $to = get_temp_dir() . '/oc_import_' . self::$exporttype . '_' . date("y-m-d_H-i-s") . '/'; + if( !self::$zip->extractTo( $to ) ){ + return false; + } + self::$zip->close(); + return $to; + } + /** * @brief connects to a MDB2 database scheme * @returns bool @@ -272,7 +442,6 @@ class OC_Migrate{ $result = $query->execute( array( self::$uid ) ); $row = $result->fetchRow(); $hash = $row ? $row['password'] : false; - die(var_dump($hash)); if( !$hash ){ OC_Log::write( 'migration', 'Failed to get the users password hash', OC_log::ERROR); return false; @@ -287,7 +456,7 @@ class OC_Migrate{ $info = array_merge( $info, (array)$array ); // Create json $json = json_encode( $info ); - return true; + return $json; } /** @@ -399,8 +568,9 @@ class OC_Migrate{ * @return bool */ static private function createZip(){ + self::$zip = new ZipArchive; // Check if properties are set - if( !self::$zip || !self::$zippath ){ + if( !self::$zippath ){ OC_Log::write('migration', 'createZip() called but $zip and/or $zippath have not been set', OC_Log::ERROR); return false; } @@ -435,9 +605,6 @@ class OC_Migrate{ * @return bool if the import succedded */ public static function importAppData( $db, $info, $uid=null ){ - - self::$uid = !is_null( $uid ) ? $uid : $info->exporteduser; - // Check if the db exists if( file_exists( $db ) ){ // Connect to the db @@ -466,12 +633,11 @@ class OC_Migrate{ // Did it succeed? if( $info->apps->$id->success ){ // Give the provider the content object - // TODO PASS THE PATH TO MIGRATION.DB - if( !self::connectDB() ){ + if( !self::connectDB( $db ) ){ return false; } - $content = new OC_Migration_Content( self::$zip, self::$db ); - $provider->setObject( $content ); + $content = new OC_Migration_Content( self::$zip, self::$MDB2 ); + $provider->setData( self::$uid, $content, $info ); // Then do the import $provider->import( $info->apps->$id, $importinfo ); } diff --git a/lib/migration/content.php b/lib/migration/content.php index fe8a21a45b..d25b5af293 100644 --- a/lib/migration/content.php +++ b/lib/migration/content.php @@ -53,7 +53,7 @@ class OC_Migration_Content{ $query = $this->processQuery( $query ); // Optimize the query - $query = $this->MDB2->prepare( $query ); + $query = $this->db->prepare( $query ); // Die if we have an error (error means: bad query, not 0 results!) if( PEAR::isError( $query ) ) { @@ -174,7 +174,9 @@ class OC_Migration_Content{ $dirname = basename($dir); $this->zip->addEmptyDir($internaldir . $dirname); $internaldir.=$dirname.='/'; - + if( !file_exists( $dir ) ){ + return false; + } if ($dirhandle = opendir($dir)) { while (false !== ( $file = readdir($dirhandle))) { diff --git a/lib/migration/provider.php b/lib/migration/provider.php index b9e2c47620..d592ed6726 100644 --- a/lib/migration/provider.php +++ b/lib/migration/provider.php @@ -7,7 +7,7 @@ abstract class OC_Migration_Provider{ protected $id=false; protected $content=false; protected $uid=false; - protected $info=false; + protected $olduid=false; protected $appinfo=false; public function __construct( $appid ){ @@ -32,11 +32,13 @@ abstract class OC_Migration_Provider{ * @breif sets the OC_Migration_Content object to $this->content * @param $content a OC_Migration_Content object */ - public function setData( $uid, $content, $info=false, $appinfo=false ){ + public function setData( $uid, $content, $info=false ){ $this->content = $content; $this->uid = $uid; - $this->info = $info; - $this->appinfo = $appinfo; + $this->olduid = $info->exporteduser; + $id = $this->id; + $this->appinfo = $info->apps->$id; + } /** From 0fa5e196ef8d0b220e4af17b008fb4908b080445 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 20 Mar 2012 20:32:01 +0000 Subject: [PATCH 045/133] Try to use old uid when importing --- apps/user_migrate/admin.php | 65 ++----------------------------- apps/user_migrate/appinfo/app.php | 1 - lib/migrate.php | 11 ++++-- 3 files changed, 11 insertions(+), 66 deletions(-) diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index d54bd6965b..c1afb0aed4 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -40,69 +40,10 @@ if (isset($_POST['user_import'])) { exit(); } - OC_Migrate::import( $to, 'user', 'newuser' ); - die(); - // Find folder - $files = scandir( $importdir ); - unset($files[0]); - unset($files[1]); - - // Get the user - if( count($files) != 1 ){ - OC_Log::write('migration', 'Invalid import file', OC_Log::ERROR); - die('invalid import, no user included'); + if( !OC_Migrate::import( $to, 'user' ) ){ + die( 'failed to to import' ); } - - $olduser = reset($files); - - // Check for dbexport.xml and export info and data dir - $files = scandir( $importdir . '/' . $olduser ); - - $required = array( 'migration.db', 'export_info.json', 'files'); - foreach($required as $require){ - if( !in_array( $require, $files) ){ - OC_Log::write('migration', 'Invlaid import file', OC_Log::ERROR); - die('invalid import'); - } - } - - $migrateinfo = $importdir . '/' . $olduser . '/export_info.json'; - $migrateinfo = json_decode( file_get_contents( $migrateinfo ) ); - - // Check if uid is available - if( OC_User::UserExists( $olduser ) ){ - OC_Log::write('migration','Username exists', OC_Log::ERROR); - die('user exists'); - } - - // Create the user - if( !OC_Migrate::createUser( $olduser, $migrateinfo->hash ) ){ - OC_Log::write('migration', 'Failed to create the new user', OC_Log::ERROR); - die('coundlt create new user'); - } - - $datadir = OC_Config::getValue( 'datadirectory' ); - // Make the new users data dir - $path = $datadir . '/' . $olduser . '/files/'; - if( !mkdir( $path, 0755, true ) ){ - OC_Log::write('migration','Failed to create users data dir: '.$path, OC_Log::ERROR); - die('failed to create users data dir'); - } - - // Copy data - if( !copy_r( $importdir . '/' . $olduser . '/files', $datadir . '/' . $olduser . '/files' ) ){ - OC_Log::write('migration','Failed to copy user files to destination', OC_Log::ERROR); - die('failed to copy user files'); - } - - // Import user data - if( !OC_Migrate::importAppData( $importdir . '/' . $olduser . '/migration.db', $migrateinfo ) ){ - OC_Log::write('migration','Failed to import user data', OC_Log::ERROR); - die('failed to import user data'); - } - - // All done! - die('done'); + } else { // fill template diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php index 18b97b93df..4b9cbd1308 100644 --- a/apps/user_migrate/appinfo/app.php +++ b/apps/user_migrate/appinfo/app.php @@ -22,7 +22,6 @@ */ OC_APP::registerPersonal('user_migrate','settings'); -OC_APP::registerAdmin('user_migrate','admin'); // add settings page to navigation $entry = array( diff --git a/lib/migrate.php b/lib/migrate.php index 0058de7391..8b0a2aa3f7 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -190,9 +190,10 @@ class OC_Migrate{ /** * @breif imports a user, or owncloud instance * @param $path string path to zip + * @param optional $type type of import (user or instance) * @param optional $uid userid of new user */ - public static function import( $path, $uid=null ){ + public static function import( $path, $type='user', $uid=null ){ OC_Util::checkAdminUser(); $datadir = OC_Config::getValue( 'datadirectory' ); // Extract the zip @@ -207,8 +208,12 @@ class OC_Migrate{ return false; } $json = json_decode( file_get_contents( $extractpath . 'export_info.json' ) ); - self::$exporttype = $json->exporttype; - + if( !$json->exporttype != $type ){ + OC_Log::write( 'migration', 'Invalid import file', OC_Log::ERROR ); + return false; + } + self::$exporttype = $type; + // Have we got a user if type is user if( self::$exporttype == 'user' ){ if( !$uid ){ From 07d7138df08834acc1ac6ff019859ceca09d4690 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 20 Mar 2012 20:34:20 +0000 Subject: [PATCH 046/133] Register admin pane --- apps/user_migrate/appinfo/app.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php index 4b9cbd1308..18ea8f52b2 100644 --- a/apps/user_migrate/appinfo/app.php +++ b/apps/user_migrate/appinfo/app.php @@ -21,7 +21,8 @@ * */ -OC_APP::registerPersonal('user_migrate','settings'); +OC_APP::registerPersonal( 'user_migrate', 'settings' ); +OC_APP::registerAdmin( 'user_migrate', 'admin' ); // add settings page to navigation $entry = array( From 892343c7c11e7ba967f69174820a501b3954badb Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Wed, 21 Mar 2012 16:30:59 +0000 Subject: [PATCH 047/133] Fix instance import --- apps/admin_export/settings.php | 2 +- lib/migrate.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index cd85bedcd7..269147a3bc 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -44,7 +44,7 @@ if (isset($_POST['admin_export'])) { } else if( isset($_POST['admin_import']) ){ $from = $_FILES['owncloud_import']['tmp_name']; - if( !OC_Migrate::import( $from ) ){ + if( !OC_Migrate::import( $from, 'instance' ) ){ die('failed'); } diff --git a/lib/migrate.php b/lib/migrate.php index 8b0a2aa3f7..718dc57b9f 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -208,7 +208,7 @@ class OC_Migrate{ return false; } $json = json_decode( file_get_contents( $extractpath . 'export_info.json' ) ); - if( !$json->exporttype != $type ){ + if( $json->exporttype != $type ){ OC_Log::write( 'migration', 'Invalid import file', OC_Log::ERROR ); return false; } From 7ed8f3974417964df18b135b154fdfdbd4f1b6ca Mon Sep 17 00:00:00 2001 From: Nils Jansen Date: Fri, 23 Mar 2012 13:34:07 +0100 Subject: [PATCH 048/133] as preperation for the sgf viewer app i added a file icon for .sgf files (saved go games), and an according mimetype. --- core/img/filetypes/application-sgf.png | Bin 0 -> 725 bytes lib/mimetypes.fixlist.php | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 core/img/filetypes/application-sgf.png diff --git a/core/img/filetypes/application-sgf.png b/core/img/filetypes/application-sgf.png new file mode 100644 index 0000000000000000000000000000000000000000..f171f5579e735fe5cc729144e059263a0bc20741 GIT binary patch literal 725 zcmV;`0xJE9P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyY| z5)UXgb;nEq00LD>L_t(2&y`Y3NK|1M{r<;&%|*p1(?-!0dSaM?nHfsC5sVfsOl)03 zZCV9E(8fiyx1e2%+GJ1=Cgn~ggT$~@vMH)@Y@8Wq#&O1b@BjE(jH$Kj>^?Y$^Ks50 zhzS1!zVjF!x;#C<8u^)9imwsS6}zkhwWV!Ml|5G;Z*fS`;p5og$k_YwnN%X}NvQ)N zf*|mHPsyOJDs(97r9Q_~884OHpv;m|Nh(1jAd>CmP-~4E0pR;mb|tpRmlNyxf^S)bF~(6N z>YYJrt(8(rD@4rYgIGMrtM<5zDBt%10EAHbUx5%pDW$b8FSTp-mGbfu)3xbJ+EYp) z=f2Vb0r=Ctuapo%N(lgU;j(bUDT0WX&Nq)w|00xe#;SqXh)O9bm6Rgpd9lEgQiW{e zTK|c=4<|VQoNWnzT1+e_3WUJ6)t{ZY) zHxvrlwqqCuW0VlW7;QXQF?6T9$lM%X-^DJ|pu?jJU!xfzl-3#%DJ3PYdHhIaU(eC2 zw_pA>z9L>c=$)L7&P9?xVp-edwc)a+!+YDBt2_E1ZE=1B;p|h!+58Bn00000NkvXX Hu0mjfh3`Xt literal 0 HcmV?d00001 diff --git a/lib/mimetypes.fixlist.php b/lib/mimetypes.fixlist.php index 51f12dbcc2..a40fbd9e22 100644 --- a/lib/mimetypes.fixlist.php +++ b/lib/mimetypes.fixlist.php @@ -16,5 +16,6 @@ return array( 'xls'=>'application/msexcel', 'xlsx'=>'application/msexcel', 'ppt'=>'application/mspowerpoint', - 'pptx'=>'application/mspowerpoint' + 'pptx'=>'application/mspowerpoint', + 'sgf' => 'application/sgf' ); From b201e5152840406f0b5de9a403fd8f6ceedd3636 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 27 Mar 2012 20:43:44 +0000 Subject: [PATCH 049/133] Stop error on export --- lib/migration/provider.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/migration/provider.php b/lib/migration/provider.php index d592ed6726..98804ee91c 100644 --- a/lib/migration/provider.php +++ b/lib/migration/provider.php @@ -32,13 +32,14 @@ abstract class OC_Migration_Provider{ * @breif sets the OC_Migration_Content object to $this->content * @param $content a OC_Migration_Content object */ - public function setData( $uid, $content, $info=false ){ + public function setData( $uid, $content, $info=null ){ $this->content = $content; $this->uid = $uid; - $this->olduid = $info->exporteduser; + if( !is_null( $info ) ){ + $this->olduid = $info->exporteduser; + $this->appinfo = $info->apps->$id; + } $id = $this->id; - $this->appinfo = $info->apps->$id; - } /** From 553f4533c081e38e65ffb9981063d944fc58f431 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 27 Mar 2012 20:45:37 +0000 Subject: [PATCH 050/133] look for migrate.php in appinfo folder --- apps/bookmarks/{lib => appinfo}/migrate.php | 0 lib/migrate.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename apps/bookmarks/{lib => appinfo}/migrate.php (100%) diff --git a/apps/bookmarks/lib/migrate.php b/apps/bookmarks/appinfo/migrate.php similarity index 100% rename from apps/bookmarks/lib/migrate.php rename to apps/bookmarks/appinfo/migrate.php diff --git a/lib/migrate.php b/lib/migrate.php index 718dc57b9f..6b1497d1d0 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -64,7 +64,7 @@ class OC_Migrate{ $apps = OC_App::getAllApps(); foreach($apps as $app){ - $path = OC::$SERVERROOT . '/apps/' . $app . '/lib/migrate.php'; + $path = OC::$SERVERROOT . '/apps/' . $app . '/appinfo/migrate.php'; if( file_exists( $path ) ){ include( $path ); } From 31d268fe929abefbbf14e76a96c02f18235451a8 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 27 Mar 2012 20:55:53 +0000 Subject: [PATCH 051/133] check for sqlite --- lib/migrate.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/migrate.php b/lib/migrate.php index 6b1497d1d0..d7d1400e2f 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -482,6 +482,12 @@ class OC_Migrate{ $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); + // DB type + if( !is_callable( 'sqlite_open' ) || !class_exists( 'SQLite3' ) ){ + OC_Log::write( 'migration', 'SQLite not found', OC_Log::ERROR ); + return false; + } + // Prepare options array $options = array( 'portability' => MDB2_PORTABILITY_ALL & (!MDB2_PORTABILITY_FIX_CASE), From ef33219e4f83652676c7a668b6126c3c1c0da34d Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 27 Mar 2012 21:21:14 +0000 Subject: [PATCH 052/133] import method returns each apps' import status --- apps/user_migrate/admin.php | 16 ++++++++++++++- lib/migrate.php | 40 ++++++++++++++++++++++++------------- lib/migration/provider.php | 2 +- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index c1afb0aed4..1c4894c0f2 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -40,8 +40,22 @@ if (isset($_POST['user_import'])) { exit(); } - if( !OC_Migrate::import( $to, 'user' ) ){ + if( !$appsstatus = OC_Migrate::import( $to, 'user' ) ){ die( 'failed to to import' ); + } else { + // Check import status + foreach( $appsstatus as $app => $status ){ + if( $status != 'true' ){ + // It failed for some reason + if( $status == 'notsupported' ){ + $notsupported[] = $app; + } else if( !$status ){ + $failed[] = $app; + } + } + } + die(print_r($notsupported)); + die( 'Some apps failed to import, or were not supported.' ); } diff --git a/lib/migrate.php b/lib/migrate.php index d7d1400e2f..b16b84b2a4 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -246,14 +246,14 @@ class OC_Migrate{ return false; } // Import user app data - if( !self::importAppData( $extractpath . $json->exporteduser . '/migration.db', $json, self::$uid ) ){ + if( !$appsimported = self::importAppData( $extractpath . $json->exporteduser . '/migration.db', $json, self::$uid ) ){ return false; } // All done! if( !self::unlink_r( $extractpath ) ){ OC_Log::write( 'migration', 'Failed to delete the extracted zip', OC_Log::ERROR ); } - return true; + return $appsimported; break; case 'instance': // Check for new data dir and dbexport before doing anything @@ -611,9 +611,9 @@ class OC_Migrate{ /** * @breif imports a new user * @param $db string path to migration.db - * @param $info array of migration ino + * @param $info object of migration info * @param $uid optional uid to use - * @return bool if the import succedded + * @return array of apps with import statuses, or false on failure. */ public static function importAppData( $db, $info, $uid=null ){ // Check if the db exists @@ -641,21 +641,33 @@ class OC_Migrate{ // Is the app in the export? $id = $provider->getID(); if( isset( $info->apps->$id ) ){ - // Did it succeed? - if( $info->apps->$id->success ){ - // Give the provider the content object - if( !self::connectDB( $db ) ){ - return false; + // Is the app installed + if( !OC_App::isEnabled( $id ) ){ + OC_Log::write( 'migration', 'App: ' . $id . ' is not installed, can\'t import data.', OC_Log::INFO ); + $appsstatus[$id] = 'notsupported'; + } else { + // Did it succeed on export? + if( $info->apps->$id->success ){ + // Give the provider the content object + if( !self::connectDB( $db ) ){ + return false; + } + $content = new OC_Migration_Content( self::$zip, self::$MDB2 ); + $provider->setData( self::$uid, $content, $info ); + // Then do the import + if( !$appsstatus[$id] = $provider->import( $info->apps->$id, $importinfo ) ){ + // Failed to import app + OC_Log::write( 'migration', 'Failed to import app data for user: ' . self::$uid . ' for app: ' . $id, OC_Log::ERROR ); + } + } else { + // Add to failed list + $appsstatus[$id] = false; } - $content = new OC_Migration_Content( self::$zip, self::$MDB2 ); - $provider->setData( self::$uid, $content, $info ); - // Then do the import - $provider->import( $info->apps->$id, $importinfo ); } } } - return true; + return $appsstatus; } diff --git a/lib/migration/provider.php b/lib/migration/provider.php index 98804ee91c..feae29f135 100644 --- a/lib/migration/provider.php +++ b/lib/migration/provider.php @@ -35,11 +35,11 @@ abstract class OC_Migration_Provider{ public function setData( $uid, $content, $info=null ){ $this->content = $content; $this->uid = $uid; + $id = $this->id; if( !is_null( $info ) ){ $this->olduid = $info->exporteduser; $this->appinfo = $info->apps->$id; } - $id = $this->id; } /** From bd4fd76bfb94a7b4af0f85838ac9e539e730fa5d Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 27 Mar 2012 21:35:29 +0000 Subject: [PATCH 053/133] Fix bookmarks migration provider --- apps/bookmarks/appinfo/migrate.php | 4 ++-- apps/user_migrate/admin.php | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/bookmarks/appinfo/migrate.php b/apps/bookmarks/appinfo/migrate.php index 02c96e5963..603a8b72d3 100644 --- a/apps/bookmarks/appinfo/migrate.php +++ b/apps/bookmarks/appinfo/migrate.php @@ -48,9 +48,9 @@ class OC_Migration_Provider_Bookmarks extends OC_Migration_Provider{ } // Now tags foreach($idmap as $oldid => $newid){ - $query = $this->content->prepare( "SELECT * FROM bookmarks_tags WHERE user_id LIKE ?" ); + $query = $this->content->prepare( "SELECT * FROM bookmarks_tags WHERE bookmark_id LIKE ?" ); $results = $query->execute( array( $oldid ) ); - while( $row = $data->fetchRow() ){ + while( $row = $results->fetchRow() ){ // Import the tags for this bookmark, using the new bookmark id $query = OC_DB::prepare( "INSERT INTO *PREFIX*bookmarks_tags(bookmark_id, tag) VALUES (?, ?)" ); $query->execute( array( $newid, $row['tag'] ) ); diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index 1c4894c0f2..61b83c2a14 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -54,11 +54,16 @@ if (isset($_POST['user_import'])) { } } } - die(print_r($notsupported)); - die( 'Some apps failed to import, or were not supported.' ); + // Any problems? + if( isset( $notsupported ) || isset( $failed ) ){ + if( count( $failed ) > 0 ){ + die( 'Some apps failed to import. View the log please.' ); + } else if( count( $notsupported ) > 0 ){ + die( 'Some apps were not found in this owncloud instance and therefore could not be installed' ); + } + } } - } else { // fill template $tmpl = new OC_Template('user_migrate', 'admin'); From bcda46eda3b3b9208609bc5a9497632aa0dabfa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Wed, 28 Mar 2012 16:07:50 +0200 Subject: [PATCH 054/133] webfinger installation creates symlink in document root --- apps/user_webfinger/appinfo/install.php | 32 ++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/user_webfinger/appinfo/install.php b/apps/user_webfinger/appinfo/install.php index f570a3a249..9ba953a4e6 100644 --- a/apps/user_webfinger/appinfo/install.php +++ b/apps/user_webfinger/appinfo/install.php @@ -3,4 +3,34 @@ $appInfoDir = __DIR__; $thisAppDir = dirname($appInfoDir); $appsDir = dirname($thisAppDir); $ownCloudDir = dirname($appsDir); -@symlink($thisAppDir, $ownCloudDir.'/.well-known'); +$docRoot = $_SERVER['DOCUMENT_ROOT']; +if(file_exists($docRoot . '/.well-known/host-meta')) { + OC_Log::write( + 'user_webfinger', + $docRoot . "/.well-known already exists; installation aborted", + OC_Log::ERROR + ); +} else { + if(@symlink($thisAppDir, $docRoot . '/.well-known')) { + OC_Log::write( + 'user_webfinger', + "Webfinger symlink created at " . $docRoot . "/.well-known", + OC_Log::INFO + ); + } else { + if(@symlink($thisAppDir, $ownCloudDir . '/.well-known')) { + OC_Log::write( + 'user_webfinger', + "Couldn't create webfinger symlink in document root, linked to " . $ownCloudDir . "/.well-known instead", + OC_Log::WARN + ); + } else { + OC_Log::write( + 'user_webfinger', + "Couldn't create webfinger symlink, either check write permissions or create the link manually!", + OC_Log::ERROR + ); + } + } +} +?> From 34a0128ddf01a44302f89a1dfa14bb9565a8ae47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Wed, 28 Mar 2012 16:12:34 +0200 Subject: [PATCH 055/133] whitespace indentation fix --- apps/user_webfinger/appinfo/install.php | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/user_webfinger/appinfo/install.php b/apps/user_webfinger/appinfo/install.php index 9ba953a4e6..775141dce4 100644 --- a/apps/user_webfinger/appinfo/install.php +++ b/apps/user_webfinger/appinfo/install.php @@ -5,32 +5,32 @@ $appsDir = dirname($thisAppDir); $ownCloudDir = dirname($appsDir); $docRoot = $_SERVER['DOCUMENT_ROOT']; if(file_exists($docRoot . '/.well-known/host-meta')) { - OC_Log::write( - 'user_webfinger', - $docRoot . "/.well-known already exists; installation aborted", - OC_Log::ERROR - ); + OC_Log::write( + 'user_webfinger', + $docRoot . "/.well-known already exists; installation aborted", + OC_Log::ERROR + ); } else { - if(@symlink($thisAppDir, $docRoot . '/.well-known')) { - OC_Log::write( - 'user_webfinger', - "Webfinger symlink created at " . $docRoot . "/.well-known", - OC_Log::INFO - ); - } else { - if(@symlink($thisAppDir, $ownCloudDir . '/.well-known')) { - OC_Log::write( - 'user_webfinger', - "Couldn't create webfinger symlink in document root, linked to " . $ownCloudDir . "/.well-known instead", - OC_Log::WARN - ); - } else { - OC_Log::write( - 'user_webfinger', - "Couldn't create webfinger symlink, either check write permissions or create the link manually!", - OC_Log::ERROR - ); - } - } + if(@symlink($thisAppDir, $docRoot . '/.well-known')) { + OC_Log::write( + 'user_webfinger', + "Webfinger symlink created at " . $docRoot . "/.well-known", + OC_Log::INFO + ); + } else { + if(@symlink($thisAppDir, $ownCloudDir . '/.well-known')) { + OC_Log::write( + 'user_webfinger', + "Couldn't create webfinger symlink in document root, linked to " . $ownCloudDir . "/.well-known instead", + OC_Log::WARN + ); + } else { + OC_Log::write( + 'user_webfinger', + "Couldn't create webfinger symlink, either check write permissions or create the link manually!", + OC_Log::ERROR + ); + } + } } ?> From 73eca66a892ade0894e35fb4b57d7b45a5e1c872 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Thu, 29 Mar 2012 10:17:08 +0000 Subject: [PATCH 056/133] Fix comments --- lib/migrate.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/migrate.php b/lib/migrate.php index b16b84b2a4..fe5ac45600 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -75,7 +75,8 @@ class OC_Migrate{ * @breif exports a user, or owncloud instance * @param ootional $type string type of export, defualts to user * @param otional $path string path to zip output folder - * @param optional $uid string user id of user to export if export type is user, defaults to current + * @param optional $uid string user id of user to export if export type is user, defaults to current + * @return false on error, path to zip on success */ public static function export( $type='user', $path=null, $uid=null ){ $datadir = OC_Config::getValue( 'datadirectory' ); From 65eee1f69dbbbe86bb4bf0716a464dc9f2c66a67 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 30 Mar 2012 19:44:38 +0200 Subject: [PATCH 057/133] clean pre-path_hash fscache entries --- db_structure.xml | 3 +-- files/ajax/scan.php | 1 + lib/filecache.php | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index 82d2a731d4..2df218d359 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -67,8 +67,7 @@ path_hash text - - + true 32 diff --git a/files/ajax/scan.php b/files/ajax/scan.php index 565275911b..db09b7d5c6 100644 --- a/files/ajax/scan.php +++ b/files/ajax/scan.php @@ -17,6 +17,7 @@ if($force or !OC_FileCache::inCache('')){ if(!$checkOnly){ OC_DB::beginTransaction(); OC_FileCache::scan('',$eventSource); + OC_FileCache::clean(); OC_DB::commit(); $eventSource->send('success',true); }else{ diff --git a/lib/filecache.php b/lib/filecache.php index a8c48e3f14..86d865ed9f 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -637,6 +637,14 @@ class OC_FileCache{ self::fileSystemWatcherWrite(array('path'=>$path),$root); } } + + /** + * clean old pre-path_hash entries + */ + public static function clean(){ + $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE LENGTH(path_hash)<30'); + $query->execute(); + } } //watch for changes and try to keep the cache up to date From 6abb2cb92edc1edaefea7e37cd46a6c866239c27 Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Fri, 30 Mar 2012 21:14:01 +0200 Subject: [PATCH 058/133] fix sharing nested galleries --- apps/gallery/js/album_cover.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js index 061bbcd0b4..cd26001964 100644 --- a/apps/gallery/js/album_cover.js +++ b/apps/gallery/js/album_cover.js @@ -43,8 +43,9 @@ function shareGallery() { {text: 'Shared gallery address', name: 'address', type: 'text', value: existing_token}]; OC.dialogs.form(form_fields, t('gallery', 'Share gallery'), function(values){ var p = ''; - for (var i in paths) p += '/'+paths[i]; + for (var i in paths) p += paths[i]+'/'; if (p == '') p = '/'; + alert(p); $.getJSON(OC.filePath('gallery', 'ajax', 'galleryOp.php'), {operation: 'share', path: p, share: values[0].value, recursive: values[1].value}, function(r) { if (r.status == 'success') { Albums.shared = r.sharing; From 5161758921efb5bb50e579d8e6debfa93e595a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Fri, 30 Mar 2012 21:35:09 +0200 Subject: [PATCH 059/133] create static host-meta instead of symlink and .htaccess --- apps/user_webfinger/appinfo/install.php | 57 ++++++++++++------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/apps/user_webfinger/appinfo/install.php b/apps/user_webfinger/appinfo/install.php index 775141dce4..678d57ae8f 100644 --- a/apps/user_webfinger/appinfo/install.php +++ b/apps/user_webfinger/appinfo/install.php @@ -4,33 +4,32 @@ $thisAppDir = dirname($appInfoDir); $appsDir = dirname($thisAppDir); $ownCloudDir = dirname($appsDir); $docRoot = $_SERVER['DOCUMENT_ROOT']; -if(file_exists($docRoot . '/.well-known/host-meta')) { - OC_Log::write( - 'user_webfinger', - $docRoot . "/.well-known already exists; installation aborted", - OC_Log::ERROR - ); -} else { - if(@symlink($thisAppDir, $docRoot . '/.well-known')) { - OC_Log::write( - 'user_webfinger', - "Webfinger symlink created at " . $docRoot . "/.well-known", - OC_Log::INFO - ); - } else { - if(@symlink($thisAppDir, $ownCloudDir . '/.well-known')) { - OC_Log::write( - 'user_webfinger', - "Couldn't create webfinger symlink in document root, linked to " . $ownCloudDir . "/.well-known instead", - OC_Log::WARN - ); - } else { - OC_Log::write( - 'user_webfinger', - "Couldn't create webfinger symlink, either check write permissions or create the link manually!", - OC_Log::ERROR - ); - } - } +try { + $webRoot = substr(realpath($ownCloudDir), strlen(realpath($docRoot))); +} catch(Exception $e) { + // some servers fail on realpath(), let's try it the unsecure way: + $webRoot = substr($ownCloudDir, strlen($docRoot)); } -?> +$serverName = $_SERVER['SERVER_NAME']; +$lrddTmpl = 'http'; +if(isset($_SERVER['HTTPS'])) { + $lrddTmpl .= 's'; +} +$lrddTmpl .= '://' . $serverName . $webRoot . '/apps/user_webfinger/webfinger.php?q={uri}'; +$hostMetaPath = $docRoot . '/.well-known/host-meta'; +$hostMetaContents = " + + " . $serverName . " + + Resource Descriptor + +"; +@mkdir(dirname($hostMetaPath)); +$hostMeta = fopen($hostMetaPath, 'w'); +if(!$hostMeta) { + die("Could not open " . $hostMetaPath . " for writing, please check permissions!"); +} +if(!fwrite($hostMeta, $hostMetaContents, strlen($hostMetaContents))) { + die("Could not write to " . $hostMetaPath . ", please check permissions!"); +} +fclose($hostMeta); From 6b704a780dbe3daa4c13ad49ad4265c1db4a67aa Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Fri, 30 Mar 2012 22:50:57 +0200 Subject: [PATCH 060/133] select field added to oc.dialogs.form, gallery ported to use it --- apps/gallery/js/album_cover.js | 46 +++++++++++--------------------- apps/gallery/templates/index.php | 39 +-------------------------- core/js/oc-dialogs.js | 14 +++++++--- 3 files changed, 28 insertions(+), 71 deletions(-) diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js index cd26001964..d44e7f83d1 100644 --- a/apps/gallery/js/album_cover.js +++ b/apps/gallery/js/album_cover.js @@ -113,42 +113,28 @@ function scanForAlbums(cleanup) { } function settings() { - $( '#g-dialog-settings' ).dialog({ - height: 180, - width: 350, - modal: false, - buttons: [ - { - text: t('gallery', 'Apply'), - click: function() { - var scanning_root = $('#g-scanning-root').val(); - var disp_order = $('#g-display-order option:selected').val(); + OC.dialogs.form([{text: t('gallery', 'Scanning root'), name: 'root', type:'text', value:gallery_scanning_root}, + {text: t('gallery', 'Default order'), name: 'order', type:'select', value:gallery_default_order, options:[ + {text:t('gallery', 'Ascending'), value:'ASC'}, {text: t('gallery', 'Descending'), value:'DESC'} ]}], + t('gallery', 'Settings'), + function(values) { + var scanning_root = values[0].value; + var disp_order = values[1].value; if (scanning_root == '') { - alert('Scanning root cannot be empty'); + OC.dialogs.alert(t('gallery', 'Scanning root cannot be empty'), t('gallery', 'Error')); return; } $.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) { if (r.status == 'success') { - if (r.rescan == 'yes') { - $('#g-dialog-settings').dialog('close'); - Albums.clear(document.getElementById('gallery_list')); - scanForAlbums(true); - return; - } + if (r.rescan == 'yes') { + Albums.clear(document.getElementById('gallery_list')); + scanForAlbums(true); + } + gallery_scanning_root = scanning_root; } else { - alert('Error: ' + r.cause); - return; + OC.dialogs.alert(t('gallery', 'Error: ') + r.cause, t('gallery', 'Error')); + return; } - $('#g-dialog-settings').dialog('close'); }); - } - }, - { - text: t('gallery', 'Cancel'), - click: function() { - $(this).dialog('close'); - } - } - ], - }); + }); } diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php index c6373d3b0a..9bec5db1b9 100644 --- a/apps/gallery/templates/index.php +++ b/apps/gallery/templates/index.php @@ -9,7 +9,7 @@ OC_Util::addScript('files_imageviewer', 'jquery.fancybox-1.3.4.pack'); OC_Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' ); $l = new OC_L10N('gallery'); ?> - +
@@ -29,40 +29,3 @@ $l = new OC_L10N('gallery');
- - - - - - - diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index c11ac13332..35d0a0c5c4 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -84,10 +84,18 @@ OCdialogs = { } else content += '>'; } else if (type == 'text' || type == 'password' && fields[a].value) content += ' value="'+fields[a].value+'">'; + } else if (type == 'select') { + content += ''; } - content += "" + content += ''; } - content += "
"; + content += ''; OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback); }, message:function(content, title, dialog_type, buttons, callback) { @@ -144,7 +152,7 @@ OCdialogs = { if (callback != undefined) { var r = []; var c = 0; - $(c_id + ' input').each(function(i, elem) { + $(c_id + ' input, '+c_id+' select').each(function(i, elem) { r[c] = {name: $(elem).attr('name'), value: OCdialogs.determineValue(elem)}; c++; }); From 3a4521a012fe75d8ec3685e1eb87374e5abd9da2 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 30 Mar 2012 23:15:48 +0200 Subject: [PATCH 061/133] Add support for logging to syslog --- lib/log.php | 71 +++++++++--------------------------- lib/log/owncloud.php | 78 ++++++++++++++++++++++++++++++++++++++++ lib/log/syslog.php | 37 +++++++++++++++++++ settings/ajax/getlog.php | 2 +- settings/log.php | 2 +- 5 files changed, 133 insertions(+), 57 deletions(-) create mode 100644 lib/log/owncloud.php create mode 100644 lib/log/syslog.php diff --git a/lib/log.php b/lib/log.php index 4e450a027f..8bb2839be6 100644 --- a/lib/log.php +++ b/lib/log.php @@ -1,78 +1,39 @@ . - * + * Copyright (c) 2012 Bart Visscher + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. */ /** - *logging utilities + * logging utilities * - * Log is saved at data/owncloud.log (on default) + * Log is saved by default at data/owncloud.log using OC_Log_Owncloud. + * Selecting other backend is done with a config option 'log_type'. */ -class OC_Log{ +class OC_Log { const DEBUG=0; const INFO=1; const WARN=2; const ERROR=3; const FATAL=4; + static protected $class = null; + /** * write a message in the log * @param string $app * @param string $message * @param int level */ - public static function write($app,$message,$level){ - $minLevel=OC_Config::getValue( "loglevel", 2 ); - if($level>=$minLevel){ - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); - $logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); - $entry=array('app'=>$app,'message'=>$message,'level'=>$level,'time'=>time()); - $fh=fopen($logFile,'a'); - fwrite($fh,json_encode($entry)."\n"); - fclose($fh); + public static function write($app, $message, $level) { + if (!self::$class) { + self::$class = 'OC_Log_'.ucfirst(OC_Config::getValue('log_type', 'owncloud')); + call_user_func(array(self::$class, 'init')); } - } - - /** - * get entries from the log in reverse chronological order - * @param int limit - * @param int offset - * @return array - */ - public static function getEntries($limit=50,$offset=0){ - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); - $logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); - $entries=array(); - if(!file_exists($logFile)){ - return array(); - } - $contents=file($logFile); - if(!$contents){//error while reading log - return array(); - } - $end=max(count($contents)-$offset-1,0); - $start=max($end-$limit,0); - for($i=$end;$i>$start;$i--){ - $entries[]=json_decode($contents[$i]); - } - return $entries; + $log_class=self::$class; + $log_class::write($app, $message, $level); } } diff --git a/lib/log/owncloud.php b/lib/log/owncloud.php new file mode 100644 index 0000000000..6df346e9b1 --- /dev/null +++ b/lib/log/owncloud.php @@ -0,0 +1,78 @@ +. + * + */ + +/** + * logging utilities + * + * Log is saved at data/owncloud.log (on default) + */ + +class OC_Log_Owncloud { + /** + * Init class data + */ + public static function init() { + } + + /** + * write a message in the log + * @param string $app + * @param string $message + * @param int level + */ + public static function write($app, $message, $level) { + $minLevel=OC_Config::getValue( "loglevel", 2 ); + if($level>=$minLevel){ + $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); + $logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); + $entry=array('app'=>$app, 'message'=>$message, 'level'=>$level,'time'=>time()); + $fh=fopen($logFile, 'a'); + fwrite($fh, json_encode($entry)."\n"); + fclose($fh); + } + } + + /** + * get entries from the log in reverse chronological order + * @param int limit + * @param int offset + * @return array + */ + public static function getEntries($limit=50, $offset=0){ + $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); + $logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); + $entries=array(); + if(!file_exists($logFile)) { + return array(); + } + $contents=file($logFile); + if(!$contents) {//error while reading log + return array(); + } + $end=max(count($contents)-$offset-1, 0); + $start=max($end-$limit,0); + for($i=$end;$i>$start;$i--) { + $entries[]=json_decode($contents[$i]); + } + return $entries; + } +} diff --git a/lib/log/syslog.php b/lib/log/syslog.php new file mode 100644 index 0000000000..d1fb28d8b0 --- /dev/null +++ b/lib/log/syslog.php @@ -0,0 +1,37 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Log_Syslog { + static protected $levels = array( + OC_Log::DEBUG => LOG_DEBUG, + OC_Log::INFO => LOG_INFO, + OC_Log::WARN => LOG_WARNING, + OC_Log::ERROR => LOG_ERR, + OC_Log::FATAL => LOG_CRIT, + ); + + /** + * Init class data + */ + public static function init() { + openlog('ownCloud', LOG_PID | LOG_CONS, LOG_USER); + // Close at shutdown + register_shutdown_function('closelog'); + } + + /** + * write a message in the log + * @param string $app + * @param string $message + * @param int level + */ + public static function write($app, $message, $level) { + $syslog_level = self::$levels[$level]; + syslog($syslog_level, '{'.$app.'} '.$message); + } +} diff --git a/settings/ajax/getlog.php b/settings/ajax/getlog.php index 600ebefcec..ed48b2cae1 100644 --- a/settings/ajax/getlog.php +++ b/settings/ajax/getlog.php @@ -13,5 +13,5 @@ OC_JSON::checkAdminUser(); $count=(isset($_GET['count']))?$_GET['count']:50; $offset=(isset($_GET['offset']))?$_GET['offset']:0; -$entries=OC_Log::getEntries($count,$offset); +$entries=OC_Log_Owncloud::getEntries($count,$offset); OC_JSON::success(array("data" => $entries)); diff --git a/settings/log.php b/settings/log.php index 946f2b6f8e..ddbf72c443 100644 --- a/settings/log.php +++ b/settings/log.php @@ -28,7 +28,7 @@ OC_Util::addStyle( "settings", "settings" ); OC_Util::addScript( "settings", "apps" ); OC_App::setActiveNavigationEntry( "core_log" ); -$entries=OC_Log::getEntries(); +$entries=OC_Log_Owncloud::getEntries(); OC_Util::addScript('settings','log'); OC_Util::addStyle('settings','settings'); From a7438189f315288c5e57bbf3bfb59a37c896cd6c Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 30 Mar 2012 23:31:05 +0200 Subject: [PATCH 062/133] Move more from base init to separate functions --- lib/base.php | 64 ++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/lib/base.php b/lib/base.php index b031572f17..e9788f54b6 100644 --- a/lib/base.php +++ b/lib/base.php @@ -229,6 +229,39 @@ class OC{ } } + public static function initTemplateEngine() { + // if the formfactor is not yet autodetected do the autodetection now. For possible forfactors check the detectFormfactor documentation + if(!isset($_SESSION['formfactor'])){ + $_SESSION['formfactor']=OC::detectFormfactor(); + } + // allow manual override via GET parameter + if(isset($_GET['formfactor'])){ + $_SESSION['formfactor']=$_GET['formfactor']; + } + + // Add the stuff we need always + OC_Util::addScript( "jquery-1.6.4.min" ); + OC_Util::addScript( "jquery-ui-1.8.16.custom.min" ); + OC_Util::addScript( "jquery-showpassword" ); + OC_Util::addScript( "jquery.infieldlabel.min" ); + OC_Util::addScript( "jquery-tipsy" ); + OC_Util::addScript( "oc-dialogs" ); + OC_Util::addScript( "js" ); + OC_Util::addScript( "eventsource" ); + OC_Util::addScript( "config" ); + //OC_Util::addScript( "multiselect" ); + OC_Util::addScript('search','result'); + OC_Util::addStyle( "styles" ); + OC_Util::addStyle( "multiselect" ); + OC_Util::addStyle( "jquery-ui-1.8.16.custom" ); + OC_Util::addStyle( "jquery-tipsy" ); + } + + public static function initSession() { + ini_set('session.cookie_httponly','1;'); + session_start(); + } + public static function init(){ // register autoloader spl_autoload_register(array('OC','autoload')); @@ -272,35 +305,8 @@ class OC{ self::checkSSL(); self::checkUpgrade(); - ini_set('session.cookie_httponly','1;'); - session_start(); - - // if the formfactor is not yet autodetected do the autodetection now. For possible forfactors check the detectFormfactor documentation - if(!isset($_SESSION['formfactor'])){ - $_SESSION['formfactor']=OC::detectFormfactor(); - } - // allow manual override via GET parameter - if(isset($_GET['formfactor'])){ - $_SESSION['formfactor']=$_GET['formfactor']; - } - - - // Add the stuff we need always - OC_Util::addScript( "jquery-1.6.4.min" ); - OC_Util::addScript( "jquery-ui-1.8.16.custom.min" ); - OC_Util::addScript( "jquery-showpassword" ); - OC_Util::addScript( "jquery.infieldlabel.min" ); - OC_Util::addScript( "jquery-tipsy" ); - OC_Util::addScript( "oc-dialogs" ); - OC_Util::addScript( "js" ); - OC_Util::addScript( "eventsource" ); - OC_Util::addScript( "config" ); - //OC_Util::addScript( "multiselect" ); - OC_Util::addScript('search','result'); - OC_Util::addStyle( "styles" ); - OC_Util::addStyle( "multiselect" ); - OC_Util::addStyle( "jquery-ui-1.8.16.custom" ); - OC_Util::addStyle( "jquery-tipsy" ); + self::initSession(); + self::initTemplateEngine(); $errors=OC_Util::checkServer(); if(count($errors)>0) { From 3300d6ea532a973e987c7aeeef1af63a60487c58 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 30 Mar 2012 23:33:36 +0200 Subject: [PATCH 063/133] checkUpgrade has to be after template initialization The error path of checkUpgrade uses the template --- lib/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base.php b/lib/base.php index e9788f54b6..22f7f4ea48 100644 --- a/lib/base.php +++ b/lib/base.php @@ -303,10 +303,10 @@ class OC{ self::checkInstalled(); self::checkSSL(); - self::checkUpgrade(); self::initSession(); self::initTemplateEngine(); + self::checkUpgrade(); $errors=OC_Util::checkServer(); if(count($errors)>0) { From 20fc23c82bbcaff56caafe6a6cc0ef15db9b2bf8 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 30 Mar 2012 23:40:16 +0200 Subject: [PATCH 064/133] Move logfile determination to init function --- lib/log/owncloud.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/log/owncloud.php b/lib/log/owncloud.php index 6df346e9b1..5e14320556 100644 --- a/lib/log/owncloud.php +++ b/lib/log/owncloud.php @@ -27,10 +27,14 @@ */ class OC_Log_Owncloud { + static protected $logFile; + /** * Init class data */ public static function init() { + $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); + self::$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); } /** @@ -42,10 +46,8 @@ class OC_Log_Owncloud { public static function write($app, $message, $level) { $minLevel=OC_Config::getValue( "loglevel", 2 ); if($level>=$minLevel){ - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); - $logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); $entry=array('app'=>$app, 'message'=>$message, 'level'=>$level,'time'=>time()); - $fh=fopen($logFile, 'a'); + $fh=fopen(self::$logFile, 'a'); fwrite($fh, json_encode($entry)."\n"); fclose($fh); } @@ -58,10 +60,9 @@ class OC_Log_Owncloud { * @return array */ public static function getEntries($limit=50, $offset=0){ - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); - $logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); + self::init(); $entries=array(); - if(!file_exists($logFile)) { + if(!file_exists(self::$logFile)) { return array(); } $contents=file($logFile); From 71b70bb05f2c3a90a51d70c78c68332111400cf5 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 30 Mar 2012 23:40:29 +0200 Subject: [PATCH 065/133] Fix HTML, misspelled span close tag --- files/templates/index.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/templates/index.php b/files/templates/index.php index 418a170fec..f591d066d8 100644 --- a/files/templates/index.php +++ b/files/templates/index.php @@ -63,12 +63,12 @@

- t('Files are being scanned, please wait.');?> + t('Files are being scanned, please wait.');?>

- t('Current scanning');?> + t('Current scanning');?>

- \ No newline at end of file + From f9f91a08b47c0a086c43966b1ecdf87185ceef83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Sat, 31 Mar 2012 02:42:41 +0200 Subject: [PATCH 066/133] webfinger reimplementation started --- apps/remoteStorage/appinfo/info.xml | 2 +- apps/remoteStorage/appinfo/webfinger.php | 6 +++ apps/user_webfinger/.htaccess | 3 -- apps/user_webfinger/appinfo/info.xml | 6 +-- apps/user_webfinger/host-meta | 1 - apps/user_webfinger/host-meta.php | 16 ------- apps/user_webfinger/webfinger.php | 57 +++++++++++++++++------- 7 files changed, 52 insertions(+), 39 deletions(-) create mode 100644 apps/remoteStorage/appinfo/webfinger.php delete mode 100644 apps/user_webfinger/.htaccess delete mode 100644 apps/user_webfinger/host-meta delete mode 100644 apps/user_webfinger/host-meta.php diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml index 121587795d..1ab55e8c09 100644 --- a/apps/remoteStorage/appinfo/info.xml +++ b/apps/remoteStorage/appinfo/info.xml @@ -3,7 +3,7 @@ remoteStorage remoteStorage compatibility Enables your users to use ownCloud as their remote storage for unhosted applications. - 0.5 + 0.6 AGPL or MIT Michiel de Jong 2 diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php new file mode 100644 index 0000000000..bb3fe1681b --- /dev/null +++ b/apps/remoteStorage/appinfo/webfinger.php @@ -0,0 +1,6 @@ + + diff --git a/apps/user_webfinger/.htaccess b/apps/user_webfinger/.htaccess deleted file mode 100644 index 4d4d2e9c58..0000000000 --- a/apps/user_webfinger/.htaccess +++ /dev/null @@ -1,3 +0,0 @@ -RewriteEngine On -RewriteBase / -RewriteRule host-meta$ \/\.well-known\/host-meta\.php [L] diff --git a/apps/user_webfinger/appinfo/info.xml b/apps/user_webfinger/appinfo/info.xml index 55cf2cf220..d47fb723a3 100644 --- a/apps/user_webfinger/appinfo/info.xml +++ b/apps/user_webfinger/appinfo/info.xml @@ -2,9 +2,9 @@ user_webfinger Webfinger - Provide WebFinger for all users so they get a user address like user@owncloudinstance which can be used for unhosted applications. If you don't run ownCloud in the root of your domain, for instance if you run it on example.com/owncloud/, then make sure you link example.com/.well-known/ to example.com/owncloud/apps/user_webfinger/ - by running something like "ln -s /var/www/owncloud/apps/user_webfinger /var/www/.well-known". Only enable this app if you run this ownCloud installation on a public web address, not if you run it on an intranet or on localhost. - 0.2 + Provide WebFinger for all users so they get a user address like user@owncloudinstance which can be used for external applications. Other apps can provide information for webfinger requests, such as remoteStorage compatibility. + 0.3 AGPL or MIT - Michiel de Jong + Michiel de Jong, Florian Hülsmann 2 diff --git a/apps/user_webfinger/host-meta b/apps/user_webfinger/host-meta deleted file mode 100644 index dfaf363614..0000000000 --- a/apps/user_webfinger/host-meta +++ /dev/null @@ -1 +0,0 @@ -please run 'a2enmod rewrite' on your server, set 'AllowOverride All' for /var/www in /etc/apache2/sites-enabled/000-default or equivalent, and then run '/etc/init.d/apache2 restart' diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php deleted file mode 100644 index ac577cf9a0..0000000000 --- a/apps/user_webfinger/host-meta.php +++ /dev/null @@ -1,16 +0,0 @@ - -?xml version="1.0" encoding="UTF-8"?> - - - - - - diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index 5c2a24aa07..ecbfeed8e4 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -1,41 +1,68 @@ /apps/myApp/profile.php?user="> + * + * + '* but can also use complex database queries to generate the webfinger result + **/ // calculate the documentroot // modified version of the one in lib/base.php that takes the .well-known symlink into account -$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']); +/*$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']); $SERVERROOT=str_replace("\\",'/',dirname(dirname(dirname(dirname(__FILE__))))); $SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT)); $WEBROOT=substr($SUBURI,0,-34); +*/ +require_once('../../lib/base.php'); +$id = $_GET['q']; if($_GET['q']) { $bits = explode('@', $_GET['q']); $userName = $bits[0]; } else { + $id = ''; $userName = ''; } if(substr($userName, 0, 5) == 'acct:') { $userName = substr($userName, 5); } if(isset($_SERVER['HTTPS'])) { - $baseAddress = 'https://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; + $baseAddress = 'https://'; } else { - $baseAddress = 'http://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; + $baseAddress = 'http://'; } +$baseAddress .= $_SERVER['SERVER_NAME'].OC::$WEBROOT; +define('WF_USER', $userName); +define('WF_ADDRESS', $id); +define('WF_ROOT', $baseAddress); echo "<"; ?> ?xml version="1.0" encoding="UTF-8"?> - - + + acct: + From ad495a92180da7e5dc369f7d8606a525dfe8a4e5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 31 Mar 2012 16:10:29 +0200 Subject: [PATCH 067/133] fix potential problem when using multiply eventsource's --- core/js/eventsource.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/js/eventsource.js b/core/js/eventsource.js index dece1a69d0..34bce60c24 100644 --- a/core/js/eventsource.js +++ b/core/js/eventsource.js @@ -33,8 +33,12 @@ */ OC.EventSource=function(src,data){ var dataStr=''; - for(name in data){ - dataStr+=name+'='+encodeURIComponent(data[name])+'&'; + this.typelessListeners=[]; + this.listeners={}; + if(data){ + for(name in data){ + dataStr+=name+'='+encodeURIComponent(data[name])+'&'; + } } if(!this.useFallBack && typeof EventSource !='undefined'){ this.source=new EventSource(src+'?'+dataStr); @@ -42,7 +46,7 @@ OC.EventSource=function(src,data){ for(var i=0;i Date: Sat, 31 Mar 2012 16:20:32 +0200 Subject: [PATCH 068/133] make sure output buffering is dissabled when using eventsource --- lib/eventsource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/eventsource.php b/lib/eventsource.php index 523f72403c..dc28616c2d 100644 --- a/lib/eventsource.php +++ b/lib/eventsource.php @@ -32,6 +32,7 @@ class OC_EventSource{ private $fallBackId=0; public function __construct(){ + @ob_end_clean(); header('Cache-Control: no-cache'); $this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true'; if($this->fallback){ From 9d2f8aa717826c1db8f8ec5f49a4128af3651edb Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 31 Mar 2012 16:24:53 +0200 Subject: [PATCH 069/133] send more progress updates when scanning large folders --- lib/filecache.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/filecache.php b/lib/filecache.php index 86d865ed9f..59b0fb1b50 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -469,6 +469,10 @@ class OC_FileCache{ * @param string root (optionak) */ public static function scan($path,$eventSource=false,&$count=0,$root=''){ + if($eventSource){ + $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); + } + $lastSend=$count; if(!$root){ $view=OC_Filesystem::getView(); }else{ @@ -482,13 +486,15 @@ class OC_FileCache{ if($filename != '.' and $filename != '..'){ $file=$path.'/'.$filename; if($view->is_dir($file.'/')){ - if($eventSource){ - $eventSource->send('scanning',array('file'=>$file,'count'=>$count)); - } self::scan($file,$eventSource,$count,$root); }else{ $totalSize+=self::scanFile($file,$root); $count++; + if($count>$lastSend+25){ + if($eventSource){ + $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); + } + } } } } From ebc7a6a0a6249213122bf97104f850d2ce6b7c1a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 31 Mar 2012 16:28:22 +0200 Subject: [PATCH 070/133] dont send to much when scanning large folders --- lib/filecache.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/filecache.php b/lib/filecache.php index 59b0fb1b50..4a4183cbdb 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -490,10 +490,9 @@ class OC_FileCache{ }else{ $totalSize+=self::scanFile($file,$root); $count++; - if($count>$lastSend+25){ - if($eventSource){ - $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); - } + if($count>$lastSend+25 and $eventSource){ + $lastSend=$count; + $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); } } } From 2f68b084919437013cc6d977c0f077e541cf83f9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 31 Mar 2012 16:40:42 +0200 Subject: [PATCH 071/133] fix eventsource for ie --- core/js/eventsource.js | 2 +- lib/eventsource.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/js/eventsource.js b/core/js/eventsource.js index 34bce60c24..08259e02ca 100644 --- a/core/js/eventsource.js +++ b/core/js/eventsource.js @@ -68,7 +68,7 @@ OC.EventSource=function(src,data){ OC.EventSource.fallBackSources=[]; OC.EventSource.iframeCount=0;//number of fallback iframes OC.EventSource.fallBackCallBack=function(id,type,data){ - OC.EventSource.fallBackSources[id].fallBackCallBack(type,JSON.parse(data)); + OC.EventSource.fallBackSources[id].fallBackCallBack(type,data); } OC.EventSource.prototype={ typelessListeners:[], diff --git a/lib/eventsource.php b/lib/eventsource.php index dc28616c2d..cf10660b94 100644 --- a/lib/eventsource.php +++ b/lib/eventsource.php @@ -59,7 +59,7 @@ class OC_EventSource{ $type=null; } if($this->fallback){ - $response=''.PHP_EOL; + $response=''.PHP_EOL; echo $response; }else{ if($type){ From 4e327295c65b25fc5d6ceec5a8242eecf57b94e2 Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Sun, 1 Apr 2012 00:30:52 +0200 Subject: [PATCH 072/133] adding callback when ok click on alert dialog --- core/js/oc-dialogs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 35d0a0c5c4..17c987ae87 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -115,7 +115,7 @@ OCdialogs = { var f; switch(dialog_type) { case OCdialogs.ALERT_DIALOG: - f = function(){$(c_id).dialog('close'); }; + f = function(){$(c_id).dialog('close'); callback();}; break; case OCdialogs.PROMPT_DIALOG: f = function(){OCdialogs.prompt_ok_handler(callback, c_id)}; From d20eea9761238e0569f538c6f8b1bb553068bf7b Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 31 Mar 2012 22:41:43 +0000 Subject: [PATCH 073/133] Use ajax to download file, OC_Dialogs for errors --- apps/user_migrate/ajax/export.php | 63 ++++++++++++++++++++++++ apps/user_migrate/appinfo/app.php | 1 + apps/user_migrate/js/export.js | 27 ++++++++++ apps/user_migrate/settings.php | 22 ++------- apps/user_migrate/templates/settings.php | 14 +++--- lib/migrate.php | 4 +- 6 files changed, 102 insertions(+), 29 deletions(-) create mode 100644 apps/user_migrate/ajax/export.php create mode 100644 apps/user_migrate/js/export.js diff --git a/apps/user_migrate/ajax/export.php b/apps/user_migrate/ajax/export.php new file mode 100644 index 0000000000..ef947c610f --- /dev/null +++ b/apps/user_migrate/ajax/export.php @@ -0,0 +1,63 @@ +. + * + */ +// Init owncloud +require_once('../../../lib/base.php'); + +// Check if we are a user +OC_JSON::checkLoggedIn(); +OC_Util::checkAppEnabled('user_migrate'); + OC_JSON::error(); + die(); +// Which operation +if( $_GET['operation']=='create' ){ +$uid = !empty( $_POST['uid'] ) ? $_POST['uid'] : OC_User::getUser(); +if( $uid != OC_User::getUser() ){ + // Needs to be admin to export someone elses account + OC_JSON::error(); + die(); +} +// Create the export zip +if( !$path = OC_Migrate::export( $uid ) ){ + // Error + OC_JSON::error(); + die(); +} else { + // Save path in session + $_SESSION['ocuserexportpath'] = $path; +} +OC_JSON::success(); +die(); +} else if( $_GET['operation']=='download' ){ + // Download the export + $path = isset( $_SESSION['ocuserexportpath'] ) ? $_SESSION['ocuserexportpath'] : false; + if( !$path ){ + die(); + } + header("Content-Type: application/zip"); + header("Content-Disposition: attachment; filename=" . basename($path)); + header("Content-Length: " . filesize($path)); + @ob_end_clean(); + readfile($path); + unlink( $path ); + $_SESSION['ocuserexportpath'] = ''; +} diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php index 18ea8f52b2..a59b6dd705 100644 --- a/apps/user_migrate/appinfo/app.php +++ b/apps/user_migrate/appinfo/app.php @@ -23,6 +23,7 @@ OC_APP::registerPersonal( 'user_migrate', 'settings' ); OC_APP::registerAdmin( 'user_migrate', 'admin' ); +OC_Util::addScript( 'user_migrate', 'export'); // add settings page to navigation $entry = array( diff --git a/apps/user_migrate/js/export.js b/apps/user_migrate/js/export.js new file mode 100644 index 0000000000..0e1e396f65 --- /dev/null +++ b/apps/user_migrate/js/export.js @@ -0,0 +1,27 @@ +$(document).ready(function(){ + // Do the export + $('#exportbtn').click(function(){ + // Show loader + $('.loading').show(); + $.getJSON( + OC.filePath('user_migrate','ajax','export.php'), + {operation:'create'}, + function(result){ + if(result.status == 'success'){ + // Download the file + window.location = OC.filePath('user_migrate','ajax','export.php?operation=download') ; + $('.loading').hide(); + $('#exportbtn').val(t('user_migrate', 'Export')); + } else { + // Cancel loading + $('#exportbtn').html('Failed'); + // Show Dialog + OC.dialogs.alert(t('user_migrate', 'Something went wrong while the export file was being generated'), t('user_migrate', 'An error has occurred'), function(){ + $('#exportbtn').html(t('user_migrate', 'Export')+''); + }); + } + } + // End ajax + ); + }); +}); \ No newline at end of file diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php index 38eee990b4..62f347b355 100644 --- a/apps/user_migrate/settings.php +++ b/apps/user_migrate/settings.php @@ -24,22 +24,6 @@ */ OC_Util::checkAppEnabled('user_migrate'); -if (isset($_POST['user_export'])) { - // Create the export zip - if( !$path = OC_Migrate::export() ){ - // Error - die('error'); - } else { - // Download it - header("Content-Type: application/zip"); - header("Content-Disposition: attachment; filename=" . basename($path)); - header("Content-Length: " . filesize($path)); - @ob_end_clean(); - readfile($path); - unlink( $path ); - } -} else { - // fill template - $tmpl = new OC_Template('user_migrate', 'settings'); - return $tmpl->fetchPage(); -} \ No newline at end of file +// fill template +$tmpl = new OC_Template('user_migrate', 'settings'); +return $tmpl->fetchPage(); \ No newline at end of file diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php index 389de563a6..5f4857de5f 100644 --- a/apps/user_migrate/templates/settings.php +++ b/apps/user_migrate/templates/settings.php @@ -1,8 +1,6 @@ -
-
- t('Export your user account');?> -

t('This will create a compressed file that contains your ownCloud account.');?> -

- -
-
\ No newline at end of file +
+ t('Export your user account');?> +

t('This will create a compressed file that contains your ownCloud account.');?> +

+ +
diff --git a/lib/migrate.php b/lib/migrate.php index fe5ac45600..db3852fb83 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -73,12 +73,12 @@ class OC_Migrate{ /** * @breif exports a user, or owncloud instance + * @param optional $uid string user id of user to export if export type is user, defaults to current * @param ootional $type string type of export, defualts to user * @param otional $path string path to zip output folder - * @param optional $uid string user id of user to export if export type is user, defaults to current * @return false on error, path to zip on success */ - public static function export( $type='user', $path=null, $uid=null ){ + public static function export( $uid=null, $type='user', $path=null ){ $datadir = OC_Config::getValue( 'datadirectory' ); // Validate export type $types = array( 'user', 'instance', 'system', 'userfiles' ); From aba3182a7d6c2dc5573a5cca75b2ed5e8abbc3e0 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 31 Mar 2012 22:47:38 +0000 Subject: [PATCH 074/133] Fix loading image after export failure --- apps/user_migrate/js/export.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user_migrate/js/export.js b/apps/user_migrate/js/export.js index 0e1e396f65..2d660b2de6 100644 --- a/apps/user_migrate/js/export.js +++ b/apps/user_migrate/js/export.js @@ -17,7 +17,7 @@ $(document).ready(function(){ $('#exportbtn').html('Failed'); // Show Dialog OC.dialogs.alert(t('user_migrate', 'Something went wrong while the export file was being generated'), t('user_migrate', 'An error has occurred'), function(){ - $('#exportbtn').html(t('user_migrate', 'Export')+''); + $('#exportbtn').html(t('user_migrate', 'Export')+''); }); } } From 3e84d8548296152112c09aa0e4a3a4c02b816494 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 31 Mar 2012 22:50:57 +0000 Subject: [PATCH 075/133] remove debug --- apps/user_migrate/ajax/export.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/user_migrate/ajax/export.php b/apps/user_migrate/ajax/export.php index ef947c610f..fac96577fa 100644 --- a/apps/user_migrate/ajax/export.php +++ b/apps/user_migrate/ajax/export.php @@ -26,8 +26,6 @@ require_once('../../../lib/base.php'); // Check if we are a user OC_JSON::checkLoggedIn(); OC_Util::checkAppEnabled('user_migrate'); - OC_JSON::error(); - die(); // Which operation if( $_GET['operation']=='create' ){ $uid = !empty( $_POST['uid'] ) ? $_POST['uid'] : OC_User::getUser(); From d01b78a4b486860ab7110677e9969a37ee2a832f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 1 Apr 2012 00:55:12 +0200 Subject: [PATCH 076/133] prevent an possible xss exploit --- core/templates/login.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/login.php b/core/templates/login.php index 82222c8212..4ba92221a7 100644 --- a/core/templates/login.php +++ b/core/templates/login.php @@ -7,7 +7,7 @@

- autocomplete="off" required /> + autocomplete="off" required />

From ce89ff15aaa971ec930196db64e7437c2fe47fa5 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 31 Mar 2012 23:20:08 +0000 Subject: [PATCH 077/133] Remove db tmp file after export creation --- lib/migration/content.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/migration/content.php b/lib/migration/content.php index d25b5af293..a9fa05596b 100644 --- a/lib/migration/content.php +++ b/lib/migration/content.php @@ -28,7 +28,7 @@ class OC_Migration_Content{ private $zip=false; // Holds the MDB2 object - private $db=false; + private $db=null; // Holds an array of tmpfiles to delete after zip creation private $tmpfiles=false; @@ -38,11 +38,18 @@ class OC_Migration_Content{ * @param optional $db a MDB2 database object (required for exporttype user) * @return bool */ - public function __construct( $zip, $db=false ){ + public function __construct( $zip, $db=null ){ $this->zip = $zip; $this->db = $db; + if( !is_null( $db ) ){ + // Get db path + $db = $this->db->getDatabase(); + $this->tmpfiles[] = $db; + OC_Log::write('user-migrate',$db, OC_Log::INFO); + } + } // @breif prepares the db From daf742c086fefab9d715be3308088e626de1216c Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 31 Mar 2012 23:55:41 +0000 Subject: [PATCH 078/133] Fix owncloud log --- apps/contacts/appinfo/migrate.php | 68 +++++++++++++++++++++++++++++++ lib/log/owncloud.php | 2 +- 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 apps/contacts/appinfo/migrate.php diff --git a/apps/contacts/appinfo/migrate.php b/apps/contacts/appinfo/migrate.php new file mode 100644 index 0000000000..a6c6bc20fa --- /dev/null +++ b/apps/contacts/appinfo/migrate.php @@ -0,0 +1,68 @@ +'contacts_addressbooks', + 'matchcol'=>'userid', + 'matchval'=>$this->uid, + 'idcol'=>'id' + ); + $ids = $this->content->copyRows( $options ); + + $options = array( + 'table'=>'contacts_cards', + 'matchcol'=>'addressbookid', + 'matchval'=>$ids + ); + + // Export tags + $ids2 = $this->content->copyRows( $options ); + + // If both returned some ids then they worked + if( is_array( $ids ) && is_array( $ids2 ) ) + { + return true; + } else { + return false; + } + + } + + // Import function for bookmarks + function import( ){ + switch( $this->appinfo->version ){ + default: + // All versions of the app have had the same db structure, so all can use the same import function + $query = $this->content->prepare( "SELECT * FROM contacts_addressbooks WHERE userid LIKE ?" ); + $results = $query->execute( array( $this->olduid ) ); + $idmap = array(); + while( $row = $results->fetchRow() ){ + // Import each bookmark, saving its id into the map + $query = OC_DB::prepare( "INSERT INTO *PREFIX*contacts_addressbooks (`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)" ); + $query->execute( array( $this->uid, $row['displayname'], $row['uri'], $row['description'], $row['ctag'] ) ); + // Map the id + $idmap[$row['id']] = OC_DB::insertid(); + } + // Now tags + foreach($idmap as $oldid => $newid){ + $query = $this->content->prepare( "SELECT * FROM contacts_cards WHERE addressbookid LIKE ?" ); + $results = $query->execute( array( $oldid ) ); + while( $row = $results->fetchRow() ){ + // Import the tags for this bookmark, using the new bookmark id + $query = OC_DB::prepare( "INSERT INTO *PREFIX*contacts_cards (`addressbookid`, `fullname`, `carddata`, `uri`, `lastmodified`) VALUES (?, ?, ?, ?, ?)" ); + $query->execute( array( $newid, $row['fullname'], $row['carddata'], $row['uri'], $row['lastmodified'] ) ); + } + } + // All done! + break; + } + + return true; + } + +} + +// Load the provider +new OC_Migration_Provider_Contacts( 'contacts' ); \ No newline at end of file diff --git a/lib/log/owncloud.php b/lib/log/owncloud.php index 5e14320556..0ed3051013 100644 --- a/lib/log/owncloud.php +++ b/lib/log/owncloud.php @@ -65,7 +65,7 @@ class OC_Log_Owncloud { if(!file_exists(self::$logFile)) { return array(); } - $contents=file($logFile); + $contents=file(self::$logFile); if(!$contents) {//error while reading log return array(); } From ffbd72bbcf775ac31a43b958ec1e8eaddf0f8356 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 1 Apr 2012 00:07:39 +0000 Subject: [PATCH 079/133] Fix user app data export --- lib/migration/content.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/migration/content.php b/lib/migration/content.php index a9fa05596b..d304051f3e 100644 --- a/lib/migration/content.php +++ b/lib/migration/content.php @@ -47,7 +47,6 @@ class OC_Migration_Content{ // Get db path $db = $this->db->getDatabase(); $this->tmpfiles[] = $db; - OC_Log::write('user-migrate',$db, OC_Log::INFO); } } @@ -110,7 +109,8 @@ class OC_Migration_Content{ foreach( $options['matchval'] as $matchval ){ // Run the query for this match value (where x = y value) - $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?" ); + $sql = "SELECT * FROM *PREFIX*" . $options['table'] . " WHERE " . $options['matchcol'] . " LIKE ?"; + $query = OC_DB::prepare( $sql ); $results = $query->execute( array( $matchval ) ); $newreturns = $this->insertData( $results, $options ); $return = array_merge( $return, $newreturns ); @@ -118,7 +118,8 @@ class OC_Migration_Content{ } else { // Just get everything - $query = OC_DB::prepare( "SELECT * FROM *PREFIX*" . $options['table'] ); + $sql = "SELECT * FROM *PREFIX*" . $options['table']; + $query = OC_DB::prepare( $sql ); $results = $query->execute(); $return = $this->insertData( $results, $options ); @@ -136,6 +137,7 @@ class OC_Migration_Content{ */ private function insertData( $data, $options ){ $return = array(); + // Foreach row of data to insert while( $row = $data->fetchRow() ){ // Now save all this to the migration.db foreach($row as $field=>$value){ @@ -166,6 +168,8 @@ class OC_Migration_Content{ $return[] = reset($row); } } + $fields = ''; + $values = ''; } return $return; } From eba6a65908ab848741a29467a2c054818e3c740c Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 1 Apr 2012 00:25:47 +0000 Subject: [PATCH 080/133] try to use transactions to replace db --- apps/admin_export/settings.php | 2 +- lib/db.php | 9 +++------ lib/migrate.php | 10 ++++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php index 269147a3bc..719bedb66e 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_export/settings.php @@ -28,7 +28,7 @@ OC_Util::checkAppEnabled('admin_export'); // Export? if (isset($_POST['admin_export'])) { // Create the export zip - if( !$path = OC_Migrate::export( $_POST['export_type'] ) ){ + if( !$path = OC_Migrate::export( null, $_POST['export_type'] ) ){ // Error die('error'); } else { diff --git a/lib/db.php b/lib/db.php index a7b7ae75da..9c46a40add 100644 --- a/lib/db.php +++ b/lib/db.php @@ -489,7 +489,7 @@ class OC_DB { public static function replaceDB( $file ){ $apps = OC_App::getAllApps(); - + self::beginTransaction(); // Delete the old tables self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' ); @@ -501,11 +501,8 @@ class OC_DB { } // Create new tables - if( self::createDBFromStructure( $file ) ){ - return true; - } else { - return false; - } + self::createDBFromStructure( $file ); + self::commit(); } diff --git a/lib/migrate.php b/lib/migrate.php index db3852fb83..815333b4d8 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -257,28 +257,30 @@ class OC_Migrate{ return $appsimported; break; case 'instance': + /* // Check for new data dir and dbexport before doing anything // TODO - /* + // Delete current data folder. OC_Log::write( 'migration', "Deleting current data dir", OC_Log::INFO ); - if( self::unlink_r( $datadir, false ) ){ + if( !self::unlink_r( $datadir, false ) ){ OC_Log::write( 'migration', 'Failed to delete the current data dir', OC_Log::ERROR ); return false; } // Copy over data - if( !self::copy_r( $extractname . 'data', $datadir ) ){ + if( !self::copy_r( $extractpath . 'userdata', $datadir ) ){ OC_Log::write( 'migration', 'Failed to copy over data directory', OC_Log::ERROR ); return false; } - */ + // Import the db if( !OC_DB::replaceDB( $extractpath . 'dbexport.xml' ) ){ return false; } // Done return true; + */ break; } From 6bb48b2731185dd93ad1a1edc52f33725cd99061 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 1 Apr 2012 02:38:26 -0400 Subject: [PATCH 081/133] Check file handle exists before trying to read file --- lib/filesystemview.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 39e47975b2..a3736f1976 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -137,13 +137,16 @@ class OC_FilesystemView { } public function readfile($path){ $handle=$this->fopen($path,'r'); - $chunkSize = 1024*1024;// 1 MB chunks - while (!feof($handle)) { - echo fread($handle, $chunkSize); - @ob_flush(); - flush(); + if ($handle) { + $chunkSize = 1024*1024;// 1 MB chunks + while (!feof($handle)) { + echo fread($handle, $chunkSize); + @ob_flush(); + flush(); + } + return $this->filesize($path); } - return $this->filesize($path); + return false; } public function is_readable($path){ return $this->basicOperation('is_readable',$path); From 6545e4878715c10d85a72b1cdb3f7cef73d7e383 Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Sun, 1 Apr 2012 11:20:12 +0200 Subject: [PATCH 082/133] Show the different editions to the user. Used in the status call, on the personal settings page and in the updater to update to the next available version from the same edition. --- lib/updater.php | 1 + lib/util.php | 8 ++++++++ settings/templates/personal.php | 2 +- status.php | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/updater.php b/lib/updater.php index 57623797ae..196822ac35 100644 --- a/lib/updater.php +++ b/lib/updater.php @@ -36,6 +36,7 @@ class OC_Updater{ $version['installed']=OC_Config::getValue('installedat'); $version['updated']=OC_Appconfig::getValue('core', 'lastupdatedat', OC_Config::getValue( 'lastupdatedat')); $version['updatechannel']='stable'; + $version['edition']=OC_Util::getEditionString(); $versionstring=implode('x',$version); //fetch xml data from updater diff --git a/lib/util.php b/lib/util.php index 529b6d958f..722b7404d0 100644 --- a/lib/util.php +++ b/lib/util.php @@ -77,6 +77,14 @@ class OC_Util { return '3'; } + /** + * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise". + * @return string + */ + public static function getEditionString(){ + return ''; + } + /** * add a javascript file * diff --git a/settings/templates/personal.php b/settings/templates/personal.php index 57731d979d..d40da7eb77 100644 --- a/settings/templates/personal.php +++ b/settings/templates/personal.php @@ -50,7 +50,7 @@ };?>

- ownCloud
+ ownCloud
developed by the ownCloud community

source code licensed freely under AGPL diff --git a/status.php b/status.php index 94c8cfce84..81f339fa53 100644 --- a/status.php +++ b/status.php @@ -26,7 +26,7 @@ $RUNTIME_NOAPPS = TRUE; //no apps, yet require_once('lib/base.php'); if(OC_Config::getValue('installed')==1) $installed='true'; else $installed='false'; -$values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString()); +$values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString(),'edition'=>OC_Util::getEditionString()); echo(json_encode($values)); From 1b8a644c31a83279e9829aac74fbbc843d49f65d Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Sun, 1 Apr 2012 16:06:08 +0200 Subject: [PATCH 083/133] update outdated README --- README | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README b/README index 4d4be2728e..77379a4645 100644 --- a/README +++ b/README @@ -3,10 +3,11 @@ A personal cloud which runs on your own server. http://ownCloud.org -Installation instructions: http://owncloud.org/support/setup-and-installation/ -Source code: http://gitorious.org/owncloud +Installation instructions: http://owncloud.org/support +Source code: http://gitorious.org/owncloud Mailing list: http://mail.kde.org/mailman/listinfo/owncloud IRC channel: http://webchat.freenode.net/?channels=owncloud Diaspora: https://joindiaspora.com/u/owncloud Identi.ca: http://identi.ca/owncloud + From b758725bf7fad960e971adfeb826596e02673244 Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Sun, 1 Apr 2012 17:02:32 +0200 Subject: [PATCH 084/133] =?UTF-8?q?Try=20to=20configure=20php=20to=20enabl?= =?UTF-8?q?e=20big=20file=20uploads.=20This=20doesn=C2=B4t=20work=20always?= =?UTF-8?q?=20depending=20on=20the=20webserver=20and=20php=20configuration?= =?UTF-8?q?.=20Let=C2=B4s=20try=20to=20overwrite=20some=20defaults=20anywa?= =?UTF-8?q?ys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/base.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/base.php b/lib/base.php index 22f7f4ea48..a4a94e8696 100644 --- a/lib/base.php +++ b/lib/base.php @@ -277,6 +277,24 @@ class OC{ date_default_timezone_set('Europe/Berlin'); ini_set('arg_separator.output','&'); + //try to configure php to enable big file uploads. + //this doesn´t work always depending on the webserver and php configuration. + //Let´s try to overwrite some defaults anyways + + //try to set the maximum execution time to 60min + @set_time_limit(3600); + @ini_set('max_execution_time',3600); + @ini_set('max_input_time',3600); + + //try to set the maximum filesize to 10G + @ini_set('upload_max_filesize','10G'); + @ini_set('post_max_size','10G'); + @ini_set('file_uploads','50'); + + //try to set the session lifetime to 60min + @ini_set('gc_maxlifetime','3600'); + + //set http auth headers for apache+php-cgi work around if (isset($_SERVER['HTTP_AUTHORIZATION']) && preg_match('/Basic\s+(.*)$/i', $_SERVER['HTTP_AUTHORIZATION'], $matches)) { From 39e8981bc2ef098a020476a9d94a4b0cc4d9e1c9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 1 Apr 2012 17:31:44 +0200 Subject: [PATCH 085/133] oc_db is not pdo also pgsql does not like double quotes --- lib/app.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/app.php b/lib/app.php index 6c882963a0..625ef88967 100755 --- a/lib/app.php +++ b/lib/app.php @@ -103,9 +103,9 @@ class OC_App{ */ public static function getEnabledApps(){ $apps=array(); - $query = OC_DB::prepare( 'SELECT appid FROM *PREFIX*appconfig WHERE configkey = "enabled" AND configvalue="yes"' ); - $query->execute(); - while($row=$query->fetchRow()){ + $query = OC_DB::prepare( 'SELECT appid FROM *PREFIX*appconfig WHERE configkey = \'enabled\' AND configvalue=\'yes\'' ); + $result=$query->execute(); + while($row=$result->fetchRow()){ $apps[]=$row['appid']; } return $apps; @@ -452,7 +452,7 @@ class OC_App{ */ public static function getAppVersions(){ $versions=array(); - $query = OC_DB::prepare( 'SELECT appid, configvalue FROM *PREFIX*appconfig WHERE configkey = "installed_version"' ); + $query = OC_DB::prepare( 'SELECT appid, configvalue FROM *PREFIX*appconfig WHERE configkey = \'installed_version\'' ); $result = $query->execute(); while($row = $result->fetchRow()){ $versions[$row['appid']]=$row['configvalue']; From cce59df2ae399bc43f1c11a3162b4855de70a1bc Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 1 Apr 2012 19:20:59 +0200 Subject: [PATCH 086/133] the core apps don't have types --- lib/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app.php b/lib/app.php index 625ef88967..7d5e8fa91c 100755 --- a/lib/app.php +++ b/lib/app.php @@ -55,7 +55,7 @@ class OC_App{ // Our very own core apps are hardcoded foreach( array('files', 'settings') as $app ){ - if(is_null($types) or self::isType($app,$types)){ + if(is_null($types)){ require( $app.'/appinfo/app.php' ); } } From ff4b0c4d7f6b422c9426205897c015b497429b03 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 1 Apr 2012 13:30:41 -0400 Subject: [PATCH 087/133] Move writable check into local filestorage so shared files can be renamed --- lib/filestorage/local.php | 4 ++++ lib/filesystemview.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index 688501aee9..bd757f52ce 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -86,6 +86,10 @@ class OC_Filestorage_Local extends OC_Filestorage{ return $this->delTree($path); } public function rename($path1,$path2){ + if (!$this->is_writable($path1)) { + OC_Log::write('core','unable to rename, file is not writable : '.$path1,OC_Log::ERROR); + return false; + } if(! $this->file_exists($path1)){ OC_Log::write('core','unable to rename, file does not exists : '.$path1,OC_Log::ERROR); return false; diff --git a/lib/filesystemview.php b/lib/filesystemview.php index a3736f1976..9d530c7ad6 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -192,7 +192,7 @@ class OC_FilesystemView { return $this->basicOperation('unlink',$path,array('delete')); } public function rename($path1,$path2){ - if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and $this->is_writable($path1) and OC_Filesystem::isValidPath($path2)){ + if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and OC_Filesystem::isValidPath($path2)){ $run=true; OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run)); if($run){ From 3e150ff548a8bf3b29729ab9b04a1f38538737f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20H=C3=BClsmann?= Date: Mon, 2 Apr 2012 06:31:00 +0200 Subject: [PATCH 088/133] webfinger protocol compatible + minor changes --- apps/remoteStorage/appinfo/webfinger.php | 4 +-- apps/user_webfinger/appinfo/install.php | 18 +++++++++++++- apps/user_webfinger/webfinger.php | 31 +++++++++++++----------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php index bb3fe1681b..7c0ab84605 100644 --- a/apps/remoteStorage/appinfo/webfinger.php +++ b/apps/remoteStorage/appinfo/webfinger.php @@ -1,6 +1,6 @@ + auth="/apps/remoteStorage/auth.php/"> diff --git a/apps/user_webfinger/appinfo/install.php b/apps/user_webfinger/appinfo/install.php index 678d57ae8f..c8d9a42742 100644 --- a/apps/user_webfinger/appinfo/install.php +++ b/apps/user_webfinger/appinfo/install.php @@ -1,4 +1,8 @@ '*', + 'Content-Type' => 'application/xml+xrd' +); $appInfoDir = __DIR__; $thisAppDir = dirname($appInfoDir); $appsDir = dirname($thisAppDir); @@ -17,6 +21,7 @@ if(isset($_SERVER['HTTPS'])) { } $lrddTmpl .= '://' . $serverName . $webRoot . '/apps/user_webfinger/webfinger.php?q={uri}'; $hostMetaPath = $docRoot . '/.well-known/host-meta'; +$hostMetaDir = $docRoot . '/.well-known'; $hostMetaContents = " " . $serverName . " @@ -24,7 +29,7 @@ $hostMetaContents = " Resource Descriptor "; -@mkdir(dirname($hostMetaPath)); +@mkdir($hostMetaDir); $hostMeta = fopen($hostMetaPath, 'w'); if(!$hostMeta) { die("Could not open " . $hostMetaPath . " for writing, please check permissions!"); @@ -33,3 +38,14 @@ if(!fwrite($hostMeta, $hostMetaContents, strlen($hostMetaContents))) { die("Could not write to " . $hostMetaPath . ", please check permissions!"); } fclose($hostMeta); + +// write custom headers into .htaccess: +$htaccess = fopen($hostMetaDir . '/.htaccess', 'w'); +//TODO: check compatibility! +fwrite($htaccess, " +\n"); +foreach($hostMetaHeader as $header => $value) { + fwrite($htaccess, "Header set " . $header . " \"" . $value . "\"\n"); +} +fwrite($htaccess, "\n"); +fclose($htaccess); diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index ecbfeed8e4..9ada473ca8 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -6,13 +6,13 @@ header("Content-Type: application/xrd+xml"); * To include your app in the webfinger XML, add a new script with file name * 'webfinger.php' to /apps/yourapp/appinfo/, which prints out the XML parts * to be included. That script can make use of the constants WF_USER (e. g. - * "user"), WF_ADDRESS ("user@host") and WF_ROOT ("https://host/owncloud"). + * "user"), WF_ID (user@host) and WF_BASEURL (e. g. https://host/owncloud). * An example could look like this: * * + * href="/apps/myApp/profile.php?user="> * * '* but can also use complex database queries to generate the webfinger result @@ -24,19 +24,25 @@ $SERVERROOT=str_replace("\\",'/',dirname(dirname(dirname(dirname(__FILE__))))); $SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT)); $WEBROOT=substr($SUBURI,0,-34); */ -require_once('../../lib/base.php'); -$id = $_GET['q']; +require_once('../../lib/base.php'); +$request = urldecode($_GET['q']); if($_GET['q']) { - $bits = explode('@', $_GET['q']); - $userName = $bits[0]; + $reqParts = explode('@', $request); + $userName = $reqParts[0]; + $hostName = $reqParts[1]; } else { - $id = ''; $userName = ''; + $hostName = ''; } if(substr($userName, 0, 5) == 'acct:') { $userName = substr($userName, 5); } +if($userName == "") { + $id = ""; +} else { + $id = $userName . '@' . $hostName; +} if(isset($_SERVER['HTTPS'])) { $baseAddress = 'https://'; } else { @@ -44,22 +50,19 @@ if(isset($_SERVER['HTTPS'])) { } $baseAddress .= $_SERVER['SERVER_NAME'].OC::$WEBROOT; define('WF_USER', $userName); -define('WF_ADDRESS', $id); -define('WF_ROOT', $baseAddress); +define('WF_ID', $id); +define('WF_BASEURL', $baseAddress); echo "<"; ?> ?xml version="1.0" encoding="UTF-8"?> - - acct: + + acct: Date: Mon, 2 Apr 2012 17:12:52 +0200 Subject: [PATCH 089/133] webfinger protocol compatible + minor changes --- apps/remoteStorage/appinfo/webfinger.php | 4 +-- apps/user_webfinger/appinfo/install.php | 18 +++++++++++++- apps/user_webfinger/webfinger.php | 31 +++++++++++++----------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php index bb3fe1681b..7c0ab84605 100644 --- a/apps/remoteStorage/appinfo/webfinger.php +++ b/apps/remoteStorage/appinfo/webfinger.php @@ -1,6 +1,6 @@ + auth="/apps/remoteStorage/auth.php/"> diff --git a/apps/user_webfinger/appinfo/install.php b/apps/user_webfinger/appinfo/install.php index 678d57ae8f..c8d9a42742 100644 --- a/apps/user_webfinger/appinfo/install.php +++ b/apps/user_webfinger/appinfo/install.php @@ -1,4 +1,8 @@ '*', + 'Content-Type' => 'application/xml+xrd' +); $appInfoDir = __DIR__; $thisAppDir = dirname($appInfoDir); $appsDir = dirname($thisAppDir); @@ -17,6 +21,7 @@ if(isset($_SERVER['HTTPS'])) { } $lrddTmpl .= '://' . $serverName . $webRoot . '/apps/user_webfinger/webfinger.php?q={uri}'; $hostMetaPath = $docRoot . '/.well-known/host-meta'; +$hostMetaDir = $docRoot . '/.well-known'; $hostMetaContents = " " . $serverName . " @@ -24,7 +29,7 @@ $hostMetaContents = " Resource Descriptor "; -@mkdir(dirname($hostMetaPath)); +@mkdir($hostMetaDir); $hostMeta = fopen($hostMetaPath, 'w'); if(!$hostMeta) { die("Could not open " . $hostMetaPath . " for writing, please check permissions!"); @@ -33,3 +38,14 @@ if(!fwrite($hostMeta, $hostMetaContents, strlen($hostMetaContents))) { die("Could not write to " . $hostMetaPath . ", please check permissions!"); } fclose($hostMeta); + +// write custom headers into .htaccess: +$htaccess = fopen($hostMetaDir . '/.htaccess', 'w'); +//TODO: check compatibility! +fwrite($htaccess, " +\n"); +foreach($hostMetaHeader as $header => $value) { + fwrite($htaccess, "Header set " . $header . " \"" . $value . "\"\n"); +} +fwrite($htaccess, "\n"); +fclose($htaccess); diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index ecbfeed8e4..9ada473ca8 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -6,13 +6,13 @@ header("Content-Type: application/xrd+xml"); * To include your app in the webfinger XML, add a new script with file name * 'webfinger.php' to /apps/yourapp/appinfo/, which prints out the XML parts * to be included. That script can make use of the constants WF_USER (e. g. - * "user"), WF_ADDRESS ("user@host") and WF_ROOT ("https://host/owncloud"). + * "user"), WF_ID (user@host) and WF_BASEURL (e. g. https://host/owncloud). * An example could look like this: * * + * href="/apps/myApp/profile.php?user="> * * '* but can also use complex database queries to generate the webfinger result @@ -24,19 +24,25 @@ $SERVERROOT=str_replace("\\",'/',dirname(dirname(dirname(dirname(__FILE__))))); $SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT)); $WEBROOT=substr($SUBURI,0,-34); */ -require_once('../../lib/base.php'); -$id = $_GET['q']; +require_once('../../lib/base.php'); +$request = urldecode($_GET['q']); if($_GET['q']) { - $bits = explode('@', $_GET['q']); - $userName = $bits[0]; + $reqParts = explode('@', $request); + $userName = $reqParts[0]; + $hostName = $reqParts[1]; } else { - $id = ''; $userName = ''; + $hostName = ''; } if(substr($userName, 0, 5) == 'acct:') { $userName = substr($userName, 5); } +if($userName == "") { + $id = ""; +} else { + $id = $userName . '@' . $hostName; +} if(isset($_SERVER['HTTPS'])) { $baseAddress = 'https://'; } else { @@ -44,22 +50,19 @@ if(isset($_SERVER['HTTPS'])) { } $baseAddress .= $_SERVER['SERVER_NAME'].OC::$WEBROOT; define('WF_USER', $userName); -define('WF_ADDRESS', $id); -define('WF_ROOT', $baseAddress); +define('WF_ID', $id); +define('WF_BASEURL', $baseAddress); echo "<"; ?> ?xml version="1.0" encoding="UTF-8"?> - - acct: + + acct: Date: Mon, 2 Apr 2012 16:35:11 +0000 Subject: [PATCH 090/133] use OC_Dialogs instead of alert() --- apps/files_texteditor/js/editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index 02d39b9843..9a87601a4f 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -210,7 +210,7 @@ function showFileEditor(dir,filename){ }); } else { // Failed to get the file. - alert(result.data.message); + OC.dialogs.alert(result.data.message, t('files_texteditor','An error occurred!')); } // End success } From dc499c5b4e9dc1c64ca81487bfa979121bd407f6 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Mon, 2 Apr 2012 17:27:06 +0000 Subject: [PATCH 091/133] add * to filename when changes have been made --- apps/files_texteditor/js/editor.js | 68 ++++++++++++++++++------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index 9a87601a4f..ca6a3a965f 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -141,31 +141,38 @@ function doSearch(){ // Tries to save the file. function doFileSave(){ if(editorIsShown()){ - // Get file path - var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename'); - // Get original mtime - var mtime = $('#editor').attr('data-mtime'); - // Show saving spinner - $("#editor_save").die('click',doFileSave); - $('#save_result').remove(); - $('#editor_save').text(t('files_texteditor','Saving...')); - // Get the data - var filecontents = window.aceEditor.getSession().getValue(); - // Send the data - $.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){ - if(jsondata.status!='success'){ - // Save failed - $('#editor_save').text(t('files_texteditor','Save')); - $('#editor_save').after('

Failed to save file

'); - $("#editor_save").live('click',doFileSave); - } else { - // Save OK - // Update mtime - $('#editor').attr('data-mtime',jsondata.data.mtime); - $('#editor_save').text(t('files_texteditor','Save')); - $("#editor_save").live('click',doFileSave); - } - },'json'); + // Changed contents? + if($('#editor').attr('data-edited')=='true'){ + // Get file path + var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename'); + // Get original mtime + var mtime = $('#editor').attr('data-mtime'); + // Show saving spinner + $("#editor_save").die('click',doFileSave); + $('#save_result').remove(); + $('#editor_save').text(t('files_texteditor','Saving...')); + // Get the data + var filecontents = window.aceEditor.getSession().getValue(); + // Send the data + $.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){ + if(jsondata.status!='success'){ + // Save failed + $('#editor_save').text(t('files_texteditor','Save')); + $('#editor_save').after('

Failed to save file

'); + $("#editor_save").live('click',doFileSave); + } else { + // Save OK + // Update mtime + $('#editor').attr('data-mtime',jsondata.data.mtime); + $('#editor_save').text(t('files_texteditor','Save')); + $("#editor_save").live('click',doFileSave); + // Update titles + $('#editor').attr('data-edited', 'false'); + $('#breadcrumb_file').text($('#editor').attr('data-filename')); + document.title = $('#editor').attr('data-filename')+' - ownCloud'; + } + },'json'); + } } }; @@ -192,10 +199,11 @@ function showFileEditor(dir,filename){ // Show the control bar showControls(filename,result.data.write); // Update document title - document.title = filename; + document.title = filename+' - ownCloud'; $('#editor').text(result.data.filecontents); $('#editor').attr('data-dir', dir); $('#editor').attr('data-filename', filename); + $('#editor').attr('data-edited', 'false'); window.aceEditor = ace.edit("editor"); aceEditor.setShowPrintMargin(false); aceEditor.getSession().setUseWrapMode(true); @@ -207,6 +215,13 @@ function showFileEditor(dir,filename){ OC.addScript('files_texteditor','aceeditor/theme-clouds', function(){ window.aceEditor.setTheme("ace/theme/clouds"); }); + window.aceEditor.getSession().on('change', function(){ + if($('#editor').attr('data-edited')!='true'){ + $('#editor').attr('data-edited', 'true'); + $('#breadcrumb_file').text($('#breadcrumb_file').text()+' *'); + document.title = $('#editor').attr('data-filename')+' * - ownCloud'; + } + }); }); } else { // Failed to get the file. @@ -287,4 +302,5 @@ $(document).ready(function(){ $('#editor').remove(); // Binds the save keyboard shortcut events //$(document).unbind('keydown').bind('keydown',checkForSaveKeyPress); + }); From f21d6d4f9fcd48ffe4e2e42b4f3a4e8b7bcfd142 Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Mon, 2 Apr 2012 19:39:24 +0200 Subject: [PATCH 092/133] dialogs filepicker first draft --- core/css/styles.css | 6 +++ core/js/js.js | 8 +++- core/js/oc-dialogs.js | 91 ++++++++++++++++++++++++++++++++++++------ files/ajax/rawlist.php | 23 +++++++++++ 4 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 files/ajax/rawlist.php diff --git a/core/css/styles.css b/core/css/styles.css index f5a181c452..1c50df9e58 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -131,3 +131,9 @@ li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ff .separator { display: inline; border-left: 1px solid #d3d3d3; border-right: 1px solid #fff; height: 10px; width:0px; margin: 4px; } a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px } + +/* ---- DIALOGS ---- */ + +#dirtree {width: 100%;} +#filelist {height: 270px; overflow:scroll; background-color: white;} +.filepicker_element_selected { background-color: lightblue;} diff --git a/core/js/js.js b/core/js/js.js index df1b5c6ce7..44b4f503b8 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -126,7 +126,13 @@ OC={ }); } }, - dialogs:OCdialogs + dialogs:OCdialogs, + mtime2date:function(mtime) { + mtime = parseInt(mtime); + var date = new Date(1000*mtime); + var ret = date.getDate()+'.'+(date.getMonth()+1)+'.'+date.getFullYear()+', '+date.getHours()+':'+date.getMinutes(); + return ret; + } }; OC.search.customResults={}; OC.search.currentResult=-1; diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 17c987ae87..31aa76d96c 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -17,7 +17,6 @@ * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * - * todo(bartek): add select option in form */ /** @@ -30,9 +29,9 @@ OCdialogs = { * @param title dialog title * @param callback which will be triggered when user press OK */ - alert:function(text, title, callback) { + alert:function(text, title, callback, modal) { var content = '

'+text+'

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback); + OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); }, /** * displays info dialog @@ -40,9 +39,9 @@ OCdialogs = { * @param title dialog title * @param callback which will be triggered when user press OK */ - info:function(text, title, callback) { + info:function(text, title, callback, modal) { var content = '

'+text+'

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback); + OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal); }, /** * displays confirmation dialog @@ -50,9 +49,9 @@ OCdialogs = { * @param title dialog title * @param callback which will be triggered when user press YES or NO (true or false would be passed to callback respectively) */ - confirm:function(text, title, callback) { + confirm:function(text, title, callback, modal) { var content = '

'+text+'

'; - OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback); + OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback, modal); }, /** * prompt for user input @@ -60,9 +59,9 @@ OCdialogs = { * @param title dialog title * @param callback which will be triggered when user press OK (input text will be passed to callback) */ - prompt:function(text, title, default_value, callback) { + prompt:function(text, title, default_value, callback, modal) { var content = '

'+text+':

'; - OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback); + OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback, modal); }, /** * prompt user for input with custom form @@ -71,7 +70,7 @@ OCdialogs = { * @param title dialog title * @param callback which will be triggered when user press OK (user answers will be passed to callback in following format: [{name:'return name', value: 'user value'},...]) */ - form:function(fields, title, callback) { + form:function(fields, title, callback, modal) { var content = ''; for (var a in fields) { content += ''; } content += '
'+fields[a].text+''; @@ -96,12 +95,41 @@ OCdialogs = { content += '
'; - OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback); + OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback, modal); }, - message:function(content, title, dialog_type, buttons, callback) { + filepicker:function(title, name_filter, mimetype_filter, callback, modal) { + var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content'; + var c_id = '#'+c_name; + var d = '
'; + if (modal == undefined) modal = false; + $('body').append(d); + $(c_id + ' #dirtree').focus(function() { var t = $(this); t.data('oldval', t.val())}) + .change({dcid: c_id}, OC.dialogs.handleTreeListSelect); + $(c_id).ready(function(){ + $.getJSON(OC.webroot+'/files/ajax/rawlist.php', function(r){OC.dialogs.fillFilePicker(r, c_id, callback)}); + }); + // build buttons + var b = [ + {text: t('dialogs', 'Choose'), click: function(){ + if (callback != undefined) { + var p = $(c_id).attr('data'); + if (p == undefined) p = ''; + callback(p+'/'+$(c_id+' .filepicker_element_selected #filename').text()); + $(c_id).dialog('close'); + } + } + }, + {text: t('dialogs', 'Cancel'), click: function(){$(c_id).dialog('close'); }} + ]; + $(c_id).dialog({width: 4*$(document).width()/9, height: 400, modal: modal, buttons: b}); + OCdialogs.dialogs_counter++; + }, + // guts, dont use, dont touch + message:function(content, title, dialog_type, buttons, callback, modal) { var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content'; var c_id = '#'+c_name; var d = '
'+content+'
'; + if (modal == undefined) modal = false; $('body').append(d); var b = []; switch (buttons) { @@ -128,7 +156,7 @@ OCdialogs = { break; } var possible_height = ($('tr', d).size()+1)*30; - $(c_id).dialog({width: 4*$(document).width()/9, height: possible_height + 120, modal: false, buttons: b}); + $(c_id).dialog({width: 4*$(document).width()/9, height: possible_height + 120, modal: modal, buttons: b}); OCdialogs.dialogs_counter++; }, // dialogs buttons types @@ -161,5 +189,42 @@ OCdialogs = { } else { $(c_id).dialog('close'); } + }, + fillFilePicker:function(r, dialog_content_id) { + var entry_template = '
*NAME*
*LASTMODDATE*
'; + var names = ''; + for (var a in r.data) { + names += entry_template.replace('*LASTMODDATE*', OC.mtime2date(r.data[a].mtime)).replace('*NAME*', r.data[a].name).replace('*MIMETYPEICON*', OC.webroot+'/core/img/filetypes/'+(r.data[a].type=='dir'?'folder':r.data[a].mimetype.replace('/','-'))+'.png').replace('*ENTRYNAME*', r.data[a].name).replace('*ENTRYTYPE*', r.data[a].type); + } + $(dialog_content_id + ' #filelist').html(names); + }, + handleTreeListSelect:function(event) { + var newval = parseInt($(this).val()); + var oldval = parseInt($(this).data('oldval')); + while (newval != oldval && oldval > 0) { + $('option:last', this).remove(); + $('option:last', this).attr('selected','selected'); + oldval--; + } + var skip_first = true; + var path = ''; + $(this).children().each(function(i, element) { if (skip_first) {skip_first = false; return; }path += '/'+$(element).text(); }); + $(event.data.dcid).attr('data', path); + $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path}, function(r){OC.dialogs.fillFilePicker(r, event.data.dcid)}); + }, + // this function is in early development state, please dont use it unlsess you know what you are doing + handlePickerClick:function(element, name, dcid) { + var p = $(dcid).attr('data'); + if (p == undefined) p = ''; + p = p+'/'+name; + if ($(element).attr('data') == 'file'){ + $(element).toggleClass('filepicker_element_selected'); + return; + } + $(dcid).attr('data', p); + $(dcid + ' #dirtree option:last').removeAttr('selected'); + var newval = parseInt($(dcid + ' #dirtree option:last').val())+1; + $(dcid + ' #dirtree').append(''); + $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: p}, function(r){OC.dialogs.fillFilePicker(r, dcid)}); } }; diff --git a/files/ajax/rawlist.php b/files/ajax/rawlist.php new file mode 100644 index 0000000000..0abe81e672 --- /dev/null +++ b/files/ajax/rawlist.php @@ -0,0 +1,23 @@ + $files)); + +?> From adc9d906e4b45082dc08463afce6835a49feb438 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Mon, 2 Apr 2012 17:44:15 +0000 Subject: [PATCH 093/133] confirm close when there are unsaved changes --- apps/files_texteditor/js/editor.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index ca6a3a965f..1e136fe68e 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -76,7 +76,7 @@ function showControls(filename,writeperms){ function bindControlEvents(){ $("#editor_save").die('click',doFileSave).live('click',doFileSave); - $('#editor_close').die('click',hideFileEditor).live('click',hideFileEditor); + $('#editor_close').die('click',closeBtnClick).live('click',closeBtnClick); $('#gotolineval').die('keyup', goToLine).live('keyup', goToLine); $('#editorsearchval').die('keyup', doSearch).live('keyup', doSearch); $('#clearsearchbtn').die('click', resetSearch).live('click', resetSearch); @@ -235,6 +235,19 @@ function showFileEditor(dir,filename){ } } +function closeBtnClick(){ + if($('#editor').attr('data-edited')=='true'){ + // Show confirm + OC.dialogs.confirm(t('files_texteditor','You have unsaved changes that will be lost! Do you still want to close?'),t('files_texteditor','Really close?'),function(close){ + if(close){ + hideFileEditor(); + } + }); + } else { + hideFileEditor(); + } +} + // Fades out the editor. function hideFileEditor(){ // Fades out editor controls From ff5dbc52b8137bc1d4e082c422049271d5f89978 Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Mon, 2 Apr 2012 21:31:34 +0200 Subject: [PATCH 094/133] multiselect handle for filepicker --- core/js/oc-dialogs.js | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 31aa76d96c..d40c433bda 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -97,24 +97,34 @@ OCdialogs = { content += ''; OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback, modal); }, - filepicker:function(title, name_filter, mimetype_filter, callback, modal) { + filepicker:function(title, callback, multiselect, mimetype_filter, modal) { var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content'; var c_id = '#'+c_name; var d = '
'; - if (modal == undefined) modal = false; + if (!modal) modal = false; + if (!multiselect) multiselect = false; $('body').append(d); $(c_id + ' #dirtree').focus(function() { var t = $(this); t.data('oldval', t.val())}) .change({dcid: c_id}, OC.dialogs.handleTreeListSelect); $(c_id).ready(function(){ $.getJSON(OC.webroot+'/files/ajax/rawlist.php', function(r){OC.dialogs.fillFilePicker(r, c_id, callback)}); - }); + }).data('multiselect', multiselect); // build buttons var b = [ {text: t('dialogs', 'Choose'), click: function(){ if (callback != undefined) { - var p = $(c_id).attr('data'); - if (p == undefined) p = ''; - callback(p+'/'+$(c_id+' .filepicker_element_selected #filename').text()); + var p; + if ($(c_id).data('multiselect') == true) { + p = []; + $(c_id+' .filepicker_element_selected #filename').each(function(i, elem) { + p.push(($(c_id).data('path')?$(c_id).data('path'):'')+'/'+$(elem).text()); + }); + } else { + var p = $(c_id).data('path'); + if (p == undefined) p = ''; + p = p+'/'+$(c_id+' .filepicker_element_selected #filename').text() + } + callback(p); $(c_id).dialog('close'); } } @@ -209,19 +219,21 @@ OCdialogs = { var skip_first = true; var path = ''; $(this).children().each(function(i, element) { if (skip_first) {skip_first = false; return; }path += '/'+$(element).text(); }); - $(event.data.dcid).attr('data', path); + $(event.data.dcid).data('path', path); $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path}, function(r){OC.dialogs.fillFilePicker(r, event.data.dcid)}); }, // this function is in early development state, please dont use it unlsess you know what you are doing handlePickerClick:function(element, name, dcid) { - var p = $(dcid).attr('data'); + var p = $(dcid).data('path'); if (p == undefined) p = ''; p = p+'/'+name; if ($(element).attr('data') == 'file'){ + if ($(dcid).data('multiselect') != true) + $(dcid+' .filepicker_element_selected').removeClass('filepicker_element_selected'); $(element).toggleClass('filepicker_element_selected'); return; } - $(dcid).attr('data', p); + $(dcid).data('path', p); $(dcid + ' #dirtree option:last').removeAttr('selected'); var newval = parseInt($(dcid + ' #dirtree option:last').val())+1; $(dcid + ' #dirtree').append(''); From 896bff57480448456b2ca7d57f5ff2b0439315f6 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 29 Mar 2012 18:54:06 +0200 Subject: [PATCH 095/133] Contacts: rescan categories on load if there are none. --- apps/contacts/index.php | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/contacts/index.php b/apps/contacts/index.php index 04f6c65a14..076b10c2ee 100644 --- a/apps/contacts/index.php +++ b/apps/contacts/index.php @@ -34,7 +34,28 @@ if(!is_null($id)) { } $property_types = OC_Contacts_App::getAddPropertyOptions(); $phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); -$categories = OC_Contacts_App::$categories->categories(); +$categories = OC_Contacts_App::getCategories(); +if(count($categories) == 0) { + $vcaddressbooks = OC_Contacts_Addressbook::all(OC_User::getUser()); + if(count($vcaddressbooks) > 0) { + $vcaddressbookids = array(); + foreach($vcaddressbooks as $vcaddressbook) { + $vcaddressbookids[] = $vcaddressbook['id']; + } + $vccontacts = OC_Contacts_VCard::all($vcaddressbookids); + if(count($vccontacts) == 0) { + bailOut(OC_Contacts_App::$l10n->t('No contacts found.')); + } + + $cards = array(); + foreach($vccontacts as $vccontact) { + $cards[] = $vccontact['carddata']; + } + + OC_Contacts_App::$categories->rescan($cards); + $categories = OC_Contacts_App::$categories->categories(); + } +} $upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize')); $post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size')); @@ -61,7 +82,6 @@ $tmpl = new OC_Template( "contacts", "index", "user" ); $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize)); $tmpl->assign('property_types',$property_types); -$tmpl->assign('categories',OC_Contacts_App::getCategories()); $tmpl->assign('phone_types',$phone_types); $tmpl->assign('categories',$categories); $tmpl->assign('addressbooks', $addressbooks); From b3f107dddaf4f3b5b65d8dd67d66bf1bd36d9ae1 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 3 Apr 2012 03:28:12 +0200 Subject: [PATCH 096/133] Contacts: First work on cleaner design. --- apps/contacts/ajax/addcontact.php | 5 +- apps/contacts/css/contacts.css | 32 ++++---- apps/contacts/img/person_large.png | Bin 11517 -> 7929 bytes apps/contacts/js/contacts.js | 74 ++++++++++-------- apps/contacts/js/jquery.combobox.js | 23 +++--- apps/contacts/js/jquery.multi-autocomplete.js | 4 +- apps/contacts/templates/part.contact.php | 73 ++++++++--------- 7 files changed, 113 insertions(+), 98 deletions(-) diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php index 839a391998..947b35bab5 100644 --- a/apps/contacts/ajax/addcontact.php +++ b/apps/contacts/ajax/addcontact.php @@ -39,7 +39,10 @@ foreach ($_POST as $key=>$element) { debug('_POST: '.$key.'=>'.$element); } -$aid = $_POST['aid']; +$aid = isset($_POST['aid'])?$_POST['aid']:null; +if(!$aid) { + $aid = min(OC_Contacts_Addressbook::activeIds()); // first active addressbook. +} OC_Contacts_App::getAddressbook( $aid ); // is owner access check $fn = trim($_POST['fn']); diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 76b5972ba3..9d238c36f3 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -14,21 +14,21 @@ #contacts_propertymenu li:hover { background-color: #1d2d44; } #contacts_propertymenu li a:hover { color: #fff } #actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0;} -#card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ } +#card { width: auto;/*max-width: 70em; border: thin solid lightgray; display: block;*/ } #firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; } #firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; } -#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; } +#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; float: left; } .categories { float: left; width: 16em; } -#card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #f8f8f8; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } +#card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #fefefe; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } #card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; } -input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { background-color: #ffc0c0 !important; } +input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { color: #bbb !important; } dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } -.form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; font-weight: bold; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ } +.form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom; color: #bbb;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ } .form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; } .loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; } - +.float { float: left; } .listactions { height: 1em; width:60px; float: left; clear: right; } .add,.edit,.delete,.mail, .globe { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; display: none; } .add { background:url('../../../core/img/actions/add.svg') no-repeat center; clear: both; } @@ -43,18 +43,18 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } #edit_address_dialog { /*width: 30em;*/ } #edit_address_dialog > input { width: 15em; } #edit_photo_dialog_img { display: block; width: 150; height: 200; border: thin solid black; } -#fn { float: left; } -/** - * Create classes form, floateven and floatodd which flows left and right respectively. - */ -.contactsection { float: left; min-width: 30em; max-width: 40em; margin: 0.5em; border: thin solid lightgray; -webkit-border-radius: 0.5em; -moz-border-radius: 0.5em; border-radius: 0.5em; background-color: #f8f8f8; } +#fn { float: left !important; width: 18em !important; } +#name { /*position: absolute; top: 0px; left: 0px;*/ min-width: 25em; height: 2em; clear: right; display: block; } +#identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ } +/*#contact_photo { max-width: 250px; }*/ +#contact_identity { min-width: 30em; } +.contactsection { position: relative; float: left; /*max-width: 40em;*/ padding: 0.5em; height: auto: border: thin solid lightgray;/* -webkit-border-radius: 0.5em; -moz-border-radius: 0.5em; border-radius: 0.5em; background-color: #f8f8f8;*/ } .contactpart legend { width:auto; padding:.3em; border:1px solid #ddd; font-weight:bold; cursor:pointer; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; } #cropbox { margin: auto; } - -#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; padding: 0.5em; margin: 1em 1em 1em 7em; cursor: pointer; background: url(../../../core/img/loading.gif) no-repeat center center; clear: right; } +#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; padding: 0.5em; margin: 0.3em; cursor: pointer; background: url(../../../core/img/loading.gif) no-repeat center center; display: block; /* clear: right;*/ } #contacts_details_photo:hover { background: #fff; } -#contacts_details_photo_progress { margin: 0.3em 0.3em 0.3em 7em; clear: left; } +/*#contacts_details_photo_progress { margin: 0.3em 0.3em 0.3em 7em; clear: left; }*/ /* Address editor */ #addressdisplay { padding: 0.5em; } dl.addresscard { background-color: #fff; float: left; width: 45%; margin: 0 0.3em 0.3em 0.3em; padding: 0; border: thin solid lightgray; } @@ -72,8 +72,10 @@ dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; } #file_upload_target, #crop_target { display:none; } -#file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; position:absolute; left:0; top:0; cursor:pointer; width:0; height:0;} +#file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; /*position:absolute; left:0; top:0;*/ cursor:pointer; width:0; height:0;} input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; } +.big { font-weight:bold; font-size:1.2em; } +.huge { font-weight:bold; font-size:1.5em; } .propertycontainer dd { float: left; width: 25em; } .propertylist { clear: none; max-width: 28em; } .propertylist li { /*background-color: cyan; */ min-width: 25em; /*max-width: 30em;*/ display: block; clear: right; } diff --git a/apps/contacts/img/person_large.png b/apps/contacts/img/person_large.png index f57511e1000690c223bc5de2313ddfface12f29a..4edba0c5489d1e92b35f9f813189bd8b7c391f17 100644 GIT binary patch literal 7929 zcmW-m1z1yW8^#aau%U#cj2aRmDIlO@v?7j@ZcrMeOHg8floAd^q)V9y3~7*1VbY7< z4DKkOAasps!B;q~6B_(Z=c8l(1OS-3|7~QM;>_Ftz?Z9wyki{vW-}t}h1F!p?6ykK zOsz^kuV4O@bs{V~V^Pmo|3=O4FU;vb?Iq1&?wN_jhA8?muT3trxFTx4(31UwJ(Ule zBEs<(ZKYuiMnyzc`7RdjzKP2 z4*L{R%L5U!`^~dKZTorK;qf~+I={aXZD#5^3`6=WMor_Vw2>6}TfjSS;zx5>j?3~D zpcWVaf+umCMx@!N7xP-GVS?|HBLVdn$;k<=VI&cX=ngDo*cCX{00`(u3_#I9|4)24 zQ-`oiWD+{kA!m7TCS)Zqwn#g*FH7}Q*++wQuZaH){vEFkoH9o>qst!;e#f^1JF^L^ zB@{Hw5^@_&KfW+&eoMF{isOp&<&VO$4#T5-lDtgpmY*OGTAa;A)h~_)@8ee0m=@v$ z7RSwOTjSAhX?92n@UfIInJ4M!nM~CA#>rOmHkRN!Xz|krF%!IytRwR3$P9U~jfR^K zDj{R+q|vvpMuBfTX<9(~-lZ((&QB7rOX*yyNjKPm)3Qf$$D${s?LqA3y`1Mv!i$UF zU!5TjXpjd&Q3GyYsh{6L7CrC}3UchvlBA9Al4hhp&~j>3q%?SPqx62_k`V<_?vvH_ zzP`{#E9TP26QOS|%eH^R&$e8gpSb4iJ#+7fgYF$~U|DiXk7m)Uoo-(>VNga!#&=jO zOI}`{!Q3PR0hg{m6_J>#wg=7}KLn>!1l}HC?2izyHD0_M_$8jT3sVs7I5@fZc3cM83;1R{BY-xp+*+ z)^u}?<5y-%atldQYL`HEG2!A2XF^L7NAzu=VJ1{OaC?SH;962NMgxDk6mjx5A~YG> zEK+yRnO8lsWmNh_HrdpQi3x&s%CBFNlb3g#J%yvM&crnHnt#a*W0%ie2W&>jcYBxc zgJJE@K68lJVxP4?W}bw|I%}ft=KUJotA#X}BLJG-Y>1{66cqf~7SW7NL{WL=h}<2O z5d6|Qd&UnicHIX5+fAOougHN8oF~DKpde+_vMT)I^-&aL{%TbAge`sjFneaee0RbJ zm$#3Upo3k>0hl{dPi zrDbHEZEbBOo?Lnmg>DP~H_(O@T6u^aQJitX*r7T@{vh-gYXUG-^~#|J9j>-r{>ow* z>y`gF=b?o~WqHSRcTAVP$7~yKr=6i0&2$$&e$tYe_|7`>Sm4o^qpdB!g6D5z+z)F= zj{`KzS?Q!Dc2hfL>n8BcrBeo%Q`fRdVqhBqPLIc8hccfQ;12lx9+lA=PF z=_BswT{)82J}dom9kS#W6wiV9bKGif5~tp=4ODQjD!_bc|XH>svA$H2_WmP87Un{yQvbS=Xo#!(NsfF?=8RK7;j_2M&)i8nox6{`Ygx`uTW}+Z78v zS}2k1XY$LV)o{-^dpj^qolDCb(i?2X{Aw;IXnvv?_GGT| zT}jNtEH;{t-P!dCo+<>C9aKFLzHzOq?qj8Dm)fY4jz!>9x|t&^<0>72Pjb$%|lO z2>dcRREg;ppiDHR-u|ma-~LES8l$d5^pa{aQ=2^JCz^+uz zywD0^-bmKPOhiNXXKMvMi6b@tOG79+C_>^l&NHO71sttG=EF+W#M)VCanX#szF56I z((3olu5h>9k7p1V!_~Q&>Q|740dFeA5>z{oFcqA9 zMPTmsKCm38t@s-|sUNo$4mdL#)R(uh^54x@f7({U)Rh%a1|6nRt}mkXrMchS>=4m5 zRJoCCdf>G;9;k`l!jxpJr1Gh*6C6CKe?zka3>t3a4|d7j`bl@ zlO%IvvS^fe)WQ$BWX%|LaS~N`<&CRk*DDss1KVx4Ynrwkfwt#iuYY~=JShrb*S=RL zD3wHZI@0_=FTr2gTDhalVREyoG_g{0{U%jjY{@zcuK?12`CGc6_z%e(y*hCm35z+J zY~dbVlauHl2`nHl=lbvCTY5gYDDqB(kQ#N*U{#Cl=uoxch!Nh(K5ub9q@DUTd!*Zs z*1_fX!IDp& zc_Z~sZ}rC_$QrE-NYN5Mac7a!*IStDG!YX0O|X(TBrmWOWA1952%pp!a#!e&DQsh< zmi6=!tfW>}mReoRCy=%Mn(;Wur+|$KU4ib5AQf2^CL|kqF}wU!V-|7g96Qna^)rY3 zYi(^19d@f4TtY)b>-gebiX>zHn{A(ci8@D_(dz5#8>r4(8($4nH8oZpA+uuH-jynS z?BMLIsUl~fcho-^vGqi9ljgxA0;dM-n0o@gVtg&Ou~EtM4zP5?Dsuh%*=+M*t92}Y zqdIMUb#H2LjYUEGXb!3ZsS;n;+JiU4G!C{-f0D3j^rK4GL8?5XgR>QLku7O`(3@_p z?&WDx#nZ3(a~T;KMH0VCK+p1zXZ+3{<;H~Xbkby#wRCgsD`xl4?Je~?cMSl>XH6R) z{zQ+Y$y;jy3W}3dFmx%_}zyD3Y+lV9^UBNf69<@sNJi` z)$Q%C-@c(0s@dV@&NGkFEKq_maHFs-$Ks6Fxw&24-7&a$NQWFWVtH7b|BRqD8}Q-L z#!aGVe)ygsaFLENYNC$}3}nxivi75&Zib}G#lVdN)8kluEqm46DB)2Pe(7Wfrd2T=EBg%18bYTDShR<-h%@uzJ>p` z=H0XmRuGrwd_KQ-9kwhBF6oVUVX3#gyeus*zvTMB9I#12!Y->C@`=P*PdI7#vaSgk!y@SwU1yYzKWH|BKtuE;y z5O9{3oJG%KIlyGLa{-w^w7;H9EA6|Jw4&mQzSdR9ZCJX`T_*t~VYKsjP77B2N9wi! zC2IoZ-cbu~ffkC-2a%hqx7rXi>UKzeshP{4#}j3zbvc9p;PP_PuC?=w0{xF1fQ zfc&y8=X!`y;dhi@9V^J#+os)x@i4m=F%%BN|85fEb%aLJ8msonNhFdWYr?8b<~gr` z4QE`GOB80Mdy$fIY~h8{n|2hyxFS&+>M!PPO)VZL%wz;XPtxZE7 z4IH1G+_-fMJe>J5J(Z4F|EC-9mBO6g4v!wu2F-+sdNpq<92^{6%`gpkTi%D6|XS>f?)Pl3n3P=kDrzU!V?rHp~H`%EmpxU&6 zrUirZXLFEukB}6|w_qrwBy317g33?Q5@-Cz41Rg%FTwjnBufC6i@9L$V0qs8CulZw z<3mZ{ee9$H!X|99KE~NRpKNE4>{J)+J$7 z{LPo&(7+HfnC{ip@@*a%I8SU@S!R05%F2BMW+x`Mc0ut~-|73-ogJ_Oqz#ZyGY3~@ z)x!^c^j{6u#iZ@-?ixySo-)BgDLb&x;fc^KHb&}yD>yf2FZ!7Y&XQxgYaDrmo0^zd z{*haM6qz}BIMw*%bv~9;xPYQ41DKPC9orWe9)6$eimz&>oIsaC_dr)b^p=c_pGv04 z(Xk|;VkpDqHX{h3y*bKLp*O|*^Bwx5C*ix^2L=atSdEEUTjkU^EXF5Jx%>~P&aNM) z+#^LLd~;f6@gSTVbxEJEbUvra<5_62YBd~Wxp0BN~$z(_JN|RYp z`mlDeFm?sEU)eYLfcZBSP=Ri5I)QUB=%xmR7bmElq@|-eJMVxZ{iF$**i$G=is$0p z__0}4Ofys_!hyKTm8^V+)!N#c?MY_2yR*!des;th*h>KT6h{A5Q+A7b^sV|Z4Y4dmG@8A$E!dA z<|i|}w*1C#R@>UhS{*|BFHp|T&IT@r&z!(@#2&yeORK6TSD9gtQ~HQ^xH^QzmPjQT z>gHBfw@4+|BPy0(WOBb zq}I}3of4Gu5zfDYoDCXh|E{Qr;=2>;ZZJ=$9i-;r`}Yy^Wp9J!`+OrK`9M@?>kLp! zgIClp;7@J&Lstu$n$hlAc<@4b$b%Tjd_SJ2A|S<0Sd^9SU%q4RDn~OAL)|?IS~U;H z2I7jiV!`4B$}W!)4*Ba|Lae@MpJtm3Dt7u<2z_kjfc{dew?b}DcQ++FJG)y>|JLY( z99I%&lzPZ^D@m<#eCR;309-E!l-Fh1n^r&zW4?hlwspG6qXlL{mZW8xju2Q7!bRP8 z-?CUe^(9SW#d)a2N(5Cad#WYi*Xf#UiTIgC#YmD73CzRKfty?+qM}vFzlqPtS_bjg zSNs~c)poltwc&8My56r}i$LiOT#!X=djOT*%9y)_4GkRNlvK(crZ3=%`(_ZMtlodI z_MEh_h=BVwWp?K5>>M07MVhMd>p@9dVg% z>-diAv+CFk7f2aba^w^K(EJ|}Z75d`DXa3w3bOk=vKM~^Q*Z5snqO(Wc#)bqD_(M{ z51h?JAZS5b$quXGM4!(tk?h#T7Y~ezvs}H5Dqk0tVNl`cpWkRWt7@WdYQQR_?jmx24&-x8WQ5AnpCG7X%>@v ze4~}e-TLnmogPqVPp{u4-J%CIH&`~n7zC*R#LT}{j$W?I+5nFUcT3D^rGW{NL|Oo! zzSp_K3XEj?=6zYp9}F?Qub_#vbrPgJ+N?4y-|}?ZvW6j~Ig#+om8;St5t-m3S~XYh*3#-rQs$+iv!p z;>L@9b4{BKVhLkdg{DS}?EXqsUH^ls>aavEKH-^(oA1mKs>!aNX(^?9dD1Mjh^LXN|AL4aLjK1Wq zj1Vi`X0oy1ck3Zyc}ab*=?6`Vsf`WczUChv28`!xb%aoJWImJmY8sMXkabK|-w|ML zKT4CoG=pUdjSuq4PPF)-ml-weQlEO&i`ds_OE69c2&x(PZx5em<_PqpL z0!#e^%+@W>Xm@ie@ssy{n(D>!bEUJBE&|8YuceoIt>)3k6R&pT2gd%8y<2SPTk_Kw zI(Qt@HJA8|#5x~mJExeGVc|4(JnJ!DI?)>NqS|BZwh#-YEDIQi@yz^cR>rI-5e51_ z{AlYt0$I6ibX7&A*0+k%wj51^T3u9WdqW9UxmWH?oI+e&+#V1{fJr0RY3I`&M&|5& z5vU;3JGgj)F7O^Mzo~;WXBbGXy%2d&xOy?Lnp9u+)_`tD#=uq>J zy>CIa6#72%y{WD~nADFTp>x{k`>HSNJewIhK!2V*y5j??FR`lX;Y05^ao#nvkDz3X zMRZO&d5LhP=hU+ewGtpA{(iZ?r;U8Y11!Eml0b_Gv6sQT)rC z^vIf=g7;s3rZU-xGg^VdnU(yfZ5lC%0zQDwMD+7UlAD;6zn#>Q%=;|lvP<;d)G^g? z<2_qa;hWT%O;lgCA$&~m?&#DPeBKO2hIyINFiF6~&a%FaQ;{K~)5sI3Oo0f*D3ESN zF}uvbVj!wjn`tt}RqszhNPfdT^WQ)F8(oQsJC#c?c&;PXEYZKt1gttQC;IU74?39y zl+ad8w1(8)cJ3co^84LHzJv7+@CtTGVGszV%#LH>w1JKUlo}hEyD6KDgU4yas#UCA z`0rGC2bhZ)&fDbzAC1iH&GelMp0CSSu?VrrRD>)THM4O^dxgV&7(N z*S%i}eF@&vpN|*(v=3Mv%(J|4u=YI$>gSP4@!n8W--GQ~<8!fTjq;?Bg1wDnyhh|` zG=?{|XMWxx_L^BIOH-=R<~~ACiOX&|QCDROQ_yO-AO#x6Z(e}c!$|3@m^NM!DDl_w zU)m2T9)0W|5RiFAo=AB)^gM_G$Zjn(f?xg!ssSTsF$b{ty{QRtLwEdO)%wXF2;2uxkKDsUq#k0<*K` zHPLH^*A8Q2sYEf+v#^qGJf2bQbVp_SI01rnL>Y<8FZiCA?2*l{R*4C6{R zWmGiWWM(sbzs|Ds@W0K?0EzTeaiz+gX^o2UV^BDQf;NwY67dmYt>FCn4L|ULab+-n z(qI;e)W>;V6!?PJqu=A;%L1LAi?~X_)zsB3>LhX*c$l+iWoPH@PA4)Fb3vgeEp6z+ zSswrQ<)TPGug|33-W2JH`t#~Pt%KAVcTY#l?iU%tGxEF%3aE+<+1X6Dn>KCXSIX|^ z_nw}%V3o5`ZCt5eFVcWNjVo^n$s=hq&2zFX=9)7B0Rc+I<+~BrG+$bPKF;&!&wtjs z-nX2FuUEMXV^Dhk+9)@lo`?Us%CGs1Yo)voo~d_aKvOM6_s1K!bzF~)*nF|SIH*dR z!s?0HiWFM9t7ga$GR(~+*m+S?M^%6S`}I6eB`ESgV-xv**!QoabO*^sS-xd%LQQ;_ zix21PaJ;Wd93w=D7fq!;Jze-d82PtW9abpd(#B*$9dm6TX9$1yPy#{wAdA-+DkCck zlIzg)^jQtb`xh=*)^*?y$hse18~(D6hl|&y;5aW0`?{RxVV|CDv>S5w>#Y$dZSyb2 z%G8J;C66He8KMkas@GrpDW*kjT>2`$9}*hM1?th&_4N_v-|fSlc#!XcSMq{<*2ly| z=et!6kmLMBIdsA|hh;jK^CCon^k2N}V8*@gE0)T$#}f@&F^~%s?Ji|hVtH?yWAy17x8*By1P^91_9}zg&{=#G{OMVARr*E z@A3QI^|ID**Ua3r@4aWv+535Z=R|3$D-hsQ<6~f85GX0iYJ+1TcunHo1OMHf7mL9G z`c_6s7Z>~m;95n3_jqoKhHo)2-pT!YVGa&K2*HO`9&!dAI<7Vz-WKlG7~bCA-1aVx zZ(m!uS#!I(+vfZfr^djb!%&iaq3e@-nCG4NcrI{TO2XIXEhG|Y!k~#^uLVo^t<+eN zVr7WKz#BrDN@_M2Ub?+5m1c67t!71mD&uwOTrn z6z>t#t6|>6e8VY!J}`K!+eX7RVopHfxd7@;&r>a8u5R@#U^~O~@@aNy1HNOf$eVTc zLY&V~1KU$PH%!WwtAo{s*TWpBrC-+}jmpSYxAlQ27W5p#BxcF4pz;aCkD|3_37iqD zaXu@t@*67tW_`d5-)e~<00US5GRY8qgVZYY9*(DR)xGo;ifiS<)Bx8IIKDx4R4u1w zg!~0<5Q29Mk*>Vtee0j2g zf4mwZg#npyRvXcL`swfeITP>i(YHl6rhfSB@9Q&e~@K(SUgqUjrnofJ`iQa4-VwyC4GI0sP~?p(Zv|%7lhl}+f+0()=o~b zQBiomt^!2IA(=Rm4R5z6TvLs-YRu(`|6L#dm>Hc?GW!re%%CSo!oe!{T3#b zS8?XNing|_d3kwG-`msv{0X|gxlv(WXAKc_E9j{)4*IVPdT@An$A#uFJraQNz@l_x zeZ0O3#;LOCa6kNusx|5OvwYOT#k!Y`=<4d4!nd@uc9qaJ7g4iTXO8|ix9Q2?vsY&AVGZYtOLt)F*x zX^}lOBO_zy^F}S{o+{>4tpF$gQ zkK_~GTwLPyO?|_5ckOvv&7dnij&rq5WDcf-n%+=xzcV|=V@C6i1Bpk^T&$K_oXI@- zJNuNFx7|h6^TYS$)lj z_9TQt{NpP+?7BI!nwlEp!1Ec|4^zo~oRyWy>SZG~P34vNA|w;LI$ET~Q+7$G8=HAC zyyF_d*E-2cyA8Osi%qtZ-X*l!a{pmtCOj5}74lkMO1M~)L_z0_-x5U0_PzDauB^1; zr{d3wC0)y{HB5^e-e7{^%}Iq_J)07Y3k$;zy4|1^kdop#g$La;2H~E-OWlODc+|fP zC(_X(Tb!37hd1&^GDfU2b8;9{MleVh-dL{Q?qX+`@<~Ze&d+BuN98^yrI%}UUShfV ze4*)ME0mDUicVrwC`M9Pje?<<-qIrcuD9@0_OxJ}E_(@4UL!;P1FMk=dujmz0j87@ z28AJlUh~A+S-siXG{V3ji<9p&f)4qJ?9xEp=}*mGYY#rKw%bpaG9=6$4#VMT;HKT% z9!D1&B!5xAZhJUU6TqHk84$4CGS|QNJY0re*m+Twj@T`LB4+88+O*`}nTsQ)kcKo` zI7{aH_wOyg)5spe;mm2uyYcU&MIC1ElgiN-ObUpKR`}1kypfU90uSur?TwO@-}??H z5mG}^m<8}bmeG3LPZFBmY{<_gMx5Y~x8zGd)t)3uuB;sW@#A9^HTfaoy-=s+&q+Sw z+&XszF-P?_TGP;v^{Vn!Mn#_cvq}UNu(|2oPftu6bh=D6f8MnjyC_8tsxz=h%ZNO- z`QGly4{z1>i4cm(W+^PSZ&jKX^YHLcJokzW(kt@fD6vtg(DzwGKV~KU?V_L%5la(U zYA;k1?wF3?={%O9{5(}-{PwNDiO*v>t&jg@F6`8O%s%%!Iyg8;_GQ#d*3=@kY@i$; zALpN0`FzRk2oq9~M(c+Nx5|K&BKO@{T|PiK2fMudi~|S!uFs!W9X-D*8XykF`=sTQ zAr$qB1s!39@2k}#)Bd0PQ3CG$h2EW5aC=|$Zs?^J_3-}V#1vBN_;0-1-ob%^JVq(Z zv|2akWV(oEGaw@Qr%H*ooaekKlt(!IHps9JfAMLYO4z0bv6Q#a1-RNpFp74ToEKns; z{TyV+RC#9$SyUp}NU$#{j-4C5;>7m4h?Z{CAsS)Ffr7<)6P(@GC-2U8nLifPo*i>>#OV~$Yp zCrWWzTk0^E_ylZ+BPKb`dtFg_4i3|qpJ2$=90-FkFr;HPWZ}-N2e(%tC zkU4Sxz`~4X7z*JogKTZ`TQL_t$o-g4@huIX1J6!U4-WTQJIziF(!7CsyibF^FOf}+ zOA~#lY|}&f)DDNW1OC|GO-2vT0P+#nju~?~k_l^JzL0`wVOLa7J#viJG7UlYuGGF3 zDp+M+pY9c;Bz@ZO;Hvs(8hxp%xkSMvN{n7;7xN_nNg_;_cm9GUv#zbT3i_$4NB8Mx z2P{3>Z|w8&9z&-g(#VtHMn*+ZSxz|qs24}=A|fq}1M~FiHkpj@;0qgxepA)(;XF~! zSD$fl)Fh$a>gqim{;ubWpDlf@%w1~UtjDVv5_v^q%TKi+ zQg}w)gNn;$rgKv+{o2`Iw9Yq93$)nha;wJPtmYMf(ZYx!RqN8FT9k22NvFK{+4( zUDuQTu|rnRm|`$!dQaU-Fr%Tv#(Sbt@>J+lmZM5|(_Pf?#uZb9Ri~c1+?p)5)ZP{2 z?@)y*dm8(z>4lNdv%Sd7qgFBfy>6W@{w*2doSAK+4z!tT$Dy#RnI9F%WwJ7!<(03j z?q>xPH_@8fJ%m=<^IZO3ay64?h|E?m zi#eb#BaTTSO~o5dB}r~rdX_I>tJCP9BbJv=&|6*&t9Q!v;7CMmdZhheA+vCO{dC9=i`6~Y+{VwcS#hiq#Tb#KdcjOOOacY7*acr}mej=d|h53>$nX8rJ2 zu~bt?>l7-h4Z-hVOHvd`=U^tqa&PE$kF?vwG&%%h1t?Oh9FtR6{TBR~#(S9$YV5`V z>h4E9a_lQNRZ-!>v}>|qJ!@J0qP$9q0-FdFCKcK`>)nj(IEmZCT5U~YP2>n+DpjU= zxc(!}$3`YiIz4)ukh?FbnEM}ygO1Yyu4a=a8e98hHANa=593y zU0!1h{^{#`cR=?(Tfaw>6)O9UFc(z(g2@gW7NUEh*TmdZmus<^3F9|wZu94&m6dx7 zsZ5%ugt*nb5PTudjYyzWjg%UfVZ79F?X_kbQ-4Cmk`SI2SS!BUd7((TR`(%#Wfvn} z%1`4Rj^Wh;3=<0`txb0aTZqV;LHs()oh+zL`N*ilAdL zxhN4P^@+$>EX!{4IC&ZrL&N$O?Rf6V^|Z0C6^W8+uFwbRqV97GBxQ&d?@bPA}0 zoft6>(BPNz>l+dzk+)}M%FhZIvC{T>;$|`*tZ4?ljZ!VDsCde4+F4#liDto;8|64Q zgNQxkBgF@@Jw5STr8*o=MN4bzpzP;m{jZIbIxbR0n%r$ng)XRCmZ3MCz?JL4k*o|` z`D+fWRgH)NyNjsXCoYpfSdEM*4Gj-lcdTk7rNCIi{x1&N3CvBnklIaUY-o~l2_+Sk zg{5VmRx#CN)F&dB->=(XU)kP_&y9If^`MrQb7K!9O6|?MKpSKKcn56Hmq@u=dO9-P zoUAMhAD?;m`DB{5_g4HFDa?8B?^~}oZmpZHg^sE!M+ajly2@7skL#V*UP+rgbX)BX zqzDzyC6*AGUuIiUWt(6b18A{Gr?0vm^L;xO#LLc5IENN z=kWL95jVdNb7ln(pM8QDk9?}HM_yivfc7as+K@qJhA{Y2xiM>Sozwk>{DGfpb6bPx z=;)X(1{`$<-Z*$c+G#)tjGK!iDSUAjqaX{5kcI}ni2^B}y9Y01Ub{wJwZ}rC(J*{5 zB?&lu#ESm`0m1H%lz~-D4aML#Y8exl+ryswx6&=gx3}UHJf>DYJ_gFgxRe6!=?s`* z(-wrhOKG&!)W2YE;WGHO2}i3vX4y5(@7tNtf9`IYkTgC9L0&;Y`D<$y;5U@0*wAGA zWJO#>5;Y)7e|0d5TfrLXd$KOa!phq9aTW5}|L>nW&oV!3QXD)U~YXSZ;*J z<_6v{B3ZkZ1)e=qFf%ioO5pmCEazhSOsJAA`sYbDye^HMJb9ttFBqc^<8LpDpgFRg zl@1w#h4bfe0(~S%K@+*6{a_xKINOx5j5qxplhq})R-&zI$fasCjz8U=+?Sw9&V;&% zxuFrPZwzA$9zu-=jv&DtMh4S@Ha0dQZ?{yZAQ|d}c=IHxNcJE*1x~eTk>Ed_-DCAk zy66ck9707d3*-HA|3_aVUq%qhAxzPXjTdauGDb#^j9Q!wVHa38FCcnRkg#a4_Q}ZJ z-WT!;3f4GT_{+h^=-&1{m$!Y<40On%ml4?^M_5P6U97~oUuS2Bw#IWLY6`8#rXN1I zS3_05MTqvo}^=_GWsdj}a&nge)gKxmn1D-Run5JCf zb2|R4tNI8h%>2~<-&I;H394Xw_R^KCf zvrnwQ1}*uhsj2S)u%bmFu&mr0orZ|n!BJ#_2?`7Qwew}5_{7D>c6RI*;y#0^L_$Jh zdXJKrFv2i(MJUGxMO5vB$L0kBeAzzDZ|`Va-~?F!8v0^!aZ%X;aqJ@z>%anfWpR;R ze4k7WnZ}KI6N>{9?7+qw4YD7nzJN7k<#~=%w?MO0!@IjBHGb%jh5@(sTHHMi;Tg-^ zpZe<6tH?pieD;w|cfX#717dDCT-5(}^;{AB)2C0Q1m<#<&I5i!%B+Rfwt*@tDpLo~ zs6e;c%S1_Z(e5RF;mgj_O__+FQ&M-xB*MP zzP0+5zd1k-LEi!6nE#j%;UlgNV8ToZ95T!2Y2a<_Im|Y?7_WZttIDdmr($x`1^P0nEhhM+^5vu$d$b8CGu;*yF-KxJBE?fyj%SZ zRV~p5L*UI}^PFfvCF-GrPoII6;(n_Wp;bCkVXK5}m-3a|35|@o`MgWel0C zzKY6y;7uE2Seo@aU%&^HiTXb)OE-OQ1>->SS5m^))zwKP5;3QRir?AUK{ukfKXi0F zlGCzM(5i#gn*zCXua#dfemGmm(V)qO;Gg;L@9#f5Kj-7+MccEbNrTA=o&eZd3heAb zQjMtmWYA}f@ifo9?$@u@9Y-b7syDyOZ^C+sT*IY9i zk)>Mt#ojIDRikLsqeqV<;?glC@jJc1X$m1chbvu6H*|y4FTbm!{*fhI5sX{H<9O}8 zoxQ#FQI0x~@kU7@p=PTY_STEpsi`%wpS*eMr{7d~e5valkZi}q$7$a?yyu)k$~?^?^74rdPDMMb!nLu&~W@C)Ce7Az|V6d(Ed;;$X6MUTx(( zdDRVliKIZ(9WFEtFYU}xOMfT+7ipi3_^1_ezWLwG)Ku^9!=o`ST*01;L>aMPs zgp~eajyG7?_7`tPHJjw25KMr6hOW=|CF1-5uQX`HPbaIceI_V4A20NYy>`+@EuTXUphSO&3~C zPe}K1-~C5V726ZAGOi811*tcqIcf{5s;VNH8kfe#RHxd%s>i6kdZ9&1{pitoMWGFB zagd~_{@uP~UDpsuq6KwU_bwgL9M6{BUaFl{>xLmFrTzvG&&$17JRDH4#|gV!0TpQX z=g$^ZVaE2!+)~|%u`$vLQz{sBgjdugNEkq&Os%KsK}mi0#_&EV$jgg(?xog+GLWA* z<7=N>J#y`A;5;gO%*laS5d7zgfK>5(R619+SUT8-bt_UQvcLig*lU3Upu3o&vVxiG?CKM^r%b=gc-;eJEFe!skO^z2L!SWVVqx&o1w zQDYZ5ndj@7Bk77v(f>6l#?CJUL+FEJrsK^Vm-gL)yV_+! zGDh@Tf5dB~2{IU~wst@uJ3D*6-ID{b>~8}CGFJcP=H)R18>dp1HN%H4kB<9YlI5{= zXD3*R(<4{^%F7eZ_%I?o{GXnv=wfrZR6uJWY(ebOXmM$zH$?Jc`K<)f74r9?Tf#s( zqS(?r}^=a(F5mvFk3kR7ALKR>o|T>nHBkY%VLZ3_au2##+^bMKoe%z^CK-rhc6ZxQe$IxcW!MWKptqj*cHd(qp4)DP{wecBvw(u_vy<7@$l$ zyGX#`C+{;Vt>YdqbopQ8pM7Xd#=AUdniR&kz_@^Pb8-=b8+36gEhz~DJ@;*S+4$T0 zH_vmF6It`rm;6$OF8G9m27zV=e#eYT#Y&AoLx_auNdh47s{a1|Q-x5D2>A#11^)C} z1yPjoUj^j<)7SYD``(-n@kiXd@oraEy8Kbmnf@$jfk|(AmOfsD0 z(>)z?UW;j2`T1MRZEyW5U(qIE|9N(NW#rOk{W%q1{NZtyZN5ELK9;1xXg_qoj|~)6 zK|w*{m_Z3@)B&VLhKOdKa)Qiks%TA*HbuB7**NubRF z^HK+$;L^59uCcC{jnI&~JF`aenRm?1;sNw6_WZfMt1Bxk>g(69(hNy%2aH@{ZV;f2 z10;{`FN_VR%|CmB1EeILNSSb3UMu^DN_JpwKq)0V_q;&_W4d&n&l;^JcD;!Hf02cd zTFBGn6U)HOi3xRB6m-f?_lOL`_nl~(fGNptla=2CcCPW;dElVi{Vsk5T1~vPpqxh{ zF+xK_nbn>*BL(aK&KkK&FzC#uAv7e;|8qebAvX7-yl}&0s*PIXfB*K9DN5|t?)om$ z>CRlZGeXQWwOr-^@6P{CCP)96kR(ptM8WNOUMok+h}A#wcXxNk$(h~c^oja*rxCoHR)^1O-V;!avGjM}P0o%{dXC&`5 zn{4{B;7(7^4SR_u^;?$R4`NDQRQtt^Hd71_5BuJo##TC)pzXS5P0cZW&m z(|06(^gtjI9$6TwWRVK@?6PLza@9ur)^7Kv=5Q!elEyjq;5*C*gpvPlrVbMEl^s6$cE3uyRbF*AHLT|gW zraiAVDzK=iZ|yj_AxiI@YLX*bh6$ELh0*ceh*?Rx>S=;rv9E>23!;ikvI?G=t(2O% z{c}I&5|cJ3VHN(Y{}ebzvIU}UI+$uPFdn%4+Y2CTIRf({H|VQLc0wvLtvg(7o?a!} zI*llbNk#6!uSfGQUcz${GI9znxekVg{72Jy2)(c|H^xG{OjW9~;LHbY z)l79xhK8w*6r=e@+)sCPXFH`NB-D(ImS8EU2O~`A38=WojwZ?1O}&^Tgcm&}6{Nk0 zG-r}Xh{q^{qQcIzJG*jGWx<$xoBBB^y(onTjhILIF1k1L8lq%_$42%JEQ;3>k=(Vq zxG@@*I7za8X<6ED*2ZOfYgVFUcW#uOU|mYgX(@2rD8j~27<$lpycP9A zR?11H!*O#y5#{UTInIW8NcTFOt1lm+s4Q*BI(Xg;Gcf< zu9t4j&47heXMk@KrWxz^R1OghY}k!_E09wAPhQeD_6E=j|WNLkkm z3Cspu(rgP;^8TN4^Fn@YPTiXx@>Zi)YSLA2O%ukq4;I;;AFwAWeo=kLvz}>-m#N6~ zSb^#OXSIlG?3_u0v`=_l`DWY|cG!jy{7({4sT}K>l0i57W`F%KqOg0%`|AY9{lc_f zNOz38glWGeG-)6WpN=jKay8B0*7Fiwa=!47Z2kPxRRgd?O zVPVL2p#io^gn4y3i$Gs_(04_)QM>G+`FYA5VdrEP*@}Uu6hWUR?_V8Epz;MQgMzy4 zEHDg#1D{)FfATtH5c(gJd@sTicSx1LX`Jq5A20-~_c(R&O&9g>uX1bCWP~|@29cMS z|9qzHiicru41`8H86N!HSQ^nZQB~f$!Ocp zIa&eonBAz>F2xsA3bJE5=>}6~+u2$Wzu$-&H0gWcr2vQ}+^>niV!5ul_Otd82b}*p z%z(4Btms%-nhGeGtSDs_yvX;^R4Vrm_P_b0UsEv_m6nxh!Dpr-o5jrH@)OfDYCvKj=emD4+aS-6=wz7GbLN=4f!O%6>E3=;RaRq4ztU_(t|?$;nVq}=c}^j! zPY0G~a;!~1t8SX&iAYH$l-lR_L#o(lz$z&mo>fqA;ZZQ=y8j~4DZJHB=A$Jdjjm9U zkg;k-UrX{m3rTR0;g{Q%WEe5IC-&AHx5{%rNh50b#QN7;dk6p?-|#bhjTODguvM55gOtrvWc*8 z3&UPVADNHxT+ENYv5$sEwne8}o34krEGoN)8&8sGh(COwMM=Gp-LGG&UH7|ShwsfJ z>jwP40B3uo0J*51jo|NZWXj9tt>-JWUnHmM;+~e~feLPInJ}Y&F_Pl%9<90x9nh11 zW_}+*S(pPobNCw-T)33l1HVjg6RB1a;E`b9w@C*#MW01!&PJNtI1QHcO38jL)hZ+R zz=jZFT&N~IO_k*(d)lv>4=;Z3CS?^r@x>6Jx%H!%K8{ zzxiV!PA(@qxOesopzZ$cNKZd2}60kkOB8_`+ zFH|loc8nNG%EakO)e27~d~4cub>)k4i!EjYR154hD4T#R%rwemmo|5#H7a)>+?%q4 z!(-gffuq}L+Q!D?X!AN_uLcWiT5xzl1Q2+Dm;tdx$TlAdKmt2oG1+qZTO(P2jOpSj zz0B%#(j7S&IKO=P5peicS`$_ zCCUBQt*R3zL&z0~`2X=%0VmZHJCKR|KL-WS+|wgKkN>$O8XA+viJ0R7Sx&d{Y@zGG^{^;J1#hJFG_pI- zJV?WmWP9w$$;~bCJ_R=?eJPzWONs>;myZUn#+y^0L8% zmlyor_OC2pSKuQOz$}3Ybxiuf&rmS=c593(aIr)yn<|gk;+e7UW?HT2)`7N4mek?I z3}fu-($DGp_i%7BZpn-FWuaxZwzi_cvgap{r7N@3O7#ZcP(1YC+sNLx@&@)`;5;WI`tA7s7BG!F- z3krF;k5_#wl%@r_jXxv7vW>m7^UP^H)kd()DAi2e%r-||r;MYcqf*tg5v0@pCEf;+ zRuzj3d)j)0)U8F*M*(@p*zKZY39uIi*jQCn)x>cdr4nua7)`M?eYxQcv8<^Dkv;e)~K0S*5 z?a_gKWL?0XPQkb5`58a%2i=|wcZ852f4diyvPI|nt7?%-*cT;5>J0-R5OCQ~TWhGK zop8A@88ie)h}K8b!$?-Y?r*!F3hvHaF`i4_?P?SVl4O^jEIQVm{6g_8!PLQ(jBL0wss_-B+lbZ6vofhJ zD|8VvCT92LAP+kd3%T8(z6&RZp8UEJ&r+4Y7>H$x9o{hZKWzR7U5L0;bGkC7Brb#)kim?mkMonSlD zUsvoC6Z9YdpKaUBS$*HYqQybOuovVniBIcA%N3TKmYtp5*H)kz0ciwwn8Q?g%Uu-k z@vNMj*Ir)fAlU>~xg+na(cU0J{%cd0U#ijw1=>JCjDrv6v@XAlUtrUR2g`lXGTF>b z7z`45T-(kxL$2^1EIdmn)cLOZ9K2*3aLp|--wxamD7cE<9!isy4*3@VHy{&2bphdG z*4M+1-75MFGWP-A3y8hfL)=zrOc}##lk~+jGsx(T7b z#_Z*08GP5FCp7AMyi9fG4A-#Y`@%SSZ&9QWd2ktUxQBz+2zS>|`R z{Iv9}AXFxnv+Hw-YJu0yQ*%N`_c)1TB$m+JrWW39@LjoH!-I?BEU;A>LrG3uwpPac G-Twfpl}P*m diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index d314878cc0..e838d713d4 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -65,7 +65,7 @@ Contacts={ propertyTypeFor:function(obj) { return $(obj).parents('.propertycontainer').first().data('element'); }, - showHideContactInfo:function() { + /*showHideContactInfo:function() { var show = ($('#emaillist li.propertycontainer').length > 0 || $('#phonelist li.propertycontainer').length > 0 || $('#addressdisplay dl.propertycontainer').length > 0); console.log('showHideContactInfo: ' + show); if(show) { @@ -73,8 +73,8 @@ Contacts={ } else { $('#contact_communication').hide(); } - }, - checkListFor:function(obj) { + },*/ + /*checkListFor:function(obj) { var type = $(obj).parents('.propertycontainer').first().data('element'); console.log('checkListFor: ' + type); switch (type) { @@ -101,7 +101,7 @@ Contacts={ case 'BDAY': break; } - }, + },*/ loading:function(obj, state) { if(state) { $(obj).addClass('loading'); @@ -137,18 +137,14 @@ Contacts={ $(this).find('.add').fadeOut(500); } );*/ - $('.button,.action').tipsy(); - $('#contacts_deletecard').tipsy({gravity: 'ne'}); - $('#contacts_downloadcard').tipsy({gravity: 'ne'}); //$('#fn').jec(); $('#fn_select').combobox({ 'id': 'fn', 'name': 'value', - 'classes': ['contacts_property'], + 'classes': ['contacts_property', 'huge', 'tip', 'float'], + 'attributes': {'placeholder': t('contacts', 'Enter name')}, 'title': t('contacts', 'Format custom, Short name, Full name, Reverse or Reverse with comma')}); //$('.jecEditableOption').attr('title', t('contacts','Custom')); - $('#fn').tipsy(); - $('#contacts_details_photo_wrapper').tipsy(); $('#bday').datepicker({ dateFormat : 'dd-mm-yy' }); @@ -200,6 +196,9 @@ Contacts={ } ] ); $('#categories').multiple_autocomplete({source: categories}); + $('.button,.action,.tip').tipsy(); + $('#contacts_deletecard').tipsy({gravity: 'ne'}); + $('#contacts_downloadcard').tipsy({gravity: 'ne'}); Contacts.UI.loadListHandlers(); }, Card:{ @@ -267,7 +266,7 @@ Contacts={ import:function(){ Contacts.UI.notImplemented(); }, - add:function(n, fn, aid){ // add a new contact + add:function(n, fn, aid, isnew){ // add a new contact console.log('Add contact: ' + n + ', ' + fn + ' ' + aid); $.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid }, function(jsondata) { @@ -291,7 +290,15 @@ Contacts={ if(!added) { $('#leftcontent ul').append(item); } - + if(isnew) { + Contacts.UI.Card.addProperty('EMAIL'); + Contacts.UI.Card.addProperty('TEL'); + Contacts.UI.Card.addProperty('BDAY'); + Contacts.UI.Card.addProperty('NICKNAME'); + Contacts.UI.Card.addProperty('ORG'); + Contacts.UI.Card.addProperty('CATEGORIES'); + $('#fn').focus(); + } } else{ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); @@ -356,7 +363,7 @@ Contacts={ return false; }, loadContact:function(jsondata){ - $('#contact_communication').hide(); + //$('#contact_communication').hide(); this.data = jsondata; this.id = this.data.id; $('#rightcontent').data('id',this.id); @@ -368,7 +375,6 @@ Contacts={ this.loadPhones(); this.loadAddresses(); this.loadSingleProperties(); - // TODO: load NOTE ;-) if(this.data.NOTE) { $('#note').data('checksum', this.data.NOTE[0]['checksum']); $('#note').find('textarea').val(this.data.NOTE[0]['value']); @@ -376,7 +382,7 @@ Contacts={ } else { $('#note').data('checksum', ''); $('#note').find('textarea').val(''); - $('#note').hide(); + //$('#note').hide(); } }, loadSingleProperties:function() { @@ -521,17 +527,18 @@ Contacts={ },*/ editNew:function(){ // add a new contact this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; - $.getJSON(OC.filePath('contacts', 'ajax', 'newcontact.php'),{},function(jsondata){ + Contacts.UI.Card.add(';;;;', '', '', true); + /*$.getJSON(OC.filePath('contacts', 'ajax', 'newcontact.php'),{},function(jsondata){ if(jsondata.status == 'success'){ id = ''; $('#rightcontent').data('id',''); $('#rightcontent').html(jsondata.data.page); - Contacts.UI.Card.editName(); + //Contacts.UI.Card.editName(); } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert(jsondata.data.message); } - }); + });*/ }, savePropertyInternal:function(name, fields, oldchecksum, checksum){ // TODO: Add functionality for new fields. @@ -627,8 +634,8 @@ Contacts={ },'json'); } }, - addProperty:function(obj){ - var type = $(obj).data('type'); + addProperty:function(type){ + //var type = $(obj).data('type'); console.log('addProperty:' + type); switch (type) { case 'PHOTO': @@ -647,21 +654,21 @@ Contacts={ $('#emails').show(); } Contacts.UI.Card.addMail(); - Contacts.UI.showHideContactInfo(); + //Contacts.UI.showHideContactInfo(); break; case 'TEL': if($('#phonelist>li').length == 1) { $('#phones').show(); } Contacts.UI.Card.addPhone(); - Contacts.UI.showHideContactInfo(); + //Contacts.UI.showHideContactInfo(); break; case 'ADR': if($('#addressdisplay>dl').length == 1) { $('#addresses').show(); } Contacts.UI.Card.editAddress('new', true); - Contacts.UI.showHideContactInfo(); + //Contacts.UI.showHideContactInfo(); break; case 'NICKNAME': case 'ORG': @@ -682,8 +689,8 @@ Contacts={ if(jsondata.status == 'success'){ if(type == 'list') { Contacts.UI.propertyContainerFor(obj).remove(); - Contacts.UI.showHideContactInfo(); - Contacts.UI.checkListFor(obj); + //Contacts.UI.showHideContactInfo(); + //Contacts.UI.checkListFor(obj); } else if(type == 'single') { var proptype = Contacts.UI.propertyTypeFor(obj); console.log('deleteProperty, hiding: ' + proptype); @@ -718,8 +725,8 @@ Contacts={ } else { // Property hasn't been saved so there's nothing to delete. if(type == 'list') { Contacts.UI.propertyContainerFor(obj).remove(); - Contacts.UI.showHideContactInfo(); - Contacts.UI.checkListFor(obj); + //Contacts.UI.showHideContactInfo(); + //Contacts.UI.checkListFor(obj); } else if(type == 'single') { var proptype = Contacts.UI.propertyTypeFor(obj); console.log('deleteProperty, hiding: ' + proptype); @@ -891,7 +898,7 @@ Contacts={ if(isnew) { container.remove(); } - Contacts.UI.showHideContactInfo(); + //Contacts.UI.showHideContactInfo(); } }, close : function(event, ui) { @@ -900,7 +907,7 @@ Contacts={ if(isnew) { container.remove(); } - Contacts.UI.showHideContactInfo(); + //Contacts.UI.showHideContactInfo(); }/*, open : function(event, ui) { // load 'ADR' property - maybe :-P @@ -973,7 +980,7 @@ Contacts={ } }, loadPhoto:function(force){ - if(this.data.PHOTO||force==true) { + //if(this.data.PHOTO||force==true) { $.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){ if(jsondata.status == 'success'){ //alert(jsondata.data.page); @@ -986,11 +993,11 @@ Contacts={ }); $('#file_upload_form').show(); $('#contacts_propertymenu a[data-type="PHOTO"]').parent().hide(); - } else { + /*} else { $('#contacts_details_photo_wrapper').empty(); $('#file_upload_form').hide(); $('#contacts_propertymenu a[data-type="PHOTO"]').parent().show(); - } + }*/ }, editPhoto:function(id, tmp_path){ //alert('editPhoto: ' + tmp_path); @@ -1430,7 +1437,8 @@ $(document).ready(function(){ } }); $('#contacts_propertymenu a').live('click',function(){ - Contacts.UI.Card.addProperty(this); + var type = $(this).data('type'); + Contacts.UI.Card.addProperty(type); $('#contacts_propertymenu').hide(); }); }); diff --git a/apps/contacts/js/jquery.combobox.js b/apps/contacts/js/jquery.combobox.js index 6da4ecb514..f46d7c14c1 100644 --- a/apps/contacts/js/jquery.combobox.js +++ b/apps/contacts/js/jquery.combobox.js @@ -72,17 +72,10 @@ .appendTo( ul ); }; - this.button = $( "" ) + /*this.button = $( "" ) .attr( "tabIndex", -1 ) .attr( "title", "Show All Items" ) .insertAfter( input ) - /*.button({ - icons: { - primary: "ui-icon-triangle-1-s" - }, - text: false - }) - .removeClass( "ui-corner-all" )*/ .addClass('svg') .addClass('action') .addClass('combo-button') @@ -99,7 +92,7 @@ // pass empty string as value to search for, displaying all results input.autocomplete( "search", "" ); input.focus(); - }); + });*/ $.each(this.options, function(key, value) { self._setOption(key, value); }); @@ -123,17 +116,23 @@ case "id": this.options['id'] = value; this.input.attr('id', value); - break; + break; case "name": this.options['name'] = value; this.input.attr('name', value); - break; + break; + case "attributes": + var input = this.input; + $.each(this.options['attributes'], function(key, value) { + input.attr(key, value); + }); + break; case "classes": var input = this.input; $.each(this.options['classes'], function(key, value) { input.addClass(value); }); - break; + break; } // In jQuery UI 1.8, you have to manually invoke the _setOption method from the base widget $.Widget.prototype._setOption.apply( this, arguments ); diff --git a/apps/contacts/js/jquery.multi-autocomplete.js b/apps/contacts/js/jquery.multi-autocomplete.js index 7607de3f91..e1c5d63dc5 100644 --- a/apps/contacts/js/jquery.multi-autocomplete.js +++ b/apps/contacts/js/jquery.multi-autocomplete.js @@ -62,7 +62,7 @@ return false; } }); - this.button = $( "" ) + /*this.button = $( "" ) .attr( "tabIndex", -1 ) .attr( "title", "Show All Items" ) .insertAfter( this.element ) @@ -86,7 +86,7 @@ // pass empty string as value to search for, displaying all results self.element.autocomplete( "search", "" ); self.element.focus(); - }); + });*/ }, }); })( jQuery ); diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index d243c2b5e1..961ce693e6 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -21,12 +21,12 @@ $id = isset($_['id']) ? $_['id'] : ''; -
+
-
+ +
+ -
-
+
-
- -
-
- -
+ + + + +
- + - + - - - + + +
-
- +
+
+
+ + +
+
+
+
From fc555b48ec0f5600a7f9ae25354ffaca2e0533de Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 3 Apr 2012 07:29:47 +0200 Subject: [PATCH 097/133] Contacts: Added autocomplete using geonames.org. --- apps/contacts/css/contacts.css | 1 + apps/contacts/js/contacts.js | 88 +++++++++++++++++++++++++++- apps/contacts/templates/settings.php | 1 + 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 9d238c36f3..4e7d8c285a 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -28,6 +28,7 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } .form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; } .loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; } +.ui-autocomplete-loading { background: url('../../../core/img/loading.gif') right center no-repeat; } .float { float: left; } .listactions { height: 1em; width:60px; float: left; clear: right; } .add,.edit,.delete,.mail, .globe { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; display: none; } diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index e838d713d4..b72f3c9a67 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -908,10 +908,92 @@ Contacts={ container.remove(); } //Contacts.UI.showHideContactInfo(); - }/*, + }, open : function(event, ui) { - // load 'ADR' property - maybe :-P - }*/ + $( "#adr_city" ).autocomplete({ + source: function( request, response ) { + $.ajax({ + url: "http://ws.geonames.org/searchJSON", + dataType: "jsonp", + data: { + featureClass: "P", + style: "full", + maxRows: 12, + name_startsWith: request.term + }, + success: function( data ) { + response( $.map( data.geonames, function( item ) { + /*for(var key in item) { + console.log(key + ': ' + item[key]); + }*/ + return { + label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName, + value: item.name, + country: item.countryName + } + })); + } + }); + }, + minLength: 2, + select: function( event, ui ) { + if(ui.item) { + $('#adr_country').val(ui.item.country); + } + /*log( ui.item ? + "Selected: " + ui.item.label : + "Nothing selected, input was " + this.value);*/ + }, + open: function() { + $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" ); + }, + close: function() { + $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); + } + }); + $( "#adr_country" ).autocomplete({ + source: function( request, response ) { + $.ajax({ + url: "http://ws.geonames.org/searchJSON", + dataType: "jsonp", + data: { + /*featureClass: "A",*/ + featureCode: "PCLI", + /*countryBias: "true",*/ + /*style: "full",*/ + maxRows: 12, + name_startsWith: request.term + }, + success: function( data ) { + response( $.map( data.geonames, function( item ) { + for(var key in item) { + console.log(key + ': ' + item[key]); + } + return { + label: item.name, + value: item.name + } + })); + } + }); + }, + minLength: 2, + select: function( event, ui ) { + /*if(ui.item) { + $('#adr_country').val(ui.item.country); + } + log( ui.item ? + "Selected: " + ui.item.label : + "Nothing selected, input was " + this.value);*/ + }, + open: function() { + $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" ); + }, + close: function() { + $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); + } + }); + } }); } else { alert(jsondata.data.message); diff --git a/apps/contacts/templates/settings.php b/apps/contacts/templates/settings.php index f56de0ec8b..d77c4d3802 100644 --- a/apps/contacts/templates/settings.php +++ b/apps/contacts/templates/settings.php @@ -9,4 +9,5 @@
/principals//
+ Powered by geonames.org webservice From dd4e577f6db422aed3d528d12a8e77de9c3cfaef Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 3 Apr 2012 08:13:10 +0200 Subject: [PATCH 098/133] Contacts: l11n for geo autocomplete. Not sure if it works though. --- apps/contacts/js/contacts.js | 4 +++- apps/contacts/templates/index.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index b72f3c9a67..cc4da5837e 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -919,6 +919,7 @@ Contacts={ featureClass: "P", style: "full", maxRows: 12, + lang: lang, name_startsWith: request.term }, success: function( data ) { @@ -937,7 +938,7 @@ Contacts={ }, minLength: 2, select: function( event, ui ) { - if(ui.item) { + if(ui.item && $('#adr_country').val().trim().length == 0) { $('#adr_country').val(ui.item.country); } /*log( ui.item ? @@ -961,6 +962,7 @@ Contacts={ featureCode: "PCLI", /*countryBias: "true",*/ /*style: "full",*/ + lang: lang, maxRows: 12, name_startsWith: request.term }, diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php index af159ce9c6..d68dd68f60 100644 --- a/apps/contacts/templates/index.php +++ b/apps/contacts/templates/index.php @@ -1,6 +1,7 @@
From cd05dfb9430da93f2584d928ef963c625562c8fa Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Tue, 3 Apr 2012 22:17:34 +0200 Subject: [PATCH 099/133] adding icons to filepicker --- core/js/oc-dialogs.js | 2 +- files/ajax/rawlist.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index d40c433bda..7ae9bc9d2a 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -204,7 +204,7 @@ OCdialogs = { var entry_template = '
*NAME*
*LASTMODDATE*
'; var names = ''; for (var a in r.data) { - names += entry_template.replace('*LASTMODDATE*', OC.mtime2date(r.data[a].mtime)).replace('*NAME*', r.data[a].name).replace('*MIMETYPEICON*', OC.webroot+'/core/img/filetypes/'+(r.data[a].type=='dir'?'folder':r.data[a].mimetype.replace('/','-'))+'.png').replace('*ENTRYNAME*', r.data[a].name).replace('*ENTRYTYPE*', r.data[a].type); + names += entry_template.replace('*LASTMODDATE*', OC.mtime2date(r.data[a].mtime)).replace('*NAME*', r.data[a].name).replace('*MIMETYPEICON*', r.data[a].mimetype_icon).replace('*ENTRYNAME*', r.data[a].name).replace('*ENTRYTYPE*', r.data[a].type); } $(dialog_content_id + ' #filelist').html(names); }, diff --git a/files/ajax/rawlist.php b/files/ajax/rawlist.php index 0abe81e672..88ba48a6c4 100644 --- a/files/ajax/rawlist.php +++ b/files/ajax/rawlist.php @@ -5,6 +5,7 @@ $RUNTIME_APPTYPES=array('filesystem'); // Init owncloud require_once('../../lib/base.php'); +require_once('../../lib/template.php'); OC_JSON::checkLoggedIn(); @@ -15,6 +16,7 @@ $dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; $files = array(); foreach( OC_Files::getdirectorycontent( $dir ) as $i ){ $i["date"] = OC_Util::formatDate($i["mtime"] ); + $i['mimetype_icon'] = $i['type'] == 'dir' ? mimetype_icon('dir'): mimetype_icon($i['mimetype']); $files[] = $i; } From 95c2ac5d585bfd1106b173c00b4c29719c08b25f Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 3 Apr 2012 21:14:55 +0000 Subject: [PATCH 100/133] Dont typecast variables as integers --- core/lostpassword/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lostpassword/index.php b/core/lostpassword/index.php index da0428e3ce..9529c0c957 100644 --- a/core/lostpassword/index.php +++ b/core/lostpassword/index.php @@ -12,7 +12,7 @@ require_once('../../lib/base.php'); // Someone lost their password: if (isset($_POST['user'])) { if (OC_User::userExists($_POST['user'])) { - $token = sha1($_POST['user']+uniqId()); + $token = sha1($_POST['user'].uniqId()); OC_Preferences::setValue($_POST['user'], 'owncloud', 'lostpassword', $token); $email = OC_Preferences::getValue($_POST['user'], 'settings', 'email', ''); if (!empty($email)) { From acdce2b1e01f7c0a77b7e7949540e1b0ba94efd1 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Tue, 3 Apr 2012 22:31:34 +0000 Subject: [PATCH 101/133] Check blacklist before saving to filesystem --- lib/base.php | 3 +++ lib/filesystem.php | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/base.php b/lib/base.php index a4a94e8696..83dd0c98f4 100644 --- a/lib/base.php +++ b/lib/base.php @@ -365,6 +365,9 @@ class OC{ OC_App::loadApps(); } } + + // Check for blacklisted files + OC_Hook::connect('OC_Filesystem','write','OC_Filesystem','isBlacklisted'); //make sure temporary files are cleaned up register_shutdown_function(array('OC_Helper','cleanTmp')); diff --git a/lib/filesystem.php b/lib/filesystem.php index 12905d189f..b6909f5acd 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -298,6 +298,19 @@ class OC_Filesystem{ } return true; } + + /** + * checks if a file is blacklsited for storage in the filesystem + * @param array $data from hook + */ + static public function isBlacklisted($data){ + $blacklist = array('.htaccess'); + $filename = strtolower(basename($data['path'])); + if(in_array($filename,$blacklist)){ + $data['run'] = false; + } + } + /** * following functions are equivilent to their php buildin equivilents for arguments/return values. */ From 60e3b563e26478eab257413b5cac9b3f619570ac Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 4 Apr 2012 12:41:32 +0200 Subject: [PATCH 102/133] webdav needs to load authentication apps --- apps/user_ldap/appinfo/info.xml | 3 +++ apps/user_openid/appinfo/info.xml | 3 +++ files/webdav.php | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml index 9a6ee1436f..99830dd1ff 100644 --- a/apps/user_ldap/appinfo/info.xml +++ b/apps/user_ldap/appinfo/info.xml @@ -7,4 +7,7 @@ AGPL Dominik Schmidt 2 + + + diff --git a/apps/user_openid/appinfo/info.xml b/apps/user_openid/appinfo/info.xml index 37be15abfd..721db1877e 100644 --- a/apps/user_openid/appinfo/info.xml +++ b/apps/user_openid/appinfo/info.xml @@ -7,4 +7,7 @@ AGPL Robin Appelman 2 + + + diff --git a/files/webdav.php b/files/webdav.php index 1120973787..25e3302447 100644 --- a/files/webdav.php +++ b/files/webdav.php @@ -27,7 +27,7 @@ $RUNTIME_NOSETUPFS = true; // only need filesystem apps -$RUNTIME_APPTYPES=array('filesystem'); +$RUNTIME_APPTYPES=array('filesystem','authentication'); require_once('../lib/base.php'); From 85f9869f6925ef52c1015916bbc28e13c15abc73 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Wed, 4 Apr 2012 13:17:03 +0000 Subject: [PATCH 103/133] Make the token really random --- core/lostpassword/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lostpassword/index.php b/core/lostpassword/index.php index 9529c0c957..a9b7d10804 100644 --- a/core/lostpassword/index.php +++ b/core/lostpassword/index.php @@ -12,7 +12,7 @@ require_once('../../lib/base.php'); // Someone lost their password: if (isset($_POST['user'])) { if (OC_User::userExists($_POST['user'])) { - $token = sha1($_POST['user'].uniqId()); + $token = sha1($_POST['user'].md5(uniqid(rand(), true))); OC_Preferences::setValue($_POST['user'], 'owncloud', 'lostpassword', $token); $email = OC_Preferences::getValue($_POST['user'], 'settings', 'email', ''); if (!empty($email)) { From 5b4a30367235a27f21d44b98eaae953e1cec1eac Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Thu, 5 Apr 2012 21:29:54 +0200 Subject: [PATCH 104/133] loading screen for filepicker --- core/css/styles.css | 1 + core/js/oc-dialogs.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/css/styles.css b/core/css/styles.css index 1c50df9e58..0eb299a899 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -137,3 +137,4 @@ a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padd #dirtree {width: 100%;} #filelist {height: 270px; overflow:scroll; background-color: white;} .filepicker_element_selected { background-color: lightblue;} +.filepicker_loader {height: 270px; width: 100%; background-color: #333; opacity: 0.3; visibility: visible; position:absolute; top:0; left:0; text-align:center; padding-top: 150px;} diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 7ae9bc9d2a..e027ffb510 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -100,7 +100,7 @@ OCdialogs = { filepicker:function(title, callback, multiselect, mimetype_filter, modal) { var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content'; var c_id = '#'+c_name; - var d = '
'; + var d = '
'; if (!modal) modal = false; if (!multiselect) multiselect = false; $('body').append(d); @@ -207,6 +207,7 @@ OCdialogs = { names += entry_template.replace('*LASTMODDATE*', OC.mtime2date(r.data[a].mtime)).replace('*NAME*', r.data[a].name).replace('*MIMETYPEICON*', r.data[a].mimetype_icon).replace('*ENTRYNAME*', r.data[a].name).replace('*ENTRYTYPE*', r.data[a].type); } $(dialog_content_id + ' #filelist').html(names); + $(dialog_content_id + ' .filepicker_loader').css('visibility', 'hidden'); }, handleTreeListSelect:function(event) { var newval = parseInt($(this).val()); @@ -220,6 +221,7 @@ OCdialogs = { var path = ''; $(this).children().each(function(i, element) { if (skip_first) {skip_first = false; return; }path += '/'+$(element).text(); }); $(event.data.dcid).data('path', path); + $(event.data.dcid + ' .filepicker_loader').css('visibility', 'visible'); $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path}, function(r){OC.dialogs.fillFilePicker(r, event.data.dcid)}); }, // this function is in early development state, please dont use it unlsess you know what you are doing @@ -237,6 +239,7 @@ OCdialogs = { $(dcid + ' #dirtree option:last').removeAttr('selected'); var newval = parseInt($(dcid + ' #dirtree option:last').val())+1; $(dcid + ' #dirtree').append(''); + $(dcid + ' .filepicker_loader').css('visibility', 'visible'); $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: p}, function(r){OC.dialogs.fillFilePicker(r, dcid)}); } }; From fe3d3be399f7b64b3f81c4f8fcd932b2d810ee45 Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Thu, 5 Apr 2012 22:25:58 +0200 Subject: [PATCH 105/133] filepicker loading style fixup --- core/css/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/css/styles.css b/core/css/styles.css index 0eb299a899..9d9f6d9830 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -137,4 +137,4 @@ a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padd #dirtree {width: 100%;} #filelist {height: 270px; overflow:scroll; background-color: white;} .filepicker_element_selected { background-color: lightblue;} -.filepicker_loader {height: 270px; width: 100%; background-color: #333; opacity: 0.3; visibility: visible; position:absolute; top:0; left:0; text-align:center; padding-top: 150px;} +.filepicker_loader {height: 120px; width: 100%; background-color: #333; opacity: 0.3; visibility: visible; position:absolute; top:0; left:0; text-align:center; padding-top: 150px;} From 96c99125da5fe74d4a8fa329869cca7f775805cf Mon Sep 17 00:00:00 2001 From: Bartek Przybylski Date: Thu, 5 Apr 2012 23:18:44 +0200 Subject: [PATCH 106/133] mimetype filter for filepicker --- core/js/oc-dialogs.js | 8 ++++---- files/ajax/rawlist.php | 3 ++- lib/filecache.php | 6 +++--- lib/files.php | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index e027ffb510..a3aa1e8c14 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -107,8 +107,8 @@ OCdialogs = { $(c_id + ' #dirtree').focus(function() { var t = $(this); t.data('oldval', t.val())}) .change({dcid: c_id}, OC.dialogs.handleTreeListSelect); $(c_id).ready(function(){ - $.getJSON(OC.webroot+'/files/ajax/rawlist.php', function(r){OC.dialogs.fillFilePicker(r, c_id, callback)}); - }).data('multiselect', multiselect); + $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {mimetype: mimetype_filter} ,function(r){OC.dialogs.fillFilePicker(r, c_id, callback)}); + }).data('multiselect', multiselect).data('mimetype',mimetype_filter); // build buttons var b = [ {text: t('dialogs', 'Choose'), click: function(){ @@ -222,7 +222,7 @@ OCdialogs = { $(this).children().each(function(i, element) { if (skip_first) {skip_first = false; return; }path += '/'+$(element).text(); }); $(event.data.dcid).data('path', path); $(event.data.dcid + ' .filepicker_loader').css('visibility', 'visible'); - $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path}, function(r){OC.dialogs.fillFilePicker(r, event.data.dcid)}); + $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path, mimetype: $(event.data.dcid).data('mimetype')}, function(r){OC.dialogs.fillFilePicker(r, event.data.dcid)}); }, // this function is in early development state, please dont use it unlsess you know what you are doing handlePickerClick:function(element, name, dcid) { @@ -240,6 +240,6 @@ OCdialogs = { var newval = parseInt($(dcid + ' #dirtree option:last').val())+1; $(dcid + ' #dirtree').append(''); $(dcid + ' .filepicker_loader').css('visibility', 'visible'); - $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: p}, function(r){OC.dialogs.fillFilePicker(r, dcid)}); + $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: p, mimetype: $(dcid).data('mimetype')}, function(r){OC.dialogs.fillFilePicker(r, dcid)}); } }; diff --git a/files/ajax/rawlist.php b/files/ajax/rawlist.php index 88ba48a6c4..e02c5b6273 100644 --- a/files/ajax/rawlist.php +++ b/files/ajax/rawlist.php @@ -11,10 +11,11 @@ OC_JSON::checkLoggedIn(); // Load the files $dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; +$mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : ''; // make filelist $files = array(); -foreach( OC_Files::getdirectorycontent( $dir ) as $i ){ +foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $i ){ $i["date"] = OC_Util::formatDate($i["mtime"] ); $i['mimetype_icon'] = $i['type'] == 'dir' ? mimetype_icon('dir'): mimetype_icon($i['mimetype']); $files[] = $i; diff --git a/lib/filecache.php b/lib/filecache.php index 4a4183cbdb..cdd91dcbfa 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -240,7 +240,7 @@ class OC_FileCache{ * - encrypted * - versioned */ - public static function getFolderContent($path,$root=''){ + public static function getFolderContent($path,$root='',$mimetype_filter=''){ if(self::isUpdated($path,$root)){ self::updateFolder($path,$root); } @@ -252,8 +252,8 @@ class OC_FileCache{ } $path=$root.$path; $parent=self::getFileId($path); - $query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=?'); - $result=$query->execute(array($parent))->fetchAll(); + $query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=? AND (mimetype LIKE ? OR mimetype = ?)'); + $result=$query->execute(array($parent, $mimetype_filter.'%', 'httpd/unix-directory'))->fetchAll(); if(is_array($result)){ return $result; }else{ diff --git a/lib/files.php b/lib/files.php index e7bfbbc19b..a68c29ad98 100644 --- a/lib/files.php +++ b/lib/files.php @@ -32,11 +32,11 @@ class OC_Files { * get the content of a directory * @param dir $directory */ - public static function getDirectoryContent($directory){ + public static function getDirectoryContent($directory, $mimetype_filter = ''){ if(strpos($directory,OC::$CONFIG_DATADIRECTORY)===0){ $directory=substr($directory,strlen(OC::$CONFIG_DATADIRECTORY)); } - $files=OC_FileCache::getFolderContent($directory); + $files=OC_FileCache::getFolderContent($directory, '', $mimetype_filter); foreach($files as &$file){ $file['directory']=$directory; $file['type']=($file['mimetype']=='httpd/unix-directory')?'dir':'file'; From 2fe0716cb4ba74d338d6af04a8b02fbb8eb1f903 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 6 Apr 2012 20:52:41 +0000 Subject: [PATCH 107/133] Show nice errors --- apps/user_migrate/admin.php | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index 61b83c2a14..280f7b7196 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -36,12 +36,18 @@ if (isset($_POST['user_import'])) { $from = $_FILES['owncloud_import']['tmp_name']; $to = get_temp_dir().'/'.$importname.'.zip'; if( !move_uploaded_file( $from, $to ) ){ + $errors[] = array('error'=>'Failed to move the uploaded file','hint'=>'Try checking the permissions of the '.get_temp_dir().' dir.'); OC_Log::write( 'user_migrate', "Failed to copy the uploaded file", OC_Log::ERROR ); - exit(); + $t = new OC_Template( '', 'error', 'user' ); + $t->assign('errors',$errors); + $t->fetchPage(); } if( !$appsstatus = OC_Migrate::import( $to, 'user' ) ){ - die( 'failed to to import' ); + $errors[] = array('error'=>'There was an error while importing the user!','hint'=>'Please check the logs for a more detailed explaination'); + $t = new OC_Template( '', 'error', 'user' ); + $t->assign('errors',$errors); + $t->fetchPage(); } else { // Check import status foreach( $appsstatus as $app => $status ){ @@ -57,10 +63,20 @@ if (isset($_POST['user_import'])) { // Any problems? if( isset( $notsupported ) || isset( $failed ) ){ if( count( $failed ) > 0 ){ - die( 'Some apps failed to import. View the log please.' ); + $errors[] = array('error'=>'Some app data failed to import','hint'=>'App data for: '.implode(', ', $failed).' failed to import.'); + $t = new OC_Template( '', 'error', 'user' ); + $t->assign('errors',$errors); + $t->fetchPage(); } else if( count( $notsupported ) > 0 ){ - die( 'Some apps were not found in this owncloud instance and therefore could not be installed' ); + $errors[] = array('error'=>'Some app data could not be imported, as the apps are not installed on this instance','hint'=>'App data for: '.implode(', ', $notsupported).' failed to import as they were not found. Please install the apps and try again'); + $t = new OC_Template( '', 'error', 'user' ); + $t->assign('errors',$errors); + $t->fetchPage(); } + } else { + // Went swimmingly! + $tmpl = new OC_Template('user_migrate', 'admin'); + return $tmpl->fetchPage(); } } From 22466010fc8d5a7d5cad876b8603f2bffee58182 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 6 Apr 2012 21:37:28 +0000 Subject: [PATCH 108/133] Provide undo when closing editor with unsaved changes --- apps/files_texteditor/js/editor.js | 100 ++++++++++++++++------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index 1e136fe68e..3d98a91c05 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -76,7 +76,7 @@ function showControls(filename,writeperms){ function bindControlEvents(){ $("#editor_save").die('click',doFileSave).live('click',doFileSave); - $('#editor_close').die('click',closeBtnClick).live('click',closeBtnClick); + $('#editor_close').die('click',hideFileEditor).live('click',hideFileEditor); $('#gotolineval').die('keyup', goToLine).live('keyup', goToLine); $('#editorsearchval').die('keyup', doSearch).live('keyup', doSearch); $('#clearsearchbtn').die('click', resetSearch).live('click', resetSearch); @@ -183,6 +183,8 @@ function giveEditorFocus(){ // Loads the file editor. Accepts two parameters, dir and filename. function showFileEditor(dir,filename){ + // Delete any old editors + $('#editor').remove(); if(!editorIsShown()){ // Loads the file editor and display it. $('#content').append('
'); @@ -235,52 +237,56 @@ function showFileEditor(dir,filename){ } } -function closeBtnClick(){ - if($('#editor').attr('data-edited')=='true'){ - // Show confirm - OC.dialogs.confirm(t('files_texteditor','You have unsaved changes that will be lost! Do you still want to close?'),t('files_texteditor','Really close?'),function(close){ - if(close){ - hideFileEditor(); - } - }); - } else { - hideFileEditor(); - } -} - // Fades out the editor. function hideFileEditor(){ - // Fades out editor controls - $('#editorcontrols').fadeOut('slow',function(){ - $(this).remove(); - $(".crumb:last").addClass('last'); - }); - // Fade out editor - $('#editor').fadeOut('slow', function(){ - $(this).remove(); - // Reset document title - document.title = "ownCloud"; - var editorhtml = '
'; - $('table').after(editorhtml); - $('.actions,#file_access_panel').fadeIn('slow'); - $('table').fadeIn('slow'); - }); - is_editor_shown = false; + if($('#editor').attr('data-edited') == 'true'){ + // Hide, not remove + $('#editorcontrols').fadeOut('slow',function(){ + // Check if there is a folder in the breadcrumb + if($('.crumb.ui-droppable').length){ + $('.crumb.ui-droppable:last').addClass('last'); + } + }); + // Fade out editor + $('#editor').fadeOut('slow', function(){ + // Reset document title + document.title = "ownCloud"; + $('.actions,#file_access_panel').fadeIn('slow'); + $('table').fadeIn('slow'); + }); + $('#notification').text(t('files_texteditor','There were unsaved changes, click here to go back')); + $('#notification').data('reopeneditor',true); + $('#notification').fadeIn(); + is_editor_shown = false; + } else { + // Remove editor + $('#editorcontrols').fadeOut('slow',function(){ + $(this).remove(); + $(".crumb:last").addClass('last'); + }); + // Fade out editor + $('#editor').fadeOut('slow', function(){ + $(this).remove(); + // Reset document title + document.title = "ownCloud"; + $('.actions,#file_access_panel').fadeIn('slow'); + $('table').fadeIn('slow'); + }); + is_editor_shown = false; + } } -// Keyboard Shortcuts -var ctrlBtn = false; +// Reopens the last document +function reopenEditor(){ + $('.actions,#file_action_panel').fadeOut('slow'); + $('table').fadeOut('slow', function(){ + $('#controls .last').not('#breadcrumb_file').removeClass('last'); + $('#editor').fadeIn('fast'); + $('#editorcontrols').fadeIn('fast', function(){ -// TODO fix detection of ctrl keyup -// returns true if ctrl+s or cmd+s is being pressed -function checkForSaveKeyPress(e){ - if(e.which == 17 || e.which == 91) ctrlBtn=true; - if(e.which == 83 && ctrlBtn == true) { - e.preventDefault(); - $('#editor_save').trigger('click'); - return false; - - } + }); + }); + is_editor_shown = true; } // resizes the editor window @@ -313,7 +319,11 @@ $(document).ready(function(){ // Binds the file save and close editor events, and gotoline button bindControlEvents(); $('#editor').remove(); - // Binds the save keyboard shortcut events - //$(document).unbind('keydown').bind('keydown',checkForSaveKeyPress); - + $('#notification').click(function(){ + if($('#notification').data('reopeneditor')) + { + reopenEditor(); + } + $('#notification').fadeOut(); + }); }); From a098c7c6858a7c2c646e38d1ba150b13821f4085 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Fri, 6 Apr 2012 21:38:38 +0000 Subject: [PATCH 109/133] Give editor focus after saving --- apps/files_texteditor/js/editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index 3d98a91c05..016632c997 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -174,6 +174,7 @@ function doFileSave(){ },'json'); } } + giveEditorFocus(); }; // Gives the editor focus From 42e110b49cc2efa63a46f39289f1bfb3e7dbca6f Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 7 Apr 2012 13:07:44 +0000 Subject: [PATCH 110/133] Update url --- settings/templates/apps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/templates/apps.php b/settings/templates/apps.php index 27133e9e67..1e49b4c892 100644 --- a/settings/templates/apps.php +++ b/settings/templates/apps.php @@ -5,7 +5,7 @@ */?>
    From 660951dc64087e7ebedaad5c0ae1104d8dd4d0cf Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 7 Apr 2012 13:32:00 +0000 Subject: [PATCH 111/133] Add some filetypes --- apps/files_texteditor/js/editor.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index 016632c997..bc8a20408c 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -22,9 +22,11 @@ function setSyntaxMode(ext){ filetype["css"] = "css"; filetype["groovy"] = "groovy"; filetype["haxe"] = "hx"; + filetype["htm"] = "html"; filetype["html"] = "html"; filetype["java"] = "java"; filetype["js"] = "javascript"; + filetype["jsm"] = "javascript"; filetype["json"] = "json"; filetype["latex"] = "latex"; filetype["ly"] = "latex"; From b71cf1a4f97bb660a279a16ca4da864b56986a59 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Sat, 7 Apr 2012 16:01:50 +0200 Subject: [PATCH 112/133] Fix bug oc-413: PHP fatal error in contacts page when no contacts in ownCloud. --- apps/contacts/index.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/contacts/index.php b/apps/contacts/index.php index 076b10c2ee..776c57ca60 100644 --- a/apps/contacts/index.php +++ b/apps/contacts/index.php @@ -43,17 +43,15 @@ if(count($categories) == 0) { $vcaddressbookids[] = $vcaddressbook['id']; } $vccontacts = OC_Contacts_VCard::all($vcaddressbookids); - if(count($vccontacts) == 0) { - bailOut(OC_Contacts_App::$l10n->t('No contacts found.')); + if(count($vccontacts) > 0) { + $cards = array(); + foreach($vccontacts as $vccontact) { + $cards[] = $vccontact['carddata']; + } + + OC_Contacts_App::$categories->rescan($cards); + $categories = OC_Contacts_App::$categories->categories(); } - - $cards = array(); - foreach($vccontacts as $vccontact) { - $cards[] = $vccontact['carddata']; - } - - OC_Contacts_App::$categories->rescan($cards); - $categories = OC_Contacts_App::$categories->categories(); } } From d1ae6512cc760c76798a5a8636d1d7908c706ae8 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Sat, 7 Apr 2012 16:03:11 +0200 Subject: [PATCH 113/133] Contacts: Misc. cleanup tweaks. --- apps/contacts/ajax/saveproperty.php | 58 +++++++++---------- apps/contacts/css/contacts.css | 11 ++-- apps/contacts/js/contacts.js | 29 +++------- apps/contacts/templates/part.contact.php | 19 +++--- .../templates/part.edit_address_dialog.php | 14 ++--- apps/contacts/templates/settings.php | 2 +- core/js/oc-vcategories.js | 4 +- 7 files changed, 64 insertions(+), 73 deletions(-) diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php index 924d873652..4cef4d1e7a 100644 --- a/apps/contacts/ajax/saveproperty.php +++ b/apps/contacts/ajax/saveproperty.php @@ -97,39 +97,39 @@ switch($element) { } break; case 'CATEGORIES': - /* multi autocomplete triggers an save with empty value */ + /* multi autocomplete triggers an save with empty value if (!$value) { $value = $vcard->getAsString('CATEGORIES'); } - break; + break;*/ case 'EMAIL': $value = strtolower($value); break; } if(!$value) { - bailOut(OC_Contacts_App::$l10n->t('Cannot save empty value.')); -} - -/* setting value */ -switch($element) { - case 'BDAY': - case 'FN': - case 'N': - case 'ORG': - case 'NOTE': - case 'NICKNAME': - case 'CATEGORIES': - debug('Setting string:'.$name.' '.$value); - $vcard->setString($name, $value); - break; - case 'EMAIL': - case 'TEL': - case 'ADR': // should I delete the property if empty or throw an error? - debug('Setting element: (EMAIL/TEL/ADR)'.$element); - if(!$value) { - unset($vcard->children[$line]); // Should never happen... - } else { + unset($vcard->children[$line]); + $checksum = ''; +} else { + /* setting value */ + switch($element) { + case 'BDAY': + case 'FN': + case 'N': + case 'ORG': + case 'NOTE': + case 'NICKNAME': + debug('Setting string:'.$name.' '.$value); + $vcard->setString($name, $value); + break; + case 'CATEGORIES': + debug('Setting string:'.$name.' '.$value); + $vcard->children[$line]->setValue($value); + break; + case 'EMAIL': + case 'TEL': + case 'ADR': // should I delete the property if empty or throw an error? + debug('Setting element: (EMAIL/TEL/ADR)'.$element); $vcard->children[$line]->setValue($value); $vcard->children[$line]->parameters = array(); if(!is_null($parameters)) { @@ -142,12 +142,12 @@ switch($element) { } } } - } - break; + break; + } + // Do checksum and be happy + $checksum = md5($vcard->children[$line]->serialize()); } -// Do checksum and be happy -$checksum = md5($vcard->children[$line]->serialize()); -debug('New checksum: '.$checksum); +//debug('New checksum: '.$checksum); if(!OC_Contacts_VCard::edit($id,$vcard)) { bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.')); diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 4e7d8c285a..5d3ebf65fb 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -13,19 +13,22 @@ #contacts_propertymenu li a { padding: 3px; display: block } #contacts_propertymenu li:hover { background-color: #1d2d44; } #contacts_propertymenu li a:hover { color: #fff } -#actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0;} +#actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0; z-index: 1000; } #card { width: auto;/*max-width: 70em; border: thin solid lightgray; display: block;*/ } #firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; } #firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; } #card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; float: left; } .categories { float: left; width: 16em; } -#card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #fefefe; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } -#card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; } -input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { color: #bbb !important; } +#card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select, textarea { background-color: #fefefe; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } +#card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active, textarea:focus, textarea:hover { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; } +input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid, textarea:invalid { color: #bbb !important; } +textarea { min-height: 4em; } dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } .form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom; color: #bbb;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ } .form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; } +#address.form dt { min-width: 5em; } +#address.form dl { min-width: 10em; } .loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; } .ui-autocomplete-loading { background: url('../../../core/img/loading.gif') right center no-repeat; } diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index cc4da5837e..3b264c0197 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -116,7 +116,7 @@ Contacts={ }, loadListHandlers:function() { //$('.add,.delete').hide(); - $('.globe,.mail,.delete,.edit').tipsy(); + $('.globe,.mail,.delete,.edit,.tip').tipsy(); $('.addresscard,.propertylist li,.propertycontainer').hover( function () { $(this).find('.globe,.mail,.delete,.edit').fadeIn(100); @@ -171,10 +171,6 @@ Contacts={ // Contacts.UI.Card.editAddress(); // return false; // }); - $('#n').click(function(){ - Contacts.UI.Card.editName(); - //return false; - }); $('#edit_name').click(function(){ Contacts.UI.Card.editName(); return false; @@ -258,12 +254,12 @@ Contacts={ }); } }, - export:function() { + do_export:function() { document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id; //$.get(OC.linkTo('contacts', 'export.php'),{'contactid':this.id},function(jsondata){ //}); }, - import:function(){ + do_import:function(){ Contacts.UI.notImplemented(); }, add:function(n, fn, aid, isnew){ // add a new contact @@ -293,11 +289,11 @@ Contacts={ if(isnew) { Contacts.UI.Card.addProperty('EMAIL'); Contacts.UI.Card.addProperty('TEL'); - Contacts.UI.Card.addProperty('BDAY'); Contacts.UI.Card.addProperty('NICKNAME'); Contacts.UI.Card.addProperty('ORG'); Contacts.UI.Card.addProperty('CATEGORIES'); $('#fn').focus(); + $('#fn').select(); } } else{ @@ -315,7 +311,7 @@ Contacts={ } }); }, - delete:function() { + do_delete:function() { $('#contacts_deletecard').tipsy('hide'); OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) { if(answer == true) { @@ -1256,7 +1252,7 @@ Contacts={ }); } }, - import:function(){ + do_import:function(){ Contacts.UI.notImplemented(); }, submit:function(button, bookid){ @@ -1289,9 +1285,7 @@ Contacts={ } }, Contacts:{ - /** - * Reload the contacts list. - */ + // Reload the contacts list. update:function(){ console.log('Contacts.update, start'); $.getJSON('ajax/contacts.php',{},function(jsondata){ @@ -1306,9 +1300,7 @@ Contacts={ }); setTimeout(Contacts.UI.Contacts.lazyupdate, 500); }, - /** - * Add thumbnails to the contact list as they become visible in the viewport. - */ + // Add thumbnails to the contact list as they become visible in the viewport. lazyupdate:function(){ $('#contacts li').live('inview', function(){ if (!$(this).find('a').attr('style')) { @@ -1328,9 +1320,6 @@ $(document).ready(function(){ OCCategories.changed = Contacts.UI.Card.categoriesChanged; OCCategories.app = 'contacts'; - /** - * Show the Addressbook chooser - */ $('#chooseaddressbook').click(function(){ Contacts.UI.Addressbooks.overview(); return false; @@ -1363,7 +1352,7 @@ $(document).ready(function(){ }); $('#contacts_deletecard').live('click',function(){ - Contacts.UI.Card.delete(); + Contacts.UI.Card.do_delete(); }); $('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) { diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index 961ce693e6..03d2fad853 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -17,7 +17,7 @@ $id = isset($_['id']) ? $_['id'] : '';
  • t('Categories'); ?>
- + @@ -37,7 +37,7 @@ $id = isset($_['id']) ? $_['id'] : ''; - +
@@ -62,7 +62,7 @@ $id = isset($_['id']) ? $_['id'] : '';
-
+
@@ -73,7 +73,7 @@ $id = isset($_['id']) ? $_['id'] : '';
@@ -85,7 +85,7 @@ $id = isset($_['id']) ? $_['id'] : '';
@@ -131,7 +130,7 @@ $(document).ready(function(){ Contacts.UI.Card.loadContact(jsondata.data); } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); } diff --git a/apps/contacts/templates/part.edit_address_dialog.php b/apps/contacts/templates/part.edit_address_dialog.php index 0ecdc4e191..507a3acaa0 100644 --- a/apps/contacts/templates/part.edit_address_dialog.php +++ b/apps/contacts/templates/part.edit_address_dialog.php @@ -22,44 +22,44 @@ foreach(isset($adr['parameters']['TYPE'])?array($adr['parameters']['TYPE']):arra
- +
- +
- +
- +
- +
- +
- +
diff --git a/apps/contacts/templates/settings.php b/apps/contacts/templates/settings.php index d77c4d3802..5627a15c50 100644 --- a/apps/contacts/templates/settings.php +++ b/apps/contacts/templates/settings.php @@ -8,6 +8,6 @@
t('iOS/OS X'); ?>
/principals//
+ Powered by geonames.org webservice - Powered by geonames.org webservice diff --git a/core/js/oc-vcategories.js b/core/js/oc-vcategories.js index a6dcccf88e..931ea37edb 100644 --- a/core/js/oc-vcategories.js +++ b/core/js/oc-vcategories.js @@ -19,7 +19,7 @@ OCCategories={ height: 350, minHeight:200, width: 250, minWidth: 200, buttons: { 'Delete':function() { - OCCategories.delete(); + OCCategories.do_delete(); }, 'Rescan':function() { OCCategories.rescan(); @@ -53,7 +53,7 @@ OCCategories={ } }); }, - delete:function(){ + do_delete:function(){ var categories = $('#categorylist').find('input[type="checkbox"]').serialize(); categories += '&app=' + OCCategories.app; console.log('OCCategories.delete: ' + categories); From 23d39f7ef0362d27c6a840585f878bc54c438409 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 7 Apr 2012 16:13:18 +0000 Subject: [PATCH 114/133] Display errors on import --- apps/user_migrate/admin.php | 32 +++++++++++++-------------- apps/user_migrate/templates/admin.php | 4 ++++ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index 280f7b7196..510c54abe3 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -36,18 +36,18 @@ if (isset($_POST['user_import'])) { $from = $_FILES['owncloud_import']['tmp_name']; $to = get_temp_dir().'/'.$importname.'.zip'; if( !move_uploaded_file( $from, $to ) ){ - $errors[] = array('error'=>'Failed to move the uploaded file','hint'=>'Try checking the permissions of the '.get_temp_dir().' dir.'); + $error = array('error'=>'Failed to move the uploaded file','hint'=>'Try checking the permissions of the '.get_temp_dir().' dir.'); OC_Log::write( 'user_migrate', "Failed to copy the uploaded file", OC_Log::ERROR ); - $t = new OC_Template( '', 'error', 'user' ); - $t->assign('errors',$errors); - $t->fetchPage(); + $tmpl = new OC_Template('user_migrate', 'admin'); + $tmpl->assign('error',$error); + return $tmpl->fetchPage(); } if( !$appsstatus = OC_Migrate::import( $to, 'user' ) ){ - $errors[] = array('error'=>'There was an error while importing the user!','hint'=>'Please check the logs for a more detailed explaination'); - $t = new OC_Template( '', 'error', 'user' ); - $t->assign('errors',$errors); - $t->fetchPage(); + $error = array('error'=>'There was an error while importing the user!','hint'=>'Please check the logs for a more detailed explaination'); + $tmpl = new OC_Template('user_migrate', 'admin'); + $tmpl->assign('error',$error); + return $tmpl->fetchPage(); } else { // Check import status foreach( $appsstatus as $app => $status ){ @@ -63,15 +63,15 @@ if (isset($_POST['user_import'])) { // Any problems? if( isset( $notsupported ) || isset( $failed ) ){ if( count( $failed ) > 0 ){ - $errors[] = array('error'=>'Some app data failed to import','hint'=>'App data for: '.implode(', ', $failed).' failed to import.'); - $t = new OC_Template( '', 'error', 'user' ); - $t->assign('errors',$errors); - $t->fetchPage(); + $error = array('error'=>'Some app data failed to import','hint'=>'App data for: '.implode(', ', $failed).' failed to import.'); + $tmpl = new OC_Template('user_migrate', 'admin'); + $tmpl->assign('error',$error); + return $tmpl->fetchPage(); } else if( count( $notsupported ) > 0 ){ - $errors[] = array('error'=>'Some app data could not be imported, as the apps are not installed on this instance','hint'=>'App data for: '.implode(', ', $notsupported).' failed to import as they were not found. Please install the apps and try again'); - $t = new OC_Template( '', 'error', 'user' ); - $t->assign('errors',$errors); - $t->fetchPage(); + $error = array('error'=>'Some app data could not be imported, as the apps are not installed on this instance','hint'=>'App data for: '.implode(', ', $notsupported).' failed to import as they were not found. Please install the apps and try again'); + $tmpl = new OC_Template('user_migrate', 'admin'); + $tmpl->assign('error',$error); + return $tmpl->fetchPage(); } } else { // Went swimmingly! diff --git a/apps/user_migrate/templates/admin.php b/apps/user_migrate/templates/admin.php index b5a9951841..b01e5c7579 100644 --- a/apps/user_migrate/templates/admin.php +++ b/apps/user_migrate/templates/admin.php @@ -1,5 +1,9 @@
+ +

+

+ t('Import user account');?>

From 00449cdf3781e9cbd1e7ab6e42cbeebf1fdb2f35 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 7 Apr 2012 17:23:25 +0000 Subject: [PATCH 115/133] rename admin_export -> admin_migrate --- apps/{admin_export => admin_migrate}/appinfo/app.php | 10 ++++------ apps/{admin_export => admin_migrate}/appinfo/info.xml | 6 +++--- apps/{admin_export => admin_migrate}/settings.php | 6 +++--- .../templates/settings.php | 0 4 files changed, 10 insertions(+), 12 deletions(-) rename apps/{admin_export => admin_migrate}/appinfo/app.php (76%) rename apps/{admin_export => admin_migrate}/appinfo/info.xml (57%) rename apps/{admin_export => admin_migrate}/settings.php (92%) rename apps/{admin_export => admin_migrate}/templates/settings.php (100%) diff --git a/apps/admin_export/appinfo/app.php b/apps/admin_migrate/appinfo/app.php similarity index 76% rename from apps/admin_export/appinfo/app.php rename to apps/admin_migrate/appinfo/app.php index 4e896abd6a..e45d3f6a52 100644 --- a/apps/admin_export/appinfo/app.php +++ b/apps/admin_migrate/appinfo/app.php @@ -1,10 +1,8 @@ "admin_export_settings", + 'id' => "admin_migrate_settings", 'order'=>1, - 'href' => OC_Helper::linkTo( "admin_export", "settings.php" ), + 'href' => OC_Helper::linkTo( "admin_migrate", "settings.php" ), 'name' => 'Export' ); diff --git a/apps/admin_export/appinfo/info.xml b/apps/admin_migrate/appinfo/info.xml similarity index 57% rename from apps/admin_export/appinfo/info.xml rename to apps/admin_migrate/appinfo/info.xml index e434705c9a..67fc3f9c5a 100644 --- a/apps/admin_export/appinfo/info.xml +++ b/apps/admin_migrate/appinfo/info.xml @@ -1,8 +1,8 @@ - admin_export - Import/Export - Import/Export your owncloud data + admin_migrate + ownCloud Instance Migration + Import/Export your owncloud instance 0.1 AGPL Thomas Schmidt and Tom Needham diff --git a/apps/admin_export/settings.php b/apps/admin_migrate/settings.php similarity index 92% rename from apps/admin_export/settings.php rename to apps/admin_migrate/settings.php index 719bedb66e..981d5f4ca6 100644 --- a/apps/admin_export/settings.php +++ b/apps/admin_migrate/settings.php @@ -1,7 +1,7 @@ fetchPage(); } \ No newline at end of file diff --git a/apps/admin_export/templates/settings.php b/apps/admin_migrate/templates/settings.php similarity index 100% rename from apps/admin_export/templates/settings.php rename to apps/admin_migrate/templates/settings.php From d2886f202024243ae4d92e2eea9d3726037a9601 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 7 Apr 2012 17:27:09 +0000 Subject: [PATCH 116/133] Hide instance import as it eats data --- apps/admin_migrate/templates/settings.php | 9 ++++++++- lib/migrate.php | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/admin_migrate/templates/settings.php b/apps/admin_migrate/templates/settings.php index 36eec84d48..91e305074e 100644 --- a/apps/admin_migrate/templates/settings.php +++ b/apps/admin_migrate/templates/settings.php @@ -6,12 +6,16 @@

What would you like to export?

- ownCloud instance ( suitable for import )
+ ownCloud instance (suitable for import )
ownCloud system files
Just user files

+
t('Import an ownCloud instance. THIS WILL DELETE ALL CURRENT OWNCLOUD DATA');?> @@ -22,3 +26,6 @@
+ diff --git a/lib/migrate.php b/lib/migrate.php index 815333b4d8..22c0560691 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -258,6 +258,7 @@ class OC_Migrate{ break; case 'instance': /* + * EXPERIMENTAL // Check for new data dir and dbexport before doing anything // TODO From c0869887cf91bf17421a9163069584d29ec5ed49 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 7 Apr 2012 21:55:16 +0000 Subject: [PATCH 117/133] Return JSON for import and export methods of OC_Migrate --- apps/bookmarks/appinfo/migrate.php | 1 - apps/user_migrate/admin.php | 6 ++--- apps/user_migrate/ajax/export.php | 35 +++++++++++++------------ lib/migrate.php | 42 +++++++++++++++--------------- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/apps/bookmarks/appinfo/migrate.php b/apps/bookmarks/appinfo/migrate.php index 603a8b72d3..c1251e816e 100644 --- a/apps/bookmarks/appinfo/migrate.php +++ b/apps/bookmarks/appinfo/migrate.php @@ -3,7 +3,6 @@ class OC_Migration_Provider_Bookmarks extends OC_Migration_Provider{ // Create the xml for the user supplied function export( ){ - OC_Log::write('migration','starting export for bookmarks',OC_Log::INFO); $options = array( 'table'=>'bookmarks', 'matchcol'=>'user_id', diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php index 510c54abe3..0160753af2 100644 --- a/apps/user_migrate/admin.php +++ b/apps/user_migrate/admin.php @@ -42,15 +42,15 @@ if (isset($_POST['user_import'])) { $tmpl->assign('error',$error); return $tmpl->fetchPage(); } - - if( !$appsstatus = OC_Migrate::import( $to, 'user' ) ){ + $response = json_decode( OC_Migrate::import( $to, 'user' ) ); + if( !$response->success ){ $error = array('error'=>'There was an error while importing the user!','hint'=>'Please check the logs for a more detailed explaination'); $tmpl = new OC_Template('user_migrate', 'admin'); $tmpl->assign('error',$error); return $tmpl->fetchPage(); } else { // Check import status - foreach( $appsstatus as $app => $status ){ + foreach( $response->data as $app => $status ){ if( $status != 'true' ){ // It failed for some reason if( $status == 'notsupported' ){ diff --git a/apps/user_migrate/ajax/export.php b/apps/user_migrate/ajax/export.php index fac96577fa..86745d6b16 100644 --- a/apps/user_migrate/ajax/export.php +++ b/apps/user_migrate/ajax/export.php @@ -28,28 +28,29 @@ OC_JSON::checkLoggedIn(); OC_Util::checkAppEnabled('user_migrate'); // Which operation if( $_GET['operation']=='create' ){ -$uid = !empty( $_POST['uid'] ) ? $_POST['uid'] : OC_User::getUser(); -if( $uid != OC_User::getUser() ){ - // Needs to be admin to export someone elses account - OC_JSON::error(); + $uid = !empty( $_POST['uid'] ) ? $_POST['uid'] : OC_User::getUser(); + if( $uid != OC_User::getUser() ){ + // Needs to be admin to export someone elses account + OC_JSON::error(); + die(); + } + // Create the export zip + $response = json_decode( OC_Migrate::export( $uid ) ); + if( !$response->success ){ + // Error + OC_JSON::error(); + die(); + } else { + // Save path in session + $_SESSION['ocuserexportpath'] = $response->data; + } + OC_JSON::success(); die(); -} -// Create the export zip -if( !$path = OC_Migrate::export( $uid ) ){ - // Error - OC_JSON::error(); - die(); -} else { - // Save path in session - $_SESSION['ocuserexportpath'] = $path; -} -OC_JSON::success(); -die(); } else if( $_GET['operation']=='download' ){ // Download the export $path = isset( $_SESSION['ocuserexportpath'] ) ? $_SESSION['ocuserexportpath'] : false; if( !$path ){ - die(); + OC_JSON::error(); } header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=" . basename($path)); diff --git a/lib/migrate.php b/lib/migrate.php index 22c0560691..6734c805fc 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -84,7 +84,7 @@ class OC_Migrate{ $types = array( 'user', 'instance', 'system', 'userfiles' ); if( !in_array( $type, $types ) ){ OC_Log::write( 'migration', 'Invalid export type', OC_Log::ERROR ); - return false; + return json_encode( array( array( 'success' => false ) ) ); } self::$exporttype = $type; // Userid? @@ -93,7 +93,7 @@ class OC_Migrate{ if( !is_null($uid) ){ if( !OC_User_Database::userExists( $uid ) ){ OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); - return false; + return json_encode( array( 'success' => false ) ); } self::$uid = $uid; } else { @@ -114,7 +114,7 @@ class OC_Migrate{ // Validate custom path if( !file_exists( $path ) || !is_writeable( $path ) ){ OC_Log::write( 'migration', 'Path supplied is invalid.', OC_Log::ERROR ); - return false; + return json_encode( array( 'success' => false ) ); } self::$zippath = $path . $zipname; } else { @@ -124,7 +124,7 @@ class OC_Migrate{ } // Create the zip object if( !self::createZip() ){ - return false; + return json_encode( array( 'success' => false ) ); } // Do the export self::findProviders(); @@ -134,7 +134,7 @@ class OC_Migrate{ // Connect to the db self::$dbpath = $datadir . '/' . self::$uid . '/migration.db'; if( !self::connectDB() ){ - return false; + return json_encode( array( 'success' => false ) ); } self::$content = new OC_Migration_Content( self::$zip, self::$MDB2 ); // Export the app info @@ -178,14 +178,14 @@ class OC_Migrate{ break; } if( !$info = self::getExportInfo( $exportdata ) ){ - return false; + return json_encode( array( 'success' => false ) ); } // Add the export info json to the export zip self::$content->addFromString( $info, 'export_info.json' ); if( !self::$content->finish() ){ - return false; + return json_encode( array( 'success' => false ) ); } - return self::$zippath; + return json_encode( array( 'success' => true, 'data' => self::$zippath ) ); } /** @@ -199,19 +199,19 @@ class OC_Migrate{ $datadir = OC_Config::getValue( 'datadirectory' ); // Extract the zip if( !$extractpath = self::extractZip( $path ) ){ - return false; + return json_encode( array( 'success' => false ) ); } // Get export_info.json $scan = scandir( $extractpath ); // Check for export_info.json if( !in_array( 'export_info.json', $scan ) ){ OC_Log::write( 'migration', 'Invalid import file, export_info.json note found', OC_Log::ERROR ); - return false; + return json_encode( array( 'success' => false ) ); } $json = json_decode( file_get_contents( $extractpath . 'export_info.json' ) ); if( $json->exporttype != $type ){ OC_Log::write( 'migration', 'Invalid import file', OC_Log::ERROR ); - return false; + return json_encode( array( 'success' => false ) ); } self::$exporttype = $type; @@ -230,31 +230,31 @@ class OC_Migrate{ // Check user availability if( OC_User::userExists( self::$uid ) ){ OC_Log::write( 'migration', 'User already exists', OC_Log::ERROR ); - return false; + return json_encode( array( 'success' => false ) ); } // Create the user if( !self::createUser( self::$uid, $json->hash ) ){ - return false; + return json_encode( array( 'success' => false ) ); } // Make the new users data dir $path = $datadir . '/' . self::$uid . '/files/'; if( !mkdir( $path, 0755, true ) ){ OC_Log::write( 'migration', 'Failed to create users data dir: '.$path, OC_Log::ERROR ); - return false; + return json_encode( array( 'success' => false ) ); } // Copy data if( !self::copy_r( $extractpath . $json->exporteduser . '/files', $datadir . '/' . self::$uid . '/files' ) ){ - return false; + return json_encode( array( 'success' => false ) ); } // Import user app data if( !$appsimported = self::importAppData( $extractpath . $json->exporteduser . '/migration.db', $json, self::$uid ) ){ - return false; + return json_encode( array( 'success' => false ) ); } // All done! if( !self::unlink_r( $extractpath ) ){ OC_Log::write( 'migration', 'Failed to delete the extracted zip', OC_Log::ERROR ); } - return $appsimported; + return json_encode( array( 'success' => true, 'data' => $appsimported ) ); break; case 'instance': /* @@ -266,21 +266,21 @@ class OC_Migrate{ OC_Log::write( 'migration', "Deleting current data dir", OC_Log::INFO ); if( !self::unlink_r( $datadir, false ) ){ OC_Log::write( 'migration', 'Failed to delete the current data dir', OC_Log::ERROR ); - return false; + return json_encode( array( 'success' => false ) ); } // Copy over data if( !self::copy_r( $extractpath . 'userdata', $datadir ) ){ OC_Log::write( 'migration', 'Failed to copy over data directory', OC_Log::ERROR ); - return false; + return json_encode( array( 'success' => false ) ); } // Import the db if( !OC_DB::replaceDB( $extractpath . 'dbexport.xml' ) ){ - return false; + return json_encode( array( 'success' => false ) ); } // Done - return true; + return json_encode( 'success' => true ); */ break; } From 5e314e8effb42b60d720de40fbf74591ae421534 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sat, 7 Apr 2012 22:00:32 +0000 Subject: [PATCH 118/133] Emit hooks for user creation --- lib/migrate.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/migrate.php b/lib/migrate.php index 6734c805fc..2730d26a74 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -232,10 +232,19 @@ class OC_Migrate{ OC_Log::write( 'migration', 'User already exists', OC_Log::ERROR ); return json_encode( array( 'success' => false ) ); } + $run = true; + OC_Hook::emit( "OC_User", "pre_createUser", array( "run" => &$run, "uid" => self::$uid, "password" => $json->hash )); + if( !$run ){ + // Something stopped the user creation + OC_Log::write( 'migration', 'User creation failed', OC_Log::ERROR ); + return json_encode( array( 'success' => false ) ); + } // Create the user if( !self::createUser( self::$uid, $json->hash ) ){ return json_encode( array( 'success' => false ) ); } + // Emit the post_createUser hook (password is already hashed, will cause problems + OC_Hook::emit( "OC_User", "post_createUser", array( "uid" => self::$uid, "password" => $json->hash )); // Make the new users data dir $path = $datadir . '/' . self::$uid . '/files/'; if( !mkdir( $path, 0755, true ) ){ From e5ebbacc9e951d1da82762daf67f993ee20e95cb Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 8 Apr 2012 18:52:31 +0000 Subject: [PATCH 119/133] Fix sqlite version detection --- lib/migrate.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/migrate.php b/lib/migrate.php index 2730d26a74..a8a5e581c3 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -496,10 +496,14 @@ class OC_Migrate{ $datadir = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); // DB type - if( !is_callable( 'sqlite_open' ) || !class_exists( 'SQLite3' ) ){ + if( class_exists( 'SQLite3' ) ){ + $dbtype = 'sqlite3'; + } else if( is_callable( 'sqlite_open' ) ){ + $dbtype = 'sqlite'; + } else { OC_Log::write( 'migration', 'SQLite not found', OC_Log::ERROR ); return false; - } + } // Prepare options array $options = array( @@ -510,7 +514,7 @@ class OC_Migrate{ 'quote_identifier' => true ); $dsn = array( - 'phptype' => 'sqlite3', + 'phptype' => $dbtype, 'database' => self::$dbpath, 'mode' => '0644' ); From ecc596534a9d6d6faffc6038a5bce61ebdf59781 Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 8 Apr 2012 19:08:17 +0000 Subject: [PATCH 120/133] Decode json response for admin exports --- apps/admin_migrate/settings.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/admin_migrate/settings.php b/apps/admin_migrate/settings.php index 981d5f4ca6..94bf6052a6 100644 --- a/apps/admin_migrate/settings.php +++ b/apps/admin_migrate/settings.php @@ -28,10 +28,12 @@ OC_Util::checkAppEnabled('admin_migrate'); // Export? if (isset($_POST['admin_export'])) { // Create the export zip - if( !$path = OC_Migrate::export( null, $_POST['export_type'] ) ){ + $response = json_decode( OC_Migrate::export( null, $_POST['export_type'] ) ); + if( !$response->success ){ // Error die('error'); } else { + $path = $response->data; // Download it header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=" . basename($path)); From 8e188cd96f4c1f19664aa5ee870420a26d230c5c Mon Sep 17 00:00:00 2001 From: Tom Needham Date: Sun, 8 Apr 2012 19:16:03 +0000 Subject: [PATCH 121/133] Copy over all file app data for imported user --- lib/migrate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/migrate.php b/lib/migrate.php index a8a5e581c3..dff3abe9e9 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -246,13 +246,13 @@ class OC_Migrate{ // Emit the post_createUser hook (password is already hashed, will cause problems OC_Hook::emit( "OC_User", "post_createUser", array( "uid" => self::$uid, "password" => $json->hash )); // Make the new users data dir - $path = $datadir . '/' . self::$uid . '/files/'; + $path = $datadir . '/' . self::$uid; if( !mkdir( $path, 0755, true ) ){ OC_Log::write( 'migration', 'Failed to create users data dir: '.$path, OC_Log::ERROR ); return json_encode( array( 'success' => false ) ); } // Copy data - if( !self::copy_r( $extractpath . $json->exporteduser . '/files', $datadir . '/' . self::$uid . '/files' ) ){ + if( !self::copy_r( $extractpath . $json->exporteduser, $datadir . '/' . self::$uid ) ){ return json_encode( array( 'success' => false ) ); } // Import user app data From 53091551d8019fa64dbcf5197a380f60bb2e6827 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 9 Apr 2012 16:02:59 +0200 Subject: [PATCH 122/133] Re-added file I had accidentally removed earlier :-P --- apps/contacts/templates/part.no_contacts.php | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 apps/contacts/templates/part.no_contacts.php diff --git a/apps/contacts/templates/part.no_contacts.php b/apps/contacts/templates/part.no_contacts.php new file mode 100644 index 0000000000..d24f7ae980 --- /dev/null +++ b/apps/contacts/templates/part.no_contacts.php @@ -0,0 +1,8 @@ +
+ You have no contacts in your list. +
+ + + +
+
\ No newline at end of file From 9c247ce874697ed6833c58d21ddecee418ddb525 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 9 Apr 2012 16:29:35 +0200 Subject: [PATCH 123/133] Removed padding. --- apps/contacts/js/contacts.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index 3b264c0197..b2678e8c52 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -264,6 +264,18 @@ Contacts={ }, add:function(n, fn, aid, isnew){ // add a new contact console.log('Add contact: ' + n + ', ' + fn + ' ' + aid); + var card = $('#card')[0]; + if(!card) { + console.log('Loading proper card DOM'); + $.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{},function(jsondata){ + if(jsondata.status == 'success'){ + $('#rightcontent').html(jsondata.data.page); + Contacts.UI.loadHandlers(); + } else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } $.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid }, function(jsondata) { if (jsondata.status == 'success'){ From 4fda6af725ea775750905983255ab3dc9ceeb2b9 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 9 Apr 2012 16:29:56 +0200 Subject: [PATCH 124/133] Contacts: Fix for not being able to add contact to empty/non-existant address book. --- apps/contacts/ajax/addcontact.php | 2 -- apps/contacts/css/contacts.css | 2 +- apps/contacts/lib/addressbook.php | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php index 947b35bab5..68da54655a 100644 --- a/apps/contacts/ajax/addcontact.php +++ b/apps/contacts/ajax/addcontact.php @@ -47,8 +47,6 @@ OC_Contacts_App::getAddressbook( $aid ); // is owner access check $fn = trim($_POST['fn']); $n = trim($_POST['n']); -debug('N: '.$n); -debug('FN: '.$fn); $vcard = new OC_VObject('VCARD'); $vcard->setUID(); diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 5d3ebf65fb..2d20794384 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -56,7 +56,7 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } .contactpart legend { width:auto; padding:.3em; border:1px solid #ddd; font-weight:bold; cursor:pointer; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; } #cropbox { margin: auto; } -#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; padding: 0.5em; margin: 0.3em; cursor: pointer; background: url(../../../core/img/loading.gif) no-repeat center center; display: block; /* clear: right;*/ } +#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; margin: 0.3em; cursor: pointer; background: url(../../../core/img/loading.gif) no-repeat center center; display: block; /* clear: right;*/ } #contacts_details_photo:hover { background: #fff; } /*#contacts_details_photo_progress { margin: 0.3em 0.3em 0.3em 7em; clear: left; }*/ /* Address editor */ diff --git a/apps/contacts/lib/addressbook.php b/apps/contacts/lib/addressbook.php index 052c19e55f..9061fa1914 100644 --- a/apps/contacts/lib/addressbook.php +++ b/apps/contacts/lib/addressbook.php @@ -169,7 +169,7 @@ class OC_Contacts_Addressbook{ $uid = OC_User::getUser(); } $prefbooks = OC_Preferences::getValue($uid,'contacts','openaddressbooks',null); - if(is_null($prefbooks)){ + if(!$prefbooks){ $addressbooks = OC_Contacts_Addressbook::all($uid); if(count($addressbooks) == 0){ OC_Contacts_Addressbook::add($uid,'default','Default Address Book'); From b0894b314845a52e979539cbf1e6bd94444dca54 Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Mon, 9 Apr 2012 22:10:29 +0200 Subject: [PATCH 125/133] Add Craigs granite library to 3rdparty and files_versioning. Still not working and lots ot stuff to do. --- 3rdparty/granite/git/blob.php | 162 +++++ 3rdparty/granite/git/commit.php | 232 +++++++ 3rdparty/granite/git/object/index.php | 210 ++++++ 3rdparty/granite/git/object/loose.php | 81 +++ 3rdparty/granite/git/object/packed.php | 304 ++++++++ 3rdparty/granite/git/object/raw.php | 153 +++++ 3rdparty/granite/git/repository.php | 293 ++++++++ 3rdparty/granite/git/tag.php | 38 + 3rdparty/granite/git/tree.php | 198 ++++++ 3rdparty/granite/git/tree/node.php | 126 ++++ apps/files_versioning/ajax/gethead.php | 12 + apps/files_versioning/ajax/sethead.php | 14 + apps/files_versioning/appinfo/app.php | 20 + apps/files_versioning/appinfo/info.xml | 14 + apps/files_versioning/css/settings.css | 3 + apps/files_versioning/js/settings.js | 25 + apps/files_versioning/lib_granite.php | 12 + apps/files_versioning/settings.php | 34 + apps/files_versioning/templates/settings.php | 12 + apps/files_versioning/versionstorage.php | 386 +++++++++++ apps/files_versioning/versionwrapper.php | 686 +++++++++++++++++++ 21 files changed, 3015 insertions(+) create mode 100644 3rdparty/granite/git/blob.php create mode 100644 3rdparty/granite/git/commit.php create mode 100644 3rdparty/granite/git/object/index.php create mode 100644 3rdparty/granite/git/object/loose.php create mode 100644 3rdparty/granite/git/object/packed.php create mode 100644 3rdparty/granite/git/object/raw.php create mode 100644 3rdparty/granite/git/repository.php create mode 100644 3rdparty/granite/git/tag.php create mode 100644 3rdparty/granite/git/tree.php create mode 100644 3rdparty/granite/git/tree/node.php create mode 100644 apps/files_versioning/ajax/gethead.php create mode 100644 apps/files_versioning/ajax/sethead.php create mode 100644 apps/files_versioning/appinfo/app.php create mode 100644 apps/files_versioning/appinfo/info.xml create mode 100644 apps/files_versioning/css/settings.css create mode 100644 apps/files_versioning/js/settings.js create mode 100644 apps/files_versioning/lib_granite.php create mode 100644 apps/files_versioning/settings.php create mode 100644 apps/files_versioning/templates/settings.php create mode 100644 apps/files_versioning/versionstorage.php create mode 100644 apps/files_versioning/versionwrapper.php diff --git a/3rdparty/granite/git/blob.php b/3rdparty/granite/git/blob.php new file mode 100644 index 0000000000..781a697d56 --- /dev/null +++ b/3rdparty/granite/git/blob.php @@ -0,0 +1,162 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git; +use \Granite\Git\Object\Raw as Raw; +use \InvalidArgumentException as InvalidArgumentException; +use \finfo as finfo; +/** + * **Granite\Git\Blob** represents the raw content of an object in a Git repository, + * typically a **file**. This class provides methods related to the handling of + * blob content, mimetypes, sizes and write support. + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Blob +{ + + /** + * Stores the SHA-1 id of the object requested; accessed through the `sha()` + * method where it is recalculated based on the blob content. + */ + private $sha = null; + /** + * The raw binary string of the file contents. + */ + private $content = ""; + /** + * The path to the repository location. + */ + private $path; + + /** + * Fetches a raw Git object and parses the result. Throws an + * InvalidArgumentException if the object is not of the correct type, + * or cannot be found. + * + * @param string $path The path to the repository root. + * @param string $sha The SHA-1 id of the requested object, or `null` if + * creating a new blob object. + * + * @throws InvalidArgumentException If the SHA-1 id provided is not a blob. + */ + public function __construct($path, $sha = NULL) + { + $this->path = $path; + if ($sha !== NULL) { + $this->sha = $sha; + $object = Raw::factory($path, $sha); + + if ($object->type() !== Raw::OBJ_BLOB) { + throw new InvalidArgumentException( + "The object $sha is not a blob, type is {$object->type()}" + ); + } + + $this->content = $object->content(); + unset($object); + } + } + + /** + * Sets or returns the raw file content, depending whether the parameter is + * provided. + * + * @param string $content The object content to set, or `null` if requesting the + * current content. + * + * @return string The raw binary string of the file contents. + */ + public function content($content = NULL) + { + if ($content == NULL) { + return $this->content; + } + $this->content = $content; + } + + /** + * Returns the size of the file content in bytes, equivalent to + * `strlen($blob->content())`. + * + * @return int The size of the object in bytes. + */ + public function size() + { + return strlen($this->content); + } + + /** + * Updates and returns the SHA-1 id of the object, based on it's contents. + * + * @return int The SHA-1 id of the object. + */ + public function sha() + { + $sha = hash_init('sha1'); + $header = 'blob ' . strlen($this->content) . "\0"; + hash_update($sha, $header); + hash_update($sha, $this->content); + $this->sha = hash_final($sha); + return $this->sha; + } + + /** + * Returns the mimetype of the object, using `finfo()` to determine the mimetype + * of the string. + * + * @return string The object mimetype. + * @see http://php.net/manual/en/function.finfo-open.php + */ + public function mimetype() + { + $finfo = new finfo(FILEINFO_MIME); + return $finfo->buffer($this->content); + } + + /** + * Encode and compress the object content, saving it to a 'loose' file. + * + * @return boolean True on success, false on failure. + */ + public function write() + { + $sha = $this->sha(TRUE); + $path = $this->path + . 'objects' + . DIRECTORY_SEPARATOR + . substr($sha, 0, 2) + . DIRECTORY_SEPARATOR + . substr($sha, 2); + // FIXME: currently writes loose objects only + if (file_exists($path)) { + return FALSE; + } + + if (!is_dir(dirname($path))) { + mkdir(dirname($path), 0777, TRUE); + } + + $loose = fopen($path, 'wb'); + $data = 'blob ' . strlen($this->content) . "\0" . $this->content; + $write = fwrite($loose, gzcompress($data)); + fclose($loose); + + return ($write !== FALSE); + } + +} diff --git a/3rdparty/granite/git/commit.php b/3rdparty/granite/git/commit.php new file mode 100644 index 0000000000..51077e89f3 --- /dev/null +++ b/3rdparty/granite/git/commit.php @@ -0,0 +1,232 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git; +use \Granite\Git\Object\Raw as Raw; +use \InvalidArgumentException as InvalidArgumentException; + +/** + * Commit represents a full commit object + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Commit +{ + + /** + * The path to the repository root + */ + private $path; + /** + * The SHA-1 id of the requested commit + */ + private $sha; + /** + * The size of the commit in bytes + */ + private $size; + /** + * The commit message + */ + private $message; + /** + * The full committer string + */ + private $committer; + /** + * The full author string + */ + private $author; + /** + * The SHA-1 ids of the parent commits + */ + private $parents = array(); + + /** + * Fetches a raw Git object and parses the result. Throws an + * InvalidArgumentException if the object is not of the correct type, + * or cannot be found. + * + * @param string $path The path to the repository root + * @param string $sha The SHA-1 id of the requested object + * + * @throws InvalidArgumentException + */ + public function __construct($path, $sha = NULL) + { + $this->path = $path; + if ($sha !== NULL) { + $this->sha = $sha; + $object = Raw::factory($path, $sha); + $this->size = $object->size(); + + if ($object->type() !== Raw::OBJ_COMMIT) { + throw new InvalidArgumentException( + "The object $sha is not a commit, type is " . $object->type() + ); + } + + // Parse headers and commit message (delimited with "\n\n") + list($headers, $this->message) = explode("\n\n", $object->content(), 2); + $headers = explode("\n", $headers); + + foreach ($headers as $header) { + list($header, $value) = explode(' ', $header, 2); + if ($header == 'parent') { + $this->parents[] = $value; + } else { + $this->$header = $value; + } + } + + $this->tree = new Tree($this->path, $this->tree); + } + } + + /** + * Returns the message stored in the commit + * + * @return string The commit message + */ + public function message($message = NULL) + { + if ($message !== NULL) { + $this->message = $message; + return $this; + } + return $this->message; + } + + /** + * Returns the commiter string + * + * @return string The committer string + */ + public function committer($committer = NULL) + { + if ($committer !== NULL) { + $this->committer = $committer; + return $this; + } + return $this->committer; + } + + /** + * Returns the author string + * + * @return string The author string + */ + public function author($author = NULL) + { + if ($author !== NULL) { + $this->author = $author; + return $this; + } + return $this->author; + } + + /** + * Returns the parents of the commit, or an empty array if none + * + * @return array The parents of the commit + */ + public function parents($parents = NULL) + { + if ($parents !== NULL) { + $this->parents = $parents; + return $this; + } + return $this->parents; + } + + /** + * Returns a tree object associated with the commit + * + * @return Tree + */ + public function tree(Tree $tree = NULL) + { + if ($tree !== NULL) { + $this->tree = $tree; + return $this; + } + return $this->tree; + } + + /** + * Returns the size of the commit in bytes (Git header + data) + * + * @return int + */ + public function size() + { + return $this->size; + } + + /** + * Returns the size of the commit in bytes (Git header + data) + * + * @return int + */ + public function sha() + { + $this->sha = hash('sha1', $this->_raw()); + return $this->sha; + } + + public function write() + { + $sha = $this->sha(); + $path = $this->path + . 'objects' + . DIRECTORY_SEPARATOR + . substr($sha, 0, 2) + . DIRECTORY_SEPARATOR + . substr($sha, 2); + // FIXME: currently writes loose objects only + if (file_exists($path)) { + return FALSE; + } + + if (!is_dir(dirname($path))) { + mkdir(dirname($path), 0777, TRUE); + } + + $loose = fopen($path, 'wb'); + $data = $this->_raw(); + $write = fwrite($loose, gzcompress($data)); + fclose($loose); + + return ($write !== FALSE); + } + + public function _raw() + { + $data = 'tree ' . $this->tree->sha() . "\n"; + foreach ($this->parents as $parent) + { + $data .= "parent $parent\n"; + } + $data .= 'author ' . $this->author . "\n"; + $data .= 'committer ' . $this->committer . "\n\n"; + $data .= $this->message; + + $data = 'commit ' . strlen($data) . "\0" . $data; + return $data; + } + +} diff --git a/3rdparty/granite/git/object/index.php b/3rdparty/granite/git/object/index.php new file mode 100644 index 0000000000..239706d4ef --- /dev/null +++ b/3rdparty/granite/git/object/index.php @@ -0,0 +1,210 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git\Object; +use \UnexpectedValueException as UnexpectedValueException; + +/** + * Index represents a packfile index + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @link http://craig0990.github.com/Granite/ + */ +class Index +{ + const INDEX_MAGIC = "\377tOc"; + + /** + * The full path to the packfile index + */ + private $path; + /** + * The offset at which the fanout begins, version 2+ indexes have a 2-byte header + */ + private $offset = 8; + /** + * The size of the SHA-1 entries, version 1 stores 4-byte offsets alongside to + * total 24 bytes, version 2+ stores offsets separately + */ + private $size = 20; + /** + * The version of the index file format, versions 1 and 2 are in use and + * currently supported + */ + private $version; + + /** + * Fetches a raw Git object and parses the result + * + * @param string $path The path to the repository root + * @param string $packname The name of the packfile index to read + */ + public function __construct($path, $packname) + { + $this->path = $path + . 'objects' + . DIRECTORY_SEPARATOR + . 'pack' + . DIRECTORY_SEPARATOR + . 'pack-' . $packname . '.idx'; + + $this->version = $this->_readVersion(); + if ($this->version !== 1 && $this->version !== 2) { + throw new UnexpectedValueException( + "Unsupported index version (version $version)" + ); + } + + if ($this->version == 1) { + $this->offset = 0; // Version 1 index has no header/version + $this->size = 24; // Offsets + SHA-1 ids are stored together + } + } + + /** + * Returns the offset of the object stored in the index + * + * @param string $sha The SHA-1 id of the object being requested + * + * @return int The offset of the object in the packfile + */ + public function find($sha) + { + $index = fopen($this->path, 'rb'); + $offset = false; // Offset for object in packfile not found by default + + // Read the fanout to skip to the start char in the sorted SHA-1 list + list($start, $after) = $this->_readFanout($index, $sha); + + if ($start == $after) { + fclose($index); + return false; // Object is apparently located in a 0-length section + } + + // Seek $offset + 255 4-byte fanout entries and read 256th entry + fseek($index, $this->offset + 4 * 255); + $totalObjects = $this->_uint32($index); + + // Look up the SHA-1 id of the object + // TODO: Binary search + fseek($index, $this->offset + 1024 + $this->size * $start); + for ($i = $start; $i < $after; $i++) { + if ($this->version == 1) { + $offset = $this->_uint32($index); + } + + $name = fread($index, 20); + if ($name == pack('H40', $sha)) { + break; // Found it + } + } + + if ($i == $after) { + fclose($index); + return false; // Scanned entire section, couldn't find it + } + + if ($this->version == 2) { + // Jump to the offset location and read it + fseek($index, 1032 + 24 * $totalObjects + 4 * $i); + $offset = $this->_uint32($index); + if ($offset & 0x80000000) { + // Offset is a 64-bit integer; packfile is larger than 2GB + fclose($index); + throw new UnexpectedValueException( + "Packfile larger than 2GB, currently unsupported" + ); + } + } + + fclose($index); + return $offset; + } + + /** + * Converts a binary string into a 32-bit unsigned integer + * + * @param handle $file Binary string to convert + * + * @return int Integer value + */ + private function _uint32($file) + { + $val = unpack('Nx', fread($file, 4)); + return $val['x']; + } + + /** + * Reads the fanout for a particular SHA-1 id + * + * Largely modified from Glip, with some reference to Grit - largely because I + * can't see how to re-implement this in PHP + * + * @param handle $file File handle to the index file + * @param string $sha The SHA-1 id to search for + * @param int $offset The offset at which the fanout begins + * + * @return array Array containing integer 'start' and + * 'past-the-end' locations + */ + private function _readFanout($file, $sha) + { + $sha = pack('H40', $sha); + fseek($file, $this->offset); + if ($sha{0} == "\00") { + /** + * First character is 0, read first fanout entry to provide + * 'past-the-end' location (since first fanout entry provides start + * point for '1'-prefixed SHA-1 ids) + */ + $start = 0; + fseek($file, $this->offset); // Jump to start of fanout, $offset bytes in + $after = $this->_uint32($file); + } else { + /** + * Take ASCII value of first character, minus one to get the fanout + * position of the offset (minus one because the fanout does not + * contain an entry for "\00"), multiplied by four bytes per entry + */ + fseek($file, $this->offset + (ord($sha{0}) - 1) * 4); + $start = $this->_uint32($file); + $after = $this->_uint32($file); + } + + return array($start, $after); + } + + /** + * Returns the version number of the index file, or 1 if there is no version + * information + * + * @return int + */ + private function _readVersion() + { + $file = fopen($this->path, 'rb'); + $magic = fread($file, 4); + $version = $this->_uint32($file); + + if ($magic !== self::INDEX_MAGIC) { + $version = 1; + } + + fclose($file); + return $version; + } + +} diff --git a/3rdparty/granite/git/object/loose.php b/3rdparty/granite/git/object/loose.php new file mode 100644 index 0000000000..32f894845b --- /dev/null +++ b/3rdparty/granite/git/object/loose.php @@ -0,0 +1,81 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git\Object; +use \UnexpectedValueException as UnexpectedValueException; + +/** + * Loose represents a loose object in the Git repository + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Loose extends Raw +{ + + /** + * Reads an object from a loose object file based on the SHA-1 id + * + * @param string $path The path to the repository root + * @param string $sha The SHA-1 id of the requested object + * + * @throws UnexpectedValueException If the type is not 'commit', 'tree', + * 'tag' or 'blob' + */ + public function __construct($path, $sha) + { + $this->sha = $sha; + + $loose_path = $path + . 'objects/' + . substr($sha, 0, 2) + . '/' + . substr($sha, 2); + + if (!file_exists($loose_path)) { + throw new InvalidArgumentException("Cannot open loose object file for $sha"); + } + + $raw = gzuncompress(file_get_contents($loose_path)); + $data = explode("\0", $raw, 2); + + $header = $data[0]; + $this->content = $data[1]; + + list($this->type, $this->size) = explode(' ', $header); + + switch ($this->type) { + case 'commit': + $this->type = Raw::OBJ_COMMIT; + break; + case 'tree': + $this->type = Raw::OBJ_TREE; + break; + case 'blob': + $this->type = Raw::OBJ_BLOB; + break; + case 'tag': + $this->type = Raw::OBJ_TAG; + break; + default: + throw new UnexpectedValueException( + "Unexpected type '{$this->type}'" + ); + break; + } + } + +} diff --git a/3rdparty/granite/git/object/packed.php b/3rdparty/granite/git/object/packed.php new file mode 100644 index 0000000000..7e8d663b32 --- /dev/null +++ b/3rdparty/granite/git/object/packed.php @@ -0,0 +1,304 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git\Object; +use \UnexpectedValueException as UnexpectedValueException; + +/** + * Packed represents a packed object in the Git repository + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Packed extends Raw +{ + + /** + * The name of the packfile being read + */ + private $_packfile; + + /** + * Added to the object size to make a 'best-guess' effort at how much compressed + * data to read - should be reimplemented, ideally with streams. + */ + const OBJ_PADDING = 512; + + /** + * Reads the object data from the compressed data at $offset in $packfile + * + * @param string $packfile The path to the packfile + * @param int $offset The offset of the object data + */ + public function __construct($packfile, $offset) + { + $this->_packfile = $packfile; + + list($this->type, $this->size, $this->content) + = $this->_readPackedObject($offset); + } + + /** + * Reads the object data at $this->_offset + * + * @param int $offset Offset of the object header + * + * @return array Containing the type, size and object data + */ + private function _readPackedObject($offset) + { + $file = fopen($this->_packfile, 'rb'); + fseek($file, $offset); + // Read the type and uncompressed size from the object header + list($type, $size) = $this->_readHeader($file, $offset); + $object_offset = ftell($file); + + if ($type == self::OBJ_OFS_DELTA || $type == self::OBJ_REF_DELTA) { + return $this->_unpackDeltified( + $file, $offset, $object_offset, $type, $size + ); + } + + $content = gzuncompress(fread($file, $size + self::OBJ_PADDING), $size); + + return array($type, $size, $content); + } + + /** + * Reads a packed object header, returning the type and the size. For more + * detailed information, refer to the @see tag. + * + * From the @see tag: "Each byte is really 7 bits of data, with the first bit + * being used to say if that hunk is the last one or not before the data starts. + * If the first bit is a 1, you will read another byte, otherwise the data starts + * next. The first 3 bits in the first byte specifies the type of data..." + * + * @param handle $file File handle to read + * @param int $offset Offset of the object header + * + * @return array Containing the type and the size + * @see http://book.git-scm.com/7_the_packfile.html + */ + private function _readHeader($file, $offset) + { + // Read the object header byte-by-byte + fseek($file, $offset); + $byte = ord(fgetc($file)); + /** + * Bit-shift right by four, then ignore the first bit with a bitwise AND + * This gives us the object type in binary: + * 001 commit self::OBJ_COMMIT + * 010 tree self::OBJ_TREE + * 011 blob self::OBJ_BLOB + * 100 tag self::OBJ_TAG + * 110 offset delta self::OBJ_OFS_DELTA + * 111 ref delta self::OBJ_REF_DELTA + * + * (000 is undefined, 101 is not currently in use) + * See http://book.git-scm.com/7_the_packfile.html for details + */ + $type = ($byte >> 4) & 0x07; + + // Read the last four bits of the first byte, used to find the size + $size = $byte & 0x0F; + + /** + * $shift initially set to four, since we use the last four bits of the first + * byte + * + * $byte & 0x80 checks the initial bit is set to 1 (i.e. keep reading data) + * + * Finally, $shift is incremented by seven for each consecutive byte (because + * we ignore the initial bit) + */ + for ($shift = 4; $byte & 0x80; $shift += 7) { + $byte = ord(fgetc($file)); + /** + * The size is ANDed against 0x7F to strip the initial bit, then + * bitshifted by left $shift (4 or 7, depending on whether it's the + * initial byte) and ORed against the existing binary $size. This + * continuously increments the $size variable. + */ + $size |= (($byte & 0x7F) << $shift); + } + + return array($type, $size); + } + + /** + * Unpacks a deltified object located at $offset in $file + * + * @param handle $file File handle to read + * @param int $offset Offset of the object data + * @param int $object_offset Offset of the object data, past the header + * @param int $type The object type, either OBJ_REF_DELTA + or OBJ_OFS_DELTA + * @param int $size The expected size of the uncompressed data + * + * @return array Containing the type, size and object data + */ + private function _unpackDeltified($file, $offset, $object_offset, $type, $size) + { + fseek($file, $object_offset); + + if ($type == self::OBJ_REF_DELTA) { + + $base_sha = bin2hex(fread($file, 20)); + + $path = substr($this->_packfile, 0, strpos($this->_packfile, '.git')+5); + $base = Raw::factory($path, $base_sha); + $type = $base->type(); + $base = $base->content(); + + $delta = gzuncompress( + fread($file, $size + self::OBJ_PADDING), $size + ); + + $content = $this->_applyDelta($base, $delta); + + } elseif ($type == self::OBJ_OFS_DELTA) { + + // 20 = maximum varint size according to Glip + $data = fread($file, $size + self::OBJ_PADDING + 20); + + list($base_offset, $length) = $this->_bigEndianNumber($data); + + $delta = gzuncompress(substr($data, $length), $size); + unset($data); + + $base_offset = $offset - $base_offset; + list($type, $size, $base) = $this->_readPackedObject($base_offset); + + $content = $this->_applyDelta($base, $delta); + + } else { + throw new UnexpectedValueException( + "Unknown type $type for deltified object" + ); + } + + return array($type, strlen($content), $content); + } + + /** + * Applies the $delta byte-sequence to $base and returns the + * resultant binary string. + * + * This code is modified from Grit (see below), the Ruby + * implementation used for GitHub under an MIT license. + * + * @param string $base The base string for the delta to be applied to + * @param string $delta The delta string to apply + * + * @return string The patched binary string + * @see + * https://github.com/mojombo/grit/blob/master/lib/grit/git-ruby/internal/pack.rb + */ + private function _applyDelta($base, $delta) + { + $pos = 0; + $src_size = $this->_varint($delta, $pos); + $dst_size = $this->_varint($delta, $pos); + + if ($src_size !== strlen($base)) { + throw new UnexpectedValueException( + 'Expected base delta size ' . strlen($base) . ' does not match the expected ' + . "value $src_size" + ); + } + + $dest = ""; + while ($pos < strlen($delta)) { + $byte = ord($delta{$pos++}); + + if ($byte & 0x80) { + /* copy a part of $base */ + $offset = 0; + if ($byte & 0x01) $offset = ord($delta{$pos++}); + if ($byte & 0x02) $offset |= ord($delta{$pos++}) << 8; + if ($byte & 0x04) $offset |= ord($delta{$pos++}) << 16; + if ($byte & 0x08) $offset |= ord($delta{$pos++}) << 24; + $length = 0; + if ($byte & 0x10) $length = ord($delta{$pos++}); + if ($byte & 0x20) $length |= ord($delta{$pos++}) << 8; + if ($byte & 0x40) $length |= ord($delta{$pos++}) << 16; + if ($length == 0) $length = 0x10000; + $dest .= substr($base, $offset, $length); + } else { + /* take the next $byte bytes as they are */ + $dest .= substr($delta, $pos, $byte); + $pos += $byte; + } + } + + if (strlen($dest) !== $dst_size) { + throw new UnexpectedValueException( + "Deltified string expected to be $dst_size bytes, but actually " + . strlen($dest) . ' bytes' + ); + } + + return $dest; + } + + /** + * Parse a Git varint (variable-length integer). Used in the `_applyDelta()` + * method to read the delta header. + * + * @param string $string The string to parse + * @param int &$pos The position in the string to read from + * + * @return int The integer value + */ + private function _varint($string, &$pos = 0) + { + $varint = 0; + $bitmask = 0x80; + for ($i = 0; $bitmask & 0x80; $i += 7) { + $bitmask = ord($string{$pos++}); + $varint |= (($bitmask & 0x7F) << $i); + } + return $varint; + } + + /** + * Decodes a big endian modified base 128 number (refer to @see tag); this only + * appears to be used in one place, the offset delta in packfiles. The offset + * is the number of bytes to seek back from the start of the delta object to find + * the base object. + * + * This code has been implemented using the C code given in the @see tag below. + * + * @param string &$data The data to read from and decode the number + * + * @return Array Containing the base offset (number of bytes to seek back) and + * the length to use when reading the delta + * @see http://git.rsbx.net/Documents/Git_Data_Formats.txt + */ + private function _bigEndianNumber(&$data) + { + $i = 0; + $byte = ord($data{$i++}); + $number = $byte & 0x7F; + while ($byte & 0x80) { + $byte = ord($data{$i++}); + $number = (($number + 1) << 7) | ($byte & 0x7F); + } + + return array($number, $i); + } + +} diff --git a/3rdparty/granite/git/object/raw.php b/3rdparty/granite/git/object/raw.php new file mode 100644 index 0000000000..56f363c37b --- /dev/null +++ b/3rdparty/granite/git/object/raw.php @@ -0,0 +1,153 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git\Object; +use \InvalidArgumentException as InvalidArgumentException; + +/** + * Raw represents a raw Git object, using Index to locate + * packed objects. + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Raw +{ + /** + * Integer values for Git objects + * @see http://book.git-scm.com/7_the_packfile.html + */ + const OBJ_COMMIT = 1; + const OBJ_TREE = 2; + const OBJ_BLOB = 3; + const OBJ_TAG = 4; + const OBJ_OFS_DELTA = 6; + const OBJ_REF_DELTA = 7; + + /** + * The SHA-1 id of the requested object + */ + protected $sha; + /** + * The type of the requested object (see class constants) + */ + protected $type; + /** + * The binary string content of the requested object + */ + protected $content; + + /** + * Returns an instance of a raw Git object + * + * @param string $path The path to the repository root + * @param string $sha The SHA-1 id of the requested object + * + * @return Packed|Loose + */ + public static function factory($path, $sha) + { + $loose_path = $path + . 'objects/' + . substr($sha, 0, 2) + . '/' + . substr($sha, 2); + if (file_exists($loose_path)) { + return new Loose($path, $sha); + } else { + return self::_findPackedObject($path, $sha); + } + } + + /** + * Returns the raw content of the Git object requested + * + * @return string Raw object content + */ + public function content() + { + return $this->content; + } + + /** + * Returns the size of the Git object + * + * @return int The size of the object in bytes + */ + public function size() + { + return strlen($this->content); + } + + /** + * Returns the type of the object as either commit, tag, blob or tree + * + * @return string The object type + */ + public function type() + { + return $this->type; + } + + /** + * Searches a packfile for the SHA id and reads the object from the packfile + * + * @param string $path The path to the repository + * @param string $sha The SHA-1 id of the object being requested + * + * @throws \InvalidArgumentException + * @return array An array containing the type, size and object data + */ + private static function _findPackedObject($path, $sha) + { + $packfiles = glob( + $path + . 'objects' + . DIRECTORY_SEPARATOR + . 'pack' + . DIRECTORY_SEPARATOR + . 'pack-*.pack' + ); + + $offset = false; + foreach ($packfiles as $packfile) { + $packname = substr(basename($packfile, '.pack'), 5); + $idx = new Index($path, $packname); + $offset = $idx->find($sha); + + if ($offset !== false) { + break; // Found it + } + } + + if ($offset == false) { + throw new InvalidArgumentException("Could not find packed object $sha"); + } + + $packname = $path + . 'objects' + . DIRECTORY_SEPARATOR + . 'pack' + . DIRECTORY_SEPARATOR + . 'pack-' . $packname . '.pack'; + $object = new Packed($packname, $offset); + + return $object; + } + +} + +?> diff --git a/3rdparty/granite/git/repository.php b/3rdparty/granite/git/repository.php new file mode 100644 index 0000000000..30b58a39f5 --- /dev/null +++ b/3rdparty/granite/git/repository.php @@ -0,0 +1,293 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git; +use \InvalidArgumentException as InvalidArgumentException; +use \UnexpectedValueException as UnexpectedValueException; + +/** + * Repository represents a Git repository, providing a variety of methods for + * fetching objects from SHA-1 ids or the tip of a branch with `head()` + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Repository +{ + + /** + * The path to the repository root + */ + private $_path; + /** + * The indexed version of a commit, ready to write with `commit()` + */ + private $idx_commit; + /** + * The indexed version of a tree, modified to with `add()` and `remove()` + */ + private $idx_tree; + + /** + * Sets the repository path + * + * @param string $path The path to the repository root (i.e. /repo/.git/) + */ + public function __construct($path) + { + if (!is_dir($path)) { + throw new InvalidArgumentException("Unable to find directory $path"); + } elseif (!is_readable($path)) { + throw new InvalidArgumentException("Unable to read directory $path"); + } elseif (!is_dir($path . DIRECTORY_SEPARATOR . 'objects') + || !is_dir($path . DIRECTORY_SEPARATOR . 'refs') + ) { + throw new UnexpectedValueException( + "Invalid directory, could not find 'objects' or 'refs' in $path" + ); + } + + $this->_path = $path; + $this->idx_commit = $this->factory('commit'); + $this->idx_tree = $this->factory('tree'); + } + + /** + * Returns an object from the Repository of the given type, with the given + * SHA-1 id, or false if it cannot be found + * + * @param string $type The type (blob, commit, tag or tree) of object being + * requested + * @param string $sha The SHA-1 id of the object (or the name of a tag) + * + * @return Blob|Commit|Tag|Tree + */ + public function factory($type, $sha = null) + { + if (!in_array($type, array('blob', 'commit', 'tag', 'tree'))) { + throw new InvalidArgumentException("Invalid type: $type"); + } + + if ($type == 'tag') { + $sha = $this->_ref('tags' . DIRECTORY_SEPARATOR . $sha); + } + $type = 'Granite\\Git\\' . ucwords($type); + + return new $type($this->_path, $sha); + } + + /** + * Returns a Commit object representing the HEAD commit + * + * @param string $branch The branch name to lookup, defaults to 'master' + * + * @return Commit An object representing the HEAD commit + */ + public function head($branch = 'master', $value = NULL) + { + if ($value == NULL) + return $this->factory( + 'commit', $this->_ref('heads' . DIRECTORY_SEPARATOR . $branch) + ); + + file_put_contents( + $this->_path . DIRECTORY_SEPARATOR + . 'refs' . DIRECTORY_SEPARATOR + . 'heads' . DIRECTORY_SEPARATOR . 'master', + $value + ); + } + + /** + * Returns a string representing the repository's location, which may or may + * not be initialised + * + * @return string A string representing the repository's location + */ + public function path() + { + return $this->_path; + } + + /** + * Returns an array of the local branches under `refs/heads` + * + * @return array + */ + public function tags() + { + return $this->_refs('tags'); + } + + /** + * Returns an array of the local tags under `refs/tags` + * + * @return array + */ + public function branches() + { + return $this->_refs('heads'); + } + + private function _refs($type) + { + $dir = $this->_path . 'refs' . DIRECTORY_SEPARATOR . $type; + $refs = glob($dir . DIRECTORY_SEPARATOR . '*'); + foreach ($refs as &$ref) { + $ref = basename($ref); + } + return $refs; + } + + /** + * Initialises a Git repository + * + * @return boolean Returns true on success, false on error + */ + public static function init($path) + { + $path .= '/'; + if (!is_dir($path)) { + mkdir($path); + } elseif (is_dir($path . 'objects')) { + return false; + } + + mkdir($path . 'objects'); + mkdir($path . 'objects/info'); + mkdir($path . 'objects/pack'); + mkdir($path . 'refs'); + mkdir($path . 'refs/heads'); + mkdir($path . 'refs/tags'); + + file_put_contents($path . 'HEAD', 'ref: refs/heads/master'); + + return true; + } + + /** + * Writes the indexed commit to disk, with blobs added/removed via `add()` and + * `rm()` + * + * @param string $message The commit message + * @param string $author The author name + * + * @return boolean True on success, or false on failure + */ + public function commit($message, $author) + { + $user_string = $username . ' ' . time() . ' +0000'; + + try { + $parents = array($this->repo->head()->sha()); + } catch (InvalidArgumentException $e) { + $parents = array(); + } + + $this->idx_commit->message($message); + $this->idx_commit->author($user_string); + $this->idx_commit->committer($user_string); + $this->idx_commit->tree($this->idx_tree); + $commit->parents($parents); + + $this->idx_tree->write(); + $this->idx_commit->write(); + + $this->repo->head('master', $this->idx_commit->sha()); + + $this->idx_commit = $this->factory('commit'); + $this->idx_tree = $this->factory('tree'); + } + + /** + * Adds a file to the indexed commit, to be written to disk with `commit()` + * + * @param string $filename The filename to save it under + * @param Granite\Git\Blob $blob The raw blob object to add to the tree + */ + public function add($filename, Granite\Git\Blob $blob) + { + $blob->write(); + $nodes = $this->idx_tree->nodes(); + $nodes[$filename] = new Granite\Git\Tree\Node($filename, '100644', $blob->sha()); + $this->idx_tree->nodes($nodes); + } + + /** + * Removes a file from the indexed commit + */ + public function rm($filename) + { + $nodes = $this->idx_tree->nodes(); + unset($nodes[$filename]); + $this->idx_tree->nodes($nodes); + } + + /** + * Returns an SHA-1 id of the ref resource + * + * @param string $ref The ref name to lookup + * + * @return string An SHA-1 id of the ref resource + */ + private function _ref($ref) + { + // All refs are stored in `.git/refs` + $file = $this->_path . 'refs' . DIRECTORY_SEPARATOR . $ref; + + if (file_exists($file)) { + return trim(file_get_contents($file)); + } + + $sha = $this->_packedRef($ref); + + if ($sha == false) { + throw new InvalidArgumentException("The ref $ref could not be found"); + } + + return $sha; + } + + /** + * Returns an SHA-1 id of the ref resource, or false if it cannot be found + * + * @param string $ref The ref name to lookup + * + * @return string An SHA-1 id of the ref resource + */ + private function _packedRef($ref) + { + $sha = false; + if (file_exists($this->_path . 'packed-refs')) { + $file = fopen($this->_path . 'packed-refs', 'r'); + + while (($line = fgets($file)) !== false) { + $info = explode(' ', $line); + if (count($info) == 2 + && trim($info[1]) == 'refs' . DIRECTORY_SEPARATOR . $ref + ) { + $sha = trim($info[0]); + break; + } + } + + fclose($file); + } + + return $sha; + } + +} diff --git a/3rdparty/granite/git/tag.php b/3rdparty/granite/git/tag.php new file mode 100644 index 0000000000..e26ddaffa6 --- /dev/null +++ b/3rdparty/granite/git/tag.php @@ -0,0 +1,38 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git; + +/** + * Tag represents a full tag object + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Tag +{ + + public function __construct($path, $sha) + { + $this->sha = $sha; + } + + public function sha() + { + return $this->sha; + } + +} diff --git a/3rdparty/granite/git/tree.php b/3rdparty/granite/git/tree.php new file mode 100644 index 0000000000..2de7227453 --- /dev/null +++ b/3rdparty/granite/git/tree.php @@ -0,0 +1,198 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git; +use \Granite\Git\Tree\Node as Node; + +/** + * Tree represents a full tree object, with nodes pointing to other tree objects + * and file blobs + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Tree +{ + + /** + * The SHA-1 id of the requested tree + */ + private $sha; + /** + * The nodes/entries for the requested tree + */ + private $nodes = array(); + /** + * The path to the repository + */ + private $path; + + /** + * Reads a tree object by fetching the raw object + * + * @param string $path The path to the repository root + * @param string $sha The SHA-1 id of the requested object + */ + public function __construct($path, $sha = NULL, $dbg = FALSE) + { + $this->path = $path; + if ($sha !== NULL) { + $object = Object\Raw::factory($path, $sha); + $this->sha = $sha; + + if ($object->type() !== Object\Raw::OBJ_TREE) { + throw new \InvalidArgumentException( + "The object $sha is not a tree, type is " . $object->type() + ); + } + + $content = $object->content(); + file_put_contents('/tmp/tree_from_real_repo'.time(), $content); + $nodes = array(); + + for ($i = 0; $i < strlen($content); $i = $data_start + 21) { + $data_start = strpos($content, "\0", $i); + $info = substr($content, $i, $data_start-$i); + list($mode, $name) = explode(' ', $info, 2); + // Read the object SHA-1 id + $sha = bin2hex(substr($content, $data_start + 1, 20)); + + $this->nodes[$name] = new Node($name, $mode, $sha); + } + } + } + + /** + * Returns an array of Tree and Granite\Git\Blob objects, + * representing subdirectories and files + * + * @return array Array of Tree and Granite\Git\Blob objects + */ + public function nodes($nodes = null) + { + if ($nodes == null) { + return $this->nodes; + } + $this->nodes = $nodes; + } + + /** + * Adds a blob or a tree to the list of nodes + * + * @param string $name The basename (filename) of the blob or tree + * @param string $mode The mode of the blob or tree (see above) + * @param string $sha The SHA-1 id of the blob or tree to add + */ + public function add($name, $mode, $sha) + { + $this->nodes[$name] = new Node($name, $mode, $sha); + uasort($this->nodes, array($this, '_sort')); + } + + public function write() + { + $sha = $this->sha(); + $path = $this->path + . 'objects' + . DIRECTORY_SEPARATOR + . substr($sha, 0, 2) + . DIRECTORY_SEPARATOR + . substr($sha, 2); + // FIXME: currently writes loose objects only + if (file_exists($path)) { + return FALSE; + } + + if (!is_dir(dirname($path))) { + mkdir(dirname($path), 0777, TRUE); + } + + $loose = fopen($path, 'wb'); + $data = $this->_raw(); + $data = 'tree ' . strlen($data) . "\0" . $data; + $write = fwrite($loose, gzcompress($data)); + fclose($loose); + + return ($write !== FALSE); + } + + /** + * Returns the SHA-1 id of the Tree + * + * @return string SHA-1 id of the Tree + */ + public function sha() + { + $data = $this->_raw(); + $raw = 'tree ' . strlen($data) . "\0" . $data; + $this->sha = hash('sha1', $raw); + return $this->sha; + } + + /** + * Generates the raw object content to be saved to disk + */ + public function _raw() + { + uasort($this->nodes, array($this, '_sort')); + $data = ''; + foreach ($this->nodes as $node) + { + $data .= base_convert($node->mode(), 10, 8) . ' ' . $node->name() . "\0"; + $data .= pack('H40', $node->sha()); + } + file_put_contents('/tmp/tree_made'.time(), $data); + return $data; + } + + /** + * Sorts the node entries in a tree, general sort method adapted from original + * Git C code (see @see tag below). + * + * @return 1, 0 or -1 if the first entry is greater than, the same as, or less + * than the second, respectively. + * @see https://github.com/gitster/git/blob/master/read-cache.c Around line 352, + * the `base_name_compare` function + */ + public function _sort(&$a, &$b) + { + $length = strlen($a->name()) < strlen($b->name()) ? strlen($a->name()) : strlen($b->name()); + + $cmp = strncmp($a->name(), $b->name(), $length); + if ($cmp) { + return $cmp; + } + + $suffix1 = $a->name(); + $suffix1 = (strlen($suffix1) > $length) ? $suffix1{$length} : FALSE; + $suffix2 = $b->name(); + $suffix2 = (strlen($suffix2) > $length) ? $suffix2{$length} : FALSE; + if (!$suffix1 && $a->isDirectory()) { + $suffix1 = '/'; + } + if (!$suffix2 && $b->isDirectory()) { + $suffix2 = '/'; + } + if ($suffix1 < $suffix2) { + return -1; + } elseif ($suffix1 > $suffix2) { + return 1; + } + + return 0; + } + +} diff --git a/3rdparty/granite/git/tree/node.php b/3rdparty/granite/git/tree/node.php new file mode 100644 index 0000000000..f99eb1ae28 --- /dev/null +++ b/3rdparty/granite/git/tree/node.php @@ -0,0 +1,126 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ + +namespace Granite\Git\Tree; + +/** + * Node represents an entry in a Tree + * + * @category Git + * @package Granite + * @author Craig Roberts + * @license http://www.opensource.org/licenses/mit-license.php MIT Expat License + * @link http://craig0990.github.com/Granite/ + */ +class Node +{ + + /** + * Name of the file, directory or submodule + */ + private $_name; + /** + * Mode of the object, in octal + */ + private $_mode; + /** + * SHA-1 id of the tree + */ + private $_sha; + /** + * Boolean value for whether the entry represents a directory + */ + private $_is_dir; + /** + * Boolean value for whether the entry represents a submodule + */ + private $_is_submodule; + + /** + * Sets up a Node class with properties corresponding to the $mode parameter + * + * @param string $name The name of the object (file, directory or submodule name) + * @param int $mode The mode of the object, retrieved from the repository + * @param string $sha The SHA-1 id of the object + */ + public function __construct($name, $mode, $sha) + { + $this->_name = $name; + $this->_mode = intval($mode, 8); + $this->_sha = $sha; + + $this->_is_dir = (bool) ($this->_mode & 0x4000); + $this->_is_submodule = ($this->_mode == 0xE000); + } + + /** + * Returns a boolean value indicating whether the node is a directory + * + * @return boolean + */ + public function isDirectory() + { + return $this->_is_dir; + } + + /** + * Returns a boolean value indicating whether the node is a submodule + * + * @return boolean + */ + public function isSubmodule() + { + return $this->_is_submodule; + } + + /** + * Returns the object name + * + * @return string + */ + public function name() + { + return $this->_name; + } + + /** + * Returns the object's SHA-1 id + * + * @return string + */ + public function sha() + { + return $this->_sha; + } + + /** + * Returns the octal value of the file mode + * + * @return int + */ + public function mode() + { + return $this->_mode; + } + + public function type() + { + if ($this->isDirectory()) { + return 'tree'; + } elseif ($this->isSubmodule()) { + return 'commit'; + } else { + return 'blob'; + } + } +} diff --git a/apps/files_versioning/ajax/gethead.php b/apps/files_versioning/ajax/gethead.php new file mode 100644 index 0000000000..cc93b7a1d1 --- /dev/null +++ b/apps/files_versioning/ajax/gethead.php @@ -0,0 +1,12 @@ + $head)); diff --git a/apps/files_versioning/ajax/sethead.php b/apps/files_versioning/ajax/sethead.php new file mode 100644 index 0000000000..d1b2df9b00 --- /dev/null +++ b/apps/files_versioning/ajax/sethead.php @@ -0,0 +1,14 @@ + 10, + 'id' => 'files_versioning', + 'name' => 'Versioning and Backup' )); + +// Include stylesheets for the settings page +OC_Util::addStyle( 'files_versioning', 'settings' ); +OC_Util::addScript('files_versioning','settings'); + +// Register a settings section in the Admin > Personal page +OC_APP::registerPersonal('files_versioning','settings'); diff --git a/apps/files_versioning/appinfo/info.xml b/apps/files_versioning/appinfo/info.xml new file mode 100644 index 0000000000..d5546be54a --- /dev/null +++ b/apps/files_versioning/appinfo/info.xml @@ -0,0 +1,14 @@ + + + files_versioning + Versioning and Backup + 1.0.0 + GPLv2 + Craig Roberts + 3 + Versions files using Git repositories, providing a simple backup facility. Currently in *beta* and explicitly without warranty of any kind. + + + + + diff --git a/apps/files_versioning/css/settings.css b/apps/files_versioning/css/settings.css new file mode 100644 index 0000000000..afe2cd5508 --- /dev/null +++ b/apps/files_versioning/css/settings.css @@ -0,0 +1,3 @@ +#file_versioning_commit_chzn { + width: 15em; +} diff --git a/apps/files_versioning/js/settings.js b/apps/files_versioning/js/settings.js new file mode 100644 index 0000000000..8dd13bac03 --- /dev/null +++ b/apps/files_versioning/js/settings.js @@ -0,0 +1,25 @@ +$(document).ready(function(){ + $('#file_versioning_head').chosen(); + + $.getJSON(OC.filePath('files_versioning', 'ajax', 'gethead.php'), function(jsondata, status) { + + if (jsondata.head == 'HEAD') { + // Most recent commit, do nothing + } else { + $("#file_versioning_head").val(jsondata.head); + // Trigger the chosen update call + // See http://harvesthq.github.com/chosen/ + $("#file_versioning_head").trigger("liszt:updated"); + } + }); + + $('#file_versioning_head').change(function() { + + var data = $(this).serialize(); + $.post( OC.filePath('files_versioning', 'ajax', 'sethead.php'), data, function(data){ + if(data == 'error'){ + console.log('Saving new HEAD failed'); + } + }); + }); +}); diff --git a/apps/files_versioning/lib_granite.php b/apps/files_versioning/lib_granite.php new file mode 100644 index 0000000000..c69c62d9c4 --- /dev/null +++ b/apps/files_versioning/lib_granite.php @@ -0,0 +1,12 @@ +head(); +for ($i = 0; $i < 50; $i++) { + $commits[] = $commit; + $parents = $commit->parents(); + if (count($parents) > 0) { + $parent = $parents[0]; + } else { + break; + } + + $commit = $repository->factory('commit', $parent); +} + +$tmpl = new OC_Template( 'files_versioning', 'settings'); +$tmpl->assign('commits', $commits); +return $tmpl->fetchPage(); diff --git a/apps/files_versioning/templates/settings.php b/apps/files_versioning/templates/settings.php new file mode 100644 index 0000000000..17f4cc7f77 --- /dev/null +++ b/apps/files_versioning/templates/settings.php @@ -0,0 +1,12 @@ +
+ Versioning and Backup
+

Please note: Backing up large files (around 16MB+) will cause your backup history to grow very large, very quickly.

+ + +
diff --git a/apps/files_versioning/versionstorage.php b/apps/files_versioning/versionstorage.php new file mode 100644 index 0000000000..d083e623df --- /dev/null +++ b/apps/files_versioning/versionstorage.php @@ -0,0 +1,386 @@ +. + */ + +// Include Granite +require_once('lib_granite.php'); + +// Create a top-level 'Backup' directory if it does not already exist +$user = OC_User::getUser(); +if (OC_Filesystem::$loaded and !OC_Filesystem::is_dir('/Backup')) { + OC_Filesystem::mkdir('/Backup'); + OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD'); +} + +// Generate the repository path (currently using 'full' repositories, as opposed to bare ones) +$repo_path = DIRECTORY_SEPARATOR + . OC_User::getUser() + . DIRECTORY_SEPARATOR + . 'files' + . DIRECTORY_SEPARATOR + . 'Backup'; + +// Mount the 'Backup' folder using the versioned storage provider below +OC_Filesystem::mount('OC_Filestorage_Versioned', array('repo'=>$repo_path), $repo_path . DIRECTORY_SEPARATOR); + +class OC_Filestorage_Versioned extends OC_Filestorage { + + /** + * Holds an instance of Granite\Git\Repository + */ + protected $repo; + + /** + * Constructs a new OC_Filestorage_Versioned instance, expects an associative + * array with a `repo` key set to the path of the repository's `.git` folder + * + * @param array $parameters An array containing the key `repo` pointing to the + * repository path. + */ + public function __construct($parameters) { + // Get the full path to the repository folder + $path = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data') + . $parameters['repo'] + . DIRECTORY_SEPARATOR + . '.git' + . DIRECTORY_SEPARATOR; + + try { + // Attempt to load the repository + $this->repo = new Granite\Git\Repository($path); + } catch (InvalidArgumentException $e) { + // $path is not a valid Git repository, we must create one + Granite\Git\Repository::init($path); + + // Load the newly-initialised repository + $this->repo = new Granite\Git\Repository($path); + + /** + * Create an initial commit with a README file + * FIXME: This functionality should be transferred to the Granite library + */ + $blob = new Granite\Git\Blob($this->repo->path()); + $blob->content('Your Backup directory is now ready for use.'); + + // Create a new tree to hold the README file + $tree = $this->repo->factory('tree'); + // Create a tree node to represent the README blob + $tree_node = new Granite\Git\Tree\Node('README', '100644', $blob->sha()); + $tree->nodes(array($tree_node->name() => $tree_node)); + + // Create an initial commit + $commit = new Granite\Git\Commit($this->repo->path()); + $user_string = OC_User::getUser() . ' ' . time() . ' +0000'; + $commit->author($user_string); + $commit->committer($user_string); + $commit->message('Initial commit'); + $commit->tree($tree); + + // Write it all to disk + $blob->write(); + $tree->write(); + $commit->write(); + + // Update the HEAD for the 'master' branch + $this->repo->head('master', $commit->sha()); + } + + // Update the class pointer to the HEAD + $head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD'); + + // Load the most recent commit if the preference is not set + if ($head == 'HEAD') { + $this->head = $this->repo->head()->sha(); + } else { + $this->head = $head; + } + } + + public function mkdir($path) { + if (mkdir("versioned:/{$this->repo->path()}$path#{$this->head}")) { + $this->head = $this->repo->head()->sha(); + OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $head); + return true; + } + + return false; + } + + public function rmdir($path) { + + } + + /** + * Returns a directory handle to the requested path, or FALSE on failure + * + * @param string $path The directory path to open + * + * @return boolean|resource A directory handle, or FALSE on failure + */ + public function opendir($path) { + return opendir("versioned:/{$this->repo->path()}$path#{$this->head}"); + } + + /** + * Returns TRUE if $path is a directory, or FALSE if not + * + * @param string $path The path to check + * + * @return boolean + */ + public function is_dir($path) { + return $this->filetype($path) == 'dir'; + } + + /** + * Returns TRUE if $path is a file, or FALSE if not + * + * @param string $path The path to check + * + * @return boolean + */ + public function is_file($path) { + return $this->filetype($path) == 'file'; + } + + public function stat($path) + { + return stat("versioned:/{$this->repo->path()}$path#{$this->head}"); + } + + /** + * Returns the strings 'dir' or 'file', depending on the type of $path + * + * @param string $path The path to check + * + * @return string Returns 'dir' if a directory, 'file' otherwise + */ + public function filetype($path) { + if ($path == "" || $path == "/") { + return 'dir'; + } else { + if (substr($path, -1) == '/') { + $path = substr($path, 0, -1); + } + + $node = $this->tree_search($this->repo, $this->repo->factory('commit', $this->head)->tree(), $path); + + // Does it exist, or is it new? + if ($node == null) { + // New file + return 'file'; + } else { + // Is it a tree? + try { + $this->repo->factory('tree', $node); + return 'dir'; + } catch (InvalidArgumentException $e) { + // Nope, must be a blob + return 'file'; + } + } + } + } + + public function filesize($path) { + return filesize("versioned:/{$this->repo->path()}$path#{$this->head}"); + } + + /** + * Returns a boolean value representing whether $path is readable + * + * @param string $path The path to check + *( + * @return boolean Whether or not the path is readable + */ + public function is_readable($path) { + return true; + } + + /** + * Returns a boolean value representing whether $path is writable + * + * @param string $path The path to check + *( + * @return boolean Whether or not the path is writable + */ + public function is_writable($path) { + + $head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD'); + if ($head !== 'HEAD' && $head !== $this->repo->head()->sha()) { + // Cannot modify previous commits + return false; + } + return true; + } + + /** + * Returns a boolean value representing whether $path exists + * + * @param string $path The path to check + *( + * @return boolean Whether or not the path exists + */ + public function file_exists($path) { + return file_exists("versioned:/{$this->repo->path()}$path#{$this->head}"); + } + + /** + * Returns an integer value representing the inode change time + * (NOT IMPLEMENTED) + * + * @param string $path The path to check + *( + * @return int Timestamp of the last inode change + */ + public function filectime($path) { + return -1; + } + + /** + * Returns an integer value representing the file modification time + * + * @param string $path The path to check + *( + * @return int Timestamp of the last file modification + */ + public function filemtime($path) { + return filemtime("versioned:/{$this->repo->path()}$path#{$this->head}"); + } + + public function file_get_contents($path) { + return file_get_contents("versioned:/{$this->repo->path()}$path#{$this->head}"); + } + + public function file_put_contents($path, $data) { + $success = file_put_contents("versioned:/{$this->repo->path()}$path#{$this->head}", $data); + if ($success !== false) { + // Update the HEAD in the preferences + OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $this->repo->head()->sha()); + return $success; + } + + return false; + } + + public function unlink($path) { + + } + + public function rename($path1, $path2) { + + } + + public function copy($path1, $path2) { + + } + + public function fopen($path, $mode) { + return fopen("versioned:/{$this->repo->path()}$path#{$this->head}", $mode); + } + + public function getMimeType($path) { + if ($this->filetype($path) == 'dir') { + return 'httpd/unix-directory'; + } elseif ($this->filesize($path) == 0) { + // File's empty, returning text/plain allows opening in the web editor + return 'text/plain'; + } else { + $finfo = new finfo(FILEINFO_MIME_TYPE); + /** + * We need to represent the repository path, the file path, and the + * revision, which can be simply achieved with a convention of using + * `.git` in the repository directory (bare or not) and the '#part' + * segment of a URL to specify the revision. For example + * + * versioned://var/www/myrepo.git/docs/README.md#HEAD ('bare' repo) + * versioned://var/www/myrepo/.git/docs/README.md#HEAD ('full' repo) + * versioned://var/www/myrepo/.git/docs/README.md#6a8f...8a54 ('full' repo and SHA-1 commit ID) + */ + $mime = $finfo->buffer(file_get_contents("versioned:/{$this->repo->path()}$path#{$this->head}")); + return $mime; + } + } + + /** + * Generates a hash based on the file contents + * + * @param string $type The hashing algorithm to use (e.g. 'md5', 'sha256', etc.) + * @param string $path The file to be hashed + * @param boolean $raw Outputs binary data if true, lowercase hex digits otherwise + * + * @return string Hashed string representing the file contents + */ + public function hash($type, $path, $raw) { + return hash($type, file_get_contents($path), $raw); + } + + public function free_space($path) { + } + + public function search($query) { + + } + + public function touch($path, $mtime=null) { + + } + + + public function getLocalFile($path) { + } + + /** + * Recursively searches a tree for a path, returning FALSE if is not found + * or an SHA-1 id if it is found. + * + * @param string $repo The repository containing the tree object + * @param string $tree The tree object to search + * @param string $path The path to search for (relative to the tree) + * @param int $depth The depth of the current search (for recursion) + * + * @return string|boolean The SHA-1 id of the sub-tree + */ + private function tree_search($repo, $tree, $path, $depth = 0) + { + $paths = array_values(explode(DIRECTORY_SEPARATOR, $path)); + + $current_path = $paths[$depth]; + + $nodes = $tree->nodes(); + foreach ($nodes as $node) { + if ($node->name() == $current_path) { + + if (count($paths)-1 == $depth) { + // Stop, found it + return $node->sha(); + } + + // Recurse if necessary + if ($node->isDirectory()) { + $tree = $this->repo->factory('tree', $node->sha()); + return $this->tree_search($repo, $tree, $path, $depth + 1); + } + } + } + + return false; + } + +} diff --git a/apps/files_versioning/versionwrapper.php b/apps/files_versioning/versionwrapper.php new file mode 100644 index 0000000000..b83a4fd3b2 --- /dev/null +++ b/apps/files_versioning/versionwrapper.php @@ -0,0 +1,686 @@ +tree); + return true; + } + + /** + * Open directory handle + */ + public function dir_opendir($path, $options) { + // Parse the URL into a repository directory, file path and commit ID + list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); + + if ($repo_file == '' || $repo_file == '/') { + // Set the tree property for the future `readdir()` etc. calls + $this->tree = array_values($this->commit->tree()->nodes()); + return true; + } elseif ($this->tree_search($this->repo, $this->commit->tree(), $repo_file) !== false) { + // Something exists at this path, is it a directory though? + try { + $tree = $this->repo->factory( + 'tree', + $this->tree_search($this->repo, $this->commit->tree(), $repo_file) + ); + $this->tree = array_values($tree->nodes()); + return true; + } catch (InvalidArgumentException $e) { + // Trying to call `opendir()` on a file, return false below + } + } + + // Unable to find the directory, return false + return false; + } + + /** + * Read entry from directory handle + */ + public function dir_readdir() { + return isset($this->tree[$this->dir_position]) + ? $this->tree[$this->dir_position++]->name() + : false; + } + + /** + * Rewind directory handle + */ + public function dir_rewinddir() { + $this->dir_position = 0; + } + + /** + * Create a directory + * Git doesn't track empty directories, so a ".empty" file is added instead + */ + public function mkdir($path, $mode, $options) { + // Parse the URL into a repository directory, file path and commit ID + list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); + + // Create an empty file for Git + $empty = new Granite\Git\Blob($this->repo->path()); + $empty->content(''); + $empty->write(); + + if (dirname($repo_file) == '.') { + // Adding a new directory to the root tree + $tree = $this->repo->head()->tree(); + } else { + $tree = $this->repo->factory('tree', $this->tree_search( + $this->repo, $this->repo->head()->tree(), dirname($repo_file) + ) + ); + } + + // Create our new tree, with our empty file + $dir = $this->repo->factory('tree'); + $nodes = array(); + $nodes[self::EMPTYFILE] = new Granite\Git\Tree\Node(self::EMPTYFILE, '100644', $empty->sha()); + $dir->nodes($nodes); + $dir->write(); + + // Add our new tree to its parent + $nodes = $tree->nodes(); + $nodes[basename($repo_file)] = new Granite\Git\Tree\Node(basename($repo_file), '040000', $dir->sha()); + $tree->nodes($nodes); + $tree->write(); + + // We need to recursively update each parent tree, since they are all + // hashed and the changes will cascade back up the chain + + // So, we're currently at the bottom-most directory + $current_dir = dirname($repo_file); + $previous_tree = $tree; + + if ($current_dir !== '.') { + do { + // Determine the parent directory + $previous_dir = $current_dir; + $current_dir = dirname($current_dir); + + $current_tree = $current_dir !== '.' + ? $this->repo->factory( + 'tree', $this->tree_search( + $this->repo, + $this->repo->head()->tree(), + $current_dir + ) + ) + : $this->repo->head()->tree(); + + $current_nodes = $current_tree->nodes(); + $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node( + basename($previous_dir), '040000', $previous_tree->sha() + ); + $current_tree->nodes($current_nodes); + $current_tree->write(); + + $previous_tree = $current_tree; + } while ($current_dir !== '.'); + + $tree = $previous_tree; + } + + // Create a new commit to represent this write + $commit = $this->repo->factory('commit'); + $username = OC_User::getUser(); + $user_string = $username . ' ' . time() . ' +0000'; + $commit->author($user_string); + $commit->committer($user_string); + $commit->message("$username created the `$repo_file` directory, " . date('d F Y H:i', time()) . '.'); + $commit->parents(array($this->repo->head()->sha())); + $commit->tree($tree); + + // Write it to disk + $commit->write(); + + // Update the HEAD for the 'master' branch + $this->repo->head('master', $commit->sha()); + + return true; + } + + /** + * Renames a file or directory + */ + public function rename($path_from, $path_to) { + + } + + /** + * Removes a directory + */ + public function rmdir($path, $options) { + + } + + /** + * Retrieve the underlaying resource (NOT IMPLEMENTED) + */ + public function stream_cast($cast_as) { + return false; + } + + /** + * Close a resource + */ + public function stream_close() { + unset($this->blob); + return true; + } + + /** + * Tests for end-of-file on a file pointer + */ + public function stream_eof() { + return !($this->file_position < strlen($this->blob)); + } + + /** + * Flushes the output (NOT IMPLEMENTED) + */ + public function stream_flush() { + return false; + } + + /** + * Advisory file locking (NOT IMPLEMENTED) + */ + public function stream_lock($operation) { + return false; + } + + /** + * Change stream options (NOT IMPLEMENTED) + * Called in response to `chgrp()`, `chown()`, `chmod()` and `touch()` + */ + public function stream_metadata($path, $option, $var) { + return false; + } + + /** + * Opens file or URL + */ + public function stream_open($path, $mode, $options, &$opened_path) { + // Store the path, so we can use it later in `stream_write()` if necessary + $this->path = $path; + // Parse the URL into a repository directory, file path and commit ID + list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); + + $file = $this->tree_search($this->repo, $this->commit->tree(), $repo_file); + if ($file !== false) { + try { + $this->blob = $this->repo->factory('blob', $file)->content(); + return true; + } catch (InvalidArgumentException $e) { + // Trying to open a directory, return false below + } + } elseif ($mode !== 'r') { + // All other modes allow opening for reading and writing, clearly + // some 'write' files may not exist yet... + return true; + } + + // File could not be found or is not actually a file + return false; + } + + /** + * Read from stream + */ + public function stream_read($count) { + // Fetch the remaining set of bytes + $bytes = substr($this->blob, $this->file_position, $count); + + // If EOF or empty string, return false + if ($bytes == '' || $bytes == false) { + return false; + } + + // If $count does not extend past EOF, add $count to stream offset + if ($this->file_position + $count < strlen($this->blob)) { + $this->file_position += $count; + } else { + // Otherwise return all remaining bytes + $this->file_position = strlen($this->blob); + } + + return $bytes; + } + + /** + * Seeks to specific location in a stream + */ + public function stream_seek($offset, $whence = SEEK_SET) { + $new_offset = false; + + switch ($whence) + { + case SEEK_SET: + $new_offset = $offset; + break; + case SEEK_CUR: + $new_offset = $this->file_position += $offset; + break; + case SEEK_END: + $new_offset = strlen($this->blob) + $offset; + break; + } + + $this->file_position = $offset; + + return ($new_offset !== false); + } + + /** + * Change stream options (NOT IMPLEMENTED) + */ + public function stream_set_option($option, $arg1, $arg2) { + return false; + } + + /** + * Retrieve information about a file resource (NOT IMPLEMENTED) + */ + public function stream_stat() { + + } + + /** + * Retrieve the current position of a stream + */ + public function stream_tell() { + return $this->file_position; + } + + /** + * Truncate stream + */ + public function stream_truncate($new_size) { + + } + + /** + * Write to stream + * FIXME: Could use heavy refactoring + */ + public function stream_write($data) { + /** + * FIXME: This also needs to be added to Granite, in the form of `add()`, + * `rm()` and `commit()` calls + */ + + // Parse the URL into a repository directory, file path and commit ID + list($this->repo, $repo_file, $this->commit) = $this->parse_url($this->path); + + $node = $this->tree_search($this->repo, $this->commit->tree(), $repo_file); + + if ($node !== false) { + // File already exists, attempting modification of existing tree + try { + $this->repo->factory('blob', $node); + + // Create our new blob with the provided $data + $blob = $this->repo->factory('blob'); + $blob->content($data); + $blob->write(); + + // We know the tree exists, so strip the filename from the path and + // find it... + + if (dirname($repo_file) == '.' || dirname($repo_file) == '') { + // Root directory + $tree = $this->repo->head()->tree(); + } else { + // Sub-directory + $tree = $this->repo->factory('tree', $this->tree_search( + $this->repo, + $this->repo->head()->tree(), + dirname($repo_file) + ) + ); + } + + // Replace the old blob with our newly modified one + $tree_nodes = $tree->nodes(); + $tree_nodes[basename($repo_file)] = new Granite\Git\Tree\Node( + basename($repo_file), '100644', $blob->sha() + ); + $tree->nodes($tree_nodes); + $tree->write(); + + // We need to recursively update each parent tree, since they are all + // hashed and the changes will cascade back up the chain + + // So, we're currently at the bottom-most directory + $current_dir = dirname($repo_file); + $previous_tree = $tree; + + if ($current_dir !== '.') { + do { + // Determine the parent directory + $previous_dir = $current_dir; + $current_dir = dirname($current_dir); + + $current_tree = $current_dir !== '.' + ? $this->repo->factory( + 'tree', $this->tree_search( + $this->repo, + $this->repo->head()->tree(), + $current_dir + ) + ) + : $this->repo->head()->tree(); + + $current_nodes = $current_tree->nodes(); + $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node( + basename($previous_dir), '040000', $previous_tree->sha() + ); + $current_tree->nodes($current_nodes); + $current_tree->write(); + + $previous_tree = $current_tree; + } while ($current_dir !== '.'); + } + + // Create a new commit to represent this write + $commit = $this->repo->factory('commit'); + $username = OC_User::getUser(); + $user_string = $username . ' ' . time() . ' +0000'; + $commit->author($user_string); + $commit->committer($user_string); + $commit->message("$username modified the `$repo_file` file, " . date('d F Y H:i', time()) . '.'); + $commit->parents(array($this->repo->head()->sha())); + $commit->tree($previous_tree); + + // Write it to disk + $commit->write(); + + // Update the HEAD for the 'master' branch + $this->repo->head('master', $commit->sha()); + + // If we made it this far, write was successful - update the stream + // position and return the number of bytes written + $this->file_position += strlen($data); + return strlen($data); + + } catch (InvalidArgumentException $e) { + // Attempting to write to a directory or other error, fail + return 0; + } + } else { + // File does not exist, needs to be created + + // Create our new blob with the provided $data + $blob = $this->repo->factory('blob'); + $blob->content($data); + $blob->write(); + + if (dirname($repo_file) == '.') { + // Trying to add a new file to the root tree, nice and easy + $tree = $this->repo->head()->tree(); + $tree_nodes = $tree->nodes(); + $tree_nodes[basename($repo_file)] = new Granite\Git\Tree\Node( + basename($repo_file), '100644', $blob->sha() + ); + $tree->nodes($tree_nodes); + $tree->write(); + } else { + // Trying to add a new file to a subdirectory, try and find it + $tree = $this->repo->factory('tree', $this->tree_search( + $this->repo, $this->repo->head()->tree(), dirname($repo_file) + ) + ); + + // Add the blob to the tree + $nodes = $tree->nodes(); + $nodes[basename($repo_file)] = new Granite\Git\Tree\Node( + basename($repo_file), '100644', $blob->sha() + ); + $tree->nodes($nodes); + $tree->write(); + + // We need to recursively update each parent tree, since they are all + // hashed and the changes will cascade back up the chain + + // So, we're currently at the bottom-most directory + $current_dir = dirname($repo_file); + $previous_tree = $tree; + + if ($current_dir !== '.') { + do { + // Determine the parent directory + $previous_dir = $current_dir; + $current_dir = dirname($current_dir); + + $current_tree = $current_dir !== '.' + ? $this->repo->factory( + 'tree', $this->tree_search( + $this->repo, + $this->repo->head()->tree(), + $current_dir + ) + ) + : $this->repo->head()->tree(); + + $current_nodes = $current_tree->nodes(); + $current_nodes[basename($previous_dir)] = new Granite\Git\Tree\Node( + basename($previous_dir), '040000', $previous_tree->sha() + ); + $current_tree->nodes($current_nodes); + $current_tree->write(); + + $previous_tree = $current_tree; + } while ($current_dir !== '.'); + + $tree = $previous_tree; + } + } + + // Create a new commit to represent this write + $commit = $this->repo->factory('commit'); + $username = OC_User::getUser(); + $user_string = $username . ' ' . time() . ' +0000'; + $commit->author($user_string); + $commit->committer($user_string); + $commit->message("$username created the `$repo_file` file, " . date('d F Y H:i', time()) . '.'); + $commit->parents(array($this->repo->head()->sha())); + $commit->tree($tree); // Top-level tree (NOT the newly modified tree) + + // Write it to disk + $commit->write(); + + // Update the HEAD for the 'master' branch + $this->repo->head('master', $commit->sha()); + + // If we made it this far, write was successful - update the stream + // position and return the number of bytes written + $this->file_position += strlen($data); + return strlen($data); + } + + // Write failed + return 0; + } + + /** + * Delete a file + */ + public function unlink($path) { + + } + + /** + * Retrieve information about a file + */ + public function url_stat($path, $flags) { + // Parse the URL into a repository directory, file path and commit ID + list($this->repo, $repo_file, $this->commit) = $this->parse_url($path); + + $node = $this->tree_search($this->repo, $this->commit->tree(), $repo_file); + + if ($node == false && $this->commit->sha() == $this->repo->head()->sha()) { + // A new file - no information available + $size = 0; + $mtime = -1; + } else { + + // Is it a directory? + try { + $this->repo->factory('tree', $node); + $size = 4096; // FIXME + } catch (InvalidArgumentException $e) { + // Must be a file + $size = strlen(file_get_contents($path)); + } + + // Parse the timestamp from the commit message + preg_match('/[0-9]{10}+/', $this->commit->committer(), $matches); + $mtime = $matches[0]; + } + + $stat["dev"] = ""; + $stat["ino"] = ""; + $stat["mode"] = ""; + $stat["nlink"] = ""; + $stat["uid"] = ""; + $stat["gid"] = ""; + $stat["rdev"] = ""; + $stat["size"] = $size; + $stat["atime"] = $mtime; + $stat["mtime"] = $mtime; + $stat["ctime"] = $mtime; + $stat["blksize"] = ""; + $stat["blocks"] = ""; + + return $stat; + } + + /** + * Debug function for development purposes + */ + private function debug($message, $level = OC_Log::DEBUG) + { + if ($this->debug) { + OC_Log::write('files_versioning', $message, $level); + } + } + + /** + * Parses a URL of the form: + * `versioned://path/to/git/repository/.git/path/to/file#SHA-1-commit-id` + * FIXME: Will throw an InvalidArgumentException if $path is invaid + * + * @param string $path The path to parse + * + * @return array An array containing an instance of Granite\Git\Repository, + * the file path, and an instance of Granite\Git\Commit + * @throws InvalidArgumentException If the repository cannot be loaded + */ + private function parse_url($path) + { + preg_match('/\/([A-Za-z0-9\/]+\.git\/)([A-Za-z0-9\/\.\/]*)(#([A-Fa-f0-9]+))*/', $path, $matches); + + // Load up the repo + $repo = new \Granite\Git\Repository($matches[1]); + // Parse the filename (stripping any trailing slashes) + $repo_file = $matches[2]; + if (substr($repo_file, -1) == '/') { + $repo_file = substr($repo_file, 0, -1); + } + + // Default to HEAD if no commit is provided + $repo_commit = isset($matches[4]) + ? $matches[4] + : $repo->head()->sha(); + + // Load the relevant commit + $commit = $repo->factory('commit', $repo_commit); + + return array($repo, $repo_file, $commit); + } + + /** + * Recursively searches a tree for a path, returning FALSE if is not found + * or an SHA-1 id if it is found. + * + * @param string $repo The repository containing the tree object + * @param string $tree The tree object to search + * @param string $path The path to search for (relative to the tree) + * @param int $depth The depth of the current search (for recursion) + * + * @return string|boolean The SHA-1 id of the sub-tree + */ + private function tree_search($repo, $tree, $path, $depth = 0) + { + $paths = array_values(explode(DIRECTORY_SEPARATOR, $path)); + + $current_path = $paths[$depth]; + + $nodes = $tree->nodes(); + foreach ($nodes as $node) { + if ($node->name() == $current_path) { + + if (count($paths)-1 == $depth) { + // Stop, found it + return $node->sha(); + } + + // Recurse if necessary + if ($node->isDirectory()) { + $tree = $this->repo->factory('tree', $node->sha()); + return $this->tree_search($repo, $tree, $path, $depth + 1); + } + } + } + + return false; + } + +} From 6cf3f10d3546eda09ceb823af5c2cf1190f18743 Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Mon, 9 Apr 2012 22:51:53 +0200 Subject: [PATCH 126/133] fix includes --- apps/files_versioning/lib_granite.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/files_versioning/lib_granite.php b/apps/files_versioning/lib_granite.php index c69c62d9c4..62aebdd99d 100644 --- a/apps/files_versioning/lib_granite.php +++ b/apps/files_versioning/lib_granite.php @@ -1,12 +1,12 @@ Date: Tue, 10 Apr 2012 13:53:27 +0200 Subject: [PATCH 127/133] fix: drag'n'drop actions with files containing special chars, fixes oc-420 --- files/js/files.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/js/files.js b/files/js/files.js index df9f45a7af..1c0a40c236 100644 --- a/files/js/files.js +++ b/files/js/files.js @@ -417,7 +417,7 @@ var folderDropOptions={ var dir=$('#dir').val(); $.ajax({ url: 'ajax/move.php', - data: "dir="+dir+"&file="+file+'&target='+dir+'/'+target, + data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(dir)+'/'+encodeURIComponent(target), complete: function(data){boolOperationFinished(data, function(){ var el = $('#fileList tr').filterAttr('data-file',file).find('td.filename'); el.draggable('destroy'); @@ -443,7 +443,7 @@ var crumbDropOptions={ } $.ajax({ url: 'ajax/move.php', - data: "dir="+dir+"&file="+file+'&target='+target, + data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(target), complete: function(data){boolOperationFinished(data, function(){ FileList.remove(file); });} From 47f94548ce4c015782ebfe8566f009f5d4091fb3 Mon Sep 17 00:00:00 2001 From: Frank Karlitschek Date: Tue, 10 Apr 2012 15:05:33 +0200 Subject: [PATCH 128/133] =?UTF-8?q?don=C2=B4t=20enable=20by=20default.=20s?= =?UTF-8?q?till=20broken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/files_versioning/appinfo/info.xml | 1 - apps/files_versioning/lib_granite.php | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/files_versioning/appinfo/info.xml b/apps/files_versioning/appinfo/info.xml index d5546be54a..4c67894f9f 100644 --- a/apps/files_versioning/appinfo/info.xml +++ b/apps/files_versioning/appinfo/info.xml @@ -7,7 +7,6 @@ Craig Roberts 3 Versions files using Git repositories, providing a simple backup facility. Currently in *beta* and explicitly without warranty of any kind. - diff --git a/apps/files_versioning/lib_granite.php b/apps/files_versioning/lib_granite.php index 62aebdd99d..571e5cea63 100644 --- a/apps/files_versioning/lib_granite.php +++ b/apps/files_versioning/lib_granite.php @@ -1,12 +1,12 @@ Date: Sat, 31 Mar 2012 00:03:21 +0200 Subject: [PATCH 129/133] Silence error for missing appinfo info file --- lib/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/app.php b/lib/app.php index 7d5e8fa91c..1c81fbd424 100755 --- a/lib/app.php +++ b/lib/app.php @@ -319,7 +319,7 @@ class OC_App{ $file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml'; } $data=array(); - $content=file_get_contents($file); + $content=@file_get_contents($file); if(!$content){ return; } From fd72556fd16260088ec4a4340b6ea5d9e6a126a3 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 10 Apr 2012 21:15:38 +0200 Subject: [PATCH 130/133] Contacts: Make part.no_contacts.php translatable --- apps/contacts/templates/part.no_contacts.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/contacts/templates/part.no_contacts.php b/apps/contacts/templates/part.no_contacts.php index d24f7ae980..89b7575293 100644 --- a/apps/contacts/templates/part.no_contacts.php +++ b/apps/contacts/templates/part.no_contacts.php @@ -1,8 +1,8 @@
- You have no contacts in your list. + t('You have no contacts in your list.') ?>
- - - + + +
-
\ No newline at end of file + From 3f6e971571825dd66650348ef84b4e57d4cd15b8 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 10 Apr 2012 21:19:03 +0200 Subject: [PATCH 131/133] Contacts: Don't lowercase categories value when saving --- apps/contacts/ajax/saveproperty.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php index 4cef4d1e7a..99d55e7927 100644 --- a/apps/contacts/ajax/saveproperty.php +++ b/apps/contacts/ajax/saveproperty.php @@ -96,7 +96,7 @@ switch($element) { //$value = getOtherValue(); } break; - case 'CATEGORIES': + //case 'CATEGORIES': /* multi autocomplete triggers an save with empty value if (!$value) { $value = $vcard->getAsString('CATEGORIES'); From 2dfa02a34647010d26efe61598ff800c94b9c0d3 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 10 Apr 2012 21:53:36 +0200 Subject: [PATCH 132/133] Contacts: fix function names to coding standard --- apps/contacts/js/contacts.js | 10 +++++----- apps/contacts/templates/part.contact.php | 2 +- apps/contacts/templates/part.no_contacts.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index b2678e8c52..6f34a42a73 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -254,12 +254,12 @@ Contacts={ }); } }, - do_export:function() { + doExport:function() { document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id; //$.get(OC.linkTo('contacts', 'export.php'),{'contactid':this.id},function(jsondata){ //}); }, - do_import:function(){ + doImport:function(){ Contacts.UI.notImplemented(); }, add:function(n, fn, aid, isnew){ // add a new contact @@ -323,7 +323,7 @@ Contacts={ } }); }, - do_delete:function() { + doDelete:function() { $('#contacts_deletecard').tipsy('hide'); OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) { if(answer == true) { @@ -1264,7 +1264,7 @@ Contacts={ }); } }, - do_import:function(){ + doImport:function(){ Contacts.UI.notImplemented(); }, submit:function(button, bookid){ @@ -1364,7 +1364,7 @@ $(document).ready(function(){ }); $('#contacts_deletecard').live('click',function(){ - Contacts.UI.Card.do_delete(); + Contacts.UI.Card.doDelete(); }); $('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) { diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index 03d2fad853..ff1f081c4d 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -17,7 +17,7 @@ $id = isset($_['id']) ? $_['id'] : '';
  • t('Categories'); ?>
  • - + diff --git a/apps/contacts/templates/part.no_contacts.php b/apps/contacts/templates/part.no_contacts.php index 89b7575293..7024a142ae 100644 --- a/apps/contacts/templates/part.no_contacts.php +++ b/apps/contacts/templates/part.no_contacts.php @@ -1,7 +1,7 @@
    t('You have no contacts in your list.') ?>
    - +
    From 9f547a1b3987838fda8db238a8999c9f474bb542 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 10 Apr 2012 21:53:51 +0200 Subject: [PATCH 133/133] VCategories: fix function names to coding standard --- core/js/oc-vcategories.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/js/oc-vcategories.js b/core/js/oc-vcategories.js index 931ea37edb..e3b1abba08 100644 --- a/core/js/oc-vcategories.js +++ b/core/js/oc-vcategories.js @@ -19,7 +19,7 @@ OCCategories={ height: 350, minHeight:200, width: 250, minWidth: 200, buttons: { 'Delete':function() { - OCCategories.do_delete(); + OCCategories.doDelete(); }, 'Rescan':function() { OCCategories.rescan(); @@ -53,7 +53,7 @@ OCCategories={ } }); }, - do_delete:function(){ + doDelete:function(){ var categories = $('#categorylist').find('input[type="checkbox"]').serialize(); categories += '&app=' + OCCategories.app; console.log('OCCategories.delete: ' + categories);