From 29372677aee0c415af7686712ab829e13e309999 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Tue, 29 May 2012 13:36:51 +0200 Subject: [PATCH 1/6] add lib collection and lib scanner to media's appinfo/app.php --- apps/media/appinfo/app.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/media/appinfo/app.php b/apps/media/appinfo/app.php index 26cb204554..d6a09c4805 100644 --- a/apps/media/appinfo/app.php +++ b/apps/media/appinfo/app.php @@ -23,6 +23,8 @@ $l=OC_L10N::get('media'); require_once('apps/media/lib_media.php'); +require_once('apps/media/lib_collection.php'); +require_once('apps/media/lib_scanner.php'); OCP\Util::addscript('media','loader'); OCP\App::registerPersonal('media','settings'); From dff16e70a9e9d26ebc416dcd046e3d65e55c5c35 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 28 May 2012 15:59:58 +0200 Subject: [PATCH 2/6] Contacts: Improve loading and cleanup of dialogs. --- apps/contacts/ajax/chooseaddressbook.php | 5 ++- apps/contacts/ajax/editaddress.php | 3 +- apps/contacts/ajax/editname.php | 5 +-- apps/contacts/css/contacts.css | 5 ++- apps/contacts/js/contacts.js | 55 ++++++++++++++---------- 5 files changed, 44 insertions(+), 29 deletions(-) diff --git a/apps/contacts/ajax/chooseaddressbook.php b/apps/contacts/ajax/chooseaddressbook.php index 9088a4e9d7..1ce8dd46ee 100644 --- a/apps/contacts/ajax/chooseaddressbook.php +++ b/apps/contacts/ajax/chooseaddressbook.php @@ -10,5 +10,6 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('contacts'); -$output = new OCP\Template("contacts", "part.chooseaddressbook"); -$output -> printpage(); +$tmpl = new OCP\Template("contacts", "part.chooseaddressbook"); +$page = $tmpl->fetchPage(); +OCP\JSON::success(array('data' => array('page'=>$page))); diff --git a/apps/contacts/ajax/editaddress.php b/apps/contacts/ajax/editaddress.php index 9fb35a0b5f..969aeeba8f 100644 --- a/apps/contacts/ajax/editaddress.php +++ b/apps/contacts/ajax/editaddress.php @@ -26,6 +26,7 @@ if($checksum) { $tmpl->assign('id',$id); $tmpl->assign('adr_types',$adr_types); -$tmpl->printpage(); +$page = $tmpl->fetchPage(); +OCP\JSON::success(array('data' => array('page'=>$page, 'checksum'=>$checksum))); ?> diff --git a/apps/contacts/ajax/editname.php b/apps/contacts/ajax/editname.php index dc64eeb510..9ba280ae54 100644 --- a/apps/contacts/ajax/editname.php +++ b/apps/contacts/ajax/editname.php @@ -35,9 +35,8 @@ if($id) { $tmpl->assign('id',$id); } else { bailOut(OC_Contacts_App::$l10n->t('Contact ID is missing.')); - //$addressbooks = OC_Contacts_Addressbook::active(OCP\USER::getUser()); - //$tmpl->assign('addressbooks', $addressbooks); } -$tmpl->printpage(); +$page = $tmpl->fetchPage(); +OCP\JSON::success(array('data' => array('page'=>$page))); ?> diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 9aa63f016c..1d02b3722d 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -30,6 +30,7 @@ textarea { width: 80%; min-height: 5em; min-width: 30em; margin: 0 !important; p dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } .form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom; color: #bbb;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ } .form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; } +label:hover, dt:hover { color: #333; } #address.form dt { min-width: 5em; } #address.form dl { min-width: 10em; } .droptarget { margin: 0.5em; padding: 0.5em; border: thin solid #ccc; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; } @@ -65,6 +66,8 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } .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;*/ } #cropbox { margin: auto; } +#contacts_details_photo_wrapper { min-width: 80px; } +#contacts_details_photo_wrapper.wait { opacity: 0.6; filter:alpha(opacity=0.6); z-index:1000; background: url('%webroot%/core/img/loading.gif') no-repeat center center; cursor: wait; } #contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; margin: 0.3em; background: url('%webroot%/core/img/loading.gif') no-repeat center center; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; } #contacts_details_photo:hover { background: #fff; cursor: default; } #phototools { position:absolute; margin: 5px 0 0 10px; width:auto; height:22px; padding:0px; background-color:#fff; list-style-type:none; border-radius: 0.5em; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; } @@ -76,7 +79,7 @@ dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; } #addressdisplay { padding: 0.5em; } dl.addresscard { background-color: #fff; float: left; width: auto; margin: 0 0.3em 0.3em 0.3em; padding: 0; border: 0; } dl.addresscard dd {} -dl.addresscard dt { padding: 0.3em; /*border-bottom: thin solid lightgray;*/ font-weight: bold; clear: both; color: #bbb;} +dl.addresscard dt { padding: 0.3em; font-weight: bold; clear: both; color: #bbb;} dl.addresscard dt:hover { color:#777; } dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; } dl.addresscard .action { float: right; } diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index 34afdcc4e4..d8aa724a22 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -758,28 +758,29 @@ Contacts={ } }, editName:function(){ - var isnew = (this.id == ''); + var params = {id: this.id}; /* Initialize the name edit dialog */ if($('#edit_name_dialog').dialog('isOpen') == true){ $('#edit_name_dialog').dialog('moveToTop'); }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editname.php')+'?id='+this.id, function(jsondata){ - if(jsondata.status != 'error'){ - $('#edit_name_dialog' ).dialog({ + $.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata){ + if(jsondata.status == 'success'){ + $('body').append('
'); + $('#name_dialog').html(jsondata.data.page).find('#edit_name_dialog' ).dialog({ modal: true, - closeOnEscape: (isnew == '' && false || true), - title: (isnew && t('contacts', 'Add contact') || t('contacts', 'Edit name')), + closeOnEscape: true, + title: t('contacts', 'Edit name'), height: 'auto', width: 'auto', buttons: { 'Ok':function() { Contacts.UI.Card.saveName(this); - $(this).dialog('destroy').remove(); + $(this).dialog('close'); }, - 'Cancel':function() { $(this).dialog('destroy').remove(); } + 'Cancel':function() { $(this).dialog('close'); } }, close: function(event, ui) { $(this).dialog('destroy').remove(); - //return event; + $('#name_dialog').remove(); }, open: function(event, ui) { // load 'N' property - maybe :-P @@ -881,23 +882,23 @@ Contacts={ }, editAddress:function(obj, isnew){ var container = undefined; - var q = q = '?id=' + this.id; + var params = {id: this.id}; if(obj === 'new') { isnew = true; $('#addressdisplay dl').first().clone(true).insertAfter($('#addressdisplay dl').last()).show(); container = $('#addressdisplay dl').last(); container.removeClass('template').addClass('propertycontainer'); } else { - q = q + '&checksum='+Contacts.UI.checksumFor(obj); + params['checksum'] = Contacts.UI.checksumFor(obj); } /* Initialize the address edit dialog */ if($('#edit_address_dialog').dialog('isOpen') == true){ $('#edit_address_dialog').dialog('moveToTop'); }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editaddress.php')+q, function(jsondata){ - if(jsondata.status != 'error'){ - $('#edit_address_dialog' ).dialog({ - /*modal: true,*/ + $.getJSON(OC.filePath('contacts', 'ajax', 'editaddress.php'),params,function(jsondata){ + if(jsondata.status == 'success'){ + $('body').append('
'); + $('#address_dialog').html(jsondata.data.page).find('#edit_address_dialog' ).dialog({ height: 'auto', width: 'auto', buttons: { 'Ok':function() { @@ -906,10 +907,10 @@ Contacts={ } else { Contacts.UI.Card.saveAddress(this, obj, isnew); } - $(this).dialog('destroy').remove(); + $(this).dialog('close'); }, 'Cancel':function() { - $(this).dialog('destroy').remove(); + $(this).dialog('close'); if(isnew) { container.remove(); } @@ -917,6 +918,7 @@ Contacts={ }, close : function(event, ui) { $(this).dialog('destroy').remove(); + $('#address_dialog').remove(); if(isnew) { container.remove(); } @@ -1122,13 +1124,16 @@ Contacts={ }, loadPhoto:function(refresh){ $('#phototools li a').tipsy('hide'); + var wrapper = $('#contacts_details_photo_wrapper'); + wrapper.addClass('wait'); $.getJSON(OC.filePath('contacts', 'ajax', 'loadphoto.php'),{'id':this.id, 'refresh': refresh},function(jsondata){ if(jsondata.status == 'success'){ $('#contacts_details_photo_wrapper').data('checksum', jsondata.data.checksum); - $('#contacts_details_photo_wrapper').html(jsondata.data.page); + wrapper.html(jsondata.data.page).ready(function(){ wrapper.removeClass('wait').tipsy() }); Contacts.UI.Card.loadPhotoHandlers(); } else{ + wrapper.removeClass('wait'); OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); @@ -1143,6 +1148,7 @@ Contacts={ $('#edit_photo_dialog_img').html(jsondata.data.page); } else{ + wrapper.removeClass('wait'); OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); @@ -1167,12 +1173,14 @@ Contacts={ savePhoto:function(){ var target = $('#crop_target'); var form = $('#cropform'); + var wrapper = $('#contacts_details_photo_wrapper'); + wrapper.addClass('wait'); form.submit(); target.load(function(){ var response=jQuery.parseJSON(target.contents().text()); if(response != undefined && response.status == 'success'){ // load cropped photo. - $('#contacts_details_photo_wrapper').html(response.data.page); + wrapper.html(response.data.page).ready(function(){ wrapper.removeClass('wait') }); Contacts.UI.Card.data.PHOTO = true; Contacts.UI.Card.loadPhotoHandlers(); }else{ @@ -1280,16 +1288,19 @@ Contacts={ if($('#chooseaddressbook_dialog').dialog('isOpen') == true){ $('#chooseaddressbook_dialog').dialog('moveToTop'); }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(jsondata){ - if(jsondata.status != 'error'){ - $('#chooseaddressbook_dialog').dialog({ + $('body').append('
'); + $.getJSON(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(jsondata){ + if(jsondata.status == 'success'){ + $('#addressbook_dialog').html(jsondata.data.page).find('#chooseaddressbook_dialog').dialog({ width : 600, close : function(event, ui) { $(this).dialog('destroy').remove(); + $('#addressbook_dialog').remove(); } }).css('overflow','visible'); } else { alert(jsondata.data.message); + $('#addressbook_dialog').remove(); } }); } From 51aa84e70adaa83ee195c4a187c490b073d16a00 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 29 May 2012 16:42:54 +0200 Subject: [PATCH 3/6] Contacts: NOTE wasn't saved properly. --- apps/contacts/ajax/saveproperty.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php index 1af05682b6..e4537cd979 100644 --- a/apps/contacts/ajax/saveproperty.php +++ b/apps/contacts/ajax/saveproperty.php @@ -102,6 +102,8 @@ switch($element) { $value = $vcard->getAsString('CATEGORIES'); } break;*/ + case 'NOTE': + $value = str_replace('\n', '\\n', $value); case 'EMAIL': $value = strtolower($value); break; From c9df18ed8c62f2d6eb454b2554e6bb40382f3701 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 29 May 2012 16:46:54 +0200 Subject: [PATCH 4/6] Forgot a break. --- apps/contacts/ajax/saveproperty.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php index e4537cd979..6509ac21f2 100644 --- a/apps/contacts/ajax/saveproperty.php +++ b/apps/contacts/ajax/saveproperty.php @@ -96,14 +96,9 @@ switch($element) { //$value = getOtherValue(); } break; - //case 'CATEGORIES': - /* multi autocomplete triggers an save with empty value - if (!$value) { - $value = $vcard->getAsString('CATEGORIES'); - } - break;*/ case 'NOTE': $value = str_replace('\n', '\\n', $value); + break; case 'EMAIL': $value = strtolower($value); break; From d334f33eba3c1b600f077caa8bbaf8bf872e86cc Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 16 May 2012 21:51:45 -0400 Subject: [PATCH 5/6] Initial support for Amazon S3 storage backend Conflicts: apps/files_external/tests/config.php --- apps/files_external/lib/amazons3.php | 236 +++++++++++++++++++++++++ apps/files_external/tests/amazons3.php | 50 ++++++ apps/files_external/tests/config.php | 6 + 3 files changed, 292 insertions(+) create mode 100644 apps/files_external/lib/amazons3.php create mode 100644 apps/files_external/tests/amazons3.php diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php new file mode 100644 index 0000000000..e847ef143c --- /dev/null +++ b/apps/files_external/lib/amazons3.php @@ -0,0 +1,236 @@ +. +*/ + +require_once 'aws-sdk-1.5.5/sdk.class.php'; + +class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { + + private $s3; + private $bucket; + private $objects = array(); + + private static $tempFiles = array(); + + // TODO options: storage class, encryption server side, encrypt before upload? + + public function __construct($params) { + $this->s3 = new AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); + $this->bucket = $params['bucket']; + } + + private function getObject($path) { + if (array_key_exists($path, $this->objects)) { + return $this->objects[$path]; + } else { + $response = $this->s3->get_object_metadata($this->bucket, $path); + if ($response) { + $this->objects[$path] = $response; + return $response; + // This object could be a folder, a '/' must be at the end of the path + } else if (substr($path, -1) != '/') { + $response = $this->s3->get_object_metadata($this->bucket, $path.'/'); + if ($response) { + $this->objects[$path] = $response; + return $response; + } + } + } + return false; + } + + public function mkdir($path) { + // Folders in Amazon S3 are 0 byte objects with a '/' at the end of the name + if (substr($path, -1) != '/') { + $path .= '/'; + } + $response = $this->s3->create_object($this->bucket, $path, array('body' => '')); + return $response->isOK(); + } + + public function rmdir($path) { + if (substr($path, -1) != '/') { + $path .= '/'; + } + return $this->unlink($path); + } + + public function opendir($path) { + if ($path == '' || $path == '/') { + // Use the '/' delimiter to only fetch objects inside the folder + $opt = array('delimiter' => '/'); + } else { + if (substr($path, -1) != '/') { + $path .= '/'; + } + $opt = array('delimiter' => '/', 'prefix' => $path); + } + $response = $this->s3->list_objects($this->bucket, $opt); + if ($response->isOK()) { + $files = array(); + foreach ($response->body->Contents as $object) { + // The folder being opened also shows up in the list of objects, don't add it to the files + if ($object->Key != $path) { + $files[] = basename($object->Key); + } + } + // Sub folders show up as CommonPrefixes + foreach ($response->body->CommonPrefixes as $object) { + $files[] = basename($object->Prefix); + } + OC_FakeDirStream::$dirs['amazons3'] = $files; + return opendir('fakedir://amazons3'); + } + return false; + } + + public function stat($path) { + if ($path == '' || $path == '/') { + $stat['size'] = $this->s3->get_bucket_filesize($this->bucket); + $stat['atime'] = time(); + $stat['mtime'] = $stat['atime']; + $stat['ctime'] = $stat['atime']; + } else if ($object = $this->getObject($path)) { + $stat['size'] = $object['Size']; + $stat['atime'] = time(); + $stat['mtime'] = strtotime($object['LastModified']); + $stat['ctime'] = $stat['mtime']; + } + if (isset($stat)) { + return $stat; + } + return false; + } + + public function filetype($path) { + if ($path == '' || $path == '/') { + return 'dir'; + } else if ($object = $this->getObject($path)) { + // Amazon S3 doesn't have typical folders, this is an alternative method to detect a folder + if (substr($object['Key'], -1) == '/' && $object['Size'] == 0) { + return 'dir'; + } else { + return 'file'; + } + } + return false; + } + + public function is_readable($path) { + // TODO Check acl and determine who grantee is + return true; + } + + public function is_writable($path) { + // TODO Check acl and determine who grantee is + return true; + } + + public function file_exists($path) { + if ($this->filetype($path) == 'dir' && substr($path, -1) != '/') { + $path .= '/'; + } + return $this->s3->if_object_exists($this->bucket, $path); + } + + public function unlink($path) { + $response = $this->s3->delete_object($this->bucket, $path); + return $response->isOK(); + } + + public function fopen($path, $mode) { + switch ($mode) { + case 'r': + case 'rb': + $tmpFile = OC_Helper::tmpFile(); + $handle = fopen($tmpFile, 'w'); + $response = $this->s3->get_object($this->bucket, $path, array('fileDownload' => $handle)); + if ($response->isOK()) { + return fopen($tmpFile, 'r'); + } + break; + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + if (strrpos($path, '.') !== false) { + $ext = substr($path, strrpos($path, '.')); + } else { + $ext = ''; + } + $tmpFile = OC_Helper::tmpFile($ext); + OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + if ($this->file_exists($path)) { + $source = $this->fopen($path, 'r'); + file_put_contents($tmpFile, $source); + } + self::$tempFiles[$tmpFile] = $path; + return fopen('close://'.$tmpFile, $mode); + } + return false; + } + + public function writeBack($tmpFile) { + if (isset(self::$tempFiles[$tmpFile])) { + $handle = fopen($tmpFile, 'r'); + $response = $this->s3->create_object($this->bucket, self::$tempFiles[$tmpFile], array('fileUpload' => $handle)); + if ($response->isOK()) { + unlink($tmpFile); + } + } + } + + public function getMimeType($path) { + if ($this->filetype($path) == 'dir') { + return 'httpd/unix-directory'; + } else if ($object = $this->getObject($path)) { + return $object['ContentType']; + } + return false; + } + + public function free_space($path) { + // Infinite? + return false; + } + + public function touch($path, $mtime = null) { + if (is_null($mtime)) { + $mtime = time(); + } + if ($this->filetype($path) == 'dir' && substr($path, -1) != '/') { + $path .= '/'; + } + $response = $this->s3->update_object($this->bucket, $path, array('meta' => array('LastModified' => $mtime))); + return $response->isOK(); + } + +} + +?> \ No newline at end of file diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php new file mode 100644 index 0000000000..d0084c94af --- /dev/null +++ b/apps/files_external/tests/amazons3.php @@ -0,0 +1,50 @@ +. +*/ + +$config = include('apps/files_external/tests/config.php'); +if (!is_array($config) or !isset($config['amazons3']) or !$config['amazons3']['run']) { + abstract class Test_Filestorage_AmazonS3 extends Test_FileStorage{} + return; +} else { + class Test_Filestorage_AmazonS3 extends Test_FileStorage { + + private $config; + private $id; + + public function setUp() { + $id = uniqid(); + $this->config = include('apps/files_external/tests/config.php'); + $this->config['amazons3']['bucket'] = $id; // Make sure we have a new empty bucket to work in + $this->instance = new OC_Filestorage_AmazonS3($this->config['amazons3']); + } + + public function tearDown() { + $s3 = new AmazonS3(array('key' => $this->config['amazons3']['key'], 'secret' => $this->config['amazons3']['secret'])); + if ($s3->delete_all_objects($this->id)) { + $s3->delete_bucket($this->id); + } + } + } +} + +?> + diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index 35b5fa0386..970008d642 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -36,4 +36,10 @@ return array( 'host'=>'localhost', 'root'=>'/test', ), + 'amazons3'=>array( + 'run'=>false, + 'key'=>'test', + 'secret'=>'test', + 'bucket'=>'bucket', + ), ); From fbe58755e58675231feb443b52d2f670b8a78434 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 29 May 2012 12:31:47 -0400 Subject: [PATCH 6/6] Restrict requested app to apps directory --- lib/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base.php b/lib/base.php index cc715afac5..fdb682bf50 100644 --- a/lib/base.php +++ b/lib/base.php @@ -437,7 +437,7 @@ class OC{ register_shutdown_function(array('OC_Helper','cleanTmp')); //parse the given parameters - self::$REQUESTEDAPP = (isset($_GET['app'])?str_replace('\0', '', strip_tags($_GET['app'])):OC_Config::getValue('defaultapp', 'files')); + self::$REQUESTEDAPP = (isset($_GET['app'])?str_replace(array('\0', '/', '\\', '..'), '', strip_tags($_GET['app'])):OC_Config::getValue('defaultapp', 'files')); if(substr_count(self::$REQUESTEDAPP, '?') != 0){ $app = substr(self::$REQUESTEDAPP, 0, strpos(self::$REQUESTEDAPP, '?')); $param = substr(self::$REQUESTEDAPP, strpos(self::$REQUESTEDAPP, '?') + 1);