summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorGeorg Ehrke <dev@georgswebsite.de>2012-04-24 21:59:56 +0200
committerGeorg Ehrke <dev@georgswebsite.de>2012-04-24 21:59:56 +0200
commitf17eea506afaab0c0755d0fefe3a3f62eeb38e9d (patch)
treeb5c1f8a33aef46f1a718d5313bef8f4e52700acf /apps
parentd6346b5b0bece4feecc36b7b97308dd3a5a4d6cc (diff)
parent9b134b063683aca678e19912ebc3928321751714 (diff)
downloadnextcloud-server-f17eea506afaab0c0755d0fefe3a3f62eeb38e9d.tar.gz
nextcloud-server-f17eea506afaab0c0755d0fefe3a3f62eeb38e9d.zip
fix merge conflicts
Diffstat (limited to 'apps')
-rw-r--r--apps/contacts/ajax/importaddressbook.php24
-rw-r--r--apps/contacts/ajax/uploadimport.php50
-rw-r--r--apps/contacts/css/contacts.css7
-rw-r--r--apps/contacts/import.php21
-rw-r--r--apps/contacts/js/contacts.js114
-rw-r--r--apps/contacts/lib/app.php1
-rw-r--r--apps/contacts/lib/vcard.php41
-rw-r--r--apps/contacts/templates/part.chooseaddressbook.php3
-rw-r--r--apps/contacts/templates/part.chooseaddressbook.rowfields.php2
-rw-r--r--apps/contacts/templates/part.contact.php2
-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.xml12
-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/files_versions/appinfo/app.php19
-rw-r--r--apps/files_versions/appinfo/info.xml12
-rw-r--r--apps/files_versions/appinfo/version (renamed from apps/files_versioning/appinfo/version)0
-rw-r--r--apps/files_versions/css/versions.css2
-rw-r--r--apps/files_versions/history.php60
-rw-r--r--apps/files_versions/js/versions.js2
-rw-r--r--apps/files_versions/settings.php10
-rw-r--r--apps/files_versions/templates/history.php18
-rw-r--r--apps/files_versions/templates/settings.php7
-rw-r--r--apps/files_versions/versions.php216
31 files changed, 585 insertions, 1242 deletions
diff --git a/apps/contacts/ajax/importaddressbook.php b/apps/contacts/ajax/importaddressbook.php
new file mode 100644
index 00000000000..5776c801a76
--- /dev/null
+++ b/apps/contacts/ajax/importaddressbook.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+require_once('../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_Util::checkAppEnabled('contacts');
+$upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize'));
+$post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size'));
+$maxUploadFilesize = min($upload_max_filesize, $post_max_size);
+
+$freeSpace=OC_Filesystem::free_space('/');
+$freeSpace=max($freeSpace,0);
+$maxUploadFilesize = min($maxUploadFilesize ,$freeSpace);
+
+$tmpl = new OC_Template('contacts', 'part.importaddressbook');
+$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
+$tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
+$tmpl->printpage();
+?>
diff --git a/apps/contacts/ajax/uploadimport.php b/apps/contacts/ajax/uploadimport.php
new file mode 100644
index 00000000000..ab680c8823f
--- /dev/null
+++ b/apps/contacts/ajax/uploadimport.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * 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_JSON::checkAppEnabled('contacts');
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/uploadimport.php: '.$msg, OC_Log::ERROR);
+ exit();
+}
+function debug($msg) {
+ OC_Log::write('contacts','ajax/uploadimport.php: '.$msg, OC_Log::DEBUG);
+}
+
+// If it is a Drag'n'Drop transfer it's handled here.
+$fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
+if($fn) {
+ $view = OC_App::getStorage('contacts');
+ $tmpfile = md5(rand());
+ if($view->file_put_contents('/'.$tmpfile, file_get_contents('php://input'))) {
+ debug($fn.' uploaded');
+ OC_JSON::success(array('data' => array('path'=>'', 'file'=>$tmpfile)));
+ } else {
+ bailOut(OC_Contacts_App::$l10n->t('Error uploading contacts to storage.'));
+ }
+}
+
+?>
diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css
index 15f98cffbfb..2ea99a56a7b 100644
--- a/apps/contacts/css/contacts.css
+++ b/apps/contacts/css/contacts.css
@@ -33,8 +33,9 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; }
.form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; }
#address.form dt { min-width: 5em; }
#address.form dl { min-width: 10em; }
-
.loading {/*cursor: progress; */ cursor: wait; }
+.droptarget { margin: 0.5em; padding: 0.5em; border: thin solid #ccc; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; }
+.droppable { margin: 0.5em; padding: 0.5em; border: thin dashed #333; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; }
.float { float: left; }
.listactions { height: 1em; width:60px; float: left; clear: right; }
.add,.edit,.delete,.mail, .globe, .upload, .cloud { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; opacity: 0.1; }
@@ -51,6 +52,7 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; }
#identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ }
/*#contact_photo { max-width: 250px; }*/
#contact_identity { min-width: 30em; }
+#note { min-width: 200px; }
.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; }
@@ -90,5 +92,4 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
.propertylist li > input[type="checkbox"],input[type="radio"] { float: left; clear: left; width: 20px; height: 20px; vertical-align: middle; }
.propertylist li > select { float: left; max-width: 8em; }
.typelist { float: left; max-width: 10em; } /* for multiselect */
-.addresslist { clear: both; }
-
+.addresslist { clear: both; } \ No newline at end of file
diff --git a/apps/contacts/import.php b/apps/contacts/import.php
index b80021e00ea..ca2c1e1605d 100644
--- a/apps/contacts/import.php
+++ b/apps/contacts/import.php
@@ -17,8 +17,14 @@ if(is_writable('import_tmp/')){
fwrite($progressfopen, '10');
fclose($progressfopen);
}
-$file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']);
-if($_POST['method'] == 'new'){
+$view = $file = null;
+if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
+ $view = OC_App::getStorage('contacts');
+ $file = $view->file_get_contents('/' . $_POST['file']);
+} else {
+ $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']);
+}
+if(isset($_POST['method']) && $_POST['method'] == 'new'){
$id = OC_Contacts_Addressbook::add(OC_User::getUser(), $_POST['addressbookname']);
OC_Contacts_Addressbook::setActive($id, 1);
}else{
@@ -99,12 +105,16 @@ if(is_writable('import_tmp/')){
if(count($parts) == 1){
$importready = array($file);
}
+$imported = 0;
+$failed = 0;
foreach($importready as $import){
$card = OC_VObject::parse($import);
if (!$card) {
+ $failed += 1;
OC_Log::write('contacts','Import: skipping card. Error parsing VCard: '.$import, OC_Log::ERROR);
continue; // Ditch cards that can't be parsed by Sabre.
}
+ $imported += 1;
OC_Contacts_VCard::add($id, $card);
}
//done the import
@@ -117,4 +127,9 @@ sleep(3);
if(is_writable('import_tmp/')){
unlink($progressfile);
}
-OC_JSON::success();
+if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
+ if(!$view->unlink('/' . $_POST['file'])) {
+ OC_Log::write('contacts','Import: Error unlinking OC_FilesystemView ' . '/' . $_POST['file'], OC_Log::ERROR);
+ }
+}
+OC_JSON::success(array('data' => array('imported'=>$imported, 'failed'=>$failed)));
diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js
index f684576b787..b94bf77c5ec 100644
--- a/apps/contacts/js/contacts.js
+++ b/apps/contacts/js/contacts.js
@@ -1263,7 +1263,8 @@ Contacts={
for(ptype in this.data.TEL[phone]['parameters'][param]) {
var pt = this.data.TEL[phone]['parameters'][param][ptype];
$('#phonelist li:last-child').find('select option').each(function(){
- if ($(this).val().toUpperCase() == pt.toUpperCase()) {
+ //if ($(this).val().toUpperCase() == pt.toUpperCase()) {
+ if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) {
$(this).attr('selected', 'selected');
}
});
@@ -1285,6 +1286,7 @@ Contacts={
},
},
Addressbooks:{
+ droptarget:undefined,
overview:function(){
if($('#chooseaddressbook_dialog').dialog('isOpen') == true){
$('#chooseaddressbook_dialog').dialog('moveToTop');
@@ -1317,14 +1319,13 @@ Contacts={
var tr = $(document.createElement('tr'))
.load(OC.filePath('contacts', 'ajax', 'addbook.php'));
$(object).closest('tr').after(tr).hide();
- /* TODO: Shouldn't there be some kinda error checking here? */
},
editAddressbook:function(object, bookid){
var tr = $(document.createElement('tr'))
.load(OC.filePath('contacts', 'ajax', 'editaddressbook.php') + "?bookid="+bookid);
$(object).closest('tr').after(tr).hide();
},
- deleteAddressbook:function(bookid){
+ deleteAddressbook:function(obj, bookid){
var check = confirm("Do you really want to delete this address book?");
if(check == false){
return false;
@@ -1332,9 +1333,10 @@ Contacts={
$.post(OC.filePath('contacts', 'ajax', 'deletebook.php'), { id: bookid},
function(jsondata) {
if (jsondata.status == 'success'){
- $('#chooseaddressbook_dialog').dialog('destroy').remove();
+ $(obj).closest('tr').remove();
+ //$('#chooseaddressbook_dialog').dialog('destroy').remove();
Contacts.UI.Contacts.update();
- Contacts.UI.Addressbooks.overview();
+ //Contacts.UI.Addressbooks.overview();
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
//alert('Error: ' + data.message);
@@ -1342,8 +1344,95 @@ Contacts={
});
}
},
- doImport:function(){
- Contacts.UI.notImplemented();
+ loadImportHandlers:function() {
+ this.droptarget = $('#import_drop_target');
+ console.log($('#import_drop_target').html());
+ $(this.droptarget).bind('dragover',function(event){
+ $(event.target).addClass('droppable');
+ event.stopPropagation();
+ event.preventDefault();
+ });
+ $(this.droptarget).bind('dragleave',function(event){
+ $(event.target).removeClass('droppable');
+ });
+ $(this.droptarget).bind('drop',function(event){
+ event.stopPropagation();
+ event.preventDefault();
+ console.log('drop');
+ $(event.target).removeClass('droppable');
+ $(event.target).html(t('contacts', 'Uploading...'));
+ Contacts.UI.loading(event.target, true);
+ $.fileUpload(event.originalEvent.dataTransfer.files);
+ });
+
+ $.fileUpload = function(files){
+ console.log(files + ', ' + files.length);
+ var file = files[0];
+ console.log('size: '+file.size+', type: '+file.type);
+ if(file.size > $('#max_upload').val()){
+ OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
+ $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Drop a VCF file to import contacts.'));
+ Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
+ return;
+ }
+ if(file.type.indexOf('text') != 0) {
+ OC.dialogs.alert(t('contacts','You have dropped a file type that cannot be imported: ') + file.type, t('contacts','Wrong file type'));
+ $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Drop a VCF file to import contacts.'));
+ Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
+ return;
+ }
+ var xhr = new XMLHttpRequest();
+
+ if (!xhr.upload) {
+ OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please upload the contacts file to ownCloud and import that way.'), t('contacts', 'Error'))
+ }
+ fileUpload = xhr.upload,
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4){
+ response = $.parseJSON(xhr.responseText);
+ if(response.status == 'success') {
+ if(xhr.status == 200) {
+ $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Importing...'));
+ Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, true);
+ Contacts.UI.Addressbooks.doImport(response.data.path, response.data.file);
+ } else {
+ $(Contacts.UI.Addressbooks.droptarget).html(t('contacts', 'Drop a VCF file to import contacts.'));
+ Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
+ OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error'));
+ }
+ } else {
+ OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
+ }
+ }
+ };
+ xhr.open("POST", 'ajax/uploadimport.php?file='+encodeURIComponent(file.name), true);
+ xhr.setRequestHeader('Cache-Control', 'no-cache');
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name));
+ xhr.setRequestHeader('X-File-Size', file.size);
+ xhr.setRequestHeader('Content-Type', file.type);
+ xhr.send(file);
+ }
+ },
+ importAddressbook:function(object){
+ var tr = $(document.createElement('tr'))
+ .load(OC.filePath('contacts', 'ajax', 'importaddressbook.php'));
+ $(object).closest('tr').after(tr).hide();
+ },
+ doImport:function(path, file){
+ var id = $('#importaddressbook_dialog').find('#book').val();
+ console.log('Selected book: ' + id);
+ $.post(OC.filePath('contacts', '', 'import.php'), { id: id, path: path, file: file, fstype: 'OC_FilesystemView' },
+ function(jsondata){
+ if(jsondata.status == 'success'){
+ Contacts.UI.Addressbooks.droptarget.html(t('contacts', 'Import done. Success/Failure: ')+jsondata.data.imported+'/'+jsondata.data.failed);
+ $('#chooseaddressbook_dialog').find('#close_button').val(t('contacts', 'OK'));
+ Contacts.UI.Contacts.update();
+ } else {
+ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+ }
+ });
+ Contacts.UI.loading(Contacts.UI.Addressbooks.droptarget, false);
},
submit:function(button, bookid){
var displayname = $("#displayname_"+bookid).val().trim();
@@ -1368,7 +1457,7 @@ Contacts={
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
- });
+ });
},
cancel:function(button, bookid){
$(button).closest('tr').prev().show().next().remove();
@@ -1385,7 +1474,6 @@ Contacts={
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
- //alert(jsondata.data.message);
}
});
setTimeout(Contacts.UI.Contacts.lazyupdate, 500);
@@ -1507,13 +1595,13 @@ $(document).ready(function(){
});
$('#contacts_details_photo_wrapper').bind('dragover',function(event){
console.log('dragover');
- $(event.target).css('background-color','red');
+ $(event.target).addClass('droppable');
event.stopPropagation();
event.preventDefault();
});
$('#contacts_details_photo_wrapper').bind('dragleave',function(event){
console.log('dragleave');
- $(event.target).css('background-color','white');
+ $(event.target).removeClass('droppable');
//event.stopPropagation();
//event.preventDefault();
});
@@ -1521,7 +1609,7 @@ $(document).ready(function(){
event.stopPropagation();
event.preventDefault();
console.log('drop');
- $(event.target).css('background-color','white')
+ $(event.target).removeClass('droppable');
$.fileUpload(event.originalEvent.dataTransfer.files);
});
@@ -1618,4 +1706,4 @@ $(document).ready(function() {
$('.ui-autocomplete-loading').css('background', 'url('+OC.filePath('core', 'img', 'loading.gif')+' right center no-repeat');
$('#contacts_details_photo').css('background', 'url('+OC.filePath('core', 'img', 'loading.gif')+' no-repeat center center');
$('#contacts_propertymenu_button').css('background', 'url('+OC.filePath('core', 'img/actions', 'add.svg')+') no-repeat center');
-}); \ No newline at end of file
+});
diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php
index 475d2c8dc2e..2c2cc331ed7 100644
--- a/apps/contacts/lib/app.php
+++ b/apps/contacts/lib/app.php
@@ -149,6 +149,7 @@ class OC_Contacts_App {
'WORK' => $l->t('Work'),
'TEXT' => $l->t('Text'),
'VOICE' => $l->t('Voice'),
+ 'MSG' => $l->t('Message'),
'FAX' => $l->t('Fax'),
'VIDEO' => $l->t('Video'),
'PAGER' => $l->t('Pager'),
diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php
index 90b037f76ee..96fc8cf7121 100644
--- a/apps/contacts/lib/vcard.php
+++ b/apps/contacts/lib/vcard.php
@@ -141,10 +141,38 @@ class OC_Contacts_VCard{
}
/**
- * @brief Tries to update imported VCards to adhere to rfc2426 (VERSION: 3.0)
+ * @brief Checks if a contact with the same UID already exist in the address book.
+ * @param $aid Address book ID.
+ * @param $uid UID (passed by reference).
+ * @returns true if the UID has been changed.
+ */
+ protected static function trueUID($aid, &$uid) {
+ $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri = ?' );
+ $uri = $uid.'.vcf';
+ $result = $stmt->execute(array($aid,$uri));
+ if($result->numRows() > 0){
+ while(true) {
+ $tmpuid = substr(md5(rand().time()),0,10);
+ $uri = $tmpuid.'.vcf';
+ $result = $stmt->execute(array($aid,$uri));
+ if($result->numRows() > 0){
+ continue;
+ } else {
+ $uid = $tmpuid;
+ return true;
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @brief Tries to update imported VCards to adhere to rfc2426 (VERSION: 3.0) and add mandatory fields if missing.
+ * @param aid Address book id.
* @param vcard An OC_VObject of type VCARD (passed by reference).
*/
- protected static function updateValuesFromAdd(&$vcard) { // any suggestions for a better method name? ;-)
+ protected static function updateValuesFromAdd($aid, &$vcard) { // any suggestions for a better method name? ;-)
$stringprops = array('N', 'FN', 'ORG', 'NICK', 'ADR', 'NOTE');
$typeprops = array('ADR', 'TEL', 'EMAIL');
$upgrade = false;
@@ -207,14 +235,19 @@ class OC_Contacts_VCard{
}
if(!$uid) {
$vcard->setUID();
+ $uid = $vcard->getAsString('UID');
OC_Log::write('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid,OC_Log::DEBUG);
}
+ if(self::trueUID($aid, $uid)) {
+ $vcard->setString('UID', $uid);
+ }
$vcard->setString('VERSION','3.0');
// Add product ID is missing.
$prodid = trim($vcard->getAsString('PRODID'));
if(!$prodid) {
$appinfo = OC_App::getAppInfo('contacts');
- $prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appinfo['version'].'//EN';
+ $appversion = OC_App::getAppVersion('contacts');
+ $prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN';
$vcard->setString('PRODID', $prodid);
}
$now = new DateTime;
@@ -236,7 +269,7 @@ class OC_Contacts_VCard{
OC_Contacts_App::loadCategoriesFromVCard($card);
- self::updateValuesFromAdd($card);
+ self::updateValuesFromAdd($aid, $card);
$fn = $card->getAsString('FN');
if (empty($fn)) {
diff --git a/apps/contacts/templates/part.chooseaddressbook.php b/apps/contacts/templates/part.chooseaddressbook.php
index 90894220ef8..adfc8c15161 100644
--- a/apps/contacts/templates/part.chooseaddressbook.php
+++ b/apps/contacts/templates/part.chooseaddressbook.php
@@ -1,4 +1,4 @@
-<div id="chooseaddressbook_dialog" title="<?php echo $l->t("Choose active Address Books"); ?>">
+<div id="chooseaddressbook_dialog" title="<?php echo $l->t("Configure Address Books"); ?>">
<table width="100%" style="border: 0;">
<?php
$option_addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
@@ -14,6 +14,7 @@ for($i = 0; $i < count($option_addressbooks); $i++){
<tr>
<td colspan="5" style="padding: 0.5em;">
<a class="button" href="#" onclick="Contacts.UI.Addressbooks.newAddressbook(this);"><?php echo $l->t('New Address Book') ?></a>
+ <a class="button" href="#" onclick="Contacts.UI.Addressbooks.importAddressbook(this);"><?php echo $l->t('Import from VCF') ?></a>
</td>
</tr>
<tr>
diff --git a/apps/contacts/templates/part.chooseaddressbook.rowfields.php b/apps/contacts/templates/part.chooseaddressbook.rowfields.php
index 8518c1acd13..20b67a4161e 100644
--- a/apps/contacts/templates/part.chooseaddressbook.rowfields.php
+++ b/apps/contacts/templates/part.chooseaddressbook.rowfields.php
@@ -2,4 +2,4 @@
// FIXME: Make this readable.
echo "<td width=\"20px\"><input id=\"active_" . $_['addressbook']["id"] . "\" type=\"checkbox\" onClick=\"Contacts.UI.Addressbooks.activation(this, " . $_['addressbook']["id"] . ")\"" . (OC_Contacts_Addressbook::isActive($_['addressbook']["id"]) ? ' checked="checked"' : '') . "></td>";
echo "<td><label for=\"active_" . $_['addressbook']["id"] . "\">" . htmlspecialchars($_['addressbook']["displayname"]) . "</label></td>";
- echo "<td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.showCardDAVUrl('" . OC_User::getUser() . "', '" . rawurlencode($_['addressbook']["uri"]) . "');\" title=\"" . $l->t("CardDav Link") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/public.svg\"></a></td><td width=\"20px\"><a href=\"export.php?bookid=" . $_['addressbook']["id"] . "\" title=\"" . $l->t("Download") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/download.svg\"></a></td><td width=\"20px\"><a href=\"#\" title=\"" . $l->t("Edit") . "\" class=\"action\" onclick=\"Contacts.UI.Addressbooks.editAddressbook(this, " . $_['addressbook']["id"] . ");\"><img class=\"svg action\" src=\"../../core/img/actions/rename.svg\"></a></td><td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.Addressbooks.deleteAddressbook('" . $_['addressbook']["id"] . "');\" title=\"" . $l->t("Delete") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/delete.svg\"></a></td>";
+ echo "<td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.showCardDAVUrl('" . OC_User::getUser() . "', '" . rawurlencode($_['addressbook']["uri"]) . "');\" title=\"" . $l->t("CardDav Link") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/public.svg\"></a></td><td width=\"20px\"><a href=\"export.php?bookid=" . $_['addressbook']["id"] . "\" title=\"" . $l->t("Download") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/download.svg\"></a></td><td width=\"20px\"><a href=\"#\" title=\"" . $l->t("Edit") . "\" class=\"action\" onclick=\"Contacts.UI.Addressbooks.editAddressbook(this, " . $_['addressbook']["id"] . ");\"><img class=\"svg action\" src=\"../../core/img/actions/rename.svg\"></a></td><td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.Addressbooks.deleteAddressbook(this, '" . $_['addressbook']["id"] . "');\" title=\"" . $l->t("Delete") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/delete.svg\"></a></td>";
diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php
index b90fa92c2c5..dec081a9b89 100644
--- a/apps/contacts/templates/part.contact.php
+++ b/apps/contacts/templates/part.contact.php
@@ -112,7 +112,7 @@ $id = isset($_['id']) ? $_['id'] : '';
<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>
+ <textarea class="contacts_property note" name="value" cols="60" rows="15" required="required" placeholder="<?php echo $l->t('Add notes here.'); ?>"></textarea>
</fieldset>
</form>
</div> <!-- contact_note -->
diff --git a/apps/files_versioning/ajax/gethead.php b/apps/files_versioning/ajax/gethead.php
deleted file mode 100644
index a0bfe77db51..00000000000
--- a/apps/files_versioning/ajax/gethead.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-/**
- * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- */
-
-
-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
deleted file mode 100644
index dd8b924b118..00000000000
--- a/apps/files_versioning/ajax/sethead.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-/**
- * Copyright (c) 2011 Craig Roberts craig0990@googlemail.com
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- */
-
-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
deleted file mode 100644
index 24a8701dbb0..00000000000
--- a/apps/files_versioning/appinfo/app.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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
deleted file mode 100644
index b9f56f674a0..00000000000
--- a/apps/files_versioning/appinfo/info.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<info>
- <id>files_versioning</id>
- <name>Versioning and Backup</name>
- <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
deleted file mode 100644
index afe2cd5508f..00000000000
--- a/apps/files_versioning/css/settings.css
+++ /dev/null
@@ -1,3 +0,0 @@
-#file_versioning_commit_chzn {
- width: 15em;
-}
diff --git a/apps/files_versioning/js/settings.js b/apps/files_versioning/js/settings.js
deleted file mode 100644
index 8dd13bac033..00000000000
--- a/apps/files_versioning/js/settings.js
+++ /dev/null
@@ -1,25 +0,0 @@
-$(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
deleted file mode 100644
index 571e5cea637..00000000000
--- a/apps/files_versioning/lib_granite.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?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
deleted file mode 100644
index 94af587a215..00000000000
--- a/apps/files_versioning/settings.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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
deleted file mode 100644
index 17f4cc7f77f..00000000000
--- a/apps/files_versioning/templates/settings.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<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
deleted file mode 100644
index d083e623df9..00000000000
--- a/apps/files_versioning/versionstorage.php
+++ /dev/null
@@ -1,386 +0,0 @@
-<?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
deleted file mode 100644
index b83a4fd3b22..00000000000
--- a/apps/files_versioning/versionwrapper.php
+++ /dev/null
@@ -1,686 +0,0 @@
-<?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/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php
new file mode 100644
index 00000000000..6e7a803252e
--- /dev/null
+++ b/apps/files_versions/appinfo/app.php
@@ -0,0 +1,19 @@
+<?php
+
+require_once('apps/files_versions/versions.php');
+
+// Add an entry in the app list
+OC_App::register( array(
+ 'order' => 10,
+ 'id' => 'files_versions',
+ 'name' => 'Versioning' ));
+
+OC_APP::registerAdmin('files_versions', 'settings');
+
+// Listen to write signals
+OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, "OCA_Versions\Storage", "write_hook");
+
+
+
+
+?>
diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml
new file mode 100644
index 00000000000..9936a2ad8b2
--- /dev/null
+++ b/apps/files_versions/appinfo/info.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<info>
+ <id>files_versions</id>
+ <name>Versions</name>
+ <licence>AGPL</licence>
+ <author>Frank Karlitschek</author>
+ <require>3</require>
+ <description>Versioning of files</description>
+ <types>
+ <filesystem/>
+ </types>
+</info>
diff --git a/apps/files_versioning/appinfo/version b/apps/files_versions/appinfo/version
index afaf360d37f..afaf360d37f 100644
--- a/apps/files_versioning/appinfo/version
+++ b/apps/files_versions/appinfo/version
diff --git a/apps/files_versions/css/versions.css b/apps/files_versions/css/versions.css
new file mode 100644
index 00000000000..139597f9cb0
--- /dev/null
+++ b/apps/files_versions/css/versions.css
@@ -0,0 +1,2 @@
+
+
diff --git a/apps/files_versions/history.php b/apps/files_versions/history.php
new file mode 100644
index 00000000000..6c7626ca4ed
--- /dev/null
+++ b/apps/files_versions/history.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * ownCloud - History page of the Versions App
+ *
+ * @author Frank Karlitschek
+ * @copyright 2011 Frank Karlitschek karlitschek@kde.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 Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+require_once('../../lib/base.php');
+
+OC_Util::checkLoggedIn();
+
+if (isset($_GET['path'])) {
+
+ $path = $_GET['path'];
+ $path = strip_tags($path);
+
+ // roll back to old version if button clicked
+ if(isset($_GET['revert'])) {
+ \OCA_Versions\Storage::rollback($path,$_GET['revert']);
+ }
+
+ // show the history only if there is something to show
+ if(OCA_Versions\Storage::isversioned($path)) {
+
+ $count=5; //show the newest revisions
+ $versions=OCA_Versions\Storage::getversions($path,$count);
+
+ $tmpl = new OC_Template('files_versions', 'history', 'user');
+ $tmpl->assign('path', $path);
+ $tmpl->assign('versions', array_reverse($versions));
+ $tmpl->printPage();
+ }else{
+ $tmpl = new OC_Template('files_versions', 'history', 'user');
+ $tmpl->assign('path', $path);
+ $tmpl->assign('message', 'No old versions available');
+ $tmpl->printPage();
+ }
+}else{
+ $tmpl = new OC_Template('files_versions', 'history', 'user');
+ $tmpl->assign('message', 'No path specified');
+ $tmpl->printPage();
+}
+
+
+?>
diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js
new file mode 100644
index 00000000000..139597f9cb0
--- /dev/null
+++ b/apps/files_versions/js/versions.js
@@ -0,0 +1,2 @@
+
+
diff --git a/apps/files_versions/settings.php b/apps/files_versions/settings.php
new file mode 100644
index 00000000000..eb154d3edd3
--- /dev/null
+++ b/apps/files_versions/settings.php
@@ -0,0 +1,10 @@
+<?php
+
+OC_Util::checkAdminUser();
+
+OC_Util::addScript( 'files_versions', 'versions' );
+
+$tmpl = new OC_Template( 'files_versions', 'settings');
+
+return $tmpl->fetchPage();
+?>
diff --git a/apps/files_versions/templates/history.php b/apps/files_versions/templates/history.php
new file mode 100644
index 00000000000..1b3de9ce77c
--- /dev/null
+++ b/apps/files_versions/templates/history.php
@@ -0,0 +1,18 @@
+<?php
+ if(isset($_['message'])){
+
+
+ if(isset($_['path'])) echo('<strong>File: '.$_['path']).'</strong><br>';
+ echo('<strong>'.$_['message']).'</strong><br>';
+
+ }else{
+
+ echo('<strong>Versions of '.$_['path']).'</strong><br>';
+ echo('<p><em>You can click on the revert button to revert to the specific verson.</em></p><br />');
+ foreach ($_['versions'] as $v){
+ echo(' '.OC_Util::formatDate($v).' <a href="history.php?path='.urlencode($_['path']).'&revert='.$v.'" class="button">revert</a><br /><br />');
+ }
+
+ }
+
+?>
diff --git a/apps/files_versions/templates/settings.php b/apps/files_versions/templates/settings.php
new file mode 100644
index 00000000000..8c8def94429
--- /dev/null
+++ b/apps/files_versions/templates/settings.php
@@ -0,0 +1,7 @@
+<form id="external">
+ <fieldset class="personalblock">
+ <strong>Versions</strong><br />
+
+ Configuration goes here...
+ </fieldset>
+</form>
diff --git a/apps/files_versions/versions.php b/apps/files_versions/versions.php
new file mode 100644
index 00000000000..156a4f59c73
--- /dev/null
+++ b/apps/files_versions/versions.php
@@ -0,0 +1,216 @@
+<?php
+/**
+ * Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * Versions
+ *
+ * A class to handle the versioning of files.
+ */
+
+namespace OCA_Versions;
+
+class Storage {
+
+
+ // config.php configuration:
+ // - files_versions
+ // - files_versionsfolder
+ // - files_versionsblacklist
+ // - files_versionsmaxfilesize
+ // - files_versionsinterval
+ // - files_versionmaxversions
+ //
+ // todo:
+ // - port to oc_filesystem to enable network transparency
+ // - check if it works well together with encryption
+ // - do configuration web interface
+ // - implement expire all function. And find a place to call it ;-)
+ // - add transparent compression. first test if it´s worth it.
+
+ const DEFAULTENABLED=true;
+ const DEFAULTFOLDER='versions';
+ const DEFAULTBLACKLIST='avi mp3 mpg mp4';
+ const DEFAULTMAXFILESIZE=1048576; // 10MB
+ const DEFAULTMININTERVAL=300; // 5 min
+ const DEFAULTMAXVERSIONS=50;
+
+ /**
+ * init the versioning and create the versions folder.
+ */
+ public static function init() {
+ if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ // create versions folder
+ $foldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+ if(!is_dir($foldername)){
+ mkdir($foldername);
+ }
+ }
+ }
+
+
+ /**
+ * listen to write event.
+ */
+ public static function write_hook($params) {
+ if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $path = $params[\OC_Filesystem::signal_param_path];
+ if($path<>'') Storage::store($path);
+ }
+ }
+
+
+
+ /**
+ * store a new version of a file.
+ */
+ public static function store($filename) {
+ if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+ $filesfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/files';
+ Storage::init();
+
+ // check if filename is a directory
+ if(is_dir($filesfoldername.$filename)){
+ return false;
+ }
+
+ // check filetype blacklist
+ $blacklist=explode(' ',\OC_Config::getValue('files_versionsblacklist', Storage::DEFAULTBLACKLIST));
+ foreach($blacklist as $bl) {
+ $parts=explode('.', $filename);
+ $ext=end($parts);
+ if(strtolower($ext)==$bl) {
+ return false;
+ }
+ }
+
+ // check filesize
+ if(filesize($filesfoldername.$filename)>\OC_Config::getValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){
+ return false;
+ }
+
+
+ // check mininterval
+ $matches=glob($versionsfoldername.$filename.'.v*');
+ sort($matches);
+ $parts=explode('.v',end($matches));
+ if((end($parts)+Storage::DEFAULTMININTERVAL)>time()){
+ return false;
+ }
+
+
+ // create all parent folders
+ $info=pathinfo($filename);
+ @mkdir($versionsfoldername.$info['dirname'],0700,true);
+
+
+ // store a new version of a file
+ copy($filesfoldername.$filename,$versionsfoldername.$filename.'.v'.time());
+
+ // expire old revisions
+ Storage::expire($filename);
+ }
+ }
+
+
+ /**
+ * rollback to an old version of a file.
+ */
+ public static function rollback($filename,$revision) {
+ if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+ $filesfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/files';
+ // rollback
+ @copy($versionsfoldername.$filename.'.v'.$revision,$filesfoldername.$filename);
+ }
+ }
+
+ /**
+ * check if old versions of a file exist.
+ */
+ public static function isversioned($filename) {
+ if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+
+ // check for old versions
+ $matches=glob($versionsfoldername.$filename.'.v*');
+ if(count($matches)>1){
+ return true;
+ }else{
+ return false;
+ }
+ }else{
+ return(false);
+ }
+ }
+
+
+
+ /**
+ * get a list of old versions of a file.
+ */
+ public static function getversions($filename,$count=0) {
+ if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+ $versions=array();
+
+ // fetch for old versions
+ $matches=glob($versionsfoldername.$filename.'.v*');
+ sort($matches);
+ foreach($matches as $ma) {
+ $parts=explode('.v',$ma);
+ $versions[]=(end($parts));
+ }
+
+ // only show the newest commits
+ if($count<>0 and (count($versions)>$count)) {
+ $versions=array_slice($versions,count($versions)-$count);
+ }
+
+ return($versions);
+
+
+ }else{
+ return(array());
+ }
+ }
+
+
+
+ /**
+ * expire old versions of a file.
+ */
+ public static function expire($filename) {
+ if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+
+ $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+
+ // check for old versions
+ $matches=glob($versionsfoldername.$filename.'.v*');
+ if(count($matches)>\OC_Config::getValue('files_versionmaxversions', Storage::DEFAULTMAXVERSIONS)){
+ $numbertodelete=count($matches-\OC_Config::getValue('files_versionmaxversions', Storage::DEFAULTMAXVERSIONS));
+
+ // delete old versions of a file
+ $deleteitems=array_slice($matches,0,$numbertodelete);
+ foreach($deleteitems as $de){
+ unlink($versionsfoldername.$filename.'.v'.$de);
+ }
+ }
+ }
+ }
+
+ /**
+ * expire all old versions.
+ */
+ public static function expireall($filename) {
+ // todo this should go through all the versions directories and delete all the not needed files and not needed directories.
+ // useful to be included in a cleanup cronjob.
+ }
+
+
+}