diff --git a/3rdparty/MDB2/Driver/mysql.php b/3rdparty/MDB2/Driver/mysql.php index b9b46c0d76..2cec1e9c03 100644 --- a/3rdparty/MDB2/Driver/mysql.php +++ b/3rdparty/MDB2/Driver/mysql.php @@ -794,7 +794,7 @@ class MDB2_Driver_mysql extends MDB2_Driver_Common ? 'mysql_query' : 'mysql_unbuffered_query'; $result = @$function($query, $connection); if (!$result) { - $err =& $this->raiseError(null, null, null, + $err =$this->raiseError(null, null, null, 'Could not execute statement', __FUNCTION__); return $err; } diff --git a/3rdparty/phpass/PasswordHash.php b/3rdparty/phpass/PasswordHash.php new file mode 100644 index 0000000000..12958c7f19 --- /dev/null +++ b/3rdparty/phpass/PasswordHash.php @@ -0,0 +1,253 @@ + in 2004-2006 and placed in +# the public domain. Revised in subsequent years, still public domain. +# +# There's absolutely no warranty. +# +# The homepage URL for this framework is: +# +# http://www.openwall.com/phpass/ +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function PasswordHash($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime(); + if (function_exists('getmypid')) + $this->random_state .= getmypid(); + } + + function get_random_bytes($count) + { + $output = ''; + if (is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + $id = substr($setting, 0, 3); + # We use "$P$", phpBB3 uses "$H$" for the same thing + if ($id != '$P$' && $id != '$H$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + + # We're kind of forced to use MD5 here since it's the only + # cryptographic primitive available in all versions of PHP + # currently in use. To implement our own low-level crypto + # in PHP would result in much worse performance and + # consequently in lower iteration counts and hashes that are + # quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + # This should be odd to not reveal weak DES keys, and the + # maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { + # This one needs to use a different order of characters and a + # different encoding scheme from the one in encode64() above. + # We care because the last character in our encoded string will + # only represent 2 bits. While two known implementations of + # bcrypt will happily accept and correct a salt string which + # has the 4 unused bits set to non-zero, we do not want to take + # chances and we also do not want to waste an additional byte + # of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + + # Returning '*' on error is safe here, but would _not_ be safe + # in a crypt(3)-like function used _both_ for generating new + # hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash == $stored_hash; + } +} + +?> diff --git a/3rdparty/phpass/c/Makefile b/3rdparty/phpass/c/Makefile new file mode 100644 index 0000000000..fe48917f7f --- /dev/null +++ b/3rdparty/phpass/c/Makefile @@ -0,0 +1,21 @@ +# +# Written by Solar Designer and placed in the public domain. +# See crypt_private.c for more information. +# +CC = gcc +LD = $(CC) +RM = rm -f +CFLAGS = -Wall -O2 -fomit-frame-pointer -funroll-loops +LDFLAGS = -s +LIBS = -lcrypto + +all: crypt_private-test + +crypt_private-test: crypt_private-test.o + $(LD) $(LDFLAGS) $(LIBS) crypt_private-test.o -o $@ + +crypt_private-test.o: crypt_private.c + $(CC) -c $(CFLAGS) crypt_private.c -DTEST -o $@ + +clean: + $(RM) crypt_private-test* diff --git a/3rdparty/phpass/c/crypt_private.c b/3rdparty/phpass/c/crypt_private.c new file mode 100644 index 0000000000..6abc05bc1d --- /dev/null +++ b/3rdparty/phpass/c/crypt_private.c @@ -0,0 +1,106 @@ +/* + * This code exists for the sole purpose to serve as another implementation + * of the "private" password hashing method implemened in PasswordHash.php + * and thus to confirm that these password hashes are indeed calculated as + * intended. + * + * Other uses of this code are discouraged. There are much better password + * hashing algorithms available to C programmers; one of those is bcrypt: + * + * http://www.openwall.com/crypt/ + * + * Written by Solar Designer in 2005 and placed in + * the public domain. + * + * There's absolutely no warranty. + */ + +#include +#include + +#ifdef TEST +#include +#endif + +static char *itoa64 = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void encode64(char *dst, char *src, int count) +{ + int i, value; + + i = 0; + do { + value = (unsigned char)src[i++]; + *dst++ = itoa64[value & 0x3f]; + if (i < count) + value |= (unsigned char)src[i] << 8; + *dst++ = itoa64[(value >> 6) & 0x3f]; + if (i++ >= count) + break; + if (i < count) + value |= (unsigned char)src[i] << 16; + *dst++ = itoa64[(value >> 12) & 0x3f]; + if (i++ >= count) + break; + *dst++ = itoa64[(value >> 18) & 0x3f]; + } while (i < count); +} + +char *crypt_private(char *password, char *setting) +{ + static char output[35]; + MD5_CTX ctx; + char hash[MD5_DIGEST_LENGTH]; + char *p, *salt; + int count_log2, length, count; + + strcpy(output, "*0"); + if (!strncmp(setting, output, 2)) + output[1] = '1'; + + if (strncmp(setting, "$P$", 3)) + return output; + + p = strchr(itoa64, setting[3]); + if (!p) + return output; + count_log2 = p - itoa64; + if (count_log2 < 7 || count_log2 > 30) + return output; + + salt = setting + 4; + if (strlen(salt) < 8) + return output; + + length = strlen(password); + + MD5_Init(&ctx); + MD5_Update(&ctx, salt, 8); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + + count = 1 << count_log2; + do { + MD5_Init(&ctx); + MD5_Update(&ctx, hash, MD5_DIGEST_LENGTH); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + } while (--count); + + memcpy(output, setting, 12); + encode64(&output[12], hash, MD5_DIGEST_LENGTH); + + return output; +} + +#ifdef TEST +int main(int argc, char **argv) +{ + if (argc != 3) return 1; + + puts(crypt_private(argv[1], argv[2])); + + return 0; +} +#endif diff --git a/3rdparty/phpass/test.php b/3rdparty/phpass/test.php new file mode 100644 index 0000000000..2f4a41c8c3 --- /dev/null +++ b/3rdparty/phpass/test.php @@ -0,0 +1,72 @@ +HashPassword($correct); + +print 'Hash: ' . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$wrong = 'test12346'; +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +unset($t_hasher); + +# Force the use of weaker portable hashes. +$t_hasher = new PasswordHash(8, TRUE); + +$hash = $t_hasher->HashPassword($correct); + +print 'Hash: ' . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +# A correct portable hash for 'test12345'. +# Please note the use of single quotes to ensure that the dollar signs will +# be interpreted literally. Of course, a real application making use of the +# framework won't store password hashes within a PHP source file anyway. +# We only do this for testing. +$hash = '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0'; + +print 'Hash: ' . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +if ($ok == 6) + print "All tests have PASSED\n"; +else + print "Some tests have FAILED\n"; + +?> diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php index a1f6139d89..09d7b5df52 100644 --- a/apps/bookmarks/appinfo/app.php +++ b/apps/bookmarks/appinfo/app.php @@ -8,6 +8,7 @@ */ OC::$CLASSPATH['OC_Bookmarks_Bookmarks'] = 'apps/bookmarks/lib/bookmarks.php'; +OC::$CLASSPATH['OC_Search_Provider_Bookmarks'] = 'apps/bookmarks/lib/search.php'; OC_App::register( array( 'order' => 70, 'id' => 'bookmark', 'name' => 'Bookmarks' )); @@ -15,5 +16,5 @@ $l = new OC_l10n('bookmarks'); OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'href' => OC_Helper::linkTo( 'bookmarks', 'index.php' ), 'icon' => OC_Helper::imagePath( 'bookmarks', 'bookmarks.png' ), 'name' => $l->t('Bookmarks'))); OC_App::registerPersonal('bookmarks', 'settings'); -require_once('apps/bookmarks/lib/search.php'); OC_Util::addScript('bookmarks','bookmarksearch'); +OC_Search::registerProvider('OC_Search_Provider_Bookmarks'); diff --git a/apps/bookmarks/css/bookmarks.css b/apps/bookmarks/css/bookmarks.css index b1139f2c34..3a3e0fbf6b 100644 --- a/apps/bookmarks/css/bookmarks.css +++ b/apps/bookmarks/css/bookmarks.css @@ -17,7 +17,9 @@ } .bookmarks_list { - margin-top: 36px; + overflow: auto; + position: fixed; + top: 6.5em; } .bookmarks_addBml { diff --git a/apps/bookmarks/js/bookmarks.js b/apps/bookmarks/js/bookmarks.js index 7317a154a7..fa5adde254 100644 --- a/apps/bookmarks/js/bookmarks.js +++ b/apps/bookmarks/js/bookmarks.js @@ -5,7 +5,11 @@ var bookmarks_sorting = 'bookmarks_sorting_recent'; $(document).ready(function() { $('#bookmark_add_submit').click(addOrEditBookmark); - $(window).scroll(updateOnBottom); + $(window).resize(function () { + fillWindow($('.bookmarks_list')); + }); + $(window).resize(); + $($('.bookmarks_list')).scroll(updateOnBottom); $('.bookmarks_list').empty(); getBookmarks(); @@ -21,7 +25,9 @@ function getBookmarks() { url: 'ajax/updateList.php', data: 'tag=' + encodeURI($('#bookmarkFilterTag').val()) + '&page=' + bookmarks_page + '&sort=' + bookmarks_sorting, success: function(bookmarks){ - bookmarks_page += 1; + if (bookmarks.data.length) { + bookmarks_page += 1; + } $('.bookmark_link').unbind('click', recordClick); $('.bookmark_delete').unbind('click', delBookmark); $('.bookmark_edit').unbind('click', showBookmark); @@ -39,6 +45,9 @@ function getBookmarks() { $('.bookmark_edit').click(showBookmark); bookmarks_loading = false; + if (bookmarks.data.length) { + updateOnBottom() + } } }); } @@ -146,7 +155,11 @@ function updateBookmarksList(bookmark) { function updateOnBottom() { //check wether user is on bottom of the page - if ($('body').height() <= ($(window).height() + $(window).scrollTop())) { + var top = $('.bookmarks_list>:last-child').position().top; + var height = $('.bookmarks_list').height(); + // use a bit of margin to begin loading before we are really at the + // bottom + if (top < height * 1.2) { getBookmarks(); } } diff --git a/apps/bookmarks/lib/search.php b/apps/bookmarks/lib/search.php index 59495db82e..235587855d 100644 --- a/apps/bookmarks/lib/search.php +++ b/apps/bookmarks/lib/search.php @@ -20,8 +20,8 @@ * */ -class OC_Search_Provider_Bookmarks extends OC_Search_Provider{ - function search($query){ +class OC_Search_Provider_Bookmarks implements OC_Search_Provider{ + static function search($query){ $results=array(); $offset = 0; @@ -45,6 +45,3 @@ class OC_Search_Provider_Bookmarks extends OC_Search_Provider{ return $results; } } -new OC_Search_Provider_Bookmarks(); - -?> diff --git a/apps/bookmarks/templates/bookmarklet.php b/apps/bookmarks/templates/bookmarklet.php index 3d8bbcc7b3..5ea67f04df 100644 --- a/apps/bookmarks/templates/bookmarklet.php +++ b/apps/bookmarks/templates/bookmarklet.php @@ -3,6 +3,6 @@ function createBookmarklet() { $l = new OC_L10N('bookmarks'); echo '' . $l->t('Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:') . '' - . '' + . '' . $l->t('Read later') . ''; } diff --git a/apps/calendar/ajax/events.php b/apps/calendar/ajax/events.php index 6ea556a7ab..dff02e1587 100755 --- a/apps/calendar/ajax/events.php +++ b/apps/calendar/ajax/events.php @@ -27,25 +27,40 @@ function create_return_event($event, $vevent){ OC_JSON::checkLoggedIn(); OC_JSON::checkAppEnabled('calendar'); -$start = DateTime::createFromFormat('U', $_GET['start']); -$end = DateTime::createFromFormat('U', $_GET['end']); +if(version_compare(PHP_VERSION, '5.3.0', '>=')){ + $start = DateTime::createFromFormat('U', $_GET['start']); + $end = DateTime::createFromFormat('U', $_GET['end']); +}else{ + $start = new DateTime('@' . $_GET['start']); + $end = new DateTime('@' . $_GET['end']); +} -$calendar = OC_Calendar_App::getCalendar($_GET['calendar_id']); -OC_Response::enableCaching(0); -OC_Response::setETagHeader($calendar['ctag']); +$calendar_id = $_GET['calendar_id']; +if (is_numeric($calendar_id)) { + $calendar = OC_Calendar_App::getCalendar($calendar_id); + OC_Response::enableCaching(0); + OC_Response::setETagHeader($calendar['ctag']); + $events = OC_Calendar_Object::allInPeriod($calendar_id, $start, $end); +} else { + $events = array(); + OC_Hook::emit('OC_Calendar', 'getEvents', array('calendar_id' => $calendar_id, 'events' => &$events)); +} -$events = OC_Calendar_Object::allInPeriod($_GET['calendar_id'], $start, $end); $user_timezone = OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); $return = array(); foreach($events as $event){ - $object = OC_VObject::parse($event['calendardata']); - $vevent = $object->VEVENT; + if (isset($event['calendardata'])) { + $object = OC_VObject::parse($event['calendardata']); + $vevent = $object->VEVENT; + } else { + $vevent = $event['vevent']; + } $return_event = create_return_event($event, $vevent); $dtstart = $vevent->DTSTART; - $dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent); $start_dt = $dtstart->getDateTime(); + $dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent); $end_dt = $dtend->getDateTime(); if ($dtstart->getDateType() == Sabre_VObject_Property_DateTime::DATE){ $return_event['allDay'] = true; diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php index 96d7af4834..c0797f6e42 100644 --- a/apps/calendar/ajax/import/import.php +++ b/apps/calendar/ajax/import/import.php @@ -11,7 +11,7 @@ require_once('../../../../lib/base.php'); OC_JSON::checkLoggedIn(); OC_Util::checkAppEnabled('calendar'); $nl = "\n"; -$progressfile = OC::$SERVERROOT . '/apps/calendar/import_tmp/' . md5(session_id()) . '.txt'; +$progressfile = OC::$APPSROOT . '/apps/calendar/import_tmp/' . md5(session_id()) . '.txt'; if(is_writable('import_tmp/')){ $progressfopen = fopen($progressfile, 'w'); fwrite($progressfopen, '10'); @@ -117,4 +117,4 @@ sleep(3); if(is_writable('import_tmp/')){ unlink($progressfile); } -OC_JSON::success(); \ No newline at end of file +OC_JSON::success(); diff --git a/apps/calendar/ajax/settings/getfirstday.php b/apps/calendar/ajax/settings/getfirstday.php new file mode 100644 index 0000000000..cab5870509 --- /dev/null +++ b/apps/calendar/ajax/settings/getfirstday.php @@ -0,0 +1,12 @@ + + * 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(); +$firstday = OC_Preferences::getValue( OC_User::getUser(), 'calendar', 'firstday', 'mo'); +OC_JSON::encodedPrint(array('firstday' => $firstday)); +?> diff --git a/apps/calendar/ajax/settings/guesstimezone.php b/apps/calendar/ajax/settings/guesstimezone.php index cfa92e1aee..d45a70e1ce 100755 --- a/apps/calendar/ajax/settings/guesstimezone.php +++ b/apps/calendar/ajax/settings/guesstimezone.php @@ -5,44 +5,23 @@ * later. * See the COPYING-README file. */ -function make_array_out_of_xml ($xml){ - $returnarray = array(); - $xml = (array)$xml ; - foreach ($xml as $property => $value){ - $value = (array)$value; - if(!isset($value[0])){ - $returnarray[$property] = make_array_out_of_xml($value); - }else{ - $returnarray[$property] = trim($value[0]); - } - } - return $returnarray; -} require_once('../../../../lib/base.php'); + OC_JSON::checkLoggedIn(); OC_JSON::checkAppEnabled('calendar'); -$l = new OC_L10N('calendar'); -$lat = $_GET['lat']; -$long = $_GET['long']; -if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'position') == $lat . '-' . $long && OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone') != null){ - OC_JSON::success(); - exit; -} -OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'position', $lat . '-' . $long); -$geolocation = file_get_contents('http://ws.geonames.org/timezone?lat=' . $lat . '&lng=' . $long); -//Information are by Geonames (http://www.geonames.org) and licensed under the Creative Commons Attribution 3.0 License -$geoxml = simplexml_load_string($geolocation); -$geoarray = make_array_out_of_xml($geoxml); -if($geoarray['timezone']['timezoneId'] == OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone')){ - OC_JSON::success(); - exit; -} -if(in_array($geoarray['timezone']['timezoneId'], DateTimeZone::listIdentifiers())){ - OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezone', $geoarray['timezone']['timezoneId']); - $message = array('message'=> $l->t('New Timezone:') . $geoarray['timezone']['timezoneId']); - OC_JSON::success($message); -}else{ - OC_JSON::error(); -} -?> +$l = new OC_L10N('calendar'); + +$lat = $_GET['lat']; +$lng = $_GET['long']; + +$timezone = OC_Geo::timezone($lat, $lng); + +if($timezone == OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone')){ + OC_JSON::success(); + exit; +} +OC_Preferences::setValue(OC_USER::getUser(), 'calendar', 'timezone', $timezone); +$message = array('message'=> $l->t('New Timezone:') . $timezone); +OC_JSON::success($message); +?> \ No newline at end of file diff --git a/apps/calendar/ajax/settings/setfirstday.php b/apps/calendar/ajax/settings/setfirstday.php new file mode 100644 index 0000000000..3b65221220 --- /dev/null +++ b/apps/calendar/ajax/settings/setfirstday.php @@ -0,0 +1,17 @@ + + * 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(); +if(isset($_POST["firstday"])){ + OC_Preferences::setValue(OC_User::getUser(), 'calendar', 'firstday', $_POST["firstday"]); + OC_JSON::success(); +}else{ + OC_JSON::error(); +} +?> + diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php index 9c95768895..f297c4d16d 100644 --- a/apps/calendar/appinfo/app.php +++ b/apps/calendar/appinfo/app.php @@ -1,23 +1,23 @@ =')){ - $l=new OC_L10N('calendar'); - OC::$CLASSPATH['OC_Calendar_App'] = 'apps/calendar/lib/app.php'; - OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php'; - OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php'; - OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php'; - OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php'; - OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser'); - OC_Util::addScript('calendar','loader'); - OC_App::register( array( - 'order' => 10, - 'id' => 'calendar', - 'name' => 'Calendar' )); - OC_App::addNavigationEntry( array( - 'id' => 'calendar_index', - 'order' => 10, - 'href' => OC_Helper::linkTo( 'calendar', 'index.php' ), - 'icon' => OC_Helper::imagePath( 'calendar', 'icon.svg' ), - 'name' => $l->t('Calendar'))); - OC_App::registerPersonal('calendar', 'settings'); - require_once('apps/calendar/lib/search.php'); -} +$l=new OC_L10N('calendar'); +OC::$CLASSPATH['OC_Calendar_App'] = 'apps/calendar/lib/app.php'; +OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php'; +OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php'; +OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php'; +OC::$CLASSPATH['OC_Search_Provider_Calendar'] = 'apps/calendar/lib/search.php'; +OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser'); +OC_Hook::connect('OC_DAV', 'initialize', 'OC_Calendar_Hooks', 'initializeCalDAV'); +OC_Util::addScript('calendar','loader'); +OC_App::register( array( + 'order' => 10, + 'id' => 'calendar', + 'name' => 'Calendar' )); +OC_App::addNavigationEntry( array( + 'id' => 'calendar_index', + 'order' => 10, + 'href' => OC_Helper::linkTo( 'calendar', 'index.php' ), + 'icon' => OC_Helper::imagePath( 'calendar', 'icon.svg' ), + 'name' => $l->t('Calendar'))); +OC_App::registerPersonal('calendar', 'settings'); +OC_Search::registerProvider('OC_Search_Provider_Calendar'); \ No newline at end of file diff --git a/apps/calendar/caldav.php b/apps/calendar/caldav.php index db0b35da11..b710b99ea4 100644 --- a/apps/calendar/caldav.php +++ b/apps/calendar/caldav.php @@ -25,7 +25,7 @@ $nodes = array( // Fire up server $server = new Sabre_DAV_Server($nodes); -$server->setBaseUri(OC::$WEBROOT.'/apps/calendar/caldav.php'); +$server->setBaseUri(OC::$APPSWEBROOT.'/apps/calendar/caldav.php'); // Add plugins $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud')); $server->addPlugin(new Sabre_CalDAV_Plugin()); diff --git a/apps/calendar/index.php b/apps/calendar/index.php index 12b51f564b..c00a4098f7 100644 --- a/apps/calendar/index.php +++ b/apps/calendar/index.php @@ -9,16 +9,20 @@ require_once ('../../lib/base.php'); OC_Util::checkLoggedIn(); OC_Util::checkAppEnabled('calendar'); + // Create default calendar ... $calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1); if( count($calendars) == 0){ OC_Calendar_Calendar::addCalendar(OC_User::getUser(),'Default calendar'); $calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1); } + $eventSources = array(); foreach($calendars as $calendar){ $eventSources[] = OC_Calendar_Calendar::getEventSourceInfo($calendar); } +OC_Hook::emit('OC_Calendar', 'getSources', array('sources' => &$eventSources)); + //Fix currentview for fullcalendar if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'currentview', 'month') == "oneweekview"){ OC_Preferences::setValue(OC_USER::getUser(), "calendar", "currentview", "agendaWeek"); diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js index 84b76a3c88..907d94edb0 100644 --- a/apps/calendar/js/calendar.js +++ b/apps/calendar/js/calendar.js @@ -73,6 +73,9 @@ Calendar={ } }, editEvent:function(calEvent, jsEvent, view){ + if (calEvent.editable == false || calEvent.source.editable == false) { + return; + } var id = calEvent.id; if($('#event').dialog('isOpen') == true){ // TODO: save event @@ -214,7 +217,7 @@ Calendar={ }, initScroll:function(){ if(window.addEventListener) - document.addEventListener('DOMMouseScroll', Calendar.UI.scrollCalendar); + document.addEventListener('DOMMouseScroll', Calendar.UI.scrollCalendar, false); //}else{ document.onmousewheel = Calendar.UI.scrollCalendar; //} @@ -661,7 +664,7 @@ $(document).ready(function(){ Calendar.UI.initScroll(); $('#calendar_holder').fullCalendar({ header: false, - firstDay: 1, + firstDay: firstDay, editable: true, defaultView: defaultView, timeFormat: { diff --git a/apps/calendar/js/geo.js b/apps/calendar/js/geo.js index c9cc5dd095..7018c6298a 100755 --- a/apps/calendar/js/geo.js +++ b/apps/calendar/js/geo.js @@ -10,7 +10,6 @@ if (navigator.geolocation) { function(data){ if (data.status == 'success' && typeof(data.message) != 'undefined'){ $('#notification').html(data.message); - $('#notification').attr('title', 'CC BY 3.0 by Geonames.org'); $('#notification').slideDown(); window.setTimeout(function(){$('#notification').slideUp();}, 5000); }else{ diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js index fcbfc423db..c768a47a79 100644 --- a/apps/calendar/js/settings.js +++ b/apps/calendar/js/settings.js @@ -17,6 +17,14 @@ $(document).ready(function(){ } }); }); + $('#firstday').change( function(){ + var data = $('#firstday').serialize(); + $.post( OC.filePath('calendar', 'ajax/settings', 'setfirstday.php'), data, function(data){ + if(data == 'error'){ + console.log('saving firstday failed'); + } + }); + }); $('#timezonedetection').change( function(){ var post = $('#timezonedetection').serialize(); $.post( OC.filePath('calendar', 'ajax/settings', 'timezonedetection.php'), post, function(data){ @@ -32,4 +40,8 @@ $(document).ready(function(){ $('#timezonedetection').attr('checked', 'checked'); } }); + $.getJSON(OC.filePath('calendar', 'ajax/settings', 'getfirstday.php'), function(jsondata, status) { + $('#' + jsondata.firstday).attr('selected',true); + $('#firstday').chosen(); + }); }); diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php index 277539af97..7eeb004d18 100644 --- a/apps/calendar/lib/calendar.php +++ b/apps/calendar/lib/calendar.php @@ -240,9 +240,10 @@ class OC_Calendar_Calendar{ '#9fc6e7', // "light blue" ); } + public static function getEventSourceInfo($calendar){ return array( - 'url' => 'ajax/events.php?calendar_id='.$calendar['id'], + 'url' => OC_Helper::linkTo('calendar', 'ajax/events.php').'?calendar_id='.$calendar['id'], 'backgroundColor' => $calendar['calendarcolor'], 'borderColor' => '#888', 'textColor' => 'black', diff --git a/apps/calendar/lib/hooks.php b/apps/calendar/lib/hooks.php index 14f96bb5fe..54f1680a36 100644 --- a/apps/calendar/lib/hooks.php +++ b/apps/calendar/lib/hooks.php @@ -17,11 +17,24 @@ class OC_Calendar_Hooks{ */ public static function deleteUser($parameters) { $calendars = OC_Calendar_Calendar::allCalendars($parameters['uid']); - + foreach($calendars as $calendar) { OC_Calendar_Calendar::deleteCalendar($calendar['id']); } return true; } + + /** + * @brief Adds the CardDAV resource to the DAV server + * @param paramters parameters from initialize-Hook + * @return array + */ + public static function initializeCalDAV($parameters){ + // We need a backend, the root node and the caldav plugin + $parameters['backends']['caldav'] = new OC_Connector_Sabre_CalDAV(); + $parameters['nodes'][] = new Sabre_CalDAV_CalendarRootNode($parameters['backends']['principal'], $parameters['backends']['caldav']); + $parameters['plugins'][] = new Sabre_CalDAV_Plugin(); + return true; + } } diff --git a/apps/calendar/lib/object.php b/apps/calendar/lib/object.php index 0b2cbb3815..a471aa8403 100644 --- a/apps/calendar/lib/object.php +++ b/apps/calendar/lib/object.php @@ -96,8 +96,7 @@ class OC_Calendar_Object{ list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object); if(is_null($uid)){ - $uid = self::createUID(); - $object->add('UID',$uid); + $object->setUID(); $data = $object->serialize(); } @@ -208,14 +207,6 @@ class OC_Calendar_Object{ return true; } - /** - * @brief Creates a UID - * @return string - */ - protected static function createUID(){ - return substr(md5(rand().time()),0,10); - } - /** * @brief Extracts data from a vObject-Object * @param Sabre_VObject $object @@ -309,6 +300,8 @@ class OC_Calendar_Object{ $dtend = $vevent->DTEND; }else{ $dtend = clone $vevent->DTSTART; + // clone creates a shallow copy, also clone DateTime + $dtend->setDateTime(clone $dtend->getDateTime(), $dtend->getDateType()); if ($vevent->DURATION){ $duration = strval($vevent->DURATION); $invert = 0; @@ -817,4 +810,4 @@ class OC_Calendar_Object{ return $vcalendar; } -} \ No newline at end of file +} diff --git a/apps/calendar/lib/search.php b/apps/calendar/lib/search.php index 79c8dba0f4..10fc3f8434 100644 --- a/apps/calendar/lib/search.php +++ b/apps/calendar/lib/search.php @@ -1,6 +1,6 @@ t('The event ends before it starts')) ?>'; var missing_field_dberror = 't('There was a database fail')) ?>'; var totalurl = '/calendars'; + var firstDay = ''; $(document).ready(function() { - " name="timeformat"> + + + + t('Calendar CalDAV syncing address:');?> diff --git a/apps/contacts/ajax/addcard.php b/apps/contacts/ajax/addcard.php deleted file mode 100644 index b1dc69a469..0000000000 --- a/apps/contacts/ajax/addcard.php +++ /dev/null @@ -1,103 +0,0 @@ -. - * - */ - -// Init owncloud -require_once('../../../lib/base.php'); -function bailOut($msg) { - OC_JSON::error(array('data' => array('message' => $msg))); - OC_Log::write('contacts','ajax/addcard.php: '.$msg, OC_Log::DEBUG); - exit(); -} - -// Check if we are a user -OC_JSON::checkLoggedIn(); -OC_JSON::checkAppEnabled('contacts'); - -$aid = $_POST['id']; -OC_Contacts_App::getAddressbook( $aid ); // is owner access check - -$fn = trim($_POST['fn']); -$values = $_POST['value']; -$parameters = $_POST['parameters']; - -$vcard = new OC_VObject('VCARD'); -$vcard->setUID(); - -$n = isset($values['N'][0])?trim($values['N'][0]).';':';'; -$n .= isset($values['N'][1])?trim($values['N'][1]).';':';'; -$n .= isset($values['N'][2])?trim($values['N'][2]).';;':';;'; - -if(!$fn || ($n == ';;;;')) { - bailOut('You have to enter both the extended name and the display name.'); -} - -$vcard->setString('N',$n); -$vcard->setString('FN',$fn); - -// Data to add ... -$add = array('TEL', 'EMAIL', 'ORG'); -$address = false; -for($i = 0; $i < 7; $i++){ - if( isset($values['ADR'][$i] ) && $values['ADR'][$i]) $address = true; -} -if( $address ) $add[] = 'ADR'; - -// Add data -foreach( $add as $propname){ - if( !( isset( $values[$propname] ) && $values[$propname] )){ - continue; - } - $value = $values[$propname]; - if( isset( $parameters[$propname] ) && count( $parameters[$propname] )){ - $prop_parameters = $parameters[$propname]; - } else { - $prop_parameters = array(); - } - if(is_array($value)){ - ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form! - $value = OC_VObject::escapeSemicolons($value); - } - $vcard->addProperty($propname, strip_tags($value)); //, $prop_parameters); - $line = count($vcard->children) - 1; - foreach ($prop_parameters as $key=>$element) { - if(is_array($element) && strtoupper($key) == 'TYPE') { - // FIXME: Maybe this doesn't only apply for TYPE? - // And it probably shouldn't be done here anyways :-/ - foreach($element as $e){ - if($e != '' && !is_null($e)){ - $vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key,$e); - } - } - } else { - $vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($key,$element); - } - } -} -$id = OC_Contacts_VCard::add($aid,$vcard->serialize()); -if(!$id) { - OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.')))); - OC_Log::write('contacts','ajax/addcard.php: Recieved non-positive ID on adding card: '.$id, OC_Log::ERROR); - exit(); -} - -// NOTE: Why is this in OC_Contacts_App? -OC_Contacts_App::renderDetails($id, $vcard); diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php index 5d17631caa..839a391998 100644 --- a/apps/contacts/ajax/addcontact.php +++ b/apps/contacts/ajax/addcontact.php @@ -52,7 +52,7 @@ $vcard->setUID(); $vcard->setString('FN',$fn); $vcard->setString('N',$n); -$id = OC_Contacts_VCard::add($aid,$vcard->serialize()); +$id = OC_Contacts_VCard::add($aid,$vcard); if(!$id) { OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.')))); OC_Log::write('contacts','ajax/addcontact.php: Recieved non-positive ID on adding card: '.$id, OC_Log::ERROR); diff --git a/apps/contacts/ajax/addproperty.php b/apps/contacts/ajax/addproperty.php index 028974e1c6..e1a3129283 100644 --- a/apps/contacts/ajax/addproperty.php +++ b/apps/contacts/ajax/addproperty.php @@ -27,14 +27,16 @@ require_once('../../../lib/base.php'); OC_JSON::checkLoggedIn(); OC_JSON::checkAppEnabled('contacts'); -$id = $_POST['id']; -$vcard = OC_Contacts_App::getContactVCard( $id ); +$id = isset($_POST['id'])?$_POST['id']:null; +$name = isset($_POST['name'])?$_POST['name']:null; +$value = isset($_POST['value'])?$_POST['value']:null; +$parameters = isset($_POST['parameters'])?$_POST['parameters']:array(); + +$vcard = OC_Contacts_App::getContactVCard($id); -$name = $_POST['name']; -$value = $_POST['value']; if(!is_array($value)){ $value = trim($value); - if(!$value && in_array($name, array('TEL', 'EMAIL', 'ORG', 'BDAY', 'NICKNAME'))) { + if(!$value && in_array($name, array('TEL', 'EMAIL', 'ORG', 'BDAY', 'NICKNAME', 'NOTE'))) { OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Cannot add empty property.')))); exit(); } @@ -51,7 +53,6 @@ if(!is_array($value)){ exit(); } } -$parameters = isset($_POST['parameters']) ? $_POST['parameters'] : array(); // Prevent setting a duplicate entry $current = $vcard->select($name); @@ -82,7 +83,9 @@ switch($name) { } case 'N': case 'ORG': + case 'NOTE': case 'NICKNAME': + // TODO: Escape commas and semicolons. break; case 'EMAIL': $value = strtolower($value); @@ -113,7 +116,7 @@ foreach ($parameters as $key=>$element) { } $checksum = md5($vcard->children[$line]->serialize()); -if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) { +if(!OC_Contacts_VCard::edit($id,$vcard)) { OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error adding contact property.')))); OC_Log::write('contacts','ajax/addproperty.php: Error updating contact property: '.$name, OC_Log::ERROR); exit(); diff --git a/apps/contacts/ajax/categories/categoriesfor.php b/apps/contacts/ajax/categories/categoriesfor.php new file mode 100644 index 0000000000..c02c37914a --- /dev/null +++ b/apps/contacts/ajax/categories/categoriesfor.php @@ -0,0 +1,28 @@ + + * 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_JSON::checkAppEnabled('contacts'); + +$id = isset($_GET['id'])?$_GET['id']:null; +if(is_null($id)) { + OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('No ID provided')))); + exit(); +} +$vcard = OC_Contacts_App::getContactVCard( $id ); +foreach($vcard->children as $property){ + //OC_Log::write('contacts','ajax/categories/checksumfor.php: '.$property->name, OC_Log::DEBUG); + if($property->name == 'CATEGORIES') { + $checksum = md5($property->serialize()); + OC_JSON::success(array('data' => array('value'=>$property->value, 'checksum'=>$checksum))); + exit(); + } +} +OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error setting checksum.')))); +?> diff --git a/apps/contacts/ajax/categories/delete.php b/apps/contacts/ajax/categories/delete.php new file mode 100644 index 0000000000..3ba5aa1606 --- /dev/null +++ b/apps/contacts/ajax/categories/delete.php @@ -0,0 +1,60 @@ + + * 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_JSON::checkAppEnabled('contacts'); + +foreach ($_POST as $key=>$element) { + debug('_POST: '.$key.'=>'.print_r($element, true)); +} + +function bailOut($msg) { + OC_JSON::error(array('data' => array('message' => $msg))); + OC_Log::write('contacts','ajax/categories/delete.php: '.$msg, OC_Log::DEBUG); + exit(); +} +function debug($msg) { + OC_Log::write('contacts','ajax/categories/delete.php: '.$msg, OC_Log::DEBUG); +} + +$categories = isset($_POST['categories'])?$_POST['categories']:null; + +if(is_null($categories)) { + bailOut(OC_Contacts_App::$l10n->t('No categories selected for deletion.')); +} + +debug(print_r($categories, true)); + +$addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser()); +if(count($addressbooks) == 0) { + bailOut(OC_Contacts_App::$l10n->t('No address books found.')); +} +$addressbookids = array(); +foreach($addressbooks as $addressbook) { + $addressbookids[] = $addressbook['id']; +} +$contacts = OC_Contacts_VCard::all($addressbookids); +if(count($contacts) == 0) { + bailOut(OC_Contacts_App::$l10n->t('No contacts found.')); +} + +$cards = array(); +foreach($contacts as $contact) { + $cards[] = array($contact['id'], $contact['carddata']); +} + +debug('Before delete: '.print_r($categories, true)); + +$catman = new OC_VCategories('contacts'); +$catman->delete($categories, $cards); +debug('After delete: '.print_r($catman->categories(), true)); +OC_Contacts_VCard::updateDataByID($cards); +OC_JSON::success(array('data' => array('categories'=>$catman->categories()))); + +?> diff --git a/apps/contacts/ajax/categories/list.php b/apps/contacts/ajax/categories/list.php new file mode 100644 index 0000000000..3b41b7bfa9 --- /dev/null +++ b/apps/contacts/ajax/categories/list.php @@ -0,0 +1,17 @@ + + * 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_JSON::checkAppEnabled('contacts'); + +$categories = OC_Contacts_App::$categories->categories(); + +OC_JSON::success(array('data' => array('categories'=>$categories))); + +?> diff --git a/apps/contacts/ajax/categories/rescan.php b/apps/contacts/ajax/categories/rescan.php new file mode 100644 index 0000000000..dd27192baa --- /dev/null +++ b/apps/contacts/ajax/categories/rescan.php @@ -0,0 +1,49 @@ + + * 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_JSON::checkAppEnabled('contacts'); + +foreach ($_POST as $key=>$element) { + debug('_POST: '.$key.'=>'.print_r($element, true)); +} + +function bailOut($msg) { + OC_JSON::error(array('data' => array('message' => $msg))); + OC_Log::write('contacts','ajax/categories/rescan.php: '.$msg, OC_Log::DEBUG); + exit(); +} +function debug($msg) { + OC_Log::write('contacts','ajax/categories/rescan.php: '.$msg, OC_Log::DEBUG); +} + +$addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser()); +if(count($addressbooks) == 0) { + bailOut(OC_Contacts_App::$l10n->t('No address books found.')); +} +$addressbookids = array(); +foreach($addressbooks as $addressbook) { + $addressbookids[] = $addressbook['id']; +} +$contacts = OC_Contacts_VCard::all($addressbookids); +if(count($contacts) == 0) { + bailOut(OC_Contacts_App::$l10n->t('No contacts found.')); +} + +$cards = array(); +foreach($contacts as $contact) { + $cards[] = $contact['carddata']; +} + +OC_Contacts_App::$categories->rescan($cards); +$categories = OC_Contacts_App::$categories->categories(); + +OC_JSON::success(array('data' => array('categories'=>$categories))); + +?> diff --git a/apps/contacts/ajax/contactdetails.php b/apps/contacts/ajax/contactdetails.php index f35fd595c5..03895c862a 100644 --- a/apps/contacts/ajax/contactdetails.php +++ b/apps/contacts/ajax/contactdetails.php @@ -71,5 +71,5 @@ if(isset($details['PHOTO'])) { $details['PHOTO'] = false; } $details['id'] = $id; - +OC_Contacts_App::setLastModifiedHeader($vcard); OC_JSON::success(array('data' => $details)); diff --git a/apps/contacts/ajax/deleteproperty.php b/apps/contacts/ajax/deleteproperty.php index a9afffaad4..ab0958cac5 100644 --- a/apps/contacts/ajax/deleteproperty.php +++ b/apps/contacts/ajax/deleteproperty.php @@ -39,7 +39,7 @@ if(is_null($line)){ unset($vcard->children[$line]); -if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) { +if(!OC_Contacts_VCard::edit($id,$vcard)) { OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error deleting contact property.')))); OC_Log::write('contacts','ajax/deleteproperty.php: Error deleting contact property', OC_Log::ERROR); exit(); diff --git a/apps/contacts/ajax/showsetproperty.php b/apps/contacts/ajax/loadcard.php similarity index 53% rename from apps/contacts/ajax/showsetproperty.php rename to apps/contacts/ajax/loadcard.php index 73bef65535..037fe2a6df 100644 --- a/apps/contacts/ajax/showsetproperty.php +++ b/apps/contacts/ajax/loadcard.php @@ -2,8 +2,8 @@ /** * ownCloud - Addressbook * - * @author Jakob Sack - * @copyright 2011 Jakob Sack mail@jakobsack.de + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -22,31 +22,38 @@ // Init owncloud require_once('../../../lib/base.php'); +function bailOut($msg) { + OC_JSON::error(array('data' => array('message' => $msg))); + OC_Log::write('contacts','ajax/loadcard.php: '.$msg, OC_Log::DEBUG); + exit(); +} +function debug($msg) { + OC_Log::write('contacts','ajax/loadcard.php: '.$msg, OC_Log::DEBUG); +} +// foreach ($_POST as $key=>$element) { +// debug('_POST: '.$key.'=>'.$element); +// } // Check if we are a user OC_JSON::checkLoggedIn(); OC_JSON::checkAppEnabled('contacts'); -$id = $_GET['id']; -$checksum = $_GET['checksum']; - -$vcard = OC_Contacts_App::getContactVCard( $id ); - -$line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum); -if(is_null($line)){ - OC_JSON::error(array('data' => array( 'message' => OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page.')))); - exit(); -} +$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); $adr_types = OC_Contacts_App::getTypesOfProperty('ADR'); $phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); -$tmpl = new OC_Template('contacts','part.setpropertyform'); -$tmpl->assign('id',$id); -$tmpl->assign('checksum',$checksum); -$tmpl->assign('property',OC_Contacts_VCard::structureProperty($vcard->children[$line])); +$tmpl = new OC_Template('contacts','part.contact'); +$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); +$tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize)); $tmpl->assign('adr_types',$adr_types); $tmpl->assign('phone_types',$phone_types); +$tmpl->assign('id',''); $page = $tmpl->fetchPage(); OC_JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/contacts/ajax/loadphoto.php b/apps/contacts/ajax/loadphoto.php index 358e046942..2c8bb7bf1e 100644 --- a/apps/contacts/ajax/loadphoto.php +++ b/apps/contacts/ajax/loadphoto.php @@ -18,8 +18,6 @@ * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * - * TODO: Translatable strings. - * Remember to delete tmp file at some point. */ // Init owncloud require_once('../../../lib/base.php'); @@ -33,7 +31,7 @@ OC_JSON::checkAppEnabled('contacts'); function bailOut($msg) { OC_JSON::error(array('data' => array('message' => $msg))); - OC_Log::write('contacts','ajax/savecrop.php: '.$msg, OC_Log::DEBUG); + OC_Log::write('contacts','ajax/loadphoto.php: '.$msg, OC_Log::DEBUG); exit(); } @@ -42,11 +40,20 @@ $image = null; $id = isset($_GET['id']) ? $_GET['id'] : ''; if($id == '') { - bailOut('Missing contact id.'); + bailOut(OC_Contacts_App::$l10n->t('Missing contact id.')); +} + +$checksum = ''; +$vcard = OC_Contacts_App::getContactVCard( $id ); +foreach($vcard->children as $property){ + if($property->name == 'PHOTO') { + $checksum = md5($property->serialize()); + break; + } } $tmpl = new OC_TEMPLATE("contacts", "part.contactphoto"); $tmpl->assign('id', $id); $page = $tmpl->fetchPage(); -OC_JSON::success(array('data' => array('page'=>$page))); +OC_JSON::success(array('data' => array('page'=>$page, 'checksum'=>$checksum))); ?> diff --git a/apps/contacts/ajax/messagebox.php b/apps/contacts/ajax/messagebox.php deleted file mode 100644 index 408e7a537a..0000000000 --- a/apps/contacts/ajax/messagebox.php +++ /dev/null @@ -1,15 +0,0 @@ - - * 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_JSON::checkAppEnabled('contacts'); - -$output = new OC_TEMPLATE("contacts", "part.messagebox"); -$output -> printpage(); -?> diff --git a/apps/contacts/ajax/savecrop.php b/apps/contacts/ajax/savecrop.php index 1a84f6fdfa..0df4e1998c 100644 --- a/apps/contacts/ajax/savecrop.php +++ b/apps/contacts/ajax/savecrop.php @@ -95,7 +95,7 @@ if(file_exists($tmp_path)) { OC_Log::write('contacts','savecrop.php: files: Adding PHOTO property.', OC_Log::DEBUG); $card->addProperty('PHOTO', $image->__toString(), array('ENCODING' => 'b', 'TYPE' => $image->mimeType())); } - if(!OC_Contacts_VCard::edit($id,$card->serialize())) { + if(!OC_Contacts_VCard::edit($id,$card)) { bailOut('Error saving contact.'); } unlink($tmpfname); @@ -104,6 +104,7 @@ if(file_exists($tmp_path)) { $tmpl->assign('tmp_path', $tmpfname); $tmpl->assign('mime', $image->mimeType()); $tmpl->assign('id', $id); + $tmpl->assign('refresh', true); $tmpl->assign('width', $image->width()); $tmpl->assign('height', $image->height()); $page = $tmpl->fetchPage(); diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php index 6f8366243f..95a7ac2019 100644 --- a/apps/contacts/ajax/saveproperty.php +++ b/apps/contacts/ajax/saveproperty.php @@ -36,7 +36,7 @@ function debug($msg) { OC_Log::write('contacts','ajax/saveproperty.php: '.$msg, OC_Log::DEBUG); } foreach ($_POST as $key=>$element) { - debug('_POST: '.$key.'=>'.$element); + debug('_POST: '.$key.'=>'.print_r($element, true)); } $id = isset($_POST['id'])?$_POST['id']:null; @@ -51,12 +51,8 @@ $checksum = isset($_POST['checksum'])?$_POST['checksum']:null; // } // } -if(is_array($value)){ - $value = array_map('strip_tags', $value); - ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form! - $value = OC_VObject::escapeSemicolons($value); -} else { - $value = trim(strip_tags($value)); +if(!$name) { + bailOut(OC_Contacts_App::$l10n->t('element name is not set.')); } if(!$id) { bailOut(OC_Contacts_App::$l10n->t('id is not set.')); @@ -64,14 +60,22 @@ if(!$id) { if(!$checksum) { bailOut(OC_Contacts_App::$l10n->t('checksum is not set.')); } -if(!$name) { - bailOut(OC_Contacts_App::$l10n->t('element name is not set.')); +if(is_array($value)){ + $value = array_map('strip_tags', $value); + ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form! + //if($name == 'CATEGORIES') { + // $value = OC_Contacts_VCard::escapeDelimiters($value, ','); + //} else { + $value = OC_Contacts_VCard::escapeDelimiters($value, ';'); + //} +} else { + $value = trim(strip_tags($value)); } $vcard = OC_Contacts_App::getContactVCard( $id ); $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum); if(is_null($line)) { - bailOut(OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page.'.$checksum.' "'.$line.'"')); + bailOut(OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page: ').$checksum); } $element = $vcard->children[$line]->name; @@ -91,7 +95,9 @@ switch($element) { } case 'N': case 'ORG': + case 'NOTE': case 'NICKNAME': + case 'CATEGORIES': debug('Setting string:'.$name.' '.$value); $vcard->setString($name, $value); break; @@ -122,13 +128,9 @@ switch($element) { $checksum = md5($vcard->children[$line]->serialize()); debug('New checksum: '.$checksum); -if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) { - OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error updating contact property.')))); - OC_Log::write('contacts','ajax/setproperty.php: Error updating contact property: '.$value, OC_Log::ERROR); +if(!OC_Contacts_VCard::edit($id,$vcard)) { + bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.')); exit(); } -//$adr_types = OC_Contacts_App::getTypesOfProperty('ADR'); -//$phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); - OC_JSON::success(array('data' => array( 'line' => $line, 'checksum' => $checksum, 'oldchecksum' => $_POST['checksum'] ))); diff --git a/apps/contacts/ajax/setproperty.php b/apps/contacts/ajax/setproperty.php deleted file mode 100644 index f9e2a8e864..0000000000 --- a/apps/contacts/ajax/setproperty.php +++ /dev/null @@ -1,106 +0,0 @@ -. - * - */ - -// Init owncloud -require_once('../../../lib/base.php'); - -// Check if we are a user -OC_JSON::checkLoggedIn(); -OC_JSON::checkAppEnabled('contacts'); - -$id = $_POST['id']; -$checksum = $_POST['checksum']; - -$vcard = OC_Contacts_App::getContactVCard( $id ); -$line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum); - -// Set the value -$value = $_POST['value']; -if(is_array($value)){ - ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form! - foreach(array_keys($value) as $key) { - OC_Log::write('contacts','ajax/setproperty.php: setting: '.$key.': '.$value[$key], OC_Log::DEBUG); - } - $value = OC_VObject::escapeSemicolons($value); -} -OC_Log::write('contacts','ajax/setproperty.php: setting: '.$vcard->children[$line]->name.': '.$value, OC_Log::DEBUG); -$vcard->children[$line]->setValue(strip_tags($value)); - -// Add parameters -$postparameters = isset($_POST['parameters'])?$_POST['parameters']:array(); -if ($vcard->children[$line]->name == 'TEL' && !array_key_exists('TYPE', $postparameters)){ - $postparameters['TYPE']=''; -} -for($i=0;$ichildren[$line]->parameters);$i++){ - $name = $vcard->children[$line]->parameters[$i]->name; - if(array_key_exists($name,$postparameters)){ - if($postparameters[$name] == '' || is_null($postparameters[$name])){ - unset($vcard->children[$line]->parameters[$i]); - } - else{ - unset($vcard->children[$line][$name]); - $values = $postparameters[$name]; - if (!is_array($values)){ - $values = array($values); - } - foreach($values as $value){ - $vcard->children[$line]->add($name, $value); - } - } - unset($postparameters[$name]); - } -} -$missingparameters = array_keys($postparameters); -foreach($missingparameters as $i){ - if(!$postparameters[$i] == '' && !is_null($postparameters[$i])){ - $vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($i,$postparameters[$i]); - } -} - -// Do checksum and be happy -// NOTE: This checksum is not used..? -$checksum = md5($vcard->children[$line]->serialize()); - -if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) { - OC_JSON::error(array('data' => array('message' => $l->t('Error updating contact property.')))); - OC_Log::write('contacts','ajax/setproperty.php: Error updating contact property: '.$value, OC_Log::ERROR); - exit(); -} - -$adr_types = OC_Contacts_App::getTypesOfProperty('ADR'); -$phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); - -if ($vcard->children[$line]->name == 'FN'){ - $tmpl = new OC_Template('contacts','part.property.FN'); -} -elseif ($vcard->children[$line]->name == 'N'){ - $tmpl = new OC_Template('contacts','part.property.N'); -} -else{ - $tmpl = new OC_Template('contacts','part.property'); -} -$tmpl->assign('adr_types',$adr_types); -$tmpl->assign('phone_types',$phone_types); -$tmpl->assign('property',OC_Contacts_VCard::structureProperty($vcard->children[$line],$line)); -$page = $tmpl->fetchPage(); - -OC_JSON::success(array('data' => array( 'page' => $page, 'line' => $line, 'checksum' => $checksum, 'oldchecksum' => $_POST['checksum'] ))); diff --git a/apps/contacts/ajax/showaddcard.php b/apps/contacts/ajax/showaddcard.php deleted file mode 100644 index 54592c89c0..0000000000 --- a/apps/contacts/ajax/showaddcard.php +++ /dev/null @@ -1,40 +0,0 @@ -. - * - */ - -// Init owncloud -require_once('../../../lib/base.php'); - -// Check if we are a user -OC_JSON::checkLoggedIn(); -OC_JSON::checkAppEnabled('contacts'); - -$adr_types = OC_Contacts_App::getTypesOfProperty('ADR'); -$phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); - -$addressbooks = OC_Contacts_Addressbook::all(OC_USER::getUser()); -$tmpl = new OC_Template('contacts','part.addcardform'); -$tmpl->assign('addressbooks',$addressbooks); -$tmpl->assign('adr_types',$adr_types); -$tmpl->assign('phone_types',$phone_types); -$page = $tmpl->fetchPage(); - -OC_JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/contacts/ajax/showaddproperty.php b/apps/contacts/ajax/showaddproperty.php deleted file mode 100644 index 30eb7634f8..0000000000 --- a/apps/contacts/ajax/showaddproperty.php +++ /dev/null @@ -1,37 +0,0 @@ -. - * - */ - -// Init owncloud -require_once('../../../lib/base.php'); - -// Check if we are a user -OC_JSON::checkLoggedIn(); -OC_JSON::checkAppEnabled('contacts'); - -$id = $_GET['id']; -$card = OC_Contacts_App::getContactObject( $id ); - -$tmpl = new OC_Template('contacts','part.addpropertyform'); -$tmpl->assign('id',$id); -$page = $tmpl->fetchPage(); - -OC_JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php index 9e424aa89f..85c383c4c3 100644 --- a/apps/contacts/appinfo/app.php +++ b/apps/contacts/appinfo/app.php @@ -4,7 +4,11 @@ OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php'; OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php'; OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php'; OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php'; +OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'apps/contacts/lib/search.php'; OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser'); +OC_HOOK::connect('OC_Calendar', 'getEvents', 'OC_Contacts_Hooks', 'getBirthdayEvents'); +OC_HOOK::connect('OC_Calendar', 'getSources', 'OC_Contacts_Hooks', 'getCalenderSources'); +OC_Hook::connect('OC_DAV', 'initialize', 'OC_Contacts_Hooks', 'initializeCardDAV'); OC_App::register( array( 'order' => 10, @@ -21,4 +25,4 @@ OC_App::addNavigationEntry( array( OC_APP::registerPersonal('contacts','settings'); OC_UTIL::addScript('contacts', 'loader'); -require_once('apps/contacts/lib/search.php'); +OC_Search::registerProvider('OC_Search_Provider_Contacts'); diff --git a/apps/contacts/carddav.php b/apps/contacts/carddav.php index a2bf492e20..654aeb66a7 100644 --- a/apps/contacts/carddav.php +++ b/apps/contacts/carddav.php @@ -39,7 +39,7 @@ $nodes = array( // Fire up server $server = new Sabre_DAV_Server($nodes); -$server->setBaseUri(OC::$WEBROOT.'/apps/contacts/carddav.php'); +$server->setBaseUri(OC::$APPSWEBROOT.'/apps/contacts/carddav.php'); // Add plugins $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud')); $server->addPlugin(new Sabre_CardDAV_Plugin()); diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index b24ec438f2..7c36a511d6 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -20,7 +20,8 @@ #firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; } #firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; } -#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 16em; } +#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; } +.categories { float: left; width: 16em; } #card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #f8f8f8; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; } #card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; } input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { background-color: #ffc0c0 !important; } @@ -68,7 +69,7 @@ dl.form /*background-color: yellow;*/ } -.loading { background: url('../../../core/img/loading.gif') no-repeat center !important;} +.loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; } /*.add { cursor: pointer; width: 25px; height: 25px; margin: 0px; float: right; position:relative; content: "\+"; font-weight: bold; color: #666; font-size: large; bottom: 0px; right: 0px; clear: both; text-align: center; vertical-align: bottom; display: none; }*/ @@ -185,4 +186,5 @@ 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; } \ No newline at end of file +.addresslist { clear: both; } + diff --git a/apps/contacts/css/styles.css b/apps/contacts/css/styles.css deleted file mode 100644 index 58e1bf6c93..0000000000 --- a/apps/contacts/css/styles.css +++ /dev/null @@ -1,37 +0,0 @@ -#contacts { padding-left:2px; padding-top: 5px; background: #fff; } -#leftcontent a { height: 23px; display: block; margin: 0 0 0 0; padding: 0 0 0 25px; } -#chooseaddressbook {margin-right: 170px; float: right;} -#contacts_details_name { font-weight:bold;font-size:1.1em;margin-left:25%;} -#contacts_details_name_n { font-size:0.8em;margin-left:25%;color:#666;} -#contacts_details_photo { margin:.5em 0em .5em 25%; } - -#contacts_deletecard {position:absolute;top:15px;right:25px;} -#contacts_downloadcard {position:absolute;top:15px;right:50px;} -#contacts_details_list { list-style:none; } -#contacts_details_list li { overflow:visible; } -#contacts_details_list li p.contacts_property_name { width:25%; float:left;text-align:right;padding-right:0.3em;color:#666; } -#contacts_details_list li p.contacts_property_data, #contacts_details_list li ul.contacts_property_data { width:72%;float:left; clear: right; } -#contacts_setproperty_button { margin-left:25%; } - -#contacts_addcardform legend,label { font-weight: bold; width: 10em; overflow: ellipsis; } -#contacts_addcardform legend { padding-left: 3em; font-size:1.1em; } -#contacts_addcardform input[type="text"] { width: 25em; } -#contacts_addcardform input[type="email"] { width: 15em; } -#contacts_addcardform input[type="tel"] { width: 15em; } - -dl.form { width: 100%; float: left; clear: right; margin: 1em; padding: 0; } -.form dt { display: table-cell; clear: left; float: left; min-width: 10em; margin: 0; padding-top: 0.5em; padding-right: 1em;font-weight: bold; text-align:right; vertical-align: text-bottom; bottom: 0px; } -.form dd { display: table-cell; clear: right; float: left; min-width: 20em; margin: 0; padding: 0; white-space: nowrap; top: 0px; } -.form input { position: relative; width: 20em; } - -.contacts_property_data ul, ol.contacts_property_data { list-style:none; } -.contacts_property_data li { overflow: hidden; } -.contacts_property_data li label { width:20%; float:left; text-align:right;padding-right:0.3em; } -.contacts_property_data input { float:left; } -.contacts_property_data li input { width:70%;overflow:hidden; } - -.chzn-container { margin:3px 0 0; } -.chzn-container .chzn-choices { border-radius: 0.5em; } -.chzn-container.chzn-container-active .chzn-choices { border-bottom-left-radius: 0;border-bottom-right-radius: 0; } -.chzn-container .chzn-drop { border-bottom-left-radius: 0.5em;border-bottom-right-radius: 0.5em; } - diff --git a/apps/contacts/import.php b/apps/contacts/import.php index 4638bf0d73..04cfc397d5 100644 --- a/apps/contacts/import.php +++ b/apps/contacts/import.php @@ -97,11 +97,15 @@ if(is_writable('import_tmp/')){ fclose($progressfopen); } if(count($parts) == 1){ - OC_Contacts_VCard::add($id, $file); -}else{ - foreach($importready as $import){ - OC_Contacts_VCard::add($id, $import); + $importready = array($file); +} +foreach($importready as $import){ + $card = OC_VObject::parse($import); + if (!$card) { + OC_Log::write('contacts','Import: skipping card. Error parsing VCard: '.$import, OC_Log::ERROR); + continue; // Ditch cards that can't be parsed by Sabre. } + OC_Contacts_VCard::add($id, $card); } //done the import if(is_writable('import_tmp/')){ @@ -113,4 +117,4 @@ sleep(3); if(is_writable('import_tmp/')){ unlink($progressfile); } -OC_JSON::success(); \ No newline at end of file +OC_JSON::success(); diff --git a/apps/contacts/index.php b/apps/contacts/index.php index 0a21ddd04b..04f6c65a14 100644 --- a/apps/contacts/index.php +++ b/apps/contacts/index.php @@ -34,6 +34,7 @@ if(!is_null($id)) { } $property_types = OC_Contacts_App::getAddPropertyOptions(); $phone_types = OC_Contacts_App::getTypesOfProperty('TEL'); +$categories = OC_Contacts_App::$categories->categories(); $upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize')); $post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size')); @@ -44,12 +45,14 @@ $freeSpace=max($freeSpace,0); $maxUploadFilesize = min($maxUploadFilesize ,$freeSpace); OC_Util::addScript('','jquery.multiselect'); +OC_Util::addScript('','oc-vcategories'); OC_Util::addScript('contacts','contacts'); OC_Util::addScript('contacts','jquery.combobox'); OC_Util::addScript('contacts','jquery.inview'); OC_Util::addScript('contacts','jquery.Jcrop'); +OC_Util::addScript('contacts','jquery.multi-autocomplete'); OC_Util::addStyle('','jquery.multiselect'); -//OC_Util::addStyle('contacts','styles'); +OC_Util::addStyle('','oc-vcategories'); OC_Util::addStyle('contacts','jquery.combobox'); OC_Util::addStyle('contacts','jquery.Jcrop'); OC_Util::addStyle('contacts','contacts'); @@ -58,7 +61,9 @@ $tmpl = new OC_Template( "contacts", "index", "user" ); $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize)); $tmpl->assign('property_types',$property_types); +$tmpl->assign('categories',OC_Contacts_App::getCategories()); $tmpl->assign('phone_types',$phone_types); +$tmpl->assign('categories',$categories); $tmpl->assign('addressbooks', $addressbooks); $tmpl->assign('contacts', $contacts); $tmpl->assign('details', $details ); diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index d033e3f21c..18214cb1cc 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -10,17 +10,16 @@ String.prototype.strip_tags = function(){ return stripped; }; - Contacts={ UI:{ notImplemented:function() { - Contacts.UI.messageBox(t('contacts', 'Not implemented'), t('contacts', 'Sorry, this functionality has not been implemented yet')); + OC.dialogs.alert(t('contacts', 'Sorry, this functionality has not been implemented yet'), t('contacts', 'Not implemented')); }, searchOSM:function(obj) { var adr = Contacts.UI.propertyContainerFor(obj).find('.adr').val(); console.log('adr 1: ' + adr); if(adr == undefined) { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', 'Couldn\'t get a valid address.')); + OC.dialogs.alert(t('contacts', 'Couldn\'t get a valid address.'), t('contacts', 'Error')); return; } // FIXME: I suck at regexp. /Tanghus @@ -48,12 +47,11 @@ Contacts={ console.log('uri: ' + uri); var newWindow = window.open(uri,'_blank'); newWindow.focus(); - //Contacts.UI.notImplemented(); }, mailTo:function(obj) { var adr = Contacts.UI.propertyContainerFor($(obj)).find('input[type="email"]').val().trim(); if(adr == '') { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', 'Please enter an email address.')); + OC.dialogs.alert(t('contacts', 'Please enter an email address.'), t('contacts', 'Error')); return; } window.location.href='mailto:' + adr; @@ -68,7 +66,7 @@ Contacts={ return $(obj).parents('.propertycontainer').first().data('element'); }, showHideContactInfo:function() { - var show = ($('#emaillist li[class*="propertycontainer"]').length > 0 || $('#phonelist li[class*="propertycontainer"]').length > 0 || $('#addressdisplay dl[class*="propertycontainer"]').length > 0); + var show = ($('#emaillist li.propertycontainer').length > 0 || $('#phonelist li.propertycontainer').length > 0 || $('#addressdisplay dl.propertycontainer').length > 0); console.log('showHideContactInfo: ' + show); if(show) { $('#contact_communication').show(); @@ -82,19 +80,19 @@ Contacts={ switch (type) { case 'EMAIL': console.log('emails: '+$('#emaillist>li').length); - if($('#emaillist li[class*="propertycontainer"]').length == 0) { + if($('#emaillist li.propertycontainer').length == 0) { $('#emails').hide(); } break; case 'TEL': console.log('phones: '+$('#phonelist>li').length); - if($('#phonelist li[class*="propertycontainer"]').length == 0) { + if($('#phonelist li.propertycontainer').length == 0) { $('#phones').hide(); } break; case 'ADR': console.log('addresses: '+$('#addressdisplay>dl').length); - if($('#addressdisplay dl[class*="propertycontainer"]').length == 0) { + if($('#addressdisplay dl.propertycontainer').length == 0) { $('#addresses').hide(); } break; @@ -116,34 +114,6 @@ Contacts={ $('#carddav_url').show(); $('#carddav_url_close').show(); }, - messageBox:function(title, msg) { - if(msg.toLowerCase().indexOf('auth') != -1) { - // fugly hack, I know - alert(msg); - } - if($('#messagebox').dialog('isOpen') == true){ - // NOTE: Do we ever get here? - $('#messagebox').dialog('moveToTop'); - }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'messagebox.php'), function(){ - $('#messagebox').dialog( - { - autoOpen: true, - title: title, - buttons: [{ - text: "Ok", - click: function() { $(this).dialog("close"); } - }], - close: function(event, ui) { - $(this).dialog('destroy').remove(); - }, - open: function(event, ui) { - $('#messagebox_msg').html(msg); - } - }); - }); - }; - }, loadListHandlers:function() { //$('.add,.delete').hide(); $('.globe,.mail,.delete,.edit').tipsy(); @@ -182,8 +152,14 @@ Contacts={ $('#bday').datepicker({ dateFormat : 'dd-mm-yy' }); + /*$('#categories_value').find('select').multiselect({ + noneSelectedText: t('contacts', 'Select categories'), + header: false, + selectedList: 6, + classes: 'categories' + });*/ // Style phone types - $('#phonelist').find('select[class*="contacts_property"]').multiselect({ + $('#phonelist').find('select.contacts_property').multiselect({ noneSelectedText: t('contacts', 'Select type'), header: false, selectedList: 4, @@ -223,6 +199,7 @@ Contacts={ click: function() { $(this).dialog('close'); } } ] ); + $('#categories').multiple_autocomplete({source: categories}); Contacts.UI.loadListHandlers(); }, Card:{ @@ -252,12 +229,12 @@ Contacts={ if(jsondata.status == 'success'){ Contacts.UI.Card.loadContact(jsondata.data); } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); } } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); } @@ -269,7 +246,7 @@ Contacts={ $('#rightcontent').data('id',''); $('#rightcontent').html(jsondata.data.page); } else { - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); } @@ -309,7 +286,7 @@ Contacts={ } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert(jsondata.data.message); } }); @@ -318,7 +295,7 @@ Contacts={ // TODO: Add to contacts list. } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert(jsondata.data.message); } }); @@ -344,13 +321,13 @@ Contacts={ $('#rightcontent').html(jsondata.data.page); } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); } } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert(jsondata.data.message); } }); @@ -361,19 +338,29 @@ Contacts={ this.data = jsondata; this.id = this.data.id; $('#rightcontent').data('id',this.id); - //console.log('loaded: ' + this.data.FN[0]['value']); + console.log('loaded: ' + this.data.FN[0]['value']); this.populateNameFields(); - this.loadCategories(); + //this.loadCategories(); this.loadPhoto(); this.loadMails(); this.loadPhones(); this.loadAddresses(); this.loadSingleProperties(); + // TODO: load NOTE ;-) + if(this.data.NOTE) { + $('#note').data('checksum', this.data.NOTE[0]['checksum']); + $('#note').find('textarea').val(this.data.NOTE[0]['value']); + $('#note').show(); + } else { + $('#note').data('checksum', ''); + $('#note').find('textarea').val(''); + $('#note').hide(); + } }, loadSingleProperties:function() { - var props = ['BDAY', 'NICKNAME', 'ORG']; + var props = ['BDAY', 'NICKNAME', 'ORG', 'CATEGORIES']; // Clear all elements - $('#ident .propertycontainer[class*="propertycontainer"]').each(function(){ + $('#ident .propertycontainer').each(function(){ if(props.indexOf($(this).data('element')) > -1) { $(this).data('checksum', ''); $(this).find('input').val(''); @@ -407,6 +394,12 @@ Contacts={ $('#contact_identity').find('#org_label').show(); $('#contact_identity').find('#org_value').show(); break; + case 'CATEGORIES': + $('#contact_identity').find('#categories').val(value); + $('#contact_identity').find('#categories_value').data('checksum', checksum); + $('#contact_identity').find('#categories_label').show(); + $('#contact_identity').find('#categories_value').show(); + break; } } else { $('#contacts_propertymenu a[data-type="'+props[prop]+'"]').parent().show(); @@ -424,11 +417,11 @@ Contacts={ } else { narray = this.data.N[0]['value']; } - this.famname = narray[0]; - this.givname = narray[1]; - this.addname = narray[2]; - this.honpre = narray[3]; - this.honsuf = narray[4]; + this.famname = narray[0] || ''; + this.givname = narray[1] || ''; + this.addname = narray[2] || ''; + this.honpre = narray[3] || ''; + this.honsuf = narray[4] || ''; if(this.honpre.length > 0) { this.fullname += this.honpre + ' '; } @@ -448,6 +441,9 @@ Contacts={ $('#fn_select option').remove(); $('#fn_select').combobox('value', this.fn); var names = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; + if(this.data.ORG) { + names[names.length]=this.data.ORG[0].value; + } $.each(names, function(key, value) { $('#fn_select') .append($('') @@ -457,36 +453,79 @@ Contacts={ $('#contact_identity').find('*[data-element="FN"]').data('checksum', this.data.FN[0]['checksum']); $('#contact_identity').show(); }, - loadCategories:function(){ + hasCategory:function(category) { if(this.data.CATEGORIES) { - // + var categories = this.data.CATEGORIES[0]['value'].split(/,\s*/); + for(var c in categories) { + var cat = this.data.CATEGORIES[0]['value'][c]; + console.log('hasCategory: ' + cat + ' === ' + category + '?'); + if(typeof cat === 'string' && (cat.toUpperCase() === category.toUpperCase())) { + console.log('Yes'); + return true; + } + } } + return false; }, + categoriesChanged:function(newcategories) { // Categories added/deleted. + console.log('categoriesChanged for ' + Contacts.UI.Card.id + ' : ' + newcategories); + categories = newcategories; + var categorylist = $('#categories_value').find('input'); + $.getJSON(OC.filePath('contacts', 'ajax', 'categories/categoriesfor.php'),{'id':Contacts.UI.Card.id},function(jsondata){ + if(jsondata.status == 'success'){ + console.log('Setting checksum: ' + jsondata.data.checksum + ', value: ' + jsondata.data.value); + $('#categories_value').data('checksum', jsondata.data.checksum); + categorylist.val(jsondata.data.value); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + }, + /*loadCategories:function(){ // On loading contact. + var categories = $('#categories_value').find('select'); + if(this.data.CATEGORIES) { + $('#categories_value').data('checksum', this.data.CATEGORIES[0]['checksum']); + } else { + $('#categories_value').data('checksum', ''); + } + categories.find('option').each(function(){ + if(Contacts.UI.Card.hasCategory($(this).val())) { + $(this).attr('selected', 'selected'); + } else { + $(this).removeAttr('selected'); + } + }); + categories.multiselect('refresh'); + },*/ editNew:function(){ // add a new contact this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; - $.getJSON('ajax/newcontact.php',{},function(jsondata){ + $.getJSON(OC.filePath('contacts', 'ajax', 'newcontact.php'),{},function(jsondata){ if(jsondata.status == 'success'){ id = ''; $('#rightcontent').data('id',''); $('#rightcontent').html(jsondata.data.page); Contacts.UI.Card.editName(); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert(jsondata.data.message); } }); }, savePropertyInternal:function(name, fields, oldchecksum, checksum){ // TODO: Add functionality for new fields. - //console.log('savePropertyInternal: ' + name + ', checksum: ' + checksum); - //console.log('savePropertyInternal: ' + this.data[name]); + console.log('savePropertyInternal: ' + name + ', fields: ' + fields + 'checksum: ' + checksum); + console.log('savePropertyInternal: ' + this.data[name]); + var multivalue = ['CATEGORIES']; var params = {}; - var value = undefined; + var value = multivalue.indexOf(name) != -1 ? new Array() : undefined; jQuery.each(fields, function(i, field){ //.substring(11,'parameters[TYPE][]'.indexOf(']')) if(field.name.substring(0, 5) === 'value') { - value = field.value; + if(multivalue.indexOf(name) != -1) { + value.push(field.value); + } else { + value = field.value; + } } else if(field.name.substring(0, 10) === 'parameters') { var p = field.name.substring(11,'parameters[TYPE][]'.indexOf(']')); if(!(p in params)) { @@ -506,11 +545,11 @@ Contacts={ saveProperty:function(obj){ // I couldn't get the selector to filter on 'contacts_property' so I filter by hand here :-/ if(!$(obj).hasClass('contacts_property')) { - //console.log('Filtering out object.' + obj); + console.log('Filtering out object.' + obj); return false; } if($(obj).hasClass('nonempty') && $(obj).val().trim() == '') { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', 'This property has to be non-empty.')); + OC.dialogs.alert(t('contacts', 'This property has to be non-empty.'), t('contacts', 'Error')); return false; } container = $(obj).parents('.propertycontainer').first(); // get the parent holding the metadata. @@ -518,8 +557,8 @@ Contacts={ var checksum = container.data('checksum'); var name = container.data('element'); console.log('saveProperty: ' + name); - var fields = container.find('input[class*="contacts_property"],select[class*="contacts_property"]').serializeArray(); - var q = container.find('input[class*="contacts_property"],select[class*="contacts_property"]').serialize(); + var fields = container.find('input.contacts_property,select.contacts_property').serializeArray(); + var q = container.find('input.contacts_property,select.contacts_property,textarea.contacts_property').serialize(); if(q == '' || q == undefined) { console.log('Couldn\'t serialize elements.'); Contacts.UI.loading(container, false); @@ -529,32 +568,38 @@ Contacts={ if(checksum != undefined && checksum != '') { // save q = q + '&checksum=' + checksum; console.log('Saving: ' + q); + $(obj).attr('disabled', 'disabled'); $.post('ajax/saveproperty.php',q,function(jsondata){ if(jsondata.status == 'success'){ container.data('checksum', jsondata.data.checksum); Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum); Contacts.UI.loading(container, false); + $(obj).removeAttr('disabled'); return true; } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); Contacts.UI.loading(container, false); + $(obj).removeAttr('disabled'); return false; } },'json'); } else { // add console.log('Adding: ' + q); + $(obj).attr('disabled', 'disabled'); $.post('ajax/addproperty.php',q,function(jsondata){ if(jsondata.status == 'success'){ container.data('checksum', jsondata.data.checksum); // TODO: savePropertyInternal doesn't know about new fields //Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum); Contacts.UI.loading(container, false); + $(obj).removeAttr('disabled'); return true; } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); Contacts.UI.loading(container, false); + $(obj).removeAttr('disabled'); return false; } },'json'); @@ -565,10 +610,14 @@ Contacts={ console.log('addProperty:' + type); switch (type) { case 'PHOTO': - this.loadPhoto(); + this.loadPhoto(true); $('#file_upload_form').show(); $('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide(); break; + case 'NOTE': + $('#note').show(); + $('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide(); + break; case 'EMAIL': if($('#emaillist>li').length == 1) { $('#emails').show(); @@ -593,6 +642,7 @@ Contacts={ case 'NICKNAME': case 'ORG': case 'BDAY': + case 'CATEGORIES': $('dl dt[data-element="'+type+'"],dd[data-element="'+type+'"]').show(); $('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide(); break; @@ -612,17 +662,25 @@ Contacts={ } else if(type == 'single') { var proptype = Contacts.UI.propertyTypeFor(obj); console.log('deleteProperty, hiding: ' + proptype); - $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); + var othertypes = ['NOTE', 'PHOTO']; + if(othertypes.indexOf(proptype) != -1) { + console.log('NOTE or PHOTO'); + Contacts.UI.propertyContainerFor(obj).hide(); + Contacts.UI.propertyContainerFor(obj).data('checksum', ''); + } else { + $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); + $('dl dd[data-element="'+proptype+'"]').data('checksum', ''); + } $('#contacts_propertymenu a[data-type="'+proptype+'"]').parent().show(); Contacts.UI.loading(obj, false); } else { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org')); + OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error')); Contacts.UI.loading(obj, false); } } else{ Contacts.UI.loading(obj, false); - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); } else { // Property hasn't been saved so there's nothing to delete. @@ -637,7 +695,7 @@ Contacts={ $('#contacts_propertymenu a[data-type="'+proptype+'"]').parent().show(); Contacts.UI.loading(obj, false); } else { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org')); + OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error')); } } }, @@ -647,8 +705,9 @@ Contacts={ if($('#edit_name_dialog').dialog('isOpen') == true){ $('#edit_name_dialog').dialog('moveToTop'); }else{ // TODO: If id=='' call addcontact.php (or whatever name) instead and reload view with id. - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editname.php')+'?id='+this.id, function(){ - $('#edit_name_dialog' ).dialog({ + $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editname.php')+'?id='+this.id, function(jsondata){ + if(jsondata.status != 'error'){ + $('#edit_name_dialog' ).dialog({ modal: (isnew && true || false), closeOnEscape: (isnew == '' && false || true), title: (isnew && t('contacts', 'Add contact') || t('contacts', 'Edit name')), @@ -667,7 +726,10 @@ Contacts={ open : function(event, ui) { // load 'N' property - maybe :-P }*/ - }); + }); + } else { + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } }); } }, @@ -692,7 +754,14 @@ Contacts={ $('#fn_select option').remove(); //$('#fn_select').combobox('value', this.fn); - var names = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; + var tmp = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; + var names = new Array(); + for(var name in tmp) { + console.log('idx: ' + names.indexOf(tmp[name])); + if(names.indexOf(tmp[name]) == -1) { + names.push(tmp[name]); + } + } $.each(names, function(key, value) { $('#fn_select') .append($('') @@ -708,7 +777,7 @@ Contacts={ }, loadAddresses:function(){ $('#addresses').hide(); - $('#addressdisplay dl[class*="propertycontainer"]').remove(); + $('#addressdisplay dl.propertycontainer').remove(); for(var adr in this.data.ADR) { $('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show(); $('#addressdisplay dl').last().removeClass('template').addClass('propertycontainer'); @@ -771,8 +840,9 @@ Contacts={ if($('#edit_address_dialog').dialog('isOpen') == true){ $('#edit_address_dialog').dialog('moveToTop'); }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editaddress.php')+q, function(){ - $('#edit_address_dialog' ).dialog({ + $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editaddress.php')+q, function(jsondata){ + if(jsondata.status != 'error'){ + $('#edit_address_dialog' ).dialog({ /*modal: true,*/ height: 'auto', width: 'auto', buttons: { @@ -803,7 +873,10 @@ Contacts={ open : function(event, ui) { // load 'ADR' property - maybe :-P }*/ - }); + }); + } else { + alert(jsondata.data.message); + } }); } }, @@ -843,7 +916,7 @@ Contacts={ }, uploadPhoto:function(filelist) { if(!filelist) { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts','No files selected for upload.')); + OC.dialogs.alert(t('contacts','No files selected for upload.'), t('contacts', 'Error')); return; } //var file = filelist.item(0); @@ -852,7 +925,7 @@ Contacts={ var form = $('#file_upload_form'); var totalSize=0; if(file.size > $('#max_upload').val()){ - Contacts.UI.messageBox(t('Upload too large'), t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.')); + OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts', 'Error')); return; } else { target.load(function(){ @@ -861,21 +934,22 @@ Contacts={ Contacts.UI.Card.editPhoto(response.data.id, response.data.tmp); //alert('File: ' + file.tmp + ' ' + file.name + ' ' + file.mime); }else{ - Contacts.UI.messageBox(t('contacts', 'Error'), response.data.message); + OC.dialogs.alert(response.data.message, t('contacts', 'Error')); } }); form.submit(); } }, - loadPhoto:function(){ - if(this.data.PHOTO) { + loadPhoto:function(force){ + if(this.data.PHOTO||force==true) { $.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){ if(jsondata.status == 'success'){ //alert(jsondata.data.page); + $('#file_upload_form').data('checksum', jsondata.data.checksum); $('#contacts_details_photo_wrapper').html(jsondata.data.page); } else{ - Contacts.UI.messageBox(jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); $('#file_upload_form').show(); @@ -894,7 +968,7 @@ Contacts={ $('#edit_photo_dialog_img').html(jsondata.data.page); } else{ - Contacts.UI.messageBox(jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); if($('#edit_photo_dialog').dialog('isOpen') == true){ @@ -913,22 +987,22 @@ Contacts={ // load cropped photo. $('#contacts_details_photo_wrapper').html(response.data.page); }else{ - Contacts.UI.messageBox(t('contacts','Error'), response.data.message); + OC.dialogs.alert(response.data.message, t('contacts', 'Error')); } }); $('#contacts [data-id="'+this.id+'"]').find('a').css('background','url(thumbnail.php?id='+this.id+'&refresh=1'+Math.random()+') no-repeat'); }, addMail:function() { //alert('addMail'); - $('#emaillist li[class*="template"]:first-child').clone().appendTo($('#emaillist')).show(); - $('#emaillist li[class*="template"]:last-child').removeClass('template').addClass('propertycontainer'); + $('#emaillist li.template:first-child').clone().appendTo($('#emaillist')).show(); + $('#emaillist li.template:last-child').removeClass('template').addClass('propertycontainer'); $('#emaillist li:last-child').find('input[type="email"]').focus(); Contacts.UI.loadListHandlers(); return false; }, loadMails:function() { $('#emails').hide(); - $('#emaillist li[class*="propertycontainer"]').remove(); + $('#emaillist li.propertycontainer').remove(); for(var mail in this.data.EMAIL) { this.addMail(); //$('#emaillist li:first-child').clone().appendTo($('#emaillist')).show(); @@ -950,9 +1024,9 @@ Contacts={ return false; }, addPhone:function() { - $('#phonelist li[class*="template"]:first-child').clone().appendTo($('#phonelist')); //.show(); - $('#phonelist li[class*="template"]:last-child').find('select').addClass('contacts_property'); - $('#phonelist li[class*="template"]:last-child').removeClass('template').addClass('propertycontainer'); + $('#phonelist li.template:first-child').clone().appendTo($('#phonelist')); //.show(); + $('#phonelist li.template:last-child').find('select').addClass('contacts_property'); + $('#phonelist li.template:last-child').removeClass('template').addClass('propertycontainer'); $('#phonelist li:last-child').find('input[type="text"]').focus(); Contacts.UI.loadListHandlers(); $('#phonelist li:last-child').find('select').multiselect({ @@ -966,7 +1040,7 @@ Contacts={ }, loadPhones:function() { $('#phones').hide(); - $('#phonelist li[class*="propertycontainer"]').remove(); + $('#phonelist li.propertycontainer').remove(); for(var phone in this.data.TEL) { this.addPhone(); $('#phonelist li:last-child').find('select').multiselect('destroy'); @@ -1006,13 +1080,17 @@ Contacts={ if($('#chooseaddressbook_dialog').dialog('isOpen') == true){ $('#chooseaddressbook_dialog').dialog('moveToTop'); }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(){ - $('#chooseaddressbook_dialog').dialog({ - width : 600, - close : function(event, ui) { - $(this).dialog('destroy').remove(); - } - }); + $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(jsondata){ + if(jsondata.status != 'error'){ + $('#chooseaddressbook_dialog').dialog({ + width : 600, + close : function(event, ui) { + $(this).dialog('destroy').remove(); + } + }); + } else { + alert(jsondata.data.message); + } }); } }, @@ -1049,7 +1127,7 @@ Contacts={ Contacts.UI.Contacts.update(); Contacts.UI.Addressbooks.overview(); } else { - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert('Error: ' + data.message); } }); @@ -1064,7 +1142,7 @@ Contacts={ var description = $("#description_"+bookid).val(); if(displayname.length == 0) { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', 'Displayname cannot be empty.')); + OC.dialogs.alert(t('contacts', 'Displayname cannot be empty.'), t('contacts', 'Error')); return false; } var url; @@ -1079,7 +1157,7 @@ Contacts={ $(button).closest('tr').prev().html(jsondata.page).show().next().remove(); Contacts.UI.Contacts.update(); } else { - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); }, @@ -1099,7 +1177,7 @@ Contacts={ Contacts.UI.Card.update(); } else{ - Contacts.UI.messageBox(t('contacts', 'Error'),jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert(jsondata.data.message); } }); @@ -1121,6 +1199,8 @@ Contacts={ $(document).ready(function(){ Contacts.UI.loadHandlers(); + OCCategories.changed = Contacts.UI.Card.categoriesChanged; + OCCategories.app = 'contacts'; /** * Show the Addressbook chooser @@ -1148,7 +1228,7 @@ $(document).ready(function(){ Contacts.UI.Card.loadContact(jsondata.data); } else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); //alert(jsondata.data.message); } }); @@ -1189,7 +1269,8 @@ $(document).ready(function(){ // NOTE: For some reason the selector doesn't work when I select by '.contacts_property' too... // I do the filtering in the event handler instead. - $('input[type="text"],input[type="checkbox"],input[type="email"],input[type="tel"],input[type="date"], select').live('change', function(){ + //$('input[type="text"],input[type="checkbox"],input[type="email"],input[type="tel"],input[type="date"], select').live('change', function(){ + $('.contacts_property').live('change', function(){ Contacts.UI.Card.saveProperty(this); }); @@ -1246,17 +1327,17 @@ $(document).ready(function(){ var file = files[0]; console.log('size: '+file.size); if(file.size > $('#max_upload').val()){ - Contacts.UI.messageBox(t('contacts','Upload too large'), t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.')); + 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')); return; } if (file.type.indexOf("image") != 0) { - Contacts.UI.messageBox(t('contacts','Wrong file type'), t('contacts','Only image files can be used as profile picture.')); + OC.dialogs.alert(t('contacts','Only image files can be used as profile picture.'), t('contacts','Wrong file type')); return; } var xhr = new XMLHttpRequest(); if (!xhr.upload) { - Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', 'Your browser doesn\'t support AJAX upload. Please click on the profile picture to select a photo to upload.')) + OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please click on the profile picture to select a photo to upload.'), t('contacts', 'Error')) } fileUpload = xhr.upload, xhr.onreadystatechange = function() { @@ -1266,11 +1347,11 @@ $(document).ready(function(){ if(xhr.status == 200) { Contacts.UI.Card.editPhoto(response.data.id, response.data.tmp); } else { - Contacts.UI.messageBox(t('contacts', 'Error'), xhr.status + ': ' + xhr.responseText); + OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error')); } } else { //alert(xhr.responseText); - Contacts.UI.messageBox(t('contacts', 'Error'), response.data.message); + OC.dialogs.alert(response.data.message, t('contacts', 'Error')); } // stop loading indicator //$('#contacts_details_photo_progress').hide(); @@ -1298,8 +1379,19 @@ $(document).ready(function(){ xhr.send(file); } + $('body').live('click',function(e){ + if(!$(e.target).is('#contacts_propertymenu_button')) { + $('#contacts_propertymenu').hide(); + } + }); $('#contacts_propertymenu_button').live('click',function(){ - $('#contacts_propertymenu').is(':hidden') && $('#contacts_propertymenu').slideDown() || $('#contacts_propertymenu').slideUp(); + var menu = $('#contacts_propertymenu'); + if(menu.is(':hidden')) { + menu.show(); + menu.find('ul').focus(); + } else { + menu.hide(); + } }); $('#contacts_propertymenu a').live('click',function(){ Contacts.UI.Card.addProperty(this); diff --git a/apps/contacts/js/interface.js b/apps/contacts/js/interface.js deleted file mode 100644 index 5908dd767a..0000000000 --- a/apps/contacts/js/interface.js +++ /dev/null @@ -1,409 +0,0 @@ -/** - * ownCloud - Addressbook - * - * @author Jakob Sack - * @copyright 2011 Jakob Sack mail@jakobsack.de - * @copyright 2011-2012 Thomas Tanghus - * - * 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 . - * - */ - - -Contacts={ - UI:{ - showCardDAVUrl:function(username, bookname){ - $('#carddav_url').val(totalurl + '/' + username + '/' + bookname); - $('#carddav_url').show(); - $('#carddav_url_close').show(); - }, - messageBox:function(title, msg) { - if($('#messagebox').dialog('isOpen') == true){ - // NOTE: Do we ever get here? - $('#messagebox').dialog('moveToTop'); - }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'messagebox.php'), function(){ - $('#messagebox').dialog( - { - autoOpen: true, - title: title, - buttons: [{ - text: "Ok", - click: function() { $(this).dialog("close"); } - }], - close: function(event, ui) { - $(this).dialog('destroy').remove(); - }, - open: function(event, ui) { - $('#messagebox_msg').html(msg); - } - }); - }); - } - }, - Addressbooks:{ - overview:function(){ - if($('#chooseaddressbook_dialog').dialog('isOpen') == true){ - $('#chooseaddressbook_dialog').dialog('moveToTop'); - }else{ - $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(){ - $('#chooseaddressbook_dialog').dialog({ - width : 600, - close : function(event, ui) { - $(this).dialog('destroy').remove(); - } - }); - }); - } - }, - activation:function(checkbox, bookid) - { - $.post(OC.filePath('contacts', 'ajax', 'activation.php'), { bookid: bookid, active: checkbox.checked?1:0 }, - function(data) { - /* - * Arguments: - * data.status - * data.bookid - * data.active - */ - if (data.status == 'success'){ - checkbox.checked = data.active == 1; - Contacts.UI.Contacts.update(); - } - }); - }, - newAddressbook:function(object){ - 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){ - var check = confirm("Do you really want to delete this address book?"); - if(check == false){ - return false; - }else{ - $.post(OC.filePath('contacts', 'ajax', 'deletebook.php'), { id: bookid}, - function(data) { - if (data.status == 'success'){ - $('#chooseaddressbook_dialog').dialog('destroy').remove(); - Contacts.UI.Contacts.update(); - Contacts.UI.Addressbooks.overview(); - } else { - Contacts.UI.messageBox(t('contacts', 'Error'), data.message); - //alert('Error: ' + data.message); - } - }); - } - }, - submit:function(button, bookid){ - var displayname = $("#displayname_"+bookid).val(); - var active = $("#edit_active_"+bookid+":checked").length; - var description = $("#description_"+bookid).val(); - - var url; - if (bookid == 'new'){ - url = OC.filePath('contacts', 'ajax', 'createaddressbook.php'); - }else{ - url = OC.filePath('contacts', 'ajax', 'updateaddressbook.php'); - } - $.post(url, { id: bookid, name: displayname, active: active, description: description }, - function(jsondata){ - if(jsondata.status == 'success'){ - $(button).closest('tr').prev().html(data.page).show().next().remove(); - Contacts.UI.Contacts.update(); - } else { - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - } - }); - }, - cancel:function(button, bookid){ - $(button).closest('tr').prev().show().next().remove(); - } - }, - Contacts:{ - /** - * Reload the contacts list. - */ - update:function(){ - $.getJSON('ajax/contacts.php',{},function(jsondata){ - if(jsondata.status == 'success'){ - $('#contacts').html(jsondata.data.page); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'),jsondata.data.message); - //alert(jsondata.data.message); - } - }); - setTimeout(Contacts.UI.Contacts.lazyupdate, 500); - }, - /** - * Add thumbnails to the contact list as they become visible in the viewport. - */ - lazyupdate:function(){ - $('#contacts li').live('inview', function(){ - if (!$(this).find('a').attr('style')) { - $(this).find('a').css('background','url(thumbnail.php?id='+$(this).data('id')+') no-repeat'); - } - }); - } - } - } -} - -$(document).ready(function(){ - /*------------------------------------------------------------------------- - * Event handlers - *-----------------------------------------------------------------------*/ - - /** - * Load the details view for a contact. - */ - $('#leftcontent li').live('click',function(){ - var id = $(this).data('id'); - var oldid = $('#rightcontent').data('id'); - if(oldid != 0){ - $('#leftcontent li[data-id="'+oldid+'"]').removeClass('active'); - } - $.getJSON('ajax/getdetails.php',{'id':id},function(jsondata){ - if(jsondata.status == 'success'){ - $('#rightcontent').data('id',jsondata.data.id); - $('#rightcontent').html(jsondata.data.page); - $('#leftcontent li[data-id="'+jsondata.data.id+'"]').addClass('active'); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - //alert(jsondata.data.message); - } - }); - return false; - }); - - /** - * Delete currently selected contact (and clear form?) - */ - $('#contacts_deletecard').live('click',function(){ - $('#contacts_deletecard').tipsy('hide'); - var id = $('#rightcontent').data('id'); - $.getJSON('ajax/deletecard.php',{'id':id},function(jsondata){ - if(jsondata.status == 'success'){ - $('#leftcontent [data-id="'+jsondata.data.id+'"]').remove(); - $('#rightcontent').data('id',''); - $('#rightcontent').empty(); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - //alert(jsondata.data.message); - } - }); - return false; - }); - - /** - * Add a property to the contact. - * NOTE: Where does 'contacts_addproperty' exist? - */ - $('#contacts_addproperty').live('click',function(){ - var id = $('#rightcontent').data('id'); - $.getJSON('ajax/showaddproperty.php',{'id':id},function(jsondata){ - if(jsondata.status == 'success'){ - $('#contacts_details_list').append(jsondata.data.page); - $('#contacts_addproperty').hide(); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - alert('From handler: '+jsondata.data.message); - } - }); - return false; - }); - - /** - * Change the inputs based on which type of property is selected for addition. - */ - $('#contacts_addpropertyform [name="name"]').live('change',function(){ - $('#contacts_addpropertyform #contacts_addresspart').remove(); - $('#contacts_addpropertyform #contacts_phonepart').remove(); - $('#contacts_addpropertyform #contacts_fieldpart').remove(); - $('#contacts_addpropertyform #contacts_generic').remove(); - if($(this).val() == 'ADR'){ - $('#contacts_addresspart').clone().insertAfter($('#contacts_addpropertyform .contacts_property_name')); - } - else if($(this).val() == 'TEL'){ - $('#contacts_phonepart').clone().insertAfter($('#contacts_addpropertyform .contacts_property_name')); - } - else{ - $('#contacts_generic').clone().insertAfter($('#contacts_addpropertyform .contacts_property_name')); - } - $('#contacts_addpropertyform .contacts_property_data select').chosen(); - }); - - $('#contacts_addpropertyform input[type="submit"]').live('click',function(){ - $.post('ajax/addproperty.php',$('#contacts_addpropertyform').serialize(),function(jsondata){ - if(jsondata.status == 'success'){ - $('#contacts_addpropertyform').before(jsondata.data.page); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - } - }, 'json'); - return false; - }); - - /** - * Show the Addressbook chooser - */ - $('#chooseaddressbook').click(function(){ - Contacts.UI.Addressbooks.overview(); - return false; - }); - - /** - * Open blank form to add new contact. - */ - $('#contacts_newcontact').click(function(){ - $.getJSON('ajax/showaddcard.php',{},function(jsondata){ - if(jsondata.status == 'success'){ - $('#rightcontent').data('id',''); - $('#rightcontent').html(jsondata.data.page) - .find('select').chosen(); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - //alert(jsondata.data.message); - } - }); - return false; - }); - - /** - * Add and insert a new contact into the list. - */ - $('#contacts_addcardform input[type="submit"]').live('click',function(){ - $.post('ajax/addcard.php',$('#contacts_addcardform').serialize(),function(jsondata){ - if(jsondata.status == 'success'){ - $('#rightcontent').data('id',jsondata.data.id); - $('#rightcontent').html(jsondata.data.page); - $('#leftcontent .active').removeClass('active'); - var item = '
  • '+jsondata.data.name+'
  • '; - var added = false; - $('#leftcontent ul li').each(function(){ - if ($(this).text().toLowerCase() > jsondata.data.name.toLowerCase()) { - $(this).before(item).fadeIn('fast'); - added = true; - return false; - } - }); - if(!added) { - $('#leftcontent ul').append(item); - } - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - //alert(jsondata.data.message); - } - }, 'json'); - return false; - }); - - /** - * Show inputs for editing a property. - */ - $('.contacts_property [data-use="edit"]').live('click',function(){ - var id = $('#rightcontent').data('id'); - var checksum = $(this).parents('.contacts_property').first().data('checksum'); - $.getJSON('ajax/showsetproperty.php',{'id': id, 'checksum': checksum },function(jsondata){ - if(jsondata.status == 'success'){ - $('.contacts_property[data-checksum="'+checksum+'"]').html(jsondata.data.page) - .find('select').chosen(); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - //alert(jsondata.data.message); - } - }); - return false; - }); - - /** - * Save the edited property - */ - $('#contacts_setpropertyform input[type="submit"]').live('click',function(){ - $.post('ajax/setproperty.php',$(this).parents('form').first().serialize(),function(jsondata){ - if(jsondata.status == 'success'){ - $('.contacts_property[data-checksum="'+jsondata.data.oldchecksum+'"]').replaceWith(jsondata.data.page); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - //alert(jsondata.data.message); - } - },'json'); - return false; - }); - - $('.contacts_property [data-use="delete"]').live('click',function(){ - var id = $('#rightcontent').data('id'); - var checksum = $(this).parents('li').first().data('checksum'); - $.getJSON('ajax/deleteproperty.php',{'id': id, 'checksum': checksum },function(jsondata){ - if(jsondata.status == 'success'){ - $('.contacts_property[data-checksum="'+checksum+'"]').remove(); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - //alert(jsondata.data.message); - } - }); - return false; - }); - - - $('.contacts_property').live('mouseenter',function(){ - $(this).find('span[data-use]').show(); - }); - - $('.contacts_property').live('mouseleave',function(){ - $(this).find('span[data-use]').hide(); - }); - - $('#contacts_addcardform select').chosen(); - - $('#contacts li').bind('inview', function(event, isInView, visiblePartX, visiblePartY) { - if (isInView) { //NOTE: I've kept all conditions for future reference ;-) - // element is now visible in the viewport - if (visiblePartY == 'top') { - // top part of element is visible - } else if (visiblePartY == 'bottom') { - // bottom part of element is visible - } else { - // whole part of element is visible - if (!$(this).find('a').attr('style')) { - //alert($(this).data('id') + ' has background: ' + $(this).attr('style')); - $(this).find('a').css('background','url(thumbnail.php?id='+$(this).data('id')+') no-repeat'); - }/* else { - alert($(this).data('id') + ' has style ' + $(this).attr('style').match('url')); - }*/ - } - } else { - // element has gone out of viewport - } - }); - - $('.button').tipsy(); - //Contacts.UI.messageBox('Hello','Sailor'); -}); diff --git a/apps/contacts/js/jquery.multi-autocomplete.js b/apps/contacts/js/jquery.multi-autocomplete.js new file mode 100644 index 0000000000..1c923a2543 --- /dev/null +++ b/apps/contacts/js/jquery.multi-autocomplete.js @@ -0,0 +1,81 @@ +/** + * Inspired by http://jqueryui.com/demos/autocomplete/#multiple + */ + +(function( $ ) { + $.widget('ui.multiple_autocomplete', { + _create: function() { + function split( val ) { + return val.split( /,\s*/ ); + } + function extractLast( term ) { + return split( term ).pop(); + } + //console.log('_create: ' + this.options['id']); + var self = this; + this.element.bind('blur', function( event ) { + var tmp = self.element.val().trim(); + if(tmp[tmp.length-1] == ',') { + self.element.val(tmp.substring(0, tmp.length-1)); + } else { + self.element.val(tmp); + } + self.element.trigger('change'); // Changes wasn't saved when only using the dropdown. + }); + this.element.bind( "keydown", function( event ) { + if ( event.keyCode === $.ui.keyCode.TAB && + $( this ).data( "autocomplete" ).menu.active ) { + event.preventDefault(); + } + }) + .autocomplete({ + minLength: 0, + source: function( request, response ) { + // delegate back to autocomplete, but extract the last term + response( $.ui.autocomplete.filter( + self.options.source, extractLast( request.term ) ) ); + }, + focus: function() { + // prevent value inserted on focus + return false; + }, + select: function( event, ui ) { + var terms = split( this.value ); + // remove the current input + terms.pop(); + // add the selected item + terms.push( ui.item.value ); + // add placeholder to get the comma-and-space at the end + terms.push( "" ); + this.value = terms.join( ", " ); + return false; + } + }); + this.button = $( "" ) + .attr( "tabIndex", -1 ) + .attr( "title", "Show All Items" ) + .insertAfter( this.element ) + .addClass('svg') + .addClass('action') + .addClass('combo-button') + .click(function() { + // close if already visible + if ( self.element.autocomplete( "widget" ).is( ":visible" ) ) { + self.element.autocomplete( "close" ); + return; + } + + // work around a bug (likely same cause as #5265) + $( this ).blur(); + + var tmp = self.element.val().trim(); + if(tmp[tmp.length-1] != ',') { + self.element.val(tmp+', '); + } + // pass empty string as value to search for, displaying all results + self.element.autocomplete( "search", "" ); + self.element.focus(); + }); + }, + }); +})( jQuery ); diff --git a/apps/contacts/l10n/xgettextfiles b/apps/contacts/l10n/xgettextfiles index 91d5da46db..e2492431ff 100644 --- a/apps/contacts/l10n/xgettextfiles +++ b/apps/contacts/l10n/xgettextfiles @@ -1,20 +1,17 @@ ../appinfo/app.php ../ajax/activation.php ../ajax/addbook.php -../ajax/addcard.php ../ajax/addproperty.php ../ajax/createaddressbook.php ../ajax/deletebook.php ../ajax/deleteproperty.php ../ajax/getdetails.php -../ajax/setproperty.php +../ajax/saveproperty.php ../ajax/updateaddressbook.php ../lib/app.php ../templates/index.php -../templates/part.addcardform.php ../templates/part.chooseaddressbook.php ../templates/part.chooseaddressbook.rowfields.php -../templates/part.details.php ../templates/part.editaddressbook.php ../templates/part.property.php ../templates/part.setpropertyform.php diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php index ff348403a9..cc33c73300 100644 --- a/apps/contacts/lib/app.php +++ b/apps/contacts/lib/app.php @@ -10,8 +10,10 @@ * This class manages our app actions */ OC_Contacts_App::$l10n = new OC_L10N('contacts'); +OC_Contacts_App::$categories = new OC_VCategories('contacts'); class OC_Contacts_App { public static $l10n; + public static $categories; /** * Render templates/part.details to json output @@ -83,7 +85,7 @@ class OC_Contacts_App { $vcard = OC_VObject::parse($card['carddata']); // Try to fix cards with missing 'N' field from pre ownCloud 4. Hot damn, this is ugly... if(!is_null($vcard) && !$vcard->__isset('N')) { - $appinfo = $info=OC_App::getAppInfo('contacts'); + $appinfo = OC_App::getAppInfo('contacts'); if($appinfo['version'] >= 5) { OC_Log::write('contacts','OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OC_Log::DEBUG); } @@ -92,7 +94,7 @@ class OC_Contacts_App { OC_Log::write('contacts','getContactVCard, found FN field: '.$vcard->__get('FN'), OC_Log::DEBUG); $n = implode(';', array_reverse(array_slice(explode(' ', $vcard->__get('FN')), 0, 2))).';;;'; $vcard->setString('N', $n); - OC_Contacts_VCard::edit( $id, $vcard->serialize()); + OC_Contacts_VCard::edit( $id, $vcard); } else { // Else just add an empty 'N' field :-P $vcard->setString('N', 'Unknown;Name;;;'); } @@ -153,6 +155,10 @@ class OC_Contacts_App { } } + public static function getCategories() { + return self::$categories->categories(); + } + public static function setLastModifiedHeader($contact) { $rev = $contact->getAsString('REV'); if ($rev) { diff --git a/apps/contacts/lib/hooks.php b/apps/contacts/lib/hooks.php index 155cf40f91..e09da20be8 100644 --- a/apps/contacts/lib/hooks.php +++ b/apps/contacts/lib/hooks.php @@ -29,7 +29,7 @@ class OC_Contacts_Hooks{ * @param paramters parameters from postDeleteUser-Hook * @return array */ - public function deleteUser($parameters) { + static public function deleteUser($parameters) { $addressbooks = OC_Contacts_Addressbook::all($parameters['uid']); foreach($addressbooks as $addressbook) { @@ -38,4 +38,62 @@ class OC_Contacts_Hooks{ return true; } + + /** + * @brief Adds the CardDAV resource to the DAV server + * @param paramters parameters from initialize-Hook + * @return array + */ + static public function initializeCardDAV($parameters){ + // We need a backend, the root node and the carddav plugin + $parameters['backends']['carddav'] = new OC_Connector_Sabre_CardDAV(); + $parameters['nodes'][] = new Sabre_CardDAV_AddressBookRoot($parameters['backends']['principal'], $parameters['backends']['carddav']); + $parameters['plugins'][] = new Sabre_CardDAV_Plugin(); + return true; + } + + static public function getCalenderSources($parameters) { + $base_url = OC_Helper::linkTo('calendar', 'ajax/events.php').'?calendar_id='; + foreach(OC_Contacts_Addressbook::all(OC_User::getUser()) as $addressbook) { + $parameters['sources'][] = + array( + 'url' => $base_url.'birthday_'. $addressbook['id'], + 'backgroundColor' => '#cccccc', + 'borderColor' => '#888', + 'textColor' => 'black', + 'cache' => true, + 'editable' => false, + ); + } + } + + static public function getBirthdayEvents($parameters) { + $name = $parameters['calendar_id']; + if (strpos('birthday_', $name) != 0) { + return; + } + $info = explode('_', $name); + $aid = $info[1]; + OC_Contacts_App::getAddressbook($aid); + foreach(OC_Contacts_VCard::all($aid) as $card){ + $vcard = OC_VObject::parse($card['carddata']); + $birthday = $vcard->BDAY; + if ($birthday) { + $date = new DateTime($birthday); + $vevent = new OC_VObject('VEVENT'); + $vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV)); + $vevent->setDateTime('DTSTART', $date, Sabre_VObject_Element_DateTime::DATE); + $vevent->setString('DURATION', 'P1D'); + // DESCRIPTION? + $vevent->setString('RRULE', 'FREQ=YEARLY'); + $title = str_replace('{name}', $vcard->getAsString('FN'), OC_Contacts_App::$l10n->t('{name}\'s Birthday')); + $parameters['events'][] = array( + 'id' => 0,//$card['id'], + 'vevent' => $vevent, + 'repeating' => true, + 'summary' => $title, + ); + } + } + } } diff --git a/apps/contacts/lib/search.php b/apps/contacts/lib/search.php index 5aad6a25f0..cf0a5fe699 100644 --- a/apps/contacts/lib/search.php +++ b/apps/contacts/lib/search.php @@ -1,6 +1,6 @@ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -174,6 +175,9 @@ class OC_Contacts_VCard{ if($property->name == 'UID'){ $uid = $property->value; } + if($property->name == 'ORG'){ + $org = $property->value; + } if($property->name == 'EMAIL' && is_null($email)){ // only use the first email as substitute for missing N or FN. $email = $property->value; } @@ -184,6 +188,8 @@ class OC_Contacts_VCard{ $fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2))); } elseif($email) { $fn = $email; + } elseif($org) { + $fn = $org; } else { $fn = 'Unknown Name'; } @@ -217,31 +223,37 @@ class OC_Contacts_VCard{ /** * @brief Adds a card - * @param integer $id Addressbook id - * @param string $data vCard file - * @return insertid on success or null if card is not parseable. + * @param integer $aid Addressbook id + * @param OC_VObject $card vCard file + * @param string $uri the uri of the card, default based on the UID + * @return insertid on success or null if no card. */ - public static function add($id,$data){ - $fn = null; - - $card = OC_VObject::parse($data); - if(!is_null($card)){ - self::updateValuesFromAdd($card); - $data = $card->serialize(); - } - else{ - OC_Log::write('contacts','OC_Contacts_VCard::add. Error parsing VCard: '.$data,OC_Log::ERROR); - return null; // Ditch cards that can't be parsed by Sabre. + public static function add($aid, $card, $uri=null){ + if(is_null($card)){ + OC_Log::write('contacts','OC_Contacts_VCard::add. No vCard supplied', OC_Log::ERROR); + return null; }; + OC_Contacts_App::$categories->loadFromVObject($card); + + self::updateValuesFromAdd($card); + $fn = $card->getAsString('FN'); - $uid = $card->getAsString('UID'); - $uri = $uid.'.vcf'; + if (empty($fn)) { + $fn = null; + } + + if (!$uri) { + $uid = $card->getAsString('UID'); + $uri = $uid.'.vcf'; + } + + $data = $card->serialize(); $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' ); - $result = $stmt->execute(array($id,$fn,$data,$uri,time())); + $result = $stmt->execute(array($aid,$fn,$data,$uri,time())); $newid = OC_DB::insertid('*PREFIX*contacts_cards'); - OC_Contacts_Addressbook::touch($id); + OC_Contacts_Addressbook::touch($aid); return $newid; } @@ -255,49 +267,56 @@ class OC_Contacts_VCard{ */ public static function addFromDAVData($id,$uri,$data){ $card = OC_VObject::parse($data); - if(!is_null($card)){ - self::updateValuesFromAdd($card); - $data = $card->serialize(); - } else { - OC_Log::write('contacts','OC_Contacts_VCard::addFromDAVData. Error parsing VCard: '.$data, OC_Log::ERROR); - return null; // Ditch cards that can't be parsed by Sabre. - }; - $fn = $card->getAsString('FN'); + return self::add($id, $data, $uri); + } - $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' ); - $result = $stmt->execute(array($id,$fn,$data,$uri,time())); - $newid = OC_DB::insertid('*PREFIX*contacts_cards'); - - OC_Contacts_Addressbook::touch($id); - - return $newid; + /** + * @brief Mass updates an array of cards + * @param array $objects An array of [id, carddata]. + */ + public static function updateDataByID($objects){ + $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET carddata = ?, lastmodified = ? WHERE id = ?' ); + $now = new DateTime; + foreach($objects as $object) { + $vcard = OC_VObject::parse($object[1]); + if(!is_null($vcard)){ + $vcard->setString('REV', $now->format(DateTime::W3C)); + $data = $vcard->serialize(); + try { + $result = $stmt->execute(array($data,time(),$object[0])); + //OC_Log::write('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0].': '.$object[1],OC_Log::DEBUG); + } catch(Exception $e) { + OC_Log::write('contacts','OC_Contacts_VCard::updateDataByID:, exception: '.$e->getMessage(),OC_Log::DEBUG); + OC_Log::write('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0],OC_Log::DEBUG); + } + } + } } /** * @brief edits a card * @param integer $id id of card - * @param string $data vCard file + * @param OC_VObject $card vCard file * @return boolean */ - public static function edit($id, $data){ + public static function edit($id, OC_VObject $card){ $oldcard = self::find($id); - $fn = null; - $card = OC_VObject::parse($data); - if(!is_null($card)){ - foreach($card->children as $property){ - if($property->name == 'FN'){ - $fn = $property->value; - break; - } - } - } else { + if(is_null($card)) { return false; } + + OC_Contacts_App::$categories->loadFromVObject($card); + + $fn = $card->getAsString('FN'); + if (empty($fn)) { + $fn = null; + } + $now = new DateTime; $card->setString('REV', $now->format(DateTime::W3C)); - $data = $card->serialize(); + $data = $card->serialize(); $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' ); $result = $stmt->execute(array($fn,$data,time(),$id)); @@ -315,27 +334,8 @@ class OC_Contacts_VCard{ */ public static function editFromDAVData($aid,$uri,$data){ $oldcard = self::findWhereDAVDataIs($aid,$uri); - - $fn = null; $card = OC_VObject::parse($data); - if(!is_null($card)){ - foreach($card->children as $property){ - if($property->name == 'FN'){ - $fn = $property->value; - break; - } - } - } - $now = new DateTime; - $card->setString('REV', $now->format(DateTime::W3C)); - $data = $card->serialize(); - - $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' ); - $result = $stmt->execute(array($fn,$data,time(),$oldcard['id'])); - - OC_Contacts_Addressbook::touch($oldcard['addressbookid']); - - return true; + return self::edit($oldcard['id'], $card); } /** @@ -351,14 +351,6 @@ class OC_Contacts_VCard{ return true; } - /** - * @brief Creates a UID - * @return string - */ - public static function createUID(){ - return substr(md5(rand().time()),0,10); - } - /** * @brief deletes a card with the data provided by sabredav * @param integer $aid Addressbook id @@ -374,6 +366,43 @@ class OC_Contacts_VCard{ return true; } + /** + * @brief Escapes delimiters from an array and returns a string. + * @param array $value + * @param char $delimiter + * @return string + */ + public static function escapeDelimiters($value, $delimiter=';') { + foreach($value as &$i ) { + $i = implode("\\$delimiter", explode($delimiter, $i)); + } + return implode($delimiter, $value); + } + + + /** + * @brief Creates an array out of a multivalue property + * @param string $value + * @param char $delimiter + * @return array + */ + public static function unescapeDelimiters($value, $delimiter=';') { + $array = explode($delimiter,$value); + for($i=0;$ivalue; //$value = htmlspecialchars($value); if($property->name == 'ADR' || $property->name == 'N'){ - $value = OC_VObject::unescapeSemicolons($value); - } + $value = self::unescapeDelimiters($value); + }/* elseif($property->name == 'CATEGORIES') { + $value = self::unescapeDelimiters($value, ','); + }*/ $temp = array( 'name' => $property->name, 'value' => $value, diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php index e81597f23d..af159ce9c6 100644 --- a/apps/contacts/templates/index.php +++ b/apps/contacts/templates/index.php @@ -1,5 +1,6 @@
    diff --git a/apps/contacts/templates/part.addcardform.php b/apps/contacts/templates/part.addcardform.php deleted file mode 100644 index 1ad4c18b35..0000000000 --- a/apps/contacts/templates/part.addcardform.php +++ /dev/null @@ -1,138 +0,0 @@ - - - - -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    -
    - - -
    - -
    -
    - - -
    - -
    -
    - - -
    - - - -
    -
    -
    -
    -
    -
    - - -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    -
    -
    -
    - t('Address'); ?> -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - -
    -
    - - - -
    -
    - -
    -
    - - - -
    -
    - -
    -
    - -
    - -
    - -
    -
    - -
    -
    -
    - -
    diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index 5be20964f4..cb1e080a40 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -13,6 +13,8 @@ $id = isset($_['id']) ? $_['id'] : '';
  • t('Phone'); ?>
  • t('Email'); ?>
  • t('Address'); ?>
  • +
  • t('Note'); ?>
  • +
  • t('Categories'); ?>
  • @@ -21,8 +23,9 @@ $id = isset($_['id']) ? $_['id'] : '';
    -
    diff --git a/apps/contacts/templates/part.contactphoto.php b/apps/contacts/templates/part.contactphoto.php index 9e3f5876cd..bcb2f75815 100644 --- a/apps/contacts/templates/part.contactphoto.php +++ b/apps/contacts/templates/part.contactphoto.php @@ -2,8 +2,9 @@ $id = $_['id']; $wattr = isset($_['width'])?'width="'.$_['width'].'"':''; $hattr = isset($_['height'])?'height="'.$_['height'].'"':''; +$rand = isset($_['refresh'])?'&'.rand().'='.rand():''; ?> - src="?id=&refresh=" /> + src="?id=" /> diff --git a/apps/contacts/templates/part.details.php b/apps/contacts/templates/part.details.php deleted file mode 100644 index 5badd81615..0000000000 --- a/apps/contacts/templates/part.details.php +++ /dev/null @@ -1,96 +0,0 @@ - - inc('part.property.FN', array('property' => $_['details']['FN'][0])); ?> - inc('part.property.N', array('property' => $_['details']['N'][0])); ?> - - - - - - - -
      - - inc('part.property', array('property' => $_['details']['BDAY'][0])); ?> - - - - inc('part.property', array('property' => $_['details']['ORG'][0])); ?> - - - - - - inc('part.property',array('property' => $property )); ?> - - - -
    • -
      - -

      - -
      - -

      -

      - -

      -
      - -
    • -
    - - diff --git a/apps/contacts/templates/part.edit_categories_dialog.php b/apps/contacts/templates/part.edit_categories_dialog.php new file mode 100644 index 0000000000..8997fa586b --- /dev/null +++ b/apps/contacts/templates/part.edit_categories_dialog.php @@ -0,0 +1,16 @@ + +
    + +
    +
    +
      + +
    • + +
    +
    +
    +
    +
    diff --git a/apps/contacts/templates/part.messagebox.php b/apps/contacts/templates/part.messagebox.php deleted file mode 100644 index 5db10e7e6c..0000000000 --- a/apps/contacts/templates/part.messagebox.php +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    - diff --git a/apps/contacts/templates/part.no_contacts.php b/apps/contacts/templates/part.no_contacts.php deleted file mode 100644 index f58fdef09f..0000000000 --- a/apps/contacts/templates/part.no_contacts.php +++ /dev/null @@ -1,8 +0,0 @@ -
    -You have no contacts in your list. -
    - - - -
    -
    \ No newline at end of file diff --git a/apps/contacts/templates/part.property.FN.php b/apps/contacts/templates/part.property.FN.php deleted file mode 100644 index c9e21c20e6..0000000000 --- a/apps/contacts/templates/part.property.FN.php +++ /dev/null @@ -1,9 +0,0 @@ -

    - - -

    - - - diff --git a/apps/contacts/templates/part.property.N.php b/apps/contacts/templates/part.property.N.php deleted file mode 100644 index 73d599ad7b..0000000000 --- a/apps/contacts/templates/part.property.N.php +++ /dev/null @@ -1,4 +0,0 @@ -

    - () - -

    diff --git a/apps/contacts/templates/part.property.php b/apps/contacts/templates/part.property.php deleted file mode 100644 index 7b23fae45b..0000000000 --- a/apps/contacts/templates/part.property.php +++ /dev/null @@ -1,86 +0,0 @@ -
  • - -

    t('Birthday'); ?>

    -

    - l('date',new DateTime($_['property']['value'])); ?> - -

    - -

    t('Organization'); ?>

    -

    - - - -

    - -

    t('Email'); ?>

    -

    - - - -

    - -

    t('Preferred').' ' : '' ?>t('Phone'); ?>

    -

    - - -t(ucwords(strtolower($type))); - } - } - $label = join(' ', $types); -?> - () - - - -

    - -

    - t('Address'); ?> - -
    -t(ucwords(strtolower($type))); - } -?> - () - -

    -

    - -
    - - -
    - - -
    - - -
    - - -
    - - -
    - - - - - - -

    - -
  • diff --git a/apps/contacts/templates/part.setpropertyform.php b/apps/contacts/templates/part.setpropertyform.php deleted file mode 100644 index 93ade8faaa..0000000000 --- a/apps/contacts/templates/part.setpropertyform.php +++ /dev/null @@ -1,91 +0,0 @@ -
    - - - -

    -

    -
    -
    -
    -
    -
    -
    - - -
    -
    -

    - -

    - -

    -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - -
    - -
    -
    - - -
    -
    - -
    -
    - - - -
    -
    - -
    -
    - -
    - -
    - -
    -
    - -
    -
    - -

    -

    -

    - -

    -

    - -

    -

    - - -
    diff --git a/apps/contacts/templates/settings.php b/apps/contacts/templates/settings.php index 8673e4521d..f56de0ec8b 100644 --- a/apps/contacts/templates/settings.php +++ b/apps/contacts/templates/settings.php @@ -1,7 +1,12 @@ -
    +
    t('Contacts'); ?>
    - t('CardDAV syncing address:'); ?> -
    + t('CardDAV syncing addresses:'); ?> +
    +
    t('Primary address (Kontact et al)'); ?>
    +
    /
    +
    t('iOS/OS X'); ?>
    +
    /principals//
    +
    diff --git a/apps/external/ajax/setsites.php b/apps/external/ajax/setsites.php new file mode 100644 index 0000000000..0537b7ea58 --- /dev/null +++ b/apps/external/ajax/setsites.php @@ -0,0 +1,25 @@ + + * 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_Util::checkAdminUser(); + +$sites = array(); +for ($i = 0; $i < sizeof($_POST['site_name']); $i++) { + if (!empty($_POST['site_name'][$i]) && !empty($_POST['site_url'][$i])) { + array_push($sites, array($_POST['site_name'][$i], $_POST['site_url'][$i])); + } +} + +if (sizeof($sites) == 0) + OC_Appconfig::deleteKey('external', 'sites'); +else + OC_Appconfig::setValue('external', 'sites', json_encode($sites)); + +echo 'true'; +?> diff --git a/apps/external/ajax/seturls.php b/apps/external/ajax/seturls.php deleted file mode 100644 index e994385a19..0000000000 --- a/apps/external/ajax/seturls.php +++ /dev/null @@ -1,24 +0,0 @@ - - * 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_Util::checkAdminUser(); - -if(isset($_POST['s1name'])) OC_Appconfig::setValue( 'external','site1name', $_POST['s1name'] ); -if(isset($_POST['s1url'])) OC_Appconfig::setValue( 'external','site1url', $_POST['s1url'] ); -if(isset($_POST['s2name'])) OC_Appconfig::setValue( 'external','site2name', $_POST['s2name'] ); -if(isset($_POST['s2url'])) OC_Appconfig::setValue( 'external','site2url', $_POST['s2url'] ); -if(isset($_POST['s3name'])) OC_Appconfig::setValue( 'external','site3name', $_POST['s3name'] ); -if(isset($_POST['s3url'])) OC_Appconfig::setValue( 'external','site3url', $_POST['s3url'] ); -if(isset($_POST['s4name'])) OC_Appconfig::setValue( 'external','site4name', $_POST['s4name'] ); -if(isset($_POST['s4url'])) OC_Appconfig::setValue( 'external','site4url', $_POST['s4url'] ); -if(isset($_POST['s5name'])) OC_Appconfig::setValue( 'external','site5name', $_POST['s5name'] ); -if(isset($_POST['s5url'])) OC_Appconfig::setValue( 'external','site5url', $_POST['s5url'] ); - -echo 'true'; - -?> diff --git a/apps/external/appinfo/app.php b/apps/external/appinfo/app.php index 0f536cbf41..74e6d5c94c 100644 --- a/apps/external/appinfo/app.php +++ b/apps/external/appinfo/app.php @@ -1,37 +1,35 @@ . -* -*/ + * ownCloud - External plugin + * + * @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 . + * + */ -OC_APP::registerAdmin('external','settings'); +OC::$CLASSPATH['OC_External'] = 'apps/external/lib/external.php'; +OC_Util::addStyle( 'external', 'style'); -OC_App::register( array( 'order' => 70, 'id' => 'external', 'name' => 'External' )); +OC_APP::registerAdmin('external', 'settings'); -if(OC_Appconfig::getValue( "external","site1name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index1', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=1', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site1name", '' ))); - -if(OC_Appconfig::getValue( "external","site2name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index2', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=2', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site2name", '' ))); - -if(OC_Appconfig::getValue( "external","site3name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index3', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=3', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site3name", '' ))); - -if(OC_Appconfig::getValue( "external","site4name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index4', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=4', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site4name", '' ))); - -if(OC_Appconfig::getValue( "external","site5name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index5', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=5', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site5name", '' ))); +OC_App::register(array('order' => 70, 'id' => 'external', 'name' => 'External')); +$sites = OC_External::getSites(); +for ($i = 0; $i < sizeof($sites); $i++) { + OC_App::addNavigationEntry( + array('id' => 'external_index' . ($i + 1), 'order' => 80 + $i, 'href' => OC_Helper::linkTo('external', 'index.php') . '?id=' . ($i + 1), 'icon' => OC_Helper::imagePath('external', 'external.png'), 'name' => $sites[$i][0])); +} \ No newline at end of file diff --git a/apps/external/css/style.css b/apps/external/css/style.css new file mode 100644 index 0000000000..f891cb4bc5 --- /dev/null +++ b/apps/external/css/style.css @@ -0,0 +1,14 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / +/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ + +.site_url { + width: 250px; +} + +.delete_button { + display: none; +} + +.external_sites { + width: 450px; +} diff --git a/apps/external/index.php b/apps/external/index.php index 51cdc344bb..1c20f59eaf 100644 --- a/apps/external/index.php +++ b/apps/external/index.php @@ -1,42 +1,43 @@ . -* -*/ - + * ownCloud - External plugin + * + * @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 . + * + */ require_once('../../lib/base.php'); +require_once('lib/external.php'); OC_Util::checkLoggedIn(); -if(isset($_GET['id'])){ +if (isset($_GET['id'])) { - $id=$_GET['id']; + $id = $_GET['id']; $id = (int) $id; - $url=OC_Appconfig::getValue( "external","site".$id."url", '' ); - OC_App::setActiveNavigationEntry( 'external_index'.$id ); - - $tmpl = new OC_Template( 'external', 'frame', 'user' ); - $tmpl->assign('url',$url); - $tmpl->printPage(); + $sites = OC_External::getSites(); + if (sizeof($sites) >= $id) { + $url = $sites[$id - 1][1]; + OC_App::setActiveNavigationEntry('external_index' . $id); + $tmpl = new OC_Template('external', 'frame', 'user'); + $tmpl->assign('url', $url); + $tmpl->printPage(); + } } - ?> diff --git a/apps/external/js/admin.js b/apps/external/js/admin.js index 6b9b6c6773..0caaabd0b9 100644 --- a/apps/external/js/admin.js +++ b/apps/external/js/admin.js @@ -1,68 +1,57 @@ $(document).ready(function(){ + newSiteHtml = '
  • \n\ + \n\ +
  • '; - - - $('#s1name').blur(function(event){ + // Handler functions + function addSiteEventHandler(event) { event.preventDefault(); - var post = $( "#s1name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s1name .msg', data); }); - }); + + saveSites(); + } - $('#s2name').blur(function(event){ + function deleteButtonEventHandler(event) { event.preventDefault(); - var post = $( "#s2name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s2name .msg', data); }); - }); - $('#s3name').blur(function(event){ + $(this).tipsy('hide'); + $(this).parent().remove(); + + saveSites(); + } + + function saveSites() { + var post = $('#external').serialize(); + $.post( OC.filePath('external','ajax','setsites.php') , post, function(data) { + // OC.msg.finishedSaving('#site_name .msg', data); + }); + } + + function showDeleteButton(event) { + $(this).find('img.delete_button').fadeIn(100); + } + + function hideDeleteButton(event) { + $(this).find('img.delete_button').fadeOut(100); + } + + // Initialize events + $('input[name^=site_]').change(addSiteEventHandler); + $('img.delete_button').click(deleteButtonEventHandler); + $('img.delete_button').tipsy(); + + $('#external li').hover(showDeleteButton, hideDeleteButton); + + $('#add_external_site').click(function(event) { event.preventDefault(); - var post = $( "#s3name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s3name .msg', data); }); - }); + $('#external ul').append(newSiteHtml); - $('#s4name').blur(function(event){ - event.preventDefault(); - var post = $( "#s4name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s4name .msg', data); }); + $('input.site_url:last').prev('input.site_name').andSelf().change(addSiteEventHandler); + $('img.delete_button').click(deleteButtonEventHandler); + $('img.delete_button:last').tipsy(); + $('#external li:last').hover(showDeleteButton, hideDeleteButton); + }); - $('#s5name').blur(function(event){ - event.preventDefault(); - var post = $( "#s5name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s5name .msg', data); }); - }); - - $('#s1url').blur(function(event){ - event.preventDefault(); - var post = $( "#s1url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s1url .msg', data); }); - }); - - $('#s2url').blur(function(event){ - event.preventDefault(); - var post = $( "#s2url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s2url .msg', data); }); - }); - - $('#s3url').blur(function(event){ - event.preventDefault(); - var post = $( "#s3url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s3url .msg', data); }); - }); - - $('#s4url').blur(function(event){ - event.preventDefault(); - var post = $( "#s4url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s4url .msg', data); }); - }); - - $('#s5url').blur(function(event){ - event.preventDefault(); - var post = $( "#s5url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s5url .msg', data); }); - }); - - }); diff --git a/apps/external/lib/external.php b/apps/external/lib/external.php new file mode 100644 index 0000000000..9dd3232113 --- /dev/null +++ b/apps/external/lib/external.php @@ -0,0 +1,36 @@ +. + * + */ + +class OC_External { + + public static function getSites() { + if (($sites = json_decode(OC_Appconfig::getValue("external", "sites", ''))) != NULL) { + return $sites; + } + + return array(); + } + +} + +?> diff --git a/apps/external/settings.php b/apps/external/settings.php index 3e0c342512..416c9a5c11 100644 --- a/apps/external/settings.php +++ b/apps/external/settings.php @@ -6,17 +6,5 @@ OC_Util::addScript( "external", "admin" ); $tmpl = new OC_Template( 'external', 'settings'); - $tmpl->assign('s1name',OC_Appconfig::getValue( "external","site1name", '' )); - $tmpl->assign('s2name',OC_Appconfig::getValue( "external","site2name", '' )); - $tmpl->assign('s3name',OC_Appconfig::getValue( "external","site3name", '' )); - $tmpl->assign('s4name',OC_Appconfig::getValue( "external","site4name", '' )); - $tmpl->assign('s5name',OC_Appconfig::getValue( "external","site5name", '' )); - - $tmpl->assign('s1url',OC_Appconfig::getValue( "external","site1url", '' )); - $tmpl->assign('s2url',OC_Appconfig::getValue( "external","site2url", '' )); - $tmpl->assign('s3url',OC_Appconfig::getValue( "external","site3url", '' )); - $tmpl->assign('s4url',OC_Appconfig::getValue( "external","site4url", '' )); - $tmpl->assign('s5url',OC_Appconfig::getValue( "external","site5url", '' )); - return $tmpl->fetchPage(); ?> diff --git a/apps/external/templates/settings.php b/apps/external/templates/settings.php index a72327d35c..a130477d46 100644 --- a/apps/external/templates/settings.php +++ b/apps/external/templates/settings.php @@ -1,23 +1,21 @@
    External Sites
    - - -
    - - -
    - - -
    - - -
    - - -
    +
      + + + + '; + } + ?> - +
    + + +
    diff --git a/apps/files_archive/appinfo/app.php b/apps/files_archive/appinfo/app.php new file mode 100644 index 0000000000..693c28d98a --- /dev/null +++ b/apps/files_archive/appinfo/app.php @@ -0,0 +1,18 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +OC::$CLASSPATH['OC_Archive'] = 'apps/files_archive/lib/archive.php'; +foreach(array('ZIP') as $type){ + OC::$CLASSPATH['OC_Archive_'.$type] = 'apps/files_archive/lib/'.strtolower($type).'.php'; +} + +OC::$CLASSPATH['OC_Filestorage_Archive']='apps/files_archive/lib/storage.php'; + +OC_Hook::connect('OC_Filesystem','get_mountpoint','OC_Filestorage_Archive','autoMount'); + +OC_Util::addScript( 'files_archive', 'archive' ); diff --git a/apps/files_archive/appinfo/info.xml b/apps/files_archive/appinfo/info.xml new file mode 100644 index 0000000000..df767d39f6 --- /dev/null +++ b/apps/files_archive/appinfo/info.xml @@ -0,0 +1,10 @@ + + + files_archive + Archive support + Transparent opening of archives + 0.1 + AGPL + Robin Appelman + 3 + diff --git a/apps/files_archive/js/archive.js b/apps/files_archive/js/archive.js new file mode 100644 index 0000000000..ec316c7bf2 --- /dev/null +++ b/apps/files_archive/js/archive.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2012 Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +$(document).ready(function() { + if(typeof FileActions!=='undefined'){ + FileActions.register('application/zip','Open','',function(filename){ + window.location='index.php?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename); + }); + FileActions.setDefault('application/zip','Open'); + } +}); diff --git a/apps/files_archive/lib/archive.php b/apps/files_archive/lib/archive.php new file mode 100644 index 0000000000..be89f894fb --- /dev/null +++ b/apps/files_archive/lib/archive.php @@ -0,0 +1,99 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +abstract class OC_Archive{ + /** + * open any of the supporeted archive types + * @param string path + * @return OC_Archive + */ + public static function open($path){ + $ext=substr($path,strrpos($path,'.')); + switch($ext){ + case '.zip': + return new OC_Archive_ZIP($path); + } + } + + abstract function __construct($source); + /** + * add an empty folder to the archive + * @param string path + * @return bool + */ + abstract function addFolder($path); + /** + * add a file to the archive + * @param string path + * @param string source either a local file or string data + * @return bool + */ + abstract function addFile($path,$source=''); + /** + * rename a file or folder in the archive + * @param string source + * @param string dest + * @return bool + */ + abstract function rename($source,$dest); + /** + * get the uncompressed size of a file in the archive + * @param string path + * @return int + */ + abstract function filesize($path); + /** + * get the last modified time of a file in the archive + * @param string path + * @return int + */ + abstract function mtime($path); + /** + * get the files in a folder + * @param path + * @return array + */ + abstract function getFolder($path); + /** + *get all files in the archive + * @return array + */ + abstract function getFiles(); + /** + * get the content of a file + * @param string path + * @return string + */ + abstract function getFile($path); + /** + * extract a single file from the archive + * @param string path + * @param string dest + * @return bool + */ + abstract function extractFile($path,$dest); + /** + * check if a file or folder exists in the archive + * @param string path + * @return bool + */ + abstract function fileExists($path); + /** + * remove a file or folder from the archive + * @param string path + * @return bool + */ + abstract function remove($path); + /** + * get a file handler + * @param string path + * @param string mode + * @return resource + */ + abstract function getStream($path,$mode); +} diff --git a/apps/files_archive/lib/storage.php b/apps/files_archive/lib/storage.php new file mode 100644 index 0000000000..72a96ca5a5 --- /dev/null +++ b/apps/files_archive/lib/storage.php @@ -0,0 +1,142 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Filestorage_Archive extends OC_Filestorage_Common{ + /** + * underlying local storage used for missing functions + * @var OC_Archive + */ + private $archive; + private $path; + private static $mounted=array(); + private static $enableAutomount=true; + private static $rootView; + + private function stripPath($path){//files should never start with / + if(substr($path,0,1)=='/'){ + $path=substr($path,1); + } + return $path; + } + + public function __construct($params){ + $this->archive=OC_Archive::open($params['archive']); + $this->path=$params['archive']; + } + + public function mkdir($path){ + $path=$this->stripPath($path); + return $this->archive->addFolder($path); + } + public function rmdir($path){ + $path=$this->stripPath($path); + return $this->archive->remove($path.'/'); + } + public function opendir($path){ + $path=$this->stripPath($path); + $content=$this->archive->getFolder($path); + foreach($content as &$file){ + if(substr($file,-1)=='/'){ + $file=substr($file,0,-1); + } + } + $id=md5($this->path.$path); + OC_FakeDirStream::$dirs[$id]=$content; + return opendir('fakedir://'.$id); + } + public function stat($path){ + $ctime=filectime($this->path); + $path=$this->stripPath($path); + if($path==''){ + $stat=stat($this->path); + }else{ + if($this->is_dir($path)){ + $stat=array('size'=>0); + $stat['mtime']=filemtime($this->path); + }else{ + $stat=array(); + $stat['mtime']=$this->archive->mtime($path); + $stat['size']=$this->archive->filesize($path); + } + } + $stat['ctime']=$ctime; + return $stat; + } + public function filetype($path){ + $path=$this->stripPath($path); + if($path==''){ + return 'dir'; + } + if(substr($path,-1)=='/'){ + return $this->archive->fileExists($path)?'dir':'file'; + }else{ + return $this->archive->fileExists($path.'/')?'dir':'file'; + } + } + public function is_readable($path){ + return is_readable($this->path); + } + public function is_writable($path){ + return is_writable($this->path); + } + public function file_exists($path){ + $path=$this->stripPath($path); + if($path==''){ + return file_exists($this->path); + } + return $this->archive->fileExists($path) or $this->archive->fileExists($path.'/'); + } + public function unlink($path){ + $path=$this->stripPath($path); + return $this->archive->remove($path); + } + public function fopen($path,$mode){ + $path=$this->stripPath($path); + return $this->archive->getStream($path,$mode); + } + public function free_space($path){ + return 0; + } + public function touch($path, $mtime=null){ + if(is_null($mtime)){ + $tmpFile=OC_Helper::tmpFile(); + $this->archive->extractFile($path,$tmpFile); + $this->archive->addfile($path,$tmpFile); + }else{ + return false;//not supported + } + } + + /** + * automount paths from file hooks + * @param aray params + */ + public static function autoMount($params){ + if(!self::$enableAutomount){ + return; + } + $path=$params['path']; + if(!self::$rootView){ + self::$rootView=new OC_FilesystemView(''); + } + self::$enableAutomount=false;//prevent recursion + $supported=array('zip'); + foreach($supported as $type){ + $ext='.'.$type.'/'; + if(($pos=strpos(strtolower($path),$ext))!==false){ + $archive=substr($path,0,$pos+strlen($ext)-1); + if(self::$rootView->file_exists($archive) and array_search($archive,self::$mounted)===false){ + $localArchive=self::$rootView->getLocalFile($archive); + OC_Filesystem::mount('OC_Filestorage_Archive',array('archive'=>$localArchive),$archive.'/'); + self::$mounted[]=$archive; + } + } + } + self::$enableAutomount=true; + } +} diff --git a/apps/files_archive/lib/zip.php b/apps/files_archive/lib/zip.php new file mode 100644 index 0000000000..eab101b3a5 --- /dev/null +++ b/apps/files_archive/lib/zip.php @@ -0,0 +1,182 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Archive_ZIP extends OC_Archive{ + /** + * @var ZipArchive zip + */ + private $zip=null; + private $contents=array(); + private $success=false; + private $path; + + function __construct($source){ + $this->path=$source; + $this->zip=new ZipArchive(); + if($this->zip->open($source,ZipArchive::CREATE)){ + }else{ + OC_LOG::write('files_archive','Error while opening archive '.$source,OC_Log::WARN); + } + } + /** + * add an empty folder to the archive + * @param string path + * @return bool + */ + function addFolder($path){ + return $this->zip->addEmptyDir($path); + } + /** + * add a file to the archive + * @param string path + * @param string source either a local file or string data + * @return bool + */ + function addFile($path,$source=''){ + if(file_exists($source)){ + $result=$this->zip->addFile($source,$path); + }else{ + $result=$this->zip->addFromString($path,$source); + } + if($result){ + $this->zip->close();//close and reopen to save the zip + $this->zip->open($this->path); + } + return $result; + } + /** + * rename a file or folder in the archive + * @param string source + * @param string dest + * @return bool + */ + function rename($source,$dest){ + return $this->zip->renameName($source,$dest); + } + /** + * get the uncompressed size of a file in the archive + * @param string path + * @return int + */ + function filesize($path){ + $stat=$this->zip->statName($path); + return $stat['size']; + } + /** + * get the last modified time of a file in the archive + * @param string path + * @return int + */ + function mtime($path){ + $stat=$this->zip->statName($path); + return $stat['mtime']; + } + /** + * get the files in a folder + * @param path + * @return array + */ + function getFolder($path){ + $files=$this->getFiles(); + $folderContent=array(); + $pathLength=strlen($path); + foreach($files as $file){ + if(substr($file,0,$pathLength)==$path and $file!=$path){ + if(strrpos(substr($file,0,-1),'/')<=$pathLength){ + $folderContent[]=substr($file,$pathLength); + } + } + } + return $folderContent; + } + /** + *get all files in the archive + * @return array + */ + function getFiles(){ + if(count($this->contents)){ + return $this->contents; + } + $fileCount=$this->zip->numFiles; + $files=array(); + for($i=0;$i<$fileCount;$i++){ + $files[]=$this->zip->getNameIndex($i); + } + $this->contents=$files; + return $files; + } + /** + * get the content of a file + * @param string path + * @return string + */ + function getFile($path){ + return $this->zip->getFromName($path); + } + /** + * extract a single file from the archive + * @param string path + * @param string dest + * @return bool + */ + function extractFile($path,$dest){ + $fp = $this->zip->getStream($path); + file_put_contents($dest,$fp); + } + /** + * check if a file or folder exists in the archive + * @param string path + * @return bool + */ + function fileExists($path){ + return $this->zip->locateName($path)!==false; + } + /** + * remove a file or folder from the archive + * @param string path + * @return bool + */ + function remove($path){ + return $this->zip->deleteName($path); + } + /** + * get a file handler + * @param string path + * @param string mode + * @return resource + */ + function getStream($path,$mode){ + if($mode=='r' or $mode=='rb'){ + return $this->zip->getStream($path); + }else{//since we cant directly get a writable stream, make a temp copy of the file and put it back in the archive when the stream is closed + 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->fileExists($path)){ + $this->extractFile($path,$tmpFile); + } + self::$tempFiles[$tmpFile]=$path; + return fopen('close://'.$tmpFile,$mode); + } + } + + private static $tempFiles=array(); + /** + * write back temporary files + */ + function writeBack($tmpFile){ + if(isset(self::$tempFiles[$tmpFile])){ + $this->addFile(self::$tempFiles[$tmpFile],$tmpFile); + unlink($tmpFile); + } + } +} diff --git a/apps/files_archive/tests/archive.php b/apps/files_archive/tests/archive.php new file mode 100644 index 0000000000..2e26b5e03b --- /dev/null +++ b/apps/files_archive/tests/archive.php @@ -0,0 +1,97 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +abstract class Test_Archive extends UnitTestCase { + /** + * @var OC_Archive + */ + protected $instance; + + /** + * get the existing test archive + * @return OC_Archive + */ + abstract protected function getExisting(); + /** + * get a new archive for write testing + * @return OC_Archive + */ + abstract protected function getNew(); + + public function testGetFiles(){ + $this->instance=$this->getExisting(); + $allFiles=$this->instance->getFiles(); + $expected=array('lorem.txt','logo-wide.png','dir/','dir/lorem.txt'); + $this->assertEqual(4,count($allFiles)); + foreach($expected as $file){ + $this->assertNotIdentical(false,array_search($file,$allFiles),'cant find '.$file.' in archive'); + $this->assertTrue($this->instance->fileExists($file)); + } + $this->assertFalse($this->instance->fileExists('non/existing/file')); + + $rootContent=$this->instance->getFolder(''); + $expected=array('lorem.txt','logo-wide.png','dir/'); + $this->assertEqual(3,count($rootContent)); + foreach($expected as $file){ + $this->assertNotIdentical(false,array_search($file,$rootContent),'cant find '.$file.' in archive'); + } + + $dirContent=$this->instance->getFolder('dir/'); + $expected=array('lorem.txt'); + $this->assertEqual(1,count($dirContent)); + foreach($expected as $file){ + $this->assertNotIdentical(false,array_search($file,$dirContent),'cant find '.$file.' in archive'); + } + } + + public function testContent(){ + $this->instance=$this->getExisting(); + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $textFile=$dir.'/lorem.txt'; + $this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt')); + + $tmpFile=OC_Helper::tmpFile('.txt'); + $this->instance->extractFile('lorem.txt',$tmpFile); + $this->assertEqual(file_get_contents($textFile),file_get_contents($tmpFile)); + } + + public function testWrite(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $textFile=$dir.'/lorem.txt'; + $this->instance=$this->getNew(); + $this->assertEqual(0,count($this->instance->getFiles())); + $this->instance->addFile('lorem.txt',$textFile); + $this->assertEqual(1,count($this->instance->getFiles())); + $this->assertTrue($this->instance->fileExists('lorem.txt')); + + $this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt')); + $this->instance->addFile('lorem.txt','foobar'); + $this->assertEqual('foobar',$this->instance->getFile('lorem.txt')); + } + + public function testReadStream(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $this->instance=$this->getExisting(); + $fh=$this->instance->getStream('lorem.txt','r'); + $this->assertTrue($fh); + $content=fread($fh,$this->instance->filesize('lorem.txt')); + fclose($fh); + $this->assertEqual(file_get_contents($dir.'/lorem.txt'),$content); + } + public function testWriteStream(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $this->instance=$this->getNew(); + $fh=$this->instance->getStream('lorem.txt','w'); + $source=fopen($dir.'/lorem.txt','r'); + OC_Helper::streamCopy($source,$fh); + fclose($source); + fclose($fh); + $this->assertTrue($this->instance->fileExists('lorem.txt')); + $this->assertEqual(file_get_contents($dir.'/lorem.txt'),$this->instance->getFile('lorem.txt')); + } +} diff --git a/apps/files_archive/tests/storage.php b/apps/files_archive/tests/storage.php new file mode 100644 index 0000000000..4d0a83356b --- /dev/null +++ b/apps/files_archive/tests/storage.php @@ -0,0 +1,25 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_Filestorage_Archive_Zip extends Test_FileStorage { + /** + * @var string tmpDir + */ + private $tmpFile; + + public function setUp(){ + $this->tmpFile=OC_Helper::tmpFile('.zip'); + $this->instance=new OC_Filestorage_Archive(array('archive'=>$this->tmpFile)); + } + + public function tearDown(){ + unlink($this->tmpFile); + } +} + +?> \ No newline at end of file diff --git a/apps/files_archive/tests/zip.php b/apps/files_archive/tests/zip.php new file mode 100644 index 0000000000..3ff713eda7 --- /dev/null +++ b/apps/files_archive/tests/zip.php @@ -0,0 +1,20 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +require_once('archive.php'); + +class Test_Archive_ZIP extends Test_Archive{ + protected function getExisting(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + return new OC_Archive_ZIP($dir.'/data.zip'); + } + + protected function getNew(){ + return new OC_Archive_ZIP(OC_Helper::tmpFile('.zip')); + } +} diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php new file mode 100644 index 0000000000..68c445d5d7 --- /dev/null +++ b/apps/files_encryption/appinfo/app.php @@ -0,0 +1,19 @@ + + + files_encryption + Encryption + Server side encryption of files + 0.1 + AGPL + Robin Appelman + 3 + diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js new file mode 100644 index 0000000000..adbf0c8724 --- /dev/null +++ b/apps/files_encryption/js/settings.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, Robin Appelman + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + + +$(document).ready(function(){ + $('#encryption_blacklist').multiSelect({ + oncheck:blackListChange, + onuncheck:blackListChange, + createText:'...', + }); + + function blackListChange(){ + var blackList=$('#encryption_blacklist').val().join(','); + OC.AppConfig.setValue('files_encryption','type_blacklist',blackList); + } +}) \ No newline at end of file diff --git a/lib/crypt.php b/apps/files_encryption/lib/crypt.php similarity index 51% rename from lib/crypt.php rename to apps/files_encryption/lib/crypt.php index 6002067948..0a593b98c4 100644 --- a/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -37,90 +37,111 @@ require_once('Crypt_Blowfish/Blowfish.php'); * This class is for crypting and decrypting */ class OC_Crypt { + static private $bf = null; - static $encription_extension='.encrypted'; + public static function loginListener($params){ + self::init($params['uid'],$params['password']); + } public static function init($login,$password) { - $_SESSION['user_password'] = $password; // save the password as passcode for the encryption - if(OC_User::isLoggedIn()){ - // does key exist? - if(!file_exists(OC_Config::getValue( "datadirectory").'/'.$login.'/encryption.key')){ - OC_Crypt::createkey($_SESSION['user_password']); + $view=new OC_FilesystemView('/'.$login); + OC_FileProxy::$enabled=false; + if(!$view->file_exists('/encryption.key')){// does key exist? + OC_Crypt::createkey($login,$password); + } + $key=$view->file_get_contents('/encryption.key'); + OC_FileProxy::$enabled=true; + $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); + } + + /** + * get the blowfish encryption handeler for a key + * @param string $key (optional) + * @return Crypt_Blowfish + * + * if the key is left out, the default handeler will be used + */ + public static function getBlowfish($key=''){ + if($key){ + return new Crypt_Blowfish($key); + }else{ + if(!isset($_SESSION['enckey'])){ + return false; } + if(!self::$bf){ + self::$bf=new Crypt_Blowfish($_SESSION['enckey']); + } + return self::$bf; } } + public static function createkey($username,$passcode) { + // generate a random key + $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999); + // encrypt the key with the passcode of the user + $enckey=OC_Crypt::encrypt($key,$passcode); - public static function createkey($passcode) { - if(OC_User::isLoggedIn()){ - // generate a random key - $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999); - - // encrypt the key with the passcode of the user - $enckey=OC_Crypt::encrypt($key,$passcode); - - // Write the file - $username=OC_USER::getUser(); - @file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey ); - } + // Write the file + $proxyEnabled=OC_FileProxy::$enabled; + OC_FileProxy::$enabled=false; + $view=new OC_FilesystemView('/'.$username); + $view->file_put_contents('/encryption.key',$enckey); + OC_FileProxy::$enabled=$proxyEnabled; } - public static function changekeypasscode( $newpasscode) { + public static function changekeypasscode($oldPassword, $newPassword) { if(OC_User::isLoggedIn()){ - $username=OC_USER::getUser(); + $username=OC_USER::getUser(); + $view=new OC_FilesystemView('/'.$username); // read old key - $key=file_get_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key'); + $key=$view->file_get_contents('/encryption.key'); // decrypt key with old passcode - $key=OC_Crypt::decrypt($key, $_SESSION['user_password']); + $key=OC_Crypt::decrypt($key, $oldPassword); // encrypt again with new passcode - $key=OC_Crypt::encrypt($key,$newpassword); + $key=OC_Crypt::encrypt($key, $newPassword); // store the new key - file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $key ); - - $_SESSION['user_password']=$newpasscode; + $view->file_put_contents('/encryption.key', $key ); } } /** * @brief encrypts an content * @param $content the cleartext message you want to encrypt - * @param $key the encryption key + * @param $key the encryption key (optional) * @returns encrypted content * * This function encrypts an content */ - public static function encrypt( $content, $key) { - $bf = new Crypt_Blowfish($key); + public static function encrypt( $content, $key='') { + $bf = self::getBlowfish($key); return($bf->encrypt($content)); } + /** + * @brief decryption of an content + * @param $content the cleartext message you want to decrypt + * @param $key the encryption key (optional) + * @returns cleartext content + * + * This function decrypts an content + */ + public static function decrypt( $content, $key='') { + $bf = self::getBlowfish($key); + return($bf->decrypt($content)); + } - /** - * @brief decryption of an content - * @param $content the cleartext message you want to decrypt - * @param $key the encryption key - * @returns cleartext content - * - * This function decrypts an content - */ - public static function decrypt( $content, $key) { - $bf = new Crypt_Blowfish($key); - return($bf->encrypt($contents)); - } - - - /** - * @brief encryption of a file - * @param $filename - * @param $key the encryption key - * - * This function encrypts a file - */ + /** + * @brief encryption of a file + * @param $filename + * @param $key the encryption key + * + * This function encrypts a file + */ public static function encryptfile( $filename, $key) { $handleread = fopen($filename, "rb"); if($handleread<>FALSE) { @@ -158,8 +179,28 @@ class OC_Crypt { } fclose($handleread); } - - - - + + /** + * encrypt data in 8192b sized blocks + */ + public static function blockEncrypt($data){ + $result=''; + while(strlen($data)){ + $result=self::encrypt(substr($data,0,8192)); + $data=substr($data,8192); + } + return $result; + } + + /** + * decrypt data in 8192b sized blocks + */ + public static function blockDecrypt($data){ + $result=''; + while(strlen($data)){ + $result=self::decrypt(substr($data,0,8192)); + $data=substr($data,8192); + } + return $result; + } } diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php new file mode 100644 index 0000000000..86583096f1 --- /dev/null +++ b/apps/files_encryption/lib/cryptstream.php @@ -0,0 +1,153 @@ +. + * + */ + +/** + * transparently encrypted filestream + * + * you can use it as wrapper around an existing stream by setting OC_CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream) + * and then fopen('crypt://streams/foo'); + */ + +class OC_CryptStream{ + public static $sourceStreams=array(); + private $source; + private $path; + private $readBuffer;//for streams that dont support seeking + private $meta=array();//header/meta for source stream + + public function stream_open($path, $mode, $options, &$opened_path){ + $path=str_replace('crypt://','',$path); + if(dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])){ + $this->source=self::$sourceStreams[basename($path)]['stream']; + $this->path=self::$sourceStreams[basename($path)]['path']; + }else{ + $this->path=$path; + OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG); + OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file + $this->source=OC_FileSystem::fopen($path,$mode); + OC_FileProxy::$enabled=true; + if(!is_resource($this->source)){ + OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR); + } + } + if(is_resource($this->source)){ + $this->meta=stream_get_meta_data($this->source); + } + return is_resource($this->source); + } + + public function stream_seek($offset, $whence=SEEK_SET){ + fseek($this->source,$offset,$whence); + } + + public function stream_tell(){ + return ftell($this->source); + } + + public function stream_read($count){ + $pos=0; + $currentPos=ftell($this->source); + $offset=$currentPos%8192; + $result=''; + if($offset>0){ + if($this->meta['seekable']){ + fseek($this->source,-$offset,SEEK_CUR);//if seeking isnt supported the internal read buffer will be used + }else{ + $pos=strlen($this->readBuffer); + $result=$this->readBuffer; + } + } + while($count>$pos){ + $data=fread($this->source,8192); + $pos+=8192; + if(strlen($data)){ + $result.=OC_Crypt::decrypt($data); + } + } + if(!$this->meta['seekable']){ + $this->readBuffer=substr($result,$count); + } + return substr($result,0,$count); + } + + public function stream_write($data){ + $length=strlen($data); + $written=0; + $currentPos=ftell($this->source); + if($currentPos%8192!=0){ + //make sure we always start on a block start + fseek($this->source,-($currentPos%8192),SEEK_CUR); + $encryptedBlock=fread($this->source,8192); + fseek($this->source,-($currentPos%8192),SEEK_CUR); + $block=OC_Crypt::decrypt($encryptedBlock); + $data=substr($block,0,$currentPos%8192).$data; + } + while(strlen($data)>0){ + if(strlen($data)<8192){ + //fetch the current data in that block and append it to the input so we always write entire blocks + $oldPos=ftell($this->source); + $encryptedBlock=fread($this->source,8192); + fseek($this->source,$oldPos); + $block=OC_Crypt::decrypt($encryptedBlock); + $data.=substr($block,strlen($data)); + } + $encrypted=OC_Crypt::encrypt(substr($data,0,8192)); + fwrite($this->source,$encrypted); + $data=substr($data,8192); + } + return $length; + } + + public function stream_set_option($option,$arg1,$arg2){ + switch($option){ + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->source,$arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->source,$arg1,$arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->source,$arg1,$arg2); + } + } + + public function stream_stat(){ + return fstat($this->source); + } + + public function stream_lock($mode){ + flock($this->source,$mode); + } + + public function stream_flush(){ + return fflush($this->source); + } + + public function stream_eof(){ + return feof($this->source); + } + + public function stream_close(){ + OC_FileCache::put($this->path,array('encrypted'=>true)); + return fclose($this->source); + } +} \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php new file mode 100644 index 0000000000..c1c26d7754 --- /dev/null +++ b/apps/files_encryption/lib/proxy.php @@ -0,0 +1,115 @@ +. +* +*/ + +/** + * transparent encryption + */ + +class OC_FileProxy_Encryption extends OC_FileProxy{ + private static $blackList=null; //mimetypes blacklisted from encryption + private static $metaData=array(); //metadata cache + + /** + * check if a file should be encrypted during write + * @param string $path + * @return bool + */ + private static function shouldEncrypt($path){ + if(is_null(self::$blackList)){ + self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); + } + if(self::isEncrypted($path)){ + return true; + } + $extention=substr($path,strrpos($path,'.')+1); + if(array_search($extention,self::$blackList)===false){ + return true; + } + } + + /** + * check if a file is encrypted + * @param string $path + * @return bool + */ + private static function isEncrypted($path){ + if(isset(self::$metaData[$path])){ + $metadata=self::$metaData[$path]; + }else{ + $metadata=OC_FileCache::getCached($path); + self::$metaData[$path]=$metadata; + } + return (bool)$metadata['encrypted']; + } + + public function preFile_put_contents($path,&$data){ + if(self::shouldEncrypt($path)){ + if (!is_resource($data)) {//stream put contents should have been converter to fopen + $data=OC_Crypt::blockEncrypt($data); + OC_FileCache::put($path,array('encrypted'=>true)); + } + } + } + + public function postFile_get_contents($path,$data){ + if(self::isEncrypted($path)){ + $data=OC_Crypt::blockDecrypt($data); + } + return $data; + } + + public function postFopen($path,&$result){ + if(!$result){ + return $result; + } + $meta=stream_get_meta_data($result); + if(self::isEncrypted($path)){ + fclose($result); + $result=fopen('crypt://'.$path,$meta['mode']); + }elseif(self::shouldEncrypt($path) and $meta['mode']!='r' and $meta['mode']!='rb'){ + if(OC_Filesystem::file_exists($path) and OC_Filesystem::filesize($path)>0){ + //first encrypt the target file so we don't end up with a half encrypted file + OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG); + $tmp=fopen('php://temp'); + while(!feof($result)){ + $chunk=fread($result,8192); + if($chunk){ + fwrite($tmp,$chunk); + } + } + fclose($result); + OC_Filesystem::file_put_contents($path,$tmp); + fclose($tmp); + } + $result=fopen('crypt://'.$path,$meta['mode']); + } + return $result; + } + + public function postGetMimeType($path,$mime){ + if(self::isEncrypted($path)){ + $mime=OC_Helper::getMimeType('crypt://'.$path,'w'); + } + return $mime; + } +} diff --git a/apps/files_encryption/settings.php b/apps/files_encryption/settings.php new file mode 100644 index 0000000000..396ad1ba78 --- /dev/null +++ b/apps/files_encryption/settings.php @@ -0,0 +1,16 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +$tmpl = new OC_Template( 'files_encryption', 'settings'); +$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); +$tmpl->assign('blacklist',$blackList); + +OC_Util::addScript('files_encryption','settings'); +OC_Util::addScript('core','multiselect'); + +return $tmpl->fetchPage(); diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php new file mode 100644 index 0000000000..724a03836a --- /dev/null +++ b/apps/files_encryption/templates/settings.php @@ -0,0 +1,11 @@ +
    +
    + t('Encryption'); ?> + t("Exclude the following file types from encryption"); ?> + +
    +
    diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php index cb641e68a8..845659588e 100644 --- a/apps/files_sharing/sharedstorage.php +++ b/apps/files_sharing/sharedstorage.php @@ -58,7 +58,7 @@ class OC_Filestorage_Shared extends OC_Filestorage { } public function mkdir($path) { - if ($path == "" || $path == "/" || !$this->is_writeable($path)) { + if ($path == "" || $path == "/" || !$this->is_writable($path)) { return false; } else { $source = $this->getSource($path); @@ -79,7 +79,6 @@ class OC_Filestorage_Shared extends OC_Filestorage { if ($path == "" || $path == "/") { $path = $this->datadir.$path; $sharedItems = OC_Share::getItemsInFolder($path); - global $FAKEDIRS; $files = array(); foreach ($sharedItems as $item) { // If item is in the root of the shared storage provider and the item exists add it to the fakedirs @@ -87,7 +86,7 @@ class OC_Filestorage_Shared extends OC_Filestorage { $files[] = basename($item['target']); } } - $FAKEDIRS['shared'] = $files; + OC_FakeDirStream::$dirs['shared']=$files; return opendir('fakedir://shared'); } else { $source = $this->getSource($path); @@ -281,14 +280,6 @@ class OC_Filestorage_Shared extends OC_Filestorage { } } - public function readfile($path) { - $source = $this->getSource($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->readfile($this->getInternalPath($source)); - } - } - public function filectime($path) { if ($path == "" || $path == "/") { $ctime = 0; @@ -514,7 +505,13 @@ class OC_Filestorage_Shared extends OC_Filestorage { return $storage->getLocalFile($this->getInternalPath($source)); } } - + public function touch($path, $mtime=null){ + $source = $this->getSource($path); + if ($source) { + $storage = OC_Filesystem::getStorage($source); + return $storage->touch($this->getInternalPath($source),$time); + } + } } ?> diff --git a/apps/files_texteditor/ajax/savefile.php b/apps/files_texteditor/ajax/savefile.php index 589428d186..57a948478f 100644 --- a/apps/files_texteditor/ajax/savefile.php +++ b/apps/files_texteditor/ajax/savefile.php @@ -28,7 +28,7 @@ require_once('../../../lib/base.php'); OC_JSON::checkLoggedIn(); // Get paramteres -$filecontents = htmlspecialchars_decode($_POST['filecontents']); +$filecontents = $_POST['filecontents']; $path = isset($_POST['path']) ? $_POST['path'] : ''; $mtime = isset($_POST['mtime']) ? $_POST['mtime'] : ''; diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index e45652b6ef..02d39b9843 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -11,37 +11,43 @@ function getFileExtension(file){ function setSyntaxMode(ext){ // Loads the syntax mode files and tells the editor var filetype = new Array(); - // Todo finish these - filetype["h"] = "c_cpp"; - filetype["c"] = "c_cpp"; - filetype["clj"] = "clojure"; - filetype["coffee"] = "coffee"; // coffescript can be compiled to javascript - filetype["coldfusion"] = "cfc"; - filetype["cpp"] = "c_cpp"; - filetype["cs"] = "csharp"; + // add file extensions like this: filetype["extension"] = "filetype": + filetype["h"] = "c_cpp"; + filetype["c"] = "c_cpp"; + filetype["clj"] = "clojure"; + filetype["coffee"] = "coffee"; // coffescript can be compiled to javascript + filetype["coldfusion"] = "cfc"; + filetype["cpp"] = "c_cpp"; + filetype["cs"] = "csharp"; filetype["css"] = "css"; - filetype["groovy"] = "groovy"; - filetype["haxe"] = "hx"; + filetype["groovy"] = "groovy"; + filetype["haxe"] = "hx"; filetype["html"] = "html"; - filetype["java"] = "java"; + filetype["java"] = "java"; filetype["js"] = "javascript"; - filetype["json"] = "json"; - filetype["latex"] = "latex"; - filetype["lua"] = "lua"; - filetype["markdown"] = "markdown"; // also: .md .markdown .mdown .mdwn - filetype["ml"] = "ocaml"; - filetype["mli"] = "ocaml"; + filetype["json"] = "json"; + filetype["latex"] = "latex"; + filetype["ly"] = "latex"; + filetype["ily"] = "latex"; + filetype["lua"] = "lua"; + filetype["markdown"] = "markdown"; + filetype["md"] = "markdown"; + filetype["mdown"] = "markdown"; + filetype["mdwn"] = "markdown"; + filetype["mkd"] = "markdown"; + filetype["ml"] = "ocaml"; + filetype["mli"] = "ocaml"; filetype["pl"] = "perl"; filetype["php"] = "php"; filetype["powershell"] = "ps1"; filetype["py"] = "python"; filetype["rb"] = "ruby"; - filetype["scad"] = "scad"; // seems to be something like 3d model files printed with e.g. reprap - filetype["scala"] = "scala"; - filetype["scss"] = "scss"; // "sassy css" - filetype["sql"] = "sql"; - filetype["svg"] = "svg"; - filetype["textile"] = "textile"; // related to markdown + filetype["scad"] = "scad"; // seems to be something like 3d model files printed with e.g. reprap + filetype["scala"] = "scala"; + filetype["scss"] = "scss"; // "sassy css" + filetype["sql"] = "sql"; + filetype["svg"] = "svg"; + filetype["textile"] = "textile"; // related to markdown filetype["xml"] = "xml"; if(filetype[ext]!=null){ @@ -142,7 +148,7 @@ function doFileSave(){ // Show saving spinner $("#editor_save").die('click',doFileSave); $('#save_result').remove(); - $('#editor_save').text(t('files_texteditor','Saving...'));//after(''); + $('#editor_save').text(t('files_texteditor','Saving...')); // Get the data var filecontents = window.aceEditor.getSession().getValue(); // Send the data @@ -192,6 +198,7 @@ function showFileEditor(dir,filename){ $('#editor').attr('data-filename', filename); window.aceEditor = ace.edit("editor"); aceEditor.setShowPrintMargin(false); + aceEditor.getSession().setUseWrapMode(true); if(result.data.write=='false'){ aceEditor.setReadOnly(true); } diff --git a/apps/gallery/ajax/galleryOp.php b/apps/gallery/ajax/galleryOp.php index 5ac6d29510..459c30f6ac 100644 --- a/apps/gallery/ajax/galleryOp.php +++ b/apps/gallery/ajax/galleryOp.php @@ -21,6 +21,7 @@ * */ +header('Content-type: text/html; charset=UTF-8') ; require_once('../../../lib/base.php'); OC_JSON::checkLoggedIn(); @@ -41,34 +42,28 @@ function handleRemove($name) { function handleGetThumbnails($albumname) { OC_Response::enableCaching(3600 * 24); // 24 hour - $thumbnail = OC::$CONFIG_DATADIRECTORY.'/../gallery/'.$albumname.'.png'; + $thumbnail = OC::$CONFIG_DATADIRECTORY.'/../gallery/'.urldecode($albumname).'.png'; header('Content-Type: '.OC_Image::getMimeTypeForFile($thumbnail)); OC_Response::sendFile($thumbnail); } function handleGalleryScanning() { - OC_Gallery_Scanner::cleanup(); - OC_JSON::success(array('albums' => OC_Gallery_Scanner::scan('/'))); + OC_DB::beginTransaction(); + set_time_limit(0); + OC_Gallery_Album::cleanup(); + $eventSource = new OC_EventSource(); + OC_Gallery_Scanner::scan($eventSource); + $eventSource->close(); + OC_DB::commit(); } function handleFilescan($cleanup) { if ($cleanup) OC_Gallery_Album::cleanup(); - $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '').'/'; - $pathlist = OC_Gallery_Scanner::find_paths($root); + $pathlist = OC_Gallery_Scanner::find_paths(); sort($pathlist); OC_JSON::success(array('paths' => $pathlist)); } -function handlePartialCreate($path) { - if (empty($path)) OC_JSON::error(array('cause' => 'No path specified')); - if (!OC_Filesystem::is_dir($path)) OC_JSON::error(array('cause' => 'Invalid path given')); - - $album = OC_Gallery_Album::find(OC_User::getUser(), null, $path); - $albums = array(); - OC_Gallery_Scanner::scanDir($path, $albums); - OC_JSON::success(array('album_details' => $albums)); -} - function handleStoreSettings($root, $order) { if (!OC_Filesystem::file_exists($root)) { OC_JSON::error(array('cause' => 'No such file or directory')); @@ -80,13 +75,45 @@ function handleStoreSettings($root, $order) { } $current_root = OC_Preferences::getValue(OC_User::getUser(),'gallery', 'root', '/'); - $root = trim(rtrim($root, '/')); + $root = trim($root); + $root = rtrim($root, '/').'/'; $rescan = $current_root==$root?'no':'yes'; OC_Preferences::setValue(OC_User::getUser(), 'gallery', 'root', $root); OC_Preferences::setValue(OC_User::getUser(), 'gallery', 'order', $order); OC_JSON::success(array('rescan' => $rescan)); } +function handleGetGallery($path) { + $a = array(); + $root = OC_Preferences::getValue(OC_User::getUser(),'gallery', 'root', '/'); + $path = utf8_decode(rtrim($root.$path,'/')); + if($path == '') $path = '/'; + $pathLen = strlen($path); + $result = OC_Gallery_Album::find(OC_User::getUser(), null, $path); + $album_details = $result->fetchRow(); + + $result = OC_Gallery_Album::find(OC_User::getUser(), null, null, $path); + + while ($r = $result->fetchRow()) { + $album_name = $r['album_name']; + $size=OC_Gallery_Album::getAlbumSize($r['album_id']); + // this is a fallback mechanism and seems expensive + if ($size == 0) $size = OC_Gallery_Album::getIntermediateGallerySize($r['album_path']); + + $a[] = array('name' => utf8_encode($album_name), 'numOfItems' => min($size, 10),'path'=>substr($r['album_path'], $pathLen)); + } + + $result = OC_Gallery_Photo::find($album_details['album_id']); + + $p = array(); + + while ($r = $result->fetchRow()) { + $p[] = utf8_encode($r['file_path']); + } + + OC_JSON::success(array('albums'=>$a, 'photos'=>$p)); +} + if ($_GET['operation']) { switch($_GET['operation']) { case 'rename': @@ -103,15 +130,12 @@ if ($_GET['operation']) { case 'scan': handleGalleryScanning(); break; - case 'filescan': - handleFilescan($_GET['cleanup']); - break; - case 'partial_create': - handlePartialCreate(urldecode($_GET['path'])); - break; case 'store_settings': handleStoreSettings($_GET['root'], $_GET['order']); break; + case 'get_gallery': + handleGetGallery($_GET['path']); + break; default: OC_JSON::error(array('cause' => 'Unknown operation')); } diff --git a/apps/gallery/ajax/getAlbums.php b/apps/gallery/ajax/getAlbums.php deleted file mode 100644 index 9e9c6ef496..0000000000 --- a/apps/gallery/ajax/getAlbums.php +++ /dev/null @@ -1,41 +0,0 @@ -. -* -*/ - -require_once('../../../lib/base.php'); -OC_JSON::checkLoggedIn(); -OC_JSON::checkAppEnabled('gallery'); - -$a = array(); - -$result = OC_Gallery_Album::find(OC_User::getUser()); - -while ($r = $result->fetchRow()) { - $album_name = $r['album_name']; - $tmp_res = OC_Gallery_Photo::find($r['album_id']); - - $a[] = array('name' => $album_name, 'numOfItems' => min($tmp_res->numRows(), 10), 'bgPath' => OC::$WEBROOT.'/data/'.OC_User::getUser().'/gallery/'.$album_name.'.png'); -} - -OC_JSON::success(array('albums'=>$a)); - -?> diff --git a/apps/gallery/appinfo/app.php b/apps/gallery/appinfo/app.php index b8de32ea58..1e5e27d408 100644 --- a/apps/gallery/appinfo/app.php +++ b/apps/gallery/appinfo/app.php @@ -40,8 +40,8 @@ OC_App::addNavigationEntry( array( 'icon' => OC_Helper::imagePath('core', 'places/picture.svg'), 'name' => $l->t('Gallery'))); - class OC_GallerySearchProvider extends OC_Search_Provider{ - function search($query){ + class OC_GallerySearchProvider implements OC_Search_Provider{ + static function search($query){ $stmt = OC_DB::prepare('SELECT * FROM *PREFIX*gallery_albums WHERE uid_owner = ? AND album_name LIKE ?'); $result = $stmt->execute(array(OC_User::getUser(),'%'.$query.'%')); $results=array(); @@ -52,7 +52,7 @@ OC_App::addNavigationEntry( array( } } -new OC_GallerySearchProvider(); +OC_Search::registerProvider('OC_GallerySearchProvider'); require_once('apps/gallery/lib/hooks_handlers.php'); ?> diff --git a/apps/gallery/appinfo/database.xml b/apps/gallery/appinfo/database.xml index db88e4c1b5..62fdbee9cd 100644 --- a/apps/gallery/appinfo/database.xml +++ b/apps/gallery/appinfo/database.xml @@ -11,9 +11,9 @@ album_id integer 0 - true - 1 - 4 + true + 1 + 4 uid_owner @@ -27,12 +27,18 @@ true 100 - - album_path - text - true - 100 - + + album_path + text + true + 100 + + + parent_path + text + true + 100 + @@ -42,16 +48,16 @@ photo_idinteger0 - true - 1 - 4 + true + 1 + 4 album_id integer 0 - true - 4 + true + 4 file_path diff --git a/apps/gallery/appinfo/info.xml b/apps/gallery/appinfo/info.xml index 9aecb0c781..19c5dc8b25 100644 --- a/apps/gallery/appinfo/info.xml +++ b/apps/gallery/appinfo/info.xml @@ -2,7 +2,7 @@ gallery Gallery - 0.3 + 0.4 AGPL Bartek Przybylski 2 diff --git a/apps/gallery/css/styles.css b/apps/gallery/css/styles.css index da94f9ac9e..013cd1b262 100644 --- a/apps/gallery/css/styles.css +++ b/apps/gallery/css/styles.css @@ -1,13 +1,14 @@ div#gallery_list { margin: 4.5em 2em 0 2em; } div#gallery_list.leftcontent { padding-top: 15pt; margin: 0; position: absolute; bottom:0px; text-align: center; overflow: auto; } -div.gallery_album_box { width: 200px; position:relative; text-align: center; border: 0; display: inline-block; margin: 5pt; vertical-align: top; padding: 5px 5px 5px 5px; position: relative; -webkit-transition: color 0.5s ease-in-out; -o-transition: color 0.5s ease-in-out; -moz-transition: color 0.5s ease-in-out;color: #BBB;} -div.gallery_album_box h1 { font-size: 9pt; font-family: Verdana; } -div.gallery_album_decoration { width: 200px; position: absolute; border: 0; height: 20px; top: 5px; text-align:right; vertical-align:middle; background-color: #eee; opacity: 0; -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; border-bottom-right-radius: 7px; border-bottom-left-radius: 7px; -moz-border-radius-bottomright: 7px; -moz-border-radius-bottomleft:7px;} -div.gallery_album_box:hover { color: black; } -div.gallery_album_box:hover div.gallery_album_decoration { opacity: 0.7;} +div.gallery_box { width: 200px; position:relative; text-align: center; border: 0; display: inline-block; margin: 5pt; vertical-align: top; padding: 5px 5px 5px 5px; position: relative; -webkit-transition: color 0.5s ease-in-out; -o-transition: color 0.5s ease-in-out; -moz-transition: color 0.5s ease-in-out;color: #BBB;} +div.album {border: 1px solid #e0e0e0; border-radius: 7px;} +div.gallery_box h1 { font-size: 9pt; font-family: Verdana; } +div.gallery_album_decoration { width: 200px; position: absolute; border: 0; height: 20px; top: 5px; text-align:right; vertical-align:middle; background-color: #eee; opacity: 0; -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; } +div.gallery_box:hover { color: black; } +div.gallery_box:hover div.gallery_album_decoration { opacity: 0.7;} div.gallery_album_decoration a {padding: 0 4pt; cursor: pointer;} div.gallery_album_cover { width: 200px; height: 200px; border: 0; padding: 0; position:relative;} -div.gallery_album_box:hover div.gallery_control_overlay { opacity:0.5 } +div.gallery_box:hover div.gallery_control_overlay { opacity:0.5 } div.gallery_control_overlay a { color:white; } #gallery_images.rightcontent { padding:10px 5px; bottom: 0px; overflow: auto; right:0px} #scan { position:absolute; right:13.5em; top:0em; } diff --git a/apps/gallery/img/loading.gif b/apps/gallery/img/loading.gif new file mode 100644 index 0000000000..5b33f7e54f Binary files /dev/null and b/apps/gallery/img/loading.gif differ diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js index 4ddac2f211..8cafce35f9 100644 --- a/apps/gallery/js/album_cover.js +++ b/apps/gallery/js/album_cover.js @@ -1,190 +1,109 @@ var actual_cover; -$(document).ready(function() { - $.getJSON('ajax/getAlbums.php', function(r) { - if (r.status == 'success') { - for (var i in r.albums) { - var a = r.albums[i]; - Albums.add(a.name, a.numOfItems); - } - var targetDiv = document.getElementById('gallery_list'); - if (targetDiv) { - $(targetDiv).html(''); - Albums.display(targetDiv); - $('#gallery_list').sortable({revert:true}); - $('.gallery_album_box').each(function(i, e) { - $(e).draggable({connectToSortable: '#gallery_list', handle: '.dummy'}) - }); - } else { - alert('Error occured: no such layer `gallery_list`'); - } - } else { - alert('Error occured: ' + r.message); - } - }); -}); +var paths = []; +var crumbCount = 0; +$(document).ready(returnToElement(0)); -function createNewAlbum() { - var name = prompt("album name", ""); - if (name != null && name != "") { - $.getJSON("ajax/createAlbum.php", {album_name: name}, function(r) { - if (r.status == "success") { - var v = ''; - $('div#gallery_list').append(v); - } - }); - } +function returnToElement(num) { + while (crumbCount != num) { + $('#g-album-navigation .last').remove(); + $('#g-album-navigation .crumb :last').parent().addClass('last'); + crumbCount--; + paths.pop(); + } + var p=''; + for (var i in paths) p += paths[i]+'/'; + $('#g-album-loading').show(); + $.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'get_gallery', path: p }, albumClickHandler); +} + +function albumClick(title) { + paths.push(title); + crumbCount++; + var p = ''; + for (var i in paths) p += paths[i]+'/'; + $('#g-album-loading').show(); + $.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'get_gallery', path: p }, function(r) { + albumClickHandler(r); + if ($('#g-album-navigation :last-child')) + $('#g-album-navigation :last-child').removeClass('last'); + $('#g-album-navigation').append(''); + }); +} + +function albumClickHandler(r) { + Albums.photos = []; + Albums.albums = []; + if (r.status == 'success') { + for (var i in r.albums) { + var a = r.albums[i]; + Albums.add(a.name, a.numOfItems,a.path); + } + for (var i in r.photos) { + Albums.photos.push(r.photos[i]); + } + var targetDiv = document.getElementById('gallery_list'); + if (targetDiv) { + $(targetDiv).html(''); + Albums.display(targetDiv); + //$('#gallery_list').sortable({revert:true}); + $('.album').each(function(i, el) { + $(el).click(albumClick.bind(null,$(el).attr('title'))); + //$(el).draggable({connectToSortable: '#gallery_list', handle: '.dummy'}); + }); + } else { + OC.dialogs.alert(t('gallery', 'Error: no such layer `gallery_list`'), t('gallery', 'Internal error')); + } + } else { + OC.dialogs.alert(t('gallery', 'Error: ') + r.message, t('gallery', 'Internal error')); + } + $('#g-album-loading').hide(); } var albumCounter = 0; var totalAlbums = 0; function scanForAlbums(cleanup) { - cleanup = cleanup?true:false; - var albumCounter = 0; - var totalAlbums = 0; - $('#g-scan-button').attr('disabled', 'true'); - $.getJSON('ajax/galleryOp.php?operation=filescan', {cleanup: cleanup}, function(r) { - - if (r.status == 'success') { - totalAlbums = r.paths.length; - if (totalAlbums == 0) { - $('#notification').text(t('gallery', "No photos found")).fadeIn().slideDown().delay(3000).fadeOut().slideUp(); - return; - } - $('#scanprogressbar').progressbar({ value: (albumCounter/totalAlbums)*100 }).fadeIn(); - for(var a in r.paths) { - $.getJSON('ajax/galleryOp.php?operation=partial_create&path='+r.paths[a], function(r) { - - if (r.status == 'success') { - Albums.add(r.album_details.albumName, r.album_details.imagesCount); - } - - albumCounter++; - $('#scanprogressbar').progressbar({ value: (albumCounter/totalAlbums)*100 }); - if (albumCounter == totalAlbums) { - $('#scanprogressbar').fadeOut(); - var targetDiv = document.getElementById('gallery_list'); - if (targetDiv) { - targetDiv.innerHTML = ''; - Albums.display(targetDiv); - } else { - alert('Error occured: no such layer `gallery_list`'); - } - $('#g-scan-button').attr('disabled', null); - } - }); - } - } else { - alert('Error occured: ' + r.message); - } - }); -} - -function galleryRemove(albumName) { - // a workaround for a flaw in the demo system (http://dev.jqueryui.com/ticket/4375), ignore! - $( "#dialog:ui-dialog" ).dialog( "destroy" ); - $('#albumName', $("#dialog-confirm")).text(albumName); - - $( '#dialog-confirm' ).dialog({ - resizable: false, - height:150, - buttons: [{ - text: t('gallery', 'OK'), - click: function() { - $.getJSON("ajax/galleryOp.php", {operation: "remove", name: albumName}, function(r) { - if (r.status == "success") { - $(".gallery_album_box").filterAttr('data-album',albumName).remove(); - Albums.remove(albumName); - } else { - alert("Error: " + r.cause); - } - $('#dialog-confirm').dialog('close'); - }); - }}, - { - text: t('gallery', 'Cancel'), - click: function() { - $( this ).dialog( 'close' ); - }}] - }); -} - -function galleryRename(name) { - $('#name', $('#dialog-form')).val(name); - $( "#dialog-form" ).dialog({ - height: 140, - width: 350, - modal: false, - buttons: [{ - text: t('gallery', 'Change name'), - click: function() { - var newname = $('#name', $('#dialog-form')).val(); - if (newname == name || newname == '') { - $(this).dialog("close"); - return; - } - if (Albums.find(newname)) { - alert("Album ", newname, " exists"); - $(this).dialog("close"); - return; - } - $.getJSON('ajax/galleryOp.php', {operation: 'rename', oldname: name, newname: newname}, function(r) { - if (r.status == "success") { - Albums.rename($(".gallery_album_box").filterAttr('data-album',name), newname); - } else { - alert("Error: " + r.cause); - } - $('#dialog-form').dialog('close'); - }); - - } - }, - { - text: t('gallery', 'Cancel'), - click: function() { - $( this ).dialog('close'); - } - } - ], - }); + Scanner.scanAlbums(); + return; } function settings() { - $( '#g-dialog-settings' ).dialog({ - height: 180, - width: 350, - modal: false, - buttons: [{ - text: t('gallery', 'Apply'), - click: function() { - var scanning_root = $('#g-scanning-root').val(); - var disp_order = $('#g-display-order option:selected').val(); - if (scanning_root == '') { - alert('Scanning root cannot be empty'); - return; - } - $.getJSON('ajax/galleryOp.php', {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) { - if (r.status == 'success') { - if (r.rescan == 'yes') { - $('#g-dialog-settings').dialog('close'); - Albums.clear(document.getElementById('gallery_list')); - scanForAlbums(true); - return; - } - } else { - alert('Error: ' + r.cause); - return; - } - $('#g-dialog-settings').dialog('close'); - }); - } - }, - { - text: t('gallery', 'Cancel'), - click: function() { - $(this).dialog('close'); - } - } - ], - }); + $( '#g-dialog-settings' ).dialog({ + height: 180, + width: 350, + modal: false, + buttons: [ + { + text: t('gallery', 'Apply'), + click: function() { + var scanning_root = $('#g-scanning-root').val(); + var disp_order = $('#g-display-order option:selected').val(); + if (scanning_root == '') { + alert('Scanning root cannot be empty'); + return; + } + $.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) { + if (r.status == 'success') { + if (r.rescan == 'yes') { + $('#g-dialog-settings').dialog('close'); + Albums.clear(document.getElementById('gallery_list')); + scanForAlbums(true); + return; + } + } else { + alert('Error: ' + r.cause); + return; + } + $('#g-dialog-settings').dialog('close'); + }); + } + }, + { + text: t('gallery', 'Cancel'), + click: function() { + $(this).dialog('close'); + } + } + ], + }); } diff --git a/apps/gallery/js/albums.js b/apps/gallery/js/albums.js index 987412f28e..d3326841cc 100644 --- a/apps/gallery/js/albums.js +++ b/apps/gallery/js/albums.js @@ -1,89 +1,98 @@ Albums={ - // album item in this array should look as follow - // {name: string, - // numOfCovers: int} - // - // previews array should be an array of base64 decoded images - // to display to user as preview picture when scrolling throught - // the album cover - albums:new Array(), - // add simply adds new album to internal structure - // however albums names must be unique so other - // album with the same name wont be insered, - // and false will be returned - // true on success - add: function(album_name, num) { - if (Albums.albums[album_name] != undefined) return false; - Albums.albums[album_name] = {name: album_name, numOfCovers: num}; - return true; - }, - // remove element with given name - // returns remove element or undefined if no such element was present - remove: function(name) { - var i = -1, tmp = 0; - for (var a in Albums.albums) { - if (a.name == name) { - i = tmp; - break; - } - tmp++; - } - if (i != -1) { - return Albums.albums.splice(i,1); - } - return undefined; - }, - // return element which match given name - // of undefined if such element do not exist - find: function(name) { - return Albums.albums[name]; - }, - // displays gallery in linear representation - // on given element, and apply default styles for gallery - display: function(element) { - var displayTemplate = ''; - for (var i in Albums.albums) { - var a = Albums.albums[i]; - var local=$(displayTemplate); - local.attr('data-album',a.name); - $(".gallery_album_decoration a.rename", local).bind('click', {name: a.name},function(event){ - event.preventDefault(); - galleryRename(event.data.name); - }); - $(".gallery_album_decoration a.remove", local).bind('click', {name: a.name},function(event){ - event.preventDefault(); - galleryRemove(event.data.name); - }); - $("a.view", local).attr('href','?view='+escape(a.name)); - $('h1',local).text(a.name); - $(".gallery_album_cover", local).attr('title',a.name); - $(".gallery_album_cover", local).css('background-repeat', 'no-repeat'); - $(".gallery_album_cover", local).css('background-position', '0'); - $(".gallery_album_cover", local).css('background-image','url("ajax/galleryOp.php?operation=get_covers&albumname='+escape(a.name)+'")'); - $(".gallery_album_cover", local).mousemove(function(e) { - - var albumMetadata = Albums.find(this.title); - if (albumMetadata == undefined) { - return; - } - var x = Math.floor((e.layerX - this.offsetLeft)/(this.offsetWidth/albumMetadata.numOfCovers)); - x *= this.offsetWidth; - if (x < 0) x=0; - $(this).css('background-position', -x+'px 0'); - }); - $(element).append(local); - } - }, - rename: function(element, new_name) { - if (new_name) { - $(element).attr("data-album", new_name); - $("a.view", element).attr("href", "?view="+new_name); - $("h1", element).text(new_name); + // album item in this array should look as follow + // {name: string, + // numOfCovers: int} + // + // previews array should be an array of base64 decoded images + // to display to user as preview picture when scrolling throught + // the album cover + albums:new Array(), + photos:new Array(), + // add simply adds new album to internal structure + // however albums names must be unique so other + // album with the same name wont be insered, + // and false will be returned + // true on success + add: function(album_name, num,path) { + if (Albums.albums[album_name] != undefined) return false; + Albums.albums[album_name] = {name: album_name, numOfCovers: num, path:path}; + return true; + }, + // remove element with given name + // returns remove element or undefined if no such element was present + remove: function(name) { + var i = -1, tmp = 0; + for (var a in Albums.albums) { + if (a.name == name) { + i = tmp; + break; + } + tmp++; + } + if (i != -1) { + return Albums.albums.splice(i,1); + } + return undefined; + }, + // return element which match given name + // of undefined if such element do not exist + find: function(name) { + return Albums.albums[name]; + }, + // displays gallery in linear representation + // on given element, and apply default styles for gallery + display: function(element) { + var displayTemplate = ''; + for (var i in Albums.albums) { + var a = Albums.albums[i]; + var local=$(displayTemplate); + local.attr('title', a.name); + local.attr('data-path', a.path); + local.attr('data-album',a.name); + $(".gallery_album_decoration a.rename", local).bind('click', {name: a.name},function(name,event){ + event.preventDefault(); + event.stopPropagation(); + galleryRename(name); + }.bind(null,a.name)); + $(".gallery_album_decoration a.remove", local).bind('click', {name: a.name},function(name,event){ + event.preventDefault(); + event.stopPropagation(); + galleryRemove(name); + }.bind(null,a.name)); + $('h1',local).text(decodeURIComponent(escape(a.name))); + $(".gallery_album_cover", local).attr('title',decodeURIComponent(escape(a.name))); + $(".gallery_album_cover", local).css('background-repeat', 'no-repeat'); + $(".gallery_album_cover", local).css('background-position', '0'); + $(".gallery_album_cover", local).css('background-image','url("'+OC.filePath('gallery','ajax','galleryOp.php')+'?operation=get_covers&albumname='+escape(a.name)+'")'); + $(".gallery_album_cover", local).mousemove(function(e) { + var albumMetadata = Albums.find(this.title); + if (albumMetadata == undefined) { + return; + } + var x = Math.floor((e.layerX - this.offsetLeft)/(this.offsetWidth/albumMetadata.numOfCovers)); + x *= this.offsetWidth; + if (x < 0) x=0; + $(this).css('background-position', -x+'px 0'); + }); + $(element).append(local); + } + var photoDisplayTemplate = ''; + for (var i in Albums.photos) { + $(element).append(photoDisplayTemplate.replace("IMGPATH", escape(Albums.photos[i])).replace("URLPATH", escape(Albums.photos[i]))); + } + $("a[rel=images]").fancybox({ + 'titlePosition': 'inside' + }); + }, + rename: function(element, new_name) { + if (new_name) { + $(element).attr("data-album", new_name); + $("a.view", element).attr("href", "?view="+new_name); + $("h1", element).text(new_name); + } + }, + clear: function(element) { + Albums.albums = new Array(); + element.innerHTML = ''; } - }, - clear: function(element) { - Albums.albums = new Array(); - element.innerHTML = ''; - } - } diff --git a/apps/gallery/js/scanner.js b/apps/gallery/js/scanner.js new file mode 100644 index 0000000000..804ee9d229 --- /dev/null +++ b/apps/gallery/js/scanner.js @@ -0,0 +1,34 @@ +Scanner={ + albumsFound:0, + eventSource:null, + albumsScanned:0, + scanAlbums:function(callback){ + $('#scanprogressbar').progressbar({value:0}); + $('#scanprogressbar').fadeIn(); + $('#scan input.start').hide(); + $('#scan input.stop').show(); + Scanner.albumsScanned=0; + Scanner.eventSource=new OC.EventSource(OC.linkTo('gallery', 'ajax/galleryOp.php'),{operation:'scan'}); + Scanner.eventSource.listen('count', function(total){Scanner.albumsFound=total;}); + Scanner.eventSource.listen('scanned', function(data) { + Scanner.albumsScanned++; + var progress=(Scanner.albumsScanned/Scanner.albumsFound)*100; + $('#scanprogressbar').progressbar('value',progress); + }); + Scanner.eventSource.listen('done', function(count){ + $('#scan input.start').show(); + $('#scan input.stop').hide(); + $('#scanprogressbar').fadeOut(); + returnToElement(0); + }); + if (callback) + callback(); + }, + stop:function() { + Scanner.eventSource.close(); + $('#scan input.start').show(); + $('#scan input.stop').hide(); + $('#scanprogressbar').fadeOut(); + } +} + diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php index d1405333ac..070afdd6cd 100644 --- a/apps/gallery/lib/album.php +++ b/apps/gallery/lib/album.php @@ -4,97 +4,113 @@ * ownCloud - gallery application * * @author Bartek Przybylski -* @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com -* +* @copyright 2012 Bartek Przybylski +* * 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 +* 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 . -* +* License along with this library. If not, see . +* */ +require_once('base.php'); + class OC_Gallery_Album { public static function create($owner, $name, $path){ - $stmt = OC_DB::prepare('INSERT INTO *PREFIX*gallery_albums (uid_owner, album_name, album_path) VALUES (?, ?, ?)'); - $stmt->execute(array($owner, $name, $path)); - } - - public static function rename($oldname, $newname, $owner) { - $stmt = OC_DB::prepare('UPDATE *PREFIX*gallery_albums SET album_name=? WHERE uid_owner=? AND album_name=?'); - $stmt->execute(array($newname, $owner, $oldname)); + $stmt = OC_DB::prepare('INSERT INTO *PREFIX*gallery_albums (uid_owner, album_name, album_path, parent_path) VALUES (?, ?, ?, ?)'); + $stmt->execute(array($owner, $name, $path, self::getParentPath($path))); } - public static function cleanup() { - $albums = self::find(OC_User::getUser()); - while ($r = $albums->fetchRow()) { - OC_Gallery_Photo::removeByAlbumId($r['album_id']); - self::remove(OC_User::getUser(), $r['album_name']); - } - } - - public static function remove($owner, $name=null) { - $sql = 'DELETE FROM *PREFIX*gallery_albums WHERE uid_owner = ?'; + public static function cleanup() { + $albums = self::find(OC_User::getUser()); + while ($r = $albums->fetchRow()) { + OC_Gallery_Photo::removeByAlbumId($r['album_id']); + self::remove(OC_User::getUser(), $r['album_name']); + } + } + + public static function getParentPath($path) { + return $path === '/' ? '' : dirname($path); + } + + public static function remove($owner, $name=null, $path=null, $parent=null) { + $sql = 'DELETE FROM *PREFIX*gallery_albums WHERE uid_owner LIKE ?'; $args = array($owner); if (!is_null($name)){ - $sql .= ' AND album_name = ?'; + $sql .= ' AND album_name LIKE ?'; $args[] = $name; } + if (!is_null($path)){ + $sql .= ' AND album_path LIKE ?'; + $args[] = $path; + } + if (!is_null($parent)){ + $sql .= ' AND parent_path LIKE ?'; + $args[] = $parent; + } $stmt = OC_DB::prepare($sql); return $stmt->execute($args); } - public static function removeByPath($path, $owner) { - $album = self::find($owner, null, $path); - $album = $album->fetchRow(); - self::remove($owner, $album['album_name']); - OC_Gallery_Photo::removeByAlbumId($album['album_id']); - // find and remove any gallery which might be stored lower in dir hierarchy - $path = $path.'/%'; - $stmt = OC_DB::prepare('SELECT * FROM *PREFIX*gallery_albums WHERE album_path LIKE ? AND uid_owner = ?'); - $result = $stmt->execute(array($path, $owner)); - while (($album = $result->fetchRow())) { - OC_Gallery_Photo::removeByAlbumId($album['album_id']); - self::remove($owner, $album['album_name']); - } - } + public static function removeByName($owner, $name) { self::remove($ownmer, $name); } + public static function removeByPath($owner, $path) { self::remove($owner, null, $path); } + public static function removeByParentPath($owner, $parent) { self::remove($owner, null, null, $parent); } - public static function find($owner, $name=null, $path=null){ + public static function find($owner, $name=null, $path=null, $parent=null){ $sql = 'SELECT * FROM *PREFIX*gallery_albums WHERE uid_owner = ?'; $args = array($owner); if (!is_null($name)){ $sql .= ' AND album_name = ?'; $args[] = $name; - } - if (!is_null($path)){ - $sql .= ' AND album_path = ?'; - $args[] = $path; - } - $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC'); - $sql .= ' ORDER BY album_name ' . $order; + } + if (!is_null($path)){ + $sql .= ' AND album_path = ?'; + $args[] = $path; + } + if (!is_null($parent)){ + $sql .= ' AND parent_path = ?'; + $args[] = $parent; + } + $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC'); + $sql .= ' ORDER BY album_name ' . $order; $stmt = OC_DB::prepare($sql); return $stmt->execute($args); } - public static function changePath($oldname, $newname, $owner) { - $stmt = OC_DB::prepare('UPDATE *PREFIX*gallery_albums SET album_path=? WHERE uid_owner=? AND album_path=?'); - $stmt->execute(array($newname, $owner, $oldname)); - } + public static function changePath($oldname, $newname, $owner) { + $stmt = OC_DB::prepare('UPDATE *PREFIX*gallery_albums SET album_path=? WHERE uid_owner=? AND album_path=?'); + $stmt->execute(array($newname, $owner, $oldname)); + } - public static function changeThumbnailPath($oldname, $newname) { - require_once('../../../lib/base.php'); - $thumbpath = OC::$CONFIG_DATADIRECTORY.'/../gallery/'; - rename($thumbpath.$oldname.'.png', $thumbpath.$newname.'.png'); - } + public static function changeThumbnailPath($oldname, $newname) { + require_once('../../../lib/base.php'); + $thumbpath = OC::$CONFIG_DATADIRECTORY.'/../gallery/'; + rename($thumbpath.$oldname.'.png', $thumbpath.$newname.'.png'); + } + public static function getAlbumSize($id){ + $sql = 'SELECT COUNT(*) as size FROM *PREFIX*gallery_photos WHERE album_id = ?'; + $stmt = OC_DB::prepare($sql); + $result=$stmt->execute(array($id))->fetchRow(); + return $result['size']; + } + + public static function getIntermediateGallerySize($path) { + $path .= '%'; + $sql = 'SELECT COUNT(*) as size FROM *PREFIX*gallery_photos photos, *PREFIX*gallery_albums albums WHERE photos.album_id = albums.album_id AND uid_owner = ? AND file_path LIKE ?'; + $stmt = OC_DB::prepare($sql); + $result = $stmt->execute(array(OC_User::getUser(), $path))->fetchRow(); + return $result['size']; + } } ?> diff --git a/apps/gallery/lib/hooks_handlers.php b/apps/gallery/lib/hooks_handlers.php index 046866e5c5..2788337bbe 100644 --- a/apps/gallery/lib/hooks_handlers.php +++ b/apps/gallery/lib/hooks_handlers.php @@ -21,9 +21,9 @@ * */ -OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, "OC_Gallery_Hooks_Handlers", "addPhotoFromPath"); -OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_delete, "OC_Gallery_Hooks_Handlers", "removePhoto"); -OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, "OC_Gallery_Hooks_Handlers", "renamePhoto"); +//OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, "OC_Gallery_Hooks_Handlers", "addPhotoFromPath"); +//OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_delete, "OC_Gallery_Hooks_Handlers", "removePhoto"); +//OC_Hook::connect(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, "OC_Gallery_Hooks_Handlers", "renamePhoto"); require_once(OC::$CLASSPATH['OC_Gallery_Album']); require_once(OC::$CLASSPATH['OC_Gallery_Photo']); @@ -37,7 +37,7 @@ class OC_Gallery_Hooks_Handlers { } private static function directoryContainsPhotos($dirpath) { - $dirhandle = opendir(OC::$CONFIG_DATADIRECTORY.$dirpath); + $dirhandle = OC_Filesystem::opendir($dirpath.'/'); if ($dirhandle != FALSE) { while (($filename = readdir($dirhandle)) != FALSE) { if ($filename[0] == '.') continue; @@ -68,7 +68,7 @@ class OC_Gallery_Hooks_Handlers { if (!self::isPhoto($fullpath)) return; - $path = substr($fullpath, 0, strrpos($fullpath, '/')); + $path = dirname($fullpath); if (!self::pathInRoot($path)) return; OC_Gallery_Scanner::scanDir($path, $albums); @@ -76,9 +76,9 @@ class OC_Gallery_Hooks_Handlers { public static function removePhoto($params) { $path = $params[OC_Filesystem::signal_param_path]; - if (OC_Filesystem::is_dir($path) && self::directoryContainsPhotos($path)) { + if (OC_Filesystem::is_dir($path.'/') && self::directoryContainsPhotos($path)) { if(!self::pathInRoot($path)) return; - OC_Gallery_Album::removeByPath($path.'/', OC_User::getUser()); + OC_Gallery_Album::removeByPath($path, OC_User::getUser()); } elseif (self::isPhoto($path)) { OC_Gallery_Photo::removeByPath($path); } @@ -87,11 +87,11 @@ class OC_Gallery_Hooks_Handlers { public static function renamePhoto($params) { $oldpath = $params[OC_Filesystem::signal_param_oldpath]; $newpath = $params[OC_Filesystem::signal_param_newpath]; - if (OC_Filesystem::is_dir($newpath) && self::directoryContainsPhotos($newpath)) { + if (OC_Filesystem::is_dir($newpath.'/') && self::directoryContainsPhotos($newpath)) { OC_Gallery_Album::changePath($oldpath, $newpath, OC_User::getUser()); - } elseif (!self::isPhoto($newpath)) { - $olddir = substr($oldpath, 0, strrpos($oldpath, '/')); - $newdir = substr($newpath, 0, strrpos($newpath, '/')); + } elseif (self::isPhoto($newpath)) { + $olddir = dirname($oldpath); + $newdir = dirname($newpath); if ($olddir == '') $olddir = '/'; if ($newdir == '') $newdir = '/'; if (!self::isPhoto($newpath)) return; @@ -101,25 +101,26 @@ class OC_Gallery_Hooks_Handlers { $oldAlbumId; if ($olddir == $newdir) { // album changing is not needed - $album = OC_Gallery_Album::find(OC_User::getUser(), null, $olddir); - if ($album->numRows() == 0) { - $album = self::createAlbum($newdir); + $albums = OC_Gallery_Album::find(OC_User::getUser(), null, $olddir); + $album = $albums->fetchRow(); + if (!$album) { + $albums = self::createAlbum($newdir); + $album = $albums->fetchRow(); } - $album = $album->fetchRow(); $newAlbumId = $oldAlbumId = $album['album_id']; } else { $newalbum = OC_Gallery_Album::find(OC_User::getUser(), null, $newdir); $oldalbum = OC_Gallery_Album::find(OC_User::getUser(), null, $olddir); - if ($newalbum->numRows() == 0) { + if (!($newalbum = $newalbum->fetchRow())) { $newalbum = self::createAlbum($newdir); + $newalbum = $newalbum->fetchRow(); } - $newalbum = $newalbum->fetchRow(); - if ($oldalbum->numRows() == 0) { + $oldalbum = $oldalbum->fetchRow(); + if (!$oldalbum) { OC_Gallery_Photo::create($newalbum['album_id'], $newpath); return; } - $oldalbum = $oldalbum->fetchRow(); $newAlbumId = $newalbum['album_id']; $oldAlbumId = $oldalbum['album_id']; diff --git a/apps/gallery/lib/photo.php b/apps/gallery/lib/photo.php index 872ecc9488..5e6df8069b 100644 --- a/apps/gallery/lib/photo.php +++ b/apps/gallery/lib/photo.php @@ -13,11 +13,11 @@ * * 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 +* 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 . +* License along with this library. If not, see . * */ @@ -46,38 +46,39 @@ class OC_Gallery_Photo { return $stmt->execute(array($owner, $album_name)); } - public static function removeByPath($path) { - $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE file_path LIKE ?'); - $stmt->execute(array($path)); - } + public static function removeByPath($path) { + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE file_path LIKE ?'); + $stmt->execute(array($path)); + } - public static function removeById($id) { - $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE photo_id = ?'); - $stmt->execute(array($id)); - } + public static function removeById($id) { + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE photo_id = ?'); + $stmt->execute(array($id)); + } - public static function removeByAlbumId($albumid) { - $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE album_id = ?'); - $stmt->execute(array($albumid)); - } + public static function removeByAlbumId($albumid) { + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE album_id = ?'); + $stmt->execute(array($albumid)); + } - public static function changePath($oldAlbumId, $newAlbumId, $oldpath, $newpath) { - $stmt = OC_DB::prepare("UPDATE *PREFIX*gallery_photos SET file_path = ?, album_id = ? WHERE album_id = ? and file_path = ?"); - $stmt->execute(array($newpath, $newAlbumId, $oldAlbumId, $oldpath)); - } + public static function changePath($oldAlbumId, $newAlbumId, $oldpath, $newpath) { + $stmt = OC_DB::prepare("UPDATE *PREFIX*gallery_photos SET file_path = ?, album_id = ? WHERE album_id = ? and file_path = ?"); + $stmt->execute(array($newpath, $newAlbumId, $oldAlbumId, $oldpath)); + } public static function getThumbnail($image_name) { $save_dir = OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/'; $save_dir .= dirname($image_name). '/'; - $thumb_file = $save_dir . $image_name; + $image_path = $image_name; + $thumb_file = $save_dir . basename($image_name); if (file_exists($thumb_file)) { $image = new OC_Image($thumb_file); } else { - $imagePath = OC_Filesystem::getLocalFile($image_name); - if(!file_exists($imagePath)) { + $image_path = OC_Filesystem::getLocalFile($image_path); + if(!file_exists($image_path)) { return null; } - $image = new OC_Image($imagePath); + $image = new OC_Image($image_path); if ($image->valid()) { $image->centerCrop(); $image->resize(200); @@ -93,4 +94,8 @@ class OC_Gallery_Photo { } return null; } + + public static function getGalleryRoot() { + return OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', ''); + } } diff --git a/apps/gallery/lib/scanner.php b/apps/gallery/lib/scanner.php index 64efb006ad..c8825c267e 100644 --- a/apps/gallery/lib/scanner.php +++ b/apps/gallery/lib/scanner.php @@ -4,7 +4,7 @@ * ownCloud - gallery application * * @author Bartek Przybylski -* @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com +* @copyright 2012 Bartek Przybylski * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -13,111 +13,130 @@ * * 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 +* 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 . +* License along with this library. If not, see . * */ -require_once('base.php'); // base lib -require_once('images_utils.php'); - class OC_Gallery_Scanner { - public static function scan($root) { - $albums = array(); - self::scanDir($root, $albums); - return $albums; - } + public static function getGalleryRoot() { + return OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); + } + public static function getScanningRoot() { + return OC_Filesystem::getRoot().self::getGalleryRoot(); + } - public static function cleanUp() { - $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_albums'); - $stmt->execute(array()); - $stmt = OC_DB::prepare('DELETE FROM *PREFIX*gallery_photos'); - $stmt->execute(array()); - } + public static function cleanUp() { + OC_Gallery_Album::cleanup(); + } - public static function createName($name) { - $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); - $name = str_replace('/', '.', str_replace(OC::$CONFIG_DATADIRECTORY, '', $name)); - if (substr($name, 0, strlen($root)) == str_replace('/','.',$root)) { - $name = substr($name, strlen($root)); - } - $name = ($name==='.') ? 'main' : trim($name,'.'); - return $name; - } + public static function createName($name) { + $name = basename($name); + return $name == '.' ? '' : $name; + } - public static function scanDir($path, &$albums) { - $current_album = array('name'=> $path, 'imagesCount' => 0, 'images' => array()); - $current_album['name'] = self::createName($current_album['name']); + // Scan single dir relative to gallery root + public static function scan($eventSource) { + $paths = self::findPaths(); + $eventSource->send('count', count($paths)+1); + $owner = OC_User::getUser(); + foreach ($paths as $path) { + $name = self::createName($path); + $images = self::findFiles($path); - if ($dh = OC_Filesystem::opendir($path)) { - while (($filename = readdir($dh)) !== false) { - $filepath = ($path[strlen($path)-1]=='/'?$path:$path.'/').$filename; - if (substr($filename, 0, 1) == '.') continue; - if (self::isPhoto($path.'/'.$filename)) { - $current_album['images'][] = $filepath; - } - } - } - $current_album['imagesCount'] = count($current_album['images']); - $albums['imagesCount'] = $current_album['imagesCount']; - $albums['albumName'] = $current_album['name']; + $result = OC_Gallery_Album::find($owner, null, $path); + // don't duplicate galleries with same path + if (!($albumId = $result->fetchRow())) { + OC_Gallery_Album::create($owner, $name, $path); + $result = OC_Gallery_Album::find($owner, $name, $path); + $albumId = $result->fetchRow(); + } + $albumId = $albumId['album_id']; + foreach ($images as $img) { + $result = OC_Gallery_Photo::find($albumId, $img); + if (!$result->fetchRow()) + OC_Gallery_Photo::create($albumId, $img); + } + if (count($images)) + self::createThumbnails($name, $images); + $eventSource->send('scanned', ''); + } + self::createIntermediateAlbums(); + $eventSource->send('scanned', ''); + $eventSource->send('done', 1); + } - $result = OC_Gallery_Album::find(OC_User::getUser(), /*$current_album['name']*/ null, $path); - // don't duplicate galleries with same path (bug oc-33) - if ($result->numRows() == 0 && count($current_album['images'])) { - OC_Gallery_Album::create(OC_User::getUser(), $current_album['name'], $path); - $result = OC_Gallery_Album::find(OC_User::getUser(), $current_album['name']); - } - $albumId = $result->fetchRow(); - $albumId = $albumId['album_id']; - foreach ($current_album['images'] as $img) { - $result = OC_Gallery_Photo::find($albumId, $img); - if ($result->numRows() == 0) { - OC_Gallery_Photo::create($albumId, $img); - } - } - if (count($current_album['images'])) { - self::createThumbnail($current_album['name'],$current_album['images']); - } - } + public static function createThumbnails($albumName, $files) { + // create gallery thumbnail + $file_count = min(count($files), 10); + $thumbnail = imagecreatetruecolor($file_count*200, 200); + for ($i = 0; $i < $file_count; $i++) { + $image = OC_Gallery_Photo::getThumbnail($files[$i]); + if ($image && $image->valid()) { + imagecopyresampled($thumbnail, $image->resource(), $i*200, 0, 0, 0, 200, 200, 200, 200); + } + } + imagepng($thumbnail, OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/' . $albumName.'.png'); + } - public static function createThumbnail($albumName, $files) { - $file_count = min(count($files), 10); - $thumbnail = imagecreatetruecolor($file_count*200, 200); - for ($i = 0; $i < $file_count; $i++) { - $image = OC_Gallery_Photo::getThumbnail($files[$i]); - if ($image && $image->valid()) { - imagecopyresampled($thumbnail, $image->resource(), $i*200, 0, 0, 0, 200, 200, 200, 200); - } - } - imagepng($thumbnail, OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/' . $albumName.'.png'); - } + public static function createIntermediateAlbums() { + $paths = self::findPaths(); + for ($i = 1; $i < count($paths); $i++) { + $prevLen = strlen($paths[$i-1]); + if (strncmp($paths[$i-1], $paths[$i], $prevLen)==0) { + $s = substr($paths[$i], $prevLen); + if (strrpos($s, '/') != 0) { + $a = explode('/', trim($s, '/')); + $p = $paths[$i-1]; + foreach ($a as $e) { + $p .= ($p == '/'?'':'/').$e; + OC_Gallery_Album::create(OC_User::getUser(), $e, $p); + $arr = OC_FileCache::searchByMime('image','', OC_Filesystem::getRoot().$p); + $step = floor(count($arr)/10); + if ($step == 0) $step = 1; + $na = array(); + for ($j = 0; $j < count($arr); $j+=$step) { + $na[] = $p.$arr[$j]; + } + if (count($na)) + self::createThumbnails($e, $na); + } + } + } + } + } - public static function isPhoto($filename) { - $ext = strtolower(substr($filename, strrpos($filename, '.')+1)); - return $ext=='png' || $ext=='jpeg' || $ext=='jpg' || $ext=='gif'; - } + public static function isPhoto($filename) { + $ext = strtolower(substr($filename, strrpos($filename, '.')+1)); + return $ext=='png' || $ext=='jpeg' || $ext=='jpg' || $ext=='gif'; + } - public static function find_paths($path) { - $ret = array(); - $dirres; - $addpath = FALSE; - if (($dirres = OC_Filesystem::opendir($path)) == FALSE) return $ret; + public static function findFiles($path) { + $images = OC_FileCache::searchByMime('image','', OC_Filesystem::getRoot().$path); + $new = array(); + foreach ($images as $i) + if (strpos($i, '/',1) === FALSE) + $new[] = $path.$i; + return $new; + } - while (($file = readdir($dirres)) != FALSE) { - if ($file[0] == '.') continue; - if (OC_Filesystem::is_dir($path.$file)) - $ret = array_merge($ret, self::find_paths($path.$file.'/')); - if (self::isPhoto($path.$file)) $addpath = TRUE; - } - - if ($addpath) $ret[] = urlencode($path); - - return $ret; - } + public static function findPaths() { + $images=OC_FileCache::searchByMime('image','', self::getScanningRoot()); + $paths=array(); + foreach($images as $image){ + $path=dirname($image); + $path = self::getGalleryRoot().($path=='.'?'':$path); + if ($path !== '/') $path=rtrim($path,'/'); + if(array_search($path,$paths)===false){ + $paths[]=$path; + } + } + sort($paths); + return $paths; + } } -?> + diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php index 7cc7dad3ac..dc5852733b 100644 --- a/apps/gallery/templates/index.php +++ b/apps/gallery/templates/index.php @@ -1,31 +1,42 @@ -
    -
    -
    - -
    -
    - -
    +
    +
    + + + +
    +
    +
    + main +
    +
    +
    '; + for (var a in fields) { + content += '" + } + content += "
    '+fields[a].text+''; + var type=fields[a].type; + if (type == 'text' || type == 'checkbox' || type == 'password') + content += ''; + content += "
    "; + OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback); + }, + message:function(content, title, dialog_type, buttons, callback) { + var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content'; + var c_id = '#'+c_name; + var d = '
    '+content+'
    '; + $('body').append(d); + var b = []; + switch (buttons) { + case OCdialogs.YES_NO_BUTTONS: + b[1] = {text: t('dialogs', 'No'), click: function(){ if (callback != undefined) callback(false); $(c_id).dialog('close'); }}; + b[0] = {text: t('dialogs', 'Yes'), click: function(){ if (callback != undefined) callback(true); $(c_id).dialog('close');}}; + break; + case OCdialogs.OK_CANCEL_BUTTONS: + b[1] = {text: t('dialogs', 'Cancel'), click: function(){$(c_id).dialog('close'); }}; + case OCdialogs.OK_BUTTON: // fallthrough + var f; + switch(dialog_type) { + case OCdialogs.ALERT_DIALOG: + f = function(){$(c_id).dialog('close'); }; + break; + case OCdialogs.PROMPT_DIALOG: + f = function(){OCdialogs.prompt_ok_handler(callback, c_id)}; + break; + case OCdialogs.FORM_DIALOG: + f = function(){OCdialogs.form_ok_handler(callback, c_id)}; + break; + } + b[0] = {text: t('dialogs', 'Ok'), click: f}; + break; + } + $(c_id).dialog({width: 4*$(document).width()/9, height: $(d).height() + 150, modal: false, buttons: b}); + OCdialogs.dialogs_counter++; + }, + // dialogs buttons types + YES_NO_BUTTONS: 70, + OK_BUTTONS: 71, + OK_CANCEL_BUTTONS: 72, + // dialogs types + ALERT_DIALOG: 80, + INFO_DIALOG: 81, + PROMPT_DIALOG: 82, + FORM_DIALOG: 83, + dialogs_counter: 0, + determineValue: function(element) { + switch ($(element).attr('type')) { + case 'checkbox': return $(element).attr('checked') != undefined; + } + return $(element).val(); + }, + prompt_ok_handler: function(callback, c_id) { $(c_id).dialog('close'); if (callback != undefined) callback($(c_id + " input#oc-dialog-prompt-input").val()); }, + form_ok_handler: function(callback, c_id) { + if (callback != undefined) { + var r = []; + var c = 0; + $(c_id + ' input').each(function(i, elem) { + r[c] = {name: $(elem).attr('name'), value: OCdialogs.determineValue(elem)}; + c++; + }); + $(c_id).dialog('close'); + callback(r); + } else { + $(c_id).dialog('close'); + } + } +}; diff --git a/core/js/oc-vcategories.js b/core/js/oc-vcategories.js new file mode 100644 index 0000000000..a6dcccf88e --- /dev/null +++ b/core/js/oc-vcategories.js @@ -0,0 +1,115 @@ +OCCategories={ + edit:function(){ + console.log('OCCategories.edit'); + if(OCCategories.app == undefined) { + OC.dialogs.alert('OCCategories.app is not set!'); + return; + } + $('body').append('
    '); + $('#category_dialog').load(OC.filePath('core', 'ajax', 'vcategories/edit.php')+'?app='+OCCategories.app, function(response){ + try { + var jsondata = jQuery.parseJSON(response); + if(response.status == 'error'){ + OC.dialogs.alert(response.data.message, 'Error'); + return; + } + } catch(e) { + $('#edit_categories_dialog').dialog({ + modal: true, + height: 350, minHeight:200, width: 250, minWidth: 200, + buttons: { + 'Delete':function() { + OCCategories.delete(); + }, + 'Rescan':function() { + OCCategories.rescan(); + } + }, + close : function(event, ui) { + $(this).dialog('destroy').remove(); + $('#category_dialog').remove(); + }, + open : function(event, ui) { + $('#category_addinput').live('input',function(){ + if($(this).val().length > 0) { + $('#category_addbutton').removeAttr('disabled'); + } + }); + $('#categoryform').submit(function() { + OCCategories.add($('#category_addinput').val()); + $('#category_addinput').val(''); + $('#category_addbutton').attr('disabled', 'disabled'); + return false; + }); + $('#category_addbutton').live('click',function(e){ + e.preventDefault(); + if($('#category_addinput').val().length > 0) { + OCCategories.add($('#category_addinput').val()); + $('#category_addinput').val(''); + } + }); + } + }); + } + }); + }, + delete:function(){ + var categories = $('#categorylist').find('input[type="checkbox"]').serialize(); + categories += '&app=' + OCCategories.app; + console.log('OCCategories.delete: ' + categories); + $.post(OC.filePath(OCCategories.app, 'ajax', 'categories/delete.php'),categories,function(jsondata, status, xhr){ + if (status == 'error' && xhr.status == 404) { + $.post(OC.filePath('core', 'ajax', 'vcategories/delete.php'),categories,function(jsondata, status, xhr){ + if(jsondata.status == 'success'){ + OCCategories._update(jsondata.data.categories); + } else { + OC.dialogs.alert(jsondata.data.message, 'Error'); + } + }); + return; + } + if(jsondata.status == 'success'){ + OCCategories._update(jsondata.data.categories); + } else { + OC.dialogs.alert(jsondata.data.message, 'Error'); + } + }); + }, + add:function(category){ + console.log('OCCategories.add ' + category); + $.getJSON(OC.filePath('core', 'ajax', 'vcategories/add.php'),{'category':category, 'app':OCCategories.app},function(jsondata){ + if(jsondata.status == 'success'){ + OCCategories._update(jsondata.data.categories); + } else { + OC.dialogs.alert(jsondata.data.message, 'Error'); + } + }); + return false; + }, + rescan:function(){ + console.log('Categories.rescan'); + $.getJSON(OC.filePath(OCCategories.app, 'ajax', 'categories/rescan.php'),{},function(jsondata, status, xhr){ + if (status == 'error' && xhr.status == 404) { + OC.dialogs.alert('The required file ' + OC.filePath(Categories.app, 'ajax', 'categories/rescan.php') + ' is not installed!', 'Error'); + return; + } + if(jsondata.status == 'success'){ + OCCategories._update(jsondata.data.categories); + } else { + OC.dialogs.alert(jsondata.data.message, 'Error'); + } + }); + }, + _update:function(categories){ + var categorylist = $('#categorylist'); + categorylist.find('li').remove(); + for(var category in categories) { + var item = '
  • ' + categories[category] + '
  • '; + $(item).appendTo(categorylist); + } + if(OCCategories.changed != undefined) { + OCCategories.changed(categories); + } + } +} + diff --git a/core/js/oc-vcategories.txt b/core/js/oc-vcategories.txt new file mode 100644 index 0000000000..76d4245f5e --- /dev/null +++ b/core/js/oc-vcategories.txt @@ -0,0 +1,28 @@ +Using OCCategories + +This 'class' is meant for any apps that uses OC_VObjects with the CATEGORIES field e.g. +Contacts and Calendar. It provides an editor UI for adding/deleting and rescanning categories +and basic ajax functions for adding and deleting. +To use the mass updating of OC_VObjects that /lib/vcategories.php provides, the app must implement +its own ajax functions in /apps/$(APP)/ajax/categories/rescan.php and /apps/$(APP)/ajax/categories/delete.php +See examples in /apps/contacts/ajax/categories and the inline docs in /lib/vcategories.php. + +In your app make sure you load the script and stylesheet: + +OC_Util::addScript('','oc-vcategories'); +OC_Util::addStyle('','oc-vcategories'); + +Set the app specific values in your javascript file. This is what I've used for the Contacts app: + + OCCategories.app = 'contacts'; + OCCategories.changed = Contacts.UI.Card.categoriesChanged; + +If OCCategories.changed point is set that function will be called each time the categories have been changed +in the editor (add/delete/rescan) to allow the app to update the UI accordingly. The only argument to the function +is an array of the updated categories e.g.: + +OCCategories.changed = function(categories) { + for(var category in categories) { + console.log(categories[category]); + } +} \ No newline at end of file diff --git a/core/templates/edit_categories_dialog.php b/core/templates/edit_categories_dialog.php new file mode 100644 index 0000000000..8997fa586b --- /dev/null +++ b/core/templates/edit_categories_dialog.php @@ -0,0 +1,16 @@ + +
    + +
    +
    +
      + +
    • + +
    +
    +
    +
    +
    diff --git a/dav.php b/dav.php new file mode 100644 index 0000000000..78e2711aec --- /dev/null +++ b/dav.php @@ -0,0 +1,77 @@ +. +* +*/ + +require_once('lib/base.php'); + +// Backends we always need (auth, principal and files) +$backends = array( + 'auth' => new OC_Connector_Sabre_Auth(), + 'principal' => new OC_Connector_Sabre_Principal() +); + +// Root nodes +$nodes = array( + new Sabre_CalDAV_Principal_Collection($backends['principal']) +); + +// Plugins +$plugins = array( + new Sabre_DAV_Auth_Plugin($backends['auth'],'ownCloud'), + new Sabre_DAVACL_Plugin(), + new Sabre_DAV_Browser_Plugin(false) // Show something in the Browser, but no upload +); + +// Load the plugins etc we need for usual file sharing +$backends['lock'] = new OC_Connector_Sabre_Locks(); +$plugins[] = new Sabre_DAV_Locks_Plugin($backends['lock']); +// Add a RESTful user directory +// /files/$username/ +if( OC_User::isLoggedIn()){ + $currentuser = OC_User::getUser(); + $files = new Sabre_DAV_SimpleCollection('files'); + foreach( OC_User::getUsers() as $username ){ + if( $username == $currentuser ){ + $public = new OC_Connector_Sabre_Directory('.'); + $files->addChild( new Sabre_DAV_SimpleCollection( $username, $public->getChildren())); + } + else{ + $files->addChild(new Sabre_DAV_SimpleCollection( $username )); + } + } + $nodes[] = $files; +} + +// Get the other plugins and nodes +OC_Hook::emit( 'OC_DAV', 'initialize', array( 'backends' => &$backends, 'nodes' => &$nodes, 'plugins' => &$plugins )); + +// Fire up server +$server = new Sabre_DAV_Server($nodes); +$server->setBaseUri(OC::$WEBROOT.'/dav.php'); + +// Load additional plugins +foreach( $plugins as &$plugin ){ + $server->addPlugin( $plugin ); +} unset( $plugin ); // Always do this after foreach with references! + +// And off we go! +$server->exec(); diff --git a/db_structure.xml b/db_structure.xml index 1d459b75fd..5eef44d8e8 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -70,7 +70,7 @@ true - 4 + 8 @@ -96,7 +96,7 @@ integer true - 4 + 8 @@ -105,7 +105,7 @@ true - 4 + 8 @@ -114,7 +114,7 @@ true - 4 + 8 @@ -291,7 +291,7 @@ integer false - 4 + 8 @@ -427,102 +427,6 @@ - - - *dbprefix*principalgroups - - - - - id - integer - 0 - true - 1 - true - 4 - - - - principal_id - integer - - true - true - 4 - - - - member_id - integer - - true - true - 4 - - - - principals_members_index - true - - principal_id - ascending - - - member_id - ascending - - - - - -
    - - - - *dbprefix*principals - - - - - id - integer - 0 - true - 1 - true - 4 - - - - uri - text - - false - 255 - - - - displayname - text - - false - 255 - - - - uri - true - - uri - ascending - - - - - -
    - *dbprefix*properties diff --git a/files/admin.php b/files/admin.php index 861b6037f3..7e410652cf 100644 --- a/files/admin.php +++ b/files/admin.php @@ -25,23 +25,29 @@ // Init owncloud require_once('../lib/base.php'); -OC_User::checkAdminUser(); +OC_Util::checkAdminUser(); $htaccessWorking=(getenv('htaccessWorking')=='true'); -if(isset($_POST['maxUploadSize'])){ - $maxUploadFilesize=$_POST['maxUploadSize']; - OC_Files::setUploadLimit(OC_Helper::computerFileSize($maxUploadFilesize)); +if($_POST) { + if(isset($_POST['maxUploadSize'])){ + $maxUploadFilesize=$_POST['maxUploadSize']; + OC_Files::setUploadLimit(OC_Helper::computerFileSize($maxUploadFilesize)); + } + if(isset($_POST['maxZipInputSize'])) { + $maxZipInputSize=$_POST['maxZipInputSize']; + OC_Preferences::setValue('', 'files', 'maxZipInputSize', OC_Helper::computerFileSize($maxZipInputSize)); + } }else{ $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); + $maxZipInputSize = OC_Helper::humanfilesize(OC_Preferences::getValue('', 'files', 'maxZipInputSize', OC_Helper::computerFileSize('800 MB'))); } OC_App::setActiveNavigationEntry( "files_administration" ); -// return template -$tmpl = new OC_Template( "files", "admin", "user" ); + +$tmpl = new OC_Template( 'files', 'admin' ); $tmpl->assign( 'htaccessWorking', $htaccessWorking ); $tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize); -$tmpl->printPage(); - -?> +$tmpl->assign( 'maxZipInputSize', $maxZipInputSize); +return $tmpl->fetchPage(); \ No newline at end of file diff --git a/files/ajax/newfile.php b/files/ajax/newfile.php index afc444bc0a..2d1372f06e 100644 --- a/files/ajax/newfile.php +++ b/files/ajax/newfile.php @@ -6,15 +6,35 @@ require_once('../../lib/base.php'); OC_JSON::checkLoggedIn(); // Get the params -$dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : ''; -$filename = isset( $_GET['filename'] ) ? stripslashes($_GET['filename']) : ''; -$content = isset( $_GET['content'] ) ? $_GET['content'] : ''; +$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : ''; +$filename = isset( $_POST['filename'] ) ? stripslashes($_POST['filename']) : ''; +$content = isset( $_POST['content'] ) ? $_POST['content'] : ''; +$source = isset( $_POST['source'] ) ? stripslashes($_POST['source']) : ''; if($filename == '') { OC_JSON::error(array("data" => array( "message" => "Empty Filename" ))); exit(); } +if($source){ + if(substr($source,0,8)!='https://' and substr($source,0,7)!='http://'){ + OC_JSON::error(array("data" => array( "message" => "Not a valid source" ))); + exit(); + } + $sourceStream=fopen($source,'rb'); + $target=$dir.'/'.$filename; + $result=OC_Filesystem::file_put_contents($target,$sourceStream); + if($result){ + $mime=OC_Filesystem::getMimetype($target); + OC_JSON::success(array("data" => array('mime'=>$mime))); + exit(); + }else{ + OC_JSON::error(array("data" => array( "message" => "Error while downloading ".$source. ' to '.$target ))); + exit(); + } +} + + if(OC_Files::newFile($dir, $filename, 'file')) { if($content){ OC_Filesystem::file_put_contents($dir.'/'.$filename,$content); diff --git a/files/ajax/newfolder.php b/files/ajax/newfolder.php index 6db045c4e1..228e369fbe 100644 --- a/files/ajax/newfolder.php +++ b/files/ajax/newfolder.php @@ -6,8 +6,8 @@ require_once('../../lib/base.php'); OC_JSON::checkLoggedIn(); // Get the params -$dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : ''; -$foldername = isset( $_GET['foldername'] ) ? stripslashes($_GET['foldername']) : ''; +$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : ''; +$foldername = isset( $_POST['foldername'] ) ? stripslashes($_POST['foldername']) : ''; if(trim($foldername) == '') { OC_JSON::error(array("data" => array( "message" => "Empty Foldername" ))); diff --git a/files/ajax/upload.php b/files/ajax/upload.php index 241edc216f..f8b8f0e2e5 100644 --- a/files/ajax/upload.php +++ b/files/ajax/upload.php @@ -16,12 +16,13 @@ foreach ($_FILES['files']['error'] as $error) { if ($error != 0) { $l=new OC_L10N('files'); $errors = array( - 0=>$l->t("There is no error, the file uploaded with success"), - 1=>$l->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'), - 2=>$l->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"), - 3=>$l->t("The uploaded file was only partially uploaded"), - 4=>$l->t("No file was uploaded"), - 6=>$l->t("Missing a temporary folder") + UPLOAD_ERR_OK=>$l->t("There is no error, the file uploaded with success"), + UPLOAD_ERR_INI_SIZE=>$l->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'), + UPLOAD_ERR_FORM_SIZE=>$l->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"), + UPLOAD_ERR_PARTIAL=>$l->t("The uploaded file was only partially uploaded"), + UPLOAD_ERR_NO_FILE=>$l->t("No file was uploaded"), + UPLOAD_ERR_NO_TMP_DIR=>$l->t("Missing a temporary folder"), + UPLOAD_ERR_CANT_WRITE=>$l->t('Failed to write to disk'), ); OC_JSON::error(array("data" => array( "message" => $errors[$error] ))); exit(); @@ -48,7 +49,8 @@ if(strpos($dir,'..') === false){ for($i=0;$i<$fileCount;$i++){ $target=stripslashes($dir) . $files['name'][$i]; if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i],$target)){ - $result[]=array( "status" => "success", 'mime'=>OC_Filesystem::getMimeType($target),'size'=>OC_Filesystem::filesize($target),'name'=>$files['name'][$i]); + $meta=OC_FileCache::getCached($target); + $result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'],'name'=>$files['name'][$i]); } } OC_JSON::encodedPrint($result); diff --git a/files/appinfo/app.php b/files/appinfo/app.php index e434296b25..0bf73d9a07 100644 --- a/files/appinfo/app.php +++ b/files/appinfo/app.php @@ -7,4 +7,4 @@ OC_App::register( array( "order" => 2, "id" => "files", "name" => "Files" )); OC_App::addNavigationEntry( array( "id" => "files_index", "order" => 1, "href" => OC_Helper::linkTo( "files", "index.php" ), "icon" => OC_Helper::imagePath( "core", "places/home.svg" ), "name" => $l->t("Files") )); -?> +OC_Search::registerProvider('OC_Search_Provider_File'); diff --git a/files/index.php b/files/index.php index a29d3fb7e1..79261e495b 100644 --- a/files/index.php +++ b/files/index.php @@ -40,7 +40,7 @@ OC_App::setActiveNavigationEntry( "files_index" ); // Load the files $dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : ''; // Redirect if directory does not exist -if(!OC_Filesystem::is_dir($dir)) { +if(!OC_Filesystem::is_dir($dir.'/')) { header("Location: ".$_SERVER['PHP_SELF'].""); } diff --git a/files/js/files.js b/files/js/files.js index 9f1f5368df..539d559889 100644 --- a/files/js/files.js +++ b/files/js/files.js @@ -3,11 +3,11 @@ $(document).ready(function() { //little hack to set unescape filenames in attribute $(this).attr('data-file',decodeURIComponent($(this).attr('data-file'))); }); - + if($('tr[data-file]').length==0){ $('.file_upload_filename').addClass('highlight'); } - + $('#file_action_panel').attr('activeAction', false); //drag/drop of files @@ -16,7 +16,7 @@ $(document).ready(function() { $('div.crumb').droppable(crumbDropOptions); $('ul#apps>li:first-child').data('dir',''); $('ul#apps>li:first-child').droppable(crumbDropOptions); - + // Triggers invisible file input $('.file_upload_button_wrapper').live('click', function() { $(this).parent().children('.file_upload_start').trigger('click'); @@ -81,9 +81,9 @@ $(document).ready(function() { } } } - + }); - + // Sets the select_all checkbox behaviour : $('#select_all').click(function() { if($(this).attr('checked')){ @@ -97,7 +97,7 @@ $(document).ready(function() { } procesSelection(); }); - + $('td.filename input:checkbox').live('click',function(event) { if (event.shiftKey) { var last = $(lastChecked).parent().parent().prevAll().length; @@ -126,37 +126,22 @@ $(document).ready(function() { } procesSelection(); }); - - $('#file_newfolder_form').submit(function(event) { - event.preventDefault(); - $.ajax({ - url: 'ajax/newfolder.php', - data: "dir="+$('#dir').val()+"&foldername="+$('#file_newfolder_name').val(), - complete: function(data){boolOperationFinished(data, function(){ - var date=new Date(); - FileList.addDir($('#file_newfolder_name').val(),0,date); - $('#file_newfolder_name').val('New Folder'); - $('#file_newfolder_name').blur(); - });} - }); - }); - + $('#file_newfolder_name').click(function(){ if($('#file_newfolder_name').val() == 'New Folder'){ $('#file_newfolder_name').val(''); } }); - + $('.download').click('click',function(event) { var files=getSelectedFiles('name').join(';'); - - //send the browser to the download location var dir=$('#dir').val()||'/'; -// alert(files); + $('#notification').text(t('files','generating ZIP-file, it may take some time.')); + $('#notification').fadeIn(); window.location='ajax/download.php?files='+encodeURIComponent(files)+'&dir='+encodeURIComponent(dir); return false; }); - + $('.delete').click(function(event) { var files=getSelectedFiles('name'); event.preventDefault(); @@ -242,7 +227,7 @@ $(document).ready(function() { form.hide(); } }); - + //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) if(navigator.userAgent.search(/konqueror/i)==-1){ $('.file_upload_start').attr('multiple','multiple') @@ -269,7 +254,7 @@ $(document).ready(function() { text=text.substr(0,text.length-6)+'...'; crumb.text(text); } - + $(window).click(function(){ $('#new>ul').hide(); $('#new').removeClass('active'); @@ -293,14 +278,14 @@ $(document).ready(function() { if($(this).children('p').length==0){ return; } - + $('#new li').each(function(i,element){ if($(element).children('p').length==0){ $(element).children('input').remove(); $(element).append('

    '+$(element).data('text')+'

    '); } }); - + var type=$(this).data('type'); var text=$(this).children('p').text(); $(this).data('text',text); @@ -312,10 +297,10 @@ $(document).ready(function() { var name=$(this).val(); switch(type){ case 'file': - $.ajax({ - url: OC.filePath('files','ajax','newfile.php'), - data: "dir="+encodeURIComponent($('#dir').val())+"&filename="+encodeURIComponent(name)+'&content=%20%0A', - complete: function(data){boolOperationFinished(data, function(){ + $.post( + OC.filePath('files','ajax','newfile.php'), + {dir:$('#dir').val(),filename:name,content:" \n"}, + function(data){ var date=new Date(); FileList.addFile(name,0,date); var tr=$('tr').filterAttr('data-file',name); @@ -323,18 +308,49 @@ $(document).ready(function() { getMimeIcon('text/plain',function(path){ tr.find('td.filename').attr('style','background-image:url('+path+')'); }); - });} - }); + } + ); break; case 'folder': - $.ajax({ - url: OC.filePath('files','ajax','newfolder.php'), - data: "dir="+encodeURIComponent($('#dir').val())+"&foldername="+encodeURIComponent(name), - complete: function(data){boolOperationFinished(data, function(){ + $.post( + OC.filePath('files','ajax','newfolder.php'), + {dir:$('#dir').val(),foldername:name}, + function(data){ var date=new Date(); FileList.addDir(name,0,date); - });} - }); + } + ); + break; + case 'web': + if(name.substr(0,8)!='https://' && name.substr(0,7)!='http://'){ + name='http://'.name; + } + var localName=name; + if(localName.substr(localName.length-1,1)=='/'){//strip / + localName=localName.substr(0,localName.length-1) + } + if(localName.indexOf('/')){//use last part of url + localName=localName.split('/').pop(); + }else{//or the domain + localName=(localName.match(/:\/\/(.[^/]+)/)[1]).replace('www.',''); + } + $.post( + OC.filePath('files','ajax','newfile.php'), + {dir:$('#dir').val(),source:name,filename:localName}, + function(result){ + if(result.status == 'success'){ + var date=new Date(); + FileList.addFile(localName,0,date); + var tr=$('tr').filterAttr('data-file',localName); + tr.data('mime',result.data.mime); + getMimeIcon(result.data.mime,function(path){ + tr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + }else{ + + } + } + ); break; } var li=$(this).parent(); @@ -387,39 +403,6 @@ function updateBreadcrumb(breadcrumbHtml) { $('p.nav').empty().html(breadcrumbHtml); } -function humanFileSize(bytes){ - if( bytes < 1024 ){ - return bytes+' B'; - } - bytes = Math.round(bytes / 1024, 1 ); - if( bytes < 1024 ){ - return bytes+' kB'; - } - bytes = Math.round( bytes / 1024, 1 ); - if( bytes < 1024 ){ - return bytes+' MB'; - } - - // Wow, heavy duty for owncloud - bytes = Math.round( bytes / 1024, 1 ); - return bytes+' GB'; -} - -function simpleFileSize(bytes) { - mbytes = Math.round(bytes/(1024*1024/10))/10; - if(bytes == 0) { return '0'; } - else if(mbytes < 0.1) { return '< 0.1'; } - else if(mbytes > 1000) { return '> 1000'; } - else { return mbytes.toFixed(1); } -} - -function formatDate(date){ - var monthNames = [ t('files','January'), t('files','February'), t('files','March'), t('files','April'), t('files','May'), t('files','June'), - t('files','July'), t('files','August'), t('files','September'), t('files','October'), t('files','November'), t('files','December') ]; - return monthNames[date.getMonth()]+' '+date.getDate()+', '+date.getFullYear()+', '+((date.getHours()<10)?'0':'')+date.getHours()+':'+date.getMinutes(); -} - - //options for file drag/dropp var dragOptions={ distance: 20, revert: 'invalid', opacity: 0.7, diff --git a/files/templates/admin.php b/files/templates/admin.php index 0122865ee7..8c3ba56ad5 100644 --- a/files/templates/admin.php +++ b/files/templates/admin.php @@ -1,10 +1,12 @@ - - '/>
    - - - No settings currently available. - +
    + t('File handling');?> + + '/>
    + + '/>
    + +
    diff --git a/files/templates/index.php b/files/templates/index.php index 7e9505dec2..7fc51c288e 100644 --- a/files/templates/index.php +++ b/files/templates/index.php @@ -7,6 +7,7 @@
    diff --git a/lib/app.php b/lib/app.php index 1879a89cee..64cbe8894e 100644 --- a/lib/app.php +++ b/lib/app.php @@ -58,8 +58,8 @@ class OC_App{ $apps = OC_Appconfig::getApps(); foreach( $apps as $app ){ if( self::isEnabled( $app )){ - if(is_file(OC::$SERVERROOT.'/apps/'.$app.'/appinfo/app.php')){ - require( 'apps/'.$app.'/appinfo/app.php' ); + if(is_file(OC::$APPSROOT.'/apps/'.$app.'/appinfo/app.php')){ + require( $app.'/appinfo/app.php' ); } } } @@ -268,7 +268,7 @@ class OC_App{ if(is_file($appid)){ $file=$appid; }else{ - $file=OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/info.xml'; + $file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml'; if(!is_file($file)){ return array(); } @@ -325,6 +325,7 @@ class OC_App{ $source=self::$settingsForms; break; case 'admin': + $forms[] = include 'files/admin.php'; //hardcode own apps $source=self::$adminForms; break; case 'personal': @@ -363,15 +364,15 @@ class OC_App{ */ public static function getAllApps(){ $apps=array(); - $dh=opendir(OC::$SERVERROOT.'/apps'); + $dh=opendir(OC::$APPSROOT.'/apps'); while($file=readdir($dh)){ - if(is_file(OC::$SERVERROOT.'/apps/'.$file.'/appinfo/app.php')){ + if(substr($file,0,1)!='.' and is_file(OC::$APPSROOT.'/apps/'.$file.'/appinfo/app.php')){ $apps[]=$file; } } return $apps; } - + /** * check if any apps need updating and update those */ @@ -390,17 +391,17 @@ class OC_App{ } } } - + /** * update the database for the app and call the update script * @param string appid */ public static function updateApp($appid){ - if(file_exists(OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/database.xml')){ - OC_DB::updateDbFromStructure(OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/database.xml'); + if(file_exists(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/database.xml')){ + OC_DB::updateDbFromStructure(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/database.xml'); } - if(file_exists(OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/update.php')){ - include OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/update.php'; + if(file_exists(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/update.php')){ + include OC::$APPSROOT.'/apps/'.$appid.'/appinfo/update.php'; } } diff --git a/lib/base.php b/lib/base.php index a95c9d2b21..9995544f14 100644 --- a/lib/base.php +++ b/lib/base.php @@ -62,6 +62,14 @@ class OC{ * the root path of the 3rdparty folder for http requests (e.g. owncloud/3rdparty) */ public static $THIRDPARTYWEBROOT = ''; + /** + * The installation path of the apps folder on the server (e.g. /srv/http/owncloud) + */ + public static $APPSROOT = ''; + /** + * the root path of the apps folder for http requests (e.g. owncloud) + */ + public static $APPSWEBROOT = ''; /** * SPL autoload @@ -136,6 +144,12 @@ class OC{ $_SERVER['PHP_AUTH_PW'] = strip_tags($password); } + // register the stream wrappers + require_once('streamwrappers.php'); + stream_wrapper_register("fakedir", "OC_FakeDirStream"); + stream_wrapper_register('static', 'OC_StaticStreamWrapper'); + stream_wrapper_register('close', 'OC_CloseStreamWrapper'); + // calculate the documentroot OC::$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']); OC::$SERVERROOT=str_replace("\\",'/',substr(__FILE__,0,-13)); @@ -143,8 +157,17 @@ class OC{ $scriptName=$_SERVER["SCRIPT_NAME"]; if(substr($scriptName,-1)=='/'){ $scriptName.='index.php'; + //make sure suburi follows the same rules as scriptName + if(substr(OC::$SUBURI,-9)!='index.php'){ + if(substr(OC::$SUBURI,-1)!='/'){ + OC::$SUBURI=OC::$SUBURI.'/'; + } + OC::$SUBURI=OC::$SUBURI.'index.php'; + } } - OC::$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen(OC::$SUBURI)); + OC::$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen(OC::$SUBURI)); + // try a new way to detect the WEBROOT which is simpler and also works with the app directory outside the owncloud folder. let´s see if this works for everybody +// OC::$WEBROOT=substr(OC::$SERVERROOT,strlen(OC::$DOCUMENTROOT)); if(OC::$WEBROOT!='' and OC::$WEBROOT[0]!=='/'){ @@ -159,22 +182,35 @@ class OC{ OC::$THIRDPARTYROOT=OC::$SERVERROOT; OC::$THIRDPARTYWEBROOT=OC::$WEBROOT; }elseif(file_exists(OC::$SERVERROOT.'/../3rdparty')){ - $url_tmp=explode('/',OC::$WEBROOT); - $length=count($url_tmp); - unset($url_tmp[$length-1]); - OC::$THIRDPARTYWEBROOT=implode('/',$url_tmp); - $root_tmp=explode('/',OC::$SERVERROOT); - $length=count($root_tmp); - unset($root_tmp[$length-1]); - OC::$THIRDPARTYROOT=implode('/',$root_tmp); + OC::$THIRDPARTYWEBROOT=rtrim(dirname(OC::$WEBROOT), '/'); + OC::$THIRDPARTYROOT=rtrim(dirname(OC::$SERVERROOT), '/'); }else{ echo("3rdparty directory not found! Please put the ownCloud 3rdparty folder in the ownCloud folder or the folder above. You can also configure the location in the config.php file."); exit; } + // search the apps folder + if(file_exists(OC::$SERVERROOT.'/apps')){ + OC::$APPSROOT=OC::$SERVERROOT; + OC::$APPSWEBROOT=OC::$WEBROOT; + }elseif(file_exists(OC::$SERVERROOT.'/../apps')){ + OC::$APPSWEBROOT=rtrim(dirname(OC::$WEBROOT), '/'); + OC::$APPSROOT=rtrim(dirname(OC::$SERVERROOT), '/'); + }else{ + echo("apps directory not found! Please put the ownCloud apps folder in the ownCloud folder or the folder above. You can also configure the location in the config.php file."); + exit; + } // set the right include path - set_include_path(OC::$SERVERROOT.'/lib'.PATH_SEPARATOR.OC::$SERVERROOT.'/config'.PATH_SEPARATOR.OC::$THIRDPARTYROOT.'/3rdparty'.PATH_SEPARATOR.get_include_path().PATH_SEPARATOR.OC::$SERVERROOT); + set_include_path( + OC::$SERVERROOT.'/lib'.PATH_SEPARATOR. + OC::$SERVERROOT.'/config'.PATH_SEPARATOR. + OC::$THIRDPARTYROOT.'/3rdparty'.PATH_SEPARATOR. + OC::$APPSROOT.PATH_SEPARATOR. + OC::$APPSROOT.'/apps'.PATH_SEPARATOR. + get_include_path().PATH_SEPARATOR. + OC::$SERVERROOT + ); // Redirect to installer if not installed if (!OC_Config::getValue('installed', false) && OC::$SUBURI != '/index.php') { @@ -234,8 +270,10 @@ class OC{ OC_Util::addScript( "jquery-showpassword" ); OC_Util::addScript( "jquery.infieldlabel.min" ); OC_Util::addScript( "jquery-tipsy" ); + OC_Util::addScript( "oc-dialogs" ); OC_Util::addScript( "js" ); OC_Util::addScript( "eventsource" ); + OC_Util::addScript( "config" ); //OC_Util::addScript( "multiselect" ); OC_Util::addScript('search','result'); OC_Util::addStyle( "styles" ); @@ -261,6 +299,7 @@ class OC{ $_SESSION['user_id'] = ''; } + OC_User::useBackend( OC_Config::getValue( "userbackend", "database" )); OC_Group::setBackend( OC_Config::getValue( "groupbackend", "database" )); @@ -277,9 +316,8 @@ class OC{ OC_App::loadApps(); } - // Last part: connect some hooks - OC_HOOK::connect('OC_User', 'post_createUser', 'OC_Connector_Sabre_Principal', 'addPrincipal'); - OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Connector_Sabre_Principal', 'deletePrincipal'); + //make sure temporary files are cleaned up + register_shutdown_function(array('OC_Helper','cleanTmp')); } } @@ -302,15 +340,10 @@ if(!function_exists('get_temp_dir')) { unlink($temp); return dirname($temp); } + if( $temp=sys_get_temp_dir()) return $temp; + return null; } } OC::init(); - -require_once('fakedirstream.php'); - - - -// FROM search.php -new OC_Search_Provider_File(); diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php index 1e87c7cee0..8964ef7d0d 100644 --- a/lib/connector/sabre/auth.php +++ b/lib/connector/sabre/auth.php @@ -23,6 +23,7 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic { * @return bool */ protected function validateUserPass($username, $password){ + OC_Util::setUpFS();//login hooks may need early access to the filesystem if(OC_User::login($username,$password)){ OC_Util::setUpFS(); return true; diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index d1cb55ce13..912c8cd439 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -73,8 +73,8 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa $nodes = array(); // foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); - if( OC_Filesystem::is_dir($this->path)){ - $dh = OC_Filesystem::opendir($this->path); + if( OC_Filesystem::is_dir($this->path . '/')){ + $dh = OC_Filesystem::opendir($this->path . '/'); while(( $node = readdir($dh)) !== false ){ if($node!='.' && $node!='..'){ $nodes[] = $this->getChild($node); diff --git a/lib/connector/sabre/principal.php b/lib/connector/sabre/principal.php index 6cfb3449e1..d1456f7c64 100644 --- a/lib/connector/sabre/principal.php +++ b/lib/connector/sabre/principal.php @@ -8,50 +8,6 @@ */ class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend { - /** - * TODO: write doc - */ - public static function addPrincipal($params){ - // Add the user - $uri = 'principals/'.$params['uid']; - $displayname = $params['uid']; - $query = OC_DB::prepare('INSERT INTO *PREFIX*principals (uri,displayname) VALUES(?,?)'); - $query->execute(array($uri,$displayname)); - - // Add calendar and addressbook read and write support (sharing calendars) - $uri = 'principals/'.$params['uid'].'/calendar-proxy-read'; - $displayname = null; - $query->execute(array($uri,$displayname)); - $uri = 'principals/'.$params['uid'].'/calendar-proxy-write'; - $query->execute(array($uri,$displayname)); - $uri = 'principals/'.$params['uid'].'/addressbook-proxy-read'; - $query->execute(array($uri,$displayname)); - $uri = 'principals/'.$params['uid'].'/addressbook-proxy-write'; - $query->execute(array($uri,$displayname)); - - return true; - } - - /** - * TODO: write doc - */ - public static function deletePrincipal($params){ - $query = OC_DB::prepare('SELECT * FROM *PREFIX*principals'); - $result = $query->execute(); - - $deleteprincipal = OC_DB::prepare('DELETE FROM *PREFIX*principals WHERE id = ?'); - $deletegroup = OC_DB::prepare('DELETE FROM *PREFIX*principalgroups WHERE principal_id = ? OR member_id = ?'); - // We have to delete the principals and relations! Principals include - while($row = $result->fetchRow()){ - // Checking if the principal is in the prefix - $array = explode('/',$row['uri']); - if ($array[1] != $params['uid']) continue; - $deleteprincipal->execute(array($row['id'])); - $deletegroup->execute(array($row['id'],$row['id'])); - } - return true; - } - /** * Returns a list of principals based on a prefix. * diff --git a/lib/db.php b/lib/db.php index 4c17cd0dbd..9fab51edfc 100644 --- a/lib/db.php +++ b/lib/db.php @@ -316,8 +316,8 @@ class OC_DB { // read file $content = file_get_contents( $file ); - // Make changes and save them to a temporary file - $file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' ); + // Make changes and save them to an in-memory file + $file2 = 'static://db_scheme'; if($file2 == ''){ die('could not create tempfile in get_temp_dir() - aborting'); } @@ -331,7 +331,7 @@ class OC_DB { // Try to create tables $definition = self::$schema->parseDatabaseDefinitionFile( $file2 ); - // Delete our temporary file + //clean up memory unlink( $file2 ); // Die in case something went wrong @@ -371,8 +371,8 @@ class OC_DB { return false; } - // Make changes and save them to a temporary file - $file2 = tempnam( get_temp_dir(), 'oc_db_scheme_' ); + // Make changes and save them to an in-memory file + $file2 = 'static://db_scheme'; $content = str_replace( '*dbname*', $previousSchema['name'], $content ); $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't @@ -381,7 +381,7 @@ class OC_DB { file_put_contents( $file2, $content ); $op = self::$schema->updateDatabase($file2, $previousSchema, array(), false); - // Delete our temporary file + //clean up memory unlink( $file2 ); if (PEAR::isError($op)) { @@ -508,6 +508,21 @@ class OC_DB { self::$connection->commit(); self::$inTransaction=false; } + + /** + * check if a result is an error, works with MDB2 and PDOException + * @param mixed $result + * @return bool + */ + public static function isError($result){ + if(!$result){ + return true; + }elseif(self::$backend==self::BACKEND_MDB2 and PEAR::isError($result)){ + return true; + }else{ + return false; + } + } } /** @@ -527,11 +542,15 @@ class PDOStatementWrapper{ public function execute($input=array()){ $this->lastArguments=$input; if(count($input)>0){ - $this->statement->execute($input); + $result=$this->statement->execute($input); }else{ - $this->statement->execute(); + $result=$this->statement->execute(); + } + if($result){ + return $this; + }else{ + return false; } - return $this; } /** diff --git a/lib/fakedirstream.php b/lib/fakedirstream.php deleted file mode 100644 index fa3e64da62..0000000000 --- a/lib/fakedirstream.php +++ /dev/null @@ -1,45 +0,0 @@ -name=substr($path,strlen('fakedir://')); - $this->index=0; - if(isset($FAKEDIRS[$this->name])){ - $this->data=$FAKEDIRS[$this->name]; - }else{ - $this->data=array(); - } - return true; - } - - public function dir_readdir(){ - if($this->index>=count($this->data)){ - return false; - } - $filename=$this->data[$this->index]; - $this->index++; - return $filename; - } - - public function dir_closedir() { - $this->data=false; - $this->name=''; - return true; - } - - public function dir_rewinddir() { - $this->index=0; - return true; - } -} - -stream_wrapper_register("fakedir", "fakeDirStream"); - diff --git a/lib/filecache.php b/lib/filecache.php index faadddfb37..280a9929db 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -28,6 +28,8 @@ * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache */ class OC_FileCache{ + private static $savedData=array(); + /** * get the filesystem info from the cache * @param string path @@ -93,6 +95,14 @@ class OC_FileCache{ self::update($id,$data); return; } + if(isset(self::$savedData[$path])){ + $data=array_merge($data,self::$savedData[$path]); + unset(self::$savedData[$path]); + } + if(!isset($data['size']) or !isset($data['mtime'])){//save incomplete data for the next time we write it + self::$savedData[$path]=$data; + return; + } if(!isset($data['encrypted'])){ $data['encrypted']=false; } @@ -101,9 +111,11 @@ class OC_FileCache{ } $mimePart=dirname($data['mimetype']); $user=OC_User::getUser(); - $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable) VALUES(?,?,?,?,?,?,?,?,?,?)'); - $query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'])); - + $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)'); + $result=$query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned'])); + if(OC_DB::isError($result)){ + OC_Log::write('files','error while writing file('.$path.') to cache',OC_Log::ERROR); + } } /** @@ -128,7 +140,10 @@ class OC_FileCache{ $sql = 'UPDATE *PREFIX*fscache SET '.implode(' , ',$queryParts).' WHERE id=?'; $query=OC_DB::prepare($sql); - $query->execute($arguments); + $result=$query->execute($arguments); + if(OC_DB::isError($result)){ + OC_Log::write('files','error while updating file('.$path.') in cache',OC_Log::ERROR); + } } /** @@ -153,19 +168,28 @@ class OC_FileCache{ /** * delete info from the cache - * @param string $path + * @param string/int $file * @param string root (optional) */ - public static function delete($path,$root=''){ - if(!$root){ - $root=OC_Filesystem::getRoot(); + public static function delete($file,$root=''){ + if(!is_numeric($file)){ + if(!$root){ + $root=OC_Filesystem::getRoot(); + } + if($root=='/'){ + $root=''; + } + $path=$root.$file; + self::delete(self::getFileId($path)); + }elseif($file!=-1){ + $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE parent=?'); + $result=$query->execute(array($file)); + while($child=$result->fetchRow()){ + self::delete(intval($child['id'])); + } + $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE id=?'); + $query->execute(array($file)); } - if($root=='/'){ - $root=''; - } - $path=$root.$path; - $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE path=?'); - $query->execute(array($path)); } /** @@ -262,11 +286,20 @@ class OC_FileCache{ */ private static function getFileId($path){ $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path=?'); - $result=$query->execute(array($path))->fetchRow(); + if(OC_DB::isError($query)){ + OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR); + return -1; + } + $result=$query->execute(array($path)); + if(OC_DB::isError($result)){ + OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR); + return -1; + } + $result=$result->fetchRow(); if(is_array($result)){ return $result['id']; }else{ - OC_Log::write('getFileId(): file not found in cache ('.$path.')','core',OC_Log::DEBUG); + OC_Log::write('getFileId(): file not found in cache ('.$path.')','core',OC_Log::DEBUG); return -1; } } @@ -299,10 +332,11 @@ class OC_FileCache{ $path=$params['path']; $fullPath=$view->getRoot().$path; $mimetype=$view->getMimeType($path); + $dir=$view->is_dir($path.'/'); //dont use self::get here, we don't want inifinte loops when a file has changed $cachedSize=self::getCachedSize($path,$root); $size=0; - if($mimetype=='httpd/unix-directory'){ + if($dir){ if(self::inCache($path,$root)){ $parent=self::getFileId($fullPath); $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE parent=?'); @@ -323,7 +357,29 @@ class OC_FileCache{ } self::increaseSize(dirname($fullPath),$size-$cachedSize); } - + + public static function getCached($path,$root=''){ + if(!$root){ + $root=OC_Filesystem::getRoot(); + }else{ + if($root=='/'){ + $root=''; + } + } + $path=$root.$path; + $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?'); + $result=$query->execute(array($path))->fetchRow(); + if(is_array($result)){ + if(isset(self::$savedData[$path])){ + $result=array_merge($result,self::$savedData[$path]); + } + return $result; + }else{ + OC_Log::write('get(): file not found in cache ('.$path.')','core',OC_Log::DEBUG); + return false; + } + } + private static function getCachedSize($path,$root){ if(!$root){ $root=OC_Filesystem::getRoot(); @@ -332,6 +388,7 @@ class OC_FileCache{ $root=''; } } + $path=$root.$path; $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path=?'); $result=$query->execute(array($path)); if($row=$result->fetchRow()){ @@ -418,13 +475,13 @@ class OC_FileCache{ $view=new OC_FilesystemView(($root=='/')?'':$root); } self::scanFile($path,$root); - $dh=$view->opendir($path); + $dh=$view->opendir($path.'/'); $totalSize=0; if($dh){ while (($filename = readdir($dh)) !== false) { if($filename != '.' and $filename != '..'){ $file=$path.'/'.$filename; - if($view->is_dir($file)){ + if($view->is_dir($file.'/')){ if($eventSource){ $eventSource->send('scanning',array('file'=>$file,'count'=>$count)); } @@ -465,7 +522,7 @@ class OC_FileCache{ } /** - * fine files by mimetype + * find files by mimetype * @param string $part1 * @param string $part2 (optional) * @param string root (optional) @@ -477,20 +534,21 @@ class OC_FileCache{ * seccond mimetype part can be ommited * e.g. searchByMime('audio') */ - public static function searchByMime($part1,$part2='',$root=''){ + public static function searchByMime($part1,$part2=null,$root=null){ if(!$root){ $root=OC_Filesystem::getRoot(); - }elseif($root='/'){ + }elseif($root=='/'){ $root=''; } $rootLen=strlen($root); + $root .= '%'; $user=OC_User::getUser(); if(!$part2){ - $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimepart=? AND user=?'); - $result=$query->execute(array($part1,$user)); + $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimepart=? AND user=? AND path LIKE ?'); + $result=$query->execute(array($part1,$user, $root)); }else{ - $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimetype=? AND user=?'); - $result=$query->execute(array($part1.'/'.$part2,$user)); + $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimetype=? AND user=? AND path LIKE ? '); + $result=$query->execute(array($part1.'/'.$part2,$user, $root)); } $names=array(); while($row=$result->fetchRow()){ @@ -515,6 +573,9 @@ class OC_FileCache{ } $view=new OC_FilesystemView($root); } + if(!$view->file_exists($path)){ + return false; + } $mtime=$view->filemtime($path); $isDir=$view->is_dir($path); $path=$root.$path; diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 235fc8bf28..46fc2f49c5 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -34,11 +34,12 @@ * A post-proxy recieves 2 arguments, the filepath and the result of the operation. * The return calue of the post-proxy will be used as the new result of the operation * The operations that have a post-proxy are - * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, filemtime, filectime, file_get_contents, getMimeType, hash, free_space and search + * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, fileatime, filemtime, filectime, file_get_contents, getMimeType, hash, fopen, free_space and search */ class OC_FileProxy{ private static $proxies=array(); + public static $enabled=true; /** * check if this proxy implments a specific proxy operation @@ -83,16 +84,19 @@ class OC_FileProxy{ return $proxies; } - public static function runPreProxies($operation,$filepath,$filepath2=null){ + public static function runPreProxies($operation,&$filepath,&$filepath2=null){ + if(!self::$enabled){ + return true; + } $proxies=self::getProxies($operation,false); $operation='pre'.$operation; foreach($proxies as $proxy){ - if($filepath2){ - if(!$proxy->$operation($filepath,$filepath2)){ + if(!is_null($filepath2)){ + if($proxy->$operation($filepath,$filepath2)===false){ return false; } }else{ - if(!$proxy->$operation($filepath)){ + if($proxy->$operation($filepath)===false){ return false; } } @@ -101,6 +105,9 @@ class OC_FileProxy{ } public static function runPostProxies($operation,$path,$result){ + if(!self::$enabled){ + return $result; + } $proxies=self::getProxies($operation,true); $operation='post'.$operation; foreach($proxies as $proxy){ diff --git a/lib/files.php b/lib/files.php index 1f8331afb2..50223df1d3 100644 --- a/lib/files.php +++ b/lib/files.php @@ -59,6 +59,9 @@ class OC_Files { } if(is_array($files)){ + self::checkZipInputSize($dir,$files); + $executionTime = intval(ini_get('max_execution_time')); + set_time_limit(0); $zip = new ZipArchive(); $filename = get_temp_dir()."/ownCloud.zip"; if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) { @@ -75,7 +78,11 @@ class OC_Files { } } $zip->close(); + set_time_limit($executionTime); }elseif(OC_Filesystem::is_dir($dir.'/'.$files)){ + self::checkZipInputSize($dir,$files); + $executionTime = intval(ini_get('max_execution_time')); + set_time_limit(0); $zip = new ZipArchive(); $filename = get_temp_dir()."/ownCloud.zip"; if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) { @@ -84,6 +91,7 @@ class OC_Files { $file=$dir.'/'.$files; self::zipAddDir($file,$zip); $zip->close(); + set_time_limit($executionTime); }else{ $zip=false; $filename=$dir.'/'.$files; @@ -209,6 +217,40 @@ class OC_Files { } } + /** + * checks if the selected files are within the size constraint. If not, outputs an error page. + * + * @param dir $dir + * @param files $files + */ + static function checkZipInputSize($dir, $files) { + $zipLimit = OC_Preferences::getValue('', 'files', 'maxZipInputSize', OC_Helper::computerFileSize('800 MB')); + if($zipLimit > 0) { + $totalsize = 0; + if(is_array($files)){ + foreach($files as $file){ + $totalsize += OC_Filesystem::filesize($dir.'/'.$file); + } + }else{ + $totalsize += OC_Filesystem::filesize($dir.'/'.$files); + } + if($totalsize > $zipLimit) { + $l = new OC_L10N('files'); + header("HTTP/1.0 409 Conflict"); + $tmpl = new OC_Template( '', 'error', 'user' ); + $errors = array( + array( + 'error' => $l->t('Selected files too large to generate zip file.'), + 'hint' => 'Download the files in smaller chunks, seperately or kindly ask your administrator.
    ' . $l->t('Back to Files') . '', + ) + ); + $tmpl->assign('errors', $errors); + $tmpl->printPage(); + exit; + } + } + } + /** * try to detect the mime type of a file * @@ -256,7 +298,7 @@ class OC_Files { return false; } } - + /** * set the maximum upload size limit for apache hosts using .htaccess * @param int size filesisze in bytes diff --git a/lib/filestorage.php b/lib/filestorage.php index 4523144f6f..fd6497b947 100644 --- a/lib/filestorage.php +++ b/lib/filestorage.php @@ -23,33 +23,31 @@ /** * Privde a common interface to all different storage options */ -class OC_Filestorage{ +abstract class OC_Filestorage{ public function __construct($parameters){} - public function mkdir($path){} - public function rmdir($path){} - public function opendir($path){} - public function is_dir($path){} - public function is_file($path){} - public function stat($path){} - public function filetype($path){} - public function filesize($path){} - public function is_readable($path){} - public function is_writable($path){} - public function file_exists($path){} - public function readfile($path){} - public function filectime($path){} - public function filemtime($path){} - public function file_get_contents($path){} - public function file_put_contents($path,$data){} - public function unlink($path){} - public function rename($path1,$path2){} - public function copy($path1,$path2){} - public function fopen($path,$mode){} - public function toTmpFile($path){}//copy the file to a temporary file, used for cross-storage file actions - public function fromTmpFile($tmpPath,$path){}//copy a file from a temporary file, used for cross-storage file actions - public function getMimeType($path){} - public function hash($type,$path,$raw){} - public function free_space($path){} - public function search($query){} - public function getLocalFile($path){}// get a path to a local version of the file, whether the original file is local or remote + abstract public function mkdir($path); + abstract public function rmdir($path); + abstract public function opendir($path); + abstract public function is_dir($path); + abstract public function is_file($path); + abstract public function stat($path); + abstract public function filetype($path); + abstract public function filesize($path); + abstract public function is_readable($path); + abstract public function is_writable($path); + abstract public function file_exists($path); + abstract public function filectime($path); + abstract public function filemtime($path); + abstract public function file_get_contents($path); + abstract public function file_put_contents($path,$data); + abstract public function unlink($path); + abstract public function rename($path1,$path2); + abstract public function copy($path1,$path2); + abstract public function fopen($path,$mode); + abstract public function getMimeType($path); + abstract public function hash($type,$path,$raw); + abstract public function free_space($path); + abstract public function search($query); + abstract public function touch($path, $mtime=null); + abstract public function getLocalFile($path);// get a path to a local version of the file, whether the original file is local or remote } diff --git a/lib/filestorage/common.php b/lib/filestorage/common.php new file mode 100644 index 0000000000..f632474df0 --- /dev/null +++ b/lib/filestorage/common.php @@ -0,0 +1,159 @@ +. +*/ + +abstract class OC_Filestorage_Common extends OC_Filestorage { + + public function __construct($parameters){} +// abstract public function mkdir($path); +// abstract public function rmdir($path); +// abstract public function opendir($path); + public function is_dir($path){ + return $this->filetype($path)=='dir'; + } + public function is_file($path){ + return $this->filetype($path)=='file'; + } +// abstract public function stat($path); +// abstract public function filetype($path); + public function filesize($path) { + if($this->is_dir($path)){ + return 0;//by definition + }else{ + $stat = $this->stat($path); + return $stat['size']; + } + } +// abstract public function is_readable($path); +// abstract public function is_writable($path); +// abstract public function file_exists($path); + public function filectime($path) { + $stat = $this->stat($path); + return $stat['ctime']; + } + public function filemtime($path) { + $stat = $this->stat($path); + return $stat['mtime']; + } + public function fileatime($path) { + $stat = $this->stat($path); + return $stat['atime']; + } + public function file_get_contents($path) { + $handle = $this->fopen($path, "r"); + if(!$handle){ + return false; + } + $size=$this->filesize($path); + if($size==0){ + return ''; + } + return fread($handle, $size); + } + public function file_put_contents($path,$data) { + $handle = $this->fopen($path, "w"); + return fwrite($handle, $data); + } +// abstract public function unlink($path); + public function rename($path1,$path2){ + if($this->copy($path1,$path2)){ + return $this->unlink($path1); + }else{ + return false; + } + } + public function copy($path1,$path2) { + $source=$this->fopen($path1,'r'); + $target=$this->fopen($path2,'w'); + $count=OC_Helper::streamCopy($source,$target); + return $count>0; + } +// abstract public function fopen($path,$mode); + public function getMimeType($path){ + if(!$this->file_exists($path)){ + return false; + } + if($this->is_dir($path)){ + return 'httpd/unix-directory'; + } + $source=$this->fopen($path,'r'); + if(!$source){ + return false; + } + $head=fread($source,8192);//8kb should suffice to determine a mimetype + if($pos=strrpos($path,'.')){ + $extention=substr($path,$pos); + }else{ + $extention=''; + } + $tmpFile=OC_Helper::tmpFile($extention); + file_put_contents($tmpFile,$head); + $mime=OC_Helper::getMimeType($tmpFile); + unlink($tmpFile); + return $mime; + } + public function hash($type,$path,$raw){ + $tmpFile=$this->getLocalFile(); + $hash=hash($type,$tmpFile,$raw); + unlink($tmpFile); + return $hash; + } +// abstract public function free_space($path); + public function search($query){ + return $this->searchInDir($query); + } + public function getLocalFile($path){ + return $this->toTmpFile($path); + } + private function toTmpFile($path){//no longer in the storage api, still usefull here + $source=$this->fopen($path,'r'); + if(!$source){ + return false; + } + if($pos=strrpos($path,'.')){ + $extention=substr($path,$pos); + }else{ + $extention=''; + } + $tmpFile=OC_Helper::tmpFile($extention); + $target=fopen($tmpFile,'w'); + $count=OC_Helper::streamCopy($source,$target); + return $tmpFile; + } +// abstract public function touch($path, $mtime=null); + + protected function searchInDir($query,$dir=''){ + $files=array(); + $dh=$this->opendir($dir); + if($dh){ + while($item=readdir($dh)){ + if ($item == '.' || $item == '..') continue; + if(strstr(strtolower($item),strtolower($query))!==false){ + $files[]=$dir.'/'.$item; + } + if($this->is_dir($dir.'/'.$item)){ + $files=array_merge($files,$this->searchInDir($query,$dir.'/'.$item)); + } + } + } + return $files; + } +} diff --git a/lib/filestorage/commontest.php b/lib/filestorage/commontest.php new file mode 100644 index 0000000000..1b01ff856a --- /dev/null +++ b/lib/filestorage/commontest.php @@ -0,0 +1,75 @@ +. +* +*/ + +/** + * test implementation for OC_FileStorage_Common with OC_FileStorage_Local + */ + +class OC_Filestorage_CommonTest extends OC_Filestorage_Common{ + /** + * underlying local storage used for missing functions + * @var OC_FileStorage_Local + */ + private $storage; + + public function __construct($params){ + $this->storage=new OC_Filestorage_Local($params); + } + + public function mkdir($path){ + return $this->storage->mkdir($path); + } + public function rmdir($path){ + return $this->storage->rmdir($path); + } + public function opendir($path){ + return $this->storage->opendir($path); + } + public function stat($path){ + return $this->storage->stat($path); + } + public function filetype($path){ + return $this->storage->filetype($path); + } + public function is_readable($path){ + return $this->storage->is_readable($path); + } + public function is_writable($path){ + return $this->storage->is_writable($path); + } + public function file_exists($path){ + return $this->storage->file_exists($path); + } + public function unlink($path){ + return $this->storage->unlink($path); + } + public function fopen($path,$mode){ + return $this->storage->fopen($path,$mode); + } + public function free_space($path){ + return $this->storage->free_space($path); + } + public function touch($path, $mtime=null){ + return $this->storage->touch($path,$mtime); + } +} \ No newline at end of file diff --git a/lib/filestorage/google.php b/lib/filestorage/google.php index fc271f4e4b..4998554838 100644 --- a/lib/filestorage/google.php +++ b/lib/filestorage/google.php @@ -20,36 +20,322 @@ * License along with this library. If not, see . */ -class OC_Filestorage_Google extends OC_Filestorage_Common { - - private $auth; +require_once 'common.inc.php'; - public function __construct($parameters) { +class OC_Filestorage_Google extends OC_Filestorage_Common { + + private $datadir; + private $consumer; + private $oauth_token; + private $sig_method; + private $entries; + + public function __construct($arguments) { + $this->datadir = $arguments['datadir']; + $consumer_key = isset($arguments['consumer_key']) ? $arguments['consumer_key'] : 'anonymous'; + $consumer_secret = isset($arguments['consumer_secret']) ? $arguments['consumer_secret'] : 'anonymous'; + $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); + $this->oauth_token = new OAuthToken($arguments['token'], $arguments['token_secret']); + $this->sig_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->entries = array(); + } + + private function sendRequest($feedUri, $http_method, $postData = null) { + $feedUri = trim($feedUri); + // create an associative array from each key/value url query param pair. + $params = array(); + $pieces = explode('?', $feedUri); + if (isset($pieces[1])) { + $params = explode_assoc('=', '&', $pieces[1]); + } + // urlencode each url parameter key/value pair + $tempStr = $pieces[0]; + foreach ($params as $key => $value) { + $tempStr .= '&' . urlencode($key) . '=' . urlencode($value); + } + $feedUri = preg_replace('/&/', '?', $tempStr, 1); + $req = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $http_method, $feedUri, $params); + $req->sign_request($this->sig_method, $this->consumer, $this->oauth_token); + $auth_header = $req->to_header(); + $result = send_signed_request($http_method, $feedUri, array($auth_header, 'Content-Type: application/atom+xml', 'GData-Version: 3.0'), $postData); + // TODO Return false if error is received + if (!$result) { + return false; + } + $result = explode('<', $result, 2); + $result = isset($result[1]) ? '<'.$result[1] : $result[0]; + $dom = new DOMDocument(); + $dom->loadXML($result); + return $dom; + } + + private function getResource($path) { + if (array_key_exists($path, $this->entries)) { + return $this->entries[$path]; + } else { + $title = basename($path); + $dom = $this->sendRequest('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$title, 'GET'); + // Check if request was successful and entry exists + if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) { + $this->entries[$path] = $entry; + return $entry; + } + return false; + } + } + + private function getExtension($entry) { + $mimetype = $this->getMimeType('', $entry); + switch($mimetype) { + case 'httpd/unix-directory': + return ''; + case 'application/vnd.oasis.opendocument.text': + return 'odt'; + case 'application/vnd.oasis.opendocument.spreadsheet': + return 'ods'; + case 'application/vnd.oasis.opendocument.presentation': + return 'pptx'; + case 'text/html': + return 'html'; + default: + return 'html'; + } + } + + + public function mkdir($path) { + $dir = dirname($path); + // Check if path parent is root directory + if ($dir == '/' || $dir == '\.' || $dir == '.') { + $feedUri = 'https://docs.google.com/feeds/default/private/full'; + // Get parent content link + } else if ($dom = $this->getResource(basename($dir))) { + $feedUri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src'); + } + if (isset($feedUri)) { + $title = basename($path); + // Construct post data + $postData = ''; + $postData .= ''; + $postData .= ''; + $postData .= ''.$title.''; + $postData .= ''; + if ($dom = $this->sendRequest($feedUri, 'POST', $postData)) { + return true; + } + } + return false; + } + + public function rmdir($path) { + return $this->unlink($path); + } + + public function opendir($path) { + if ($path == '' || $path == '/') { + $next = 'https://docs.google.com/feeds/default/private/full/folder%3Aroot/contents'; + } else { + if ($entry = $this->getResource($path)) { + $next = $entry->getElementsByTagName('content')->item(0)->getAttribute('src'); + } else { + return false; + } + } + $files = array(); + while ($next) { + $dom = $this->sendRequest($next, 'GET'); + $links = $dom->getElementsByTagName('link'); + foreach ($links as $link) { + if ($link->getAttribute('rel') == 'next') { + $next = $link->getAttribute('src'); + break; + } else { + $next = false; + } + } + $entries = $dom->getElementsByTagName('entry'); + foreach ($entries as $entry) { + $name = $entry->getElementsByTagName('title')->item(0)->nodeValue; + // Google Docs resources don't always include extensions in title + if (!strpos($name, '.')) { + $name .= '.'.$this->getExtension($entry); + } + $files[] = $name; + // Cache entry for future use + $this->entries[$name] = $entry; + } + } + OC_FakeDirStream::$dirs['google'] = $files; + return opendir('fakedir://google'); + } + + public function stat($path) { + if ($path == '' || $path == '/') { + $stat['size'] = $this->free_space($path); + $stat['atime'] = time(); + $stat['mtime'] = time(); + $stat['ctime'] = time(); + } else if ($entry = $this->getResource($path)) { + // NOTE: Native resources don't have a file size + $stat['size'] = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue; + $stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue); + $stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue); + $stat['ctime'] = strtotime($entry->getElementsByTagName('published')->item(0)->nodeValue); + } + return $stat; + } + + public function filetype($path) { + if ($path == '' || $path == '/') { + return 'dir'; + } else if ($entry = $this->getResource($path)) { + $categories = $entry->getElementsByTagName('category'); + foreach ($categories as $category) { + if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') { + $type = $category->getAttribute('label'); + if (strlen(strstr($type, 'folder')) > 0) { + return 'dir'; + } else { + return 'file'; + } + } + } + } + return false; + } + + public function is_readable($path) { + return true; + } + + public function is_writable($path) { + if ($path == '' || $path == '/') { + return true; + } else if ($entry = $this->getResource($path)) { + // Check if edit or edit-media links exist + $links = $entry->getElementsByTagName('link'); + foreach ($links as $link) { + if ($link->getAttribute('rel') == 'edit') { + return true; + } else if ($link->getAttribute('rel') == 'edit-media') { + return true; + } + } + } + return false; + } + + public function file_exists($path) { + if ($path == '' || $path == '/') { + return true; + } else if ($this->getResource($path)) { + return true; + } + return false; + } + + public function unlink($path) { + // Get resource self link to trash resource + if ($entry = $this->getResource($path)) { + $links = $entry->getElementsByTagName('link'); + foreach ($links as $link) { + if ($link->getAttribute('rel') == 'self') { + $feedUri = $link->getAttribute('href'); + } + } + } + if (isset($feedUri)) { + $this->sendRequest($feedUri, 'DELETE'); + return true; + } + return false; + } + + public function rename($path1, $path2) { + // TODO Add support for moving to different collections + // Get resource edit link to rename resource + if ($entry = $this->getResource($path1)) { + $etag = $entry->getElementsByTagName('entry')->item(0)->getAttribute('gd:etag'); + $links = $entry->getElementsByTagName('link'); + foreach ($links as $link) { + if ($link->getAttribute('rel') == 'edit') { + $feedUri = $link->getAttribute('href'); + } + } + } + if (isset($etag) && isset($feedUri)) { + $title = basename($path2); + // Construct post data + $postData = ''; + $postData .= ''; + $postData .= ''.$title.''; + $postData .= ''; + $this->sendRequest($feedUri, 'PUT', $postData); + return true; + } + return false; + } + + public function fopen($path, $mode) { + if ($entry = $this->getResource($path)) { + $extension = $this->getExtension($path); + $downloadUri = $entry->getElementsByTagName('content')->item(0)->getAttribute('src'); + // TODO Non-native documents don't need these additional parameters + $downloadUri .= '&exportFormat='.$extension.'&format='.$extension; + } + } + + public function getMimeType($path, $entry = null) { + if ($entry == null) { + if ($path == '' || $path == '/') { + return 'httpd/unix-directory'; + } else { + $entry = $this->getResource($path); + } + } + if ($entry) { + $mimetype = $entry->getElementsByTagName('content')->item(0)->getAttribute('type'); + // Native Google Docs resources often default to text/html, but it may be more useful to default to a corresponding ODF mimetype + // Collections get reported as application/atom+xml, make sure it actually is a folder and fix the mimetype + if ($mimetype == 'text/html' || $mimetype == 'application/atom+xml') { + $categories = $entry->getElementsByTagName('category'); + foreach ($categories as $category) { + if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') { + $type = $category->getAttribute('label'); + if (strlen(strstr($type, 'folder')) > 0) { + return 'httpd/unix-directory'; + } else if (strlen(strstr($type, 'document')) > 0) { + return 'application/vnd.oasis.opendocument.text'; + } else if (strlen(strstr($type, 'spreadsheet')) > 0) { + return 'application/vnd.oasis.opendocument.spreadsheet'; + } else if (strlen(strstr($type, 'presentation')) > 0) { + return 'application/vnd.oasis.opendocument.presentation'; + } else if (strlen(strstr($type, 'drawing')) > 0) { + return 'application/vnd.oasis.opendocument.graphics'; + } else { + // If nothing matches return text/html, all native Google Docs resources can be exported as text/html + return 'text/html'; + } + } + } + } + return $mimetype; + } + return false; + } + + public function free_space($path) { + if ($dom = $this->sendRequest('https://docs.google.com/feeds/metadata/default', 'GET')) { + // NOTE: Native Google Docs resources don't count towards quota + $total = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesTotal')->item(0)->nodeValue; + $used = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue; + return $total - $used; + } + return false; + } + + public function search($query) { } - - private function connect() { - - } - public function mkdir($path){} - public function rmdir($path){} - public function opendir($path){} - public function is_dir($path){} - public function is_file($path){} - public function stat($path){} - public function filetype($path){} - public function is_readable($path){} - public function is_writable($path){} - public function file_exists($path){} - public function unlink($path){} - public function rename($path1,$path2){} - public function fopen($path,$mode){} - public function toTmpFile($path){} - public function fromTmpFile($tmpPath,$path){} - public function fromUploadedFile($tmpPath,$path){} - public function getMimeType($path){} - public function hash($type,$path,$raw){} - public function free_space($path){} - public function search($query){} - public function getLocalFile($path){} + } \ No newline at end of file diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index dcb516a3af..688501aee9 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -21,7 +21,10 @@ class OC_Filestorage_Local extends OC_Filestorage{ return opendir($this->datadir.$path); } public function is_dir($path){ - return (is_dir($this->datadir.$path) or substr($path,-1)=='/'); + if(substr($path,-1)=='/'){ + $path=substr($path,0,-1); + } + return is_dir($this->datadir.$path); } public function is_file($path){ return is_file($this->datadir.$path); @@ -52,31 +55,32 @@ class OC_Filestorage_Local extends OC_Filestorage{ public function file_exists($path){ return file_exists($this->datadir.$path); } - public function readfile($path){ - return readfile($this->datadir.$path); - } public function filectime($path){ return filectime($this->datadir.$path); } public function filemtime($path){ return filemtime($this->datadir.$path); } - public function touch($path, $mtime){ + public function touch($path, $mtime=null){ // sets the modification time of the file to the given value. // If mtime is nil the current time is set. // note that the access time of the file always changes to the current time. - if( touch( $this->datadir.$path, $mtime ) ) { + if(!is_null($mtime)){ + $result=touch( $this->datadir.$path, $mtime ); + }else{ + $result=touch( $this->datadir.$path); + } + if( $result ) { clearstatcache( true, $this->datadir.$path ); } - return touch($this->datadir.$path, $mtime); + return $result; } public function file_get_contents($path){ return file_get_contents($this->datadir.$path); } public function file_put_contents($path,$data){ - if($return=file_put_contents($this->datadir.$path,$data)){ - } + return file_put_contents($this->datadir.$path,$data); } public function unlink($path){ return $this->delTree($path); @@ -99,9 +103,7 @@ class OC_Filestorage_Local extends OC_Filestorage{ $source=substr($path1,strrpos($path1,'/')+1); $path2.=$source; } - if($return=copy($this->datadir.$path1,$this->datadir.$path2)){ - } - return $return; + return copy($this->datadir.$path1,$this->datadir.$path2); } public function fopen($path,$mode){ if($return=fopen($this->datadir.$path,$mode)){ @@ -122,72 +124,9 @@ class OC_Filestorage_Local extends OC_Filestorage{ return $return; } - public function getMimeType($fspath){ - if($this->is_readable($fspath)){ - $mimeType='application/octet-stream'; - if ($mimeType=='application/octet-stream') { - self::$mimetypes = include('mimetypes.fixlist.php'); - $extention=strtolower(strrchr(basename($fspath), ".")); - $extention=substr($extention,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; - - } - if (@is_dir($this->datadir.$fspath)) { - // directories are easy - return "httpd/unix-directory"; - } - if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ - $mimeType =strtolower(finfo_file($finfo,$this->datadir.$fspath)); - $mimeType=substr($mimeType,0,strpos($mimeType,';')); - finfo_close($finfo); - } - if ($mimeType=='application/octet-stream' && function_exists("mime_content_type")) { - // use mime magic extension if available - $mimeType = mime_content_type($this->datadir.$fspath); - } - if ($mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) { - // it looks like we have a 'file' command, - // lets see it it does have mime support - $fspath=str_replace("'","\'",$fspath); - $fp = popen("file -i -b '{$this->datadir}$fspath' 2>/dev/null", "r"); - $reply = fgets($fp); - pclose($fp); - - //trim the character set from the end of the response - $mimeType=substr($reply,0,strrpos($reply,' ')); - } - if ($mimeType=='application/octet-stream') { - // Fallback solution: (try to guess the type by the file extension - if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ - self::$mimetypes=include('mimetypes.list.php'); - } - $extention=strtolower(strrchr(basename($fspath), ".")); - $extention=substr($extention,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; - } - return $mimeType; - }else{ - return false; - } - } - - public function toTmpFile($path){ - $tmpFolder=get_temp_dir(); - $filename=tempnam($tmpFolder,'OC_TEMP_FILE_'.substr($path,strrpos($path,'.'))); - $fileStats = stat($this->datadir.$path); - if(copy($this->datadir.$path,$filename)){ - touch($filename, $fileStats['mtime'], $fileStats['atime']); - return $filename; - }else{ - return false; - } - } - - public function fromTmpFile($tmpFile,$path){ - $fileStats = stat($tmpFile); - if(rename($tmpFile,$this->datadir.$path)){ - touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']); - return true; + public function getMimeType($path){ + if($this->is_readable($path)){ + return OC_Helper::getMimeType($this->datadir.$path); }else{ return false; } diff --git a/lib/filestoragecommon.php b/lib/filestoragecommon.php deleted file mode 100644 index f522d15c4e..0000000000 --- a/lib/filestoragecommon.php +++ /dev/null @@ -1,83 +0,0 @@ -. -*/ - -class OC_Filestorage_Common extends OC_Filestorage { - - public function __construct($parameters){} - public function mkdir($path){} - public function rmdir($path){} - public function opendir($path){} - public function is_dir($path){} - public function is_file($path){} - public function stat($path){} - public function filetype($path){} - public function filesize($path) { - $stat = $this->stat($path); - return $stat['size']; - } - public function is_readable($path){} - public function is_writable($path){} - public function file_exists($path){} - public function readfile($path) { - $handle = $this->fopen($path, "r"); - $chunk = 1024; - while (!feof($handle)) { - echo fread($handle, $chunk); - } - return $this->filesize($path); - } - public function filectime($path) { - $stat = $this->stat($path); - return $stat['ctime']; - } - public function filemtime($path) { - $stat = $this->stat($path); - return $stat['mtime']; - } - public function fileatime($path) { - $stat = $this->stat($path); - return $stat['atime']; - } - public function file_get_contents($path) { - $handle = $this->fopen($path, "r"); - return fread($handle, $this->filesize($path)); - } - public function file_put_contents($path,$data) { - $handle = $this->fopen($path, "w"); - return fwrite($handle, $data); - } - public function unlink($path){} - public function rename($path1,$path2){} - public function copy($path1,$path2) { - $data = $this->file_get_contents($path1); - return $this->file_put_contents($path2, $data); - } - public function fopen($path,$mode){} - public function toTmpFile($path){} - public function fromTmpFile($tmpPath,$path){} - public function fromUploadedFile($tmpPath,$path){} - public function getMimeType($path){} - public function hash($type,$path,$raw){} - public function free_space($path){} - public function search($query){} - public function getLocalFile($path){} -} diff --git a/lib/filesystem.php b/lib/filesystem.php index 90195bc213..12905d189f 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -146,20 +146,15 @@ class OC_Filesystem{ * @return string */ static public function getMountPoint($path){ + OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path)); if(!$path){ $path='/'; } if(substr($path,0,1)!=='/'){ $path='/'.$path; } - if(substr($path,-1)!=='/'){ - $path=$path.'/'; - } $foundMountPoint=''; foreach(OC_Filesystem::$mounts as $mountpoint=>$storage){ - if(substr($mountpoint,-1)!=='/'){ - $mountpoint=$mountpoint.'/'; - } if($mountpoint==$path){ return $mountpoint; } @@ -259,6 +254,9 @@ class OC_Filesystem{ * @param string mountpoint */ static public function mount($class,$arguments,$mountpoint){ + if(substr($mountpoint,-1)!=='/'){ + $mountpoint=$mountpoint.'/'; + } if(substr($mountpoint,0,1)!=='/'){ $mountpoint='/'.$mountpoint; } @@ -345,7 +343,7 @@ class OC_Filesystem{ static public function filemtime($path){ return self::$defaultInstance->filemtime($path); } - static public function touch($path, $mtime){ + static public function touch($path, $mtime=null){ return self::$defaultInstance->touch($path, $mtime); } static public function file_get_contents($path){ diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 91c6cd1772..89e0385fe9 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -136,7 +136,14 @@ class OC_FilesystemView { return $this->basicOperation('filesize',$path); } public function readfile($path){ - return $this->basicOperation('readfile',$path,array('read')); + $handle=$this->fopen($path,'r'); + $chunkSize = 1024*1024;// 1 MB chunks + while (!feof($handle)) { + echo fread($handle, $chunkSize); + @ob_flush(); + flush(); + } + return $this->filesize($path); } public function is_readable($path){ return $this->basicOperation('is_readable',$path); @@ -156,14 +163,27 @@ class OC_FilesystemView { public function filemtime($path){ return $this->basicOperation('filemtime',$path); } - public function touch($path, $mtime){ + public function touch($path, $mtime=null){ return $this->basicOperation('touch', $path, array('write'), $mtime); } public function file_get_contents($path){ return $this->basicOperation('file_get_contents',$path,array('read')); } public function file_put_contents($path,$data){ - return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + if(is_resource($data)){//not having to deal with streams in file_put_contents makes life easier + $target=$this->fopen($path,'w'); + if($target){ + $count=OC_Helper::streamCopy($data,$target); + fclose($target); + fclose($data); + OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path)); + return $count>0; + }else{ + return false; + } + }else{ + return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + } } public function unlink($path){ return $this->basicOperation('unlink',$path,array('delete')); @@ -179,10 +199,13 @@ class OC_FilesystemView { if($storage=$this->getStorage($path1)){ $result=$storage->rename($this->getInternalPath($path1),$this->getInternalPath($path2)); } - }elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){ - $tmpFile=$storage1->toTmpFile($this->getInternalPath($path1)); - $result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2)); + }else{ + $source=$this->fopen($path1,'r'); + $target=$this->fopen($path2,'w'); + $count=OC_Helper::streamCopy($data,$target); + $storage1=$this->getStorage($path1); $storage1->unlink($this->getInternalPath($path1)); + $result=$count>0; } OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, array( OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath=>$path2)); return $result; @@ -207,9 +230,10 @@ class OC_FilesystemView { if($storage=$this->getStorage($path1)){ $result=$storage->copy($this->getInternalPath($path1),$this->getInternalPath($path2)); } - }elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){ - $tmpFile=$storage1->toTmpFile($this->getInternalPath($path1)); - $result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2)); + }else{ + $source=$this->fopen($path1,'r'); + $target=$this->fopen($path2,'w'); + $count=OC_Helper::streamCopy($data,$target); } OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2)); if(!$exists){ @@ -224,18 +248,26 @@ class OC_FilesystemView { $hooks=array(); switch($mode){ case 'r': + case 'rb': $hooks[]='read'; break; case 'r+': + case 'rb+': case 'w+': + case 'wb+': case 'x+': + case 'xb+': case 'a+': + case 'ab+': $hooks[]='read'; $hooks[]='write'; break; case 'w': + case 'wb': case 'x': + case 'xb': case 'a': + case 'ab': $hooks[]='write'; break; default: @@ -245,29 +277,29 @@ class OC_FilesystemView { return $this->basicOperation('fopen',$path,$hooks,$mode); } public function toTmpFile($path){ - if(OC_FileProxy::runPreProxies('toTmpFile',$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_read, array( OC_Filesystem::signal_param_path => $path)); - return $storage->toTmpFile($this->getInternalPath($path)); + if(OC_Filesystem::isValidPath($path)){ + $source=$this->fopen($path,'r'); + if($source){ + $extention=substr($path,strrpos($path,'.')); + $tmpFile=OC_Helper::tmpFile($extention); + return file_put_contents($tmpFile,$source); + } } } public function fromTmpFile($tmpFile,$path){ - if(OC_FileProxy::runPreProxies('copy',$tmpFile,$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ - $run=true; - $exists=$this->file_exists($path); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); + if(OC_Filesystem::isValidPath($path)){ + if(!$tmpFile){ + debug_print_backtrace(); } - if($run){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - } - if($run){ - $result=$storage->fromTmpFile($tmpFile,$this->getInternalPath($path)); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path)); - } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path)); - return $result; + $source=fopen($tmpFile,'r'); + if($source){ + $this->file_put_contents($path,$source); + unlink($tmpFile); + return true; + }else{ } + }else{ + return false; } } @@ -291,26 +323,32 @@ class OC_FilesystemView { * @return mixed */ private function basicOperation($operation,$path,$hooks=array(),$extraParam=null){ - if(OC_FileProxy::runPreProxies($operation,$path, $extraParam) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ + if(OC_FileProxy::runPreProxies($operation,$path, $extraParam) and OC_Filesystem::isValidPath($path)){ $interalPath=$this->getInternalPath($path); $run=true; - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - }else{ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); + }else{ + OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); + } } } - if($run){ - if($extraParam){ + if($run and $storage=$this->getStorage($path)){ + if(!is_null($extraParam)){ $result=$storage->$operation($interalPath,$extraParam); }else{ $result=$storage->$operation($interalPath); } $result=OC_FileProxy::runPostProxies($operation,$path,$result); - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ + if($operation!='fopen'){//no post hooks for fopen, the file stream is still open + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + } + } } } return $result; diff --git a/lib/geo.php b/lib/geo.php new file mode 100644 index 0000000000..a967ab28a9 --- /dev/null +++ b/lib/geo.php @@ -0,0 +1,31 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +class OC_Geo{ + /* + * @brief returns the closest timezone to coordinates + * @param (string) $latitude - Latitude + * @param (string) $longitude - Longitude + * @return (string) $timezone - closest timezone + */ + public static function timezone($latitude, $longitude){ + $alltimezones = DateTimeZone::listIdentifiers(); + $variances = array(); + //calculate for all timezones the system know + foreach($alltimezones as $timezone){ + $datetimezoneobj = new DateTimeZone($timezone); + $locationinformations = $datetimezoneobj->getLocation(); + $latitudeoftimezone = $locationinformations['latitude']; + $longitudeoftimezone = $locationinformations['longitude']; + $variances[abs($latitudeoftimezone - $latitude) + abs($longitudeoftimezone - $longitude)] = $timezone; + } + //sort array and return the timezone with the smallest difference + ksort($variances); + reset($variances); + return current($variances); + } +} \ No newline at end of file diff --git a/lib/helper.php b/lib/helper.php index 2f71bdad2d..0c6c73aa76 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -25,6 +25,9 @@ * Collection of useful functions */ class OC_Helper { + private static $mimetypes=array(); + private static $tmpFiles=array(); + /** * @brief Creates an url * @param $app app @@ -37,8 +40,8 @@ class OC_Helper { if( $app != '' ){ $app .= '/'; // Check if the app is in the app folder - if( file_exists( OC::$SERVERROOT . '/apps/'. $app.$file )){ - $urlLinkTo = OC::$WEBROOT . '/apps/' . $app . $file; + if( file_exists( OC::$APPSROOT . '/apps/'. $app.$file )){ + $urlLinkTo = OC::$APPSWEBROOT . '/apps/' . $app . $file; } else{ $urlLinkTo = OC::$WEBROOT . '/' . $app . $file; @@ -81,24 +84,27 @@ class OC_Helper { * Returns the path to the image. */ public static function imagePath( $app, $image ){ - // Read the selected theme from the config file - $theme=OC_Config::getValue( "theme" ); + // Read the selected theme from the config file + $theme=OC_Config::getValue( "theme" ); - // Check if the app is in the app folder - if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image" )){ - return OC::$WEBROOT."/themes/$theme/apps/$app/img/$image"; - }elseif( file_exists( OC::$SERVERROOT."/apps/$app/img/$image" )){ - return OC::$WEBROOT."/apps/$app/img/$image"; - }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/themes/$theme/$app/img/$image" )){ - return OC::$WEBROOT."/themes/$theme/$app/img/$image"; - }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/$app/img/$image" )){ - return OC::$WEBROOT."/$app/img/$image"; - }elseif( file_exists( OC::$SERVERROOT."/themes/$theme/core/img/$image" )){ - return OC::$WEBROOT."/themes/$theme/core/img/$image"; - }else{ - return OC::$WEBROOT."/core/img/$image"; - } - } + // Check if the app is in the app folder + if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image" )){ + return OC::$WEBROOT."/themes/$theme/apps/$app/img/$image"; + }elseif( file_exists( OC::$APPSROOT."/apps/$app/img/$image" )){ + return OC::$APPSWEBROOT."/apps/$app/img/$image"; + }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/themes/$theme/$app/img/$image" )){ + return OC::$WEBROOT."/themes/$theme/$app/img/$image"; + }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/$app/img/$image" )){ + return OC::$WEBROOT."/$app/img/$image"; + }elseif( file_exists( OC::$SERVERROOT."/themes/$theme/core/img/$image" )){ + return OC::$WEBROOT."/themes/$theme/core/img/$image"; + }elseif( file_exists( OC::$SERVERROOT."/core/img/$image" )){ + return OC::$WEBROOT."/core/img/$image"; + }else{ + echo('image not found: image:'.$image.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); + die(); + } + } /** * @brief get path to icon of file type @@ -194,7 +200,7 @@ class OC_Helper { $bytes *= $bytes_array[$matches[1]]; } - $bytes = intval(round($bytes, 2)); + $bytes = round($bytes, 2); return $bytes; } @@ -267,6 +273,62 @@ class OC_Helper { unlink($dir); } } + + /** + * get the mimetype form a local file + * @param string path + * @return string + * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead + */ + static function getMimeType($path){ + $isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://'); + $mimeType='application/octet-stream'; + if ($mimeType=='application/octet-stream') { + if(count(self::$mimetypes)>0){ + self::$mimetypes = include('mimetypes.fixlist.php'); + } + $extention=strtolower(strrchr(basename($path), ".")); + $extention=substr($extention,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; + + } + if (@is_dir($path)) { + // directories are easy + return "httpd/unix-directory"; + } + if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ + $info = @strtolower(finfo_file($finfo,$path)); + if($info){ + $mimeType=substr($info,0,strpos($info,';')); + } + finfo_close($finfo); + } + if (!$isWrapped and $mimeType=='application/octet-stream' && function_exists("mime_content_type")) { + // use mime magic extension if available + $mimeType = mime_content_type($path); + } + if (!$isWrapped and $mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) { + // it looks like we have a 'file' command, + // lets see it it does have mime support + $path=str_replace("'","\'",$path); + $fp = popen("file -i -b '$path' 2>/dev/null", "r"); + $reply = fgets($fp); + pclose($fp); + + //trim the character set from the end of the response + $mimeType=substr($reply,0,strrpos($reply,' ')); + } + if ($mimeType=='application/octet-stream') { + // Fallback solution: (try to guess the type by the file extension + if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ + self::$mimetypes=include('mimetypes.list.php'); + } + $extention=strtolower(strrchr(basename($path), ".")); + $extention=substr($extention,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; + } + return $mimeType; + } /** * @brief Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d. @@ -340,4 +402,47 @@ class OC_Helper { } return false; } + + /** + * copy the contents of one stream to another + * @param resource source + * @param resource target + * @return int the number of bytes copied + */ + public static function streamCopy($source,$target){ + if(!$source or !$target){ + return false; + } + $count=0; + while(!feof($source)){ + $count+=fwrite($target,fread($source,8192)); + } + return $count; + } + + /** + * create a temporary file with an unique filename + * @param string postfix + * @return string + * + * temporary files are automatically cleaned up after the script is finished + */ + public static function tmpFile($postfix=''){ + $file=get_temp_dir().'/'.md5(time().rand()).$postfix; + $fh=fopen($file,'w'); + fclose($fh); + self::$tmpFiles[]=$file; + return $file; + } + + /** + * remove all files created by self::tmpFile + */ + public static function cleanTmp(){ + foreach(self::$tmpFiles as $file){ + if(file_exists($file)){ + unlink($file); + } + } + } } diff --git a/lib/image.php b/lib/image.php index fe8349be54..60a714880d 100644 --- a/lib/image.php +++ b/lib/image.php @@ -216,7 +216,7 @@ class OC_Image { OC_Log::write('core','OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG); return false; } - $exif = exif_read_data($this->filepath, 'IFD0'); + $exif = @exif_read_data($this->filepath, 'IFD0'); if(!$exif) { return false; } diff --git a/lib/installer.php b/lib/installer.php index b2f817e702..2a9676998f 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -62,7 +62,7 @@ class OC_Installer{ //download the file if necesary if($data['source']=='http'){ - $path=tempnam(get_temp_dir(),'oc_installer_'); + $path=OC_Helper::tmpFile('.zip'); if(!isset($data['href'])){ OC_Log::write('core','No href specified when installing app from http',OC_Log::ERROR); return false; @@ -103,7 +103,7 @@ class OC_Installer{ return false; } $info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml'); - $basedir=OC::$SERVERROOT.'/apps/'.$info['id']; + $basedir=OC::$APPSROOT.'/apps/'.$info['id']; //check if an app with the same id is already installed if(self::isInstalled( $info['id'] )){ @@ -142,9 +142,6 @@ class OC_Installer{ //remove temporary files OC_Helper::rmdirr($extractDir); - if($data['source']=='http'){ - unlink($path); - } //install the database if(is_file($basedir.'/appinfo/database.xml')){ @@ -244,10 +241,10 @@ class OC_Installer{ * If $enabled is false, apps are installed as disabled. */ public static function installShippedApps(){ - $dir = opendir( OC::$SERVERROOT."/apps" ); + $dir = opendir( OC::$APPSROOT."/apps" ); while( false !== ( $filename = readdir( $dir ))){ - if( substr( $filename, 0, 1 ) != '.' and is_dir(OC::$SERVERROOT."/apps/$filename") ){ - if( file_exists( OC::$SERVERROOT."/apps/$filename/appinfo/app.php" )){ + if( substr( $filename, 0, 1 ) != '.' and is_dir(OC::$APPSROOT."/apps/$filename") ){ + if( file_exists( OC::$APPSROOT."/apps/$filename/appinfo/app.php" )){ if(!OC_Installer::isInstalled($filename)){ $info = OC_Installer::installShippedApp($filename); $enabled = isset($info['default_enable']); @@ -270,15 +267,15 @@ class OC_Installer{ */ public static function installShippedApp($app){ //install the database - if(is_file(OC::$SERVERROOT."/apps/$app/appinfo/database.xml")){ - OC_DB::createDbFromStructure(OC::$SERVERROOT."/apps/$app/appinfo/database.xml"); + if(is_file(OC::$APPSROOT."/apps/$app/appinfo/database.xml")){ + OC_DB::createDbFromStructure(OC::$APPSROOT."/apps/$app/appinfo/database.xml"); } //run appinfo/install.php - if(is_file(OC::$SERVERROOT."/apps/$app/appinfo/install.php")){ - include(OC::$SERVERROOT."/apps/$app/appinfo/install.php"); + if(is_file(OC::$APPSROOT."/apps/$app/appinfo/install.php")){ + include(OC::$APPSROOT."/apps/$app/appinfo/install.php"); } - $info=OC_App::getAppInfo(OC::$SERVERROOT."/apps/$app/appinfo/info.xml"); + $info=OC_App::getAppInfo(OC::$APPSROOT."/apps/$app/appinfo/info.xml"); OC_Appconfig::setValue($app,'installed_version',$info['version']); return $info; } diff --git a/lib/l10n.php b/lib/l10n.php index a5544eb3a2..636326f986 100644 --- a/lib/l10n.php +++ b/lib/l10n.php @@ -243,8 +243,8 @@ class OC_L10N{ $i18ndir = OC::$SERVERROOT.'/core/l10n/'; if($app != ''){ // Check if the app is in the app folder - if(file_exists(OC::$SERVERROOT.'/apps/'.$app.'/l10n/')){ - $i18ndir = OC::$SERVERROOT.'/apps/'.$app.'/l10n/'; + if(file_exists(OC::$APPSROOT.'/apps/'.$app.'/l10n/')){ + $i18ndir = OC::$APPSROOT.'/apps/'.$app.'/l10n/'; } else{ $i18ndir = OC::$SERVERROOT.'/'.$app.'/l10n/'; diff --git a/lib/log.php b/lib/log.php index 446ddd4884..4e450a027f 100644 --- a/lib/log.php +++ b/lib/log.php @@ -3,7 +3,7 @@ * ownCloud * * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com + * @copyright 2012 Robin Appelman icewind1991@gmail.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -50,25 +50,29 @@ class OC_Log{ fclose($fh); } } - - public static function getEntries(){ + + /** + * get entries from the log in reverse chronological order + * @param int limit + * @param int offset + * @return array + */ + public static function getEntries($limit=50,$offset=0){ $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); $logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' ); $entries=array(); if(!file_exists($logFile)){ return array(); } - $fh=fopen($logFile,'r'); - if($fh === false){ // Unable to read log file! + $contents=file($logFile); + if(!$contents){//error while reading log return array(); } - while(!feof($fh)){ - $line=fgets($fh); - if($line){ - $entries[]=json_decode($line); - } + $end=max(count($contents)-$offset-1,0); + $start=max($end-$limit,0); + for($i=$end;$i>$start;$i--){ + $entries[]=json_decode($contents[$i]); } - fclose($fh); return $entries; } } diff --git a/lib/search.php b/lib/search.php index f6f805bfe6..6b33fa3814 100644 --- a/lib/search.php +++ b/lib/search.php @@ -29,7 +29,7 @@ class OC_Search{ /** * register a new search provider to be used - * @param OC_Search_Provider $provider + * @param string $provider class name of a OC_Search_Provider */ public static function registerProvider($provider){ self::$providers[]=$provider; @@ -43,7 +43,7 @@ class OC_Search{ public static function search($query){ $results=array(); foreach(self::$providers as $provider){ - $results=array_merge($results,$provider->search($query)); + $results=array_merge($results, $provider::search($query)); } return $results; } diff --git a/lib/search/provider.php b/lib/search/provider.php index cceed8b04a..9487ca51f2 100644 --- a/lib/search/provider.php +++ b/lib/search/provider.php @@ -2,15 +2,11 @@ /** * provides search functionalty */ -abstract class OC_Search_Provider{ - public function __construct(){ - OC_Search::registerProvider($this); - } - +interface OC_Search_Provider { /** * search for $query * @param string $query * @return array An array of OC_Search_Result's */ - abstract function search($query); + static function search($query); } diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php index 34803c75ae..3bdb3bcd2a 100644 --- a/lib/search/provider/file.php +++ b/lib/search/provider/file.php @@ -1,15 +1,15 @@ name=substr($path,strlen('fakedir://')); + $this->index=0; + if(!isset(self::$dirs[$this->name])){ + self::$dirs[$this->name]=array(); + } + return true; + } + + public function dir_readdir(){ + if($this->index>=count(self::$dirs[$this->name])){ + return false; + } + $filename=self::$dirs[$this->name][$this->index]; + $this->index++; + return $filename; + } + + public function dir_closedir() { + $this->name=''; + return true; + } + + public function dir_rewinddir() { + $this->index=0; + return true; + } +} + +class OC_StaticStreamWrapper { + public $context; + protected static $data = array(); + + protected $path = ''; + protected $pointer = 0; + protected $writable = false; + + public function stream_close() {} + + public function stream_eof() { + return $this->pointer >= strlen(self::$data[$this->path]); + } + + public function stream_flush() {} + + public function stream_open($path, $mode, $options, &$opened_path) { + switch ($mode[0]) { + case 'r': + if (!isset(self::$data[$path])) return false; + $this->path = $path; + $this->writable = isset($mode[1]) && $mode[1] == '+'; + break; + case 'w': + self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + break; + case 'a': + if (!isset(self::$data[$path])) self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + $this->pointer = strlen(self::$data[$path]); + break; + case 'x': + if (isset(self::$data[$path])) return false; + $this->path = $path; + $this->writable = true; + break; + case 'c': + if (!isset(self::$data[$path])) self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + break; + default: + return false; + } + $opened_path = $this->path; + return true; + } + + public function stream_read($count) { + $bytes = min(strlen(self::$data[$this->path]) - $this->pointer, $count); + $data = substr(self::$data[$this->path], $this->pointer, $bytes); + $this->pointer += $bytes; + return $data; + } + + public function stream_seek($offset, $whence = SEEK_SET) { + $len = strlen(self::$data[$this->path]); + switch ($whence) { + case SEEK_SET: + if ($offset <= $len) { + $this->pointer = $offset; + return true; + } + break; + case SEEK_CUR: + if ($this->pointer + $offset <= $len) { + $this->pointer += $offset; + return true; + } + break; + case SEEK_END: + if ($len + $offset <= $len) { + $this->pointer = $len + $offset; + return true; + } + break; + } + return false; + } + + public function stream_stat() { + $size = strlen(self::$data[$this->path]); + $time = time(); + return array( + 0 => 0, + 'dev' => 0, + 1 => 0, + 'ino' => 0, + 2 => 0777, + 'mode' => 0777, + 3 => 1, + 'nlink' => 1, + 4 => 0, + 'uid' => 0, + 5 => 0, + 'gid' => 0, + 6 => '', + 'rdev' => '', + 7 => $size, + 'size' => $size, + 8 => $time, + 'atime' => $time, + 9 => $time, + 'mtime' => $time, + 10 => $time, + 'ctime' => $time, + 11 => -1, + 'blksize' => -1, + 12 => -1, + 'blocks' => -1, + ); + } + + public function stream_tell() { + return $this->pointer; + } + + public function stream_write($data) { + if (!$this->writable) return 0; + $size = strlen($data); + $len = strlen(self::$data[$this->path]); + if ($this->stream_eof()) { + self::$data[$this->path] .= $data; + } else { + self::$data[$this->path] = substr_replace( + self::$data[$this->path], + $data, + $this->pointer + ); + } + $this->pointer += $size; + return $size; + } + + public function unlink($path) { + if (isset(self::$data[$path])) { + unset(self::$data[$path]); + } + return true; + } + + public function url_stat($path) { + if (isset(self::$data[$path])) { + $size = strlen(self::$data[$path]); + $time = time(); + return array( + 0 => 0, + 'dev' => 0, + 1 => 0, + 'ino' => 0, + 2 => 0777, + 'mode' => 0777, + 3 => 1, + 'nlink' => 1, + 4 => 0, + 'uid' => 0, + 5 => 0, + 'gid' => 0, + 6 => '', + 'rdev' => '', + 7 => $size, + 'size' => $size, + 8 => $time, + 'atime' => $time, + 9 => $time, + 'mtime' => $time, + 10 => $time, + 'ctime' => $time, + 11 => -1, + 'blksize' => -1, + 12 => -1, + 'blocks' => -1, + ); + } + return false; + } +} + +/** + * stream wrapper that provides a callback on stream close + */ +class OC_CloseStreamWrapper{ + public static $callBacks=array(); + private $path=''; + private $source; + private static $open=array(); + public function stream_open($path, $mode, $options, &$opened_path){ + $path=substr($path,strlen('close://')); + $this->path=$path; + $this->source=fopen($path,$mode); + if(is_resource($this->source)){ + $this->meta=stream_get_meta_data($this->source); + } + self::$open[]=$path; + return is_resource($this->source); + } + + public function stream_seek($offset, $whence=SEEK_SET){ + fseek($this->source,$offset,$whence); + } + + public function stream_tell(){ + return ftell($this->source); + } + + public function stream_read($count){ + return fread($this->source,$count); + } + + public function stream_write($data){ + return fwrite($this->source,$data); + } + + public function stream_set_option($option,$arg1,$arg2){ + switch($option){ + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->source,$arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->source,$arg1,$arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->source,$arg1,$arg2); + } + } + + public function stream_stat(){ + return fstat($this->source); + } + + public function stream_lock($mode){ + flock($this->source,$mode); + } + + public function stream_flush(){ + return fflush($this->source); + } + + public function stream_eof(){ + return feof($this->source); + } + + public function url_stat($path) { + $path=substr($path,strlen('close://')); + if(file_exists($path)){ + return stat($path); + }else{ + return false; + } + } + + public function stream_close(){ + fclose($this->source); + if(isset(self::$callBacks[$this->path])){ + call_user_func(self::$callBacks[$this->path],$this->path); + } + } + + public function unlink($path){ + $path=substr($path,strlen('close://')); + return unlink($path); + } +} diff --git a/lib/template.php b/lib/template.php index 5fe2eb7d6c..eea2925975 100644 --- a/lib/template.php +++ b/lib/template.php @@ -171,7 +171,7 @@ class OC_Template{ // Check if it is a app template or not. if( $app != "" ){ // Check if the app is in the app folder or in the root - if( file_exists( OC::$SERVERROOT."/apps/$app/templates/" )){ + if( file_exists( OC::$APPSROOT."/apps/$app/templates/" )){ // Check if the template is overwritten by the selected theme if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name$fext.php" )){ $template = OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name$fext.php"; @@ -179,12 +179,12 @@ class OC_Template{ }elseif( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name.php" )){ $template = OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"."$name.php"; $path = OC::$SERVERROOT."/themes/$theme/apps/$app/templates/"; - }elseif( OC::$SERVERROOT."/apps/$app/templates/"."$name$fext.php" ){ - $template = OC::$SERVERROOT."/apps/$app/templates/"."$name$fext.php"; - $path = OC::$SERVERROOT."/apps/$app/templates/"; + }elseif( OC::$APPSROOT."/apps/$app/templates/"."$name$fext.php" ){ + $template = OC::$APPSROOT."/apps/$app/templates/"."$name$fext.php"; + $path = OC::$APPSROOT."/apps/$app/templates/"; }else{ - $template = OC::$SERVERROOT."/apps/$app/templates/"."$name.php"; - $path = OC::$SERVERROOT."/apps/$app/templates/"; + $template = OC::$APPSROOT."/apps/$app/templates/"."$name.php"; + $path = OC::$APPSROOT."/apps/$app/templates/"; } }else{ // Check if the template is overwritten by the selected theme @@ -197,12 +197,15 @@ class OC_Template{ }elseif( file_exists( OC::$SERVERROOT."/$app/templates/"."$name$fext.php" )){ $template = OC::$SERVERROOT."/$app/templates/"."$name$fext.php"; $path = OC::$SERVERROOT."/$app/templates/"; - }else{ + }elseif( file_exists( OC::$SERVERROOT."/$app/templates/"."$name.php" )){ $template = OC::$SERVERROOT."/$app/templates/"."$name.php"; $path = OC::$SERVERROOT."/$app/templates/"; + }else{ + echo('template not found: template:'.$name.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); + die(); } - } + } }else{ // Check if the template is overwritten by the selected theme if( file_exists( OC::$SERVERROOT."/themes/$theme/core/templates/"."$name$fext.php" )){ @@ -357,10 +360,10 @@ class OC_Template{ $page->append( "jsfiles", OC::$WEBROOT."/themes/$theme/apps/$script.js" ); // Is it part of an app? - }elseif(is_file(OC::$SERVERROOT."/apps/$script$fext.js" )){ - $page->append( "jsfiles", OC::$WEBROOT."/apps/$script$fext.js" ); - }elseif(is_file(OC::$SERVERROOT."/apps/$script.js" )){ - $page->append( "jsfiles", OC::$WEBROOT."/apps/$script.js" ); + }elseif(is_file(OC::$APPSROOT."/apps/$script$fext.js" )){ + $page->append( "jsfiles", OC::$APPSWEBROOT."/apps/$script$fext.js" ); + }elseif(is_file(OC::$APPSROOT."/apps/$script.js" )){ + $page->append( "jsfiles", OC::$APPSWEBROOT."/apps/$script.js" ); // Is it in the owncloud root but overwritten by the theme? }elseif(is_file(OC::$SERVERROOT."/themes/$theme/$script$fext.js" )){ @@ -383,9 +386,13 @@ class OC_Template{ // Is it in core? }elseif(is_file(OC::$SERVERROOT."/core/$script$fext.js" )){ $page->append( "jsfiles", OC::$WEBROOT."/core/$script$fext.js" ); - }else{ + }elseif(is_file(OC::$SERVERROOT."/core/$script.js" )){ $page->append( "jsfiles", OC::$WEBROOT."/core/$script.js" ); + }else{ + echo('js file not found: script:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); + die(); + } } // Add the css files @@ -394,10 +401,10 @@ class OC_Template{ if(is_file(OC::$THIRDPARTYROOT."/$style.css" )){ $page->append( "cssfiles", OC::$THIRDPARTYWEBROOT."/$style.css" ); // or in apps? - }elseif(is_file(OC::$SERVERROOT."/apps/$style$fext.css" )){ - $page->append( "cssfiles", OC::$WEBROOT."/apps/$style$fext.css" ); - }elseif(is_file(OC::$SERVERROOT."/apps/$style.css" )){ - $page->append( "cssfiles", OC::$WEBROOT."/apps/$style.css" ); + }elseif(is_file(OC::$APPSROOT."/apps/$style$fext.css" )){ + $page->append( "cssfiles", OC::$APPSWEBROOT."/apps/$style$fext.css" ); + }elseif(is_file(OC::$APPSROOT."/apps/$style.css" )){ + $page->append( "cssfiles", OC::$APPSWEBROOT."/apps/$style.css" ); // or in the owncloud root? }elseif(is_file(OC::$SERVERROOT."/$style$fext.css" )){ $page->append( "cssfiles", OC::$WEBROOT."/$style$fext.css" ); @@ -406,8 +413,12 @@ class OC_Template{ // or in core ? }elseif(is_file(OC::$SERVERROOT."/core/$style$fext.css" )){ $page->append( "cssfiles", OC::$WEBROOT."/core/$style$fext.css" ); - }else{ + }elseif(is_file(OC::$SERVERROOT."/core/$style.css" )){ $page->append( "cssfiles", OC::$WEBROOT."/core/$style.css" ); + + }else{ + echo('css file not found: style:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); + die(); } } // Add the theme css files. you can override the default values here diff --git a/lib/user.php b/lib/user.php index 0746fcc6eb..fda19a3315 100644 --- a/lib/user.php +++ b/lib/user.php @@ -195,8 +195,9 @@ class OC_User { if( $run ){ $uid=self::checkPassword( $uid, $password ); if($uid){ - OC_Crypt::init($uid,$password); - return self::setUserId($uid); + self::setUserId($uid); + OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password )); + return true; } } return false; @@ -209,7 +210,6 @@ class OC_User { */ public static function setUserId($uid) { $_SESSION['user_id'] = $uid; - OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid )); return true; } diff --git a/lib/user/database.php b/lib/user/database.php index 452709c1fb..3eade276dd 100644 --- a/lib/user/database.php +++ b/lib/user/database.php @@ -33,12 +33,28 @@ * */ +require_once 'phpass/PasswordHash.php'; + /** * Class for user management in a SQL Database (e.g. MySQL, SQLite) */ class OC_User_Database extends OC_User_Backend { static private $userGroupCache=array(); + /** + * @var PasswordHash + */ + static private $hasher=null; + + private function getHasher(){ + if(!self::$hasher){ + //we don't want to use DES based crypt(), since it doesn't return a has with a recognisable prefix + $forcePortable=(CRYPT_BLOWFISH!=1); + self::$hasher=new PasswordHash(8,$forcePortable); + } + return self::$hasher; + } + /** * @brief Create a new user * @param $uid The username of the user to create @@ -51,10 +67,11 @@ class OC_User_Database extends OC_User_Backend { public function createUser( $uid, $password ){ if( $this->userExists($uid) ){ return false; - } - else{ + }else{ + $hasher=$this->getHasher(); + $hash = $hasher->HashPassword($password); $query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" ); - $result = $query->execute( array( $uid, sha1( $password ))); + $result = $query->execute( array( $uid, $hash)); return $result ? true : false; } @@ -84,8 +101,10 @@ class OC_User_Database extends OC_User_Backend { */ public function setPassword( $uid, $password ){ if( $this->userExists($uid) ){ + $hasher=$this->getHasher(); + $hash = $hasher->HashPassword($password); $query = OC_DB::prepare( "UPDATE *PREFIX*users SET password = ? WHERE uid = ?" ); - $result = $query->execute( array( sha1( $password ), $uid )); + $result = $query->execute( array( $hash, $uid )); return true; } @@ -103,12 +122,28 @@ class OC_User_Database extends OC_User_Backend { * Check if the password is correct without logging in the user */ public function checkPassword( $uid, $password ){ - $query = OC_DB::prepare( "SELECT uid FROM *PREFIX*users WHERE uid LIKE ? AND password = ?" ); - $result = $query->execute( array( $uid, sha1( $password ))); + $query = OC_DB::prepare( "SELECT uid, password FROM *PREFIX*users WHERE uid LIKE ?" ); + $result = $query->execute( array( $uid)); $row=$result->fetchRow(); if($row){ - return $row['uid']; + $storedHash=$row['password']; + if (substr($storedHash,0,1)=='$'){//the new phpass based hashing + $hasher=$this->getHasher(); + if($hasher->CheckPassword($password, $storedHash)){ + return $row['uid']; + }else{ + return false; + } + }else{//old sha1 based hashing + if(sha1($password)==$storedHash){ + //upgrade to new hashing + $this->setPassword($row['uid'],$password); + return $row['uid']; + }else{ + return false; + } + } }else{ return false; } diff --git a/lib/util.php b/lib/util.php index 05caeca0e3..fa5b3daaab 100644 --- a/lib/util.php +++ b/lib/util.php @@ -8,6 +8,7 @@ class OC_Util { public static $scripts=array(); public static $styles=array(); public static $headers=array(); + private static $rootMounted=false; private static $fsSetup=false; // Can be set up @@ -35,9 +36,12 @@ class OC_Util { $user = OC_User::getUser(); } - if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem - //first set up the local "root" storage + //first set up the local "root" storage + if(!self::$rootMounted){ OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); + self::$rootMounted=true; + } + if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root"; if( !is_dir( OC::$CONFIG_DATADIRECTORY )){ @@ -62,7 +66,7 @@ class OC_Util { * @return array */ public static function getVersion(){ - return array(3,00,2); + return array(3,00,3); } /** diff --git a/lib/vcategories.php b/lib/vcategories.php new file mode 100644 index 0000000000..5a7bacd202 --- /dev/null +++ b/lib/vcategories.php @@ -0,0 +1,211 @@ + +* @copyright 2012 Bart Visscher bartv@thisnet.nl +* +* 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 . +* +*/ + + +/** + * Class for easy access to categories in VCARD, VEVENT, VTODO and VJOURNAL. + * A Category can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or + * anything else that is either parsed from a vobject or that the user chooses + * to add. + * Category names are not case-sensitive, but will be saved with the case they + * are entered in. If a user already has a category 'family' for an app, and + * tries to add a category named 'Family' it will be silently ignored. + * NOTE: There is a limitation in that the the configvalue field in the + * preferences table is a varchar(255). + */ +class OC_VCategories { + const PREF_CATEGORIES_LABEL = 'extra_categories'; + /** + * Categories + */ + private $categories = array(); + + private $app = null; + private $user = null; + + /** + * @brief Constructor. + * @param $app The application identifier e.g. 'contacts' or 'calendar'. + * @param $user The user whos data the object will operate on. This + * parameter should normally be omitted but to make an app able to + * update categories for all users it is made possible to provide it. + * @param $defcategories An array of default categories to be used if none is stored. + * NOTE: Not implemented. + */ + public function __construct($app, $user=null, $defcategories=null) { + $this->app = $app; + $this->user = is_null($user) ? OC_User::getUser() : $user; + $categories = trim(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, '')); + $this->categories = $categories != '' ? unserialize($categories) : array(); + } + + /** + * @brief Get the categories for a specific user. + * @returns array containing the categories as strings. + */ + public function categories() { + OC_Log::write('core','OC_VCategories::categories: '.print_r($this->categories, true), OC_Log::DEBUG); + return $this->categories; + } + + /** + * @brief Checks whether a category is already saved. + * @param $name The name to check for. + * @returns bool + */ + public function hasCategory($name) { + return $this->in_arrayi($name, $this->categories); + } + + /** + * @brief Add a new category name. + * @param $names A string with a name or an array of strings containing + * the name(s) of the categor(y|ies) to add. + * @param $sync bool When true, save the categories + * @returns bool Returns false on error. + */ + public function add($names, $sync=false) { + if(!is_array($names)) { + $names = array($names); + } + $names = array_map('trim', $names); + $newones = array(); + foreach($names as $name) { + if(($this->in_arrayi($name, $this->categories) == false) && $name != '') { + $newones[] = $name; + } + } + if(count($newones) > 0) { + $this->categories = array_merge($this->categories, $newones); + natcasesort($this->categories); // Dunno if this is necessary + if($sync === true) { + $this->save(); + } + } + return true; + } + + /** + * @brief Extracts categories from a vobject and add the ones not already present. + * @param $vobject The instance of OC_VObject to load the categories from. + */ + public function loadFromVObject($vobject, $sync=false) { + $this->add($vobject->getAsArray('CATEGORIES'), $sync); + } + + /** + * @brief Reset saved categories and rescan supplied vobjects for categories. + * @param $objects An array of vobjects (as text). + * To get the object array, do something like: + * // For Addressbook: + * $categories = new OC_VCategories('contacts'); + * $stmt = OC_DB::prepare( 'SELECT carddata FROM *PREFIX*contacts_cards' ); + * $result = $stmt->execute(); + * $objects = array(); + * if(!is_null($result)) { + * while( $row = $result->fetchRow()){ + * $objects[] = $row['carddata']; + * } + * } + * $categories->rescan($objects); + */ + public function rescan($objects, $sync=true) { + $this->categories = array(); + foreach($objects as $object) { + //OC_Log::write('core','OC_VCategories::rescan: '.substr($object, 0, 100).'(...)', OC_Log::DEBUG); + $vobject = OC_VObject::parse($object); + if(!is_null($vobject)) { + $this->loadFromVObject($vobject, $sync); + } else { + OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 50).'(...)', OC_Log::DEBUG); + } + } + $this->save(); + } + + /** + * @brief Save the list with categories + */ + private function save() { + $escaped_categories = serialize($this->categories); + OC_Log::write('core','OC_VCategories::save: '.print_r($this->categories, true), OC_Log::DEBUG); + OC_Preferences::setValue($this->user, $this->app, self::PREF_CATEGORIES_LABEL, $escaped_categories); + } + + /** + * @brief Delete categories from the db and from all the vobject supplied + * @param $names An array of categories to delete + * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table. + */ + public function delete($names, array &$objects=null) { + if(!is_array($names)) { + $names = array($names); + } + OC_Log::write('core','OC_VCategories::delete, before: '.print_r($this->categories, true), OC_Log::DEBUG); + foreach($names as $name) { + OC_Log::write('core','OC_VCategories::delete: '.$name, OC_Log::DEBUG); + if($this->hasCategory($name)) { + OC_Log::write('core','OC_VCategories::delete: '.$name.' got it', OC_Log::DEBUG); + unset($this->categories[$this->array_searchi($name, $this->categories)]); + } + } + $this->save(); + OC_Log::write('core','OC_VCategories::delete, after: '.print_r($this->categories, true), OC_Log::DEBUG); + if(!is_null($objects)) { + foreach($objects as $key=>&$value) { + $vobject = OC_VObject::parse($value[1]); + if(!is_null($vobject)){ + $categories = $vobject->getAsArray('CATEGORIES'); + //OC_Log::write('core','OC_VCategories::delete, before: '.$key.': '.print_r($categories, true), OC_Log::DEBUG); + foreach($names as $name) { + $idx = $this->array_searchi($name, $categories); + OC_Log::write('core','OC_VCategories::delete, loop: '.$name.', '.print_r($idx, true), OC_Log::DEBUG); + if($idx !== false) { + OC_Log::write('core','OC_VCategories::delete, unsetting: '.$categories[$this->array_searchi($name, $categories)], OC_Log::DEBUG); + unset($categories[$this->array_searchi($name, $categories)]); + //unset($categories[$idx]); + } + } + OC_Log::write('core','OC_VCategories::delete, after: '.$key.': '.print_r($categories, true), OC_Log::DEBUG); + $vobject->setString('CATEGORIES', implode(',', $categories)); + $value[1] = $vobject->serialize(); + $objects[$key] = $value; + } else { + OC_Log::write('core','OC_VCategories::delete, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 50).'(...)', OC_Log::DEBUG); + } + } + } + } + + // case-insensitive in_array + private function in_arrayi($needle, $haystack) { + return in_array(strtolower($needle), array_map('strtolower', $haystack)); + } + + // case-insensitive array_search + private function array_searchi($needle, $haystack) { + return array_search(strtolower($needle),array_map('strtolower',$haystack)); + } + +} +?> diff --git a/owncloud.db.filesystem b/owncloud.db.filesystem deleted file mode 100644 index 082977a37e..0000000000 Binary files a/owncloud.db.filesystem and /dev/null differ diff --git a/settings/ajax/getlog.php b/settings/ajax/getlog.php new file mode 100644 index 0000000000..600ebefcec --- /dev/null +++ b/settings/ajax/getlog.php @@ -0,0 +1,17 @@ + + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +// Init owncloud +require_once('../../lib/base.php'); + +OC_JSON::checkAdminUser(); + +$count=(isset($_GET['count']))?$_GET['count']:50; +$offset=(isset($_GET['offset']))?$_GET['offset']:0; + +$entries=OC_Log::getEntries($count,$offset); +OC_JSON::success(array("data" => $entries)); diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php index dc87625a05..f59017600a 100644 --- a/settings/ajax/setquota.php +++ b/settings/ajax/setquota.php @@ -1,4 +1,9 @@ + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ // Init owncloud require_once('../../lib/base.php'); diff --git a/settings/css/settings.css b/settings/css/settings.css index 7a5873bb4d..42576953d0 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -5,6 +5,7 @@ input#openid, input#webdav { width:20em; } #passworderror { display:none; } #passwordchanged { display:none; } input#identity { width:20em; } +#email { width: 17em; } .msg.success{ color:#fff; background-color:#0f0; padding:3px; text-shadow:1px 1px #000; } .msg.error{ color:#fff; background-color:#f00; padding:3px; text-shadow:1px 1px #000; } @@ -40,3 +41,6 @@ select.quota.active { background: #fff; } li { color:#888; } li.active { color:#000; } span.version { margin-left:3em; color:#ddd; } + +/* LOF */ +#log { white-space:normal; } \ No newline at end of file diff --git a/settings/js/log.js b/settings/js/log.js new file mode 100644 index 0000000000..3814d9c10b --- /dev/null +++ b/settings/js/log.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2012, Robin Appelman + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +OC.Log={ + levels:['Debug','Info','Warning','Error','Fatal'], + loaded:50,//are initially loaded + getMore:function(){ + $.get(OC.filePath('settings','ajax','getlog.php'),{offset:OC.Log.loaded},function(result){ + if(result.status=='success'){ + OC.Log.addEntries(result.data); + } + }); + OC.Log.loaded+=50; + }, + addEntries:function(entries){ + for(var i=0;i'); + var levelTd=$('
    '); + levelTd.text(OC.Log.levels[entry.level]); + row.append(levelTd); + + var appTd=$(''); + appTd.text(entry.app); + row.append(appTd); + + var messageTd=$(''); + messageTd.text(entry.message); + row.append(messageTd); + + var timeTd=$(''); + timeTd.text(formatDate(entry.time)); + row.append(timeTd); + $('#log').append(row); + } + } +} + +$(document).ready(function(){ + $('#moreLog').click(function(){ + OC.Log.getMore(); + }) +}); diff --git a/settings/log.php b/settings/log.php index 21303c2170..946f2b6f8e 100644 --- a/settings/log.php +++ b/settings/log.php @@ -3,7 +3,7 @@ * ownCloud * * @author Robin Appelman - * @copyright 2011 Robin Appelman icewind1991@gmail.com + * @copyright 2012 Robin Appelman icewind1991@gmail.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -30,6 +30,9 @@ OC_App::setActiveNavigationEntry( "core_log" ); $entries=OC_Log::getEntries(); +OC_Util::addScript('settings','log'); +OC_Util::addStyle('settings','settings'); + function compareEntries($a,$b){ return $b->time - $a->time; } diff --git a/settings/templates/log.php b/settings/templates/log.php index bcf5258f5f..da5defc320 100644 --- a/settings/templates/log.php +++ b/settings/templates/log.php @@ -9,7 +9,7 @@ $levels=array('Debug','Info','Warning','Error','Fatal');
    - +
    -
    @@ -26,4 +26,5 @@ $levels=array('Debug','Info','Warning','Error','Fatal');
    \ No newline at end of file +
    +...'> diff --git a/tests/index.php b/tests/index.php index d29579a4bb..2e86366740 100644 --- a/tests/index.php +++ b/tests/index.php @@ -26,22 +26,29 @@ require_once 'simpletest/mock_objects.php'; require_once 'simpletest/collector.php'; require_once 'simpletest/default_reporter.php'; -//load all test cases -loadTests(); +//load core test cases +loadTests(dirname(__FILE__)); + +//load app test cases +$apps=OC_Appconfig::getApps(); +foreach($apps as $app){ + if(is_dir(OC::$SERVERROOT.'/apps/'.$app.'/tests')){ + loadTests(OC::$SERVERROOT.'/apps/'.$app.'/tests'); + } +} function loadTests($dir=''){ - $basedir=dirname(__FILE__).'/'; - if($dh=opendir($basedir.$dir)){ + if($dh=opendir($dir)){ while($name=readdir($dh)){ if(substr($name,0,1)!='.'){//no hidden files, '.' or '..' $file=$dir.'/'.$name; - if(is_dir($basedir.$file)){ + if(is_dir($file)){ loadTests($file); }elseif(substr($file,-4)=='.php' and $file!=__FILE__){ $testCase=new TestSuite(getTestName($file)); - $testCase->addFile($basedir.$file); + $testCase->addFile($file); if($testCase->getSize()>0){ - $testCase->run(new DefaultReporter()); + $testCase->run(new HtmlReporter()); } } } @@ -50,6 +57,7 @@ function loadTests($dir=''){ } function getTestName($file){ - //TODO: get better test names - return substr($file,5,-4);//strip /lib/ and .php +// //TODO: get better test names + $file=substr($file,strlen(OC::$SERVERROOT)); + return substr($file,0,-4);//strip .php } diff --git a/tests/lib/filestorage.php b/tests/lib/filestorage.php index 7f024dafaa..9ffa0eca9c 100644 --- a/tests/lib/filestorage.php +++ b/tests/lib/filestorage.php @@ -81,10 +81,6 @@ abstract class Test_FileStorage extends UnitTestCase { $this->instance->file_put_contents('/lorem.txt',$sourceText); $this->assertEqual($sourceText,$this->instance->file_get_contents('/lorem.txt'),'data returned from file_get_contents is not equal to the source data'); - //fill a file with a stream - $this->instance->file_put_contents('/lorem.txt',fopen($sourceFile,'r')); - $this->assertEqual($sourceText,$this->instance->file_get_contents('/lorem.txt'),'data returned from file_get_contents is not equal to the source data'); - //empty the file $this->instance->file_put_contents('/lorem.txt',''); $this->assertEqual('',$this->instance->file_get_contents('/lorem.txt'),'file not emptied'); @@ -98,17 +94,96 @@ abstract class Test_FileStorage extends UnitTestCase { $this->assertEqual(false,$this->instance->getMimeType('/non/existing/file')); $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; - $this->instance->file_put_contents('/lorem.txt',fopen($textFile,'r')); + $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile,'r')); $this->assertEqual('text/plain',$this->instance->getMimeType('/lorem.txt')); $pngFile=OC::$SERVERROOT.'/tests/data/logo-wide.png'; - $this->instance->file_put_contents('/logo-wide.png',fopen($pngFile,'r')); + $this->instance->file_put_contents('/logo-wide.png',file_get_contents($pngFile,'r')); $this->assertEqual('image/png',$this->instance->getMimeType('/logo-wide.png')); $svgFile=OC::$SERVERROOT.'/tests/data/logo-wide.svg'; - $this->instance->file_put_contents('/logo-wide.svg',fopen($svgFile,'r')); + $this->instance->file_put_contents('/logo-wide.svg',file_get_contents($svgFile,'r')); $this->assertEqual('image/svg+xml',$this->instance->getMimeType('/logo-wide.svg')); } + + public function testCopyAndMove(){ + $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $this->instance->file_put_contents('/source.txt',file_get_contents($textFile)); + $this->instance->copy('/source.txt','/target.txt'); + $this->assertTrue($this->instance->file_exists('/target.txt')); + $this->assertEqual($this->instance->file_get_contents('/source.txt'),$this->instance->file_get_contents('/target.txt')); + + $this->instance->rename('/source.txt','/target2.txt'); + $this->assertTrue($this->instance->file_exists('/target2.txt')); + $this->assertFalse($this->instance->file_exists('/source.txt')); + $this->assertEqual(file_get_contents($textFile),$this->instance->file_get_contents('/target.txt')); + } + + public function testLocalFile(){ + $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile)); + $localFile=$this->instance->getLocalFile('/lorem.txt'); + $this->assertTrue(file_exists($localFile)); + $this->assertEqual(file_get_contents($localFile),file_get_contents($textFile)); + } + + public function testStat(){ + $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $ctimeStart=time(); + $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile)); + $this->assertTrue($this->instance->is_readable('/lorem.txt')); + $ctimeEnd=time(); + $cTime=$this->instance->filectime('/lorem.txt'); + $mTime=$this->instance->filemtime('/lorem.txt'); + $this->assertTrue($ctimeStart<=$cTime); + $this->assertTrue($cTime<=$ctimeEnd); + $this->assertTrue($ctimeStart<=$mTime); + $this->assertTrue($mTime<=$ctimeEnd); + $this->assertEqual(filesize($textFile),$this->instance->filesize('/lorem.txt')); + + $stat=$this->instance->stat('/lorem.txt'); + //only size, mtime and ctime are requered in the result + $this->assertEqual($stat['size'],$this->instance->filesize('/lorem.txt')); + $this->assertEqual($stat['mtime'],$mTime); + $this->assertEqual($stat['ctime'],$cTime); + + $mtimeStart=time(); + $this->instance->touch('/lorem.txt'); + $mtimeEnd=time(); + $originalCTime=$cTime; + $cTime=$this->instance->filectime('/lorem.txt'); + $mTime=$this->instance->filemtime('/lorem.txt'); + $this->assertTrue($mtimeStart<=$mTime); + $this->assertTrue($mTime<=$mtimeEnd); + $this->assertEqual($cTime,$originalCTime); + + if($this->instance->touch('/lorem.txt',100)!==false){ + $mTime=$this->instance->filemtime('/lorem.txt'); + $this->assertEqual($mTime,100); + } + + $mtimeStart=time(); + $fh=$this->instance->fopen('/lorem.txt','a'); + fwrite($fh,' '); + fclose($fh); + clearstatcache(); + $mtimeEnd=time(); + $originalCTime=$cTime; + $mTime=$this->instance->filemtime('/lorem.txt'); + $this->assertTrue($mtimeStart<=$mTime); + $this->assertTrue($mTime<=$mtimeEnd); + } + + public function testSearch(){ + $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile,'r')); + $pngFile=OC::$SERVERROOT.'/tests/data/logo-wide.png'; + $this->instance->file_put_contents('/logo-wide.png',file_get_contents($pngFile,'r')); + $svgFile=OC::$SERVERROOT.'/tests/data/logo-wide.svg'; + $this->instance->file_put_contents('/logo-wide.svg',file_get_contents($svgFile,'r')); + $result=$this->instance->search('logo'); + $this->assertEqual(2,count($result)); + $this->assertNotIdentical(false,array_search('/logo-wide.svg',$result)); + $this->assertNotIdentical(false,array_search('/logo-wide.png',$result)); + } } - - diff --git a/tests/lib/filestorage/commontest.php b/tests/lib/filestorage/commontest.php new file mode 100644 index 0000000000..ab5375ec2b --- /dev/null +++ b/tests/lib/filestorage/commontest.php @@ -0,0 +1,41 @@ +. +* +*/ + +class Test_Filestorage_CommonTest extends Test_FileStorage { + /** + * @var string tmpDir + */ + private $tmpDir; + public function setUp(){ + $this->tmpDir=get_temp_dir().'/filestoragecommon'; + if(!file_exists($this->tmpDir)){ + mkdir($this->tmpDir); + } + $this->instance=new OC_Filestorage_CommonTest(array('datadir'=>$this->tmpDir)); + } + + public function tearDown(){ + OC_Helper::rmdirr($this->tmpDir); + } +} + +?> \ No newline at end of file diff --git a/tests/lib/streamwrappers.php b/tests/lib/streamwrappers.php new file mode 100644 index 0000000000..17a92c6658 --- /dev/null +++ b/tests/lib/streamwrappers.php @@ -0,0 +1,78 @@ +. +* +*/ + +class Test_StreamWrappers extends UnitTestCase { + public function testFakeDir(){ + $items=array('foo','bar'); + OC_FakeDirStream::$dirs['test']=$items; + $dh=opendir('fakedir://test'); + $result=array(); + while($file=readdir($dh)){ + $result[]=$file; + $this->assertNotIdentical(false,array_search($file,$items)); + } + $this->assertEqual(count($items),count($result)); + } + + public function testStaticStream(){ + $sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $staticFile='static://test'; + $this->assertFalse(file_exists($staticFile)); + file_put_contents($staticFile,file_get_contents($sourceFile)); + $this->assertTrue(file_exists($staticFile)); + $this->assertEqual(file_get_contents($sourceFile),file_get_contents($staticFile)); + unlink($staticFile); + clearstatcache(); + $this->assertFalse(file_exists($staticFile)); + } + + public function testCloseStream(){ + //ensure all basic stream stuff works + $sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $tmpFile=OC_Helper::TmpFile('.txt'); + $file='close://'.$tmpFile; + $this->assertTrue(file_exists($file)); + file_put_contents($file,file_get_contents($sourceFile)); + $this->assertEqual(file_get_contents($sourceFile),file_get_contents($file)); + unlink($file); + clearstatcache(); + $this->assertFalse(file_exists($file)); + + //test callback + $tmpFile=OC_Helper::TmpFile('.txt'); + $file='close://'.$tmpFile; + OC_CloseStreamWrapper::$callBacks[$tmpFile]=array('Test_StreamWrappers','closeCallBack'); + $fh=fopen($file,'w'); + fwrite($fh,'asd'); + try{ + fclose($fh); + $this->fail('Expected exception'); + }catch(Exception $e){ + $path=$e->getMessage(); + $this->assertEqual($path,$tmpFile); + } + } + + public static function closeCallBack($path){ + throw new Exception($path); + } +} \ No newline at end of file