summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/admin_export/appinfo/info.xml11
-rw-r--r--apps/admin_export/settings.php96
-rw-r--r--apps/admin_export/templates/settings.php13
-rw-r--r--apps/admin_migrate/appinfo/app.php (renamed from apps/admin_export/appinfo/app.php)12
-rw-r--r--apps/admin_migrate/appinfo/info.xml11
-rw-r--r--apps/admin_migrate/settings.php57
-rw-r--r--apps/admin_migrate/templates/settings.php31
-rw-r--r--apps/bookmarks/appinfo/app.php1
-rw-r--r--apps/bookmarks/appinfo/migrate.php68
-rw-r--r--apps/contacts/ajax/addcontact.php7
-rw-r--r--apps/contacts/ajax/saveproperty.php60
-rw-r--r--apps/contacts/appinfo/migrate.php68
-rw-r--r--apps/contacts/css/contacts.css40
-rw-r--r--apps/contacts/img/person_large.pngbin11517 -> 7929 bytes
-rw-r--r--apps/contacts/index.php22
-rw-r--r--apps/contacts/js/contacts.js203
-rw-r--r--apps/contacts/js/jquery.combobox.js23
-rw-r--r--apps/contacts/js/jquery.multi-autocomplete.js4
-rw-r--r--apps/contacts/lib/addressbook.php2
-rw-r--r--apps/contacts/templates/index.php1
-rw-r--r--apps/contacts/templates/part.contact.php84
-rw-r--r--apps/contacts/templates/part.edit_address_dialog.php14
-rw-r--r--apps/contacts/templates/part.no_contacts.php8
-rw-r--r--apps/contacts/templates/settings.php1
-rw-r--r--apps/files_texteditor/js/editor.js156
-rw-r--r--apps/files_versioning/ajax/gethead.php12
-rw-r--r--apps/files_versioning/ajax/sethead.php14
-rw-r--r--apps/files_versioning/appinfo/app.php20
-rw-r--r--apps/files_versioning/appinfo/info.xml13
-rw-r--r--apps/files_versioning/css/settings.css3
-rw-r--r--apps/files_versioning/js/settings.js25
-rw-r--r--apps/files_versioning/lib_granite.php12
-rw-r--r--apps/files_versioning/settings.php34
-rw-r--r--apps/files_versioning/templates/settings.php12
-rw-r--r--apps/files_versioning/versionstorage.php386
-rw-r--r--apps/files_versioning/versionwrapper.php686
-rw-r--r--apps/gallery/js/album_cover.js49
-rw-r--r--apps/gallery/templates/index.php39
-rw-r--r--apps/remoteStorage/appinfo/info.xml4
-rw-r--r--apps/remoteStorage/appinfo/webfinger.php6
-rw-r--r--apps/user_ldap/appinfo/info.xml3
-rw-r--r--apps/user_migrate/admin.php87
-rw-r--r--apps/user_migrate/ajax/export.php62
-rw-r--r--apps/user_migrate/appinfo/app.php35
-rw-r--r--apps/user_migrate/appinfo/info.xml11
-rw-r--r--apps/user_migrate/js/export.js27
-rw-r--r--apps/user_migrate/settings.php29
-rw-r--r--apps/user_migrate/templates/admin.php13
-rw-r--r--apps/user_migrate/templates/settings.php6
-rw-r--r--apps/user_openid/appinfo/info.xml3
-rw-r--r--apps/user_webfinger/.htaccess3
-rw-r--r--apps/user_webfinger/appinfo/info.xml6
-rw-r--r--apps/user_webfinger/appinfo/install.php47
-rw-r--r--apps/user_webfinger/host-meta1
-rw-r--r--apps/user_webfinger/host-meta.php16
-rw-r--r--apps/user_webfinger/webfinger.php64
56 files changed, 2256 insertions, 465 deletions
diff --git a/apps/admin_export/appinfo/info.xml b/apps/admin_export/appinfo/info.xml
deleted file mode 100644
index df8a07c2f5b..00000000000
--- a/apps/admin_export/appinfo/info.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0"?>
-<info>
- <id>admin_export</id>
- <name>Import/Export</name>
- <description>Import/Export your owncloud data</description>
- <version>0.1</version>
- <licence>AGPL</licence>
- <author>Thomas Schmidt</author>
- <require>2</require>
- <default_enable/>
-</info>
diff --git a/apps/admin_export/settings.php b/apps/admin_export/settings.php
deleted file mode 100644
index a33c872ccf4..00000000000
--- a/apps/admin_export/settings.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/**
- * ownCloud - admin export
- *
- * @author Thomas Schmidt
- * @copyright 2011 Thomas Schmidt tom@opensuse.org
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-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");
- }
-
- 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, basename($root) . "/");
- }
- }
-
- if (isset($_POST['owncloud_config'])) {
- // adding owncloud config
- // todo: add database export
- 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");
- }
-
- if (isset($_POST['user_files'])) {
- // adding user files
- $zip->addFile($root . '/data/.htaccess', basename($root) . "/data/.htaccess");
- $zip->addFile($root . '/data/index.html', basename($root) . "/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/");
- }
- }
-
- $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('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);
- }
-}
diff --git a/apps/admin_export/templates/settings.php b/apps/admin_export/templates/settings.php
deleted file mode 100644
index 47689facbbc..00000000000
--- a/apps/admin_export/templates/settings.php
+++ /dev/null
@@ -1,13 +0,0 @@
-<form id="export" action="#" method="post">
- <fieldset class="personalblock">
- <legend><strong><?php echo $l->t('Export this ownCloud instance');?></strong></legend>
- <p><?php echo $l->t('This will create a compressed file that contains the data of this owncloud instance.
- Please choose which components should be included:');?>
- </p>
- <p><input type="checkbox" id="user_files" name="user_files" value="true"><label for="user_files"><?php echo $l->t('User files');?></label><br/>
- <input type="checkbox" id="owncloud_system" name="owncloud_system" value="true"><label for="owncloud_system"><?php echo $l->t('ownCloud system files');?></label><br/>
- <input type="checkbox" id="owncloud_config" name="owncloud_config" value="true"><label for="owncloud_config"><?php echo $l->t('ownCloud configuration');?></label>
- </p>
- <input type="submit" name="admin_export" value="Export" />
- </fieldset>
-</form>
diff --git a/apps/admin_export/appinfo/app.php b/apps/admin_migrate/appinfo/app.php
index beebb4864e9..e45d3f6a529 100644
--- a/apps/admin_export/appinfo/app.php
+++ b/apps/admin_migrate/appinfo/app.php
@@ -1,10 +1,10 @@
<?php
/**
-* ownCloud - user_ldap
+* ownCloud - admin_migrate
*
-* @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
@@ -22,12 +22,12 @@
*/
-OC_APP::registerAdmin('admin_export','settings');
+OC_APP::registerAdmin('admin_migrate','settings');
// add settings page to navigation
$entry = array(
- 'id' => "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_migrate/appinfo/info.xml b/apps/admin_migrate/appinfo/info.xml
new file mode 100644
index 00000000000..67fc3f9c5a0
--- /dev/null
+++ b/apps/admin_migrate/appinfo/info.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<info>
+ <id>admin_migrate</id>
+ <name>ownCloud Instance Migration</name>
+ <description>Import/Export your owncloud instance</description>
+ <version>0.1</version>
+ <licence>AGPL</licence>
+ <author>Thomas Schmidt and Tom Needham</author>
+ <require>2</require>
+ <default_enable/>
+</info>
diff --git a/apps/admin_migrate/settings.php b/apps/admin_migrate/settings.php
new file mode 100644
index 00000000000..94bf6052a6b
--- /dev/null
+++ b/apps/admin_migrate/settings.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * ownCloud - admin_migrate
+ *
+ * @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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+OC_Util::checkAdminUser();
+OC_Util::checkAppEnabled('admin_migrate');
+
+// Export?
+if (isset($_POST['admin_export'])) {
+ // Create the export zip
+ $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));
+ header("Content-Length: " . filesize($path));
+ @ob_end_clean();
+ readfile( $path );
+ unlink( $path );
+ }
+// Import?
+} else if( isset($_POST['admin_import']) ){
+ $from = $_FILES['owncloud_import']['tmp_name'];
+
+ if( !OC_Migrate::import( $from, 'instance' ) ){
+ die('failed');
+ }
+
+} else {
+// fill template
+ $tmpl = new OC_Template('admin_migrate', 'settings');
+ return $tmpl->fetchPage();
+} \ No newline at end of file
diff --git a/apps/admin_migrate/templates/settings.php b/apps/admin_migrate/templates/settings.php
new file mode 100644
index 00000000000..91e305074e4
--- /dev/null
+++ b/apps/admin_migrate/templates/settings.php
@@ -0,0 +1,31 @@
+<form id="export" action="#" method="post">
+ <fieldset class="personalblock">
+ <legend><strong><?php echo $l->t('Export this ownCloud instance');?></strong></legend>
+ <p><?php echo $l->t('This will create a compressed file that contains the data of this owncloud instance.
+ Please choose the export type:');?>
+ </p>
+ <h3>What would you like to export?</h3>
+ <p>
+ <input type="radio" name="export_type" value="instance" /> ownCloud instance (suitable for import )<br />
+ <input type="radio" name="export_type" value="system" /> ownCloud system files<br />
+ <input type="radio" name="export_type" value="userfiles" /> Just user files<br />
+ <input type="submit" name="admin_export" value="<?php echo $l->t('Export'); ?>" />
+ </fieldset>
+</form>
+<?php
+/*
+ * EXPERIMENTAL
+?>
+<form id="import" action="#" method="post" enctype="multipart/form-data">
+ <fieldset class="personalblock">
+ <legend><strong><?php echo $l->t('Import an ownCloud instance. THIS WILL DELETE ALL CURRENT OWNCLOUD DATA');?></strong></legend>
+ <p><?php echo $l->t('All current ownCloud data will be replaced by the ownCloud instance that is uploaded.');?>
+ </p>
+ <p><input type="file" id="owncloud_import" name="owncloud_import"><label for="owncloud_import"><?php echo $l->t('ownCloud Export Zip File');?></label>
+ </p>
+ <input type="submit" name="admin_import" value="<?php echo $l->t('Import'); ?>" />
+ </fieldset>
+</form>
+<?php
+*/
+?>
diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php
index 09d7b5df525..b9c308ca053 100644
--- a/apps/bookmarks/appinfo/app.php
+++ b/apps/bookmarks/appinfo/app.php
@@ -17,4 +17,5 @@ OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'hr
OC_App::registerPersonal('bookmarks', 'settings');
OC_Util::addScript('bookmarks','bookmarksearch');
+
OC_Search::registerProvider('OC_Search_Provider_Bookmarks');
diff --git a/apps/bookmarks/appinfo/migrate.php b/apps/bookmarks/appinfo/migrate.php
new file mode 100644
index 00000000000..c1251e816ec
--- /dev/null
+++ b/apps/bookmarks/appinfo/migrate.php
@@ -0,0 +1,68 @@
+<?php
+class OC_Migration_Provider_Bookmarks extends OC_Migration_Provider{
+
+ // Create the xml for the user supplied
+ function export( ){
+ $options = array(
+ 'table'=>'bookmarks',
+ 'matchcol'=>'user_id',
+ 'matchval'=>$this->uid,
+ 'idcol'=>'id'
+ );
+ $ids = $this->content->copyRows( $options );
+
+ $options = array(
+ 'table'=>'bookmarks_tags',
+ 'matchcol'=>'bookmark_id',
+ '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 bookmarks WHERE user_id 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*bookmarks(url, title, user_id, public, added, lastmodified) VALUES (?, ?, ?, ?, ?, ?)" );
+ $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 = $this->content->prepare( "SELECT * FROM bookmarks_tags WHERE bookmark_id 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*bookmarks_tags(bookmark_id, tag) VALUES (?, ?)" );
+ $query->execute( array( $newid, $row['tag'] ) );
+ }
+ }
+ // All done!
+ break;
+ }
+
+ return true;
+ }
+
+}
+
+// Load the provider
+new OC_Migration_Provider_Bookmarks( 'bookmarks' ); \ No newline at end of file
diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php
index 839a3919981..68da54655ae 100644
--- a/apps/contacts/ajax/addcontact.php
+++ b/apps/contacts/ajax/addcontact.php
@@ -39,13 +39,14 @@ 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']);
$n = trim($_POST['n']);
-debug('N: '.$n);
-debug('FN: '.$fn);
$vcard = new OC_VObject('VCARD');
$vcard->setUID();
diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php
index 924d873652c..99d55e7927a 100644
--- a/apps/contacts/ajax/saveproperty.php
+++ b/apps/contacts/ajax/saveproperty.php
@@ -96,40 +96,40 @@ switch($element) {
//$value = getOtherValue();
}
break;
- case 'CATEGORIES':
- /* multi autocomplete triggers an save with empty value */
+ //case 'CATEGORIES':
+ /* 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/appinfo/migrate.php b/apps/contacts/appinfo/migrate.php
new file mode 100644
index 00000000000..a6c6bc20fa4
--- /dev/null
+++ b/apps/contacts/appinfo/migrate.php
@@ -0,0 +1,68 @@
+<?php
+class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
+
+ // Create the xml for the user supplied
+ function export( ){
+ $options = array(
+ 'table'=>'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/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css
index 76b5972ba3c..2d207943841 100644
--- a/apps/contacts/css/contacts.css
+++ b/apps/contacts/css/contacts.css
@@ -13,22 +13,26 @@
#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;}
-#card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ }
+#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; }
+#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"]: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; }
+#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; 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; }
+#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; }
+.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 +47,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; 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 +76,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 f57511e1000..4edba0c5489 100644
--- a/apps/contacts/img/person_large.png
+++ b/apps/contacts/img/person_large.png
Binary files differ
diff --git a/apps/contacts/index.php b/apps/contacts/index.php
index 04f6c65a145..776c57ca605 100644
--- a/apps/contacts/index.php
+++ b/apps/contacts/index.php
@@ -34,7 +34,26 @@ 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) {
+ $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 +80,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);
diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js
index d314878cc0a..6f34a42a73f 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');
@@ -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);
@@ -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'
});
@@ -175,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;
@@ -200,6 +192,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:{
@@ -259,16 +254,28 @@ Contacts={
});
}
},
- 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){
//});
},
- import:function(){
+ doImport: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);
+ 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'){
@@ -291,7 +298,15 @@ Contacts={
if(!added) {
$('#leftcontent ul').append(item);
}
-
+ if(isnew) {
+ Contacts.UI.Card.addProperty('EMAIL');
+ Contacts.UI.Card.addProperty('TEL');
+ Contacts.UI.Card.addProperty('NICKNAME');
+ Contacts.UI.Card.addProperty('ORG');
+ Contacts.UI.Card.addProperty('CATEGORIES');
+ $('#fn').focus();
+ $('#fn').select();
+ }
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
@@ -308,7 +323,7 @@ Contacts={
}
});
},
- 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) {
@@ -356,7 +371,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 +383,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 +390,7 @@ Contacts={
} else {
$('#note').data('checksum', '');
$('#note').find('textarea').val('');
- $('#note').hide();
+ //$('#note').hide();
}
},
loadSingleProperties:function() {
@@ -521,17 +535,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 +642,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 +662,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 +697,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 +733,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 +906,7 @@ Contacts={
if(isnew) {
container.remove();
}
- Contacts.UI.showHideContactInfo();
+ //Contacts.UI.showHideContactInfo();
}
},
close : function(event, ui) {
@@ -900,11 +915,95 @@ Contacts={
if(isnew) {
container.remove();
}
- Contacts.UI.showHideContactInfo();
- }/*,
+ //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,
+ lang: lang,
+ 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().trim().length == 0) {
+ $('#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",*/
+ lang: lang,
+ 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);
@@ -973,7 +1072,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 +1085,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);
@@ -1165,7 +1264,7 @@ Contacts={
});
}
},
- import:function(){
+ doImport:function(){
Contacts.UI.notImplemented();
},
submit:function(button, bookid){
@@ -1198,9 +1297,7 @@ Contacts={
}
},
Contacts:{
- /**
- * Reload the contacts list.
- */
+ // Reload the contacts list.
update:function(){
console.log('Contacts.update, start');
$.getJSON('ajax/contacts.php',{},function(jsondata){
@@ -1215,9 +1312,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')) {
@@ -1237,9 +1332,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;
@@ -1272,7 +1364,7 @@ $(document).ready(function(){
});
$('#contacts_deletecard').live('click',function(){
- Contacts.UI.Card.delete();
+ Contacts.UI.Card.doDelete();
});
$('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
@@ -1430,7 +1522,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 6da4ecb5147..f46d7c14c18 100644
--- a/apps/contacts/js/jquery.combobox.js
+++ b/apps/contacts/js/jquery.combobox.js
@@ -72,17 +72,10 @@
.appendTo( ul );
};
- this.button = $( "<button type='button'>&nbsp;</button>" )
+ /*this.button = $( "<button type='button'>&nbsp;</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 7607de3f918..e1c5d63dc5f 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 = $( "<button type='button'>&nbsp;</button>" )
+ /*this.button = $( "<button type='button'>&nbsp;</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/lib/addressbook.php b/apps/contacts/lib/addressbook.php
index 052c19e55fe..9061fa19140 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');
diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php
index af159ce9c60..d68dd68f605 100644
--- a/apps/contacts/templates/index.php
+++ b/apps/contacts/templates/index.php
@@ -1,6 +1,7 @@
<script type='text/javascript'>
var totalurl = '<?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/addressbooks';
var categories = <?php sort($_['categories']); echo json_encode($_['categories']); ?>;
+ var lang = '<?php echo OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang', 'en'); ?>';
</script>
<div id="controls">
<form>
diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php
index d243c2b5e14..ff1f081c4d4 100644
--- a/apps/contacts/templates/part.contact.php
+++ b/apps/contacts/templates/part.contact.php
@@ -17,16 +17,16 @@ $id = isset($_['id']) ? $_['id'] : '';
<li><a data-type="CATEGORIES"><?php echo $l->t('Categories'); ?></a></li>
</ul>
</div>
- <img onclick="Contacts.UI.Card.export();" class="svg action" id="contacts_downloadcard" src="<?php echo image_path('', 'actions/download.svg'); ?>" title="<?php echo $l->t('Download contact');?>" />
+ <img onclick="Contacts.UI.Card.doExport();" class="svg action" id="contacts_downloadcard" src="<?php echo image_path('', 'actions/download.svg'); ?>" title="<?php echo $l->t('Download contact');?>" />
<img class="svg action" id="contacts_deletecard" src="<?php echo image_path('', 'actions/delete.svg'); ?>" title="<?php echo $l->t('Delete contact');?>" />
</div>
- <div class="contactsection">
+ <div id="contact_photo" class="contactsection">
- <form style="display:none;" id="file_upload_form" action="ajax/uploadphoto.php" method="post" enctype="multipart/form-data" target="file_upload_target" class="propertycontainer" data-element="PHOTO">
- <fieldset id="photo" class="formfloat">
+ <form class="float" id="file_upload_form" action="ajax/uploadphoto.php" method="post" enctype="multipart/form-data" target="file_upload_target" class="propertycontainer" data-element="PHOTO">
+ <fieldset id="photo">
<a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a>
- <div id="contacts_details_photo_wrapper" title="<?php echo $l->t('Click or drop to upload picture'); ?> (max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
+ <div class="tip" id="contacts_details_photo_wrapper" title="<?php echo $l->t('Click or drop to upload picture'); ?> (max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
<!-- img style="padding: 1em;" id="contacts_details_photo" alt="Profile picture" src="photo.php?id=<?php echo $_['id']; ?>" / -->
<progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress>
</div>
@@ -37,58 +37,55 @@ $id = isset($_['id']) ? $_['id'] : '';
<iframe name="file_upload_target" id='file_upload_target' src=""></iframe>
</fieldset>
</form>
- <form id="contact_identity" method="post" <?php echo ($_['id']==''||!isset($_['id'])?'style="display:none;"':''); ?>>
+ </div> <!-- contact_photo -->
+
+ <div id="contact_identity" class="contactsection">
+ <form method="post">
<input type="hidden" name="id" value="<?php echo $_['id'] ?>">
- <fieldset class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></fieldset>
- <fieldset id="ident" class="formfloat">
+ <fieldset id="ident" class="contactpart">
<!-- legend>Name</legend -->
- <dl class="form">
- <!-- dt><label for="n"><?php echo $l->t('Name'); ?></label></dt>
- <dd style="padding-top: 0.8em;vertical-align: text-bottom;"><span id="n" type="text"></span></dd -->
- <dt><label for="fn"><?php echo $l->t('Display name'); ?></label></dt>
- <dd class="propertycontainer" data-element="FN">
- <select id="fn_select" title="<?php echo $l->t('Format custom, Short name, Full name, Reverse or Reverse with comma'); ?>" style="width:16em;">
- </select><a id="edit_name" class="action edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
- </dd>
+ <span class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></span>
+ <span id="name" class="propertycontainer" data-element="FN">
+ <select class="float" id="fn_select" title="<?php echo $l->t('Format custom, Short name, Full name, Reverse or Reverse with comma'); ?>" style="width:16em;">
+ </select><a id="edit_name" class="action edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
+ </span>
+ <dl id="identityprops" class="form">
<dt style="display:none;" id="org_label" data-element="ORG"><label for="org"><?php echo $l->t('Organization'); ?></label></dt>
- <dd style="display:none;" class="propertycontainer" id="org_value" data-element="ORG"><input id="org" required="required" name="value[ORG]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Organization'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
+ <dd style="display:none;" class="propertycontainer" id="org_value" data-element="ORG"><input id="org" required="required" name="value[ORG]" type="text" class="contacts_property big" name="value" value="" placeholder="<?php echo $l->t('Organization'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
<dt style="display:none;" id="nickname_label" data-element="NICKNAME"><label for="nickname"><?php echo $l->t('Nickname'); ?></label></dt>
- <dd style="display:none;" class="propertycontainer" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" name="value[NICKNAME]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
+ <dd style="display:none;" class="propertycontainer" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" name="value[NICKNAME]" type="text" class="contacts_property big" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
<dt style="display:none;" id="bday_label" data-element="BDAY"><label for="bday"><?php echo $l->t('Birthday'); ?></label></dt>
- <dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday" required="required" name="value" type="text" class="contacts_property" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
- <dt style="display:none;" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Categories'); ?></label></dt>
- <dd style="display:none;" class="propertycontainer" id="categories_value" data-element="CATEGORIES"><input id="categories" required="required" name="value[CATEGORIES]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Categories'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a><a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a></dd>
+ <dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday" required="required" name="value" type="text" class="contacts_property big" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
+ <dt style="display:none;" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Groups'); ?></label></dt>
+ <dd style="display:none;" class="propertycontainer" id="categories_value" data-element="CATEGORIES"><input id="categories" required="required" name="value[CATEGORIES]" type="text" class="contacts_property bold" name="value" value="" placeholder="
+<?php echo $l->t('Separate groups with commas'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a><a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a></dd>
</dl>
</fieldset>
- <fieldset id="note" class="formfloat propertycontainer contactpart" style="display:none;" data-element="NOTE">
- <legend><?php echo $l->t('Note'); ?><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></legend>
- <textarea class="contacts_property note" name="value" cols="60" rows="10"></textarea>
- </fieldset>
</form>
- </div>
+ </div> <!-- contact_identity -->
<!-- div class="delimiter"></div -->
- <form id="contact_communication" method="post" style="display: none;">
- <div class="contactsection">
+ <div id="contact_communication" class="contactsection">
+ <form method="post">
<!-- email addresses -->
- <div id="emails" style="display:none;">
+ <div id="emails">
<fieldset class="contactpart">
- <legend><?php echo $l->t('Email'); ?></legend>
+ <!-- legend><?php echo $l->t('Email'); ?></legend -->
<ul id="emaillist" class="propertylist">
<li class="template" style="white-space: nowrap; display: none;" data-element="EMAIL">
- <input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
+ <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
<input type="email" required="required" class="nonempty contacts_property" style="width:15em;" name="value" value="" x-moz-errormessage="<?php echo $l->t('Please specify a valid email address.'); ?>" placeholder="<?php echo $l->t('Enter email address'); ?>" /><span class="listactions"><a onclick="Contacts.UI.mailTo(this)" class="action mail" title="<?php echo $l->t('Mail to address'); ?>"></a>
<a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li>
</ul><!-- a id="add_email" class="add" title="<?php echo $l->t('Add email address'); ?>"></a -->
</div> <!-- email addresses-->
<!-- Phone numbers -->
- <div id="phones" style="display:none;">
+ <div id="phones">
<fieldset class="contactpart">
- <legend><?php echo $l->t('Phone'); ?></legend>
+ <!-- legend><?php echo $l->t('Phone'); ?></legend -->
<ul id="phonelist" class="propertylist">
<li class="template" style="white-space: nowrap; display: none;" data-element="TEL">
- <input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
+ <input type="checkbox" class="contacts_property tip" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
<input type="text" required="required" class="nonempty contacts_property" style="width:10em; border: 0px;" name="value" value="" placeholder="<?php echo $l->t('Enter phone number'); ?>" />
<select multiple="multiple" name="parameters[TYPE][]">
<?php echo html_select_options($_['phone_types'], array()) ?>
@@ -100,7 +97,7 @@ $id = isset($_['id']) ? $_['id'] : '';
<!-- Addresses -->
<div id="addresses" style="display:none;">
<fieldset class="contactpart">
- <legend><?php echo $l->t('Address'); ?></legend>
+ <!-- legend><?php echo $l->t('Address'); ?></legend -->
<div id="addressdisplay">
<dl class="addresscard template" style="display: none;" data-element="ADR"><dt>
<input class="adr contacts_property" name="value" type="hidden" value="" />
@@ -109,13 +106,18 @@ $id = isset($_['id']) ? $_['id'] : '';
</dt><dd><ul class="addresslist"></ul></dd></dl>
</fieldset>
- </div>
+ </div> <!-- addressdisplay -->
</div> <!-- Addresses -->
- </div>
- <!-- a id="add_address" onclick="Contacts.UI.Card.editAddress('new', true)" class="add" title="<?php echo $l->t('Add address'); ?>"></a -->
- </div>
</form>
-</div>
+ </div> <!-- contact_communication -->
+ <div id="contact_note" class="contactsection">
+ <form class="float" method="post">
+ <fieldset id="note" class="formfloat propertycontainer contactpart" data-element="NOTE">
+ <textarea class="contacts_property note" name="value" cols="40" rows="10" required="required" placeholder="<?php echo $l->t('Add notes here.'); ?>"></textarea>
+ </fieldset>
+ </form>
+ </div> <!-- contact_note -->
+</div> <!-- card -->
<div id="edit_photo_dialog" title="Edit photo">
<div id="edit_photo_dialog_img"></div>
</div>
@@ -128,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 0ecdc4e1915..507a3acaa0c 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
<label class="label" for="adr_pobox"><?php echo $l->t('PO Box'); ?></label>
</dt>
<dd>
- <input type="text" id="adr_pobox" name="value[ADR][0]" value="<?php echo isset($adr['value'][0])?$adr['value'][0]:''; ?>">
+ <input type="text" id="adr_pobox" name="value[ADR][0]" placeholder="<?php echo $l->t('PO Box'); ?>" value="<?php echo isset($adr['value'][0])?$adr['value'][0]:''; ?>">
</dd>
<dd>
<dt>
<label class="label" for="adr_extended"><?php echo $l->t('Extended'); ?></label>
</dt>
<dd>
- <input type="text" id="adr_extended" name="value[ADR][1]" value="<?php echo isset($adr['value'][1])?$adr['value'][1]:''; ?>">
+ <input type="text" id="adr_extended" name="value[ADR][1]" placeholder="<?php echo $l->t('Extended'); ?>" value="<?php echo isset($adr['value'][1])?$adr['value'][1]:''; ?>">
</dd>
<dt>
<label class="label" for="adr_street"><?php echo $l->t('Street'); ?></label>
</dt>
<dd>
- <input type="text" id="adr_street" name="value[ADR][2]" value="<?php echo isset($adr['value'][2])?$adr['value'][2]:''; ?>">
+ <input type="text" id="adr_street" name="value[ADR][2]" placeholder="<?php echo $l->t('Street'); ?>" value="<?php echo isset($adr['value'][2])?$adr['value'][2]:''; ?>">
</dd>
<dt>
<label class="label" for="adr_city"><?php echo $l->t('City'); ?></label>
</dt>
<dd>
- <input type="text" id="adr_city" name="value[ADR][3]" value="<?php echo isset($adr['value'][3])?$adr['value'][3]:''; ?>">
+ <input type="text" id="adr_city" name="value[ADR][3]" placeholder="<?php echo $l->t('City'); ?>" value="<?php echo isset($adr['value'][3])?$adr['value'][3]:''; ?>">
</dd>
<dt>
<label class="label" for="adr_region"><?php echo $l->t('Region'); ?></label>
</dt>
<dd>
- <input type="text" id="adr_region" name="value[ADR][4]" value="<?php echo isset($adr['value'][4])?$adr['value'][4]:''; ?>">
+ <input type="text" id="adr_region" name="value[ADR][4]" placeholder="<?php echo $l->t('Region'); ?>" value="<?php echo isset($adr['value'][4])?$adr['value'][4]:''; ?>">
</dd>
<dt>
<label class="label" for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label>
</dt>
<dd>
- <input type="text" id="adr_zipcode" name="value[ADR][5]" value="<?php echo isset($adr['value'][5])?$adr['value'][5]:''; ?>">
+ <input type="text" id="adr_zipcode" name="value[ADR][5]" placeholder="<?php echo $l->t('Zipcode'); ?>" value="<?php echo isset($adr['value'][5])?$adr['value'][5]:''; ?>">
</dd>
<dt>
<label class="label" for="adr_country"><?php echo $l->t('Country'); ?></label>
</dt>
<dd>
- <input type="text" id="adr_country" name="value[ADR][6]" value="<?php echo isset($adr['value'][6])?$adr['value'][6]:''; ?>">
+ <input type="text" id="adr_country" name="value[ADR][6]" placeholder="<?php echo $l->t('Country'); ?>" value="<?php echo isset($adr['value'][6])?$adr['value'][6]:''; ?>">
</dd>
</dl>
</fieldset>
diff --git a/apps/contacts/templates/part.no_contacts.php b/apps/contacts/templates/part.no_contacts.php
new file mode 100644
index 00000000000..7024a142aec
--- /dev/null
+++ b/apps/contacts/templates/part.no_contacts.php
@@ -0,0 +1,8 @@
+<div id="firstrun">
+ <?php echo $l->t('You have no contacts in your list.') ?>
+ <div id="selections">
+ <input type="button" value="<?php echo $l->t('Import contacts') ?>" onclick="Contacts.UI.Addressbooks.doImport()" />
+ <input type="button" value="<?php echo $l->t('Add contact') ?>" onclick="Contacts.UI.Card.editNew()" />
+ <input type="button" value="<?php echo $l->t('Edit addressbooks') ?>" onclick="Contacts.UI.Addressbooks.overview()" />
+ </div>
+</div>
diff --git a/apps/contacts/templates/settings.php b/apps/contacts/templates/settings.php
index f56de0ec8b0..5627a15c50a 100644
--- a/apps/contacts/templates/settings.php
+++ b/apps/contacts/templates/settings.php
@@ -8,5 +8,6 @@
<dt><?php echo $l->t('iOS/OS X'); ?></dt>
<dd><code><?php echo OC_Helper::linkToAbsolute('contacts', 'carddav.php'); ?>/principals/<?php echo OC_User::getUser(); ?></code>/</dd>
</dl>
+ Powered by <a href="http://geonames.org/" target="_blank">geonames.org webservice</a>
</fieldset>
</form>
diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js
index 02d39b98430..bc8a20408c2 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";
@@ -141,32 +143,40 @@ 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('<p id="save_result" style="float: left">Failed to save file</p>');
- $("#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('<p id="save_result" style="float: left">Failed to save file</p>');
+ $("#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');
+ }
}
+ giveEditorFocus();
};
// Gives the editor focus
@@ -176,6 +186,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('<div id="editor"></div>');
@@ -192,10 +204,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,10 +220,17 @@ 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.
- alert(result.data.message);
+ OC.dialogs.alert(result.data.message, t('files_texteditor','An error occurred!'));
}
// End success
}
@@ -222,37 +242,54 @@ function showFileEditor(dir,filename){
// 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 = '<div id="editor"></div>';
- $('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
@@ -285,6 +322,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();
+ });
});
diff --git a/apps/files_versioning/ajax/gethead.php b/apps/files_versioning/ajax/gethead.php
new file mode 100644
index 00000000000..cc93b7a1d17
--- /dev/null
+++ b/apps/files_versioning/ajax/gethead.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ */
+require_once('../../../lib/base.php');
+
+OC_JSON::checkLoggedIn();
+// Fetch current commit (or HEAD if not yet set)
+$head = OC_Preferences::getValue(OC_User::getUser(), 'files_versioning', 'head', 'HEAD');
+OC_JSON::encodedPrint(array("head" => $head));
diff --git a/apps/files_versioning/ajax/sethead.php b/apps/files_versioning/ajax/sethead.php
new file mode 100644
index 00000000000..d1b2df9b00f
--- /dev/null
+++ b/apps/files_versioning/ajax/sethead.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ */
+require_once('../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+if(isset($_POST["file_versioning_head"])){
+ OC_Preferences::setValue(OC_User::getUser(), 'files_versioning', 'head', $_POST["file_versioning_head"]);
+ OC_JSON::success();
+}else{
+ OC_JSON::error();
+}
diff --git a/apps/files_versioning/appinfo/app.php b/apps/files_versioning/appinfo/app.php
new file mode 100644
index 00000000000..24a8701dbb0
--- /dev/null
+++ b/apps/files_versioning/appinfo/app.php
@@ -0,0 +1,20 @@
+<?php
+
+// Include required files
+require_once('apps/files_versioning/versionstorage.php');
+require_once('apps/files_versioning/versionwrapper.php');
+// Register streamwrapper for versioned:// paths
+stream_wrapper_register('versioned', 'OC_VersionStreamWrapper');
+
+// Add an entry in the app list for versioning and backup
+OC_App::register( array(
+ 'order' => 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 00000000000..4c67894f9f9
--- /dev/null
+++ b/apps/files_versioning/appinfo/info.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<info>
+ <id>files_versioning</id>
+ <name>Versioning and Backup</name>
+ <version>1.0.0</version>
+ <licence>GPLv2</licence>
+ <author>Craig Roberts</author>
+ <require>3</require>
+ <description>Versions files using Git repositories, providing a simple backup facility. Currently in *beta* and explicitly without warranty of any kind.</description>
+ <types>
+ <filesystem/>
+ </types>
+</info>
diff --git a/apps/files_versioning/css/settings.css b/apps/files_versioning/css/settings.css
new file mode 100644
index 00000000000..afe2cd5508f
--- /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 00000000000..8dd13bac033
--- /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 00000000000..571e5cea637
--- /dev/null
+++ b/apps/files_versioning/lib_granite.php
@@ -0,0 +1,12 @@
+<?php
+
+require_once('granite/git/blob.php');
+require_once('granite/git/commit.php');
+require_once('granite/git/repository.php');
+require_once('granite/git/tag.php');
+require_once('granite/git/tree.php');
+require_once('granite/git/tree/node.php');
+require_once('granite/git/object/index.php');
+require_once('granite/git/object/raw.php');
+require_once('granite/git/object/loose.php');
+require_once('granite/git/object/packed.php');
diff --git a/apps/files_versioning/settings.php b/apps/files_versioning/settings.php
new file mode 100644
index 00000000000..94af587a215
--- /dev/null
+++ b/apps/files_versioning/settings.php
@@ -0,0 +1,34 @@
+<?php
+
+// Get the full path to the repository folder (FIXME: hard-coded to 'Backup')
+$path = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data')
+ . DIRECTORY_SEPARATOR
+ . OC_User::getUser()
+ . DIRECTORY_SEPARATOR
+ . 'files'
+ . DIRECTORY_SEPARATOR
+ . 'Backup'
+ . DIRECTORY_SEPARATOR
+ . '.git'
+ . DIRECTORY_SEPARATOR;
+
+$repository = new Granite\Git\Repository($path);
+
+$commits = array();
+// Fetch most recent 50 commits (FIXME - haven't tested this much)
+$commit = $repository->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 00000000000..17f4cc7f77f
--- /dev/null
+++ b/apps/files_versioning/templates/settings.php
@@ -0,0 +1,12 @@
+<fieldset id="status_list" class="personalblock">
+ <strong>Versioning and Backup</strong><br>
+ <p><em>Please note: Backing up large files (around 16MB+) will cause your backup history to grow very large, very quickly.</em></p>
+ <label class="bold">Backup Folder</label>
+ <select name="file_versioning_head" id="file_versioning_head">
+ <?php
+ foreach ($_['commits'] as $commit):
+ echo '<option value="' . $commit->sha() . '">' . $commit->message() . '</option>';
+ endforeach;
+ ?>
+ </select>
+</fieldset>
diff --git a/apps/files_versioning/versionstorage.php b/apps/files_versioning/versionstorage.php
new file mode 100644
index 00000000000..d083e623df9
--- /dev/null
+++ b/apps/files_versioning/versionstorage.php
@@ -0,0 +1,386 @@
+<?php
+/**
+ * ownCloud file storage implementation for Git repositories
+ * @author Craig Roberts
+ * @copyright 2012 Craig Roberts craig0990@googlemail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// 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 00000000000..b83a4fd3b22
--- /dev/null
+++ b/apps/files_versioning/versionwrapper.php
@@ -0,0 +1,686 @@
+<?php
+
+final class OC_VersionStreamWrapper {
+
+ /**
+ * Determines whether or not to log debug messages with `OC_Log::write()`
+ */
+ private $debug = true;
+
+ /**
+ * The name of the ".empty" files created in new directories
+ */
+ const EMPTYFILE = '.empty';
+
+ /**
+ * Stores the current position for `readdir()` etc. calls
+ */
+ private $dir_position = 0;
+
+ /**
+ * Stores the current position for `fread()`, `fseek()` etc. calls
+ */
+ private $file_position = 0;
+
+ /**
+ * Stores the current directory tree for `readdir()` etc. directory traversal
+ */
+ private $tree;
+
+ /**
+ * Stores the current file for `fread()`, `fseek()`, etc. calls
+ */
+ private $blob;
+
+ /**
+ * Stores the current commit for `fstat()`, `stat()`, etc. calls
+ */
+ private $commit;
+
+ /**
+ * Stores the current path for `fwrite()`, `file_put_contents()` etc. calls
+ */
+ private $path;
+
+ /**
+ * Close directory handle
+ */
+ public function dir_closedir() {
+ unset($this->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;
+ }
+
+}
diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js
index 061bbcd0b47..d44e7f83d1f 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;
@@ -112,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 c6373d3b0a2..9bec5db1b91 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');
?>
-
+<script type="text/javascript">var gallery_scanning_root='<? echo OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); ?>'; var gallery_default_order = '<? echo OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC'); ?>';</script>
<div id="controls">
<div id="scan">
<div id="scanprogressbar"></div>
@@ -29,40 +29,3 @@ $l = new OC_L10N('gallery');
</div>
<div id="gallery_list">
</div>
-
-<div id="dialog-confirm" title="<?php echo $l->t('Remove confirmation');?>" style="display: none">
- <p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><?php echo $l->t('Do you want to remove album');?> <span id="albumName"></span>?</p>
-</div>
-
-<div id="dialog-form" title="<?php echo $l->t('Change album name');?>" style="display:none">
- <form>
- <fieldset>
- <label for="name"><?php echo $l->t('New album name');?></label>
- <input type="text" name="name" id="name" class="text ui-widget-content ui-corner-all" />
- </fieldset>
- </form>
-</div>
-
-<div id="g-dialog-settings" title="<?php echo $l->t('Settings');?>" style="display:none">
- <form>
- <fieldset><?php $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC');?>
- <label for="name"><?php echo $l->t('Scanning root');?></label>
- <input type="text" name="g-scanning-root" id="g-scanning-root" class="text ui-widget-content ui-corner-all" value="<?php echo $root;?>" /><br/>
-
- <label for="sort"><?php echo $l->t('Default sorting'); ?></label>
- <select id="g-display-order">
- <option value="ASC"<?php echo $order=='ASC'?'selected':'';?>><?php echo $l->t('Ascending'); ?></option>
- <option value="DESC"<?php echo $order=='DESC'?'selected':'';?>><?php echo $l->t('Descending'); ?></option>
- </select><br/>
-<!--
- <label for="sort"><?php echo $l->t('Thumbnails size'); ?></label>
- <select>
- <option value="100">100px</option>
- <option value="150">150px</option>
- <option value="200">200px</option>
- </select>
- -->
- </fieldset>
- </form>
-</div>
-
diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml
index 0936bf9bd0f..1f9618a3334 100644
--- a/apps/remoteStorage/appinfo/info.xml
+++ b/apps/remoteStorage/appinfo/info.xml
@@ -2,8 +2,8 @@
<info>
<id>remoteStorage</id>
<name>remoteStorage compatibility</name>
- <description>Enables you to use ownCloud as their remote storage for unhosted applications. This app requires the Webfinger app to be enabled as well. More info on <a href="http://unhosted.org">the website of the unhosted movement</a>.</description>
- <version>0.5</version>
+ <description>Enables you to use ownCloud as their remote storage for unhosted applications. This app requires the Webfinger app to be installed and enabled correctly. More info on <a href="http://unhosted.org">the website of the unhosted movement</a>.</description>
+ <version>0.6</version>
<licence>AGPL or MIT</licence>
<author>Michiel de Jong</author>
<require>2</require>
diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php
new file mode 100644
index 00000000000..7c0ab846057
--- /dev/null
+++ b/apps/remoteStorage/appinfo/webfinger.php
@@ -0,0 +1,6 @@
+ <Link
+ rel="remoteStorage"
+ template="<?php echo WF_BASEURL; ?>/apps/remoteStorage/WebDAV.php/<?php echo WF_USER; ?>/remoteStorage/{category}/"
+ api="WebDAV"
+ auth="<?php echo WF_BASEURL; ?>/apps/remoteStorage/auth.php/<?php echo WF_USER; ?>">
+ </Link>
diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml
index 9a6ee1436fc..99830dd1ffd 100644
--- a/apps/user_ldap/appinfo/info.xml
+++ b/apps/user_ldap/appinfo/info.xml
@@ -7,4 +7,7 @@
<licence>AGPL</licence>
<author>Dominik Schmidt</author>
<require>2</require>
+ <types>
+ <authentication/>
+ </types>
</info>
diff --git a/apps/user_migrate/admin.php b/apps/user_migrate/admin.php
new file mode 100644
index 00000000000..0160753af22
--- /dev/null
+++ b/apps/user_migrate/admin.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * ownCloud - user_migrate
+ *
+ * @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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+OC_Util::checkAdminUser();
+OC_Util::checkAppEnabled('user_migrate');
+
+// Import?
+if (isset($_POST['user_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 ) ){
+ $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 );
+ $tmpl = new OC_Template('user_migrate', 'admin');
+ $tmpl->assign('error',$error);
+ return $tmpl->fetchPage();
+ }
+ $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( $response->data as $app => $status ){
+ if( $status != 'true' ){
+ // It failed for some reason
+ if( $status == 'notsupported' ){
+ $notsupported[] = $app;
+ } else if( !$status ){
+ $failed[] = $app;
+ }
+ }
+ }
+ // Any problems?
+ if( isset( $notsupported ) || isset( $failed ) ){
+ if( count( $failed ) > 0 ){
+ $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 ){
+ $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!
+ $tmpl = new OC_Template('user_migrate', 'admin');
+ return $tmpl->fetchPage();
+ }
+ }
+
+} 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/ajax/export.php b/apps/user_migrate/ajax/export.php
new file mode 100644
index 00000000000..86745d6b162
--- /dev/null
+++ b/apps/user_migrate/ajax/export.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * ownCloud - user_migrate
+ *
+ * @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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+// Init owncloud
+require_once('../../../lib/base.php');
+
+// Check if we are a user
+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();
+ 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();
+} else if( $_GET['operation']=='download' ){
+ // Download the export
+ $path = isset( $_SESSION['ocuserexportpath'] ) ? $_SESSION['ocuserexportpath'] : false;
+ if( !$path ){
+ OC_JSON::error();
+ }
+ 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
new file mode 100644
index 00000000000..a59b6dd705c
--- /dev/null
+++ b/apps/user_migrate/appinfo/app.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+* ownCloud - user_migrate
+*
+* @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
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+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(
+ '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/appinfo/info.xml b/apps/user_migrate/appinfo/info.xml
new file mode 100644
index 00000000000..6abcb4af92c
--- /dev/null
+++ b/apps/user_migrate/appinfo/info.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<info>
+ <id>user_migrate</id>
+ <name>User Account Migration</name>
+ <description>Migrate your user accounts</description>
+ <version>0.1</version>
+ <licence>AGPL</licence>
+ <author>Tom Needham</author>
+ <require>2</require>
+ <default_enable/>
+</info>
diff --git a/apps/user_migrate/js/export.js b/apps/user_migrate/js/export.js
new file mode 100644
index 00000000000..2d660b2de6b
--- /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')+'<img style="display: none;" class="loading" src="'+OC.filePath('core','img','loading.gif')+'" />');
+ });
+ }
+ }
+ // End ajax
+ );
+ });
+}); \ No newline at end of file
diff --git a/apps/user_migrate/settings.php b/apps/user_migrate/settings.php
new file mode 100644
index 00000000000..62f347b3557
--- /dev/null
+++ b/apps/user_migrate/settings.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * ownCloud - user_migrate
+ *
+ * @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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+OC_Util::checkAppEnabled('user_migrate');
+
+// 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/admin.php b/apps/user_migrate/templates/admin.php
new file mode 100644
index 00000000000..b01e5c7579a
--- /dev/null
+++ b/apps/user_migrate/templates/admin.php
@@ -0,0 +1,13 @@
+<form id="import" action="#" method="post" enctype="multipart/form-data">
+ <fieldset class="personalblock">
+ <?php if(isset($_['error'])){ ?>
+ <h3><?php echo $_['error']['error']; ?></h3>
+ <p><?php echo $_['error']['hint']; ?></p>
+ <?php } ?>
+ <legend><strong><?php echo $l->t('Import user account');?></strong></legend>
+ </p>
+ <p><input type="file" id="owncloud_import" name="owncloud_import"><label for="owncloud_import"><?php echo $l->t('ownCloud User Zip');?></label>
+ </p>
+ <input type="submit" name="user_import" value="<?php echo $l->t('Import'); ?>" />
+ </fieldset>
+</form>
diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php
new file mode 100644
index 00000000000..5f4857de5fa
--- /dev/null
+++ b/apps/user_migrate/templates/settings.php
@@ -0,0 +1,6 @@
+<fieldset class="personalblock">
+ <legend><strong><?php echo $l->t('Export your user account');?></strong></legend>
+ <p><?php echo $l->t('This will create a compressed file that contains your ownCloud account.');?>
+ </p>
+ <button id="exportbtn">Export<img style="display: none;" class="loading" src="<?php echo OC_Helper::linkTo('core', 'img/loading.gif'); ?>" /></button>
+</fieldset>
diff --git a/apps/user_openid/appinfo/info.xml b/apps/user_openid/appinfo/info.xml
index 37be15abfda..721db1877e3 100644
--- a/apps/user_openid/appinfo/info.xml
+++ b/apps/user_openid/appinfo/info.xml
@@ -7,4 +7,7 @@
<licence>AGPL</licence>
<author>Robin Appelman</author>
<require>2</require>
+ <types>
+ <authentication/>
+ </types>
</info>
diff --git a/apps/user_webfinger/.htaccess b/apps/user_webfinger/.htaccess
deleted file mode 100644
index 4d4d2e9c58f..00000000000
--- 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 55cf2cf2201..d47fb723a3a 100644
--- a/apps/user_webfinger/appinfo/info.xml
+++ b/apps/user_webfinger/appinfo/info.xml
@@ -2,9 +2,9 @@
<info>
<id>user_webfinger</id>
<name>Webfinger</name>
- <description>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.</description>
- <version>0.2</version>
+ <description>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.</description>
+ <version>0.3</version>
<licence>AGPL or MIT</licence>
- <author>Michiel de Jong</author>
+ <author>Michiel de Jong, Florian Hülsmann</author>
<require>2</require>
</info>
diff --git a/apps/user_webfinger/appinfo/install.php b/apps/user_webfinger/appinfo/install.php
index f570a3a249b..c8d9a427425 100644
--- a/apps/user_webfinger/appinfo/install.php
+++ b/apps/user_webfinger/appinfo/install.php
@@ -1,6 +1,51 @@
<?php
+$hostMetaHeader = array(
+ 'Access-Control-Allow-Origin' => '*',
+ 'Content-Type' => 'application/xml+xrd'
+);
$appInfoDir = __DIR__;
$thisAppDir = dirname($appInfoDir);
$appsDir = dirname($thisAppDir);
$ownCloudDir = dirname($appsDir);
-@symlink($thisAppDir, $ownCloudDir.'/.well-known');
+$docRoot = $_SERVER['DOCUMENT_ROOT'];
+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';
+$hostMetaDir = $docRoot . '/.well-known';
+$hostMetaContents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<XRD xmlns=\"http://docs.oasis-open.org/ns/xri/xrd-1.0\" xmlns:hm=\"http://host-meta.net/xrd/1.0\">
+ <hm:Host xmlns=\"http://host-meta.net/xrd/1.0\">" . $serverName . "</hm:Host>
+ <Link rel=\"lrdd\" template=\"" . $lrddTmpl . "\">
+ <Title>Resource Descriptor</Title>
+ </Link>
+</XRD>";
+@mkdir($hostMetaDir);
+$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);
+
+// write custom headers into .htaccess:
+$htaccess = fopen($hostMetaDir . '/.htaccess', 'w');
+//TODO: check compatibility!
+fwrite($htaccess, "<filesMatch \"^host-meta$\">
+<ifModule mod_headers.c>\n");
+foreach($hostMetaHeader as $header => $value) {
+ fwrite($htaccess, "Header set " . $header . " \"" . $value . "\"\n");
+}
+fwrite($htaccess, "</ifModule>\n</filesMatch>");
+fclose($htaccess);
diff --git a/apps/user_webfinger/host-meta b/apps/user_webfinger/host-meta
deleted file mode 100644
index dfaf3636145..00000000000
--- 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 ac577cf9a0c..00000000000
--- a/apps/user_webfinger/host-meta.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-if($_SERVER['SCRIPT_NAME'] == '/.well-known/host-meta.php') {
- header("Access-Control-Allow-Origin: *");
-} else {
- header('Please-first: activate');
-}
-header("Content-Type: application/xrd+xml");
-echo "<";
-?>
-?xml version="1.0" encoding="UTF-8"?>
-<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:hm="http://host-meta.net/xrd/1.0">
- <hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME'] ?></hm:Host>
- <Link rel="lrdd" template="http<?php echo (isset($_SERVER['HTTPS'])?'s':''); ?>://<?php echo $_SERVER['SERVER_NAME'] ?>/.well-known/webfinger.php?q={uri}">
- </Link>
-</XRD>
-
diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php
index 5c2a24aa070..9ada473ca87 100644
--- a/apps/user_webfinger/webfinger.php
+++ b/apps/user_webfinger/webfinger.php
@@ -1,41 +1,71 @@
<?php
-if($_SERVER['SCRIPT_NAME'] == '/.well-known/webfinger.php') {
- header("Access-Control-Allow-Origin: *");
-} else {
- header('Please-first: activate');
-}
+header("Access-Control-Allow-Origin: *");
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_ID (user@host) and WF_BASEURL (e. g. https://host/owncloud).
+ * An example could look like this:
+ *
+ * <Link
+ * rel="myProfile"
+ * type="text/html"
+ * href="<?php echo WF_BASEURL; ?>/apps/myApp/profile.php?user=<?php echo WF_USER; ?>">
+ * </Link>
+ *
+ '* 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');
+$request = urldecode($_GET['q']);
if($_GET['q']) {
- $bits = explode('@', $_GET['q']);
- $userName = $bits[0];
+ $reqParts = explode('@', $request);
+ $userName = $reqParts[0];
+ $hostName = $reqParts[1];
} else {
$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://'.$_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_ID', $id);
+define('WF_BASEURL', $baseAddress);
echo "<";
?>
?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:hm="http://host-meta.net/xrd/1.0">
- <hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME'] ?></hm:Host>
- <Link
- rel="remoteStorage"
- template="<?php echo $baseAddress ?>WebDAV.php/<?php echo $userName ?>/remoteStorage/{category}/"
- api="WebDAV"
- auth="<?php echo $baseAddress; ?>auth.php/<?php echo $userName ?>"
- ></Link>
+ <hm:Host xmlns="http://host-meta.net/xrd/1.0"><?php echo $_SERVER['SERVER_NAME']; ?></hm:Host>
+ <Subject>acct:<?php echo $id ?></Subject>
+<?php
+$apps = OC_Appconfig::getApps();
+foreach($apps as $app) {
+ if(OC_App::isEnabled($app)) {
+ if(is_file(OC::$APPSROOT . '/apps/' . $app . '/appinfo/webfinger.php')) {
+ require($app . '/appinfo/webfinger.php');
+ }
+ }
+}
+?>
</XRD>