summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--apps/bookmarks/css/bookmarks.css47
-rw-r--r--apps/bookmarks/js/bookmarks.js16
-rw-r--r--apps/calendar/ajax/createcalendar.php2
-rwxr-xr-x[-rw-r--r--]apps/calendar/ajax/events.php8
-rw-r--r--apps/calendar/ajax/updatecalendar.php2
-rw-r--r--apps/calendar/js/calendar.js13
-rw-r--r--apps/calendar/templates/part.eventform.php6
-rw-r--r--apps/calendar/templates/settings.php1
-rw-r--r--apps/contacts/ajax/addcard.php2
-rw-r--r--apps/contacts/ajax/addcontact.php62
-rw-r--r--apps/contacts/ajax/addproperty.php24
-rw-r--r--apps/contacts/ajax/contactdetails.php76
-rw-r--r--apps/contacts/ajax/createaddressbook.php2
-rw-r--r--apps/contacts/ajax/cropphoto.php38
-rw-r--r--apps/contacts/ajax/editaddress.php31
-rw-r--r--apps/contacts/ajax/editname.php41
-rw-r--r--apps/contacts/ajax/importdialog.php17
-rw-r--r--apps/contacts/ajax/loadphoto.php53
-rw-r--r--apps/contacts/ajax/newcontact.php62
-rw-r--r--apps/contacts/ajax/savecrop.php136
-rw-r--r--apps/contacts/ajax/saveproperty.php134
-rw-r--r--apps/contacts/ajax/setproperty.php6
-rw-r--r--apps/contacts/ajax/uploadphoto.php133
-rw-r--r--apps/contacts/appinfo/app.php1
-rw-r--r--apps/contacts/contacts.php60
-rw-r--r--apps/contacts/css/Jcrop.gifbin0 -> 329 bytes
-rw-r--r--apps/contacts/css/contacts.css213
-rw-r--r--apps/contacts/css/jquery.Jcrop.css84
-rw-r--r--apps/contacts/dynphoto.php35
-rw-r--r--apps/contacts/img/globe.svg102
-rw-r--r--apps/contacts/img/person_large.pngbin0 -> 11517 bytes
-rw-r--r--apps/contacts/import.php120
-rw-r--r--apps/contacts/import_tmp/Info2
-rw-r--r--apps/contacts/js/contacts.js1244
-rw-r--r--apps/contacts/js/jquery.Jcrop.js1765
-rw-r--r--apps/contacts/js/jquery.Jcrop.min.js255
-rw-r--r--apps/contacts/js/jquery.jec-1.3.3.js863
-rw-r--r--apps/contacts/js/loader.js81
-rw-r--r--apps/contacts/lib/app.php20
-rw-r--r--apps/contacts/lib/vcard.php48
-rw-r--r--apps/contacts/templates/index2.php27
-rw-r--r--apps/contacts/templates/part.chooseaddressbook.rowfields.php2
-rw-r--r--apps/contacts/templates/part.contact.php184
-rw-r--r--apps/contacts/templates/part.contactphoto.php9
-rw-r--r--apps/contacts/templates/part.cropphoto.php62
-rw-r--r--apps/contacts/templates/part.edit_address_dialog.php67
-rw-r--r--apps/contacts/templates/part.edit_name_dialog.php59
-rw-r--r--apps/contacts/templates/part.editaddressbook.php2
-rw-r--r--apps/contacts/templates/part.import.php27
-rw-r--r--apps/contacts/templates/part.no_contacts.php8
-rw-r--r--apps/contacts/templates/part.property.FN.php4
-rw-r--r--apps/contacts/templates/part.property.php20
-rw-r--r--apps/contacts/templates/part.setpropertyform.php36
-rwxr-xr-xapps/files_pdfviewer/js/viewer.js16
-rw-r--r--apps/files_sharing/appinfo/app.php1
-rw-r--r--apps/files_sharing/js/share.js1
-rw-r--r--apps/files_sharing/lib_share.php4
-rw-r--r--apps/files_sharing/sharedstorage.php91
-rw-r--r--apps/files_texteditor/ajax/loadfile.php2
-rw-r--r--apps/files_texteditor/ajax/savefile.php3
-rw-r--r--apps/gallery/ajax/galleryOp.php41
-rw-r--r--apps/gallery/ajax/thumbnail.php59
-rw-r--r--apps/gallery/css/styles.css3
-rw-r--r--apps/gallery/js/album_cover.js52
-rw-r--r--apps/gallery/js/albums.js20
-rw-r--r--apps/gallery/lib/album.php11
-rw-r--r--apps/gallery/lib/hooks_handlers.php10
-rw-r--r--apps/gallery/lib/photo.php33
-rw-r--r--apps/gallery/lib/scanner.php23
-rw-r--r--apps/gallery/templates/index.php28
-rw-r--r--apps/media/ajax/api.php28
-rw-r--r--apps/media/js/scanner.js88
-rw-r--r--apps/media/lib_scanner.php36
-rw-r--r--core/img/actions/add.pngbin0 -> 488 bytes
-rw-r--r--core/img/actions/add.svg109
-rw-r--r--core/img/actions/mail.pngbin0 -> 548 bytes
-rw-r--r--core/img/actions/mail.svg345
-rw-r--r--db_structure.xml123
-rw-r--r--files/ajax/scan.php37
-rw-r--r--files/ajax/upload.php2
-rw-r--r--files/css/files.css7
-rw-r--r--files/index.php2
-rw-r--r--files/js/files.js29
-rw-r--r--files/templates/index.php15
-rw-r--r--files/templates/part.list.php6
-rw-r--r--lib/app.php18
-rw-r--r--lib/base.php3
-rw-r--r--lib/connector/sabre/directory.php6
-rw-r--r--lib/connector/sabre/node.php30
-rw-r--r--lib/db.php9
-rw-r--r--lib/eventsource.php1
-rw-r--r--lib/filecache.php585
-rw-r--r--lib/fileproxy.php2
-rw-r--r--lib/fileproxy/quota.php4
-rw-r--r--lib/files.php53
-rw-r--r--lib/filestorage.php4
-rw-r--r--lib/filestorage/local.php106
-rw-r--r--lib/filestorage/remote.php350
-rw-r--r--lib/filestoragecommon.php83
-rw-r--r--lib/filesystem.php419
-rw-r--r--lib/filesystemview.php321
-rw-r--r--lib/image.php221
-rw-r--r--lib/search/provider/file.php11
-rw-r--r--lib/util.php10
-rw-r--r--owncloud.db.filesystembin0 -> 2348032 bytes
-rw-r--r--settings/ajax/setquota.php5
-rwxr-xr-xsettings/personal.php3
-rw-r--r--settings/users.php2
109 files changed, 8463 insertions, 1322 deletions
diff --git a/README b/README
index cd562b66c6c..4d4be2728e1 100644
--- a/README
+++ b/README
@@ -3,7 +3,7 @@ A personal cloud which runs on your own server.
http://ownCloud.org
-Installation instructions: http://owncloud.org/support
+Installation instructions: http://owncloud.org/support/setup-and-installation/
Source code: http://gitorious.org/owncloud
Mailing list: http://mail.kde.org/mailman/listinfo/owncloud
diff --git a/apps/bookmarks/css/bookmarks.css b/apps/bookmarks/css/bookmarks.css
index 233d4a02944..48f0bede110 100644
--- a/apps/bookmarks/css/bookmarks.css
+++ b/apps/bookmarks/css/bookmarks.css
@@ -18,7 +18,7 @@
}
.bookmarks_list {
- margin-top: 45px;
+ margin-top: 36px;
}
.bookmarks_addBml {
@@ -36,42 +36,49 @@
}
.bookmark_actions {
+ position: absolute;
+ right: 1em;
+ top: 0.7em;
display: none;
- font-size: smaller;
- color: #666;
- padding-left: 4em;
-}
-
-.bookmark_actions span:hover {
- cursor: pointer;
- text-decoration: underline;
}
+.bookmark_actions span { margin: 0 0.4em; }
+.bookmark_actions img { opacity: 0.3; }
+.bookmark_actions img:hover { opacity: 1; cursor: pointer; }
.bookmark_single {
+ position: relative;
padding: 0.5em 1em;
+ border-bottom: 1px solid #DDD;
+ -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms;
}
.bookmark_single:hover {
- background-color: #EAEAEA;
+ background-color:#f8f8f8
}
.bookmark_single:hover .bookmark_actions {
display: block;
}
-.bookmark_title {
- font-size: larger;
- color: blue;
- text-decoration: underline;
-}
-
-.bookmark_url {
- color: green;
+.bookmark_title { font-weight: bold; display: inline-block; margin-right: 0.8em; }
+.bookmark_url { display: none; color: #999; }
+.bookmark_single:hover .bookmark_url { display: inline; }
+.bookmark_tags {
+ position: absolute;
+ top: 0.5em;
+ right: 6em;
+ text-align: right;
}
-
.bookmark_tag {
- color: #ff3333;
+ display: inline-block;
+ color: white;
+ margin: 0 0.2em;
+ padding: 0 0.4em;
+ background-color: #1D2D44;
+ border-radius: 0.4em;
+ opacity: 0.2;
}
+.bookmark_tag:hover { opacity: 0.5; }
.loading_meta {
display: none;
diff --git a/apps/bookmarks/js/bookmarks.js b/apps/bookmarks/js/bookmarks.js
index 51646e5382b..77f767cdb81 100644
--- a/apps/bookmarks/js/bookmarks.js
+++ b/apps/bookmarks/js/bookmarks.js
@@ -85,10 +85,10 @@ function addOrEditBookmark(event) {
$('.bookmarks_add').children('p').children('.bookmarks_input').val('');
$('.bookmarks_list').prepend(
'<div class="bookmark_single" data-id="' + bookmark_id + '" >' +
+ '<p class="bookmark_actions"><span class="bookmark_delete"><img src="img/delete.png" title="Delete"></span>&nbsp;<span class="bookmark_edit"><img src="img/edit.png" title="Edit"></span></p>' +
'<p class="bookmark_title"><a href="' + url + '" target="_blank" class="bookmark_link">' + title + '</a></p>' +
- '<p class="bookmark_url">' + url + '</p>' +
'<p class="bookmark_tags">' + tagshtml + '</p>' +
- '<p class="bookmark_actions"><span class="bookmark_delete">Delete</span>&nbsp;<span class="bookmark_edit">Edit</span></p>' +
+ '<p class="bookmark_url">' + url + '</p>' +
'</div>'
);
}
@@ -137,7 +137,7 @@ function showBookmark(event) {
$('.bookmarks_add').slideToggle();
}
$('html, body').animate({
- scrollTop: $('.bookmarks_menu').offset().top
+ scrollTop: ($('.bookmarks_menu'))?$('.bookmarks_menu').offset().top:0
}, 500);
}
@@ -146,19 +146,22 @@ function updateBookmarksList(bookmark) {
var tags = encodeEntities(bookmark.tags).split(' ');
var taglist = '';
for ( var i=0, len=tags.length; i<len; ++i ){
- taglist = taglist + '<a class="bookmark_tag" href="?tag=' + encodeURI(tags[i]) + '">' + tags[i] + '</a> ';
+ if(tags[i] != '')
+ taglist = taglist + '<a class="bookmark_tag" href="?tag=' + encodeURI(tags[i]) + '">' + tags[i] + '</a> ';
}
if(!hasProtocol(bookmark.url)) {
bookmark.url = 'http://' + bookmark.url;
}
$('.bookmarks_list').append(
'<div class="bookmark_single" data-id="' + bookmark.id +'" >' +
+ '<p class="bookmark_actions"><span class="bookmark_delete"><img src="img/delete.png" title="Delete"></span>&nbsp;<span class="bookmark_edit"><img src="img/edit.png" title="Edit"></span></p>' +
'<p class="bookmark_title"><a href="' + encodeEntities(bookmark.url) + '" target="_blank" class="bookmark_link">' + encodeEntities(bookmark.title) + '</a></p>' +
'<p class="bookmark_url">' + encodeEntities(bookmark.url) + '</p>' +
- '<p class="bookmark_tags">' + taglist + '</p>' +
- '<p class="bookmark_actions"><span class="bookmark_delete">Delete</span>&nbsp;<span class="bookmark_edit">Edit</span></p>' +
'</div>'
);
+ if(taglist != '') {
+ $('div[data-id="'+ bookmark.id +'"]').append('<p class="bookmark_tags">' + taglist + '</p>');
+ }
}
function updateOnBottom() {
@@ -178,7 +181,6 @@ function recordClick(event) {
function encodeEntities(s){
try {
return $('<div/>').text(s).html();
-
} catch (ex) {
return "";
}
diff --git a/apps/calendar/ajax/createcalendar.php b/apps/calendar/ajax/createcalendar.php
index b719b207c74..8d7b12f9b89 100644
--- a/apps/calendar/ajax/createcalendar.php
+++ b/apps/calendar/ajax/createcalendar.php
@@ -25,7 +25,7 @@ foreach($calendars as $cal){
}
$userid = OC_User::getUser();
-$calendarid = OC_Calendar_Calendar::addCalendar($userid, $_POST['name'], 'VEVENT,VTODO,VJOURNAL', null, 0, $_POST['color']);
+$calendarid = OC_Calendar_Calendar::addCalendar($userid, strip_tags($_POST['name']), 'VEVENT,VTODO,VJOURNAL', null, 0, $_POST['color']);
OC_Calendar_Calendar::setCalendarActive($calendarid, 1);
$calendar = OC_Calendar_Calendar::find($calendarid);
diff --git a/apps/calendar/ajax/events.php b/apps/calendar/ajax/events.php
index 998991c2fb4..dd593ddec99 100644..100755
--- a/apps/calendar/ajax/events.php
+++ b/apps/calendar/ajax/events.php
@@ -11,8 +11,8 @@ require_once('../../../3rdparty/when/When.php');
function addoutput($event, $vevent, $return_event){
$return_event['id'] = (int)$event['id'];
- $return_event['title'] = $event['summary'];
- $return_event['description'] = isset($vevent->DESCRIPTION)?$vevent->DESCRIPTION->value:'';
+ $return_event['title'] = htmlspecialchars($event['summary']);
+ $return_event['description'] = isset($vevent->DESCRIPTION)?htmlspecialchars($vevent->DESCRIPTION->value):'';
$last_modified = $vevent->__get('LAST-MODIFIED');
if ($last_modified){
$lastmodified = $last_modified->getDateTime()->format('U');
@@ -39,13 +39,13 @@ foreach($events as $event){
$dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent);
$return_event = array();
$start_dt = $dtstart->getDateTime();
- $start_dt->setTimezone(new DateTimeZone($user_timezone));
$end_dt = $dtend->getDateTime();
- $end_dt->setTimezone(new DateTimeZone($user_timezone));
if ($dtstart->getDateType() == Sabre_VObject_Element_DateTime::DATE){
$return_event['allDay'] = true;
}else{
$return_event['allDay'] = false;
+ $start_dt->setTimezone(new DateTimeZone($user_timezone));
+ $end_dt->setTimezone(new DateTimeZone($user_timezone));
}
//Repeating Events
if($event['repeating'] == 1){
diff --git a/apps/calendar/ajax/updatecalendar.php b/apps/calendar/ajax/updatecalendar.php
index 269b7b7ca06..5add6d92bfa 100644
--- a/apps/calendar/ajax/updatecalendar.php
+++ b/apps/calendar/ajax/updatecalendar.php
@@ -26,7 +26,7 @@ foreach($calendars as $cal){
$calendarid = $_POST['id'];
$calendar = OC_Calendar_App::getCalendar($calendarid);//access check
-OC_Calendar_Calendar::editCalendar($calendarid, $_POST['name'], null, null, null, $_POST['color']);
+OC_Calendar_Calendar::editCalendar($calendarid, strip_tags($_POST['name']), null, null, null, $_POST['color']);
OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
$calendar = OC_Calendar_App::getCalendar($calendarid);
diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js
index afd1b692dd4..92a8ba20205 100644
--- a/apps/calendar/js/calendar.js
+++ b/apps/calendar/js/calendar.js
@@ -668,8 +668,18 @@ $(document).ready(function(){
agenda: agendatime,
'': defaulttime
},
+ columnFormat: {
+ month: t('calendar', 'ddd'), // Mon
+ week: t('calendar', 'ddd M/d'), // Mon 9/7
+ day: t('calendar', 'dddd M/d') // Monday 9/7
+ },
titleFormat: {
- list: 'yyyy/MMM/d dddd'
+ month: t('calendar', 'MMMM yyyy'),
+ // September 2009
+ week: t('calendar', "MMM d[ yyyy]{ '&#8212;'[ MMM] d yyyy}"),
+ // Sep 7 - 13 2009
+ day: t('calendar', 'dddd, MMM d, yyyy'),
+ // Tuesday, Sep 8, 2009
},
axisFormat: defaulttime,
monthNames: monthNames,
@@ -698,6 +708,7 @@ $(document).ready(function(){
eventDrop: Calendar.UI.moveEvent,
eventResize: Calendar.UI.resizeEvent,
eventRender: function(event, element) {
+ element.find('span.fc-event-title').html(element.find('span.fc-event-title').text());
element.tipsy({
className: 'tipsy-event',
opacity: 0.9,
diff --git a/apps/calendar/templates/part.eventform.php b/apps/calendar/templates/part.eventform.php
index 1f2073f4bc7..49214aca77c 100644
--- a/apps/calendar/templates/part.eventform.php
+++ b/apps/calendar/templates/part.eventform.php
@@ -2,7 +2,7 @@
<tr>
<th width="75px"><?php echo $l->t("Title");?>:</th>
<td>
- <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Title of the Event");?>" value="<?php echo isset($_['title']) ? $_['title'] : '' ?>" maxlength="100" name="title"/>
+ <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Title of the Event");?>" value="<?php echo isset($_['title']) ? htmlspecialchars($_['title']) : '' ?>" maxlength="100" name="title"/>
</td>
</tr>
</table>
@@ -207,7 +207,7 @@
<tr>
<th width="85px"><?php echo $l->t("Location");?>:</th>
<td>
- <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Location of the Event");?>" value="<?php echo isset($_['location']) ? $_['location'] : '' ?>" maxlength="100" name="location" />
+ <input type="text" style="width:350px;" size="100" placeholder="<?php echo $l->t("Location of the Event");?>" value="<?php echo isset($_['location']) ? htmlspecialchars($_['location']) : '' ?>" maxlength="100" name="location" />
</td>
</tr>
</table>
@@ -215,7 +215,7 @@
<tr>
<th width="85px" style="vertical-align: top;"><?php echo $l->t("Description");?>:</th>
<td>
- <textarea style="width:350px;height: 150px;" placeholder="<?php echo $l->t("Description of the Event");?>" name="description"><?php echo isset($_['description']) ? $_['description'] : '' ?></textarea>
+ <textarea style="width:350px;height: 150px;" placeholder="<?php echo $l->t("Description of the Event");?>" name="description"><?php echo isset($_['description']) ? htmlspecialchars($_['description']) : '' ?></textarea>
</td>
</tr>
</table>
diff --git a/apps/calendar/templates/settings.php b/apps/calendar/templates/settings.php
index fc8e0e061d9..e174378d02d 100644
--- a/apps/calendar/templates/settings.php
+++ b/apps/calendar/templates/settings.php
@@ -9,6 +9,7 @@
?>
<form id="calendar">
<fieldset class="personalblock">
+ <strong><?php echo $l->t('Calendar'); ?></strong>
<table class="nostyle">
<tr><td><label for="timezone" class="bold"><?php echo $l->t('Timezone');?></label></td><td><select style="display: none;" id="timezone" name="timezone">
<?php
diff --git a/apps/contacts/ajax/addcard.php b/apps/contacts/ajax/addcard.php
index 140d6a48095..f15a1685840 100644
--- a/apps/contacts/ajax/addcard.php
+++ b/apps/contacts/ajax/addcard.php
@@ -77,7 +77,7 @@ foreach( $add as $propname){
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, $value); //, $prop_parameters);
+ $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') {
diff --git a/apps/contacts/ajax/addcontact.php b/apps/contacts/ajax/addcontact.php
new file mode 100644
index 00000000000..c39d75eff88
--- /dev/null
+++ b/apps/contacts/ajax/addcontact.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/addcontact.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+function debug($msg) {
+ OC_Log::write('contacts','ajax/addcontact.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');
+$l=new OC_L10N('contacts');
+
+$aid = $_POST['aid'];
+$addressbook = OC_Contacts_App::getAddressbook( $aid );
+
+$fn = trim($_POST['fn']);
+$n = trim($_POST['n']);
+debug('N: '.$n);
+debug('FN: '.$fn);
+
+$vcard = new OC_VObject('VCARD');
+$vcard->setUID();
+$vcard->setString('FN',$fn);
+$vcard->setString('N',$n);
+
+$id = OC_Contacts_VCard::add($aid,$vcard->serialize());
+if(!$id) {
+ OC_JSON::error(array('data' => array('message' => $l->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);
+ exit();
+}
+
+OC_JSON::success(array('data' => array( 'id' => $id )));
diff --git a/apps/contacts/ajax/addproperty.php b/apps/contacts/ajax/addproperty.php
index c90af217c87..23f0a9379b2 100644
--- a/apps/contacts/ajax/addproperty.php
+++ b/apps/contacts/ajax/addproperty.php
@@ -67,8 +67,32 @@ foreach($current as $item) {
if(is_array($value)) {
ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form!
+} else {
+ $value = strip_tags($value);
}
+switch($name) {
+ case 'BDAY':
+ $date = New DateTime($value);
+ $value = $date->format(DateTime::ATOM);
+ case 'FN':
+ if(!$value) {
+ // create a method thats returns an alternative for FN.
+ //$value = getOtherValue();
+ }
+ case 'N':
+ case 'ORG':
+ case 'NICKNAME':
+ break;
+ case 'EMAIL':
+ $value = strtolower($value);
+ break;
+ case 'TEL':
+ case 'ADR': // should I delete the property if empty or throw an error?
+ break;
+}
+
+
$property = $vcard->addProperty($name, $value); //, $parameters);
$line = count($vcard->children) - 1;
diff --git a/apps/contacts/ajax/contactdetails.php b/apps/contacts/ajax/contactdetails.php
new file mode 100644
index 00000000000..f7ac25ea515
--- /dev/null
+++ b/apps/contacts/ajax/contactdetails.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/contactdetails.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+function debug($msg) {
+ OC_Log::write('contacts','ajax/contactdetails.php: '.$msg, OC_Log::DEBUG);
+}
+
+// Check if we are a user
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+$l=new OC_L10N('contacts');
+
+$id = isset($_GET['id'])?$_GET['id']:null;
+if(is_null($id)) {
+ bailOut($l->t('Missing ID'));
+}
+$vcard = OC_Contacts_App::getContactVCard( $id );
+if(is_null($vcard)) {
+ bailOut($l->t('Error parsing VCard for ID: "'.$id.'"'));
+}
+$details = OC_Contacts_VCard::structureContact($vcard);
+
+// Some Google exported files have no FN field.
+if(!isset($details['FN'])) {
+ $fn = '';
+ if(isset($details['N'])) {
+ $details['FN'] = array(implode(' ', $details['N'][0]['value']));
+ } elseif(isset($details['EMAIL'])) {
+ $details['FN'] = array('value' => $details['EMAIL'][0]['value']);
+ } else {
+ $details['FN'] = array('value' => $l->t('Unknown'));
+ }
+}
+
+// Make up for not supporting the 'N' field in earlier version.
+if(!isset($details['N'])) {
+ $details['N'] = array();
+ $details['N'][0] = array($details['FN'][0]['value'],'','','','');
+}
+
+// Don't wanna transfer the photo in a json string.
+if(isset($details['PHOTO'])) {
+ $details['PHOTO'] = true;
+ //unset($details['PHOTO']);
+} else {
+ $details['PHOTO'] = false;
+}
+$details['id'] = $id;
+
+OC_JSON::success(array('data' => $details)); \ No newline at end of file
diff --git a/apps/contacts/ajax/createaddressbook.php b/apps/contacts/ajax/createaddressbook.php
index edcf794f497..3d766b6a60a 100644
--- a/apps/contacts/ajax/createaddressbook.php
+++ b/apps/contacts/ajax/createaddressbook.php
@@ -15,7 +15,7 @@ OC_JSON::checkLoggedIn();
OC_JSON::checkAppEnabled('contacts');
$userid = OC_User::getUser();
-$bookid = OC_Contacts_Addressbook::add($userid, $_POST['name'], null);
+$bookid = OC_Contacts_Addressbook::add($userid, strip_tags($_POST['name']), null);
if(!$bookid) {
OC_JSON::error(array('data' => array('message' => $l->t('Error adding addressbook.'))));
OC_Log::write('contacts','ajax/createaddressbook.php: Error adding addressbook: '.$_POST['name'], OC_Log::ERROR);
diff --git a/apps/contacts/ajax/cropphoto.php b/apps/contacts/ajax/cropphoto.php
new file mode 100644
index 00000000000..878fb5610c6
--- /dev/null
+++ b/apps/contacts/ajax/cropphoto.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+// Check if we are a user
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+
+$tmp_path = $_GET['tmp_path'];
+$id = $_GET['id'];
+OC_Log::write('contacts','ajax/cropphoto.php: tmp_path: '.$tmp_path.', exists: '.file_exists($tmp_path), OC_Log::DEBUG);
+$tmpl = new OC_TEMPLATE("contacts", "part.cropphoto");
+$tmpl->assign('tmp_path', $tmp_path);
+$tmpl->assign('id', $id);
+$page = $tmpl->fetchPage();
+
+OC_JSON::success(array('data' => array( 'page' => $page )));
diff --git a/apps/contacts/ajax/editaddress.php b/apps/contacts/ajax/editaddress.php
new file mode 100644
index 00000000000..4e6456f6045
--- /dev/null
+++ b/apps/contacts/ajax/editaddress.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net>
+ * 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 = $_GET['id'];
+$checksum = isset($_GET['checksum'])?$_GET['checksum']:'';
+$vcard = OC_Contacts_App::getContactVCard($id);
+$adr_types = OC_Contacts_App::getTypesOfProperty('ADR');
+
+$tmpl = new OC_TEMPLATE("contacts", "part.edit_address_dialog");
+if($checksum) {
+ $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
+ $element = $vcard->children[$line];
+ $adr = OC_Contacts_VCard::structureProperty($element);
+ $tmpl->assign('adr',$adr);
+}
+
+$tmpl->assign('id',$id);
+$tmpl->assign('adr_types',$adr_types);
+
+$tmpl->printpage();
+
+?>
diff --git a/apps/contacts/ajax/editname.php b/apps/contacts/ajax/editname.php
new file mode 100644
index 00000000000..31bdd125675
--- /dev/null
+++ b/apps/contacts/ajax/editname.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Copyright (c) 2011 Thomas Tanghus <thomas@tanghus.net>
+ * 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');
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/editname.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+function debug($msg) {
+ OC_Log::write('contacts','ajax/editname.php: '.$msg, OC_Log::DEBUG);
+}
+
+$tmpl = new OC_TEMPLATE("contacts", "part.edit_name_dialog");
+
+$id = isset($_GET['id'])?$_GET['id']:'';
+if($id) {
+ $vcard = OC_Contacts_App::getContactVCard($id);
+ $name = array('', '', '', '', '');
+ if($vcard->__isset('N')) {
+ $property = $vcard->__get('N');
+ if($property) {
+ $name = OC_Contacts_VCard::structureProperty($property);
+ }
+ }
+ $tmpl->assign('name',$name);
+ $tmpl->assign('id',$id);
+} else {
+ $addressbooks = OC_Contacts_Addressbook::active(OC_User::getUser());
+ $tmpl->assign('addressbooks', $addressbooks);
+}
+$tmpl->printpage();
+
+?>
diff --git a/apps/contacts/ajax/importdialog.php b/apps/contacts/ajax/importdialog.php
new file mode 100644
index 00000000000..17b4cebdbd1
--- /dev/null
+++ b/apps/contacts/ajax/importdialog.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+require_once('../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_Util::checkAppEnabled('contacts');
+$l10n = new OC_L10N('contacts');
+$tmpl = new OC_Template('contacts', 'part.import');
+$tmpl->assign('path', $_POST['path']);
+$tmpl->assign('filename', $_POST['filename']);
+$tmpl->printpage();
+?>
diff --git a/apps/contacts/ajax/loadphoto.php b/apps/contacts/ajax/loadphoto.php
new file mode 100644
index 00000000000..d9f7e737b55
--- /dev/null
+++ b/apps/contacts/ajax/loadphoto.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * TODO: Translatable strings.
+ * Remember to delete tmp file at some point.
+ */
+// Init owncloud
+require_once('../../../lib/base.php');
+// Check if we are a user
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+$l=new OC_L10N('contacts');
+
+// foreach ($_POST as $key=>$element) {
+// OC_Log::write('contacts','ajax/savecrop.php: '.$key.'=>'.$element, OC_Log::DEBUG);
+// }
+
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/savecrop.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+
+$image = null;
+
+$id = isset($_GET['id']) ? $_GET['id'] : '';
+
+if($id == '') {
+ bailOut('Missing contact id.');
+}
+
+$tmpl = new OC_TEMPLATE("contacts", "part.contactphoto");
+$tmpl->assign('id', $id);
+$page = $tmpl->fetchPage();
+OC_JSON::success(array('data' => array('page'=>$page)));
+?>
diff --git a/apps/contacts/ajax/newcontact.php b/apps/contacts/ajax/newcontact.php
new file mode 100644
index 00000000000..fcfd12ca80d
--- /dev/null
+++ b/apps/contacts/ajax/newcontact.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/newcontact.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+function debug($msg) {
+ OC_Log::write('contacts','ajax/newcontact.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');
+
+$addressbooks = OC_Contacts_Addressbook::all(OC_USER::getUser());
+
+$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.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('addressbooks',$addressbooks);
+$tmpl->assign('id','');
+$page = $tmpl->fetchPage();
+
+OC_JSON::success(array('data' => array( 'page' => $page )));
diff --git a/apps/contacts/ajax/savecrop.php b/apps/contacts/ajax/savecrop.php
new file mode 100644
index 00000000000..7b7384723cd
--- /dev/null
+++ b/apps/contacts/ajax/savecrop.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * TODO: Translatable strings.
+ * Remember to delete tmp file at some point.
+ */
+// Init owncloud
+require_once('../../../lib/base.php');
+OC_Log::write('contacts','ajax/savecrop.php: Huzzah!!!', OC_Log::DEBUG);
+
+// Check if we are a user
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+$l=new OC_L10N('contacts');
+
+// foreach ($_POST as $key=>$element) {
+// OC_Log::write('contacts','ajax/savecrop.php: '.$key.'=>'.$element, OC_Log::DEBUG);
+// }
+
+// Firefox and Konqueror tries to download application/json for me. --Arthur
+OC_JSON::setContentTypeHeader('text/plain');
+
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/savecrop.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+
+$image = null;
+
+$x1 = (isset($_POST['x1']) && $_POST['x1']) ? $_POST['x1'] : -1;
+//$x2 = isset($_POST['x2']) ? $_POST['x2'] : -1;
+$y1 = (isset($_POST['y1']) && $_POST['y1']) ? $_POST['y1'] : -1;
+//$y2 = isset($_POST['y2']) ? $_POST['y2'] : -1;
+$w = (isset($_POST['w']) && $_POST['w']) ? $_POST['w'] : -1;
+$h = (isset($_POST['h']) && $_POST['h']) ? $_POST['h'] : -1;
+$tmp_path = isset($_POST['tmp_path']) ? $_POST['tmp_path'] : '';
+$id = isset($_POST['id']) ? $_POST['id'] : '';
+
+if(in_array(-1, array($x1, $y1, $w, $h))) {
+ bailOut('Wrong crop dimensions: '.implode(', ', array($x1, $y1, $w, $h)));
+}
+
+if($tmp_path == '') {
+ bailOut('Missing path to temporary file.');
+}
+
+if($id == '') {
+ bailOut('Missing contact id.');
+}
+
+OC_Log::write('contacts','savecrop.php: files: '.$tmp_path.' exists: '.file_exists($tmp_path), OC_Log::DEBUG);
+
+if(file_exists($tmp_path)) {
+ $image = new OC_Image();
+ if($image->loadFromFile($tmp_path)) {
+ if($image->crop($x1, $y1, $w, $h)) {
+ if($image->resize(200)) {
+ $tmpfname = tempnam("/tmp", "occCropped"); // create a new file because of caching issues.
+ if($image->save($tmpfname)) {
+ unlink($tmp_path);
+ $card = OC_Contacts_App::getContactVCard($id);
+ if(!$card) {
+ unlink($tmpfname);
+ bailOut('Error getting contact object.');
+ }
+ if($card->__isset('PHOTO')) {
+ OC_Log::write('contacts','savecrop.php: files: PHOTO property exists.', OC_Log::DEBUG);
+ $property = $card->__get('PHOTO');
+ if(!$property) {
+ unlink($tmpfname);
+ bailOut('Error getting PHOTO property.');
+ }
+ $property->setValue($image->__toString());
+ $property->parameters[] = new Sabre_VObject_Parameter('ENCODING', 'b');
+ $property->parameters[] = new Sabre_VObject_Parameter('TYPE', $image->mimeType());
+ $card->__set('PHOTO', $property);
+ } else {
+ 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())) {
+ bailOut('Error saving contact.');
+ }
+ unlink($tmpfname);
+ //$result=array( "status" => "success", 'mime'=>$image->mimeType(), 'tmp'=>$tmp_path);
+ $tmpl = new OC_TEMPLATE("contacts", "part.contactphoto");
+ $tmpl->assign('tmp_path', $tmpfname);
+ $tmpl->assign('mime', $image->mimeType());
+ $tmpl->assign('id', $id);
+ $tmpl->assign('width', $image->width());
+ $tmpl->assign('height', $image->height());
+ $page = $tmpl->fetchPage();
+ OC_JSON::success(array('data' => array('page'=>$page, 'tmp'=>$tmpfname)));
+ exit();
+ } else {
+ if(file_exists($tmpfname)) {
+ unlink($tmpfname);
+ }
+ bailOut('Error saving temporary image');
+ }
+ } else {
+ bailOut('Error resizing image');
+ }
+ } else {
+ bailOut('Error cropping image');
+ }
+ } else {
+ bailOut('Error creating temporary image');
+ }
+} else {
+ bailOut('Error finding image: '.$tmp_path);
+}
+
+if($tmp_path != '' && file_exists($tmp_path)) {
+ unlink($tmp_path);
+}
+
+?>
diff --git a/apps/contacts/ajax/saveproperty.php b/apps/contacts/ajax/saveproperty.php
new file mode 100644
index 00000000000..8f575c6b81e
--- /dev/null
+++ b/apps/contacts/ajax/saveproperty.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+// Check if we are a user
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+$l=new OC_L10N('contacts');
+
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/saveproperty.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+function debug($msg) {
+ OC_Log::write('contacts','ajax/saveproperty.php: '.$msg, OC_Log::DEBUG);
+}
+foreach ($_POST as $key=>$element) {
+ debug('_POST: '.$key.'=>'.$element);
+}
+
+$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']:null;
+$checksum = isset($_POST['checksum'])?$_POST['checksum']:null;
+// if(!is_null($parameters)) {
+// debug('parameters: '.count($parameters));
+// foreach($parameters as $key=>$val ) {
+// debug('parameter: '.$key.'=>'.implode('/',$val));
+// }
+// }
+
+if(is_array($value)){ // FIXME: How to strip_tags for compound values?
+ 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(!$id) {
+ bailOut($l->t('id is not set.'));
+}
+if(!$checksum) {
+ bailOut($l->t('checksum is not set.'));
+}
+if(!$name) {
+ bailOut($l->t('element name is not set.'));
+}
+
+$vcard = OC_Contacts_App::getContactVCard( $id );
+$line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
+if(is_null($line)) {
+ bailOut($l->t('Information about vCard is incorrect. Please reload the page.'.$checksum.' "'.$line.'"'));
+}
+$element = $vcard->children[$line]->name;
+
+if($element != $name) {
+ bailOut($l->t('Something went FUBAR. ').$name.' != '.$element);
+}
+
+switch($element) {
+ case 'BDAY':
+ $date = New DateTime($value);
+ //$vcard->setDateTime('BDAY', $date, Sabre_VObject_Element_DateTime::DATE);
+ $value = $date->format(DateTime::ATOM);
+ case 'FN':
+ if(!$value) {
+ // create a method thats returns an alternative for FN.
+ //$value = getOtherValue();
+ }
+ case 'N':
+ case 'ORG':
+ case 'NICKNAME':
+ debug('Setting string:'.$name.' '.$value);
+ $vcard->setString($name, $value);
+ break;
+ case 'EMAIL':
+ $value = strtolower($value);
+ case 'TEL':
+ case 'ADR': // should I delete the property if empty or throw an error?
+ debug('Setting element: (EMAIL/TEL/ADR)'.$element);
+ if(!$value) {
+ unset($vcard->children[$line]); // Should never happen...
+ } else {
+ $vcard->children[$line]->setValue($value);
+ $vcard->children[$line]->parameters = array();
+ if(!is_null($parameters)) {
+ debug('Setting parameters: '.$parameters);
+ foreach($parameters as $key => $parameter) {
+ debug('Adding parameter: '.$key);
+ foreach($parameter as $val) {
+ debug('Adding parameter: '.$key.'=>'.$val);
+ $vcard->children[$line]->add(new Sabre_VObject_Parameter($key, strtoupper($val)));
+ }
+ }
+ }
+ }
+ break;
+}
+// Do checksum and be happy
+$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' => $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');
+
+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
index cf3fe582247..f9e2a8e8647 100644
--- a/apps/contacts/ajax/setproperty.php
+++ b/apps/contacts/ajax/setproperty.php
@@ -37,9 +37,13 @@ $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
$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($value);
+$vcard->children[$line]->setValue(strip_tags($value));
// Add parameters
$postparameters = isset($_POST['parameters'])?$_POST['parameters']:array();
diff --git a/apps/contacts/ajax/uploadphoto.php b/apps/contacts/ajax/uploadphoto.php
new file mode 100644
index 00000000000..62cd32f5dbb
--- /dev/null
+++ b/apps/contacts/ajax/uploadphoto.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+// Init owncloud
+require_once('../../../lib/base.php');
+
+// Check if we are a user
+// Firefox and Konqueror tries to download application/json for me. --Arthur
+OC_JSON::setContentTypeHeader('text/plain');
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+function bailOut($msg) {
+ OC_JSON::error(array('data' => array('message' => $msg)));
+ OC_Log::write('contacts','ajax/uploadphoto.php: '.$msg, OC_Log::DEBUG);
+ exit();
+}
+function debug($msg) {
+ OC_Log::write('contacts','ajax/uploadphoto.php: '.$msg, OC_Log::DEBUG);
+}
+
+// foreach ($_SERVER as $key=>$element) {
+// debug('$_SERVER: '.$key.'=>'.$element);
+// }
+// foreach ($_GET as $key=>$element) {
+// debug('_GET: '.$key.'=>'.$element);
+// }
+// foreach ($_POST as $key=>$element) {
+// debug('_POST: '.$key.'=>'.$element);
+// }
+// foreach ($_FILES as $key=>$element) {
+// debug('_FILES: '.$key.'=>'.$element);
+// }
+
+// If it is a Drag'n'Drop transfer it's handled here.
+$fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
+if ($fn) {
+ // AJAX call
+ if (!isset($_GET['id'])) {
+ OC_Log::write('contacts','ajax/uploadphoto.php: No contact ID was submitted.', OC_Log::DEBUG);
+ OC_JSON::error(array('data' => array( 'message' => 'No contact ID was submitted.' )));
+ exit();
+ }
+ $id = $_GET['id'];
+ $tmpfname = tempnam('/tmp', 'occOrig');
+ file_put_contents($tmpfname, file_get_contents('php://input'));
+ debug($tmpfname.' uploaded');
+ $image = new OC_Image();
+ if($image->loadFromFile($tmpfname)) {
+ if($image->width() > 400 || $image->height() > 400) {
+ $image->resize(400); // Prettier resizing than with browser and saves bandwidth.
+ }
+ if(!$image->fixOrientation()) { // No fatal error so we don't bail out.
+ debug('Couldn\'t save correct image orientation: '.$tmpfname);
+ }
+ if($image->save($tmpfname)) {
+ OC_JSON::success(array('data' => array('mime'=>$_SERVER['CONTENT_TYPE'], 'name'=>$fn, 'id'=>$id, 'tmp'=>$tmpfname)));
+ exit();
+ } else {
+ bailOut('Couldn\'t save temporary image: '.$tmpfname);
+ }
+ } else {
+ bailOut('Couldn\'t load temporary image: '.$file['tmp_name']);
+ }
+}
+
+
+if (!isset($_POST['id'])) {
+ OC_Log::write('contacts','ajax/uploadphoto.php: No contact ID was submitted.', OC_Log::DEBUG);
+ OC_JSON::error(array('data' => array( 'message' => 'No contact ID was submitted.' )));
+ exit();
+}
+if (!isset($_FILES['imagefile'])) {
+ OC_Log::write('contacts','ajax/uploadphoto.php: No file was uploaded. Unknown error.', OC_Log::DEBUG);
+ OC_JSON::error(array('data' => array( 'message' => 'No file was uploaded. Unknown error' )));
+ exit();
+}
+$error = $_FILES['imagefile']['error'];
+if($error !== UPLOAD_ERR_OK) {
+ $l=new OC_L10N('contacts');
+ $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")
+ );
+ bailOut($errors[$error]);
+}
+$file=$_FILES['imagefile'];
+
+$tmpfname = tempnam("/tmp", "occOrig");
+if(file_exists($file['tmp_name'])) {
+ $image = new OC_Image();
+ if($image->loadFromFile($file['tmp_name'])) {
+ if($image->width() > 400 || $image->height() > 400) {
+ $image->resize(400); // Prettier resizing than with browser and saves bandwidth.
+ }
+ if(!$image->fixOrientation()) { // No fatal error so we don't bail out.
+ debug('Couldn\'t save correct image orientation: '.$tmpfname);
+ }
+ if($image->save($tmpfname)) {
+ OC_JSON::success(array('data' => array('mime'=>$file['type'],'size'=>$file['size'],'name'=>$file['name'], 'id'=>$_POST['id'], 'tmp'=>$tmpfname)));
+ exit();
+ } else {
+ bailOut('Couldn\'t save temporary image: '.$tmpfname);
+ }
+ } else {
+ bailOut('Couldn\'t load temporary image: '.$file['tmp_name']);
+ }
+} else {
+ bailOut('Temporary file: \''.$file['tmp_name'].'\' has gone AWOL?');
+}
+
+?>
diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php
index a5b31006247..23a5a4253d0 100644
--- a/apps/contacts/appinfo/app.php
+++ b/apps/contacts/appinfo/app.php
@@ -22,4 +22,5 @@ OC_App::addNavigationEntry( array(
OC_APP::registerPersonal('contacts','settings');
+OC_UTIL::addScript('contacts', 'loader');
require_once('apps/contacts/lib/search.php'); \ No newline at end of file
diff --git a/apps/contacts/contacts.php b/apps/contacts/contacts.php
new file mode 100644
index 00000000000..fded839fed8
--- /dev/null
+++ b/apps/contacts/contacts.php
@@ -0,0 +1,60 @@
+<?php
+require_once('../../lib/base.php');
+
+// Check if we are a user
+OC_Util::checkLoggedIn();
+// Get active address books. This creates a default one if none exists.
+$ids = OC_Contacts_Addressbook::activeIds(OC_User::getUser());
+$contacts = OC_Contacts_VCard::all($ids);
+
+$addressbooks = OC_Contacts_Addressbook::active(OC_User::getUser());
+
+// Load the files we need
+OC_App::setActiveNavigationEntry( 'contacts_index' );
+
+// Load a specific user?
+$id = isset( $_GET['id'] ) ? $_GET['id'] : null;
+$details = array();
+
+// FIXME: This cannot work..?
+if(is_null($id) && count($contacts) > 0) {
+ $id = $contacts[0]['id'];
+}
+if(!is_null($id)) {
+ $vcard = OC_Contacts_App::getContactVCard($id);
+ $details = OC_Contacts_VCard::structureContact($vcard);
+}
+$property_types = OC_Contacts_App::getAddPropertyOptions();
+$phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
+
+$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);
+
+OC_Util::addScript('','jquery.multiselect');
+//OC_Util::addScript('contacts','interface');
+OC_Util::addScript('contacts','contacts');
+OC_Util::addScript('contacts','jquery.inview');
+OC_Util::addScript('contacts','jquery.Jcrop');
+OC_Util::addScript('contacts','jquery.jec-1.3.3');
+OC_Util::addStyle('','jquery.multiselect');
+//OC_Util::addStyle('contacts','styles');
+OC_Util::addStyle('contacts','jquery.Jcrop');
+OC_Util::addStyle('contacts','contacts');
+
+$tmpl = new OC_Template( "contacts", "index2", "user" );
+$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
+$tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
+$tmpl->assign('property_types',$property_types);
+$tmpl->assign('phone_types',$phone_types);
+$tmpl->assign('addressbooks', $addressbooks);
+$tmpl->assign('contacts', $contacts);
+$tmpl->assign('details', $details );
+$tmpl->assign('id',$id);
+$tmpl->printPage();
+
+?>
diff --git a/apps/contacts/css/Jcrop.gif b/apps/contacts/css/Jcrop.gif
new file mode 100644
index 00000000000..72ea7ccb532
--- /dev/null
+++ b/apps/contacts/css/Jcrop.gif
Binary files differ
diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css
new file mode 100644
index 00000000000..86322a2cc2a
--- /dev/null
+++ b/apps/contacts/css/contacts.css
@@ -0,0 +1,213 @@
+/*dl > dt {
+ font-weight: bold;
+}*/
+
+#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_deletecard {position:absolute;top:15px;right:25px;}
+#contacts_downloadcard {position:absolute;top:15px;right:50px;}
+#contacts_propertymenu_button { position:absolute;top:15px;right:150px; height: 20px; width: 150px; background:url('../../../core/img/actions/add.svg') no-repeat center; }
+#contacts_propertymenu { position:absolute;top:40px;right:150px; overflow:hidden; text-overflow:ellipsis; /*border: thin solid #1d2d44;*/ -moz-box-shadow:0 0 10px #000; -webkit-box-shadow:0 0 10px #000; box-shadow:0 0 10px #000; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; }
+#contacts_propertymenu li { display: block; font-weight: bold; height: 20px; width: 100px; }
+/*#contacts_propertymenu li:first-child { border-top: thin solid #1d2d44; -moz-border-radius-topleft:0.5em; -webkit-border-top-left-radius:0.5em; border-top-left-radius:0.5em; -moz-border-radius-topright:0.5em; -webkit-border-top-right-radius:0.5em; border-top-right-radius:0.5em; }
+#contacts_propertymenu li:last-child { border-bottom: thin solid #1d2d44; -moz-border-radius-bottomleft:0.5em; -webkit-border-bottom-left-radius:0.5em; border-bottom-left-radius:0.5em; -moz-border-radius-bottomright:0.5em; -webkit-border-bottom-right-radius:0.5em; border-bottom-right-radius:0.5em; }*/
+#contacts_propertymenu li a { padding: 3px; display: block }
+#contacts_propertymenu li:hover { background-color: #1d2d44; }
+#contacts_propertymenu li a:hover { color: #fff }
+#actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0;}
+#card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ }
+#firstrun { /*border: thin solid lightgray;*/ width: 80%; margin: 5em auto auto auto; text-align: center; font-weight:bold; font-size:1.5em; color:#777;}
+#firstrun #selections { /*border: thin solid lightgray;*/ font-size:0.8em; width: 100%; margin: 2em auto auto auto; clear: both; }
+
+#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; }
+/*input[type="text"]:valid,input[type="email"]:valid,input[type="tel"]:valid,input[type="date"]:valid { background-color: #b1d28f !important; }*/
+dl.form
+{
+ width: 100%;
+ float: left;
+ clear: right;
+ margin: 0;
+ padding: 0;
+}
+
+.form dt
+{
+ display: table-cell;
+ clear: left;
+ float: left;
+ width: 7em;
+ /*overflow: hidden;*/
+ margin: 0;
+ padding: 0.8em 0.5em 0 0;
+ font-weight: bold;
+ text-align:right;
+ text-overflow:ellipsis;
+ o-text-overflow: ellipsis;
+ vertical-align: text-bottom;
+ /*
+ white-space: pre-wrap;
+ white-space: -moz-pre-wrap !important;
+ white-space: -pre-wrap;
+ white-space: -o-pre-wrap;*/
+}
+
+.form dd
+{
+ display: table-cell;
+ clear: right;
+ float: left;
+ margin: 0;
+ padding: 0px;
+ white-space: nowrap;
+ vertical-align: text-bottom;
+ /*min-width: 20em;*/
+ /*background-color: yellow;*/
+}
+
+.loading { background: url('../../../core/img/loading.gif') no-repeat center !important;}
+
+/*.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; }*/
+
+.listactions { height: 1em; width:60px; float: left; clear: right; }
+.add,.edit,.delete,.mail, .globe { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; display: none; }
+.add { background:url('../../../core/img/actions/add.svg') no-repeat center; clear: both; }
+.delete { background:url('../../../core/img/actions/delete.svg') no-repeat center; }
+.edit { background:url('../../../core/img/actions/rename.svg') no-repeat center; }
+.mail { background:url('../../../core/img/actions/mail.svg') no-repeat center; }
+.globe { background:url('../img/globe.svg') no-repeat center; }
+
+#messagebox_msg { font-weight: bold; font-size: 1.2em; }
+
+/* Name editor */
+#edit_name_dialog {
+ /*width: 25em;*/
+ padding:0;
+}
+#edit_name_dialog > input {
+ width: 15em;
+}
+/* Address editor */
+#edit_address_dialog {
+ /*width: 30em;*/
+}
+#edit_address_dialog > input {
+ width: 15em;
+}
+#edit_photo_dialog_img {
+ display: block;
+ width: 150;
+ height: 200;
+ border: thin solid black;
+}
+#fn {
+ float: left;
+}
+.jecEditableOption {
+ margin: 2px;
+ border-radius: 0.5em;
+ border: thin solid #bbb;
+ content: 'Custom';
+}
+/**
+ * Create classes form, floateven and floatodd which flows left and right respectively.
+ */
+.contactsection {
+ float: left;
+ min-width: 30em;
+ max-width: 40em;
+ margin: 0.5em;
+ border: thin solid lightgray;
+ -webkit-border-radius: 0.5em;
+ -moz-border-radius: 0.5em;
+ border-radius: 0.5em;
+ background-color: #f8f8f8;
+}
+
+.contactpart legend {
+ /*background: #fff;
+ font-weight: bold;
+ left: 1em;
+ border: thin solid gray;
+ -webkit-border-radius: 0.5em;
+ -moz-border-radius: 0.5em;
+ border-radius: 0.5em;
+ padding: 3px;*/
+width:auto; padding:.3em; border:1px solid #ddd; font-weight:bold; cursor:pointer; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em;
+}
+/*#contacts_details_photo {
+ cursor: pointer;
+ z-index:1;
+ margin: auto;
+}
+*/
+#cropbox {
+ margin: auto;
+}
+
+/* Photo editor */
+/*#contacts_details_photo_wrapper {
+ z-index: 1000;
+}*/
+#contacts_details_photo {
+ border-radius: 0.5em;
+ border: thin solid #bbb;
+ padding: 0.5em;
+ margin: 1em 1em 1em 7em;
+ cursor: pointer;
+ /*background: #f8f8f8;*/
+ background: url(../../../core/img/loading.gif) no-repeat center center;
+ clear: right;
+}
+#contacts_details_photo:hover {
+ background: #fff;
+}
+#contacts_details_photo_progress {
+ margin: 0.3em 0.3em 0.3em 7em;
+ clear: left;
+}
+/* Address editor */
+#addressdisplay { padding: 0.5em; }
+dl.addresscard { background-color: #fff; float: left; width: 45%; margin: 0 0.3em 0.3em 0.3em; padding: 0; border: thin solid lightgray; }
+dl.addresscard dd {}
+dl.addresscard dt { padding: 0.3em; border-bottom: thin solid lightgray; font-weight: bold; clear: both;}
+dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; }
+#adr_type {} /* Select */
+#adr_pobox {}
+#adr_extended {}
+#adr_street {}
+#adr_city {}
+#adr_region {}
+#adr_zipcode {}
+#adr_country {}
+
+.delimiter {
+ height: 10px;
+ clear: both;
+}
+.updatebar {
+ height: 30px;
+ clear: both;
+ padding-right: 170px;
+ border: thin solid lightgray;
+}
+.updatebar button {
+ float: left; margin: 1em;
+}
+
+/*input[type="text"] { float: left; max-width: 15em; }
+input[type="radio"] { float: left; -khtml-appearance: none; width: 20px; height: 20px; vertical-align: middle; }*/
+#file_upload_target, #crop_target { display:none; }
+
+#file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; position:absolute; left:0; top:0; cursor:pointer; width:0; height:0;}
+input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
+.propertycontainer dd { float: left; width: 25em; }
+.propertylist { clear: none; max-width: 28em; }
+.propertylist li { /*background-color: cyan; */ min-width: 25em; /*max-width: 30em;*/ display: block; clear: right; }
+.propertylist li > input[type="text"],input[type="email"],input[type="tel"] { float: left; max-width: 15em; }
+.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
diff --git a/apps/contacts/css/jquery.Jcrop.css b/apps/contacts/css/jquery.Jcrop.css
new file mode 100644
index 00000000000..554f013fd77
--- /dev/null
+++ b/apps/contacts/css/jquery.Jcrop.css
@@ -0,0 +1,84 @@
+/* jquery.Jcrop.css
+ The code contained in this file is free software under MIT License
+ Copyright (c)2008-2011 Tapmodo Interactive LLC
+*/
+
+/*
+ The outer-most container in a typical Jcrop instance
+ If you are having difficulty with formatting related to styles
+ on a parent element, place any fixes here or in a like selector
+*/
+.jcrop-holder {
+ direction: ltr;
+ text-align: left;
+}
+
+.jcrop-vline, .jcrop-hline {
+ background: white url('Jcrop.gif') top left repeat;
+ font-size: 0px;
+ position: absolute;
+}
+
+.jcrop-vline {
+ height: 100%;
+ width: 1px !important;
+}
+
+.jcrop-hline {
+ width: 100%;
+ height: 1px !important;
+}
+
+.jcrop-vline.right {
+ right: 0px;
+}
+
+.jcrop-hline.bottom {
+ bottom: 0px;
+}
+
+.jcrop-handle {
+ background-color: #333;
+ border: 1px #eee solid;
+ font-size: 1px;
+}
+
+.jcrop-tracker {
+ height: 100%;
+ -webkit-tap-highlight-color: transparent; /* "turn off" link highlight */
+ -webkit-touch-callout: none; /* disable callout, image save panel */
+ -webkit-user-select: none; /* disable cut copy paste */
+ width: 100%;
+}
+
+/*
+*/
+
+.jcrop-light .jcrop-vline, .jcrop-light .jcrop-hline {
+ background: white;
+ filter: Alpha(opacity=70) !important;
+ opacity: .70 !important;
+}
+
+.jcrop-light .jcrop-handle {
+ background-color: black;
+ border-color: white;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+}
+
+.jcrop-dark .jcrop-vline, .jcrop-dark .jcrop-hline {
+ background: black;
+ filter: Alpha(opacity=70) !important;
+ opacity: 0.70 !important;
+}
+
+.jcrop-dark .jcrop-handle {
+ background-color: white;
+ border-color: black;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+}
+
diff --git a/apps/contacts/dynphoto.php b/apps/contacts/dynphoto.php
new file mode 100644
index 00000000000..2beac15e143
--- /dev/null
+++ b/apps/contacts/dynphoto.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * ownCloud - Image generator for contacts.
+ *
+ * @author Thomas Tanghus
+ * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../lib/base.php');
+$tmp_path = $_GET['tmp_path'];
+$maxsize = isset($_GET['maxsize']) ? $_GET['maxsize'] : -1;
+header("Cache-Control: no-cache, no-store, must-revalidate");
+
+OC_Log::write('contacts','dynphoto.php: tmp_path: '.$tmp_path.', exists: '.file_exists($tmp_path), OC_Log::DEBUG);
+
+$image = new OC_Image($tmp_path);
+if($maxsize != -1) {
+ $image->resize($maxsize);
+}
+$image();
diff --git a/apps/contacts/img/globe.svg b/apps/contacts/img/globe.svg
new file mode 100644
index 00000000000..4e43cfba10a
--- /dev/null
+++ b/apps/contacts/img/globe.svg
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="16"
+ height="16"
+ id="svg2485">
+ <defs
+ id="defs2487">
+ <linearGradient
+ id="linearGradient3707-319-631">
+ <stop
+ id="stop4637"
+ style="stop-color:#254b6d;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4639"
+ style="stop-color:#415b73;stop-opacity:1"
+ offset="0.5" />
+ <stop
+ id="stop4641"
+ style="stop-color:#6195b5;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="20"
+ y1="43"
+ x2="20"
+ y2="2.6887112"
+ id="linearGradient2483"
+ xlink:href="#linearGradient3707-319-631"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3769285,0,0,0.3769199,-1.196178,-0.8960149)" />
+ <linearGradient
+ id="linearGradient2867-449-88-871-390-598-476-591-434-148">
+ <stop
+ id="stop4627"
+ style="stop-color:#51cfee;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4629"
+ style="stop-color:#49a3d2;stop-opacity:1"
+ offset="0.26238" />
+ <stop
+ id="stop4631"
+ style="stop-color:#3470b4;stop-opacity:1"
+ offset="0.704952" />
+ <stop
+ id="stop4633"
+ style="stop-color:#273567;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <radialGradient
+ cx="61.240059"
+ cy="-8.7256308"
+ r="9.7552834"
+ fx="61.240059"
+ fy="-8.7256308"
+ id="radialGradient2481"
+ xlink:href="#linearGradient2867-449-88-871-390-598-476-591-434-148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,1.3551574,-1.3551567,0,-3.824604,-80.384092)" />
+ <linearGradient
+ id="linearGradient4873">
+ <stop
+ id="stop4875"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4877"
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="63.397362"
+ y1="-12.489107"
+ x2="63.397362"
+ y2="5.4675598"
+ id="linearGradient2464"
+ xlink:href="#linearGradient4873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.7432394,0,0,0.7432224,-38.229789,10.609333)" />
+ </defs>
+ <g
+ id="layer1">
+ <path
+ d="M 15.5,7.999735 C 15.5,12.142003 12.141888,15.5 8.000094,15.5 C 3.857922,15.5 0.5,12.141965 0.5,7.999735 C 0.5,3.8576552 3.857922,0.4999997 8.000094,0.4999997 C 12.141888,0.4999997 15.5,3.8576552 15.5,7.999735 L 15.5,7.999735 z"
+ id="path6495"
+ style="fill:url(#radialGradient2481);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2483);stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none" />
+ <path
+ d="M 8.1022062,0.95621712 L 7.236144,1.0568701 L 6.2834735,1.3210847 C 6.3646244,1.2921995 6.446834,1.2606694 6.5309208,1.2330132 L 6.4195696,1.0317069 L 6.048401,1.0946151 L 5.8628152,1.2707582 L 5.5782512,1.3085031 L 5.318433,1.4343196 L 5.1947098,1.4972277 L 5.157592,1.5475543 L 4.9720082,1.5852992 L 4.8606579,1.8243504 L 4.712189,1.522391 L 4.6626994,1.6482076 L 4.6874462,1.9879118 L 4.4523728,2.1892181 L 4.3162766,2.5540857 L 4.6008407,2.5540857 L 4.712191,2.3150345 L 4.7493088,2.2269631 C 4.8744282,2.136984 4.9930427,2.0367771 5.1204782,1.9501669 L 5.4174142,2.05082 C 5.6107409,2.1843878 5.8054259,2.3200676 5.9989133,2.4534326 L 6.2834735,2.1892181 L 5.9617936,2.05082 L 5.8133266,1.7488605 L 5.2689442,1.6859524 L 5.2565714,1.6230443 L 5.4916438,1.6733709 L 5.6277409,1.5349726 L 5.9246759,1.4720643 C 5.9949454,1.4373108 6.06407,1.4122795 6.1350055,1.3839929 L 5.9494207,1.560136 L 6.6175262,2.0256568 L 6.6175262,2.302453 L 6.357707,2.5666673 L 6.7041325,3.2712399 L 6.9392071,3.1328408 L 7.2361431,2.6673207 C 7.6536127,2.5360677 8.0298636,2.383337 8.423888,2.2017997 L 8.3991411,2.3779426 L 8.5970969,2.5163408 L 8.9435226,2.2772895 L 8.7703107,2.0759833 L 8.5352353,2.2143814 L 8.4610018,2.1892185 C 8.4780536,2.1812953 8.4933662,2.1721092 8.5104914,2.1640557 L 8.8569181,1.2581766 L 8.1022062,0.95621712 z M 4.712189,2.3150345 L 4.9967521,2.5163408 L 5.2318265,2.5163408 L 5.2318265,2.2772895 L 4.9472634,2.1514733 L 4.712189,2.3150345 L 4.712189,2.3150345 z M 11.281895,2.1514733 L 10.774628,2.2772895 L 10.452951,2.4911776 L 10.452951,2.6799019 L 9.9456829,3.0070243 L 10.044661,3.4977095 L 10.341597,3.2838208 L 10.527183,3.4977095 L 10.737514,3.6235259 L 10.873608,3.2586575 L 10.799375,3.0447699 L 10.873608,2.8937896 L 11.170544,2.6169941 L 11.30664,2.6169941 L 11.170544,2.9189543 L 11.170544,3.1957496 C 11.292932,3.1618717 11.416467,3.1486778 11.541713,3.1328408 L 11.195288,3.3844746 L 11.170543,3.5354537 L 10.774628,3.8751588 L 10.366341,3.7745049 L 10.366341,3.5354537 L 10.180756,3.6612703 L 10.267364,3.8751588 L 9.9704267,3.8751588 L 9.8095868,4.1519548 L 9.61163,4.3784244 L 9.2528324,4.4539139 L 9.463162,4.6678019 L 9.5126507,4.88169 L 9.2528324,4.88169 L 8.9064068,5.0704143 L 8.9064068,5.6240059 L 9.0672458,5.6240059 L 9.2157146,5.7875676 L 9.5497684,5.6240059 L 9.6734907,5.2843013 L 9.920937,5.1333218 L 9.9704267,5.0075057 L 10.366341,4.9068526 L 10.589042,5.1584854 L 10.824117,5.2843013 L 10.688021,5.5610979 L 10.898351,5.49819 L 11.009701,5.2213934 L 10.737512,4.9068526 L 10.848862,4.9068526 L 11.121054,5.1333218 L 11.170543,5.4352821 L 11.405615,5.7120781 L 11.467479,5.3094657 L 11.591202,5.2465566 C 11.722897,5.3855865 11.826706,5.5556097 11.937626,5.7120781 L 12.345913,5.7372433 L 12.580987,5.8882234 L 12.469637,6.0517841 L 12.234564,6.2656727 L 11.888135,6.2656727 L 11.430361,6.1146942 L 11.195287,6.1398593 L 11.022074,6.3411657 L 10.527181,5.8378996 L 10.180756,5.7372462 L 9.6734907,5.8001547 L 9.2280866,5.9259711 C 8.974001,6.2188978 8.7138634,6.5150518 8.4733747,6.8192683 L 8.1888107,7.5238405 L 8.3249076,7.6748196 L 8.0774604,8.0396877 L 8.3496515,8.6813514 C 8.5761437,8.9418962 8.8039678,9.2006825 9.030129,9.461412 L 9.3641827,9.1720348 L 9.5002778,9.3481795 L 9.8590764,9.1091277 L 9.9827973,9.2475248 L 10.341595,9.2475248 L 10.551927,9.4865757 L 10.428203,9.914352 L 10.675649,10.203731 L 10.663275,10.706995 L 10.848861,11.071864 L 10.712766,11.386405 C 10.699498,11.611997 10.68802,11.827605 10.68802,12.053231 C 10.79718,12.358902 10.906621,12.663823 11.0097,12.971693 L 11.083934,13.462375 L 11.083934,13.714009 L 11.281891,13.714009 L 11.566453,13.525283 L 11.91288,13.525283 L 12.432517,12.933947 L 12.370656,12.732639 L 12.717083,12.418101 L 12.457262,12.128722 L 12.76657,11.877089 L 13.063506,11.688364 L 13.199602,11.537384 L 13.112995,11.19768 C 13.112995,10.911684 13.112996,10.628161 13.112995,10.342127 L 13.348069,9.8136993 L 13.645006,9.4865757 L 13.966685,8.6813514 L 13.966685,8.467463 C 13.806607,8.4879689 13.653171,8.506459 13.496537,8.5177905 L 13.818218,8.1906678 L 14.263621,7.8887078 L 14.498696,7.6119125 L 14.498696,7.3099534 C 14.445379,7.2077028 14.391536,7.0976601 14.337854,6.9954116 L 14.127525,7.2470431 L 13.966685,7.0583197 L 13.731609,6.8695964 L 13.731609,6.4795648 L 14.003802,6.7941047 L 14.313111,6.75636 C 14.452648,6.8851798 14.586636,6.9997236 14.709024,7.1463926 L 14.906986,6.9199159 C 14.906986,6.6761022 14.63695,5.4724677 14.053296,4.4539139 C 13.46964,3.4356954 12.444894,2.5037591 12.444895,2.5037591 L 12.370661,2.6421572 L 12.098469,2.9441167 L 11.752043,2.579249 L 12.098469,2.579249 L 12.259308,2.4031061 L 11.615949,2.2772895 L 11.281895,2.1514733 z M 3.8461259,2.3150345 L 3.7224027,2.6421572 C 3.7224027,2.6421572 3.5057941,2.6785236 3.4502116,2.6924839 C 2.7403769,3.3576613 1.3089548,4.8004591 0.97574709,7.5112538 C 0.98894063,7.5741032 1.2108206,7.9390292 1.2108206,7.9390292 L 1.7552039,8.2661509 L 2.2995852,8.417131 L 2.5346596,8.7065091 L 2.8934573,8.9833054 L 3.1037868,8.9455608 L 3.2522558,9.0210508 L 3.2522558,9.0713763 L 3.0542972,9.6375498 L 2.8934573,9.8766016 L 2.942946,9.9898366 L 2.8192238,10.442776 L 3.2769996,11.298327 L 3.7471476,11.713522 L 3.957476,12.015482 L 3.9327303,12.644563 L 4.0811982,12.99685 L 3.9327303,13.67626 C 3.9327303,13.67626 3.9129189,13.670673 3.9327303,13.739167 C 3.9527179,13.807695 4.759781,14.268689 4.8111654,14.22985 C 4.8623746,14.190288 4.9101447,14.154361 4.9101447,14.154361 L 4.860656,14.00338 L 5.0709847,13.802075 L 5.1452191,13.588188 L 5.4792709,13.474953 L 5.7390901,12.820706 L 5.6648567,12.644563 L 5.8380694,12.367767 L 6.2339838,12.279695 L 6.4319415,11.801593 L 6.3824509,11.210255 L 6.6917597,10.769898 L 6.7412484,10.316959 C 6.3168632,10.102948 5.9004921,9.8830534 5.4792709,9.6627135 L 5.2689423,9.2475198 L 4.8853999,9.1594491 L 4.6750703,8.5932746 L 4.1678047,8.6561827 L 3.7224017,8.32906 L 3.2522529,8.7442535 L 3.2522529,8.8071626 C 3.1114329,8.7658292 2.9446126,8.7597462 2.8192238,8.6813464 L 2.7202454,8.3793874 L 2.7202454,8.0522637 L 2.3985655,8.0900083 C 2.4244522,7.8816386 2.4591052,7.6692598 2.485171,7.4609265 L 2.2995852,7.4609265 L 2.1140005,7.6999783 L 1.9407887,7.7880501 L 1.6809694,7.6370692 L 1.6562245,7.3099456 L 1.7057143,6.9576606 L 2.0892557,6.6557005 L 2.3985645,6.6557005 L 2.4604271,6.4795582 L 2.8439686,6.5676288 L 3.1285316,6.932497 L 3.1780203,6.3285781 L 3.672914,5.9133853 L 3.8461259,5.4730269 L 4.2172963,5.3220476 L 4.415253,5.0200879 L 4.8854018,4.9320161 L 5.1204763,4.57973 C 4.8878,4.57973 4.6479303,4.57973 4.415253,4.57973 L 4.8606579,4.3658421 L 5.1699649,4.3658421 L 5.5658793,4.2274443 L 5.6401139,4.6174752 L 5.8133266,4.3406788 L 5.615369,4.2022808 L 5.6648596,4.0387201 L 5.5040187,3.8877395 L 5.3308048,3.8374132 L 5.3802955,3.6486888 L 5.2441994,3.3844746 L 4.9348905,3.5102908 L 4.9843801,3.2712399 L 4.6255827,3.0573516 L 4.3410204,3.5606172 L 4.3657663,3.7367603 L 4.0812032,3.8625768 L 3.9079904,4.2526078 L 3.821384,3.8877395 L 3.3388631,3.6864335 L 3.2522568,3.4096375 L 3.9079904,3.0321885 L 4.1925535,2.755392 L 4.2173002,2.4282694 L 4.0564593,2.3401977 L 3.8461298,2.315035 L 3.8461259,2.3150345 z M 9.1538541,2.9189543 L 9.1538541,3.1202592 L 9.2652062,3.246076 L 9.2652062,3.5480356 L 9.2033438,3.9506479 L 9.5250246,3.8877395 L 9.760098,3.6486888 L 9.5497694,3.4473819 C 9.4815603,3.2626763 9.4124355,3.0961113 9.3270669,2.9189543 L 9.1538541,2.9189543 z M 8.881662,3.3215656 L 8.6837063,3.3844746 L 8.7331959,3.7493421 L 8.9930152,3.6109428 L 8.881662,3.3215656 L 8.881662,3.3215656 z M 12.506754,6.6305386 L 12.803691,6.9828228 L 13.162489,7.7628866 L 13.385189,8.0145183 L 13.273839,8.2787347 L 13.471797,8.5177846 C 13.380952,8.5238987 13.292977,8.5303684 13.199605,8.5303684 C 13.029984,8.1679096 12.895741,7.802374 12.766575,7.4231829 L 12.543871,7.1715503 L 12.420149,6.7186111 L 12.506754,6.6305399 L 12.506754,6.6305386 z"
+ id="path6534"
+ style="opacity:0.4;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <path
+ d="M 14.500001,7.9997709 C 14.500001,11.589737 11.589636,14.5 8.0000818,14.5 C 4.4101984,14.5 1.5000001,11.589705 1.5000001,7.9997709 C 1.5000001,4.4099703 4.4101984,1.5000014 8.0000818,1.5000014 C 11.589636,1.5000014 14.500001,4.4099703 14.500001,7.9997709 L 14.500001,7.9997709 z"
+ id="path2451"
+ style="opacity:0.4;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2464);stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/apps/contacts/img/person_large.png b/apps/contacts/img/person_large.png
new file mode 100644
index 00000000000..f57511e1000
--- /dev/null
+++ b/apps/contacts/img/person_large.png
Binary files differ
diff --git a/apps/contacts/import.php b/apps/contacts/import.php
new file mode 100644
index 00000000000..9008208db59
--- /dev/null
+++ b/apps/contacts/import.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+//check for addressbooks rights or create new one
+ob_start();
+require_once ('../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_Util::checkAppEnabled('calendar');
+$nl = "\n";
+$progressfile = 'import_tmp/' . md5(session_id()) . '.txt';
+if(is_writable('import_tmp/')){
+ $progressfopen = fopen($progressfile, 'w');
+ fwrite($progressfopen, '10');
+ fclose($progressfopen);
+}
+$file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']);
+if($_POST['method'] == 'new'){
+ $id = OC_Contacts_Addressbook::add(OC_User::getUser(), $_POST['addressbookname']);
+ OC_Contacts_Addressbook::setActive($id, 1);
+}else{
+ $contacts = OC_Contacts_Addressbook::find($_POST['id']);
+ if($contacts['userid'] != OC_USER::getUser()){
+ OC_JSON::error();
+ exit();
+ }
+ $id = $_POST['id'];
+}
+//analyse the contacts file
+if(is_writable('import_tmp/')){
+ $progressfopen = fopen($progressfile, 'w');
+ fwrite($progressfopen, '20');
+ fclose($progressfopen);
+}
+$searchfor = array('VCARD');
+$parts = $searchfor;
+$filearr = explode($nl, $file);
+$inelement = false;
+$parts = array();
+$i = 0;
+foreach($filearr as $line){
+ foreach($searchfor as $search){
+ if(substr_count($line, $search) == 1){
+ list($attr, $val) = explode(':', $line);
+ if($attr == 'BEGIN'){
+ $parts[]['begin'] = $i;
+ $inelement = true;
+ }
+ if($attr == 'END'){
+ $parts[count($parts) - 1]['end'] = $i;
+ $inelement = false;
+ }
+ }
+ }
+ $i++;
+}
+//import the contacts
+if(is_writable('import_tmp/')){
+ $progressfopen = fopen($progressfile, 'w');
+ fwrite($progressfopen, '40');
+ fclose($progressfopen);
+}
+$start = '';
+for ($i = 0; $i < $parts[0]['begin']; $i++) {
+ if($i == 0){
+ $start = $filearr[0];
+ }else{
+ $start .= $nl . $filearr[$i];
+ }
+}
+$end = '';
+for($i = $parts[count($parts) - 1]['end'] + 1;$i <= count($filearr) - 1; $i++){
+ if($i == $parts[count($parts) - 1]['end'] + 1){
+ $end = $filearr[$parts[count($parts) - 1]['end'] + 1];
+ }else{
+ $end .= $nl . $filearr[$i];
+ }
+}
+if(is_writable('import_tmp/')){
+ $progressfopen = fopen($progressfile, 'w');
+ fwrite($progressfopen, '50');
+ fclose($progressfopen);
+}
+$importready = array();
+foreach($parts as $part){
+ for($i = $part['begin']; $i <= $part['end'];$i++){
+ if($i == $part['begin']){
+ $content = $filearr[$i];
+ }else{
+ $content .= $nl . $filearr[$i];
+ }
+ }
+ $importready[] = $start . $nl . $content . $nl . $end;
+}
+if(is_writable('import_tmp/')){
+ $progressfopen = fopen($progressfile, 'w');
+ fwrite($progressfopen, '70');
+ fclose($progressfopen);
+}
+if(count($parts) == 1){
+ OC_Contacts_VCard::add($id, $file);
+}else{
+ foreach($importready as $import){
+ OC_Contacts_VCard::add($id, $import);
+ }
+}
+//done the import
+if(is_writable('import_tmp/')){
+ $progressfopen = fopen($progressfile, 'w');
+ fwrite($progressfopen, '100');
+ fclose($progressfopen);
+}
+sleep(3);
+if(is_writable('import_tmp/')){
+ unlink($progressfile);
+}
+OC_JSON::success(); \ No newline at end of file
diff --git a/apps/contacts/import_tmp/Info b/apps/contacts/import_tmp/Info
new file mode 100644
index 00000000000..abafbce435c
--- /dev/null
+++ b/apps/contacts/import_tmp/Info
@@ -0,0 +1,2 @@
+This folder contains static files with the percentage of the import.
+Requires write permission
diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js
new file mode 100644
index 00000000000..7f4e938c48a
--- /dev/null
+++ b/apps/contacts/js/contacts.js
@@ -0,0 +1,1244 @@
+function ucwords (str) {
+ return (str + '').replace(/^([a-z])|\s+([a-z])/g, function ($1) {
+ return $1.toUpperCase();
+ });
+}
+
+String.prototype.strip_tags = function(){
+ tags = this;
+ stripped = tags.replace(/[\<\>]/gi, "");
+ return stripped;
+}
+
+
+Contacts={
+ UI:{
+ notImplemented:function() {
+ Contacts.UI.messageBox(t('contacts', 'Not implemented'), t('contacts', 'Sorry, this functionality has not been implemented yet'));
+ },
+ 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.'));
+ return;
+ }
+ // FIXME: I suck at regexp. /Tanghus
+ var adrarr = adr.split(';');
+ var adrstr = '';
+ if(adrarr[2].trim() != '') {
+ adrstr = adrstr + adrarr[2].trim() + ',';
+ }
+ if(adrarr[3].trim() != '') {
+ adrstr = adrstr + adrarr[3].trim() + ',';
+ }
+ if(adrarr[4].trim() != '') {
+ adrstr = adrstr + adrarr[4].trim() + ',';
+ }
+ if(adrarr[5].trim() != '') {
+ adrstr = adrstr + adrarr[5].trim() + ',';
+ }
+ if(adrarr[6].trim() != '') {
+ adrstr = adrstr + adrarr[6].trim();
+ }
+ console.log('adrstr: "' + adrstr + '"');
+ adrstr = encodeURIComponent(adrstr);
+ console.log('adrstr 2: ' + adrstr);
+ var uri = 'http://open.mapquestapi.com/nominatim/v1/search.php?q=' + adrstr + '&limit=10&addressdetails=1&zoom=';
+ 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.'));
+ return;
+ }
+ window.location.href='mailto:' + adr;
+ },
+ propertyContainerFor:function(obj) {
+ return $(obj).parents('.propertycontainer').first();
+ },
+ checksumFor:function(obj) {
+ return $(obj).parents('.propertycontainer').first().data('checksum');
+ },
+ propertyTypeFor:function(obj) {
+ 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);
+ console.log('showHideContactInfo: ' + show);
+ if(show) {
+ $('#contact_communication').show();
+ } else {
+ $('#contact_communication').hide();
+ }
+ },
+ checkListFor:function(obj) {
+ var type = $(obj).parents('.propertycontainer').first().data('element');
+ console.log('checkListFor: ' + type);
+ switch (type) {
+ case 'EMAIL':
+ console.log('emails: '+$('#emaillist>li').length);
+ if($('#emaillist li[class*="propertycontainer"]').length == 0) {
+ $('#emails').hide();
+ }
+ break;
+ case 'TEL':
+ console.log('phones: '+$('#phonelist>li').length);
+ if($('#phonelist li[class*="propertycontainer"]').length == 0) {
+ $('#phones').hide();
+ }
+ break;
+ case 'ADR':
+ console.log('addresses: '+$('#addressdisplay>dl').length);
+ if($('#addressdisplay dl[class*="propertycontainer"]').length == 0) {
+ $('#addresses').hide();
+ }
+ break;
+ case 'NICKNAME':
+ case 'ORG':
+ case 'BDAY':
+ break;
+ }
+ },
+ loading:function(obj, state) {
+ if(state) {
+ $(obj).addClass('loading');
+ } else {
+ $(obj).removeClass('loading');
+ }
+ },
+ showCardDAVUrl:function(username, bookname){
+ $('#carddav_url').val(totalurl + '/' + username + '/' + bookname);
+ $('#carddav_url').show();
+ $('#carddav_url_close').show();
+ },
+ messageBox:function(title, msg) {
+ //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();
+ $('.addresscard,.propertylist li,.propertycontainer').hover(
+ function () {
+ $(this).find('.globe,.mail,.delete,.edit').fadeIn(100);
+ },
+ function () {
+ $(this).find('.globe,.mail,.delete,.edit').fadeOut(100);
+ }
+ );
+ },
+ loadHandlers:function() {
+ //console.log('loadHandlers');
+ /*
+ $('.formfloat').hover(
+ function () {
+ $(this).find('.add').fadeIn(500);
+ },
+ function () {
+ $(this).find('.add').fadeOut(500);
+ }
+ );*/
+ $('.button,.action').tipsy();
+ $('#contacts_deletecard').tipsy({gravity: 'ne'});
+ $('#contacts_downloadcard').tipsy({gravity: 'ne'});
+ $('#fn').jec();
+ $('.jecEditableOption').attr('title', t('contacts','Custom'));
+ $('#fn').tipsy();
+ $('#contacts_details_photo_wrapper').tipsy();
+ $('#bday').datepicker({
+ dateFormat : 'dd-mm-yy'
+ });
+ // Style phone types
+ $('#phonelist').find('select[class*="contacts_property"]').multiselect({
+ noneSelectedText: t('contacts', 'Select type'),
+ header: false,
+ selectedList: 4,
+ classes: 'typelist'
+ });
+ $('#add_email').click(function(){
+ Contacts.UI.Card.addMail();
+ });
+ $('#add_phone').click(function(){
+ Contacts.UI.Card.addPhone();
+ });
+// $('#add_address').click(function(){
+// Contacts.UI.Card.editAddress();
+// return false;
+// });
+ $('#n').click(function(){
+ Contacts.UI.Card.editName();
+ //return false;
+ });
+ $('#edit_name').click(function(){
+ Contacts.UI.Card.editName();
+ return false;
+ });
+
+ /* Initialize the photo edit dialog */
+ $('#edit_photo_dialog').dialog({ autoOpen: false, modal: true, height: 'auto', width: 'auto' });
+ $('#edit_photo_dialog' ).dialog( 'option', 'buttons', [
+ {
+ text: "Ok",
+ click: function() {
+ Contacts.UI.Card.savePhoto(this);
+ $(this).dialog('close');
+ }
+ },
+ {
+ text: "Cancel",
+ click: function() { $(this).dialog('close'); }
+ }
+ ] );
+ Contacts.UI.loadListHandlers();
+ },
+ Card:{
+ id:'',
+ fn:'',
+ fullname:'',
+ shortname:'',
+ famname:'',
+ givname:'',
+ addname:'',
+ honpre:'',
+ honsuf:'',
+ data:undefined,
+ export:function() {
+ document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id;
+ //$.get(OC.linkTo('contacts', 'export.php'),{'contactid':this.id},function(jsondata){
+ //});
+ },
+ delete:function() {
+ $('#contacts_deletecard').tipsy('hide');
+ $.getJSON('ajax/deletecard.php',{'id':this.id},function(jsondata){
+ if(jsondata.status == 'success'){
+ $('#leftcontent [data-id="'+jsondata.data.id+'"]').remove();
+ $('#rightcontent').data('id','');
+ //$('#rightcontent').empty();
+ this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = '';
+ this.data = undefined;
+ // Load empty page.
+ var firstid = $('#contacts li:first-child').data('id');
+ console.log('trying to load: ' + firstid);
+ $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':firstid},function(jsondata){
+ if(jsondata.status == 'success'){
+ Contacts.UI.Card.loadContact(jsondata.data);
+ }
+ else{
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ }
+ });
+ }
+ else{
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ //alert(jsondata.data.message);
+ }
+ });
+ return false;
+ },
+ loadContact:function(jsondata){
+ $('#contact_communication').hide();
+ this.data = jsondata;
+ this.id = this.data.id;
+ //console.log('loaded: ' + this.data.FN[0]['value']);
+ this.populateNameFields();
+ this.loadPhoto();
+ this.loadMails();
+ this.loadPhones();
+ this.loadAddresses();
+ this.loadSingleProperties();
+ },
+ loadSingleProperties:function() {
+ var props = ['BDAY', 'NICKNAME', 'ORG'];
+ // Clear all elements
+ $('#ident .propertycontainer[class*="propertycontainer"]').each(function(){
+ if(props.indexOf($(this).data('element')) > -1) {
+ $(this).data('checksum', '');
+ $(this).find('input').val('');
+ $(this).hide();
+ $(this).prev().hide();
+ }
+ });
+ for(var prop in props) {
+ if(this.data[props[prop]] != undefined) {
+ $('#contacts_propertymenu a[data-type="'+props[prop]+'"]').parent().hide();
+ var property = this.data[props[prop]][0];
+ var value = property['value'], checksum = property['checksum'];
+ switch(props[prop]) {
+ case 'BDAY':
+ var val = $.datepicker.parseDate('yy-mm-dd', value.substring(0, 10));
+ value = $.datepicker.formatDate('dd-mm-yy', val);
+ $('#contact_identity').find('#bday').val(value);
+ $('#contact_identity').find('#bday_value').data('checksum', checksum);
+ $('#contact_identity').find('#bday_label').show();
+ $('#contact_identity').find('#bday_value').show();
+ break;
+ case 'NICKNAME':
+ $('#contact_identity').find('#nickname').val(value);
+ $('#contact_identity').find('#nickname_value').data('checksum', checksum);
+ $('#contact_identity').find('#nickname_label').show();
+ $('#contact_identity').find('#nickname_value').show();
+ break;
+ case 'ORG':
+ $('#contact_identity').find('#org').val(value);
+ $('#contact_identity').find('#org_value').data('checksum', checksum);
+ $('#contact_identity').find('#org_label').show();
+ $('#contact_identity').find('#org_value').show();
+ break;
+ }
+ } else {
+ $('#contacts_propertymenu a[data-type="'+props[prop]+'"]').parent().show();
+ }
+ }
+ },
+ populateNameFields:function() {
+ this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = '';
+ var full = '';
+ var narray = undefined;
+ this.fn = this.data.FN[0]['value'];
+ if(this.data.N == undefined) {
+ narray = [this.fn,'','','','']; // Checking for non-existing 'N' property :-P
+ full = this.fn;
+ } 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];
+ if(this.honpre.length > 0) {
+ this.fullname += this.honpre + ' ';
+ }
+ if(this.givname.length > 0) {
+ this.fullname += ' ' + this.givname;
+ }
+ if(this.addname.length > 0) {
+ this.fullname += ' ' + this.addname;
+ }
+ if(this.famname.length > 0) {
+ this.fullname += ' ' + this.famname;
+ }
+ if(this.honsuf.length > 0) {
+ this.fullname += ', ' + this.honsuf;
+ }
+ $('#n').html(this.fullname);
+ $('.jecEditableOption').attr('title', 'Custom');
+ $('.jecEditableOption').text(this.fn);
+ //$('.jecEditableOption').attr('value', 0);
+ $('#fn').val(0);
+ $('#full').text(this.fullname);
+ $('#short').text(this.givname + ' ' + this.famname);
+ $('#reverse').text(this.famname + ' ' + this.givname);
+ $('#reverse_comma').text(this.famname + ', ' + this.givname);
+ $('#contact_identity').find('*[data-element="N"]').data('checksum', this.data.N[0]['checksum']);
+ $('#contact_identity').find('*[data-element="FN"]').data('checksum', this.data.FN[0]['checksum']);
+ },
+ editNew:function(){ // add a new contact
+ //Contacts.UI.notImplemented();
+ //return false;
+ this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = '';
+ $.getJSON('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);
+ alert(jsondata.data.message);
+ }
+ });
+ },
+ add:function(n, fn, aid){ // add a new contact
+ console.log('Add contact: ' + n + ', ' + fn + ' ' + aid);
+ $.post(OC.filePath('contacts', 'ajax', 'addcontact.php'), { n: n, fn: fn, aid: aid },
+ function(jsondata) {
+ if (jsondata.status == 'success'){
+ $('#rightcontent').data('id',jsondata.data.id);
+ var id = jsondata.data.id;
+ $.getJSON('ajax/contactdetails.php',{'id':id},function(jsondata){
+ if(jsondata.status == 'success'){
+ Contacts.UI.loadHandlers();
+ Contacts.UI.Card.loadContact(jsondata.data);
+ $('#leftcontent .active').removeClass('active');
+ var item = '<li data-id="'+jsondata.data.id+'" class="active"><a href="index.php?id='+jsondata.data.id+'" style="background: url(thumbnail.php?id='+jsondata.data.id+') no-repeat scroll 0% 0% transparent;">'+Contacts.UI.Card.fn+'</a></li>';
+ var added = false;
+ $('#leftcontent ul li').each(function(){
+ if ($(this).text().toLowerCase() > Contacts.UI.Card.fn.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);
+ }
+ });
+ $('#contact_identity').show();
+ $('#actionbar').show();
+ // TODO: Add to contacts list.
+ }
+ else{
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ //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]);
+ var params = {};
+ var value = undefined;
+ jQuery.each(fields, function(i, field){
+ //.substring(11,'parameters[TYPE][]'.indexOf(']'))
+ if(field.name.substring(0, 5) === 'value') {
+ value = field.value;
+ } else if(field.name.substring(0, 10) === 'parameters') {
+ var p = field.name.substring(11,'parameters[TYPE][]'.indexOf(']'));
+ if(!(p in params)) {
+ params[p] = [];
+ }
+ params[p].push(field.value);
+ }
+ });
+ for(var i in this.data[name]) {
+ if(this.data[name][i]['checksum'] == oldchecksum) {
+ this.data[name][i]['checksum'] = checksum;
+ this.data[name][i]['value'] = value;
+ this.data[name][i]['parameters'] = params;
+ }
+ }
+ },
+ 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);
+ return false;
+ }
+ if($(obj).hasClass('nonempty') && $(obj).val().trim() == '') {
+ Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts', 'This property has to be non-empty.'));
+ return false;
+ }
+ container = $(obj).parents('.propertycontainer').first(); // get the parent holding the metadata.
+ Contacts.UI.loading(container, true);
+ 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();
+ if(q == '' || q == undefined) {
+ console.log('Couldn\'t serialize elements.');
+ Contacts.UI.loading(container, false);
+ return false;
+ }
+ q = q + '&id=' + this.id + '&name=' + name;
+ if(checksum != undefined && checksum != '') { // save
+ q = q + '&checksum=' + checksum;
+ console.log('Saving: ' + q);
+ $.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);
+ return true;
+ }
+ else{
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ Contacts.UI.loading(container, false);
+ return false;
+ }
+ },'json');
+ } else { // add
+ console.log('Adding: ' + q);
+ $.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);
+ return true;
+ }
+ else{
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ Contacts.UI.loading(container, false);
+ return false;
+ }
+ },'json');
+ }
+ },
+ addProperty:function(obj){
+ var type = $(obj).data('type');
+ console.log('addProperty:' + type);
+ switch (type) {
+ case 'PHOTO':
+ this.loadPhoto();
+ $('#file_upload_form').show();
+ $('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide();
+ break;
+ case 'EMAIL':
+ if($('#emaillist>li').length == 1) {
+ $('#emails').show();
+ }
+ Contacts.UI.Card.addMail();
+ Contacts.UI.showHideContactInfo();
+ break;
+ case 'TEL':
+ if($('#phonelist>li').length == 1) {
+ $('#phones').show();
+ }
+ Contacts.UI.Card.addPhone();
+ Contacts.UI.showHideContactInfo();
+ break;
+ case 'ADR':
+ if($('#addressdisplay>dl').length == 1) {
+ $('#addresses').show();
+ }
+ Contacts.UI.Card.editAddress('new', true);
+ Contacts.UI.showHideContactInfo();
+ break;
+ case 'NICKNAME':
+ case 'ORG':
+ case 'BDAY':
+ $('dl dt[data-element="'+type+'"],dd[data-element="'+type+'"]').show();
+ $('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide();
+ break;
+ }
+ },
+ deleteProperty:function(obj, type){
+ //console.log('deleteProperty, id: ' + this.id);
+ Contacts.UI.loading(obj, true);
+ var checksum = Contacts.UI.checksumFor(obj);
+ if(checksum != undefined) {
+ $.getJSON('ajax/deleteproperty.php',{'id': this.id, 'checksum': checksum },function(jsondata){
+ if(jsondata.status == 'success'){
+ if(type == 'list') {
+ Contacts.UI.propertyContainerFor(obj).remove();
+ Contacts.UI.showHideContactInfo();
+ Contacts.UI.checkListFor(obj);
+ } 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();
+ $('#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'));
+ Contacts.UI.loading(obj, false);
+ }
+ }
+ else{
+ Contacts.UI.loading(obj, false);
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ }
+ });
+ } else { // Property hasn't been saved so there's nothing to delete.
+ if(type == 'list') {
+ Contacts.UI.propertyContainerFor(obj).remove();
+ Contacts.UI.showHideContactInfo();
+ Contacts.UI.checkListFor(obj);
+ } 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();
+ $('#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'));
+ }
+ }
+ },
+ editName:function(){
+ var isnew = (this.id == '');
+ /* Initialize the name edit dialog */
+ 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({
+ modal: (isnew && true || false),
+ closeOnEscape: (isnew == '' && false || true),
+ title: (isnew && t('contacts', 'Add contact') || t('contacts', 'Edit name')),
+ height: 'auto', width: 'auto',
+ buttons: {
+ 'Ok':function() {
+ Contacts.UI.Card.saveName(this);
+ $(this).dialog('destroy').remove();
+ },
+ 'Cancel':function() { $(this).dialog('destroy').remove(); }
+ },
+ close : function(event, ui) {
+ $(this).dialog('destroy').remove();
+ //return event;
+ }/*,
+ open : function(event, ui) {
+ // load 'N' property - maybe :-P
+ }*/
+ });
+ });
+ }
+ },
+ saveName:function(dlg){
+ console.log('saveName, id: ' + this.id);
+ var n = new Array($(dlg).find('#fam').val().strip_tags(),$(dlg).find('#giv').val().strip_tags(),$(dlg).find('#add').val().strip_tags(),$(dlg).find('#pre').val().strip_tags(),$(dlg).find('#suf').val().strip_tags());
+ this.famname = n[0];
+ this.givname = n[1];
+ this.addname = n[2];
+ this.honpre = n[3];
+ this.honsuf = n[4];
+
+ $('#n').val(n.join(';'));
+ if(n[3].length > 0) {
+ this.fullname = n[3] + ' ';
+ }
+ this.fullname += n[1] + ' ' + n[2] + ' ' + n[0];
+ if(n[4].length > 0) {
+ this.fullname += ', ' + n[4];
+ }
+ $('#short').text(n[1] + ' ' + n[0]);
+ $('#full').text(this.fullname);
+ $('#reverse').text(n[0] + ' ' + n[1]);
+ $('#reverse_comma').text(n[0] + ', ' + n[1]);
+ //$('#n').html(full);
+ $('#fn').val(0);
+ if(this.id == '') {
+ var aid = $(dlg).find('#aid').val();
+ Contacts.UI.Card.add(n.join(';'), $('#short').text(), aid);
+ } else {
+ Contacts.UI.Card.saveProperty($('#n'));
+ }
+ },
+ loadAddresses:function(){
+ $('#addresses').hide();
+ $('#addressdisplay dl[class*="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');
+ $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']);
+ var adrarray = this.data.ADR[adr]['value'];
+ var adrtxt = '';
+ if(adrarray[0].length > 0) {
+ adrtxt = adrtxt + '<li>' + adrarray[0].strip_tags() + '</li>';
+ }
+ if(adrarray[1].length > 0) {
+ adrtxt = adrtxt + '<li>' + adrarray[1].strip_tags() + '</li>';
+ }
+ if(adrarray[2].length > 0) {
+ adrtxt = adrtxt + '<li>' + adrarray[2].strip_tags() + '</li>';
+ }
+ if(adrarray[3].length > 0 || adrarray[5].length > 0) {
+ adrtxt = adrtxt + '<li>' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + '</li>';
+ }
+ if(adrarray[4].length > 0) {
+ adrtxt = adrtxt + '<li>' + adrarray[4].strip_tags() + '</li>';
+ }
+ if(adrarray[6].length > 0) {
+ adrtxt = adrtxt + '<li>' + adrarray[6].strip_tags() + '</li>';
+ }
+ $('#addressdisplay dl').last().find('.addresslist').html(adrtxt);
+ var types = new Array();
+ var ttypes = new Array();
+ for(var param in this.data.ADR[adr]['parameters']) {
+ if(param.toUpperCase() == 'TYPE') {
+ types.push(t('contacts', ucwords(this.data.ADR[adr]['parameters'][param].toLowerCase())));
+ ttypes.push(this.data.ADR[adr]['parameters'][param]);
+ }
+ }
+ $('#addressdisplay dl').last().find('.adr_type_label').text(types.join('/'));
+ $('#addressdisplay dl').last().find('.adr_type').val(ttypes.join(','));
+ $('#addressdisplay dl').last().find('.adr').val(adrarray.join(';'));
+ $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']);
+ }
+ if($('#addressdisplay dl').length > 1) {
+ $('#addresses').show();
+ $('#contact_communication').show();
+ }
+ Contacts.UI.loadListHandlers();
+ return false;
+ },
+ editAddress:function(obj, isnew){
+ console.log('editAddress');
+ var container = undefined;
+ var q = q = '?id=' + this.id;
+ if(obj === 'new') {
+ isnew = true;
+ $('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show();
+ container = $('#addressdisplay dl').last();
+ container.removeClass('template').addClass('propertycontainer');
+ Contacts.UI.loadListHandlers();
+ } else {
+ q = q + '&checksum='+Contacts.UI.checksumFor(obj);
+ }
+ /* Initialize the address edit dialog */
+ if($('#edit_address_dialog').dialog('isOpen') == true){
+ $('#edit_address_dialog').dialog('moveToTop');
+ }else{
+ $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editaddress.php')+q, function(){
+ $('#edit_address_dialog' ).dialog({
+ /*modal: true,*/
+ height: 'auto', width: 'auto',
+ buttons: {
+ 'Ok':function() {
+ if(isnew) {
+ Contacts.UI.Card.saveAddress(this, $('#addressdisplay dl:last-child').find('input').first(), isnew);
+ } else {
+ Contacts.UI.Card.saveAddress(this, obj, isnew);
+ }
+ $(this).dialog('destroy').remove();
+ },
+ 'Cancel':function() {
+ $(this).dialog('destroy').remove();
+ if(isnew) {
+ container.remove();
+ }
+ Contacts.UI.showHideContactInfo();
+ }
+ },
+ close : function(event, ui) {
+ //alert('close');
+ $(this).dialog('destroy').remove();
+ if(isnew) {
+ container.remove();
+ }
+ Contacts.UI.showHideContactInfo();
+ }/*,
+ open : function(event, ui) {
+ // load 'ADR' property - maybe :-P
+ }*/
+ });
+ });
+ }
+ },
+ saveAddress:function(dlg, obj, isnew){
+ if(isnew) {
+ container = $('#addressdisplay dl').last();
+ obj = $('#addressdisplay dl:last-child').find('input').first();
+ } else {
+ checksum = Contacts.UI.checksumFor(obj);
+ container = Contacts.UI.propertyContainerFor(obj);
+ }
+ var adr = new Array($(dlg).find('#adr_pobox').val(),$(dlg).find('#adr_extended').val(),$(dlg).find('#adr_street').val(),$(dlg).find('#adr_city').val(),$(dlg).find('#adr_region').val(),$(dlg).find('#adr_zipcode').val(),$(dlg).find('#adr_country').val());
+ $(container).find('.adr').val(adr.join(';'));
+ $(container).find('.adr_type').val($(dlg).find('#adr_type').val());
+ $(container).find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase())));
+ Contacts.UI.Card.saveProperty($(container).find('input').first());
+ var adrtxt = '';
+ if(adr[0].length > 0) {
+ adrtxt = adrtxt + '<li>' + adr[0] + '</li>';
+ }
+ if(adr[1].length > 0) {
+ adrtxt = adrtxt + '<li>' + adr[1] + '</li>';
+ }
+ if(adr[2].length > 0) {
+ adrtxt = adrtxt + '<li>' + adr[2] + '</li>';
+ }
+ if(adr[3].length > 0 || adr[5].length > 0) {
+ adrtxt = adrtxt + '<li>' + adr[5] + ' ' + adr[3] + '</li>';
+ }
+ if(adr[4].length > 0) {
+ adrtxt = adrtxt + '<li>' + adr[4] + '</li>';
+ }
+ if(adr[6].length > 0) {
+ adrtxt = adrtxt + '<li>' + adr[6] + '</li>';
+ }
+ $(container).find('.addresslist').html(adrtxt);
+ },
+ uploadPhoto:function(filelist) {
+ if(!filelist) {
+ Contacts.UI.messageBox(t('contacts', 'Error'), t('contacts','No files selected for upload.'));
+ return;
+ }
+ //var file = filelist.item(0);
+ var file = filelist[0];
+ var target = $('#file_upload_target');
+ 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.'));
+ return;
+ } else {
+ target.load(function(){
+ var response=jQuery.parseJSON(target.contents().text());
+ if(response != undefined && response.status == 'success'){
+ 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);
+ }
+ });
+ form.submit();
+ }
+ },
+ loadPhoto:function(){
+ if(this.data.PHOTO) {
+ $('#file_upload_form').show();
+ $('#contacts_propertymenu a[data-type="PHOTO"]').parent().hide();
+ } else {
+ $('#file_upload_form').hide();
+ $('#contacts_propertymenu a[data-type="PHOTO"]').parent().show();
+ }
+ $.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){
+ if(jsondata.status == 'success'){
+ //alert(jsondata.data.page);
+ $('#contacts_details_photo_wrapper').html(jsondata.data.page);
+ }
+ else{
+ Contacts.UI.messageBox(jsondata.data.message);
+ }
+ });
+ },
+ editPhoto:function(id, tmp_path){
+ //alert('editPhoto: ' + tmp_path);
+ $.getJSON('ajax/cropphoto.php',{'tmp_path':tmp_path,'id':this.id},function(jsondata){
+ if(jsondata.status == 'success'){
+ //alert(jsondata.data.page);
+ $('#edit_photo_dialog_img').html(jsondata.data.page);
+ }
+ else{
+ Contacts.UI.messageBox(jsondata.data.message);
+ }
+ });
+ if($('#edit_photo_dialog').dialog('isOpen') == true){
+ $('#edit_photo_dialog').dialog('moveToTop');
+ } else {
+ $('#edit_photo_dialog').dialog('open');
+ }
+ },
+ savePhoto:function(){
+ var target = $('#crop_target');
+ var form = $('#cropform');
+ form.submit();
+ target.load(function(){
+ var response=jQuery.parseJSON(target.contents().text());
+ if(response != undefined && response.status == 'success'){
+ // load cropped photo.
+ $('#contacts_details_photo_wrapper').html(response.data.page);
+ }else{
+ Contacts.UI.messageBox(t('contacts','Error'), response.data.message);
+ }
+ });
+ $('#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:last-child').find('input[type="email"]').focus();
+ Contacts.UI.loadListHandlers();
+ return false;
+ },
+ loadMails:function() {
+ $('#emails').hide();
+ $('#emaillist li[class*="propertycontainer"]').remove();
+ for(var mail in this.data.EMAIL) {
+ this.addMail();
+ //$('#emaillist li:first-child').clone().appendTo($('#emaillist')).show();
+ $('#emaillist li:last-child').data('checksum', this.data.EMAIL[mail]['checksum'])
+ $('#emaillist li:last-child').find('input[type="email"]').val(this.data.EMAIL[mail]['value']);
+ for(var param in this.data.EMAIL[mail]['parameters']) {
+ if(param.toUpperCase() == 'PREF') {
+ $('#emaillist li:last-child').find('input[type="checkbox"]').attr('checked', 'checked')
+ }
+ }
+ }
+ if($('#emaillist li').length > 1) {
+ $('#emails').show();
+ $('#contact_communication').show();
+ }
+
+ $('#emaillist li:last-child').find('input[type="text"]').focus();
+ Contacts.UI.loadListHandlers();
+ 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:last-child').find('input[type="text"]').focus();
+ Contacts.UI.loadListHandlers();
+ $('#phonelist li:last-child').find('select').multiselect({
+ noneSelectedText: t('contacts', 'Select type'),
+ header: false,
+ selectedList: 4,
+ classes: 'typelist'
+ });
+ $('#phonelist li:last-child').show();
+ return false;
+ },
+ loadPhones:function() {
+ $('#phones').hide();
+ $('#phonelist li[class*="propertycontainer"]').remove();
+ for(var phone in this.data.TEL) {
+ this.addPhone();
+ $('#phonelist li:last-child').find('select').multiselect('destroy');
+ $('#phonelist li:last-child').data('checksum', this.data.TEL[phone]['checksum'])
+ $('#phonelist li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']);
+ for(var param in this.data.TEL[phone]['parameters']) {
+ if(param.toUpperCase() == 'PREF') {
+ $('#phonelist li:last-child').find('input[type="checkbox"]').attr('checked', 'checked');
+ }
+ else if(param.toUpperCase() == 'TYPE') {
+ for(ptype in this.data.TEL[phone]['parameters'][param]) {
+ var pt = this.data.TEL[phone]['parameters'][param][ptype];
+ $('#phonelist li:last-child').find('select option').each(function(){
+ if ($(this).val().toUpperCase() == pt.toUpperCase()) {
+ $(this).attr('selected', 'selected');
+ }
+ });
+ }
+ }
+ }
+ $('#phonelist li:last-child').find('select').multiselect({
+ noneSelectedText: t('contacts', 'Select type'),
+ header: false,
+ selectedList: 4,
+ classes: 'typelist'
+ });
+ }
+ if($('#phonelist li').length > 1) {
+ $('#phones').show();
+ $('#contact_communication').show();
+ }
+ return false;
+ },
+ },
+ 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) {
+ 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);
+ }
+ });
+ }
+ },
+ import:function(){
+ Contacts.UI.notImplemented();
+ },
+ 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(data){
+ if(data.status == 'success'){
+ $(button).closest('tr').prev().html(data.page).show().next().remove();
+ }
+ });
+ Contacts.UI.Contacts.update();
+ },
+ 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(){
+
+ Contacts.UI.loadHandlers();
+
+ /**
+ * Show the Addressbook chooser
+ */
+ $('#chooseaddressbook').click(function(){
+ Contacts.UI.Addressbooks.overview();
+ return false;
+ });
+
+ /**
+ * Open blank form to add new contact.
+ * FIXME: Load the same page but only show name data and popup the name edit dialog.
+ * On save load the page again with an id and show all fields.
+ * NOTE: Or: Load the full page and popup name dialog modal. On success set the newly aquired ID, on
+ * Cancel or failure give appropriate message and show ... something else :-P
+ */
+ $('#contacts_newcontact').click(function(){
+ Contacts.UI.Card.editNew();
+ });
+
+ /**
+ * 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/contactdetails.php',{'id':id},function(jsondata){
+ if(jsondata.status == 'success'){
+ Contacts.UI.Card.loadContact(jsondata.data);
+ }
+ else{
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ //alert(jsondata.data.message);
+ }
+ });
+ return false;
+ });
+
+ /**
+ * Delete currently selected contact TODO: and clear page
+ */
+ $('#contacts_deletecard').live('click',function(){
+ Contacts.UI.Card.delete();
+ });
+
+ $('#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();
+ // Triggers invisible file input
+ $('#contacts_details_photo').live('click', function() {
+ $('#file_upload_start').trigger('click');
+ return false;
+ });
+
+ // 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(){
+ Contacts.UI.Card.saveProperty(this);
+ });
+
+ // Name has changed. Update it and reorder.
+ $('#fn').live('change',function(){
+ var name = $('#fn').val();
+ var item = $('#contacts [data-id="'+Contacts.UI.Card.id+'"]').clone();
+ $('#contacts [data-id="'+Contacts.UI.Card.id+'"]').remove();
+ $(item).find('a').html(name);
+ var added = false;
+ $('#contacts li').each(function(){
+ if ($(this).text().toLowerCase() > name.toLowerCase()) {
+ $(this).before(item).fadeIn('fast');
+ added = true;
+ return false;
+ }
+ });
+ if(!added) {
+ $('#leftcontent ul').append(item);
+ }
+ });
+
+ /**
+ * Profile picture upload handling
+ */
+ // New profile picture selected
+ $('#file_upload_start').live('change',function(){
+ Contacts.UI.Card.uploadPhoto(this.files);
+ });
+ $('#contacts_details_photo').bind('dragover',function(event){
+ console.log('dragover');
+ $(event.target).css('background-color','red');
+ event.stopPropagation();
+ event.preventDefault();
+ });
+ $('#contacts_details_photo').bind('dragleave',function(event){
+ console.log('dragleave');
+ $(event.target).css('background-color','white');
+ //event.stopPropagation();
+ //event.preventDefault();
+ });
+ $('#contacts_details_photo').bind('drop',function(event){
+ event.stopPropagation();
+ event.preventDefault();
+ console.log('drop');
+ $(event.target).css('background-color','white')
+ $.fileUpload(event.originalEvent.dataTransfer.files);
+ });
+
+ /**
+ * Upload function for dropped files. Should go in the Contacts class/object.
+ */
+ $.fileUpload = function(files){
+ 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.'));
+ 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.'));
+ 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.'))
+ }
+ fileUpload = xhr.upload,
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4){
+ response = $.parseJSON(xhr.responseText);
+ if(response.status == 'success') {
+ 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);
+ }
+ } else {
+ //alert(xhr.responseText);
+ Contacts.UI.messageBox(t('contacts', 'Error'), response.data.message);
+ }
+ // stop loading indicator
+ //$('#contacts_details_photo_progress').hide();
+ }
+ };
+
+ fileUpload.onprogress = function(e){
+ if (e.lengthComputable){
+ var _progress = Math.round((e.loaded * 100) / e.total);
+ if (_progress != 100){
+ $('#contacts_details_photo_progress').text(_progress + '%');
+ $('#contacts_details_photo_progress').val(_progress);
+ }
+ }
+ };
+ // Start loading indicator.
+ //$('#contacts_details_photo_progress').show()();
+ xhr.open("POST", 'ajax/uploadphoto.php?id='+Contacts.UI.Card.id+'&imagefile='+encodeURIComponent(file.name), true);
+ xhr.setRequestHeader('Cache-Control', 'no-cache');
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ xhr.setRequestHeader('X_FILE_NAME', encodeURIComponent(file.name));
+ //xhr.setRequestHeader("X_FILENAME", file.name);
+ xhr.setRequestHeader('X-File-Size', file.size);
+ xhr.setRequestHeader('Content-Type', file.type);
+ xhr.send(file);
+ }
+
+ $('#contacts_propertymenu_button').live('click',function(){
+ $('#contacts_propertymenu').is(':hidden') && $('#contacts_propertymenu').show() || $('#contacts_propertymenu').hide();
+ });
+ $('#contacts_propertymenu a').live('click',function(){
+ Contacts.UI.Card.addProperty(this);
+ $('#contacts_propertymenu').hide();
+ });
+});
diff --git a/apps/contacts/js/jquery.Jcrop.js b/apps/contacts/js/jquery.Jcrop.js
new file mode 100644
index 00000000000..36f9a0f08f8
--- /dev/null
+++ b/apps/contacts/js/jquery.Jcrop.js
@@ -0,0 +1,1765 @@
+/**
+ * jquery.Jcrop.js v0.9.9 {{{
+ *
+ * jQuery Image Cropping Plugin - released under MIT License
+ * Author: Kelly Hallman <khallman@gmail.com>
+ * http://github.com/tapmodo/Jcrop
+ *
+ * }}}
+ * Copyright (c) 2008-2012 Tapmodo Interactive LLC {{{
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * }}}
+ */
+
+(function ($) {
+
+ $.Jcrop = function (obj, opt) {
+ var options = $.extend({}, $.Jcrop.defaults),
+ docOffset, lastcurs, ie6mode = false;
+
+ // Internal Methods {{{
+ function px(n) {
+ return parseInt(n, 10) + 'px';
+ }
+ function cssClass(cl) {
+ return options.baseClass + '-' + cl;
+ }
+ function supportsColorFade() {
+ return $.fx.step.hasOwnProperty('backgroundColor');
+ }
+ function getPos(obj) //{{{
+ {
+ var pos = $(obj).offset();
+ return [pos.left, pos.top];
+ }
+ //}}}
+ function mouseAbs(e) //{{{
+ {
+ return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])];
+ }
+ //}}}
+ function setOptions(opt) //{{{
+ {
+ if (typeof(opt) !== 'object') opt = {};
+ options = $.extend(options, opt);
+
+ $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) {
+ if (typeof(options[e]) !== 'function') options[e] = function () {};
+ });
+ }
+ //}}}
+ function startDragMode(mode, pos) //{{{
+ {
+ docOffset = getPos($img);
+ Tracker.setCursor(mode === 'move' ? mode : mode + '-resize');
+
+ if (mode === 'move') {
+ return Tracker.activateHandlers(createMover(pos), doneSelect);
+ }
+
+ var fc = Coords.getFixed();
+ var opp = oppLockCorner(mode);
+ var opc = Coords.getCorner(oppLockCorner(opp));
+
+ Coords.setPressed(Coords.getCorner(opp));
+ Coords.setCurrent(opc);
+
+ Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect);
+ }
+ //}}}
+ function dragmodeHandler(mode, f) //{{{
+ {
+ return function (pos) {
+ if (!options.aspectRatio) {
+ switch (mode) {
+ case 'e':
+ pos[1] = f.y2;
+ break;
+ case 'w':
+ pos[1] = f.y2;
+ break;
+ case 'n':
+ pos[0] = f.x2;
+ break;
+ case 's':
+ pos[0] = f.x2;
+ break;
+ }
+ } else {
+ switch (mode) {
+ case 'e':
+ pos[1] = f.y + 1;
+ break;
+ case 'w':
+ pos[1] = f.y + 1;
+ break;
+ case 'n':
+ pos[0] = f.x + 1;
+ break;
+ case 's':
+ pos[0] = f.x + 1;
+ break;
+ }
+ }
+ Coords.setCurrent(pos);
+ Selection.update();
+ };
+ }
+ //}}}
+ function createMover(pos) //{{{
+ {
+ var lloc = pos;
+ KeyManager.watchKeys();
+
+ return function (pos) {
+ Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
+ lloc = pos;
+
+ Selection.update();
+ };
+ }
+ //}}}
+ function oppLockCorner(ord) //{{{
+ {
+ switch (ord) {
+ case 'n':
+ return 'sw';
+ case 's':
+ return 'nw';
+ case 'e':
+ return 'nw';
+ case 'w':
+ return 'ne';
+ case 'ne':
+ return 'sw';
+ case 'nw':
+ return 'se';
+ case 'se':
+ return 'nw';
+ case 'sw':
+ return 'ne';
+ }
+ }
+ //}}}
+ function createDragger(ord) //{{{
+ {
+ return function (e) {
+ if (options.disabled) {
+ return false;
+ }
+ if ((ord === 'move') && !options.allowMove) {
+ return false;
+ }
+
+ // Fix position of crop area when dragged the very first time.
+ // Necessary when crop image is in a hidden element when page is loaded.
+ docOffset = getPos($img);
+
+ btndown = true;
+ startDragMode(ord, mouseAbs(e));
+ e.stopPropagation();
+ e.preventDefault();
+ return false;
+ };
+ }
+ //}}}
+ function presize($obj, w, h) //{{{
+ {
+ var nw = $obj.width(),
+ nh = $obj.height();
+ if ((nw > w) && w > 0) {
+ nw = w;
+ nh = (w / $obj.width()) * $obj.height();
+ }
+ if ((nh > h) && h > 0) {
+ nh = h;
+ nw = (h / $obj.height()) * $obj.width();
+ }
+ xscale = $obj.width() / nw;
+ yscale = $obj.height() / nh;
+ $obj.width(nw).height(nh);
+ }
+ //}}}
+ function unscale(c) //{{{
+ {
+ return {
+ x: parseInt(c.x * xscale, 10),
+ y: parseInt(c.y * yscale, 10),
+ x2: parseInt(c.x2 * xscale, 10),
+ y2: parseInt(c.y2 * yscale, 10),
+ w: parseInt(c.w * xscale, 10),
+ h: parseInt(c.h * yscale, 10)
+ };
+ }
+ //}}}
+ function doneSelect(pos) //{{{
+ {
+ var c = Coords.getFixed();
+ if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) {
+ Selection.enableHandles();
+ Selection.done();
+ } else {
+ Selection.release();
+ }
+ Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
+ }
+ //}}}
+ function newSelection(e) //{{{
+ {
+ if (options.disabled) {
+ return false;
+ }
+ if (!options.allowSelect) {
+ return false;
+ }
+ btndown = true;
+ docOffset = getPos($img);
+ Selection.disableHandles();
+ Tracker.setCursor('crosshair');
+ var pos = mouseAbs(e);
+ Coords.setPressed(pos);
+ Selection.update();
+ Tracker.activateHandlers(selectDrag, doneSelect);
+ KeyManager.watchKeys();
+
+ e.stopPropagation();
+ e.preventDefault();
+ return false;
+ }
+ //}}}
+ function selectDrag(pos) //{{{
+ {
+ Coords.setCurrent(pos);
+ Selection.update();
+ }
+ //}}}
+ function newTracker() //{{{
+ {
+ var trk = $('<div></div>').addClass(cssClass('tracker'));
+ if ($.browser.msie) {
+ trk.css({
+ opacity: 0,
+ backgroundColor: 'white'
+ });
+ }
+ return trk;
+ }
+ //}}}
+
+ // }}}
+ // Initialization {{{
+ // Sanitize some options {{{
+ if ($.browser.msie && ($.browser.version.split('.')[0] === '6')) {
+ ie6mode = true;
+ }
+ if (typeof(obj) !== 'object') {
+ obj = $(obj)[0];
+ }
+ if (typeof(opt) !== 'object') {
+ opt = {};
+ }
+ // }}}
+ setOptions(opt);
+ // Initialize some jQuery objects {{{
+ // The values are SET on the image(s) for the interface
+ // If the original image has any of these set, they will be reset
+ // However, if you destroy() the Jcrop instance the original image's
+ // character in the DOM will be as you left it.
+ var img_css = {
+ border: 'none',
+ visibility: 'visible',
+ margin: 0,
+ padding: 0,
+ position: 'absolute',
+ top: 0,
+ left: 0
+ };
+
+ var $origimg = $(obj),
+ img_mode = true;
+
+ if (obj.tagName == 'IMG') {
+ // Fix size of crop image.
+ // Necessary when crop image is within a hidden element when page is loaded.
+ if ($origimg[0].width != 0 && $origimg[0].height != 0) {
+ // Obtain dimensions from contained img element.
+ $origimg.width($origimg[0].width);
+ $origimg.height($origimg[0].height);
+ } else {
+ // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0).
+ var tempImage = new Image();
+ tempImage.src = $origimg[0].src;
+ $origimg.width(tempImage.width);
+ $origimg.height(tempImage.height);
+ }
+
+ var $img = $origimg.clone().removeAttr('id').css(img_css).show();
+
+ $img.width($origimg.width());
+ $img.height($origimg.height());
+ $origimg.after($img).hide();
+
+ } else {
+ $img = $origimg.css(img_css).show();
+ img_mode = false;
+ if (options.shade === null) { options.shade = true; }
+ }
+
+ presize($img, options.boxWidth, options.boxHeight);
+
+ var boundx = $img.width(),
+ boundy = $img.height(),
+
+
+ $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({
+ position: 'relative',
+ backgroundColor: options.bgColor
+ }).insertAfter($origimg).append($img);
+
+ if (options.addClass) {
+ $div.addClass(options.addClass);
+ }
+
+ var $img2 = $('<div />'),
+
+ $img_holder = $('<div />')
+ .width('100%').height('100%').css({
+ zIndex: 310,
+ position: 'absolute',
+ overflow: 'hidden'
+ }),
+
+ $hdl_holder = $('<div />')
+ .width('100%').height('100%').css('zIndex', 320),
+
+ $sel = $('<div />')
+ .css({
+ position: 'absolute',
+ zIndex: 600
+ }).dblclick(function(){
+ var c = Coords.getFixed();
+ options.onDblClick.call(api,c);
+ }).insertBefore($img).append($img_holder, $hdl_holder);
+
+ if (img_mode) {
+
+ $img2 = $('<img />')
+ .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy),
+
+ $img_holder.append($img2);
+
+ }
+
+ if (ie6mode) {
+ $sel.css({
+ overflowY: 'hidden'
+ });
+ }
+
+ var bound = options.boundary;
+ var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
+ position: 'absolute',
+ top: px(-bound),
+ left: px(-bound),
+ zIndex: 290
+ }).mousedown(newSelection);
+
+ /* }}} */
+ // Set more variables {{{
+ var bgcolor = options.bgColor,
+ bgopacity = options.bgOpacity,
+ xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true,
+ btndown, animating, shift_down;
+
+ docOffset = getPos($img);
+ // }}}
+ // }}}
+ // Internal Modules {{{
+ // Touch Module {{{
+ var Touch = (function () {
+ // Touch support detection function adapted (under MIT License)
+ // from code by Jeffrey Sambells - http://github.com/iamamused/
+ function hasTouchSupport() {
+ var support = {},
+ events = ['touchstart', 'touchmove', 'touchend'],
+ el = document.createElement('div'), i;
+
+ try {
+ for(i=0; i<events.length; i++) {
+ var eventName = events[i];
+ eventName = 'on' + eventName;
+ var isSupported = (eventName in el);
+ if (!isSupported) {
+ el.setAttribute(eventName, 'return;');
+ isSupported = typeof el[eventName] == 'function';
+ }
+ support[events[i]] = isSupported;
+ }
+ return support.touchstart && support.touchend && support.touchmove;
+ }
+ catch(err) {
+ return false;
+ }
+ }
+
+ function detectSupport() {
+ if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport;
+ else return hasTouchSupport();
+ }
+ return {
+ createDragger: function (ord) {
+ return function (e) {
+ e.pageX = e.originalEvent.changedTouches[0].pageX;
+ e.pageY = e.originalEvent.changedTouches[0].pageY;
+ if (options.disabled) {
+ return false;
+ }
+ if ((ord === 'move') && !options.allowMove) {
+ return false;
+ }
+ btndown = true;
+ startDragMode(ord, mouseAbs(e));
+ e.stopPropagation();
+ e.preventDefault();
+ return false;
+ };
+ },
+ newSelection: function (e) {
+ e.pageX = e.originalEvent.changedTouches[0].pageX;
+ e.pageY = e.originalEvent.changedTouches[0].pageY;
+ return newSelection(e);
+ },
+ isSupported: hasTouchSupport,
+ support: detectSupport()
+ };
+ }());
+ // }}}
+ // Coords Module {{{
+ var Coords = (function () {
+ var x1 = 0,
+ y1 = 0,
+ x2 = 0,
+ y2 = 0,
+ ox, oy;
+
+ function setPressed(pos) //{{{
+ {
+ pos = rebound(pos);
+ x2 = x1 = pos[0];
+ y2 = y1 = pos[1];
+ }
+ //}}}
+ function setCurrent(pos) //{{{
+ {
+ pos = rebound(pos);
+ ox = pos[0] - x2;
+ oy = pos[1] - y2;
+ x2 = pos[0];
+ y2 = pos[1];
+ }
+ //}}}
+ function getOffset() //{{{
+ {
+ return [ox, oy];
+ }
+ //}}}
+ function moveOffset(offset) //{{{
+ {
+ var ox = offset[0],
+ oy = offset[1];
+
+ if (0 > x1 + ox) {
+ ox -= ox + x1;
+ }
+ if (0 > y1 + oy) {
+ oy -= oy + y1;
+ }
+
+ if (boundy < y2 + oy) {
+ oy += boundy - (y2 + oy);
+ }
+ if (boundx < x2 + ox) {
+ ox += boundx - (x2 + ox);
+ }
+
+ x1 += ox;
+ x2 += ox;
+ y1 += oy;
+ y2 += oy;
+ }
+ //}}}
+ function getCorner(ord) //{{{
+ {
+ var c = getFixed();
+ switch (ord) {
+ case 'ne':
+ return [c.x2, c.y];
+ case 'nw':
+ return [c.x, c.y];
+ case 'se':
+ return [c.x2, c.y2];
+ case 'sw':
+ return [c.x, c.y2];
+ }
+ }
+ //}}}
+ function getFixed() //{{{
+ {
+ if (!options.aspectRatio) {
+ return getRect();
+ }
+ // This function could use some optimization I think...
+ var aspect = options.aspectRatio,
+ min_x = options.minSize[0] / xscale,
+
+
+ //min_y = options.minSize[1]/yscale,
+ max_x = options.maxSize[0] / xscale,
+ max_y = options.maxSize[1] / yscale,
+ rw = x2 - x1,
+ rh = y2 - y1,
+ rwa = Math.abs(rw),
+ rha = Math.abs(rh),
+ real_ratio = rwa / rha,
+ xx, yy, w, h;
+
+ if (max_x === 0) {
+ max_x = boundx * 10;
+ }
+ if (max_y === 0) {
+ max_y = boundy * 10;
+ }
+ if (real_ratio < aspect) {
+ yy = y2;
+ w = rha * aspect;
+ xx = rw < 0 ? x1 - w : w + x1;
+
+ if (xx < 0) {
+ xx = 0;
+ h = Math.abs((xx - x1) / aspect);
+ yy = rh < 0 ? y1 - h : h + y1;
+ } else if (xx > boundx) {
+ xx = boundx;
+ h = Math.abs((xx - x1) / aspect);
+ yy = rh < 0 ? y1 - h : h + y1;
+ }
+ } else {
+ xx = x2;
+ h = rwa / aspect;
+ yy = rh < 0 ? y1 - h : y1 + h;
+ if (yy < 0) {
+ yy = 0;
+ w = Math.abs((yy - y1) * aspect);
+ xx = rw < 0 ? x1 - w : w + x1;
+ } else if (yy > boundy) {
+ yy = boundy;
+ w = Math.abs(yy - y1) * aspect;
+ xx = rw < 0 ? x1 - w : w + x1;
+ }
+ }
+
+ // Magic %-)
+ if (xx > x1) { // right side
+ if (xx - x1 < min_x) {
+ xx = x1 + min_x;
+ } else if (xx - x1 > max_x) {
+ xx = x1 + max_x;
+ }
+ if (yy > y1) {
+ yy = y1 + (xx - x1) / aspect;
+ } else {
+ yy = y1 - (xx - x1) / aspect;
+ }
+ } else if (xx < x1) { // left side
+ if (x1 - xx < min_x) {
+ xx = x1 - min_x;
+ } else if (x1 - xx > max_x) {
+ xx = x1 - max_x;
+ }
+ if (yy > y1) {
+ yy = y1 + (x1 - xx) / aspect;
+ } else {
+ yy = y1 - (x1 - xx) / aspect;
+ }
+ }
+
+ if (xx < 0) {
+ x1 -= xx;
+ xx = 0;
+ } else if (xx > boundx) {
+ x1 -= xx - boundx;
+ xx = boundx;
+ }
+
+ if (yy < 0) {
+ y1 -= yy;
+ yy = 0;
+ } else if (yy > boundy) {
+ y1 -= yy - boundy;
+ yy = boundy;
+ }
+
+ return makeObj(flipCoords(x1, y1, xx, yy));
+ }
+ //}}}
+ function rebound(p) //{{{
+ {
+ if (p[0] < 0) {
+ p[0] = 0;
+ }
+ if (p[1] < 0) {
+ p[1] = 0;
+ }
+
+ if (p[0] > boundx) {
+ p[0] = boundx;
+ }
+ if (p[1] > boundy) {
+ p[1] = boundy;
+ }
+
+ return [p[0], p[1]];
+ }
+ //}}}
+ function flipCoords(x1, y1, x2, y2) //{{{
+ {
+ var xa = x1,
+ xb = x2,
+ ya = y1,
+ yb = y2;
+ if (x2 < x1) {
+ xa = x2;
+ xb = x1;
+ }
+ if (y2 < y1) {
+ ya = y2;
+ yb = y1;
+ }
+ return [Math.round(xa), Math.round(ya), Math.round(xb), Math.round(yb)];
+ }
+ //}}}
+ function getRect() //{{{
+ {
+ var xsize = x2 - x1,
+ ysize = y2 - y1,
+ delta;
+
+ if (xlimit && (Math.abs(xsize) > xlimit)) {
+ x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
+ }
+ if (ylimit && (Math.abs(ysize) > ylimit)) {
+ y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
+ }
+
+ if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) {
+ y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale);
+ }
+ if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) {
+ x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale);
+ }
+
+ if (x1 < 0) {
+ x2 -= x1;
+ x1 -= x1;
+ }
+ if (y1 < 0) {
+ y2 -= y1;
+ y1 -= y1;
+ }
+ if (x2 < 0) {
+ x1 -= x2;
+ x2 -= x2;
+ }
+ if (y2 < 0) {
+ y1 -= y2;
+ y2 -= y2;
+ }
+ if (x2 > boundx) {
+ delta = x2 - boundx;
+ x1 -= delta;
+ x2 -= delta;
+ }
+ if (y2 > boundy) {
+ delta = y2 - boundy;
+ y1 -= delta;
+ y2 -= delta;
+ }
+ if (x1 > boundx) {
+ delta = x1 - boundy;
+ y2 -= delta;
+ y1 -= delta;
+ }
+ if (y1 > boundy) {
+ delta = y1 - boundy;
+ y2 -= delta;
+ y1 -= delta;
+ }
+
+ return makeObj(flipCoords(x1, y1, x2, y2));
+ }
+ //}}}
+ function makeObj(a) //{{{
+ {
+ return {
+ x: a[0],
+ y: a[1],
+ x2: a[2],
+ y2: a[3],
+ w: a[2] - a[0],
+ h: a[3] - a[1]
+ };
+ }
+ //}}}
+
+ return {
+ flipCoords: flipCoords,
+ setPressed: setPressed,
+ setCurrent: setCurrent,
+ getOffset: getOffset,
+ moveOffset: moveOffset,
+ getCorner: getCorner,
+ getFixed: getFixed
+ };
+ }());
+
+ //}}}
+ // Shade Module {{{
+ var Shade = (function() {
+ var enabled = false,
+ holder = $('<div />').css({
+ position: 'absolute',
+ zIndex: 240,
+ opacity: 0
+ }),
+ shades = {
+ top: createShade(),
+ left: createShade().height(boundy),
+ right: createShade().height(boundy),
+ bottom: createShade()
+ };
+
+ function resizeShades(w,h) {
+ shades.left.css({ height: px(h) });
+ shades.right.css({ height: px(h) });
+ }
+ function updateAuto()
+ {
+ return updateShade(Coords.getFixed());
+ }
+ function updateShade(c)
+ {
+ shades.top.css({
+ left: px(c.x),
+ width: px(c.w),
+ height: px(c.y)
+ });
+ shades.bottom.css({
+ top: px(c.y2),
+ left: px(c.x),
+ width: px(c.w),
+ height: px(boundy-c.y2)
+ });
+ shades.right.css({
+ left: px(c.x2),
+ width: px(boundx-c.x2)
+ });
+ shades.left.css({
+ width: px(c.x)
+ });
+ }
+ function createShade() {
+ return $('<div />').css({
+ position: 'absolute',
+ backgroundColor: options.shadeColor||options.bgColor
+ }).appendTo(holder);
+ }
+ function enableShade() {
+ if (!enabled) {
+ enabled = true;
+ holder.insertBefore($img);
+ updateAuto();
+ Selection.setBgOpacity(1,0,1);
+ $img2.hide();
+
+ setBgColor(options.shadeColor||options.bgColor,1);
+ if (Selection.isAwake())
+ {
+ setOpacity(options.bgOpacity,1);
+ }
+ else setOpacity(1,1);
+ }
+ }
+ function setBgColor(color,now) {
+ colorChangeMacro(getShades(),color,now);
+ }
+ function disableShade() {
+ if (enabled) {
+ holder.remove();
+ $img2.show();
+ enabled = false;
+ if (Selection.isAwake()) {
+ Selection.setBgOpacity(options.bgOpacity,1,1);
+ } else {
+ Selection.setBgOpacity(1,1,1);
+ Selection.disableHandles();
+ }
+ colorChangeMacro($div,0,1);
+ }
+ }
+ function setOpacity(opacity,now) {
+ if (enabled) {
+ if (options.bgFade && !now) {
+ holder.animate({
+ opacity: 1-opacity
+ },{
+ queue: false,
+ duration: options.fadeTime
+ });
+ }
+ else holder.css({opacity:1-opacity});
+ }
+ }
+ function refreshAll() {
+ options.shade ? enableShade() : disableShade();
+ if (Selection.isAwake()) setOpacity(options.bgOpacity);
+ }
+ function getShades() {
+ return holder.children();
+ }
+
+ return {
+ update: updateAuto,
+ updateRaw: updateShade,
+ getShades: getShades,
+ setBgColor: setBgColor,
+ enable: enableShade,
+ disable: disableShade,
+ resize: resizeShades,
+ refresh: refreshAll,
+ opacity: setOpacity
+ };
+ }());
+ // }}}
+ // Selection Module {{{
+ var Selection = (function () {
+ var awake, hdep = 370;
+ var borders = {};
+ var handle = {};
+ var seehandles = false;
+ var hhs = options.handleOffset;
+
+ // Private Methods
+ function insertBorder(type) //{{{
+ {
+ var jq = $('<div />').css({
+ position: 'absolute',
+ opacity: options.borderOpacity
+ }).addClass(cssClass(type));
+ $img_holder.append(jq);
+ return jq;
+ }
+ //}}}
+ function dragDiv(ord, zi) //{{{
+ {
+ var jq = $('<div />').mousedown(createDragger(ord)).css({
+ cursor: ord + '-resize',
+ position: 'absolute',
+ zIndex: zi
+ }).addClass('ord-'+ord);
+
+ if (Touch.support) {
+ jq.bind('touchstart.jcrop', Touch.createDragger(ord));
+ }
+
+ $hdl_holder.append(jq);
+ return jq;
+ }
+ //}}}
+ function insertHandle(ord) //{{{
+ {
+ var hs = options.handleSize;
+ return dragDiv(ord, hdep++).css({
+ top: px(-hhs + 1),
+ left: px(-hhs + 1),
+ opacity: options.handleOpacity
+ }).width(hs).height(hs).addClass(cssClass('handle'));
+ }
+ //}}}
+ function insertDragbar(ord) //{{{
+ {
+ var s = options.handleSize,
+ h = s,
+ w = s,
+ t = hhs,
+ l = hhs;
+
+ switch (ord) {
+ case 'n':
+ case 's':
+ w = '100%';
+ break;
+ case 'e':
+ case 'w':
+ h = '100%';
+ break;
+ }
+
+ return dragDiv(ord, hdep++).width(w).height(h).css({
+ top: px(-t + 1),
+ left: px(-l + 1)
+ });
+ }
+ //}}}
+ function createHandles(li) //{{{
+ {
+ var i;
+ for (i = 0; i < li.length; i++) {
+ handle[li[i]] = insertHandle(li[i]);
+ }
+ }
+ //}}}
+ function moveHandles(c) //{{{
+ {
+ var midvert = Math.round((c.h / 2) - hhs),
+ midhoriz = Math.round((c.w / 2) - hhs),
+ north = -hhs + 1,
+ west = -hhs + 1,
+ east = c.w - hhs,
+ south = c.h - hhs,
+ x, y;
+
+ if (handle.e) {
+ handle.e.css({
+ top: px(midvert),
+ left: px(east)
+ });
+ handle.w.css({
+ top: px(midvert)
+ });
+ handle.s.css({
+ top: px(south),
+ left: px(midhoriz)
+ });
+ handle.n.css({
+ left: px(midhoriz)
+ });
+ }
+ if (handle.ne) {
+ handle.ne.css({
+ left: px(east)
+ });
+ handle.se.css({
+ top: px(south),
+ left: px(east)
+ });
+ handle.sw.css({
+ top: px(south)
+ });
+ }
+ if (handle.b) {
+ handle.b.css({
+ top: px(south)
+ });
+ handle.r.css({
+ left: px(east)
+ });
+ }
+ }
+ //}}}
+ function moveto(x, y) //{{{
+ {
+ if (!options.shade) {
+ $img2.css({
+ top: px(-y),
+ left: px(-x)
+ });
+ }
+ $sel.css({
+ top: px(y),
+ left: px(x)
+ });
+ }
+ //}}}
+ function resize(w, h) //{{{
+ {
+ $sel.width(w).height(h);
+ }
+ //}}}
+ function refresh() //{{{
+ {
+ var c = Coords.getFixed();
+
+ Coords.setPressed([c.x, c.y]);
+ Coords.setCurrent([c.x2, c.y2]);
+
+ updateVisible();
+ }
+ //}}}
+
+ // Internal Methods
+ function updateVisible(select) //{{{
+ {
+ if (awake) {
+ return update(select);
+ }
+ }
+ //}}}
+ function update(select) //{{{
+ {
+ var c = Coords.getFixed();
+
+ resize(c.w, c.h);
+ moveto(c.x, c.y);
+ if (options.shade) Shade.updateRaw(c);
+
+ if (seehandles) {
+ moveHandles(c);
+ }
+ if (!awake) {
+ show();
+ }
+
+ if (select) {
+ options.onSelect.call(api, unscale(c));
+ } else {
+ options.onChange.call(api, unscale(c));
+ }
+ }
+ //}}}
+ function setBgOpacity(opacity,force,now)
+ {
+ if (!awake && !force) return;
+ if (options.bgFade && !now) {
+ $img.animate({
+ opacity: opacity
+ },{
+ queue: false,
+ duration: options.fadeTime
+ });
+ } else {
+ $img.css('opacity', opacity);
+ }
+ }
+ function show() //{{{
+ {
+ $sel.show();
+
+ if (options.shade) Shade.opacity(bgopacity);
+ else setBgOpacity(bgopacity,true);
+
+ awake = true;
+ }
+ //}}}
+ function release() //{{{
+ {
+ disableHandles();
+ $sel.hide();
+
+ if (options.shade) Shade.opacity(1);
+ else setBgOpacity(1);
+
+ awake = false;
+ options.onRelease.call(api);
+ }
+ //}}}
+ function showHandles() //{{{
+ {
+ if (seehandles) {
+ moveHandles(Coords.getFixed());
+ $hdl_holder.show();
+ }
+ }
+ //}}}
+ function enableHandles() //{{{
+ {
+ seehandles = true;
+ if (options.allowResize) {
+ moveHandles(Coords.getFixed());
+ $hdl_holder.show();
+ return true;
+ }
+ }
+ //}}}
+ function disableHandles() //{{{
+ {
+ seehandles = false;
+ $hdl_holder.hide();
+ }
+ //}}}
+ function animMode(v) //{{{
+ {
+ if (animating === v) {
+ disableHandles();
+ } else {
+ enableHandles();
+ }
+ }
+ //}}}
+ function done() //{{{
+ {
+ animMode(false);
+ refresh();
+ }
+ //}}}
+ /* Insert draggable elements {{{*/
+
+ // Insert border divs for outline
+ if (options.drawBorders) {
+ borders = {
+ top: insertBorder('hline'),
+ bottom: insertBorder('hline bottom'),
+ left: insertBorder('vline'),
+ right: insertBorder('vline right')
+ };
+ }
+
+ // Insert handles on edges
+ if (options.dragEdges) {
+ handle.t = insertDragbar('n');
+ handle.b = insertDragbar('s');
+ handle.r = insertDragbar('e');
+ handle.l = insertDragbar('w');
+ }
+
+ // Insert side and corner handles
+ if (options.sideHandles) {
+ createHandles(['n', 's', 'e', 'w']);
+ }
+ if (options.cornerHandles) {
+ createHandles(['sw', 'nw', 'ne', 'se']);
+ }
+ //}}}
+
+ // This is a hack for iOS5 to support drag/move touch functionality
+ $(document).bind('touchstart.jcrop-ios',function(e) {
+ if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation();
+ });
+
+ var $track = newTracker().mousedown(createDragger('move')).css({
+ cursor: 'move',
+ position: 'absolute',
+ zIndex: 360
+ });
+
+ if (Touch.support) {
+ $track.bind('touchstart.jcrop', Touch.createDragger('move'));
+ }
+
+ $img_holder.append($track);
+ disableHandles();
+
+ return {
+ updateVisible: updateVisible,
+ update: update,
+ release: release,
+ refresh: refresh,
+ isAwake: function () {
+ return awake;
+ },
+ setCursor: function (cursor) {
+ $track.css('cursor', cursor);
+ },
+ enableHandles: enableHandles,
+ enableOnly: function () {
+ seehandles = true;
+ },
+ showHandles: showHandles,
+ disableHandles: disableHandles,
+ animMode: animMode,
+ setBgOpacity: setBgOpacity,
+ done: done
+ };
+ }());
+
+ //}}}
+ // Tracker Module {{{
+ var Tracker = (function () {
+ var onMove = function () {},
+ onDone = function () {},
+ trackDoc = options.trackDocument;
+
+ function toFront() //{{{
+ {
+ $trk.css({
+ zIndex: 450
+ });
+ if (Touch.support) {
+ $(document)
+ .bind('touchmove.jcrop', trackTouchMove)
+ .bind('touchend.jcrop', trackTouchEnd);
+ }
+ if (trackDoc) {
+ $(document)
+ .bind('mousemove.jcrop',trackMove)
+ .bind('mouseup.jcrop',trackUp);
+ }
+ }
+ //}}}
+ function toBack() //{{{
+ {
+ $trk.css({
+ zIndex: 290
+ });
+ $(document).unbind('.jcrop');
+ }
+ //}}}
+ function trackMove(e) //{{{
+ {
+ onMove(mouseAbs(e));
+ return false;
+ }
+ //}}}
+ function trackUp(e) //{{{
+ {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (btndown) {
+ btndown = false;
+
+ onDone(mouseAbs(e));
+
+ if (Selection.isAwake()) {
+ options.onSelect.call(api, unscale(Coords.getFixed()));
+ }
+
+ toBack();
+ onMove = function () {};
+ onDone = function () {};
+ }
+
+ return false;
+ }
+ //}}}
+ function activateHandlers(move, done) //{{{
+ {
+ btndown = true;
+ onMove = move;
+ onDone = done;
+ toFront();
+ return false;
+ }
+ //}}}
+ function trackTouchMove(e) //{{{
+ {
+ e.pageX = e.originalEvent.changedTouches[0].pageX;
+ e.pageY = e.originalEvent.changedTouches[0].pageY;
+ return trackMove(e);
+ }
+ //}}}
+ function trackTouchEnd(e) //{{{
+ {
+ e.pageX = e.originalEvent.changedTouches[0].pageX;
+ e.pageY = e.originalEvent.changedTouches[0].pageY;
+ return trackUp(e);
+ }
+ //}}}
+ function setCursor(t) //{{{
+ {
+ $trk.css('cursor', t);
+ }
+ //}}}
+
+ if (!trackDoc) {
+ $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
+ }
+
+ $img.before($trk);
+ return {
+ activateHandlers: activateHandlers,
+ setCursor: setCursor
+ };
+ }());
+ //}}}
+ // KeyManager Module {{{
+ var KeyManager = (function () {
+ var $keymgr = $('<input type="radio" />').css({
+ position: 'fixed',
+ left: '-120px',
+ width: '12px'
+ }),
+ $keywrap = $('<div />').css({
+ position: 'absolute',
+ overflow: 'hidden'
+ }).append($keymgr);
+
+ function watchKeys() //{{{
+ {
+ if (options.keySupport) {
+ $keymgr.show();
+ $keymgr.focus();
+ }
+ }
+ //}}}
+ function onBlur(e) //{{{
+ {
+ $keymgr.hide();
+ }
+ //}}}
+ function doNudge(e, x, y) //{{{
+ {
+ if (options.allowMove) {
+ Coords.moveOffset([x, y]);
+ Selection.updateVisible(true);
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ //}}}
+ function parseKey(e) //{{{
+ {
+ if (e.ctrlKey || e.metaKey) {
+ return true;
+ }
+ shift_down = e.shiftKey ? true : false;
+ var nudge = shift_down ? 10 : 1;
+
+ switch (e.keyCode) {
+ case 37:
+ doNudge(e, -nudge, 0);
+ break;
+ case 39:
+ doNudge(e, nudge, 0);
+ break;
+ case 38:
+ doNudge(e, 0, -nudge);
+ break;
+ case 40:
+ doNudge(e, 0, nudge);
+ break;
+ case 27:
+ if (options.allowSelect) Selection.release();
+ break;
+ case 9:
+ return true;
+ }
+
+ return false;
+ }
+ //}}}
+
+ if (options.keySupport) {
+ $keymgr.keydown(parseKey).blur(onBlur);
+ if (ie6mode || !options.fixedSupport) {
+ $keymgr.css({
+ position: 'absolute',
+ left: '-20px'
+ });
+ $keywrap.append($keymgr).insertBefore($img);
+ } else {
+ $keymgr.insertBefore($img);
+ }
+ }
+
+
+ return {
+ watchKeys: watchKeys
+ };
+ }());
+ //}}}
+ // }}}
+ // API methods {{{
+ function setClass(cname) //{{{
+ {
+ $div.removeClass().addClass(cssClass('holder')).addClass(cname);
+ }
+ //}}}
+ function animateTo(a, callback) //{{{
+ {
+ var x1 = parseInt(a[0], 10) / xscale,
+ y1 = parseInt(a[1], 10) / yscale,
+ x2 = parseInt(a[2], 10) / xscale,
+ y2 = parseInt(a[3], 10) / yscale;
+
+ if (animating) {
+ return;
+ }
+
+ var animto = Coords.flipCoords(x1, y1, x2, y2),
+ c = Coords.getFixed(),
+ initcr = [c.x, c.y, c.x2, c.y2],
+ animat = initcr,
+ interv = options.animationDelay,
+ ix1 = animto[0] - initcr[0],
+ iy1 = animto[1] - initcr[1],
+ ix2 = animto[2] - initcr[2],
+ iy2 = animto[3] - initcr[3],
+ pcent = 0,
+ velocity = options.swingSpeed;
+
+ x = animat[0];
+ y = animat[1];
+ x2 = animat[2];
+ y2 = animat[3];
+
+ Selection.animMode(true);
+ var anim_timer;
+
+ function queueAnimator() {
+ window.setTimeout(animator, interv);
+ }
+ var animator = (function () {
+ return function () {
+ pcent += (100 - pcent) / velocity;
+
+ animat[0] = x + ((pcent / 100) * ix1);
+ animat[1] = y + ((pcent / 100) * iy1);
+ animat[2] = x2 + ((pcent / 100) * ix2);
+ animat[3] = y2 + ((pcent / 100) * iy2);
+
+ if (pcent >= 99.8) {
+ pcent = 100;
+ }
+ if (pcent < 100) {
+ setSelectRaw(animat);
+ queueAnimator();
+ } else {
+ Selection.done();
+ if (typeof(callback) === 'function') {
+ callback.call(api);
+ }
+ }
+ };
+ }());
+ queueAnimator();
+ }
+ //}}}
+ function setSelect(rect) //{{{
+ {
+ setSelectRaw([parseInt(rect[0], 10) / xscale, parseInt(rect[1], 10) / yscale, parseInt(rect[2], 10) / xscale, parseInt(rect[3], 10) / yscale]);
+ options.onSelect.call(api, unscale(Coords.getFixed()));
+ Selection.enableHandles();
+ }
+ //}}}
+ function setSelectRaw(l) //{{{
+ {
+ Coords.setPressed([l[0], l[1]]);
+ Coords.setCurrent([l[2], l[3]]);
+ Selection.update();
+ }
+ //}}}
+ function tellSelect() //{{{
+ {
+ return unscale(Coords.getFixed());
+ }
+ //}}}
+ function tellScaled() //{{{
+ {
+ return Coords.getFixed();
+ }
+ //}}}
+ function setOptionsNew(opt) //{{{
+ {
+ setOptions(opt);
+ interfaceUpdate();
+ }
+ //}}}
+ function disableCrop() //{{{
+ {
+ options.disabled = true;
+ Selection.disableHandles();
+ Selection.setCursor('default');
+ Tracker.setCursor('default');
+ }
+ //}}}
+ function enableCrop() //{{{
+ {
+ options.disabled = false;
+ interfaceUpdate();
+ }
+ //}}}
+ function cancelCrop() //{{{
+ {
+ Selection.done();
+ Tracker.activateHandlers(null, null);
+ }
+ //}}}
+ function destroy() //{{{
+ {
+ $div.remove();
+ $origimg.show();
+ $(obj).removeData('Jcrop');
+ }
+ //}}}
+ function setImage(src, callback) //{{{
+ {
+ Selection.release();
+ disableCrop();
+ var img = new Image();
+ img.onload = function () {
+ var iw = img.width;
+ var ih = img.height;
+ var bw = options.boxWidth;
+ var bh = options.boxHeight;
+ $img.width(iw).height(ih);
+ $img.attr('src', src);
+ $img2.attr('src', src);
+ presize($img, bw, bh);
+ boundx = $img.width();
+ boundy = $img.height();
+ $img2.width(boundx).height(boundy);
+ $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2));
+ $div.width(boundx).height(boundy);
+ Shade.resize(boundx,boundy);
+ enableCrop();
+
+ if (typeof(callback) === 'function') {
+ callback.call(api);
+ }
+ };
+ img.src = src;
+ }
+ //}}}
+ function colorChangeMacro($obj,color,now) {
+ var mycolor = color || options.bgColor;
+ if (options.bgFade && supportsColorFade() && options.fadeTime && !now) {
+ $obj.animate({
+ backgroundColor: mycolor
+ }, {
+ queue: false,
+ duration: options.fadeTime
+ });
+ } else {
+ $obj.css('backgroundColor', mycolor);
+ }
+ }
+ function interfaceUpdate(alt) //{{{
+ // This method tweaks the interface based on options object.
+ // Called when options are changed and at end of initialization.
+ {
+ if (options.allowResize) {
+ if (alt) {
+ Selection.enableOnly();
+ } else {
+ Selection.enableHandles();
+ }
+ } else {
+ Selection.disableHandles();
+ }
+
+ Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
+ Selection.setCursor(options.allowMove ? 'move' : 'default');
+
+ if (options.hasOwnProperty('trueSize')) {
+ xscale = options.trueSize[0] / boundx;
+ yscale = options.trueSize[1] / boundy;
+ }
+
+ if (options.hasOwnProperty('setSelect')) {
+ setSelect(options.setSelect);
+ Selection.done();
+ delete(options.setSelect);
+ }
+
+ Shade.refresh();
+
+ if (options.bgColor != bgcolor) {
+ colorChangeMacro(
+ options.shade? Shade.getShades(): $div,
+ options.shade?
+ (options.shadeColor || options.bgColor):
+ options.bgColor
+ );
+ bgcolor = options.bgColor;
+ }
+
+ if (bgopacity != options.bgOpacity) {
+ bgopacity = options.bgOpacity;
+ if (options.shade) Shade.refresh();
+ else Selection.setBgOpacity(bgopacity);
+ }
+
+ xlimit = options.maxSize[0] || 0;
+ ylimit = options.maxSize[1] || 0;
+ xmin = options.minSize[0] || 0;
+ ymin = options.minSize[1] || 0;
+
+ if (options.hasOwnProperty('outerImage')) {
+ $img.attr('src', options.outerImage);
+ delete(options.outerImage);
+ }
+
+ Selection.refresh();
+ }
+ //}}}
+ //}}}
+
+ if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection);
+
+ $hdl_holder.hide();
+ interfaceUpdate(true);
+
+ var api = {
+ setImage: setImage,
+ animateTo: animateTo,
+ setSelect: setSelect,
+ setOptions: setOptionsNew,
+ tellSelect: tellSelect,
+ tellScaled: tellScaled,
+ setClass: setClass,
+
+ disable: disableCrop,
+ enable: enableCrop,
+ cancel: cancelCrop,
+ release: Selection.release,
+ destroy: destroy,
+
+ focus: KeyManager.watchKeys,
+
+ getBounds: function () {
+ return [boundx * xscale, boundy * yscale];
+ },
+ getWidgetSize: function () {
+ return [boundx, boundy];
+ },
+ getScaleFactor: function () {
+ return [xscale, yscale];
+ },
+
+ ui: {
+ holder: $div,
+ selection: $sel
+ }
+ };
+
+ if ($.browser.msie) {
+ $div.bind('selectstart', function () {
+ return false;
+ });
+ }
+
+ $origimg.data('Jcrop', api);
+ return api;
+ };
+ $.fn.Jcrop = function (options, callback) //{{{
+ {
+ var api;
+ // Iterate over each object, attach Jcrop
+ this.each(function () {
+ // If we've already attached to this object
+ if ($(this).data('Jcrop')) {
+ // The API can be requested this way (undocumented)
+ if (options === 'api') return $(this).data('Jcrop');
+ // Otherwise, we just reset the options...
+ else $(this).data('Jcrop').setOptions(options);
+ }
+ // If we haven't been attached, preload and attach
+ else {
+ if (this.tagName == 'IMG')
+ $.Jcrop.Loader(this,function(){
+ $(this).css({display:'block',visibility:'hidden'});
+ api = $.Jcrop(this, options);
+ if ($.isFunction(callback)) callback.call(api);
+ });
+ else {
+ $(this).css({display:'block',visibility:'hidden'});
+ api = $.Jcrop(this, options);
+ if ($.isFunction(callback)) callback.call(api);
+ }
+ }
+ });
+
+ // Return "this" so the object is chainable (jQuery-style)
+ return this;
+ };
+ //}}}
+ // $.Jcrop.Loader - basic image loader {{{
+
+ $.Jcrop.Loader = function(imgobj,success,error){
+ var $img = $(imgobj), img = $img[0];
+
+ function completeCheck(){
+ if (img.complete) {
+ $img.unbind('.jcloader');
+ if ($.isFunction(success)) success.call(img);
+ }
+ else window.setTimeout(completeCheck,50);
+ }
+
+ $img
+ .bind('load.jcloader',completeCheck)
+ .bind('error.jcloader',function(e){
+ $img.unbind('.jcloader');
+ if ($.isFunction(error)) error.call(img);
+ });
+
+ if (img.complete && $.isFunction(success)){
+ $img.unbind('.jcloader');
+ success.call(img);
+ }
+ };
+
+ //}}}
+ // Global Defaults {{{
+ $.Jcrop.defaults = {
+
+ // Basic Settings
+ allowSelect: true,
+ allowMove: true,
+ allowResize: true,
+
+ trackDocument: true,
+
+ // Styling Options
+ baseClass: 'jcrop',
+ addClass: null,
+ bgColor: 'black',
+ bgOpacity: 0.6,
+ bgFade: false,
+ borderOpacity: 0.4,
+ handleOpacity: 0.5,
+ handleSize: 7,
+ handleOffset: 5,
+
+ aspectRatio: 0,
+ keySupport: true,
+ cornerHandles: true,
+ sideHandles: true,
+ drawBorders: true,
+ dragEdges: true,
+ fixedSupport: true,
+ touchSupport: null,
+
+ shade: null,
+
+ boxWidth: 0,
+ boxHeight: 0,
+ boundary: 2,
+ fadeTime: 400,
+ animationDelay: 20,
+ swingSpeed: 3,
+
+ minSelect: [0, 0],
+ maxSize: [0, 0],
+ minSize: [0, 0],
+
+ // Callbacks / Event Handlers
+ onChange: function () {},
+ onSelect: function () {},
+ onDblClick: function () {},
+ onRelease: function () {}
+ };
+
+ // }}}
+}(jQuery));
diff --git a/apps/contacts/js/jquery.Jcrop.min.js b/apps/contacts/js/jquery.Jcrop.min.js
new file mode 100644
index 00000000000..0c05d67c22d
--- /dev/null
+++ b/apps/contacts/js/jquery.Jcrop.min.js
@@ -0,0 +1,255 @@
+/**
+ * jquery.Jcrop.min.js v0.9.9 {{{ (build:20120102)
+ * jQuery Image Cropping Plugin - released under MIT License
+ * Copyright (c) 2008-2012 Tapmodo Interactive LLC
+ * https://github.com/tapmodo/Jcrop
+ */
+
+(function($){$.Jcrop=function(obj,opt){var options=$.extend({},$.Jcrop.defaults),docOffset,lastcurs,ie6mode=false;function px(n){return parseInt(n,10)+'px';}
+function cssClass(cl){return options.baseClass+'-'+cl;}
+function supportsColorFade(){return $.fx.step.hasOwnProperty('backgroundColor');}
+function getPos(obj)
+{var pos=$(obj).offset();return[pos.left,pos.top];}
+function mouseAbs(e)
+{return[(e.pageX-docOffset[0]),(e.pageY-docOffset[1])];}
+function setOptions(opt)
+{if(typeof(opt)!=='object')opt={};options=$.extend(options,opt);$.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e){if(typeof(options[e])!=='function')options[e]=function(){};});}
+function startDragMode(mode,pos)
+{docOffset=getPos($img);Tracker.setCursor(mode==='move'?mode:mode+'-resize');if(mode==='move'){return Tracker.activateHandlers(createMover(pos),doneSelect);}
+var fc=Coords.getFixed();var opp=oppLockCorner(mode);var opc=Coords.getCorner(oppLockCorner(opp));Coords.setPressed(Coords.getCorner(opp));Coords.setCurrent(opc);Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect);}
+function dragmodeHandler(mode,f)
+{return function(pos){if(!options.aspectRatio){switch(mode){case'e':pos[1]=f.y2;break;case'w':pos[1]=f.y2;break;case'n':pos[0]=f.x2;break;case's':pos[0]=f.x2;break;}}else{switch(mode){case'e':pos[1]=f.y+1;break;case'w':pos[1]=f.y+1;break;case'n':pos[0]=f.x+1;break;case's':pos[0]=f.x+1;break;}}
+Coords.setCurrent(pos);Selection.update();};}
+function createMover(pos)
+{var lloc=pos;KeyManager.watchKeys();return function(pos){Coords.moveOffset([pos[0]-lloc[0],pos[1]-lloc[1]]);lloc=pos;Selection.update();};}
+function oppLockCorner(ord)
+{switch(ord){case'n':return'sw';case's':return'nw';case'e':return'nw';case'w':return'ne';case'ne':return'sw';case'nw':return'se';case'se':return'nw';case'sw':return'ne';}}
+function createDragger(ord)
+{return function(e){if(options.disabled){return false;}
+if((ord==='move')&&!options.allowMove){return false;}
+docOffset=getPos($img);btndown=true;startDragMode(ord,mouseAbs(e));e.stopPropagation();e.preventDefault();return false;};}
+function presize($obj,w,h)
+{var nw=$obj.width(),nh=$obj.height();if((nw>w)&&w>0){nw=w;nh=(w/$obj.width())*$obj.height();}
+if((nh>h)&&h>0){nh=h;nw=(h/$obj.height())*$obj.width();}
+xscale=$obj.width()/nw;yscale=$obj.height()/nh;$obj.width(nw).height(nh);}
+function unscale(c)
+{return{x:parseInt(c.x*xscale,10),y:parseInt(c.y*yscale,10),x2:parseInt(c.x2*xscale,10),y2:parseInt(c.y2*yscale,10),w:parseInt(c.w*xscale,10),h:parseInt(c.h*yscale,10)};}
+function doneSelect(pos)
+{var c=Coords.getFixed();if((c.w>options.minSelect[0])&&(c.h>options.minSelect[1])){Selection.enableHandles();Selection.done();}else{Selection.release();}
+Tracker.setCursor(options.allowSelect?'crosshair':'default');}
+function newSelection(e)
+{if(options.disabled){return false;}
+if(!options.allowSelect){return false;}
+btndown=true;docOffset=getPos($img);Selection.disableHandles();Tracker.setCursor('crosshair');var pos=mouseAbs(e);Coords.setPressed(pos);Selection.update();Tracker.activateHandlers(selectDrag,doneSelect);KeyManager.watchKeys();e.stopPropagation();e.preventDefault();return false;}
+function selectDrag(pos)
+{Coords.setCurrent(pos);Selection.update();}
+function newTracker()
+{var trk=$('<div></div>').addClass(cssClass('tracker'));if($.browser.msie){trk.css({opacity:0,backgroundColor:'white'});}
+return trk;}
+if($.browser.msie&&($.browser.version.split('.')[0]==='6')){ie6mode=true;}
+if(typeof(obj)!=='object'){obj=$(obj)[0];}
+if(typeof(opt)!=='object'){opt={};}
+setOptions(opt);var img_css={border:'none',visibility:'visible',margin:0,padding:0,position:'absolute',top:0,left:0};var $origimg=$(obj),img_mode=true;if(obj.tagName=='IMG'){if($origimg[0].width!=0&&$origimg[0].height!=0){$origimg.width($origimg[0].width);$origimg.height($origimg[0].height);}else{var tempImage=new Image();tempImage.src=$origimg[0].src;$origimg.width(tempImage.width);$origimg.height(tempImage.height);}
+var $img=$origimg.clone().removeAttr('id').css(img_css).show();$img.width($origimg.width());$img.height($origimg.height());$origimg.after($img).hide();}else{$img=$origimg.css(img_css).show();img_mode=false;if(options.shade===null){options.shade=true;}}
+presize($img,options.boxWidth,options.boxHeight);var boundx=$img.width(),boundy=$img.height(),$div=$('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({position:'relative',backgroundColor:options.bgColor}).insertAfter($origimg).append($img);if(options.addClass){$div.addClass(options.addClass);}
+var $img2=$('<div />'),$img_holder=$('<div />').width('100%').height('100%').css({zIndex:310,position:'absolute',overflow:'hidden'}),$hdl_holder=$('<div />').width('100%').height('100%').css('zIndex',320),$sel=$('<div />').css({position:'absolute',zIndex:600}).dblclick(function(){var c=Coords.getFixed();options.onDblClick.call(api,c);}).insertBefore($img).append($img_holder,$hdl_holder);if(img_mode){$img2=$('<img />').attr('src',$img.attr('src')).css(img_css).width(boundx).height(boundy),$img_holder.append($img2);}
+if(ie6mode){$sel.css({overflowY:'hidden'});}
+var bound=options.boundary;var $trk=newTracker().width(boundx+(bound*2)).height(boundy+(bound*2)).css({position:'absolute',top:px(-bound),left:px(-bound),zIndex:290}).mousedown(newSelection);var bgcolor=options.bgColor,bgopacity=options.bgOpacity,xlimit,ylimit,xmin,ymin,xscale,yscale,enabled=true,btndown,animating,shift_down;docOffset=getPos($img);var Touch=(function(){function hasTouchSupport(){var support={},events=['touchstart','touchmove','touchend'],el=document.createElement('div'),i;try{for(i=0;i<events.length;i++){var eventName=events[i];eventName='on'+eventName;var isSupported=(eventName in el);if(!isSupported){el.setAttribute(eventName,'return;');isSupported=typeof el[eventName]=='function';}
+support[events[i]]=isSupported;}
+return support.touchstart&&support.touchend&&support.touchmove;}
+catch(err){return false;}}
+function detectSupport(){if((options.touchSupport===true)||(options.touchSupport===false))return options.touchSupport;else return hasTouchSupport();}
+return{createDragger:function(ord){return function(e){e.pageX=e.originalEvent.changedTouches[0].pageX;e.pageY=e.originalEvent.changedTouches[0].pageY;if(options.disabled){return false;}
+if((ord==='move')&&!options.allowMove){return false;}
+btndown=true;startDragMode(ord,mouseAbs(e));e.stopPropagation();e.preventDefault();return false;};},newSelection:function(e){e.pageX=e.originalEvent.changedTouches[0].pageX;e.pageY=e.originalEvent.changedTouches[0].pageY;return newSelection(e);},isSupported:hasTouchSupport,support:detectSupport()};}());var Coords=(function(){var x1=0,y1=0,x2=0,y2=0,ox,oy;function setPressed(pos)
+{pos=rebound(pos);x2=x1=pos[0];y2=y1=pos[1];}
+function setCurrent(pos)
+{pos=rebound(pos);ox=pos[0]-x2;oy=pos[1]-y2;x2=pos[0];y2=pos[1];}
+function getOffset()
+{return[ox,oy];}
+function moveOffset(offset)
+{var ox=offset[0],oy=offset[1];if(0>x1+ox){ox-=ox+x1;}
+if(0>y1+oy){oy-=oy+y1;}
+if(boundy<y2+oy){oy+=boundy-(y2+oy);}
+if(boundx<x2+ox){ox+=boundx-(x2+ox);}
+x1+=ox;x2+=ox;y1+=oy;y2+=oy;}
+function getCorner(ord)
+{var c=getFixed();switch(ord){case'ne':return[c.x2,c.y];case'nw':return[c.x,c.y];case'se':return[c.x2,c.y2];case'sw':return[c.x,c.y2];}}
+function getFixed()
+{if(!options.aspectRatio){return getRect();}
+var aspect=options.aspectRatio,min_x=options.minSize[0]/xscale,max_x=options.maxSize[0]/xscale,max_y=options.maxSize[1]/yscale,rw=x2-x1,rh=y2-y1,rwa=Math.abs(rw),rha=Math.abs(rh),real_ratio=rwa/rha,xx,yy,w,h;if(max_x===0){max_x=boundx*10;}
+if(max_y===0){max_y=boundy*10;}
+if(real_ratio<aspect){yy=y2;w=rha*aspect;xx=rw<0?x1-w:w+x1;if(xx<0){xx=0;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;}else if(xx>boundx){xx=boundx;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;}}else{xx=x2;h=rwa/aspect;yy=rh<0?y1-h:y1+h;if(yy<0){yy=0;w=Math.abs((yy-y1)*aspect);xx=rw<0?x1-w:w+x1;}else if(yy>boundy){yy=boundy;w=Math.abs(yy-y1)*aspect;xx=rw<0?x1-w:w+x1;}}
+if(xx>x1){if(xx-x1<min_x){xx=x1+min_x;}else if(xx-x1>max_x){xx=x1+max_x;}
+if(yy>y1){yy=y1+(xx-x1)/aspect;}else{yy=y1-(xx-x1)/aspect;}}else if(xx<x1){if(x1-xx<min_x){xx=x1-min_x;}else if(x1-xx>max_x){xx=x1-max_x;}
+if(yy>y1){yy=y1+(x1-xx)/aspect;}else{yy=y1-(x1-xx)/aspect;}}
+if(xx<0){x1-=xx;xx=0;}else if(xx>boundx){x1-=xx-boundx;xx=boundx;}
+if(yy<0){y1-=yy;yy=0;}else if(yy>boundy){y1-=yy-boundy;yy=boundy;}
+return makeObj(flipCoords(x1,y1,xx,yy));}
+function rebound(p)
+{if(p[0]<0){p[0]=0;}
+if(p[1]<0){p[1]=0;}
+if(p[0]>boundx){p[0]=boundx;}
+if(p[1]>boundy){p[1]=boundy;}
+return[p[0],p[1]];}
+function flipCoords(x1,y1,x2,y2)
+{var xa=x1,xb=x2,ya=y1,yb=y2;if(x2<x1){xa=x2;xb=x1;}
+if(y2<y1){ya=y2;yb=y1;}
+return[Math.round(xa),Math.round(ya),Math.round(xb),Math.round(yb)];}
+function getRect()
+{var xsize=x2-x1,ysize=y2-y1,delta;if(xlimit&&(Math.abs(xsize)>xlimit)){x2=(xsize>0)?(x1+xlimit):(x1-xlimit);}
+if(ylimit&&(Math.abs(ysize)>ylimit)){y2=(ysize>0)?(y1+ylimit):(y1-ylimit);}
+if(ymin/yscale&&(Math.abs(ysize)<ymin/yscale)){y2=(ysize>0)?(y1+ymin/yscale):(y1-ymin/yscale);}
+if(xmin/xscale&&(Math.abs(xsize)<xmin/xscale)){x2=(xsize>0)?(x1+xmin/xscale):(x1-xmin/xscale);}
+if(x1<0){x2-=x1;x1-=x1;}
+if(y1<0){y2-=y1;y1-=y1;}
+if(x2<0){x1-=x2;x2-=x2;}
+if(y2<0){y1-=y2;y2-=y2;}
+if(x2>boundx){delta=x2-boundx;x1-=delta;x2-=delta;}
+if(y2>boundy){delta=y2-boundy;y1-=delta;y2-=delta;}
+if(x1>boundx){delta=x1-boundy;y2-=delta;y1-=delta;}
+if(y1>boundy){delta=y1-boundy;y2-=delta;y1-=delta;}
+return makeObj(flipCoords(x1,y1,x2,y2));}
+function makeObj(a)
+{return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]};}
+return{flipCoords:flipCoords,setPressed:setPressed,setCurrent:setCurrent,getOffset:getOffset,moveOffset:moveOffset,getCorner:getCorner,getFixed:getFixed};}());var Shade=(function(){var enabled=false,holder=$('<div />').css({position:'absolute',zIndex:240,opacity:0}),shades={top:createShade(),left:createShade().height(boundy),right:createShade().height(boundy),bottom:createShade()};function resizeShades(w,h){shades.left.css({height:px(h)});shades.right.css({height:px(h)});}
+function updateAuto()
+{return updateShade(Coords.getFixed());}
+function updateShade(c)
+{shades.top.css({left:px(c.x),width:px(c.w),height:px(c.y)});shades.bottom.css({top:px(c.y2),left:px(c.x),width:px(c.w),height:px(boundy-c.y2)});shades.right.css({left:px(c.x2),width:px(boundx-c.x2)});shades.left.css({width:px(c.x)});}
+function createShade(){return $('<div />').css({position:'absolute',backgroundColor:options.shadeColor||options.bgColor}).appendTo(holder);}
+function enableShade(){if(!enabled){enabled=true;holder.insertBefore($img);updateAuto();Selection.setBgOpacity(1,0,1);$img2.hide();setBgColor(options.shadeColor||options.bgColor,1);if(Selection.isAwake())
+{setOpacity(options.bgOpacity,1);}
+else setOpacity(1,1);}}
+function setBgColor(color,now){colorChangeMacro(getShades(),color,now);}
+function disableShade(){if(enabled){holder.remove();$img2.show();enabled=false;if(Selection.isAwake()){Selection.setBgOpacity(options.bgOpacity,1,1);}else{Selection.setBgOpacity(1,1,1);Selection.disableHandles();}
+colorChangeMacro($div,0,1);}}
+function setOpacity(opacity,now){if(enabled){if(options.bgFade&&!now){holder.animate({opacity:1-opacity},{queue:false,duration:options.fadeTime});}
+else holder.css({opacity:1-opacity});}}
+function refreshAll(){options.shade?enableShade():disableShade();if(Selection.isAwake())setOpacity(options.bgOpacity);}
+function getShades(){return holder.children();}
+return{update:updateAuto,updateRaw:updateShade,getShades:getShades,setBgColor:setBgColor,enable:enableShade,disable:disableShade,resize:resizeShades,refresh:refreshAll,opacity:setOpacity};}());var Selection=(function(){var awake,hdep=370;var borders={};var handle={};var seehandles=false;var hhs=options.handleOffset;function insertBorder(type)
+{var jq=$('<div />').css({position:'absolute',opacity:options.borderOpacity}).addClass(cssClass(type));$img_holder.append(jq);return jq;}
+function dragDiv(ord,zi)
+{var jq=$('<div />').mousedown(createDragger(ord)).css({cursor:ord+'-resize',position:'absolute',zIndex:zi}).addClass('ord-'+ord);if(Touch.support){jq.bind('touchstart.jcrop',Touch.createDragger(ord));}
+$hdl_holder.append(jq);return jq;}
+function insertHandle(ord)
+{var hs=options.handleSize;return dragDiv(ord,hdep++).css({top:px(-hhs+1),left:px(-hhs+1),opacity:options.handleOpacity}).width(hs).height(hs).addClass(cssClass('handle'));}
+function insertDragbar(ord)
+{var s=options.handleSize,h=s,w=s,t=hhs,l=hhs;switch(ord){case'n':case's':w='100%';break;case'e':case'w':h='100%';break;}
+return dragDiv(ord,hdep++).width(w).height(h).css({top:px(-t+1),left:px(-l+1)});}
+function createHandles(li)
+{var i;for(i=0;i<li.length;i++){handle[li[i]]=insertHandle(li[i]);}}
+function moveHandles(c)
+{var midvert=Math.round((c.h/2)-hhs),midhoriz=Math.round((c.w/2)-hhs),north=-hhs+1,west=-hhs+1,east=c.w-hhs,south=c.h-hhs,x,y;if(handle.e){handle.e.css({top:px(midvert),left:px(east)});handle.w.css({top:px(midvert)});handle.s.css({top:px(south),left:px(midhoriz)});handle.n.css({left:px(midhoriz)});}
+if(handle.ne){handle.ne.css({left:px(east)});handle.se.css({top:px(south),left:px(east)});handle.sw.css({top:px(south)});}
+if(handle.b){handle.b.css({top:px(south)});handle.r.css({left:px(east)});}}
+function moveto(x,y)
+{if(!options.shade){$img2.css({top:px(-y),left:px(-x)});}
+$sel.css({top:px(y),left:px(x)});}
+function resize(w,h)
+{$sel.width(w).height(h);}
+function refresh()
+{var c=Coords.getFixed();Coords.setPressed([c.x,c.y]);Coords.setCurrent([c.x2,c.y2]);updateVisible();}
+function updateVisible(select)
+{if(awake){return update(select);}}
+function update(select)
+{var c=Coords.getFixed();resize(c.w,c.h);moveto(c.x,c.y);if(options.shade)Shade.updateRaw(c);if(seehandles){moveHandles(c);}
+if(!awake){show();}
+if(select){options.onSelect.call(api,unscale(c));}else{options.onChange.call(api,unscale(c));}}
+function setBgOpacity(opacity,force,now)
+{if(!awake&&!force)return;if(options.bgFade&&!now){$img.animate({opacity:opacity},{queue:false,duration:options.fadeTime});}else{$img.css('opacity',opacity);}}
+function show()
+{$sel.show();if(options.shade)Shade.opacity(bgopacity);else setBgOpacity(bgopacity,true);awake=true;}
+function release()
+{disableHandles();$sel.hide();if(options.shade)Shade.opacity(1);else setBgOpacity(1);awake=false;options.onRelease.call(api);}
+function showHandles()
+{if(seehandles){moveHandles(Coords.getFixed());$hdl_holder.show();}}
+function enableHandles()
+{seehandles=true;if(options.allowResize){moveHandles(Coords.getFixed());$hdl_holder.show();return true;}}
+function disableHandles()
+{seehandles=false;$hdl_holder.hide();}
+function animMode(v)
+{if(animating===v){disableHandles();}else{enableHandles();}}
+function done()
+{animMode(false);refresh();}
+if(options.drawBorders){borders={top:insertBorder('hline'),bottom:insertBorder('hline bottom'),left:insertBorder('vline'),right:insertBorder('vline right')};}
+if(options.dragEdges){handle.t=insertDragbar('n');handle.b=insertDragbar('s');handle.r=insertDragbar('e');handle.l=insertDragbar('w');}
+if(options.sideHandles){createHandles(['n','s','e','w']);}
+if(options.cornerHandles){createHandles(['sw','nw','ne','se']);}
+$(document).bind('touchstart.jcrop-ios',function(e){if($(e.currentTarget).hasClass('jcrop-tracker'))e.stopPropagation();});var $track=newTracker().mousedown(createDragger('move')).css({cursor:'move',position:'absolute',zIndex:360});if(Touch.support){$track.bind('touchstart.jcrop',Touch.createDragger('move'));}
+$img_holder.append($track);disableHandles();return{updateVisible:updateVisible,update:update,release:release,refresh:refresh,isAwake:function(){return awake;},setCursor:function(cursor){$track.css('cursor',cursor);},enableHandles:enableHandles,enableOnly:function(){seehandles=true;},showHandles:showHandles,disableHandles:disableHandles,animMode:animMode,setBgOpacity:setBgOpacity,done:done};}());var Tracker=(function(){var onMove=function(){},onDone=function(){},trackDoc=options.trackDocument;function toFront()
+{$trk.css({zIndex:450});if(Touch.support){$(document).bind('touchmove.jcrop',trackTouchMove).bind('touchend.jcrop',trackTouchEnd);}
+if(trackDoc){$(document).bind('mousemove.jcrop',trackMove).bind('mouseup.jcrop',trackUp);}}
+function toBack()
+{$trk.css({zIndex:290});$(document).unbind('.jcrop');}
+function trackMove(e)
+{onMove(mouseAbs(e));return false;}
+function trackUp(e)
+{e.preventDefault();e.stopPropagation();if(btndown){btndown=false;onDone(mouseAbs(e));if(Selection.isAwake()){options.onSelect.call(api,unscale(Coords.getFixed()));}
+toBack();onMove=function(){};onDone=function(){};}
+return false;}
+function activateHandlers(move,done)
+{btndown=true;onMove=move;onDone=done;toFront();return false;}
+function trackTouchMove(e)
+{e.pageX=e.originalEvent.changedTouches[0].pageX;e.pageY=e.originalEvent.changedTouches[0].pageY;return trackMove(e);}
+function trackTouchEnd(e)
+{e.pageX=e.originalEvent.changedTouches[0].pageX;e.pageY=e.originalEvent.changedTouches[0].pageY;return trackUp(e);}
+function setCursor(t)
+{$trk.css('cursor',t);}
+if(!trackDoc){$trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);}
+$img.before($trk);return{activateHandlers:activateHandlers,setCursor:setCursor};}());var KeyManager=(function(){var $keymgr=$('<input type="radio" />').css({position:'fixed',left:'-120px',width:'12px'}),$keywrap=$('<div />').css({position:'absolute',overflow:'hidden'}).append($keymgr);function watchKeys()
+{if(options.keySupport){$keymgr.show();$keymgr.focus();}}
+function onBlur(e)
+{$keymgr.hide();}
+function doNudge(e,x,y)
+{if(options.allowMove){Coords.moveOffset([x,y]);Selection.updateVisible(true);}
+e.preventDefault();e.stopPropagation();}
+function parseKey(e)
+{if(e.ctrlKey||e.metaKey){return true;}
+shift_down=e.shiftKey?true:false;var nudge=shift_down?10:1;switch(e.keyCode){case 37:doNudge(e,-nudge,0);break;case 39:doNudge(e,nudge,0);break;case 38:doNudge(e,0,-nudge);break;case 40:doNudge(e,0,nudge);break;case 27:if(options.allowSelect)Selection.release();break;case 9:return true;}
+return false;}
+if(options.keySupport){$keymgr.keydown(parseKey).blur(onBlur);if(ie6mode||!options.fixedSupport){$keymgr.css({position:'absolute',left:'-20px'});$keywrap.append($keymgr).insertBefore($img);}else{$keymgr.insertBefore($img);}}
+return{watchKeys:watchKeys};}());function setClass(cname)
+{$div.removeClass().addClass(cssClass('holder')).addClass(cname);}
+function animateTo(a,callback)
+{var x1=parseInt(a[0],10)/xscale,y1=parseInt(a[1],10)/yscale,x2=parseInt(a[2],10)/xscale,y2=parseInt(a[3],10)/yscale;if(animating){return;}
+var animto=Coords.flipCoords(x1,y1,x2,y2),c=Coords.getFixed(),initcr=[c.x,c.y,c.x2,c.y2],animat=initcr,interv=options.animationDelay,ix1=animto[0]-initcr[0],iy1=animto[1]-initcr[1],ix2=animto[2]-initcr[2],iy2=animto[3]-initcr[3],pcent=0,velocity=options.swingSpeed;x=animat[0];y=animat[1];x2=animat[2];y2=animat[3];Selection.animMode(true);var anim_timer;function queueAnimator(){window.setTimeout(animator,interv);}
+var animator=(function(){return function(){pcent+=(100-pcent)/velocity;animat[0]=x+((pcent/100)*ix1);animat[1]=y+((pcent/100)*iy1);animat[2]=x2+((pcent/100)*ix2);animat[3]=y2+((pcent/100)*iy2);if(pcent>=99.8){pcent=100;}
+if(pcent<100){setSelectRaw(animat);queueAnimator();}else{Selection.done();if(typeof(callback)==='function'){callback.call(api);}}};}());queueAnimator();}
+function setSelect(rect)
+{setSelectRaw([parseInt(rect[0],10)/xscale,parseInt(rect[1],10)/yscale,parseInt(rect[2],10)/xscale,parseInt(rect[3],10)/yscale]);options.onSelect.call(api,unscale(Coords.getFixed()));Selection.enableHandles();}
+function setSelectRaw(l)
+{Coords.setPressed([l[0],l[1]]);Coords.setCurrent([l[2],l[3]]);Selection.update();}
+function tellSelect()
+{return unscale(Coords.getFixed());}
+function tellScaled()
+{return Coords.getFixed();}
+function setOptionsNew(opt)
+{setOptions(opt);interfaceUpdate();}
+function disableCrop()
+{options.disabled=true;Selection.disableHandles();Selection.setCursor('default');Tracker.setCursor('default');}
+function enableCrop()
+{options.disabled=false;interfaceUpdate();}
+function cancelCrop()
+{Selection.done();Tracker.activateHandlers(null,null);}
+function destroy()
+{$div.remove();$origimg.show();$(obj).removeData('Jcrop');}
+function setImage(src,callback)
+{Selection.release();disableCrop();var img=new Image();img.onload=function(){var iw=img.width;var ih=img.height;var bw=options.boxWidth;var bh=options.boxHeight;$img.width(iw).height(ih);$img.attr('src',src);$img2.attr('src',src);presize($img,bw,bh);boundx=$img.width();boundy=$img.height();$img2.width(boundx).height(boundy);$trk.width(boundx+(bound*2)).height(boundy+(bound*2));$div.width(boundx).height(boundy);Shade.resize(boundx,boundy);enableCrop();if(typeof(callback)==='function'){callback.call(api);}};img.src=src;}
+function colorChangeMacro($obj,color,now){var mycolor=color||options.bgColor;if(options.bgFade&&supportsColorFade()&&options.fadeTime&&!now){$obj.animate({backgroundColor:mycolor},{queue:false,duration:options.fadeTime});}else{$obj.css('backgroundColor',mycolor);}}
+function interfaceUpdate(alt)
+{if(options.allowResize){if(alt){Selection.enableOnly();}else{Selection.enableHandles();}}else{Selection.disableHandles();}
+Tracker.setCursor(options.allowSelect?'crosshair':'default');Selection.setCursor(options.allowMove?'move':'default');if(options.hasOwnProperty('trueSize')){xscale=options.trueSize[0]/boundx;yscale=options.trueSize[1]/boundy;}
+if(options.hasOwnProperty('setSelect')){setSelect(options.setSelect);Selection.done();delete(options.setSelect);}
+Shade.refresh();if(options.bgColor!=bgcolor){colorChangeMacro(options.shade?Shade.getShades():$div,options.shade?(options.shadeColor||options.bgColor):options.bgColor);bgcolor=options.bgColor;}
+if(bgopacity!=options.bgOpacity){bgopacity=options.bgOpacity;if(options.shade)Shade.refresh();else Selection.setBgOpacity(bgopacity);}
+xlimit=options.maxSize[0]||0;ylimit=options.maxSize[1]||0;xmin=options.minSize[0]||0;ymin=options.minSize[1]||0;if(options.hasOwnProperty('outerImage')){$img.attr('src',options.outerImage);delete(options.outerImage);}
+Selection.refresh();}
+if(Touch.support)$trk.bind('touchstart.jcrop',Touch.newSelection);$hdl_holder.hide();interfaceUpdate(true);var api={setImage:setImage,animateTo:animateTo,setSelect:setSelect,setOptions:setOptionsNew,tellSelect:tellSelect,tellScaled:tellScaled,setClass:setClass,disable:disableCrop,enable:enableCrop,cancel:cancelCrop,release:Selection.release,destroy:destroy,focus:KeyManager.watchKeys,getBounds:function(){return[boundx*xscale,boundy*yscale];},getWidgetSize:function(){return[boundx,boundy];},getScaleFactor:function(){return[xscale,yscale];},ui:{holder:$div,selection:$sel}};if($.browser.msie){$div.bind('selectstart',function(){return false;});}
+$origimg.data('Jcrop',api);return api;};$.fn.Jcrop=function(options,callback)
+{var api;this.each(function(){if($(this).data('Jcrop')){if(options==='api')return $(this).data('Jcrop');else $(this).data('Jcrop').setOptions(options);}
+else{if(this.tagName=='IMG')
+$.Jcrop.Loader(this,function(){$(this).css({display:'block',visibility:'hidden'});api=$.Jcrop(this,options);if($.isFunction(callback))callback.call(api);});else{$(this).css({display:'block',visibility:'hidden'});api=$.Jcrop(this,options);if($.isFunction(callback))callback.call(api);}}});return this;};$.Jcrop.Loader=function(imgobj,success,error){var $img=$(imgobj),img=$img[0];function completeCheck(){if(img.complete){$img.unbind('.jcloader');if($.isFunction(success))success.call(img);}
+else window.setTimeout(completeCheck,50);}
+$img.bind('load.jcloader',completeCheck).bind('error.jcloader',function(e){$img.unbind('.jcloader');if($.isFunction(error))error.call(img);});if(img.complete&&$.isFunction(success)){$img.unbind('.jcloader');success.call(img);}};$.Jcrop.defaults={allowSelect:true,allowMove:true,allowResize:true,trackDocument:true,baseClass:'jcrop',addClass:null,bgColor:'black',bgOpacity:0.6,bgFade:false,borderOpacity:0.4,handleOpacity:0.5,handleSize:7,handleOffset:5,aspectRatio:0,keySupport:true,cornerHandles:true,sideHandles:true,drawBorders:true,dragEdges:true,fixedSupport:true,touchSupport:null,shade:null,boxWidth:0,boxHeight:0,boundary:2,fadeTime:400,animationDelay:20,swingSpeed:3,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){},onDblClick:function(){},onRelease:function(){}};}(jQuery)); \ No newline at end of file
diff --git a/apps/contacts/js/jquery.jec-1.3.3.js b/apps/contacts/js/jquery.jec-1.3.3.js
new file mode 100644
index 00000000000..a0367dac2ae
--- /dev/null
+++ b/apps/contacts/js/jquery.jec-1.3.3.js
@@ -0,0 +1,863 @@
+/**
+ * jQuery jEC (jQuery Editable Combobox) 1.3.3
+ * http://code.google.com/p/jquery-jec
+ *
+ * Copyright (c) 2008-2009 Lukasz Rajchel (lukasz@rajchel.pl | http://rajchel.pl)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * Documentation : http://code.google.com/p/jquery-jec/wiki/Documentation
+ * Changelog : http://code.google.com/p/jquery-jec/wiki/Changelog
+ *
+ * Contributors : Lukasz Rajchel, Artem Orlov
+ */
+
+/*jslint maxerr: 50, indent: 4, maxlen: 120*/
+/*global Array, Math, String, clearInterval, document, jQuery, setInterval*/
+/*members ':', Handle, Remove, Set, acceptedKeys, addClass, all, append, appendTo, array, attr, before, bind,
+blinkingCursor, blinkingCursorInterval, blur, bool, browser, ceil, change, charCode, classes, clearCursor, click, css,
+cursorState, data, destroy, disable, each, editable, enable, eq, expr, extend, filter, find, floor, fn, focus,
+focusOnNewOption, fromCharCode, get, getId, handleCursor, ignoredKeys, ignoreOptGroups, inArray, init, initJS, integer,
+isArray, isPlainObject, jEC, jECTimer, jec, jecKill, jecOff, jecOn, jecPref, jecValue, keyCode, keyDown, keyPress,
+keyRange, keyUp, keys, length, max, maxLength, min, msie, object, openedState, optionClasses, optionStyles, parent,
+position, pref, prop, push, random, remove, removeAttr, removeClass, removeData, removeProp, safari, setEditableOption,
+styles, substring, text, trigger, triggerChangeEvent, unbind, uneditable, useExistingOptions, val, value,
+valueIsEditable, which*/
+(function ($) {
+ 'use strict';
+
+ $.jEC = (function () {
+ var pluginClass = 'jecEditableOption', cursorClass = 'hasCursor', options = {}, values = {}, lastKeyCode,
+ defaults, Validators, EventHandlers, Combobox, activeCombobox;
+
+ if ($.fn.prop === undefined) {
+ $.fn.extend({
+ 'prop': function (key, valueSet) {
+ if (valueSet) {
+ $(this).attr(key, key);
+ } else {
+ $(this).removeAttr(key);
+ }
+ },
+ 'removeProp': function (key) {
+ $(this).removeAttr(key);
+ }
+ });
+ }
+
+ defaults = {
+ position: 0,
+ ignoreOptGroups: false,
+ maxLength: 255,
+ classes: [],
+ styles: {},
+ optionClasses: [],
+ optionStyles: {},
+ triggerChangeEvent: false,
+ focusOnNewOption: false,
+ useExistingOptions: false,
+ blinkingCursor: false,
+ blinkingCursorInterval: 1000,
+ ignoredKeys: [],
+ acceptedKeys: [[32, 126], [191, 382]]
+ };
+
+ Validators = (function () {
+ return {
+ integer: function (value) {
+ return typeof value === 'number' && Math.ceil(value) === Math.floor(value);
+ },
+
+ keyRange: function (value) {
+ var min, max;
+ if ($.isPlainObject(value)) {
+ min = value.min;
+ max = value.max;
+ } else if ($.isArray(value) && value.length === 2) {
+ min = value[0];
+ max = value[1];
+ }
+ return Validators.integer(min) && Validators.integer(max) && min <= max;
+ }
+ };
+ }());
+
+ EventHandlers = (function () {
+ var getKeyCode;
+
+ getKeyCode = function (event) {
+ var charCode = event.charCode;
+ if (charCode !== undefined && charCode !== 0) {
+ return charCode;
+ } else {
+ return event.keyCode;
+ }
+ };
+
+ return {
+ // focus event handler
+ // enables blinking cursor
+ focus: function () {
+ var opt = options[Combobox.getId($(this))];
+ if (opt.blinkingCursor && $.jECTimer === undefined) {
+ activeCombobox = $(this);
+ $.jECTimer = setInterval($.jEC.handleCursor, opt.blinkingCursorInterval);
+ }
+ },
+
+ // blur event handler
+ // disables blinking cursor
+ blur: function () {
+ if ($.jECTimer !== undefined) {
+ clearInterval($.jECTimer);
+ $.jECTimer = undefined;
+ activeCombobox = undefined;
+ Combobox.clearCursor($(this));
+ }
+ Combobox.openedState($(this), false);
+ },
+
+ // keydown event handler
+ // handles keys pressed on select (backspace and delete must be handled
+ // in keydown event in order to work in IE)
+ keyDown: function (event) {
+ var keyCode = getKeyCode(event), option, value;
+
+ lastKeyCode = keyCode;
+
+ switch (keyCode) {
+ case 8: // backspace
+ case 46: // delete
+ option = $(this).find('option.' + pluginClass);
+ if (option.val().length >= 1) {
+ value = option.text().substring(0, option.text().length - 1);
+ option.val(value).text(value).prop('selected', true);
+ }
+ return (keyCode !== 8);
+ default:
+ break;
+ }
+ },
+
+ // keypress event handler
+ // handles the rest of the keys (keypress event gives more informations
+ // about pressed keys)
+ keyPress: function (event) {
+ var keyCode = getKeyCode(event), opt = options[Combobox.getId($(this))],
+ option, value, specialKeys, exit = false, text;
+
+ Combobox.clearCursor($(this));
+ if (keyCode !== 9 && keyCode !== 13 && keyCode !== 27) {
+ // special keys codes
+ specialKeys = [37, 38, 39, 40, 46];
+ // handle special keys
+ $.each(specialKeys, function (i, val) {
+ if (keyCode === val && keyCode === lastKeyCode) {
+ exit = true;
+ }
+ });
+
+ // don't handle ignored keys
+ if (!exit && $.inArray(keyCode, opt.ignoredKeys) === -1) {
+ // remove selection from all options
+ $(this).find('option:selected').removeProp('selected');
+
+ if ($.inArray(keyCode, opt.acceptedKeys) !== -1) {
+ option = $(this).find('option.' + pluginClass);
+ text = option.text();
+
+ if (text.length < opt.maxLength) {
+ value = text + String.fromCharCode(getKeyCode(event));
+ option.val(value).text(value);
+ }
+
+ option.prop('selected', true);
+ }
+ }
+
+ return false;
+ }
+ },
+
+ keyUp: function () {
+ var opt = options[Combobox.getId($(this))];
+ if (opt.triggerChangeEvent) {
+ $(this).trigger('change');
+ }
+ },
+
+ // change event handler
+ // handles editable option changing based on a pre-existing values
+ change: function () {
+ var opt = options[Combobox.getId($(this))];
+ if (opt.useExistingOptions) {
+ Combobox.setEditableOption($(this));
+ }
+ },
+
+ click: function () {
+ if (!$.browser.safari) {
+ Combobox.openedState($(this), !Combobox.openedState($(this)));
+ }
+ }
+ };
+ }());
+
+ // Combobox
+ Combobox = (function () {
+ var Parameters, EditableOption, generateId, setup;
+
+ // validates and set combobox parameters
+ Parameters = (function () {
+ var Set, Remove, Handle;
+
+ Set = (function () {
+ var parseKeys, Handles;
+
+ parseKeys = function (value) {
+ var keys = [];
+ if ($.isArray(value)) {
+ $.each(value, function (i, val) {
+ var j, min, max;
+ if (Validators.keyRange(val)) {
+ if ($.isArray(val)) {
+ min = val[0];
+ max = val[1];
+ } else {
+ min = val.min;
+ max = val.max;
+ }
+ for (j = min; j <= max; j += 1) {
+ keys.push(j);
+ }
+ } else if (typeof val === 'number' && Validators.integer(val)) {
+ keys.push(val);
+ }
+ });
+ }
+ return keys;
+ };
+
+ Handles = (function () {
+ return {
+ integer: function (elem, name, value) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined && Validators.integer(value)) {
+ opt[name] = value;
+ return true;
+ }
+ return false;
+ },
+ bool: function (elem, name, value) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined && typeof value === 'boolean') {
+ opt[name] = value;
+ return true;
+ }
+ return false;
+ },
+ array: function (elem, name, value) {
+ if (typeof value === 'string') {
+ value = [value];
+ }
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined && $.isArray(value)) {
+ opt[name] = value;
+ return true;
+ }
+ return false;
+ },
+ object: function (elem, name, value) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined && value !== null && $.isPlainObject(value)) {
+ opt[name] = value;
+ }
+ },
+ keys: function (elem, name, value) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined && $.isArray(value)) {
+ opt[name] = parseKeys(value);
+ }
+ }
+ };
+ }());
+
+ return {
+ position: function (elem, value) {
+ if (Handles.integer(elem, 'position', value)) {
+ var id = Combobox.getId(elem), opt = options[id], optionsCount;
+ optionsCount =
+ elem.find('option:not(.' + pluginClass + ')').length;
+ if (value > optionsCount) {
+ opt.position = optionsCount;
+ }
+ }
+ },
+
+ ignoreOptGroups: function (elem, value) {
+ Handles.bool(elem, 'ignoreOptGroups', value);
+ },
+
+ maxLength: function (elem, value) {
+ if (Handles.integer(elem, 'maxLength', value)) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (value < 0 || value > 255) {
+ opt.maxLength = 255;
+ }
+ }
+ },
+
+ classes: function (elem, value) {
+ Handles.array(elem, 'classes', value);
+ },
+
+ optionClasses: function (elem, value) {
+ Handles.array(elem, 'optionClasses', value);
+ },
+
+ styles: function (elem, value) {
+ Handles.object(elem, 'styles', value);
+ },
+
+ optionStyles: function (elem, value) {
+ Handles.object(elem, 'optionStyles', value);
+ },
+
+ triggerChangeEvent: function (elem, value) {
+ Handles.bool(elem, 'triggerChangeEvent', value);
+ },
+
+ focusOnNewOption: function (elem, value) {
+ Handles.bool(elem, 'focusOnNewOption', value);
+ },
+
+ useExistingOptions: function (elem, value) {
+ Handles.bool(elem, 'useExistingOptions', value);
+ },
+
+ blinkingCursor: function (elem, value) {
+ Handles.bool(elem, 'blinkingCursor', value);
+ },
+
+ blinkingCursorInterval: function (elem, value) {
+ Handles.integer(elem, 'blinkingCursorInterval', value);
+ },
+
+ ignoredKeys: function (elem, value) {
+ Handles.keys(elem, 'ignoredKeys', value);
+ },
+
+ acceptedKeys: function (elem, value) {
+ Handles.keys(elem, 'acceptedKeys', value);
+ }
+ };
+ }());
+
+ Remove = (function () {
+ var removeClasses, removeStyles;
+
+ removeClasses = function (elem, classes) {
+ $.each(classes, function (i, val) {
+ elem.removeClass(val);
+ });
+ };
+
+ removeStyles = function (elem, styles) {
+ $.each(styles, function (key) {
+ elem.css(key, '');
+ });
+ };
+
+ return {
+ classes: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ removeClasses(elem, opt.classes);
+ }
+ },
+
+ optionClasses: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ removeClasses(elem.find('option.' + pluginClass),
+ opt.optionClasses);
+ }
+ },
+
+ styles: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ removeStyles(elem, opt.styles);
+ }
+ },
+
+ optionStyles: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ removeStyles(elem.find('option.' + pluginClass),
+ opt.optionStyles);
+ }
+ },
+
+ all: function (elem) {
+ Remove.classes(elem);
+ Remove.optionClasses(elem);
+ Remove.styles(elem);
+ Remove.optionStyles(elem);
+ }
+ };
+ }());
+
+ Handle = (function () {
+ var setClasses, setStyles;
+
+ setClasses = function (elem, classes) {
+ $.each(classes, function (i, val) {
+ elem.addClass(String(val));
+ });
+ };
+
+ setStyles = function (elem, styles) {
+ $.each(styles, function (key, val) {
+ elem.css(key, val);
+ });
+ };
+
+ return {
+ position: function (elem) {
+ var opt = options[Combobox.getId(elem)], option, uneditableOptions, container;
+ option = elem.find('option.' + pluginClass);
+
+ uneditableOptions = elem.find('option:not(.' + pluginClass + ')');
+ if (opt.position < uneditableOptions.length) {
+ container = uneditableOptions.eq(opt.position);
+
+ if (!opt.ignoreOptGroups && container.parent('optgroup').length > 0) {
+ uneditableOptions.eq(opt.position).parent().before(option);
+ } else {
+ uneditableOptions.eq(opt.position).before(option);
+ }
+ } else {
+ elem.append(option);
+ }
+ },
+
+ classes: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ setClasses(elem, opt.classes);
+ }
+ },
+
+ optionClasses: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ setClasses(elem.find('option.' + pluginClass), opt.optionClasses);
+ }
+ },
+
+ styles: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ setStyles(elem, opt.styles);
+ }
+ },
+
+ optionStyles: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined) {
+ setStyles(elem.find('option.' + pluginClass), opt.optionStyles);
+ }
+ },
+
+ focusOnNewOption: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined && opt.focusOnNewOption) {
+ elem.find(':not(option.' + pluginClass + ')').removeProp('selected');
+ elem.find('option.' + pluginClass).prop('selected', true);
+ }
+ },
+
+ useExistingOptions: function (elem) {
+ var id = Combobox.getId(elem), opt = options[id];
+ if (opt !== undefined && opt.useExistingOptions) {
+ Combobox.setEditableOption(elem);
+ }
+ },
+
+ all: function (elem) {
+ Handle.position(elem);
+ Handle.classes(elem);
+ Handle.optionClasses(elem);
+ Handle.styles(elem);
+ Handle.optionStyles(elem);
+ Handle.focusOnNewOption(elem);
+ Handle.useExistingOptions(elem);
+ }
+ };
+ }());
+
+ return {
+ Set: Set,
+ Remove: Remove,
+ Handle: Handle
+ };
+ }());
+
+ EditableOption = (function () {
+ return {
+ init: function (elem) {
+ if (!elem.find('option.' + pluginClass).length) {
+ var editableOption = $('<option>');
+ editableOption.addClass(pluginClass);
+ elem.append(editableOption);
+ }
+
+ elem.bind('keydown', EventHandlers.keyDown);
+ elem.bind('keypress', EventHandlers.keyPress);
+ elem.bind('keyup', EventHandlers.keyUp);
+ elem.bind('change', EventHandlers.change);
+ elem.bind('focus', EventHandlers.focus);
+ elem.bind('blur', EventHandlers.blur);
+ elem.bind('click', EventHandlers.click);
+ },
+
+ destroy: function (elem) {
+ elem.find('option.' + pluginClass).remove();
+
+ elem.unbind('keydown', EventHandlers.keyDown);
+ elem.unbind('keypress', EventHandlers.keyPress);
+ elem.unbind('keyup', EventHandlers.keyUp);
+ elem.unbind('change', EventHandlers.change);
+ elem.unbind('focus', EventHandlers.focus);
+ elem.unbind('blur', EventHandlers.blur);
+ elem.unbind('click', EventHandlers.click);
+ }
+ };
+ }());
+
+ // generates unique identifier
+ generateId = function () {
+ while (true) {
+ var random = Math.floor(Math.random() * 100000);
+
+ if (options[random] === undefined) {
+ return random;
+ }
+ }
+ };
+
+ // sets combobox
+ setup = function (elem) {
+ EditableOption.init(elem);
+ Parameters.Handle.all(elem);
+ };
+
+ // Combobox public members
+ return {
+ // create editable combobox
+ init: function (settings) {
+ return $(this).filter(':uneditable').each(function () {
+ var id = generateId(), elem = $(this);
+
+ elem.data('jecId', id);
+
+ // override passed default options
+ options[id] = $.extend(true, {}, defaults);
+
+ // parse keys
+ Parameters.Set.ignoredKeys(elem, options[id].ignoredKeys);
+ Parameters.Set.acceptedKeys(elem, options[id].acceptedKeys);
+
+ if ($.isPlainObject(settings)) {
+ $.each(settings, function (key, val) {
+ if (val !== undefined) {
+ switch (key) {
+ case 'position':
+ Parameters.Set.position(elem, val);
+ break;
+ case 'ignoreOptGroups':
+ Parameters.Set.ignoreOptGroups(elem, val);
+ break;
+ case 'maxLength':
+ Parameters.Set.maxLength(elem, val);
+ break;
+ case 'classes':
+ Parameters.Set.classes(elem, val);
+ break;
+ case 'optionClasses':
+ Parameters.Set.optionClasses(elem, val);
+ break;
+ case 'styles':
+ Parameters.Set.styles(elem, val);
+ break;
+ case 'optionStyles':
+ Parameters.Set.optionStyles(elem, val);
+ break;
+ case 'triggerChangeEvent':
+ Parameters.Set.triggerChangeEvent(elem, val);
+ break;
+ case 'focusOnNewOption':
+ Parameters.Set.focusOnNewOption(elem, val);
+ break;
+ case 'useExistingOptions':
+ Parameters.Set.useExistingOptions(elem, val);
+ break;
+ case 'blinkingCursor':
+ Parameters.Set.blinkingCursor(elem, val);
+ break;
+ case 'blinkingCursorInterval':
+ Parameters.Set.blinkingCursorInterval(elem, val);
+ break;
+ case 'ignoredKeys':
+ Parameters.Set.ignoredKeys(elem, val);
+ break;
+ case 'acceptedKeys':
+ Parameters.Set.acceptedKeys(elem, val);
+ break;
+ }
+ }
+ });
+ }
+
+ setup($(this));
+ });
+ },
+
+ // creates editable combobox without using existing select elements
+ initJS: function (options, settings) {
+ var select, addOptions;
+
+ select = $('<select>');
+
+ addOptions = function (elem, options) {
+ if ($.isArray(options)) {
+ $.each(options, function (i, val) {
+ if ($.isPlainObject(val)) {
+ $.each(val, function (key, value) {
+ if ($.isArray(value)) {
+ var og = $('<optgroup>').attr('label', key);
+ addOptions(og, value);
+ og.appendTo(select);
+ } else if (typeof value === 'number' || typeof value === 'string') {
+ $('<option>').text(value).attr('value', key)
+ .appendTo(elem);
+ }
+ });
+ } else if (typeof val === 'string' || typeof val === 'number') {
+ $('<option>').text(val).attr('value', val).appendTo(elem);
+ }
+ });
+ }
+ };
+
+ addOptions(select, options);
+
+ return select.jec(settings);
+ },
+
+ // destroys editable combobox
+ destroy: function () {
+ return $(this).filter(':editable').each(function () {
+ $(this).jecOff();
+ $.removeData($(this).get(0), 'jecId');
+ $.removeData($(this).get(0), 'jecCursorState');
+ $.removeData($(this).get(0), 'jecOpenedState');
+ });
+ },
+
+ // enable editablecombobox
+ enable: function () {
+ return $(this).filter(':editable').each(function () {
+ var id = Combobox.getId($(this)), value = values[id];
+
+ setup($(this));
+
+ if (value !== undefined) {
+ $(this).jecValue(value);
+ }
+ });
+ },
+
+ // disable editable combobox
+ disable: function () {
+ return $(this).filter(':editable').each(function () {
+ var val = $(this).find('option.' + pluginClass).val();
+ values[Combobox.getId($(this))] = val;
+ Parameters.Remove.all($(this));
+ EditableOption.destroy($(this));
+ });
+ },
+
+ // gets or sets editable option's value
+ value: function (value, setFocus) {
+ if ($(this).filter(':editable').length > 0) {
+ if (value === null || value === undefined) {
+ // get value
+ return $(this).find('option.' + pluginClass).val();
+ } else if (typeof value === 'string' || typeof value === 'number') {
+ // set value
+ return $(this).filter(':editable').each(function () {
+ var option = $(this).find('option.' + pluginClass);
+ option.val(value).text(value);
+ if (typeof setFocus !== 'boolean' || setFocus) {
+ option.prop('selected', true);
+ }
+ });
+ }
+ }
+ },
+
+ // gets or sets editable option's preference
+ pref: function (name, value) {
+ if ($(this).filter(':editable').length > 0) {
+ if (typeof name === 'string') {
+ if (value === null || value === undefined) {
+ // get preference
+ return options[Combobox.getId($(this))][name];
+ } else {
+ // set preference
+ return $(this).filter(':editable').each(function () {
+ switch (name) {
+ case 'position':
+ Parameters.Set.position($(this), value);
+ Parameters.Handle.position($(this));
+ break;
+ case 'classes':
+ Parameters.Remove.classes($(this));
+ Parameters.Set.classes($(this), value);
+ Parameters.Handle.position($(this));
+ break;
+ case 'optionClasses':
+ Parameters.Remove.optionClasses($(this));
+ Parameters.Set.optionClasses($(this), value);
+ Parameters.Set.optionClasses($(this));
+ break;
+ case 'styles':
+ Parameters.Remove.styles($(this));
+ Parameters.Set.styles($(this), value);
+ Parameters.Set.styles($(this));
+ break;
+ case 'optionStyles':
+ Parameters.Remove.optionStyles($(this));
+ Parameters.Set.optionStyles($(this), value);
+ Parameters.Handle.optionStyles($(this));
+ break;
+ case 'focusOnNewOption':
+ Parameters.Set.focusOnNewOption($(this), value);
+ Parameters.Handle.focusOnNewOption($(this));
+ break;
+ case 'useExistingOptions':
+ Parameters.Set.useExistingOptions($(this), value);
+ Parameters.Handle.useExistingOptions($(this));
+ break;
+ case 'blinkingCursor':
+ Parameters.Set.blinkingCursor($(this), value);
+ break;
+ case 'blinkingCursorInterval':
+ Parameters.Set.blinkingCursorInterval($(this), value);
+ break;
+ case 'ignoredKeys':
+ Parameters.Set.ignoredKeys($(this), value);
+ break;
+ case 'acceptedKeys':
+ Parameters.Set.acceptedKeys($(this), value);
+ break;
+ }
+ });
+ }
+ }
+ }
+ },
+
+ // sets editable option to the value of currently selected option
+ setEditableOption: function (elem) {
+ var value = elem.find('option:selected').text();
+ elem.find('option.' + pluginClass).attr('value', elem.val()).text(value).prop('selected', true);
+ },
+
+ // get combobox id
+ getId: function (elem) {
+ return elem.data('jecId');
+ },
+
+ valueIsEditable: function (elem) {
+ return elem.find('option.' + pluginClass).get(0) === elem.find('option:selected').get(0);
+ },
+
+ clearCursor: function (elem) {
+ $(elem).find('option.' + cursorClass).each(function () {
+ var text = $(this).text();
+ $(this).removeClass(cursorClass).text(text.substring(0, text.length - 1));
+ });
+ },
+
+ cursorState: function (elem, state) {
+ return elem.data('jecCursorState', state);
+ },
+
+ openedState: function (elem, state) {
+ return elem.data('jecOpenedState', state);
+ },
+
+ //handles editable cursor
+ handleCursor: function () {
+ if (activeCombobox !== undefined && activeCombobox !== null) {
+ if ($.browser.msie && Combobox.openedState(activeCombobox)) {
+ return;
+ }
+
+ var state = Combobox.cursorState(activeCombobox), elem;
+ if (state) {
+ Combobox.clearCursor(activeCombobox);
+ } else if (Combobox.valueIsEditable(activeCombobox)) {
+ elem = activeCombobox.find('option:selected');
+ elem.addClass(cursorClass).text(elem.text() + '|');
+ }
+ Combobox.cursorState(activeCombobox, !state);
+ }
+ }
+ };
+ }());
+
+ // jEC public members
+ return {
+ init: Combobox.init,
+ enable: Combobox.enable,
+ disable: Combobox.disable,
+ destroy: Combobox.destroy,
+ value: Combobox.value,
+ pref: Combobox.pref,
+ initJS: Combobox.initJS,
+ handleCursor: Combobox.handleCursor
+ };
+ }());
+
+ // register functions
+ $.fn.extend({
+ jec: $.jEC.init,
+ jecOn: $.jEC.enable,
+ jecOff: $.jEC.disable,
+ jecKill: $.jEC.destroy,
+ jecValue: $.jEC.value,
+ jecPref: $.jEC.pref
+ });
+
+ $.extend({
+ jec: $.jEC.initJS
+ });
+
+ // register selectors
+ $.extend($.expr[':'], {
+ editable: function (a) {
+ var data = $(a).data('jecId');
+ return data !== null && data !== undefined;
+ },
+
+ uneditable: function (a) {
+ var data = $(a).data('jecId');
+ return data === null || data === undefined;
+ }
+ });
+
+}(jQuery)); \ No newline at end of file
diff --git a/apps/contacts/js/loader.js b/apps/contacts/js/loader.js
new file mode 100644
index 00000000000..eb59185d728
--- /dev/null
+++ b/apps/contacts/js/loader.js
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+Contacts_Import={
+ importdialog: function(filename){
+ var path = $('#dir').val();
+ $('body').append('<div id="contacts_import"></div>');
+ $('#contacts_import').load(OC.filePath('contacts', 'ajax', 'importdialog.php'), {filename:filename, path:path}, function(){Contacts_Import.initdialog(filename);});
+ },
+ initdialog: function(filename){
+ $('#contacts_import_dialog').dialog({
+ width : 500,
+ close : function() {
+ $(this).dialog('destroy').remove();
+ $('#contacts_import').remove();
+ }
+ });
+ $('#import_done_button').click(function(){
+ $('#contacts_import_dialog').dialog('destroy').remove();
+ $('#contacts_import').remove();
+ });
+ $('#progressbar').progressbar({value: 0});
+ $('#startimport').click(function(){
+ var filename = $('#filename').val();
+ var path = $('#path').val();
+ var addressbookid = $('#contacts option:selected').val();
+ if($('#contacts option:selected').val() == 'newaddressbook'){
+ var method = 'new';
+ var addressbookname = $('#newaddressbook').val();
+ var addressbookname = $.trim(addressbookname);
+ if(newaddressbook == ''){
+ $('#newaddressbook').css('background-color', '#FF2626');
+ $('#newaddressbook').focus(function(){
+ $('#newaddressbook').css('background-color', '#F8F8F8');
+ });
+ return false;
+ }
+ }else{
+ var method = 'old';
+ }
+ $('#newaddressbook').attr('readonly', 'readonly');
+ $('#contacts').attr('disabled', 'disabled');
+ var progressfile = $('#progressfile').val();
+ $.post(OC.filePath('contacts', '', 'import.php'), {method: String (method), addressbookname: String (addressbookname), path: String (path), file: String (filename), id: String (addressbookid)}, function(data){
+ if(data.status == 'success'){
+ $('#progressbar').progressbar('option', 'value', 100);
+ $('#import_done').css('display', 'block');
+ }
+ });
+ $('#form_container').css('display', 'none');
+ $('#progressbar_container').css('display', 'block');
+ window.setTimeout('Contacts_Import.getimportstatus(\'' + progressfile + '\')', 500);
+ });
+ $('#contacts').change(function(){
+ if($('#contacts option:selected').val() == 'newaddressbook'){
+ $('#newaddressbookform').slideDown('slow');
+ }else{
+ $('#newaddressbookform').slideUp('slow');
+ }
+ });
+ },
+ getimportstatus: function(progressfile){
+ $.get(OC.filePath('contacts', 'import_tmp', progressfile), function(percent){
+ $('#progressbar').progressbar('option', 'value', parseInt(percent));
+ if(percent < 100){
+ window.setTimeout('Contacts_Import.getimportstatus(\'' + progressfile + '\')', 500);
+ }else{
+ $('#import_done').css('display', 'block');
+ }
+ });
+ }
+}
+$(document).ready(function(){
+ if(typeof FileActions !== 'undefined'){
+ FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog);
+ FileActions.setDefault('text/vcard','importaddressbook');
+ };
+}); \ No newline at end of file
diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php
index 580cc72d5eb..f62bd311479 100644
--- a/apps/contacts/lib/app.php
+++ b/apps/contacts/lib/app.php
@@ -58,7 +58,7 @@ class OC_Contacts_App{
public static function getContactObject($id){
$card = OC_Contacts_VCard::find( $id );
if( $card === false ){
- OC_JSON::error(array('data' => array( 'message' => self::$l10n->t('Contact could not be found.'))));
+ OC_JSON::error(array('data' => array( 'message' => self::$l10n->t('Contact could not be found.').' '.$id)));
exit();
}
@@ -67,13 +67,29 @@ class OC_Contacts_App{
}
/**
- * @brief Gets the VCard as text
+ * @brief Gets the VCard as an OC_VObject
* @returns The card or null if the card could not be parsed.
*/
public static function getContactVCard($id){
$card = self::getContactObject( $id );
$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');
+ if($appinfo['version'] >= 5) {
+ OC_Log::write('contacts','OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OC_Log::DEBUG);
+ }
+ OC_Log::write('contacts','getContactVCard, Missing N field', OC_Log::DEBUG);
+ if($vcard->__isset('FN')) {
+ 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))).';;;';
+ OC_Contacts_VCard::edit( $id, $vcard->serialize());
+ } else { // Else just add an empty 'N' field :-P
+ $vcard->setString('N', 'Unknown;Name;;;');
+ }
+ $vcard->setString('N', $n);
+ }
return $vcard;
}
diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php
index aefa2c74114..f9ca427354e 100644
--- a/apps/contacts/lib/vcard.php
+++ b/apps/contacts/lib/vcard.php
@@ -99,7 +99,7 @@ class OC_Contacts_VCard{
* @brief Adds a card
* @param integer $id Addressbook id
* @param string $data vCard file
- * @return insertid
+ * @return insertid on success or null if card is not parseable.
*/
public static function add($id,$data){
$fn = null;
@@ -107,6 +107,22 @@ class OC_Contacts_VCard{
$card = OC_VObject::parse($data);
if(!is_null($card)){
$fn = $card->getAsString('FN');
+ if(!$fn){ // Fix missing 'FN' field.
+ $n = $card->getAsString('N');
+ if(!is_null($n)){
+ $fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2)));
+ $card->setString('FN', $fn);
+ OC_Log::write('contacts','OC_Contacts_VCard::add. Added missing \'FN\' field: '.$fn,OC_Log::DEBUG);
+ } else {
+ $fn = 'Unknown Name';
+ }
+ }
+ $n = $card->getAsString('N');
+ if(!$n){ // Fix missing 'N' field.
+ $n = implode(';', array_reverse(array_slice(explode(' ', $fn), 0, 2))).';;;';
+ $card->setString('N', $n);
+ OC_Log::write('contacts','OC_Contacts_VCard::add. Added missing \'N\' field: '.$n,OC_Log::DEBUG);
+ }
$uid = $card->getAsString('UID');
if(is_null($uid)){
$card->setUID();
@@ -118,8 +134,8 @@ class OC_Contacts_VCard{
// Add product ID.
$prodid = trim($card->getAsString('PRODID'));
if(!$prodid) {
- $appinfo = $info=OC_App::getAppInfo('contacts');
- $prodid = 'PRODID:-//ownCloud//NONSGML '.$appinfo['name'].' '.$appinfo['version'].'//EN';
+ $appinfo = OC_App::getAppInfo('contacts');
+ $prodid = '//ownCloud//NONSGML '.$appinfo['name'].' '.$appinfo['version'].'//EN';
$card->setString('PRODID', $prodid);
}
// VCARD must have a version
@@ -137,8 +153,10 @@ class OC_Contacts_VCard{
}
else{
// that's hard. Creating a UID and not saving it
- $uid = self::createUID();
- $uri = $uid.'.vcf';
+ 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.
+ //$uid = self::createUID();
+ //$uri = $uid.'.vcf';
};
$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
@@ -158,7 +176,7 @@ class OC_Contacts_VCard{
* @return insertid
*/
public static function addFromDAVData($id,$uri,$data){
- $fn = null;
+ $fn = $n = null;
$email = null;
$card = OC_VObject::parse($data);
if(!is_null($card)){
@@ -166,19 +184,31 @@ class OC_Contacts_VCard{
if($property->name == 'FN'){
$fn = $property->value;
}
+ if($property->name == 'N'){
+ $n = $property->value;
+ }
if($property->name == 'EMAIL' && is_null($email)){
$email = $property->value;
}
}
}
if(!$fn) {
- if($email) {
+ if($n){
+ $fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2)));
+ } elseif($email) {
$fn = $email;
} else {
- $fn = 'Unknown';
+ $fn = 'Unknown Name';
}
- $card->addProperty('EMAIL', $email);
+ $card->addProperty('FN', $fn);
+ $data = $card->serialize();
+ OC_Log::write('contacts','OC_Contacts_VCard::add. Added missing \'FN\' field: '.$n,OC_Log::DEBUG);
+ }
+ if(!$n){ // Fix missing 'N' field.
+ $n = implode(';', array_reverse(array_slice(explode(' ', $fn), 0, 2))).';;;';
+ $card->setString('N', $n);
$data = $card->serialize();
+ OC_Log::write('contacts','OC_Contacts_VCard::add. Added missing \'N\' field: '.$n,OC_Log::DEBUG);
}
$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
diff --git a/apps/contacts/templates/index2.php b/apps/contacts/templates/index2.php
new file mode 100644
index 00000000000..4c0dfad6177
--- /dev/null
+++ b/apps/contacts/templates/index2.php
@@ -0,0 +1,27 @@
+<script type='text/javascript'>
+ var totalurl = '<?php echo OC_Helper::linkTo('contacts', 'carddav.php', null, true); ?>/addressbooks';
+</script>
+<div id="controls">
+ <form>
+ <input type="button" id="contacts_newcontact" value="<?php echo $l->t('Add Contact'); ?>">
+ <input type="button" id="chooseaddressbook" value="<?php echo $l->t('Addressbooks'); ?>">
+ </form>
+</div>
+<div id="leftcontent" class="leftcontent">
+ <ul id="contacts">
+ <?php echo $this->inc("part.contacts"); ?>
+ </ul>
+</div>
+<div id="rightcontent" class="rightcontent" data-id="<?php echo $_['id']; ?>">
+ <?php
+ if ($_['id']){
+ echo $this->inc('part.contact');
+ }
+ else{
+ echo $this->inc('part.no_contacts');
+ }
+ ?>
+</div>
+<!-- Dialogs -->
+<div id="dialog_holder"></div>
+<!-- End of Dialogs -->
diff --git a/apps/contacts/templates/part.chooseaddressbook.rowfields.php b/apps/contacts/templates/part.chooseaddressbook.rowfields.php
index 0cbfe2bf803..95a4b2362aa 100644
--- a/apps/contacts/templates/part.chooseaddressbook.rowfields.php
+++ b/apps/contacts/templates/part.chooseaddressbook.rowfields.php
@@ -1,5 +1,5 @@
<?php
// FIXME: Make this readable.
echo "<td width=\"20px\"><input id=\"active_" . $_['addressbook']["id"] . "\" type=\"checkbox\" onClick=\"Contacts.UI.Addressbooks.activation(this, " . $_['addressbook']["id"] . ")\"" . (OC_Contacts_Addressbook::isActive($_['addressbook']["id"]) ? ' checked="checked"' : '') . "></td>";
- echo "<td><label for=\"active_" . $_['addressbook']["id"] . "\">" . $_['addressbook']["displayname"] . "</label></td>";
+ echo "<td><label for=\"active_" . $_['addressbook']["id"] . "\">" . htmlspecialchars($_['addressbook']["displayname"]) . "</label></td>";
echo "<td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.showCardDAVUrl('" . OC_User::getUser() . "', '" . $_['addressbook']["uri"] . "');\" title=\"" . $l->t("CardDav Link") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/public.svg\"></a></td><td width=\"20px\"><a href=\"export.php?bookid=" . $_['addressbook']["id"] . "\" title=\"" . $l->t("Download") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/download.svg\"></a></td><td width=\"20px\"><a href=\"#\" title=\"" . $l->t("Edit") . "\" class=\"action\" onclick=\"Contacts.UI.Addressbooks.editAddressbook(this, " . $_['addressbook']["id"] . ");\"><img class=\"svg action\" src=\"../../core/img/actions/rename.svg\"></a></td><td width=\"20px\"><a href=\"#\" onclick=\"Contacts.UI.Addressbooks.deleteAddressbook('" . $_['addressbook']["id"] . "');\" title=\"" . $l->t("Delete") . "\" class=\"action\"><img class=\"svg action\" src=\"../../core/img/actions/delete.svg\"></a></td>";
diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php
new file mode 100644
index 00000000000..a56999dbf39
--- /dev/null
+++ b/apps/contacts/templates/part.contact.php
@@ -0,0 +1,184 @@
+<?php
+$l=new OC_L10N('contacts');
+$id = isset($_['id']) ? $_['id'] : '';
+?>
+<div id="card">
+ <div id="actionbar">
+ <a title="<?php echo $l->t('Add field'); ?>" class="svg action" id="contacts_propertymenu_button"></a>
+ <div id="contacts_propertymenu" style="display: none;">
+ <ul>
+ <li><a data-type="PHOTO"><?php echo $l->t('Profile picture'); ?></a></li>
+ <li><a data-type="ORG"><?php echo $l->t('Organization'); ?></a></li>
+ <li><a data-type="NICKNAME"><?php echo $l->t('Nickname'); ?></a></li>
+ <li><a data-type="BDAY"><?php echo $l->t('Birthday'); ?></a></li>
+ <li><a data-type="TEL"><?php echo $l->t('Phone'); ?></a></li>
+ <li><a data-type="EMAIL"><?php echo $l->t('Email'); ?></a></li>
+ <li><a data-type="ADR"><?php echo $l->t('Address'); ?></a></li>
+ </ul>
+ </div>
+ <img onclick="Contacts.UI.Card.export();" class="svg action" id="contacts_downloadcard" src="<?php echo image_path('', 'actions/download.svg'); ?>" title="<?php echo $l->t('Download contact');?>" />
+ <img class="svg action" id="contacts_deletecard" src="<?php echo image_path('', 'actions/delete.svg'); ?>" title="<?php echo $l->t('Delete contact');?>" />
+ </div>
+
+ <div class="contactsection">
+
+ <form style="display:none;" id="file_upload_form" action="ajax/uploadphoto.php" method="post" enctype="multipart/form-data" target="file_upload_target">
+ <fieldset id="photo" class="formfloat">
+ <div id="contacts_details_photo_wrapper" title="<?php echo $l->t('Click or drop to upload picture'); ?> (max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
+ <!-- img style="padding: 1em;" id="contacts_details_photo" alt="Profile picture" src="photo.php?id=<?php echo $_['id']; ?>" / -->
+ <progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress>
+ </div>
+ <input type="hidden" name="id" value="<?php echo $_['id'] ?>">
+ <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload">
+ <input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
+ <input id="file_upload_start" type="file" accept="image/*" name="imagefile" />
+ <iframe name="file_upload_target" id='file_upload_target' src=""></iframe>
+ </fieldset>
+ </form>
+ <form id="contact_identity" method="post" <?php echo ($_['id']==''||!isset($_['id'])?'style="display:none;"':''); ?>>
+ <input type="hidden" name="id" value="<?php echo $_['id'] ?>">
+ <fieldset class="propertycontainer" data-element="N"><input type="hidden" id="n" class="contacts_property" name="value" value="" /></fieldset>
+ <fieldset id="ident" class="formfloat">
+ <!-- legend>Name</legend -->
+ <dl class="form">
+ <!-- dt><label for="n"><?php echo $l->t('Name'); ?></label></dt>
+ <dd style="padding-top: 0.8em;vertical-align: text-bottom;"><span id="n" type="text"></span></dd -->
+ <dt><label for="fn"><?php echo $l->t('Display name'); ?></label></dt>
+ <dd class="propertycontainer" data-element="FN">
+ <select id="fn" name="value" required="required" class="contacts_property" title="<?php echo $l->t('Format custom, Short name, Full name, Reverse or Reverse with comma'); ?>" style="width:16em;">
+ <option id="short" title="Short name"></option>
+ <option id="full" title="Full name"></option>
+ <option id="reverse" title="Reverse"></option>
+ <option id="reverse_comma" title="Reverse with comma"></option>
+ </select><a id="edit_name" class="edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
+ </dd>
+ <dt style="display:none;" id="org_label" data-element="ORG"><label for="org"><?php echo $l->t('Organization'); ?></label></dt>
+ <dd style="display:none;" class="propertycontainer" id="org_value" data-element="ORG"><input id="org" required="required" name="value[ORG]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Organization'); ?>" /><a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
+ <dt style="display:none;" id="nickname_label" data-element="NICKNAME"><label for="nickname"><?php echo $l->t('Nickname'); ?></label></dt>
+ <dd style="display:none;" class="propertycontainer" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" name="value[NICKNAME]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
+ <dt style="display:none;" id="bday_label" data-element="BDAY"><label for="bday"><?php echo $l->t('Birthday'); ?></label></dt>
+ <dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday" required="required" name="value" type="text" class="contacts_property" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
+ </dl>
+ </fieldset>
+ </form>
+ </div>
+
+ <!-- div class="delimiter"></div -->
+ <form id="contact_communication" method="post" style="display: none;">
+ <div class="contactsection">
+ <!-- email addresses -->
+ <div id="emails" style="display:none;">
+ <fieldset class="contactpart">
+ <legend><?php echo $l->t('Email'); ?></legend>
+ <ul id="emaillist" class="propertylist">
+ <li class="template" style="white-space: nowrap; display: none;" data-element="EMAIL">
+ <input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
+ <input type="email" required="required" class="nonempty contacts_property" style="width:15em;" name="value" value="" x-moz-errormessage="<?php echo $l->t('Please specify a valid email address.'); ?>" placeholder="<?php echo $l->t('Enter email address'); ?>" /><span class="listactions"><a onclick="Contacts.UI.mailTo(this)" class="mail" title="<?php echo $l->t('Mail to address'); ?>"></a>
+ <a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li>
+ <?php
+ if(0) { /*foreach($card['EMAIL'] as $email) {*/
+ ?>
+ <li class="propertycontainer" style="white-space: nowrap;" data-checksum="<?php echo $email['checksum'] ?>" data-element="EMAIL">
+ <input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" <?php echo (isset($email['parameters']['PREF'])?'checked="checked"':''); ?> />
+ <input type="email" required="required" class="nonempty contacts_property" style="width:15em;" name="value" value="<?php echo $email['value'] ?>" placeholder="<?php echo $l->t('Enter email address'); ?>" /><span class="listactions"><a onclick="Contacts.UI.mailTo(this)" class="mail" title="<?php echo $l->t('Mail to address'); ?>"></a>
+ <a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete email address'); ?>"></a></span></li>
+ <?php } ?>
+ </ul><!-- a id="add_email" class="add" title="<?php echo $l->t('Add email address'); ?>"></a -->
+ </div> <!-- email addresses-->
+
+ <!-- Phone numbers -->
+ <div id="phones" style="display:none;">
+ <fieldset class="contactpart">
+ <legend><?php echo $l->t('Phone'); ?></legend>
+ <ul id="phonelist" class="propertylist">
+ <li class="template" style="white-space: nowrap; display: none;" data-element="TEL">
+ <input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" />
+ <input type="text" required="required" class="nonempty contacts_property" style="width:10em; border: 0px;" name="value" value="" placeholder="<?php echo $l->t('Enter phone number'); ?>" />
+ <select multiple="multiple" name="parameters[TYPE][]">
+ <?php echo html_select_options($_['phone_types'], array()) ?>
+ </select>
+ <a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete phone number'); ?>"></a></li>
+ <?php
+ if(0) { /*foreach($card['TEL'] as $phone) {*/
+ ?>
+ <li class="propertycontainer" style="white-space: nowrap;" data-checksum="<?php echo $phone['checksum'] ?>" data-element="TEL">
+ <input type="checkbox" class="contacts_property" name="parameters[TYPE][]" value="PREF" title="<?php echo $l->t('Preferred'); ?>" <?php echo (isset($phone['parameters']['PREF'])?'checked="checked"':''); ?> />
+ <input type="text" required="required" class="nonempty contacts_property" style="width:8em; border: 0px;" name="value" value="<?php echo $phone['value'] ?>" placeholder="<?php echo $l->t('Enter phone number'); ?>" />
+ <select class="contacts_property" multiple="multiple" name="parameters[TYPE][]">
+ <?php echo html_select_options($_['phone_types'], isset($phone['parameters']['TYPE'])?$phone['parameters']['TYPE']:array()) ?>
+ </select>
+ <a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="<?php echo $l->t('Delete phone number'); ?>"></a></li>
+ <?php } ?>
+ </ul><!-- a id="add_phone" class="add" title="<?php echo $l->t('Add phone number'); ?>"></a -->
+ </div> <!-- Phone numbers -->
+
+ <!-- Addresses -->
+ <div id="addresses" style="display:none;">
+ <fieldset class="contactpart">
+ <legend><?php echo $l->t('Address'); ?></legend>
+ <div id="addressdisplay">
+ <dl class="addresscard template" style="display: none;" data-element="ADR"><dt>
+ <input class="adr contacts_property" name="value" type="hidden" value="" />
+ <input type="hidden" class="adr_type contacts_property" name="parameters[TYPE][]" value="" />
+ <span class="adr_type_label"></span><a class="globe" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.searchOSM(this);" title="<?php echo $l->t('View on map'); ?>"></a><a class="edit" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.editAddress(this, false);" title="<?php echo $l->t('Edit address details'); ?>"></a><a class="delete" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="Delete address"></a>
+ </dt><dd><ul class="addresslist"></ul></dd></dl>
+
+ <?php if(0) { /*foreach($card['ADR'] as $address) {*/ ?>
+ <dl class="addresscard propertycontainer" data-checksum="<?php echo $address['checksum']; ?>" data-element="ADR">
+ <dt>
+ <input class="adr contacts_property" name="value" type="hidden" value="<?php echo implode(';',$address['value']); ?>" />
+ <input type="hidden" class="adr_type contacts_property" name="parameters[TYPE][]" value="<?php echo strtoupper(implode(',',$address['parameters'])); ?>" />
+ <span class="adr_type_label">
+ <?php
+ if(count($address['parameters']) > 0) {
+ //array_walk($address['parameters'], ) Nah, this wont work...
+ $translated = array();
+ foreach($address['parameters'] as $type) {
+ $translated[] = $l->t(ucwords(strtolower($type)));
+ }
+ echo implode('/', $translated);
+ }
+ ?></span><a class="globe" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.searchOSM(this);" title="<?php echo $l->t('View on map'); ?>"></a><a class="edit" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.editAddress(this, false);" title="<?php echo $l->t('Edit address details'); ?>"></a><a class="delete" style="float:right;" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'list');" title="Delete address"></a>
+ </dt>
+ <dd>
+ <ul class="addresslist">
+ <?php
+ $adr = $address['value'];
+ $tmp = ($adr[0]?'<li>'.$adr[0].'</li>':'');
+ $tmp .= ($adr[1]?'<li>'.$adr[1].'</li>':'');
+ $tmp .= ($adr[2]?'<li>'.$adr[2].'</li>':'');
+ $tmp .= ($adr[3]||$adr[5]?'<li>'.$adr[5].' '.$adr[3].'</li>':'');
+ $tmp .= ($adr[4]?'<li>'.$adr[4].'</li>':'');
+ $tmp .= ($adr[6]?'<li>'.$adr[6].'</li>':'');
+ echo $tmp;
+
+ ?>
+ </ul>
+ </dd>
+ </dl>
+ <?php } ?>
+ </fieldset>
+ </div>
+ </div> <!-- Addresses -->
+ </div>
+ <!-- a id="add_address" onclick="Contacts.UI.Card.editAddress('new', true)" class="add" title="<?php echo $l->t('Add address'); ?>"></a -->
+ </div>
+ </form>
+</div>
+<div id="edit_photo_dialog" title="Edit photo">
+ <div id="edit_photo_dialog_img"></div>
+</div>
+<script language="Javascript">
+$(document).ready(function(){
+ if('<?php echo $id; ?>'!='') {
+ $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':'<?php echo $id; ?>'},function(jsondata){
+ if(jsondata.status == 'success'){
+ Contacts.UI.Card.loadContact(jsondata.data);
+ }
+ else{
+ Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+ }
+ });
+ }
+});
+</script>
diff --git a/apps/contacts/templates/part.contactphoto.php b/apps/contacts/templates/part.contactphoto.php
new file mode 100644
index 00000000000..7d7ab237561
--- /dev/null
+++ b/apps/contacts/templates/part.contactphoto.php
@@ -0,0 +1,9 @@
+<?php
+$id = $_['id'];
+$wattr = isset($_['width'])?'width="'.$_['width'].'"':'';
+$hattr = isset($_['height'])?'height="'.$_['height'].'"':'';
+?>
+<img class="loading" id="contacts_details_photo" <?php echo $wattr; ?> <?php echo $hattr; ?> src="<?php echo OC_Helper::linkTo('contacts', 'photo.php', null, true); ?>?id=<?php echo $id; ?>&amp;refresh=<?php echo rand(); ?>" />
+<progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress>
+
+
diff --git a/apps/contacts/templates/part.cropphoto.php b/apps/contacts/templates/part.cropphoto.php
new file mode 100644
index 00000000000..cb416f0e415
--- /dev/null
+++ b/apps/contacts/templates/part.cropphoto.php
@@ -0,0 +1,62 @@
+<?php
+$id = $_['id'];
+$tmp_path = $_['tmp_path'];
+OC_Log::write('contacts','templates/part.cropphoto.php: tmp_path: '.$tmp_path.', exists: '.file_exists($tmp_path), OC_Log::DEBUG);
+?>
+<script language="Javascript">
+ jQuery(function($) {
+ $('#cropbox').Jcrop({
+ onChange: showCoords,
+ onSelect: showCoords,
+ onRelease: clearCoords,
+ maxSize: [399, 399],
+ bgColor: 'black',
+ bgOpacity: .4,
+ boxWidth: 400,
+ boxHeight: 400,
+ setSelect: [ 100, 130, 50, 50 ]//,
+ //aspectRatio: 0.8
+ });
+ });
+ // Simple event handler, called from onChange and onSelect
+ // event handlers, as per the Jcrop invocation above
+ function showCoords(c) {
+ $('#x1').val(c.x);
+ $('#y1').val(c.y);
+ $('#x2').val(c.x2);
+ $('#y2').val(c.y2);
+ $('#w').val(c.w);
+ $('#h').val(c.h);
+ };
+
+ function clearCoords() {
+ $('#coords input').val('');
+ };
+ /*
+ $('#coords').submit(function() {
+ alert('Handler for .submit() called.');
+ return true;
+ });*/
+</script>
+<img id="cropbox" src="<?php echo OC_Helper::linkTo('contacts', 'dynphoto.php', null, true); ?>?tmp_path=<?php echo urlencode($tmp_path); ?>" />
+<form id="cropform"
+ class="coords"
+ method="post"
+ enctype="multipart/form-data"
+ target="crop_target"
+ action="<?php echo OC_Helper::linkTo('contacts', 'ajax/savecrop.php', null, true); ?>">
+
+ <input type="hidden" id="id" name="id" value="<?php echo $id; ?>" />
+ <input type="hidden" id="tmp_path" name="tmp_path" value="<?php echo $tmp_path; ?>" />
+ <fieldset id="coords">
+ <input type="hidden" id="x1" name="x1" value="" />
+ <input type="hidden" id="y1" name="y1" value="" />
+ <input type="hidden" id="x2" name="x2" value="" />
+ <input type="hidden" id="y2" name="y2" value="" />
+ <input type="hidden" id="w" name="w" value="" />
+ <input type="hidden" id="h" name="h" value="" />
+ </fieldset>
+ <iframe name="crop_target" id='crop_target' src=""></iframe>
+</form>
+
+
diff --git a/apps/contacts/templates/part.edit_address_dialog.php b/apps/contacts/templates/part.edit_address_dialog.php
new file mode 100644
index 00000000000..0ecdc4e1915
--- /dev/null
+++ b/apps/contacts/templates/part.edit_address_dialog.php
@@ -0,0 +1,67 @@
+<?php
+$adr = isset($_['adr'])?$_['adr']:array();
+$id = $_['id'];
+$types = array();
+foreach(isset($adr['parameters']['TYPE'])?array($adr['parameters']['TYPE']):array() as $type) {
+ $types[] = strtoupper($type);
+}
+?>
+<div id="edit_address_dialog" title="<?php echo $l->t('Edit address'); ?>">
+<!-- ?php print_r($types); ? -->
+ <fieldset id="address">
+ <dl class="form">
+ <dt>
+ <label class="label" for="adr_type"><?php echo $l->t('Type'); ?></label>
+ </dt>
+ <dd>
+ <select id="adr_type" name="parameters[ADR][TYPE]" size="1">
+ <?php echo html_select_options($_['adr_types'], $types) ?>
+ </select>
+ </dd>
+ <dt>
+ <label class="label" for="adr_pobox"><?php echo $l->t('PO Box'); ?></label>
+ </dt>
+ <dd>
+ <input type="text" id="adr_pobox" name="value[ADR][0]" value="<?php echo isset($adr['value'][0])?$adr['value'][0]:''; ?>">
+ </dd>
+ <dd>
+ <dt>
+ <label class="label" for="adr_extended"><?php echo $l->t('Extended'); ?></label>
+ </dt>
+ <dd>
+ <input type="text" id="adr_extended" name="value[ADR][1]" value="<?php echo isset($adr['value'][1])?$adr['value'][1]:''; ?>">
+ </dd>
+ <dt>
+ <label class="label" for="adr_street"><?php echo $l->t('Street'); ?></label>
+ </dt>
+ <dd>
+ <input type="text" id="adr_street" name="value[ADR][2]" value="<?php echo isset($adr['value'][2])?$adr['value'][2]:''; ?>">
+ </dd>
+ <dt>
+ <label class="label" for="adr_city"><?php echo $l->t('City'); ?></label>
+ </dt>
+ <dd>
+ <input type="text" id="adr_city" name="value[ADR][3]" value="<?php echo isset($adr['value'][3])?$adr['value'][3]:''; ?>">
+ </dd>
+ <dt>
+ <label class="label" for="adr_region"><?php echo $l->t('Region'); ?></label>
+ </dt>
+ <dd>
+ <input type="text" id="adr_region" name="value[ADR][4]" value="<?php echo isset($adr['value'][4])?$adr['value'][4]:''; ?>">
+ </dd>
+ <dt>
+ <label class="label" for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label>
+ </dt>
+ <dd>
+ <input type="text" id="adr_zipcode" name="value[ADR][5]" value="<?php echo isset($adr['value'][5])?$adr['value'][5]:''; ?>">
+ </dd>
+ <dt>
+ <label class="label" for="adr_country"><?php echo $l->t('Country'); ?></label>
+ </dt>
+ <dd>
+ <input type="text" id="adr_country" name="value[ADR][6]" value="<?php echo isset($adr['value'][6])?$adr['value'][6]:''; ?>">
+ </dd>
+ </dl>
+ </fieldset>
+ </form>
+</div>
diff --git a/apps/contacts/templates/part.edit_name_dialog.php b/apps/contacts/templates/part.edit_name_dialog.php
new file mode 100644
index 00000000000..d525fd1f99b
--- /dev/null
+++ b/apps/contacts/templates/part.edit_name_dialog.php
@@ -0,0 +1,59 @@
+<?php
+$l=new OC_L10N('contacts');
+$name = isset($_['name'])?$_['name']:'';
+//print_r($name);
+$id = isset($_['id'])?$_['id']:'';
+$addressbooks = isset($_['addressbooks'])?$_['addressbooks']:null;
+?>
+<div id="edit_name_dialog" title="Edit name">
+ <form>
+ <fieldset>
+ <dl class="form">
+ <?php if(!is_null($addressbooks)) {
+ if(count($_['addressbooks'])==1) {
+ ?>
+ <input type="hidden" id="aid" name="aid" value="<?php echo $_['addressbooks'][0]['id']; ?>">
+ <?php } else { ?>
+ <dt><label for="addressbook"><?php echo $l->t('Addressbook'); ?></label></dt>
+ <dd>
+ <select id="aid" name="aid" size="1">
+ <?php echo html_select_options($_['addressbooks'], null, array('value'=>'id', 'label'=>'displayname')); ?>
+ </select>
+ </dd>
+ <?php }} ?>
+ <dt><label for="pre"><?php echo $l->t('Hon. prefixes'); ?></label></dt>
+ <dd>
+ <input name="pre" id="pre" value="<?php echo isset($name['value'][3]) ? $name['value'][3] : ''; ?>" type="text" list="prefixes" />
+ <datalist id="prefixes">
+ <option value="<?php echo $l->t('Miss'); ?>">
+ <option value="<?php echo $l->t('Ms'); ?>">
+ <option value="<?php echo $l->t('Mr'); ?>">
+ <option value="<?php echo $l->t('Sir'); ?>">
+ <option value="<?php echo $l->t('Mrs'); ?>">
+ <option value="<?php echo $l->t('Dr'); ?>">
+ </datalist>
+ </dd>
+ <dt><label for="giv"><?php echo $l->t('Given name'); ?></label></dt>
+ <dd><input name="giv" id="giv" value="<?php echo isset($name['value'][1]) ? $name['value'][1] : ''; ?>" type="text" /></dd>
+ <dt><label for="add"><?php echo $l->t('Additional names'); ?></label></dt>
+ <dd><input name="add" id="add" value="<?php echo isset($name['value'][2]) ? $name['value'][2] : ''; ?>" type="text" /></dd>
+ <dt><label for="fam"><?php echo $l->t('Family name'); ?></label></dt>
+ <dd><input name="fam" id="fam" value="<?php echo isset($name['value'][0]) ? $name['value'][0] : ''; ?>" type="text" /></dd>
+ <dt><label for="suf"><?php echo $l->t('Hon. suffixes'); ?></label></dt>
+ <dd>
+ <input name="suf" id="suf" value="<?php echo isset($name['value'][4]) ? $name['value'][4] : ''; ?>" type="text" list="suffixes" />
+ <datalist id="suffixes">
+ <option value="<?php echo $l->t('J.D.'); ?>">
+ <option value="<?php echo $l->t('M.D.'); ?>">
+ <option value="<?php echo $l->t('D.O.'); ?>">
+ <option value="<?php echo $l->t('D.C.'); ?>">
+ <option value="<?php echo $l->t('Ph.D.'); ?>">
+ <option value="<?php echo $l->t('Esq.'); ?>">
+ <option value="<?php echo $l->t('Jr.'); ?>">
+ <option value="<?php echo $l->t('Sn.'); ?>">
+ </datalist>
+ </dd>
+ </dl>
+ </fieldset>
+ </form>
+</div>
diff --git a/apps/contacts/templates/part.editaddressbook.php b/apps/contacts/templates/part.editaddressbook.php
index 48fe5c3b378..c1c585687c4 100644
--- a/apps/contacts/templates/part.editaddressbook.php
+++ b/apps/contacts/templates/part.editaddressbook.php
@@ -11,7 +11,7 @@
<tr>
<th><?php echo $l->t('Displayname') ?></th>
<td>
- <input id="displayname_<?php echo $_['addressbook']['id'] ?>" type="text" value="<?php echo $_['addressbook']['displayname'] ?>">
+ <input id="displayname_<?php echo $_['addressbook']['id'] ?>" type="text" value="<?php echo htmlspecialchars($_['addressbook']['displayname']) ?>">
</td>
</tr>
<?php if (!$_['new']): ?>
diff --git a/apps/contacts/templates/part.import.php b/apps/contacts/templates/part.import.php
new file mode 100644
index 00000000000..570eda9b07d
--- /dev/null
+++ b/apps/contacts/templates/part.import.php
@@ -0,0 +1,27 @@
+<div id="contacts_import_dialog" title="<?php echo $l->t("Import a contacts file"); ?>">
+<div id="form_container">
+<input type="hidden" id="filename" value="<?php echo $_['filename'];?>">
+<input type="hidden" id="path" value="<?php echo $_['path'];?>">
+<input type="hidden" id="progressfile" value="<?php echo md5(session_id()) . '.txt';?>">
+<p style="text-align:center;"><b><?php echo $l->t('Please choose the addressbook'); ?></b>
+<select style="width:100%;" id="contacts" name="contacts">
+<?php
+$contacts_options = OC_Contacts_Addressbook::all(OC_User::getUser());
+$contacts_options[] = array('id'=>'newaddressbook', 'displayname'=>$l->t('create a new addressbook'));
+echo html_select_options($contacts_options, $contacts_options[0]['id'], array('value'=>'id', 'label'=>'displayname'));
+?>
+</select>
+<div id="newaddressbookform" style="display: none;">
+ <input type="text" style="width: 97%;" placeholder="<?php echo $l->t('Name of new addressbook'); ?>" id="newaddressbook" name="newaddressbook">
+</div>
+<input type="button" value="<?php echo $l->t("Import");?>!" id="startimport">
+</div>
+<div id="progressbar_container" style="display: none">
+<p style="text-align:center;"><b><?php echo $l->t('Importing contacts'); ?></b>
+<div id="progressbar"></div>
+<div id="import_done" style="display: none;">
+<p style="text-align:center;"><b><?php echo $l->t('Contacts imported successfully'); ?></b></p>
+<input type="button" value="<?php echo $l->t('Close Dialog'); ?>" id="import_done_button">
+</div>
+</div>
+</div> \ No newline at end of file
diff --git a/apps/contacts/templates/part.no_contacts.php b/apps/contacts/templates/part.no_contacts.php
new file mode 100644
index 00000000000..ab6129cdde7
--- /dev/null
+++ b/apps/contacts/templates/part.no_contacts.php
@@ -0,0 +1,8 @@
+<div id="firstrun">
+You have no contacts in your list.
+ <div id="selections">
+ <input type="button" value="Import contacts" onclick="Contacts.UI.Addressbooks.import()" />
+ <input type="button" value="Add contact" onclick="Contacts.UI.Card.add()" />
+ <input type="button" value="Edit addressbooks" onclick="Contacts.UI.Addressbooks.overview()" />
+ </div>
+</div> \ No newline at end of file
diff --git a/apps/contacts/templates/part.property.FN.php b/apps/contacts/templates/part.property.FN.php
index 83cef94e303..c9e21c20e60 100644
--- a/apps/contacts/templates/part.property.FN.php
+++ b/apps/contacts/templates/part.property.FN.php
@@ -1,9 +1,9 @@
<p id="contacts_details_name" class="contacts_property" data-checksum="<?php echo $_['property']['checksum']; ?>">
- <?php echo $_['property']['value']; ?>
+ <?php echo htmlspecialchars($_['property']['value']); ?>
<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
</p>
<?php if (!isset($_['details'])): ?>
<script>
-$('#leftcontent li.active a').text('<?php echo $_['property']['value']; ?>');
+$('#leftcontent li.active a').text('<?php echo htmlspecialchars($_['property']['value']); ?>');
</script>
<?php endif ?>
diff --git a/apps/contacts/templates/part.property.php b/apps/contacts/templates/part.property.php
index e4010397500..7b23fae45b5 100644
--- a/apps/contacts/templates/part.property.php
+++ b/apps/contacts/templates/part.property.php
@@ -8,21 +8,21 @@
<?php elseif($_['property']['name'] == 'ORG'): ?>
<p class="contacts_property_name"><?php echo $l->t('Organization'); ?></p>
<p class="contacts_property_data">
- <?php echo $_['property']['value']; ?>
+ <?php echo htmlspecialchars($_['property']['value']); ?>
<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
</p>
<?php elseif($_['property']['name'] == 'EMAIL'): ?>
<p class="contacts_property_name"><?php echo $l->t('Email'); ?></p>
<p class="contacts_property_data">
- <?php echo $_['property']['value']; ?>
+ <?php echo htmlspecialchars($_['property']['value']); ?>
<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
</p>
<?php elseif($_['property']['name'] == 'TEL'): ?>
<p class="contacts_property_name"><?php echo (isset($_['property']['parameters']['PREF']) && $_['property']['parameters']['PREF']) ? $l->t('Preferred').' ' : '' ?><?php echo $l->t('Phone'); ?></p>
<p class="contacts_property_data">
- <?php echo $_['property']['value']; ?>
+ <?php echo htmlspecialchars($_['property']['value']); ?>
<?php if(isset($_['property']['parameters']['TYPE']) && !empty($_['property']['parameters']['TYPE'])): ?>
<?php
foreach($_['property']['parameters']['TYPE'] as $type) {
@@ -59,25 +59,25 @@
</p>
<p class="contacts_property_data">
<?php if(!empty($_['property']['value'][0])): ?>
- <?php echo $_['property']['value'][0]; ?><br>
+ <?php echo htmlspecialchars($_['property']['value'][0]); ?><br>
<?php endif; ?>
<?php if(!empty($_['property']['value'][1])): ?>
- <?php echo $_['property']['value'][1]; ?><br>
+ <?php echo htmlspecialchars($_['property']['value'][1]); ?><br>
<?php endif; ?>
<?php if(!empty($_['property']['value'][2])): ?>
- <?php echo $_['property']['value'][2]; ?><br>
+ <?php echo htmlspecialchars($_['property']['value'][2]); ?><br>
<?php endif; ?>
<?php if(!empty($_['property']['value'][3])): ?>
- <?php echo $_['property']['value'][3]; ?><br>
+ <?php echo htmlspecialchars($_['property']['value'][3]); ?><br>
<?php endif; ?>
<?php if(!empty($_['property']['value'][4])): ?>
- <?php echo $_['property']['value'][4]; ?><br>
+ <?php echo htmlspecialchars($_['property']['value'][4]); ?><br>
<?php endif; ?>
<?php if(!empty($_['property']['value'][5])): ?>
- <?php echo $_['property']['value'][5]; ?><br>
+ <?php echo htmlspecialchars($_['property']['value'][5]); ?><br>
<?php endif; ?>
<?php if(!empty($_['property']['value'][6])): ?>
- <?php echo $_['property']['value'][6]; ?>
+ <?php echo htmlspecialchars($_['property']['value'][6]); ?>
<?php endif; ?>
<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
diff --git a/apps/contacts/templates/part.setpropertyform.php b/apps/contacts/templates/part.setpropertyform.php
index 49fa9662146..93ade8faaa7 100644
--- a/apps/contacts/templates/part.setpropertyform.php
+++ b/apps/contacts/templates/part.setpropertyform.php
@@ -5,18 +5,18 @@
<p class="contacts_property_name">
<dl class="contacts_property_data form">
<dt><label for="n1"><?php echo $l->t('Given name'); ?></label></dt>
- <dd><input id="n1" type="text" name="value[1]" value="<?php echo $_['property']['value'][1]; ?>"></dd>
+ <dd><input id="n1" type="text" name="value[1]" value="<?php echo htmlspecialchars($_['property']['value'][1]); ?>"></dd>
<dt><label for="n0"><?php echo $l->t('Family name'); ?></dt>
- <dd><input id="n0" type="text" name="value[0]" value="<?php echo $_['property']['value'][0]; ?>"></dd>
+ <dd><input id="n0" type="text" name="value[0]" value="<?php echo htmlspecialchars($_['property']['value'][0]); ?>"></dd>
<dt><label for="n2"><?php echo $l->t('Additional names'); ?></dt>
- <dd><input id="n2" type="text" name="value[2]" value="<?php echo $_['property']['value'][2]; ?>">
- <input id="n3" type="hidden" name="value[3]" value="<?php echo $_['property']['value'][3]; ?>">
- <input id="n4" type="hidden" name="value[4]" value="<?php echo $_['property']['value'][4]; ?>">
+ <dd><input id="n2" type="text" name="value[2]" value="<?php echo htmlspecialchars($_['property']['value'][2]); ?>">
+ <input id="n3" type="hidden" name="value[3]" value="<?php echo htmlspecialchars($_['property']['value'][3]); ?>">
+ <input id="n4" type="hidden" name="value[4]" value="<?php echo htmlspecialchars($_['property']['value'][4]); ?>">
</dd>
</dl>
</p>
<?php elseif($_['property']['name']=='FN'): ?>
- <p class="contacts_property_data"><input id="fn" type="text" name="value" value="<?php echo $_['property']['value']; ?>"></p>
+ <p class="contacts_property_data"><input id="fn" type="text" name="value" value="<?php echo htmlspecialchars($_['property']['value']); ?>"></p>
<?php elseif($_['property']['name']=='ADR'): ?>
<p class="contacts_property_name"><label for="adr_pobox"><?php echo $l->t('Address'); ?></label></p>
<dl class="contacts_property_data form" id="contacts_addresspart">
@@ -32,60 +32,60 @@
<label for="adr_pobox"><?php echo $l->t('PO Box'); ?></label>
</dt>
<dd>
- <input id="adr_pobox" type="text" name="value[0]" value="<?php echo $_['property']['value'][0] ?>">
+ <input id="adr_pobox" type="text" name="value[0]" value="<?php echo htmlspecialchars($_['property']['value'][0]) ?>">
</dd>
<!-- dt>
<label for="adr_extended"><?php echo $l->t('Extended'); ?></label>
</dt>
<dd>
- <input style="width: 7em;" id="adr_extended" type="text" name="value[1]" value="<?php echo $_['property']['value'][1] ?>">
+ <input style="width: 7em;" id="adr_extended" type="text" name="value[1]" value="<?php echo htmlspecialchars($_['property']['value'][1]) ?>">
</dd -->
<dt>
<label for="adr_street"><?php echo $l->t('Street'); ?></label>
</dt>
<dd>
- <input style="width: 12em;" id="adr_street" type="text" name="value[2]" value="<?php echo $_['property']['value'][2] ?>">
- <label for="adr_extended"><?php echo $l->t('Extended'); ?></label><input style="width: 7em;" id="adr_extended" type="text" name="value[1]" value="<?php echo $_['property']['value'][1] ?>">
+ <input style="width: 12em;" id="adr_street" type="text" name="value[2]" value="<?php echo htmlspecialchars($_['property']['value'][2]) ?>">
+ <label for="adr_extended"><?php echo $l->t('Extended'); ?></label><input style="width: 7em;" id="adr_extended" type="text" name="value[1]" value="<?php echo htmlspecialchars($_['property']['value'][1]) ?>">
</dd>
<dt>
<label for="adr_city"><?php echo $l->t('City'); ?></label>
</dt>
<dd>
- <input style="width: 12em;" id="adr_city" type="text" name="value[3]" value="<?php echo $_['property']['value'][3] ?>">
+ <input style="width: 12em;" id="adr_city" type="text" name="value[3]" value="<?php echo htmlspecialchars($_['property']['value'][3]) ?>">
<label for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label>
- <input style="width: 5em;" id="adr_zipcode" type="text" name="value[5]" value="<?php echo $_['property']['value'][5] ?>">
+ <input style="width: 5em;" id="adr_zipcode" type="text" name="value[5]" value="<?php echo htmlspecialchars($_['property']['value'][5]) ?>">
</dd>
<dt>
<label for="adr_region"><?php echo $l->t('Region'); ?></label>
</dt>
<dd>
- <input id="adr_region" type="text" name="value[4]" value="<?php echo $_['property']['value'][4] ?>">
+ <input id="adr_region" type="text" name="value[4]" value="<?php echo htmlspecialchars($_['property']['value'][4]) ?>">
</dd>
<!-- dt>
<label for="adr_zipcode"><?php echo $l->t('Zipcode'); ?></label>
</dt>
<dd>
- <input style="width: 7em;" id="adr_zipcode" type="text" name="value[5]" value="<?php echo $_['property']['value'][5] ?>">
+ <input style="width: 7em;" id="adr_zipcode" type="text" name="value[5]" value="<?php echo htmlspecialchars($_['property']['value'][5]) ?>">
</dd -->
<dt>
<label for="adr_country"><?php echo $l->t('Country'); ?></label>
</dt>
<dd>
- <input style="width: 25em;" id="adr_country" type="text" name="value[6]" value="<?php echo $_['property']['value'][6] ?>">
+ <input style="width: 25em;" id="adr_country" type="text" name="value[6]" value="<?php echo htmlspecialchars($_['property']['value'][6]) ?>">
</dd>
</dl>
<?php elseif($_['property']['name']=='TEL'): ?>
<p class="contacts_property_name"><label for="tel"><?php echo $l->t('Phone'); ?></label></p>
- <p class="contacts_property_data"><input id="tel" type="phone" name="value" value="<?php echo $_['property']['value'] ?>">
+ <p class="contacts_property_data"><input id="tel" type="phone" name="value" value="<?php echo htmlspecialchars($_['property']['value']) ?>">
<select id="tel_type<?php echo $_['property']['checksum'] ?>" name="parameters[TYPE][]" multiple="multiple" data-placeholder="<?php echo $l->t('Type') ?>">
<?php echo html_select_options($_['phone_types'], isset($_['property']['parameters']['TYPE'])?$_['property']['parameters']['TYPE']:array()) ?>
</select></p>
<?php elseif($_['property']['name']=='EMAIL'): ?>
<p class="contacts_property_name"><label for="email"><?php echo $l->t('Email'); ?></label></p>
- <p class="contacts_property_data"><input id="email" type="text" name="value" value="<?php echo $_['property']['value']; ?>"></p>
+ <p class="contacts_property_data"><input id="email" type="text" name="value" value="<?php echo htmlspecialchars($_['property']['value']); ?>"></p>
<?php elseif($_['property']['name']=='ORG'): ?>
<p class="contacts_property_name"><label for="org"><?php echo $l->t('Organization'); ?></label></p>
- <p class="contacts_property_data"><input id="org" type="text" name="value" value="<?php echo $_['property']['value']; ?>"></p>
+ <p class="contacts_property_data"><input id="org" type="text" name="value" value="<?php echo htmlspecialchars($_['property']['value']); ?>"></p>
<?php endif; ?>
<input id="contacts_setproperty_button" type="submit" value="<?php echo $l->t('Update'); ?>">
</form>
diff --git a/apps/files_pdfviewer/js/viewer.js b/apps/files_pdfviewer/js/viewer.js
index 7f6766c67dd..85bbf09362e 100755
--- a/apps/files_pdfviewer/js/viewer.js
+++ b/apps/files_pdfviewer/js/viewer.js
@@ -49,13 +49,15 @@ showPDFviewer.lastTitle='';
showPDFviewer.loaded=false;
$(document).ready(function(){
- if(location.href.indexOf("files")!=-1) {
- PDFJS.workerSrc = OC.filePath('files_pdfviewer','js','pdfjs/build/pdf.js');
- if(typeof FileActions!=='undefined'){
- FileActions.register('application/pdf','Edit','',function(filename){
- showPDFviewer($('#dir').val(),filename);
- });
- FileActions.setDefault('application/pdf','Edit');
+ if(!$.browser.msie){//doesnt work on IE
+ if(location.href.indexOf("files")!=-1) {
+ PDFJS.workerSrc = OC.filePath('files_pdfviewer','js','pdfjs/build/pdf.js');
+ if(typeof FileActions!=='undefined'){
+ FileActions.register('application/pdf','Edit','',function(filename){
+ showPDFviewer($('#dir').val(),filename);
+ });
+ FileActions.setDefault('application/pdf','Edit');
+ }
}
}
});
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 8d589ea5e2e..8049e9b0ae3 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -6,7 +6,6 @@ OC::$CLASSPATH['OC_Share'] = "apps/files_sharing/lib_share.php";
OC_Hook::connect("OC_Filesystem", "post_delete", "OC_Share", "deleteItem");
OC_Hook::connect("OC_Filesystem", "post_rename", "OC_Share", "renameItem");
OC_Hook::connect("OC_Filesystem", "post_write", "OC_Share", "updateItem");
-OC_Filesystem::registerStorageType("shared", "OC_Filestorage_Shared", array("datadir" => "string"));
OC_Util::addScript("files_sharing", "share");
OC_Util::addScript("3rdparty", "chosen/chosen.jquery.min");
OC_Util::addStyle( 'files_sharing', 'sharing' );
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 4457dddbe15..d01a07447a6 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -2,6 +2,7 @@ $(document).ready(function() {
var shared_status = {};
if (typeof FileActions !== 'undefined') {
FileActions.register('all', 'Share', function(filename) {
+ if (scanFiles.scanning){return;}//workaround to prevent aditional http request block scanning feedback
var icon;
var file = $('#dir').val()+'/'+filename;
if(shared_status[file])
diff --git a/apps/files_sharing/lib_share.php b/apps/files_sharing/lib_share.php
index 0eb0e5bf85d..049a74278b3 100644
--- a/apps/files_sharing/lib_share.php
+++ b/apps/files_sharing/lib_share.php
@@ -89,8 +89,8 @@ class OC_Share {
}
$query->execute(array($uid_owner, $uid, $source, $target, $permissions));
// Clear the folder size cache for the 'Shared' folder
- $clearFolderSize = OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?");
- $clearFolderSize->execute(array($sharedFolder));
+// $clearFolderSize = OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?");
+// $clearFolderSize->execute(array($sharedFolder));
// Emit post_create and post_write hooks to notify of a new file in the user's filesystem
OC_Hook::emit("OC_Filesystem", "post_create", array('path' => $target));
OC_Hook::emit("OC_Filesystem", "post_write", array('path' => $target));
diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php
index b0eaeecf723..cb641e68a84 100644
--- a/apps/files_sharing/sharedstorage.php
+++ b/apps/files_sharing/sharedstorage.php
@@ -22,10 +22,10 @@
require_once( 'lib_share.php' );
-if (!OC_Filesystem::is_dir('/Shared')) {
+if (OC_Filesystem::$loaded and !OC_Filesystem::is_dir('/Shared')) {
OC_Filesystem::mkdir('/Shared');
}
-OC_Filesystem::mount('shared',array('datadir'=>'/'.OC_User::getUser().'/files/Shared'),'/'.OC_User::getUser().'/files/Shared/');
+OC_Filesystem::mount('OC_Filestorage_Shared',array('datadir'=>'/'.OC_User::getUser().'/files/Shared'),'/'.OC_User::getUser().'/files/Shared/');
/**
* Convert target path to source path and pass the function call to the correct storage provider
@@ -168,19 +168,9 @@ class OC_Filestorage_Shared extends OC_Filestorage {
// TODO fill in other components of array
public function stat($path) {
if ($path == "" || $path == "/") {
- $stat["dev"] = "";
- $stat["ino"] = "";
- $stat["mode"] = "";
- $stat["nlink"] = "";
- $stat["uid"] = "";
- $stat["gid"] = "";
- $stat["rdev"] = "";
$stat["size"] = $this->filesize($path);
- $stat["atime"] = $this->fileatime($path);
$stat["mtime"] = $this->filemtime($path);
$stat["ctime"] = $this->filectime($path);
- $stat["blksize"] = "";
- $stat["blocks"] = "";
return $stat;
} else {
$source = $this->getSource($path);
@@ -217,18 +207,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
}
public function getFolderSize($path) {
- // Shared folder sizes are cached separately from the source folder sizes because folders can have different names
- $path = rtrim($path, "/");
- $path = ltrim($path, "/");
- $path = preg_replace('{(/)\1+}', "/", $path);
- $dbpath = rtrim($this->datadir.$path, "/");
- $query = OC_DB::prepare("SELECT size FROM *PREFIX*foldersize WHERE path = ?");
- $size = $query->execute(array($dbpath))->fetchAll();
- if (count($size) > 0) {
- return $size[0]['size'];
- } else {
- return $this->calculateFolderSize($path);
- }
+ return 0; //depricated
}
private function calculateFolderSize($path) {
@@ -249,8 +228,8 @@ class OC_Filestorage_Shared extends OC_Filestorage {
}
if ($size > 0) {
$dbpath = rtrim($this->datadir.$path, "/");
- $query = OC_DB::prepare("INSERT INTO *PREFIX*foldersize VALUES(?,?)");
- $result = $query->execute(array($dbpath, $size));
+// $query = OC_DB::prepare("INSERT INTO *PREFIX*foldersize VALUES(?,?)");
+// $result = $query->execute(array($dbpath, $size));
}
}
return $size;
@@ -263,8 +242,8 @@ class OC_Filestorage_Shared extends OC_Filestorage {
$path = dirname($path);
}
$dbpath = rtrim($this->datadir.$path, "/");
- $query = OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?");
- $result = $query->execute(array($dbpath));
+// $query = OC_DB::prepare("DELETE FROM *PREFIX*/*foldersize*/ WHERE path = ?");
+// $result = $query->execute(array($dbpath));
if ($path != "/" && $path != "") {
$parts = explode("/", $path);
$part = array_pop($parts);
@@ -280,7 +259,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
return true;
}
- public function is_writeable($path) {
+ public function is_writable($path) {
if($path == "" || $path == "/"){
return false;
}elseif (OC_Share::getPermissions($this->datadir.$path) & OC_Share::WRITE) {
@@ -320,8 +299,8 @@ class OC_Filestorage_Shared extends OC_Filestorage {
$ctime = $tempctime;
}
}
- return $ctime;
}
+ return $ctime;
} else {
$source = $this->getSource($path);
if ($source) {
@@ -341,8 +320,8 @@ class OC_Filestorage_Shared extends OC_Filestorage {
$mtime = $tempmtime;
}
}
- return $mtime;
}
+ return $mtime;
} else {
$source = $this->getSource($path);
if ($source) {
@@ -352,27 +331,6 @@ class OC_Filestorage_Shared extends OC_Filestorage {
}
}
- public function fileatime($path) {
- if ($path == "" || $path == "/") {
- $atime = 0;
- if ($dh = $this->opendir($path)) {
- while (($filename = readdir($dh)) !== false) {
- $tempatime = $this->fileatime($filename);
- if ($tempatime > $atime) {
- $atime = $tempatime;
- }
- }
- return $atime;
- }
- } else {
- $source = $this->getSource($path);
- if ($source) {
- $storage = OC_Filesystem::getStorage($source);
- return $storage->fileatime($this->getInternalPath($source));
- }
- }
- }
-
public function file_get_contents($path) {
$source = $this->getSource($path);
if ($source) {
@@ -382,7 +340,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
}
public function file_put_contents($path, $data) {
- if ($this->is_writeable($path)) {
+ if ($this->is_writable($path)) {
$source = $this->getSource($path);
if ($source) {
$storage = OC_Filesystem::getStorage($source);
@@ -426,7 +384,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
if ($root1 !== $root2) {
return false;
// Check if both paths have write permission
- } else if ($this->is_writeable($path1) && $this->is_writeable($path2)) {
+ } else if ($this->is_writable($path1) && $this->is_writable($path2)) {
$oldSource = $this->getSource($path1);
$newSource = $folders['source'].substr($newTarget, strlen($folders['target']));
if ($oldSource) {
@@ -456,7 +414,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
if ($path2 == "" || $path2 == "/") {
// TODO Construct new shared item or should this not be allowed?
} else {
- if ($this->is_writeable($path2)) {
+ if ($this->is_writable($path2)) {
$tmpFile = $this->toTmpFile($path1);
$result = $this->fromTmpFile($tmpFile, $path2);
if ($result) {
@@ -486,7 +444,7 @@ class OC_Filestorage_Shared extends OC_Filestorage {
}
public function fromTmpFile($tmpFile, $path) {
- if ($this->is_writeable($path)) {
+ if ($this->is_writable($path)) {
$source = $this->getSource($path);
if ($source) {
$storage = OC_Filesystem::getStorage($source);
@@ -501,23 +459,10 @@ class OC_Filestorage_Shared extends OC_Filestorage {
}
}
- public function fromUploadedFile($tmpFile, $path) {
- if ($this->is_writeable($path)) {
- $source = $this->getSource($path);
- if ($source) {
- $storage = OC_Filesystem::getStorage($source);
- $result = $storage->fromUploadedFile($tmpFile, $this->getInternalPath($source));
- if ($result) {
- $this->clearFolderSizeCache($path);
- }
- return $result;
- }
- } else {
- return false;
- }
- }
-
public function getMimeType($path) {
+ if ($path == "" || $path == "/") {
+ return 'httpd/unix-directory';
+ }
$source = $this->getSource($path);
if ($source) {
$storage = OC_Filesystem::getStorage($source);
@@ -572,4 +517,4 @@ class OC_Filestorage_Shared extends OC_Filestorage {
}
-?> \ No newline at end of file
+?>
diff --git a/apps/files_texteditor/ajax/loadfile.php b/apps/files_texteditor/ajax/loadfile.php
index 64e016be8c2..8ece844aa29 100644
--- a/apps/files_texteditor/ajax/loadfile.php
+++ b/apps/files_texteditor/ajax/loadfile.php
@@ -33,7 +33,7 @@ $filename = isset($_GET['file']) ? $_GET['file'] : '';
if(!empty($filename))
{
$path = $dir.'/'.$filename;
- if(OC_Filesystem::is_writeable($path))
+ if(OC_Filesystem::is_writable($path))
{
$mtime = OC_Filesystem::filemtime($path);
$filecontents = OC_Filesystem::file_get_contents($path);
diff --git a/apps/files_texteditor/ajax/savefile.php b/apps/files_texteditor/ajax/savefile.php
index 3d0771ea983..589428d1862 100644
--- a/apps/files_texteditor/ajax/savefile.php
+++ b/apps/files_texteditor/ajax/savefile.php
@@ -32,7 +32,6 @@ $filecontents = htmlspecialchars_decode($_POST['filecontents']);
$path = isset($_POST['path']) ? $_POST['path'] : '';
$mtime = isset($_POST['mtime']) ? $_POST['mtime'] : '';
-
if($path != '' && $mtime != '')
{
// Get file mtime
@@ -47,7 +46,7 @@ if($path != '' && $mtime != '')
{
// File same as when opened
// Save file
- if(OC_Filesystem::is_writeable($path))
+ if(OC_Filesystem::is_writable($path))
{
OC_Filesystem::file_put_contents($path, $filecontents);
// Clear statcache
diff --git a/apps/gallery/ajax/galleryOp.php b/apps/gallery/ajax/galleryOp.php
index 0c2674f8859..8df692c7735 100644
--- a/apps/gallery/ajax/galleryOp.php
+++ b/apps/gallery/ajax/galleryOp.php
@@ -45,6 +45,11 @@ function handleGetThumbnails($albumname) {
OC_JSON::checkLoggedIn();
$photo = new OC_Image();
$photo->loadFromFile(OC::$CONFIG_DATADIRECTORY.'/../gallery/'.$albumname.'.png');
+ $offset = 3600 * 24; // 24 hour
+ // calc the string in GMT not localtime and add the offset
+ header("Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT");
+ header('Cache-Control: max-age='.$offset.', must-revalidate');
+ header('Pragma: public');
$photo->show();
}
@@ -54,9 +59,11 @@ function handleGalleryScanning() {
OC_JSON::success(array('albums' => OC_Gallery_Scanner::scan('/')));
}
-function handleFilescan() {
+function handleFilescan($cleanup) {
OC_JSON::checkLoggedIn();
- $pathlist = OC_Gallery_Scanner::find_paths('/');
+ if ($cleanup) OC_Gallery_Album::cleanup();
+ $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '').'/';
+ $pathlist = OC_Gallery_Scanner::find_paths($root);
sort($pathlist);
OC_JSON::success(array('paths' => $pathlist));
}
@@ -67,11 +74,30 @@ function handlePartialCreate($path) {
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;
+ $albums = array();
OC_Gallery_Scanner::scanDir($path, $albums);
OC_JSON::success(array('album_details' => $albums));
}
+function handleStoreSettings($root, $order) {
+ OC_JSON::checkLoggedIn();
+ if (!OC_Filesystem::file_exists($root)) {
+ OC_JSON::error(array('cause' => 'No such file or directory'));
+ return;
+ }
+ if (!OC_Filesystem::is_dir($root)) {
+ OC_JSON::error(array('cause' => $root . ' is not a directory'));
+ return;
+ }
+
+ $current_root = OC_Preferences::getValue(OC_User::getUser(),'gallery', 'root', '/');
+ $root = trim(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));
+}
+
if ($_GET['operation']) {
switch($_GET['operation']) {
case 'rename':
@@ -83,16 +109,19 @@ if ($_GET['operation']) {
OC_JSON::success();
break;
case 'get_covers':
- handleGetThumbnails($_GET['albumname']);
+ handleGetThumbnails(urldecode($_GET['albumname']));
break;
case 'scan':
handleGalleryScanning();
break;
case 'filescan':
- handleFilescan();
+ handleFilescan($_GET['cleanup']);
break;
case 'partial_create':
- handlePartialCreate($_GET['path']);
+ handlePartialCreate(urldecode($_GET['path']));
+ break;
+ case 'store_settings':
+ handleStoreSettings($_GET['root'], $_GET['order']);
break;
default:
OC_JSON::error(array('cause' => 'Unknown operation'));
diff --git a/apps/gallery/ajax/thumbnail.php b/apps/gallery/ajax/thumbnail.php
index 6d25c7a2536..2dfe936d9dd 100644
--- a/apps/gallery/ajax/thumbnail.php
+++ b/apps/gallery/ajax/thumbnail.php
@@ -25,65 +25,14 @@ require_once('../../../lib/base.php');
OC_JSON::checkLoggedIn();
OC_JSON::checkAppEnabled('gallery');
-function CroppedThumbnail($imgSrc,$thumbnail_width,$thumbnail_height) { //$imgSrc is a FILE - Returns an image resource.
- //getting the image dimensions
- if(! function_exists('imagecreatefromjpeg'))
- OC_Log::write('gallery','GD module not installed',OC_Log::ERROR);
-
- list($width_orig, $height_orig) = getimagesize($imgSrc);
- switch (strtolower(substr($imgSrc, strrpos($imgSrc, '.')+1))) {
- case "jpeg":
- case "jpg":
- case "tiff":
- $myImage = imagecreatefromjpeg($imgSrc);
- break;
- case "png":
- $myImage = imagecreatefrompng($imgSrc);
- break;
- default:
- exit();
- }
- if(!$myImage) exit();
- $ratio_orig = $width_orig/$height_orig;
-
- if ($thumbnail_width/$thumbnail_height > $ratio_orig) {
- $new_height = $thumbnail_width/$ratio_orig;
- $new_width = $thumbnail_width;
- } else {
- $new_width = $thumbnail_height*$ratio_orig;
- $new_height = $thumbnail_height;
- }
-
- $x_mid = $new_width/2; //horizontal middle
- $y_mid = $new_height/2; //vertical middle
-
- $process = imagecreatetruecolor(round($new_width), round($new_height));
-
- imagecopyresampled($process, $myImage, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
- $thumb = imagecreatetruecolor($thumbnail_width, $thumbnail_height);
- imagecopyresampled($thumb, $process, 0, 0, ($x_mid-($thumbnail_width/2)), ($y_mid-($thumbnail_height/2)), $thumbnail_width, $thumbnail_height, $thumbnail_width, $thumbnail_height);
-
- imagedestroy($process);
- imagedestroy($myImage);
- return $thumb;
-}
-
-$box_size = 200;
$img = $_GET['img'];
-$imagePath = OC_Filesystem::getLocalFile($img);
-
-if(file_exists($imagePath))
-{
- $image = CroppedThumbnail($imagePath, $box_size, $box_size);
-
- header('Content-Type: image/png');
- $offset = 3600 * 24;
+$image = OC_Gallery_Photo::getThumbnail($img);
+if ($image) {
+ $offset = 3600 * 24; // 24 hour
// calc the string in GMT not localtime and add the offset
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT");
header('Cache-Control: max-age='.$offset.', must-revalidate');
header('Pragma: public');
-
- imagepng($image);
- imagedestroy($image);
+ $image->show();
}
diff --git a/apps/gallery/css/styles.css b/apps/gallery/css/styles.css
index 7872b6445ca..c039cd5ec02 100644
--- a/apps/gallery/css/styles.css
+++ b/apps/gallery/css/styles.css
@@ -12,3 +12,6 @@ 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; }
#scan #scanprogressbar { position:relative; display:inline-block; width:10em; height:1.5em; top:.4em; }
+#g-settings {position: absolute; left 13.5em; top: 0;}
+input[type=button] { -webkit-transition: opacity 0.5s ease-in-out; -moz-transition: opacity 0.5s ease-in-out; -o-transition: opacity 0.5s ease-in-out; opacity: 1}
+input[type=button]:disabled { opacity: 0.5 }
diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js
index e78db221cff..4ddac2f2111 100644
--- a/apps/gallery/js/album_cover.js
+++ b/apps/gallery/js/album_cover.js
@@ -38,10 +38,12 @@ function createNewAlbum() {
var albumCounter = 0;
var totalAlbums = 0;
-function scanForAlbums() {
+function scanForAlbums(cleanup) {
+ cleanup = cleanup?true:false;
var albumCounter = 0;
var totalAlbums = 0;
- $.getJSON('ajax/galleryOp.php?operation=filescan', function(r) {
+ $('#g-scan-button').attr('disabled', 'true');
+ $.getJSON('ajax/galleryOp.php?operation=filescan', {cleanup: cleanup}, function(r) {
if (r.status == 'success') {
totalAlbums = r.paths.length;
@@ -68,6 +70,7 @@ function scanForAlbums() {
} else {
alert('Error occured: no such layer `gallery_list`');
}
+ $('#g-scan-button').attr('disabled', null);
}
});
}
@@ -125,13 +128,13 @@ function galleryRename(name) {
$(this).dialog("close");
return;
}
- $.getJSON("ajax/galleryOp.php", {operation: "rename", oldname: name, newname: newname}, function(r) {
+ $.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");
+ $('#dialog-form').dialog('close');
});
}
@@ -139,10 +142,49 @@ function galleryRename(name) {
{
text: t('gallery', 'Cancel'),
click: function() {
- $( this ).dialog( "close" );
+ $( this ).dialog('close');
}
}
],
});
}
+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');
+ }
+ }
+ ],
+ });
+}
diff --git a/apps/gallery/js/albums.js b/apps/gallery/js/albums.js
index 59efb5b5659..987412f28e0 100644
--- a/apps/gallery/js/albums.js
+++ b/apps/gallery/js/albums.js
@@ -46,20 +46,20 @@ Albums={
var a = Albums.albums[i];
var local=$(displayTemplate);
local.attr('data-album',a.name);
- $(".gallery_album_decoration a.rename", local).click(function(name,event){
+ $(".gallery_album_decoration a.rename", local).bind('click', {name: a.name},function(event){
event.preventDefault();
- galleryRename(name);
- }.bind(null,a.name));
- $(".gallery_album_decoration a.remove", local).click(function(name,event){
+ galleryRename(event.data.name);
+ });
+ $(".gallery_album_decoration a.remove", local).bind('click', {name: a.name},function(event){
event.preventDefault();
- galleryRemove(name);
- }.bind(null,a.name));
- $("a.view", local).attr('href','?view='+a.name);
+ 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='+a.name+'")');
+ $(".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);
@@ -80,6 +80,10 @@ Albums={
$("a.view", element).attr("href", "?view="+new_name);
$("h1", element).text(new_name);
}
+ },
+ clear: function(element) {
+ Albums.albums = new Array();
+ element.innerHTML = '';
}
}
diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php
index 4eb12cc0b81..d1405333ac7 100644
--- a/apps/gallery/lib/album.php
+++ b/apps/gallery/lib/album.php
@@ -31,6 +31,14 @@ class OC_Gallery_Album {
$stmt = OC_DB::prepare('UPDATE *PREFIX*gallery_albums SET album_name=? WHERE uid_owner=? AND album_name=?');
$stmt->execute(array($newname, $owner, $oldname));
}
+
+ 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 = ?';
@@ -69,7 +77,8 @@ class OC_Gallery_Album {
$sql .= ' AND album_path = ?';
$args[] = $path;
}
- $sql .= ' ORDER BY album_name ASC';
+ $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);
diff --git a/apps/gallery/lib/hooks_handlers.php b/apps/gallery/lib/hooks_handlers.php
index 236a4b96a07..046866e5c5d 100644
--- a/apps/gallery/lib/hooks_handlers.php
+++ b/apps/gallery/lib/hooks_handlers.php
@@ -58,12 +58,18 @@ class OC_Gallery_Hooks_Handlers {
return OC_Gallery_Album::find(OC_User::getUser(), null, $path);
}
+ public static function pathInRoot($path) {
+ $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/');
+ return substr($path, 0, strlen($path)>strlen($root)?strlen($root):strlen($path)) == $root;
+ }
+
public static function addPhotoFromPath($params) {
$fullpath = $params[OC_Filesystem::signal_param_path];
if (!self::isPhoto($fullpath)) return;
$path = substr($fullpath, 0, strrpos($fullpath, '/'));
+ if (!self::pathInRoot($path)) return;
OC_Gallery_Scanner::scanDir($path, $albums);
}
@@ -71,8 +77,8 @@ 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)) {
- OC_Gallery_Album::removeByPath($path, OC_User::getUser());
- OC_Gallery_Photo::removeByPath($path.'/%');
+ if(!self::pathInRoot($path)) return;
+ OC_Gallery_Album::removeByPath($path.'/', OC_User::getUser());
} elseif (self::isPhoto($path)) {
OC_Gallery_Photo::removeByPath($path);
}
diff --git a/apps/gallery/lib/photo.php b/apps/gallery/lib/photo.php
index d1fb166aee9..15783cb341a 100644
--- a/apps/gallery/lib/photo.php
+++ b/apps/gallery/lib/photo.php
@@ -21,7 +21,7 @@
*
*/
-class OC_Gallery_Photo{
+class OC_Gallery_Photo {
public static function create($albumId, $img){
$stmt = OC_DB::prepare('INSERT INTO *PREFIX*gallery_photos (album_id, file_path) VALUES (?, ?)');
$stmt->execute(array($albumId, $img));
@@ -65,5 +65,34 @@ class OC_Gallery_Photo{
$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) {
+ $imagePath = OC_Filesystem::getLocalFile($image_name);
+ if(!file_exists($imagePath)) {
+ return null;
+ }
+ $save_dir = OC_Config::getValue("datadirectory").'/'. OC_User::getUser() .'/gallery/';
+ $save_dir .= dirname($image_name). '/';
+ $image_name = basename($image_name);
+ $thumb_file = $save_dir . $image_name;
+ if (file_exists($thumb_file)) {
+ $image = new OC_Image($thumb_file);
+ } else {
+ $image = new OC_Image($imagePath);
+ if ($image->valid()) {
+ $image->centerCrop();
+ $image->resize(200);
+ $image->fixOrientation();
+ if (!is_dir($save_dir)) {
+ mkdir($save_dir, 0777, true);
+ }
+ $image->save($thumb_file);
+ }
+ }
+ if ($image->valid()) {
+ //var_dump($image, $image->resource());
+ return $image;
+ }
+ return null;
+ }
+}
diff --git a/apps/gallery/lib/scanner.php b/apps/gallery/lib/scanner.php
index dfb9edebfea..64efb006ad1 100644
--- a/apps/gallery/lib/scanner.php
+++ b/apps/gallery/lib/scanner.php
@@ -39,12 +39,19 @@ class OC_Gallery_Scanner {
$stmt->execute(array());
}
+ 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 scanDir($path, &$albums) {
$current_album = array('name'=> $path, 'imagesCount' => 0, 'images' => array());
- $current_album['name'] = str_replace('/', '.', str_replace(OC::$CONFIG_DATADIRECTORY, '', $current_album['name']));
- $current_album['name'] = ($current_album['name']==='.') ?
- 'main' :
- trim($current_album['name'],'.');
+ $current_album['name'] = self::createName($current_album['name']);
if ($dh = OC_Filesystem::opendir($path)) {
while (($filename = readdir($dh)) !== false) {
@@ -82,8 +89,10 @@ class OC_Gallery_Scanner {
$file_count = min(count($files), 10);
$thumbnail = imagecreatetruecolor($file_count*200, 200);
for ($i = 0; $i < $file_count; $i++) {
- $imagePath = OC_Filesystem::getLocalFile($files[$i]);
- CroppedThumbnail($imagePath, 200, 200, $thumbnail, $i*200);
+ $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');
}
@@ -106,7 +115,7 @@ class OC_Gallery_Scanner {
if (self::isPhoto($path.$file)) $addpath = TRUE;
}
- if ($addpath) $ret[] = $path;
+ if ($addpath) $ret[] = urlencode($path);
return $ret;
}
diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php
index 4c2fbcfe6c6..7cc7dad3ac6 100644
--- a/apps/gallery/templates/index.php
+++ b/apps/gallery/templates/index.php
@@ -9,7 +9,10 @@ $l = new OC_L10N('gallery');
<div id="controls">
<div id="scan">
<div id="scanprogressbar"></div>
- <input type="button" value="<?php echo $l->t('Rescan');?>" onclick="javascript:scanForAlbums();" />
+ <input type="button" id="g-scan-button" value="<?php echo $l->t('Rescan');?>" onclick="javascript:scanForAlbums();" />
+ </div>
+ <div id="g-settings">
+ <input type="button" id="g-settings-button" value="<?php echo $l->t('Settings');?>" onclick="javascript:settings();"/>
</div>
</div>
<div id="gallery_list">
@@ -28,3 +31,26 @@ $l = new OC_L10N('gallery');
</form>
</div>
+<div id="g-dialog-settings" title="<?php echo $l->t('Settings');?>" style="display:none">
+ <form>
+ <fieldset><?php $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC');?>
+ <label for="name"><?php echo $l->t('Scanning root');?></label>
+ <input type="text" name="g-scanning-root" id="g-scanning-root" class="text ui-widget-content ui-corner-all" value="<?php echo $root;?>" /><br/>
+
+ <label for="sort"><?php echo $l->t('Default sorting'); ?></label>
+ <select id="g-display-order">
+ <option value="ASC"<?php echo $order=='ASC'?'selected':'';?>><?php echo $l->t('Ascending'); ?></option>
+ <option value="DESC"<?php echo $order=='DESC'?'selected':'';?>><?php echo $l->t('Descending'); ?></option>
+ </select><br/>
+<!--
+ <label for="sort"><?php echo $l->t('Thumbnails size'); ?></label>
+ <select>
+ <option value="100">100px</option>
+ <option value="150">150px</option>
+ <option value="200">200px</option>
+ </select>
+ -->
+ </fieldset>
+ </form>
+</div>
+
diff --git a/apps/media/ajax/api.php b/apps/media/ajax/api.php
index 4a93e84910e..ac6739a1386 100644
--- a/apps/media/ajax/api.php
+++ b/apps/media/ajax/api.php
@@ -70,10 +70,10 @@ if($arguments['action']){
case 'scan':
OC_DB::beginTransaction();
set_time_limit(0); //recursive scan can take a while
- $path=$arguments['path'];
- echo OC_MEDIA_SCANNER::scanFolder($path);
+ $eventSource=new OC_EventSource();
+ OC_MEDIA_SCANNER::scanCollection($eventSource);
+ $eventSource->close();
OC_DB::commit();
- flush();
break;
case 'scanFile':
echo (OC_MEDIA_SCANNER::scanFile($arguments['path']))?'true':'false';
@@ -127,29 +127,9 @@ if($arguments['action']){
OC_Filesystem::readfile($arguments['path']);
exit;
case 'find_music':
- OC_JSON::encodedPrint(findMusic());
+ OC_JSON::encodedPrint(OC_FileCache::searchByMime('audio'));
exit;
}
}
-function findMusic($path=''){
- $music=array();
- $dh=OC_Filesystem::opendir($path);
- if($dh){
- while($filename=readdir($dh)){
- if($filename[0]!='.'){
- $file=$path.'/'.$filename;
- if(OC_Filesystem::is_dir($file)){
- $music=array_merge($music,findMusic($file));
- }else{
- if(OC_MEDIA_SCANNER::isMusic($filename)){
- $music[]=$file;
- }
- }
- }
- }
- }
- return $music;
-}
-
?>
diff --git a/apps/media/js/scanner.js b/apps/media/js/scanner.js
index ed2046dd7a6..0baa9db419a 100644
--- a/apps/media/js/scanner.js
+++ b/apps/media/js/scanner.js
@@ -1,90 +1,44 @@
Scanner={
songsFound:0,
+ eventSource:null,
songsScanned:0,
- songsChecked:0,
- startTime:null,
- endTime:null,
- stopScanning:false,
- currentIndex:0,
- songs:[],
findSongs:function(ready){
$.getJSON(OC.linkTo('media','ajax/api.php')+'?action=find_music',function(songs){
Scanner.songsFound=songs.length;
- Scanner.currentIndex=-1
if(ready){
-
ready(songs)
}
});
},
- scanFile:function(path,ready){
- path=encodeURIComponent(path);
- $.getJSON(OC.linkTo('media','ajax/api.php')+'?action=get_path_info&path='+path,function(song){
- Scanner.songsChecked++;
- if(ready){
- ready(song);
- }
- if(song){//do this after the ready call so we dont hold up the next ajax call
- var artistId=song.song_artist;
- Scanner.songsScanned++;
- $('#scan span.songCount').text(Scanner.songsScanned);
- var progress=(Scanner.songsChecked/Scanner.songsFound)*100;
- $('#scanprogressbar').progressbar('value',progress)
- Collection.addSong(song);
- }
- });
- },
scanCollection:function(ready){
$('#scanprogressbar').progressbar({
value:0,
});
$('#scanprogressbar').show();
- Scanner.songsChecked=0;
- Scanner.currentIndex=0;
Scanner.songsScanned=0;
- Scanner.startTime=new Date().getTime()/1000;
- Scanner.findSongs(function(songs){
- Scanner.songs=songs;
- Scanner.start(function(){
- $('#scan input.start').show();
- $('#scan input.stop').hide();
- $('#scanprogressbar').hide();
- Collection.display();
- if(ready){
- ready();
- }
- });
+ Scanner.eventSource=new OC.EventSource(OC.linkTo('media','ajax/api.php'),{action:'scan'});
+ Scanner.eventSource.listen('count',function(total){
+ Scanner.songsFound=total;
+ });
+ Scanner.eventSource.listen('scanned',function(data){
+ Scanner.songsScanned=data.count;
+ $('#scan span.songCount').text(Scanner.songsScanned);
+ var progress=(Scanner.songsScanned/Scanner.songsFound)*100;
+ $('#scanprogressbar').progressbar('value',progress)
});
+ Scanner.eventSource.listen('done',function(count){
+ $('#scan input.start').show();
+ $('#scan input.stop').hide();
+ $('#scanprogressbar').hide();
+ Collection.load(Collection.display)
+ if(ready){
+ ready();
+ }
+ });
+ $('#scancount').show();
},
stop:function(){
- Scanner.stopScanning=true;
- },
- start:function(ready){
- Scanner.stopScanning=false;
- $('#scancount').show();
- var scanSong=function(){
- if(!Scanner.stopScanning && Scanner.currentIndex<=Scanner.songs.length){
- Scanner.scanFile(Scanner.songs[Scanner.currentIndex],scanSong)
- }else if(!Scanner.stopScanning){
- Scanner.endTime=new Date().getTime()/1000;
- if(ready){
- ready();
- ready=null;//only call ready once
- }
- }
- Scanner.currentIndex++;
- }
- scanSong();
- scanSong();
+ Scanner.close();
},
- toggle:function(){
- if(Scanner.stopScanning){
- Scanner.start();
- $('#scan input.stop').val(t('media','Pause'));
- }else{
- Scanner.stop();
- $('#scan input.stop').val(t('media','Resume'));
- }
- }
}
diff --git a/apps/media/lib_scanner.php b/apps/media/lib_scanner.php
index 8b659c73b6e..ea594ee8e3c 100644
--- a/apps/media/lib_scanner.php
+++ b/apps/media/lib_scanner.php
@@ -33,32 +33,22 @@ class OC_MEDIA_SCANNER{
/**
* scan a folder for music
- * @param string $path
+ * @param OC_EventSource eventSource (optional)
* @return int the number of songs found
*/
- public static function scanFolder($path){
- if (OC_Filesystem::is_dir($path)) {
- $songs=0;
- if ($dh = OC_Filesystem::opendir($path)) {
- while (($filename = readdir($dh)) !== false) {
- if($filename<>'.' and $filename<>'..' and substr($filename,0,1)!='.'){
- $file=$path.'/'.$filename;
- if(OC_Filesystem::is_dir($file)){
- $songs+=self::scanFolder($file);
- }elseif(OC_Filesystem::is_file($file)){
- $data=self::scanFile($file);
- if($data){
- $songs++;
- }
- }
- }
- }
+ public static function scanCollection($eventSource=null){
+ $music=OC_FileCache::searchByMime('audio');
+ $eventSource->send('count',count($music));
+ $songs=0;
+ foreach($music as $file){
+ self::scanFile($file);
+ $songs++;
+ if($eventSource){
+ $eventSource->send('scanned',array('file'=>$file,'count'=>$songs));
}
- }elseif(OC_Filesystem::is_file($path)){
- $songs=1;
- self::scanFile($path);
- }else{
- $songs=0;
+ }
+ if($eventSource){
+ $eventSource->send('done',$songs);
}
return $songs;
}
diff --git a/core/img/actions/add.png b/core/img/actions/add.png
new file mode 100644
index 00000000000..db1b1970f17
--- /dev/null
+++ b/core/img/actions/add.png
Binary files differ
diff --git a/core/img/actions/add.svg b/core/img/actions/add.svg
new file mode 100644
index 00000000000..29994747c33
--- /dev/null
+++ b/core/img/actions/add.svg
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.0"
+ width="22"
+ height="22"
+ id="svg2406"
+ inkscape:version="0.48.2 r9819"
+ sodipodi:docname="add.svg">
+ <metadata
+ id="metadata3125">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview3123"
+ showgrid="false"
+ inkscape:zoom="10.727273"
+ inkscape:cx="11.843286"
+ inkscape:cy="14.728814"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2406" />
+ <defs
+ id="defs2408">
+ <linearGradient
+ id="linearGradient2264">
+ <stop
+ id="stop2266"
+ style="stop-color:#d7e866;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop2268"
+ style="stop-color:#8cab2a;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="24.103895"
+ y1="15.180944"
+ x2="24.103895"
+ y2="34.224861"
+ id="linearGradient2401"
+ xlink:href="#linearGradient2264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.8988874,0,0,0.8934652,-10.463705,-9.5659718)" />
+ <linearGradient
+ id="linearGradient4222">
+ <stop
+ id="stop4224"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4226"
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="24.138529"
+ y1="6.5316639"
+ x2="24.138529"
+ y2="45.690399"
+ id="linearGradient2398"
+ xlink:href="#linearGradient4222"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.5399382,0,0,0.5366811,-1.8489228,-1.5061978)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4222"
+ id="linearGradient3128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.5399382,0,0,0.5366811,-1.7557025,-1.7858588)"
+ x1="24.138529"
+ y1="6.5316639"
+ x2="24.138529"
+ y2="45.690399" />
+ </defs>
+ <path
+ inkscape:connector-curvature="0"
+ style="opacity:0.40000000000000002;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3128);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+ id="path2272"
+ d="m 8.5932204,8.2151575 0,-5.9948184 4.9999996,0 0,5.9948184 6,0 0,5.0051815 -6,0 0,6 -4.9999996,0 0,-6 -6,0 0,-5.0051815 6,0 z" />
+</svg>
diff --git a/core/img/actions/mail.png b/core/img/actions/mail.png
new file mode 100644
index 00000000000..ea811ac5453
--- /dev/null
+++ b/core/img/actions/mail.png
Binary files differ
diff --git a/core/img/actions/mail.svg b/core/img/actions/mail.svg
new file mode 100644
index 00000000000..54a24faf85d
--- /dev/null
+++ b/core/img/actions/mail.svg
@@ -0,0 +1,345 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.0"
+ width="22"
+ height="22"
+ id="svg2575"
+ inkscape:version="0.48.2 r9819"
+ sodipodi:docname="mail_new.svg"
+ inkscape:export-filename="/home/tol/tanghus-owncloud/core/img/actions/mail.png"
+ inkscape:export-xdpi="65.454544"
+ inkscape:export-ydpi="65.454544">
+ <metadata
+ id="metadata59">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview57"
+ showgrid="false"
+ inkscape:zoom="10.727273"
+ inkscape:cx="11"
+ inkscape:cy="11"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2575" />
+ <defs
+ id="defs2577">
+ <linearGradient
+ id="linearGradient3943">
+ <stop
+ id="stop3945"
+ style="stop-color:#ffffff;stop-opacity:0.40000001"
+ offset="0" />
+ <stop
+ id="stop3947"
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="24.138529"
+ y1="7.0478544"
+ x2="24.138529"
+ y2="47.272728"
+ id="linearGradient2560"
+ xlink:href="#linearGradient3943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.2817955,0,0,0.2800956,-16.058706,13.975052)" />
+ <linearGradient
+ id="linearGradient2264">
+ <stop
+ id="stop2266"
+ style="stop-color:#d7e866;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop2268"
+ style="stop-color:#8cab2a;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="24.103895"
+ y1="15.168831"
+ x2="24.103895"
+ y2="32.485161"
+ id="linearGradient2558"
+ xlink:href="#linearGradient2264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4691323,0,0,0.4663025,-20.554789,9.7686284)" />
+ <linearGradient
+ id="linearGradient3495-841-851-719">
+ <stop
+ id="stop4120"
+ style="stop-color:#1e1e1e;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4122"
+ style="stop-color:#1e1e1e;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3333-314-917-262">
+ <stop
+ id="stop4102"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4104"
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2490-495-150-777">
+ <stop
+ id="stop4108"
+ style="stop-color:#787878;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4110"
+ style="stop-color:#b4b4b4;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3242-967-12-570-862-307">
+ <stop
+ id="stop4498"
+ style="stop-color:#f2f2f2;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4502"
+ style="stop-color:#dbdbdb;stop-opacity:1"
+ offset="0.87426931" />
+ <stop
+ id="stop4504"
+ style="stop-color:#999999;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5060-6-986-342-61">
+ <stop
+ id="stop4080"
+ style="stop-color:#1e1e1e;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4082"
+ style="stop-color:#1e1e1e;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <radialGradient
+ cx="605.71429"
+ cy="486.64789"
+ r="117.14286"
+ fx="605.71429"
+ fy="486.64789"
+ id="radialGradient2724-226-535-494"
+ xlink:href="#linearGradient5060-6-986-342-61"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-6.553443e-2,0,0,2.470588e-2,-21.829255,10.577352)" />
+ <linearGradient
+ id="linearGradient5060-6-252-476-367">
+ <stop
+ id="stop4074"
+ style="stop-color:#1e1e1e;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4076"
+ style="stop-color:#1e1e1e;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <radialGradient
+ cx="605.71429"
+ cy="486.64789"
+ r="117.14286"
+ fx="605.71429"
+ fy="486.64789"
+ id="radialGradient2722-303-187-273"
+ xlink:href="#linearGradient5060-6-252-476-367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(6.553443e-2,0,0,2.470588e-2,-69.175497,10.577352)" />
+ <linearGradient
+ id="linearGradient5048-7-668-934-892">
+ <stop
+ id="stop4066"
+ style="stop-color:#1e1e1e;stop-opacity:0"
+ offset="0" />
+ <stop
+ id="stop4068"
+ style="stop-color:#1e1e1e;stop-opacity:1"
+ offset="0.5" />
+ <stop
+ id="stop4070"
+ style="stop-color:#1e1e1e;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="302.85715"
+ y1="366.64789"
+ x2="302.85715"
+ y2="609.50507"
+ id="linearGradient2720-766-26-906"
+ xlink:href="#linearGradient5048-7-668-934-892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(6.553443e-2,0,0,2.470588e-2,-69.188394,10.577352)" />
+ <linearGradient
+ x1="23.903786"
+ y1="35.75"
+ x2="23.903786"
+ y2="16.00676"
+ id="linearGradient3411"
+ xlink:href="#linearGradient3495-841-851-719"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4657385,0,0,0.4850945,0.8223379,-3.5686279)" />
+ <linearGradient
+ x1="23.928667"
+ y1="30.773148"
+ x2="23.928667"
+ y2="45.530704"
+ id="linearGradient3414"
+ xlink:href="#linearGradient3495-841-851-719"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4657385,0,0,0.4850945,0.8223378,-4.102232)" />
+ <linearGradient
+ x1="25.57654"
+ y1="15.000002"
+ x2="25.57654"
+ y2="44.00053"
+ id="linearGradient3417"
+ xlink:href="#linearGradient3333-314-917-262"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4418603,0,0,0.4642857,1.3953491,-3.696433)" />
+ <linearGradient
+ x1="19.875452"
+ y1="10.389797"
+ x2="19.875452"
+ y2="45.60001"
+ id="linearGradient3420"
+ xlink:href="#linearGradient3242-967-12-570-862-307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4666666,0,0,0.4545454,0.7999999,-2.7272752)" />
+ <linearGradient
+ x1="28.103424"
+ y1="45.000065"
+ x2="28.103424"
+ y2="14.038458"
+ id="linearGradient3422"
+ xlink:href="#linearGradient2490-495-150-777"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4666666,0,0,0.5006418,0.7999998,-4.7785529)" />
+ <linearGradient
+ x1="23.903786"
+ y1="35.75"
+ x2="23.903786"
+ y2="16.00676"
+ id="linearGradient2440"
+ xlink:href="#linearGradient3495-841-851-719"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4657385,0,0,0.4850945,-0.1776621,-4.5686279)" />
+ <linearGradient
+ x1="23.928667"
+ y1="30.773148"
+ x2="23.928667"
+ y2="45.530704"
+ id="linearGradient2443"
+ xlink:href="#linearGradient3495-841-851-719"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4657385,0,0,0.4850945,-0.1776622,-5.102232)" />
+ <linearGradient
+ x1="25.57654"
+ y1="15.000002"
+ x2="25.57654"
+ y2="44.00053"
+ id="linearGradient2446"
+ xlink:href="#linearGradient3333-314-917-262"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4418603,0,0,0.4642857,0.3953491,-4.696433)" />
+ <linearGradient
+ x1="19.875452"
+ y1="10.389797"
+ x2="19.875452"
+ y2="45.60001"
+ id="linearGradient2449"
+ xlink:href="#linearGradient3242-967-12-570-862-307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4666666,0,0,0.4545454,-0.10677976,-1.5832074)" />
+ <linearGradient
+ x1="28.103424"
+ y1="45.000065"
+ x2="28.103424"
+ y2="14.038458"
+ id="linearGradient2451"
+ xlink:href="#linearGradient2490-495-150-777"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4666666,0,0,0.5006418,-0.10677986,-3.6344851)" />
+ <linearGradient
+ x1="24.103895"
+ y1="15.168831"
+ x2="24.103895"
+ y2="32.485161"
+ id="linearGradient2457"
+ xlink:href="#linearGradient2264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.4691323,0,0,0.4663025,-20.554789,9.7686284)" />
+ <linearGradient
+ x1="24.138529"
+ y1="7.0478544"
+ x2="24.138529"
+ y2="47.272728"
+ id="linearGradient2459"
+ xlink:href="#linearGradient3943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.2817955,0,0,0.2800956,-16.058706,13.975052)" />
+ </defs>
+ <rect
+ width="21"
+ height="15"
+ rx="0.46666664"
+ ry="0.45454553"
+ x="0.59322035"
+ y="3.6440678"
+ id="rect2396"
+ style="fill:url(#linearGradient2449);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2451);stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ width="19"
+ height="13"
+ rx="0.036476243"
+ ry="0.035004593"
+ x="1.5"
+ y="2.5"
+ id="rect3331"
+ style="opacity:0.4;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2446);stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ d="M 8.2139564,10.336975 L 1.1222032,16.468629 M 13.709509,10.325653 L 20.811476,16.468629"
+ id="path3341"
+ style="opacity:0.5;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2443);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ d="M 0.8491563,3.0457658 L 11.000062,12.181817 L 20.937208,3.0457658"
+ id="path3493"
+ style="opacity:0.5;fill:none;fill-rule:evenodd;stroke:url(#linearGradient2440);stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+</svg>
diff --git a/db_structure.xml b/db_structure.xml
index ddb8c44d19d..8e59a59c6e5 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -43,11 +43,20 @@
<table>
- <name>*dbprefix*foldersize</name>
+ <name>*dbprefix*fscache</name>
<declaration>
<field>
+ <name>id</name>
+ <autoincrement>1</autoincrement>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+
+ <field>
<name>path</name>
<type>text</type>
<default></default>
@@ -56,6 +65,33 @@
</field>
<field>
+ <name>parent</name>
+ <type>integer</type>
+ <default>
+ </default>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>name</name>
+ <type>text</type>
+ <default>
+ </default>
+ <notnull>true</notnull>
+ <length>512</length>
+ </field>
+
+ <field>
+ <name>user</name>
+ <type>text</type>
+ <default>
+ </default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
<name>size</name>
<type>integer</type>
<default></default>
@@ -63,12 +99,93 @@
<length>4</length>
</field>
- <index>
- <name>path_index</name>
+ <field>
+ <name>ctime</name>
+ <type>integer</type>
+ <default>
+ </default>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>mtime</name>
+ <type>integer</type>
+ <default>
+ </default>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+
+ <field>
+ <name>mimetype</name>
+ <type>text</type>
+ <default>
+ </default>
+ <notnull>true</notnull>
+ <length>32</length>
+ </field>
+
+ <field>
+ <name>mimepart</name>
+ <type>text</type>
+ <default>
+ </default>
+ <notnull>true</notnull>
+ <length>32</length>
+ </field>
+
+ <field>
+ <name>encrypted</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <length>1</length>
+ </field>
+
+ <field>
+ <name>versioned</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <length>1</length>
+ </field>
+
+ <field>
+ <name>writable</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <length>1</length>
+ </field>
+
+ <!--<index>
+ <name>fscache_path_index</name>
+ <unique>true</unique>
<field>
<name>path</name>
<sorting>ascending</sorting>
</field>
+ </index>-->
+
+ <index>
+ <name>parent_index</name>
+ <field>
+ <name>parent</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ <index>
+ <name>parent_name_index</name>
+ <field>
+ <name>parent</name>
+ <sorting>ascending</sorting>
+ </field>
+ <field>
+ <name>name</name>
+ <sorting>ascending</sorting>
+ </field>
</index>
</declaration>
diff --git a/files/ajax/scan.php b/files/ajax/scan.php
new file mode 100644
index 00000000000..565275911b4
--- /dev/null
+++ b/files/ajax/scan.php
@@ -0,0 +1,37 @@
+<?php
+
+require_once '../../lib/base.php';
+
+set_time_limit(0);//scanning can take ages
+
+$force=isset($_GET['force']) and $_GET['force']=='true';
+$checkOnly=isset($_GET['checkonly']) and $_GET['checkonly']=='true';
+
+if(!$checkOnly){
+ $eventSource=new OC_EventSource();
+}
+
+
+//create the file cache if necesary
+if($force or !OC_FileCache::inCache('')){
+ if(!$checkOnly){
+ OC_DB::beginTransaction();
+ OC_FileCache::scan('',$eventSource);
+ OC_DB::commit();
+ $eventSource->send('success',true);
+ }else{
+ OC_JSON::success(array('data'=>array('done'=>true)));
+ exit;
+ }
+}else{
+ if($checkOnly){
+ OC_JSON::success(array('data'=>array('done'=>false)));
+ exit;
+ }
+ if(isset($eventSource)){
+ $eventSource->send('success',false);
+ }else{
+ exit;
+ }
+}
+$eventSource->close(); \ No newline at end of file
diff --git a/files/ajax/upload.php b/files/ajax/upload.php
index 5f0f68d9531..241edc216ff 100644
--- a/files/ajax/upload.php
+++ b/files/ajax/upload.php
@@ -47,7 +47,7 @@ if(strpos($dir,'..') === false){
$fileCount=count($files['name']);
for($i=0;$i<$fileCount;$i++){
$target=stripslashes($dir) . $files['name'][$i];
- if(OC_Filesystem::fromUploadedFile($files['tmp_name'][$i],$target)){
+ 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]);
}
}
diff --git a/files/css/files.css b/files/css/files.css
index 6c4d603b4a6..512e462cb6f 100644
--- a/files/css/files.css
+++ b/files/css/files.css
@@ -27,7 +27,8 @@
.file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; position:absolute; left:0; top:0; width:100%; cursor:pointer;}
.file_upload_filename.active { border-bottom-right-radius:0 }
-.file_upload_filename { z-index:100; cursor:pointer; border-top-left-radius:0; border-bottom-left-radius:0; padding:.3em; }
+.file_upload_filename { position: relative; z-index:100; padding-left: 0.8em; padding-right: 0.8em; cursor:pointer; border-top-left-radius:0; border-bottom-left-radius:0; }
+.file_upload_filename img { position: absolute; top: 0.4em; left: 0.4em; }
.file_upload_form, .file_upload_wrapper, .file_upload_start, .file_upload_filename, #file_upload_submit { cursor:pointer; }
@@ -75,4 +76,6 @@ a.action>img{ max-height:16px; max-width:16px; }
/* add breadcrumb divider to the File item in navigation panel */
#navigation>ul>li:first-child { background:url('../../core/img/breadcrumb-start.svg') no-repeat 12.5em 0px; width:12.5em; padding-right:1em; position:fixed; }
-#navigation>ul>li:first-child+li { padding-top:2.9em; } \ No newline at end of file
+#navigation>ul>li:first-child+li { padding-top:2.9em; }
+
+#scanning-message{ top:40%; left:40%; position:absolute; display:none }
diff --git a/files/index.php b/files/index.php
index fbf7a4901a1..f166790ba9c 100644
--- a/files/index.php
+++ b/files/index.php
@@ -94,7 +94,7 @@ $tmpl = new OC_Template( "files", "index", "user" );
$tmpl->assign( "fileList", $list->fetchPage() );
$tmpl->assign( "breadcrumb", $breadcrumbNav->fetchPage() );
$tmpl->assign( 'dir', $dir);
-$tmpl->assign( 'readonly', !OC_Filesystem::is_writeable($dir));
+$tmpl->assign( 'readonly', !OC_Filesystem::is_writable($dir));
$tmpl->assign( "files", $files );
$tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize);
$tmpl->assign( 'uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
diff --git a/files/js/files.js b/files/js/files.js
index 7c04245c223..bebcf4e97a4 100644
--- a/files/js/files.js
+++ b/files/js/files.js
@@ -336,8 +336,37 @@ $(document).ready(function() {
$('#new>a').click();
});
});
+
+ //check if we need to scan the filesystem
+ $.get(OC.filePath('files','ajax','scan.php'),{checkonly:'true'}, function(response) {
+ if(response.data.done){
+ scanFiles();
+ }
+ }, "json");
});
+function scanFiles(force){
+ force=!!force; //cast to bool
+ scanFiles.scanning=true;
+ $('#scanning-message').show();
+ $('#fileList').remove();
+ var scannerEventSource=new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force});
+ scanFiles.cancel=scannerEventSource.close.bind(scannerEventSource);
+ scannerEventSource.listen('scanning',function(data){
+ $('#scan-count').text(data.count+' files scanned');
+ $('#scan-current').text(data.file+'/');
+ });
+ scannerEventSource.listen('success',function(success){
+ scanFiles.scanning=false;
+ if(success){
+ window.location.reload();
+ }else{
+ alert('error while scanning');
+ }
+ });
+}
+scanFiles.scanning=false;
+
function boolOperationFinished(data, callback) {
result = jQuery.parseJSON(data.responseText);
if(result.status == 'success'){
diff --git a/files/templates/index.php b/files/templates/index.php
index 9ebfa41cef2..7e9505dec2f 100644
--- a/files/templates/index.php
+++ b/files/templates/index.php
@@ -3,13 +3,10 @@
<?php if (!isset($_['readonly']) || !$_['readonly']):?>
<div class="actions">
<div id='new' class='button'>
- <a>
- <?php echo $l->t('New');?>
- </a>
+ <a><?php echo $l->t('New');?></a>
<ul class="popup popupTop">
<li style="background-image:url('<?php echo mimetype_icon('text/plain') ?>')" data-type='file'><p><?php echo $l->t('Text file');?></p></li>
<li style="background-image:url('<?php echo mimetype_icon('dir') ?>')" data-type='folder'><p><?php echo $l->t('Folder');?></p></li>
- <!-- <li style="background-image:url('<?php echo mimetype_icon('dir') ?>')" data-type='web'><p><?php echo $l->t('From the web');?></p></li> -->
</ul>
</div>
<div class="file_upload_wrapper svg">
@@ -17,7 +14,7 @@
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload">
<input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
<input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir">
- <button class="file_upload_filename"><img class='svg action' alt="Upload" src="<?php echo image_path("core", "actions/upload.svg"); ?>" /></button>
+ <button class="file_upload_filename">&nbsp;<img class='svg action' alt="Upload" src="<?php echo image_path("core", "actions/upload.svg"); ?>" /></button>
<input class="file_upload_start" type="file" name='files[]'/>
<a href="#" class="file_upload_button_wrapper" onclick="return false;" title="<?php echo $l->t('Upload'); echo ' max. '.$_['uploadMaxHumanFilesize'] ?>"></a>
<iframe name="file_upload_target_1" class='file_upload_target' src=""></iframe>
@@ -60,3 +57,11 @@
<?php echo $l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.');?>
</p>
</div>
+<div id="scanning-message">
+ <h3>
+ <?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></spann>
+ </h3>
+ <p>
+ <?php echo $l->t('Current scanning');?> <span id='scan-current'></spann>
+ </p>
+</div>
diff --git a/files/templates/part.list.php b/files/templates/part.list.php
index a364862119d..b117d81a1a5 100644
--- a/files/templates/part.list.php
+++ b/files/templates/part.list.php
@@ -1,5 +1,5 @@
<?php foreach($_['files'] as $file):
- $write = ($file['writeable']) ? 'true' : 'false';
+ $write = ($file['writable']) ? 'true' : 'false';
$simple_file_size = simple_file_size($file['size']);
$simple_size_color = intval(200-$file['size']/(1024*1024)*2); // the bigger the file, the darker the shade of grey; megabytes*2
if($simple_size_color<0) $simple_size_color = 0;
@@ -10,8 +10,8 @@
$name = str_replace('%2F','/', $name);
$directory = str_replace('+','%20',urlencode($file['directory']));
$directory = str_replace('%2F','/', $directory); ?>
- <tr data-file="<?php echo $name;?>" data-type="<?php echo ($file['type'] == 'dir')?'dir':'file'?>" data-mime="<?php echo $file['mime']?>" data-size='<?php echo $file['size'];?>' data-write='<?php echo $write;?>'>
- <td class="filename svg" style="background-image:url(<?php if($file['type'] == 'dir') echo mimetype_icon('dir'); else echo mimetype_icon($file['mime']); ?>)">
+ <tr data-file="<?php echo $name;?>" data-type="<?php echo ($file['type'] == 'dir')?'dir':'file'?>" data-mime="<?php echo $file['mimetype']?>" data-size='<?php echo $file['size'];?>' data-write='<?php echo $write;?>'>
+ <td class="filename svg" style="background-image:url(<?php if($file['type'] == 'dir') echo mimetype_icon('dir'); else echo mimetype_icon($file['mimetype']); ?>)">
<?php if(!isset($_['readonly']) || !$_['readonly']) { ?><input type="checkbox" /><?php } ?>
<a class="name" href="<?php if($file['type'] == 'dir') echo $_['baseURL'].$directory.'/'.$name; else echo $_['downloadURL'].$directory.'/'.$name; ?>" title="">
<span class="nametext">
diff --git a/lib/app.php b/lib/app.php
index 37a99823e3d..22d18b17eec 100644
--- a/lib/app.php
+++ b/lib/app.php
@@ -403,4 +403,22 @@ class OC_App{
include OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/update.php';
}
}
+
+ /**
+ * @param string appid
+ * @return OC_FilesystemView
+ */
+ public static function getStorage($appid){
+ if(OC_App::isEnabled($appid)){//sanity check
+ if(OC_User::isLoggedIn()){
+ return new OC_FilesystemView('/'.OC_User::getUser().'/'.$appid);
+ }else{
+ OC_Log::write('core','Can\'t get app storage, app, user not logged in',OC_Log::ERROR);
+ return false;
+ }
+ }else{
+ OC_Log::write('core','Can\'t get app storage, app '.$appid.' not enabled',OC_Log::ERROR);
+ false;
+ }
+ }
}
diff --git a/lib/base.php b/lib/base.php
index 4da17c70a57..5b8eeb746b1 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -216,9 +216,6 @@ class OC{
OC_User::useBackend( OC_Config::getValue( "userbackend", "database" ));
OC_Group::setBackend( OC_Config::getValue( "groupbackend", "database" ));
- // Was in required file ... put it here
- OC_Filesystem::registerStorageType('local','OC_Filestorage_Local',array('datadir'=>'string'));
-
// Set up file system unless forbidden
global $RUNTIME_NOSETUPFS;
if(!$RUNTIME_NOSETUPFS ){
diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php
index 139c6b784b1..bb03851e39d 100644
--- a/lib/connector/sabre/directory.php
+++ b/lib/connector/sabre/directory.php
@@ -116,11 +116,11 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
* @return array
*/
public function getQuotaInfo() {
-
+ $rootInfo=OC_FileCache::get('');
return array(
- OC_Filesystem::filesize('/'),
+ $rootInfo['size'],
OC_Filesystem::free_space()
- );
+ );
}
diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php
index ace572a1ee3..b8b675c1203 100644
--- a/lib/connector/sabre/node.php
+++ b/lib/connector/sabre/node.php
@@ -92,6 +92,19 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
}
+ /**
+ * sets the last modification time of the file (mtime) to the value given
+ * in the second parameter or to now if the second param is empty.
+ * Even if the modification time is set to a custom value the access time is set to now.
+ */
+ public function setLastModifiedTime($mtime) {
+ OC_Filesystem::setFileMtime($this->path, $mtime);
+ }
+
+ public function endsWith( $str, $sub ) {
+ return ( substr( $str, strlen( $str ) - strlen( $sub ) ) === $sub );
+ }
+
/**
* Updates properties on this node,
*
@@ -110,13 +123,16 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
}
}
else {
- if(!array_key_exists( $propertyName, $existing )){
- $query = OC_DB::prepare( 'INSERT INTO *PREFIX*properties (userid,propertypath,propertyname,propertyvalue) VALUES(?,?,?,?)' );
- $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue ));
- }
- else{
- $query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertyvalue = ? WHERE userid = ? AND propertypath = ? AND propertyname = ?' );
- $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName ));
+ if( $this->endsWith( $propertyName, "modificationTime")) {
+ $this->setLastModifiedTime($propertyValue);
+ } else {
+ if(!array_key_exists( $propertyName, $existing )){
+ $query = OC_DB::prepare( 'INSERT INTO *PREFIX*properties (userid,propertypath,propertyname,propertyvalue) VALUES(?,?,?,?)' );
+ $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue ));
+ } else {
+ $query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertyvalue = ? WHERE userid = ? AND propertypath = ? AND propertyname = ?' );
+ $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName ));
+ }
}
}
diff --git a/lib/db.php b/lib/db.php
index 4860651b323..9d3c20e0145 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -35,6 +35,7 @@ class OC_DB {
static private $schema=false;
static private $affected=0;
static private $result=false;
+ static private $inTransaction=false;
/**
* @brief connects to the database
@@ -486,21 +487,23 @@ class OC_DB {
*/
public static function beginTransaction(){
self::connect();
- if (self::$backend=self::BACKEND_MDB2 && !self::$connection->supports('transactions')) {
+ if (self::$backend==self::BACKEND_MDB2 && !self::$connection->supports('transactions')) {
return false;
}
self::$connection->beginTransaction();
+ self::$inTransaction=true;
}
/**
* Commit the database changes done during a transaction that is in progress
*/
- public static function commit($savePoint=''){
+ public static function commit(){
self::connect();
- if(!self::$connection->inTransaction()){
+ if(!self::$inTransaction){
return false;
}
self::$connection->commit();
+ self::$inTransaction=false;
}
}
diff --git a/lib/eventsource.php b/lib/eventsource.php
index b0450ff3d55..523f72403c3 100644
--- a/lib/eventsource.php
+++ b/lib/eventsource.php
@@ -32,7 +32,6 @@ class OC_EventSource{
private $fallBackId=0;
public function __construct(){
- ob_end_clean();
header('Cache-Control: no-cache');
$this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true';
if($this->fallback){
diff --git a/lib/filecache.php b/lib/filecache.php
new file mode 100644
index 00000000000..921d4a27902
--- /dev/null
+++ b/lib/filecache.php
@@ -0,0 +1,585 @@
+<?php
+
+/**
+* @author Robin Appelman
+* @copyright 2011 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
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+/**
+ * provide caching for filesystem info in the database
+ *
+ * not used by OC_Filesystem for reading filesystem info,
+ * instread apps should use OC_FileCache::get where possible
+ *
+ * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache
+ */
+class OC_FileCache{
+ /**
+ * get the filesystem info from the cache
+ * @param string path
+ * @param string root (optional)
+ * @return array
+ *
+ * returns an assiciative array with the following keys:
+ * - size
+ * - mtime
+ * - ctime
+ * - mimetype
+ * - encrypted
+ * - versioned
+ */
+ public static function get($path,$root=''){
+ if(self::isUpdated($path,$root)){
+ if(!$root){//filesystem hooks are only valid for the default root
+ OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path));
+ }else{
+ self::fileSystemWatcherWrite(array('path'=>$path),$root);
+ }
+ }
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ 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)){
+ return $result;
+ }else{
+ OC_Log::write('get(): file not found in cache ('.$path.')','core',OC_Log::DEBUG);
+ return false;
+ }
+ }
+
+ /**
+ * put filesystem info in the cache
+ * @param string $path
+ * @param array data
+ * @param string root (optional)
+ *
+ * $data is an assiciative array in the same format as returned by get
+ */
+ public static function put($path,$data,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $path=$root.$path;
+ if($path=='/'){
+ $parent=-1;
+ }else{
+ $parent=self::getFileId(dirname($path));
+ }
+ $id=self::getFileId($path);
+ if($id!=-1){
+ self::update($id,$data);
+ return;
+ }
+ if(!isset($data['encrypted'])){
+ $data['encrypted']=false;
+ }
+ if(!isset($data['versioned'])){
+ $data['versioned']=false;
+ }
+ $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']));
+
+ }
+
+ /**
+ * update filesystem info of a file
+ * @param int $id
+ * @param array $data
+ */
+ private static function update($id,$data){
+ $arguments=array();
+ $queryParts=array();
+ foreach(array('size','mtime','ctime','mimetype','encrypted','versioned','writable') as $attribute){
+ if(isset($data[$attribute])){
+ $arguments[]=$data[$attribute];
+ $queryParts[]=$attribute.'=?';
+ }
+ }
+ if(isset($data['mimetype'])){
+ $arguments[]=dirname($data['mimetype']);
+ $queryParts[]='mimepart=?';
+ }
+ $arguments[]=$id;
+
+ $sql = 'UPDATE *PREFIX*fscache SET '.implode(' , ',$queryParts).' WHERE id=?';
+ $query=OC_DB::prepare($sql);
+ $query->execute($arguments);
+ }
+
+ /**
+ * register a file move in the cache
+ * @param string oldPath
+ * @param string newPath
+ * @param string root (optional)
+ */
+ public static function move($oldPath,$newPath,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $oldPath=$root.$oldPath;
+ $newPath=$root.$newPath;
+ $newParent=self::getParentId($newPath);
+ $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=? WHERE path=?');
+ $query->execute(array($newParent,basename($newPath),$newPath,$oldPath));
+ }
+
+ /**
+ * delete info from the cache
+ * @param string $path
+ * @param string root (optional)
+ */
+ public static function delete($path,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $path=$root.$path;
+ $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE path=?');
+ $query->execute(array($path));
+ }
+
+ /**
+ * return array of filenames matching the querty
+ * @param string $query
+ * @param boolean $returnData
+ * @param string root (optional)
+ * @return array of filepaths
+ */
+ public static function search($search,$returnData=false,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $rootLen=strlen($root);
+ if(!$returnData){
+ $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE name LIKE ? AND user=?');
+ }else{
+ $query=OC_DB::prepare('SELECT * FROM *PREFIX*fscache WHERE name LIKE ? AND user=?');
+ }
+ $result=$query->execute(array("%$search%",OC_User::getUser()));
+ $names=array();
+ while($row=$result->fetchRow()){
+ if(!$returnData){
+ $names[]=substr($row['path'],$rootLen);
+ }else{
+ $row['path']=substr($row['path'],$rootLen);
+ $names[]=$row;
+ }
+ }
+ return $names;
+ }
+
+ /**
+ * get all files and folders in a folder
+ * @param string path
+ * @param string root (optional)
+ * @return array
+ *
+ * returns an array of assiciative arrays with the following keys:
+ * - name
+ * - size
+ * - mtime
+ * - ctime
+ * - mimetype
+ * - encrypted
+ * - versioned
+ */
+ public static function getFolderContent($path,$root=''){
+ if(self::isUpdated($path,$root)){
+ self::updateFolder($path,$root);
+ }
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $path=$root.$path;
+ $parent=self::getFileId($path);
+ $query=OC_DB::prepare('SELECT name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=?');
+ $result=$query->execute(array($parent))->fetchAll();
+ if(is_array($result)){
+ return $result;
+ }else{
+ OC_Log::write('getFolderContent(): file not found in cache ('.$path.')','core',OC_Log::DEBUG);
+ return false;
+ }
+ }
+
+ /**
+ * check if a file or folder is in the cache
+ * @param string $path
+ * @param string root (optional)
+ * @return bool
+ */
+ public static function inCache($path,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $path=$root.$path;
+ return self::getFileId($path)!=-1;
+ }
+
+ /**
+ * get the file id as used in the cache
+ * @param string $path
+ * @return int
+ */
+ private static function getFileId($path){
+ $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path=?');
+ $result=$query->execute(array($path))->fetchRow();
+ if(is_array($result)){
+ return $result['id'];
+ }else{
+ OC_Log::write('getFieldId(): file not found in cache ('.$path.')','core',OC_Log::DEBUG);
+ return -1;
+ }
+ }
+
+ /**
+ * get the file id of the parent folder, taking into account '/' has no parent
+ * @param string $path
+ * @return int
+ */
+ private static function getParentId($path){
+ if($path=='/'){
+ return -1;
+ }else{
+ return self::getFileId(dirname($path));
+ }
+ }
+
+ /**
+ * called when changes are made to files
+ * @param array $params
+ * @param string root (optional)
+ */
+ public static function fileSystemWatcherWrite($params,$root=''){
+ if(!$root){
+ $view=OC_Filesystem::getView();
+ }else{
+ $view=new OC_FilesystemView(($root=='/')?'':$root);
+ }
+
+ $path=$params['path'];
+ $fullPath=$view->getRoot().$path;
+ $mimetype=$view->getMimeType($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(self::inCache($path,$root)){
+ $parent=self::getFileId($fullPath);
+ $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE parent=?');
+ $result=$query->execute(array($parent));
+ while($row=$result->fetchRow()){
+ $size+=$row['size'];
+ }
+ $mtime=$view->filemtime($path);
+ $ctime=$view->filectime($path);
+ $writable=$view->is_writable($path);
+ self::put($path,array('size'=>$size,'mtime'=>$mtime,'ctime'=>$ctime,'mimetype'=>$mimetype,'writable'=>$writable));
+ }else{
+ $count=0;
+ self::scan($path,null,$count,$root);
+ }
+ }else{
+ $size=self::scanFile($path,$root);
+ }
+ self::increaseSize(dirname($fullPath),$size-$cachedSize);
+ }
+
+ private static function getCachedSize($path,$root){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }else{
+ if($root=='/'){
+ $root='';
+ }
+ }
+ $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path=?');
+ $result=$query->execute(array($path));
+ if($row=$result->fetchRow()){
+ return $row['size'];
+ }else{//file not in cache
+ return 0;
+ }
+ }
+
+ /**
+ * called when files are deleted
+ * @param array $params
+ * @param string root (optional)
+ */
+ public static function fileSystemWatcherDelete($params,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $path=$params['path'];
+ $fullPath=$root.$path;
+ if(self::getFileId($fullPath)==-1){
+ return;
+ }
+ $size=self::getCachedSize($path,$root);
+ self::increaseSize(dirname($fullPath),-$size);
+ self::delete($path);
+ }
+
+ /**
+ * called when files are deleted
+ * @param array $params
+ * @param string root (optional)
+ */
+ public static function fileSystemWatcherRename($params,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }
+ if($root=='/'){
+ $root='';
+ }
+ $oldPath=$params['oldpath'];
+ $newPath=$params['newpath'];
+ $fullOldPath=$root.$oldPath;
+ $fullNewPath=$root.$newPath;
+ if(($id=self::getFileId($fullOldPath))!=-1){
+ $oldInfo=self::get($fullOldPath);
+ $oldSize=$oldInfo['size'];
+ }else{
+ return;
+ }
+ $size=OC_Filesystem::filesize($oldPath);
+ self::increaseSize(dirname($fullOldPath),-$oldSize);
+ self::increaseSize(dirname($fullNewPath),$oldSize);
+ self::move($oldPath,$newPath);
+ }
+
+ /**
+ * adjust the size of the parent folders
+ * @param string $path
+ * @param int $sizeDiff
+ */
+ private static function increaseSize($path,$sizeDiff){
+ if($sizeDiff==0) return;
+ while(($id=self::getFileId($path))!=-1){//walk up the filetree increasing the size of all parent folders
+ $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET size=size+? WHERE id=?');
+ $query->execute(array($sizeDiff,$id));
+ $path=dirname($path);
+ }
+ }
+
+ /**
+ * recursively scan the filesystem and fill the cache
+ * @param string $path
+ * @param OC_EventSource $enventSource (optional)
+ * @param int count (optional)
+ * @param string root (optionak)
+ */
+ public static function scan($path,$eventSource=false,&$count=0,$root=''){
+ if(!$root){
+ $view=OC_Filesystem::getView();
+ }else{
+ $view=new OC_FilesystemView(($root=='/')?'':$root);
+ }
+ self::scanFile($path,$root);
+ $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($eventSource){
+ $eventSource->send('scanning',array('file'=>$file,'count'=>$count));
+ }
+ self::scan($file,$eventSource,$count,$root);
+ }else{
+ $totalSize+=self::scanFile($file,$root);
+ $count++;
+ }
+ }
+ }
+ }
+ self::increaseSize($view->getRoot().$path,$totalSize);
+ }
+
+ /**
+ * scan a single file
+ * @param string path
+ * @param string root (optional)
+ * @return int size of the scanned file
+ */
+ public static function scanFile($path,$root=''){
+ if(!$root){
+ $view=OC_Filesystem::getView();
+ }else{
+ $view=new OC_FilesystemView(($root=='/')?'':$root);
+ }
+ if(!$view->is_readable($path)) return; //cant read, nothing we can do
+ $stat=$view->stat($path);
+ $mimetype=$view->getMimeType($path);
+ $writable=$view->is_writable($path);
+ $stat['mimetype']=$mimetype;
+ $stat['writable']=$writable;
+ if($path=='/'){
+ $path='';
+ }
+ self::put($path,$stat,$root);
+ return $stat['size'];
+ }
+
+ /**
+ * fine files by mimetype
+ * @param string $part1
+ * @param string $part2 (optional)
+ * @param string root (optional)
+ * @return array of file paths
+ *
+ * $part1 and $part2 together form the complete mimetype.
+ * e.g. searchByMime('text','plain')
+ *
+ * seccond mimetype part can be ommited
+ * e.g. searchByMime('audio')
+ */
+ public static function searchByMime($part1,$part2='',$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ }elseif($root='/'){
+ $root='';
+ }
+ $rootLen=strlen($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));
+ }else{
+ $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE mimetype=? AND user=?');
+ $result=$query->execute(array($part1.'/'.$part2,$user));
+ }
+ $names=array();
+ while($row=$result->fetchRow()){
+ $names[]=substr($row['path'],$rootLen);
+ }
+ return $names;
+ }
+
+ /**
+ * check if a file or folder is updated outside owncloud
+ * @param string path
+ * @param string root (optional)
+ * @return bool
+ */
+ public static function isUpdated($path,$root=''){
+ if(!$root){
+ $root=OC_Filesystem::getRoot();
+ $view=OC_Filesystem::getView();
+ }else{
+ if($root=='/'){
+ $root='';
+ }
+ $view=new OC_FilesystemView($root);
+ }
+ $mtime=$view->filemtime($path);
+ $isDir=$view->is_dir($path);
+ $path=$root.$path;
+ $query=OC_DB::prepare('SELECT mtime FROM *PREFIX*fscache WHERE path=?');
+ $result=$query->execute(array($path));
+ if($row=$result->fetchRow()){
+ $cachedMTime=$row['mtime'];
+ return ($mtime>$cachedMTime);
+ }else{//file not in cache, so it has to be updated
+ return !($isDir);//new folders are handeled sperate
+ }
+ }
+
+ /**
+ * update the cache according to changes in the folder
+ * @param string path
+ * @param string root (optional)
+ */
+ private static function updateFolder($path,$root=''){
+ if(!$root){
+ $view=OC_Filesystem::getView();
+ }else{
+ $view=new OC_FilesystemView(($root=='/')?'':$root);
+ }
+ $dh=$view->opendir($path);
+ if($dh){//check for changed/new files
+ while (($filename = readdir($dh)) !== false) {
+ if($filename != '.' and $filename != '..'){
+ $file=$path.'/'.$filename;
+ if(self::isUpdated($file,$root)){
+ if(!$root){//filesystem hooks are only valid for the default root
+ OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$file));
+ }else{
+ self::fileSystemWatcherWrite(array('path'=>$file),$root);
+ }
+ }
+ }
+ }
+ }
+
+ //check for removed files, not using getFolderContent to prevent loops
+ $parent=self::getFileId($view->getRoot().$path);
+ $query=OC_DB::prepare('SELECT name FROM *PREFIX*fscache WHERE parent=?');
+ $result=$query->execute(array($parent));
+ while($row=$result->fetchRow()){
+ $file=$path.'/'.$row['name'];
+ if(!$view->file_exists($file)){
+ if(!$root){//filesystem hooks are only valid for the default root
+ OC_Hook::emit('OC_Filesystem','post_delete',array('path'=>$file));
+ }else{
+ self::fileSystemWatcherDelete(array('path'=>$file),$root);
+ }
+ }
+ }
+ //update the folder last, so we can calculate the size correctly
+ if(!$root){//filesystem hooks are only valid for the default root
+ OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path));
+ }else{
+ self::fileSystemWatcherWrite(array('path'=>$path),$root);
+ }
+ }
+}
+
+//watch for changes and try to keep the cache up to date
+OC_Hook::connect('OC_Filesystem','post_write','OC_FileCache','fileSystemWatcherWrite');
+OC_Hook::connect('OC_Filesystem','post_delete','OC_FileCache','fileSystemWatcherDelete');
+OC_Hook::connect('OC_Filesystem','post_rename','OC_FileCache','fileSystemWatcherRename');
diff --git a/lib/fileproxy.php b/lib/fileproxy.php
index 549b7015a6a..235fc8bf284 100644
--- a/lib/fileproxy.php
+++ b/lib/fileproxy.php
@@ -34,7 +34,7 @@
* 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, fileatime, 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, filemtime, filectime, file_get_contents, getMimeType, hash, free_space and search
*/
class OC_FileProxy{
diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php
index f770c9cb32b..94a49176ee6 100644
--- a/lib/fileproxy/quota.php
+++ b/lib/fileproxy/quota.php
@@ -27,8 +27,10 @@
class OC_FileProxy_Quota extends OC_FileProxy{
private function getFreeSpace(){
- $usedSpace=OC_Filesystem::filesize('');
+ $rootInfo=OC_FileCache::get('');
+ $usedSpace=$rootInfo['size'];
$totalSpace=OC_Preferences::getValue(OC_User::getUser(),'files','quota',0);
+ $totalSpace=OC_Helper::computerFileSize($totalSpace);
if($totalSpace==0){
return 0;
}
diff --git a/lib/files.php b/lib/files.php
index 9ae5320ad1d..457c8ea38f2 100644
--- a/lib/files.php
+++ b/lib/files.php
@@ -36,44 +36,13 @@ class OC_Files {
if(strpos($directory,OC::$CONFIG_DATADIRECTORY)===0){
$directory=substr($directory,strlen(OC::$CONFIG_DATADIRECTORY));
}
- $filesfound=true;
- $content=array();
- $dirs=array();
- $file=array();
- $files=array();
- if(OC_Filesystem::is_dir($directory)) {
- if ($dh = OC_Filesystem::opendir($directory)) {
- while (($filename = readdir($dh)) !== false) {
- if($filename<>'.' and $filename<>'..' and substr($filename,0,1)!='.'){
- $file=array();
- $filesfound=true;
- $file['name']=$filename;
- $file['directory']=$directory;
- $stat=OC_Filesystem::stat($directory.'/'.$filename);
- $file=array_merge($file,$stat);
- $file['size']=OC_Filesystem::filesize($directory.'/'.$filename);
- $file['mime']=OC_Files::getMimeType($directory .'/'. $filename);
- $file['readable']=OC_Filesystem::is_readable($directory .'/'. $filename);
- $file['writeable']=OC_Filesystem::is_writeable($directory .'/'. $filename);
- $file['type']=OC_Filesystem::filetype($directory .'/'. $filename);
- if($file['type']=='dir'){
- $dirs[$file['name']]=$file;
- }else{
- $files[$file['name']]=$file;
- }
- }
- }
- closedir($dh);
- }
- }
- uksort($dirs, "strnatcasecmp");
- uksort($files, "strnatcasecmp");
- $content=array_merge($dirs,$files);
- if($filesfound){
- return $content;
- }else{
- return false;
+ $files=OC_FileCache::getFolderContent($directory);
+ foreach($files as &$file){
+ $file['directory']=$directory;
+ $file['type']=($file['mimetype']=='httpd/unix-directory')?'dir':'file';
}
+ usort($files, "fileCmp");//TODO: remove this once ajax is merged
+ return $files;
}
@@ -321,3 +290,13 @@ class OC_Files {
return $path;
}
}
+
+function fileCmp($a,$b){
+ if($a['type']=='dir' and $b['type']!='dir'){
+ return -1;
+ }elseif($a['type']!='dir' and $b['type']=='dir'){
+ return 1;
+ }else{
+ return strnatcasecmp($a['name'],$b['name']);
+ }
+}
diff --git a/lib/filestorage.php b/lib/filestorage.php
index 34fa6457fd2..4523144f6f4 100644
--- a/lib/filestorage.php
+++ b/lib/filestorage.php
@@ -34,12 +34,11 @@ class OC_Filestorage{
public function filetype($path){}
public function filesize($path){}
public function is_readable($path){}
- public function is_writeable($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 fileatime($path){}
public function file_get_contents($path){}
public function file_put_contents($path,$data){}
public function unlink($path){}
@@ -48,7 +47,6 @@ class OC_Filestorage{
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 fromUploadedFile($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){}
diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php
index e846aa420e4..292d2a84e7d 100644
--- a/lib/filestorage/local.php
+++ b/lib/filestorage/local.php
@@ -13,13 +13,11 @@ class OC_Filestorage_Local extends OC_Filestorage{
}
public function mkdir($path){
if($return=mkdir($this->datadir.$path)){
- $this->clearFolderSizeCache($path);
}
return $return;
}
public function rmdir($path){
if($return=rmdir($this->datadir.$path)){
- $this->clearFolderSizeCache($path);
}
return $return;
}
@@ -52,7 +50,7 @@ class OC_Filestorage_Local extends OC_Filestorage{
public function is_readable($path){
return is_readable($this->datadir.$path);
}
- public function is_writeable($path){
+ public function is_writable($path){
return is_writable($this->datadir.$path);
}
public function file_exists($path){
@@ -67,20 +65,22 @@ class OC_Filestorage_Local extends OC_Filestorage{
public function filemtime($path){
return filemtime($this->datadir.$path);
}
- public function fileatime($path){
- return fileatime($this->datadir.$path);
- }
+
+ public function setFileMtime($path, $mtime){
+ // 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.
+ return touch($this->datadir.$path, $mtime);
+ }
+
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)){
- $this->clearFolderSizeCache($path);
}
}
public function unlink($path){
$return=$this->delTree($path);
- $this->clearFolderSizeCache($path);
return $return;
}
public function rename($path1,$path2){
@@ -90,8 +90,6 @@ class OC_Filestorage_Local extends OC_Filestorage{
}
if($return=rename($this->datadir.$path1,$this->datadir.$path2)){
- $this->clearFolderSizeCache($path1);
- $this->clearFolderSizeCache($path2);
}
return $return;
}
@@ -104,7 +102,6 @@ class OC_Filestorage_Local extends OC_Filestorage{
$path2.=$source;
}
if($return=copy($this->datadir.$path1,$this->datadir.$path2)){
- $this->clearFolderSizeCache($path2);
}
return $return;
}
@@ -117,12 +114,10 @@ class OC_Filestorage_Local extends OC_Filestorage{
case 'w+':
case 'x+':
case 'a+':
- $this->clearFolderSizeCache($path);
break;
case 'w':
case 'x':
case 'a':
- $this->clearFolderSizeCache($path);
break;
}
}
@@ -192,18 +187,6 @@ class OC_Filestorage_Local extends OC_Filestorage{
$fileStats = stat($tmpFile);
if(rename($tmpFile,$this->datadir.$path)){
touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']);
- $this->clearFolderSizeCache($path);
- return true;
- }else{
- return false;
- }
- }
-
- public function fromUploadedFile($tmpFile,$path){
- $fileStats = stat($tmpFile);
- if(move_uploaded_file($tmpFile,$this->datadir.$path)){
- touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']);
- $this->clearFolderSizeCache($path);
return true;
}else{
return false;
@@ -219,7 +202,6 @@ class OC_Filestorage_Local extends OC_Filestorage{
if ($item == '.' || $item == '..') continue;
if(is_file($dir.'/'.$item)){
if(unlink($dir.'/'.$item)){
- $this->clearFolderSizeCache($dir);
}
}elseif(is_dir($dir.'/'.$item)){
if (!$this->delTree($dirRelative. "/" . $item)){
@@ -228,7 +210,6 @@ class OC_Filestorage_Local extends OC_Filestorage{
}
}
if($return=rmdir($dir)){
- $this->clearFolderSizeCache($dir);
}
return $return;
}
@@ -268,75 +249,6 @@ class OC_Filestorage_Local extends OC_Filestorage{
* @return int size of folder and it's content
*/
public function getFolderSize($path){
- $path=str_replace('//','/',$path);
- if($this->is_dir($path) and substr($path,-1)!='/'){
- $path.='/';
- }
- $query=OC_DB::prepare("SELECT size FROM *PREFIX*foldersize WHERE path=?");
- $size=$query->execute(array($path))->fetchAll();
- if(count($size)>0){// we already the size, just return it
- return $size[0]['size'];
- }else{//the size of the folder isn't know, calulate it
- return $this->calculateFolderSize($path);
- }
- }
-
- /**
- * @brief calulate the size of folder and it's content and cache it
- * @param string $path file path
- * @return int size of folder and it's content
- */
- public function calculateFolderSize($path){
- if($this->is_file($path)){
- $path=dirname($path);
- }
- $path=str_replace('//','/',$path);
- if($this->is_dir($path) and substr($path,-1)!='/'){
- $path.='/';
- }
- $size=0;
- if ($dh = $this->opendir($path)) {
- while (($filename = readdir($dh)) !== false) {
- if($filename!='.' and $filename!='..'){
- $subFile=$path.'/'.$filename;
- if($this->is_file($subFile)){
- $size+=$this->filesize($subFile);
- }else{
- $size+=$this->getFolderSize($subFile);
- }
- }
- }
- if($size>0){
- $query=OC_DB::prepare("INSERT INTO *PREFIX*foldersize VALUES(?,?)");
- $result=$query->execute(array($path,$size));
- }
- }
- return $size;
- }
-
- /**
- * @brief clear the folder size cache of folders containing a file
- * @param string $path
- */
- public function clearFolderSizeCache($path){
- if($this->is_file($path)){
- $path=dirname($path);
- }
- $path=str_replace('//','/',$path);
- if($this->is_dir($path) and substr($path,-1)!='/'){
- $path.='/';
- }
- $query=OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?");
- $result=$query->execute(array($path));
- if($path!='/' and $path!=''){
- $parts=explode('/',$path);
- //pop empty part
- $part=array_pop($parts);
- if(empty($part)){
- array_pop($parts);
- }
- $parent=implode('/',$parts);
- $this->clearFolderSizeCache($parent);
- }
+ return 0;//depricated, use OC_FileCach instead
}
}
diff --git a/lib/filestorage/remote.php b/lib/filestorage/remote.php
deleted file mode 100644
index 88bdbca481c..00000000000
--- a/lib/filestorage/remote.php
+++ /dev/null
@@ -1,350 +0,0 @@
-<?php
-
-/**
-* ownCloud
-*
-* @author Frank Karlitschek
-* @copyright 2010 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 Affero General Public
-* License along with this library. If not, see <http://www.gnu.org/licenses/>.
-*
-*/
-
-class OC_Filestorage_Remote extends OC_Filestorage{
- private $url;
- private $username;
- private $password;
- private $remote=false;
- private $statCache;
- private $statCacheDir=false;
- private $changed=array();
-
- private function cacheDir($dir){
- if($this->statCacheDir!=$dir or $this->statCacheDir===false){
- $this->statCache=$this->remote->getFiles($dir);
- $keys=array_keys($this->statCache);
- $this->statCacheDir=$dir;
- }
- }
-
- public function __construct($arguments){
- $this->url=$arguments['url'];
- $this->username=$arguments['username'];
- $this->password=$arguments['password'];
- }
- private function connect(){
- if($this->remote===false){
- $this->remote=OC_Connect::connect($this->url,$this->username,$this->password);
- }
- }
- public function mkdir($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $return=$this->remote->newFile($parent,$name,'dir');
- if($return){
- $this->notifyObservers($path,OC_FILEACTION_CREATE);
- }
- return $return;
- }
- public function rmdir($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $return=$this->remote->delete($parent,$name);
- if($return){
- $this->notifyObservers($path,OC_FILEACTION_DELETE);
- }
- return $return;
- }
- public function opendir($path){
- $this->connect();
- $this->cacheDir($path);
- $dirs=array_keys($this->statCache);
- $id=uniqid();
- global $FAKEDIRS;
- $FAKEDIRS[$id]=$dirs;
- if($return=opendir("fakedir://$id")){
- $this->notifyObservers($path,OC_FILEACTION_READ);
- }
- return $return;
- }
- public function is_dir($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($path);
- if($path=='' or $path=='/'){
- return true;
- }
- if(!isset($this->statCache[$name])){
- return false;
- }
- return ($this->statCache[$name]['type'=='dir']);
- }
- public function is_file($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return ($this->statCache[$name]['type'!='dir']);
- }
- public function stat($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return $false;
- }
- return $this->statCache[$name];
- }
- public function filetype($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return $this->statCache[$name]['type'];
- }
- public function filesize($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return $false;
- }
- return $this->statCache[$name]['size'];
- }
- public function is_readable($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return $this->statCache[$name]['readable'];
- }
- public function is_writeable($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return $this->statCache[$name]['writeable'];
- }
- public function file_exists($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- return isset($this->statCache[$name]);
- }
- public function readfile($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $file=$this->remote->getFile($parent,$name);
- readfile($file);
- unlink($file);
- }
- public function filectime($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return $this->statCache[$name]['ctime'];
- }
- public function filemtime($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return $this->statCache[$name]['mtime'];
- }
- public function fileatime($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return $this->statCache[$name]['atime'];
- }
- public function file_get_contents($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $file=$this->remote->getFile($parent,$name);
- file_get_contents($file);
- unlink($file);
- }
- public function file_put_contents($path,$data){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $file=$this->remote->getFile($parent,$name);
- $file=tempnam(get_temp_dir(),'oc_');
- file_put_contents($file,$data);
- if($return=$this->remote->sendTmpFile($file,$parent,$name)){
- $this->notifyObservers($path,OC_FILEACTION_WRITE);
- }
- }
- public function unlink($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- if($return=$this->remote->delete($paren,$name)){
- $this->notifyObservers($path,OC_FILEACTION_DELETE);
- }
- return $return;
- }
- public function rename($path1,$path2){
- $this->connect();
- $parent1=dirname($path1);
- $name1=substr($path1,strlen($parent1)+1);
- $parent2=dirname($path2);
- $name2=substr($path2,strlen($parent2)+1);
- if($return=$this->remote->move($parent1,$name1,$parent2,$name2)){
- $this->notifyObservers($path1.'->'.$path2,OC_FILEACTION_RENAME);
- }
- return $return;
- }
- public function copy($path1,$path2){
- $this->connect();
- $parent1=dirname($path1);
- $name1=substr($path1,strlen($parent1)+1);
- $parent2=dirname($path2);
- $name2=substr($path2,strlen($parent2)+1);
- if($return=$this->copy->rename($parent1,$name1,$parent2,$name2)){
- $this->notifyObservers($path1.'->'.$path2,OC_FILEACTION_RENAME);
- }
- return $return;
- }
- public function fopen($path,$mode){
- $this->connect();
- $changed=false;
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- $file=$this->remote->getFile($parent,$name);
- if($return=fopen($file,$mode)){
- switch($mode){
- case 'r':
- $this->notifyObservers($path,OC_FILEACTION_READ);
- break;
- case 'r+':
- case 'w+':
- case 'x+':
- case 'a+':
- $this->notifyObservers($path,OC_FILEACTION_READ | OC_FILEACTION_WRITE);
- $this->changed[]=array('dir'=>$parent,'file'=>$name,'tmp'=>$file);
- break;
- case 'w':
- case 'x':
- case 'a':
- $this->notifyObservers($path,OC_FILEACTION_WRITE);
- $this->changed[]=array('dir'=>$parent,'file'=>$name,'tmp'=>$file);
- break;
- }
- }
- return $return;
- }
-
- public function getMimeType($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- if(substr($name,0,1)=='/'){
- $name=substr($name,1);
- }
- $this->cacheDir($parent);
- if(!isset($this->statCache[$name])){
- return false;
- }
- return $this->statCache[$name]['mime'];
- }
-
- public function toTmpFile($path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- if(substr($name,0,1)=='/'){
- $name=substr($name,1);
- }
- $filename=$this->remote->getFile($parent,$name);
- if($filename){
- $this->notifyObservers($path,OC_FILEACTION_READ);
- return $filename;
- }else{
- return false;
- }
- }
-
- public function fromTmpFile($tmpFile,$path){
- $this->connect();
- $parent=dirname($path);
- $name=substr($path,strlen($parent)+1);
- if($this->remote->sendTmpFile($tmpFile,$parent,$name)){
- $this->notifyObservers($path,OC_FILEACTION_CREATE);
- return true;
- }else{
- return false;
- }
- }
-
- public function delTree($dir) {
- $this->connect();
- $parent=dirname($dir);
- $name=substr($dir,strlen($parent)+1);
- $return=$this->remote->delete($parent,$name);
- if($return=rmdir($dir)){
- $this->notifyObservers($dir,OC_FILEACTION_DELETE);
- }
- return $return;
- }
-
- public function find($path){
- return $this->getTree($path);
- }
-
- public function getTree($dir) {
- $this->connect();
- if($return=$this->remote->getTree($dir)){
- $this->notifyObservers($dir,OC_FILEACTION_READ);
- }
- return $return;
- }
-
- public function __destruct(){
- foreach($this->changed as $changed){
- $this->remote->sendTmpFile($changed['tmp'],$changed['dir'],$changed['file']);
- }
- }
-}
diff --git a/lib/filestoragecommon.php b/lib/filestoragecommon.php
new file mode 100644
index 00000000000..f522d15c4e9
--- /dev/null
+++ b/lib/filestoragecommon.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski GapczynskiM@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
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+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 bd953deb210..75997c244ff 100644
--- a/lib/filesystem.php
+++ b/lib/filesystem.php
@@ -42,11 +42,15 @@
*
* the &run parameter can be set to false to prevent the operation from occuring
*/
+
class OC_Filesystem{
static private $storages=array();
static private $mounts=array();
- static private $fakeRoot='';
static private $storageTypes=array();
+ public static $loaded=false;
+ private $fakeRoot='';
+ static private $defaultInstance;
+
/**
* classname which used for hooks handling
@@ -134,31 +138,78 @@ class OC_Filesystem{
*/
const signal_param_run = 'run';
- /**
- * register a storage type
- * @param string type
- * @param string classname
- * @param array arguments an associative array in the form of name=>type (eg array('datadir'=>'string'))
+ /**
+ * get the mountpoint of the storage object for a path
+ ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
+ *
+ * @param string path
+ * @return string
*/
- static public function registerStorageType($type,$classname,$arguments){
- self::$storageTypes[$type]=array('type'=>$type,'classname'=>$classname,'arguments'=>$arguments);
+ static public function getMountPoint($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;
+ }
+ if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)){
+ $foundMountPoint=$mountpoint;
+ }
+ }
+ return $foundMountPoint;
}
-
+
/**
- * check if the filesystem supports a specific storagetype
- * @param string type
+ * get the part of the path relative to the mountpoint of the storage it's stored in
+ * @param string path
* @return bool
*/
- static public function hasStorageType($type){
- return isset(self::$storageTypes[$type]);
+ static public function getInternalPath($path){
+ $mountPoint=self::getMountPoint($path);
+ $internalPath=substr($path,strlen($mountPoint));
+ return $internalPath;
}
-
/**
- * get the list of names of storagetypes that the filesystem supports
- * @return array
+ * get the storage object for a path
+ * @param string path
+ * @return OC_Filestorage
*/
- static public function getStorageTypeNames(){
- return array_keys(self::$storageTypes);
+ static public function getStorage($path){
+ $mountpoint=self::getMountPoint($path);
+ if($mountpoint){
+ if(!isset(OC_Filesystem::$storages[$mountpoint])){
+ $mount=OC_Filesystem::$mounts[$mountpoint];
+ OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'],$mount['arguments']);
+ }
+ return OC_Filesystem::$storages[$mountpoint];
+ }
+ }
+
+ static public function init($root){
+ if(self::$defaultInstance){
+ return false;
+ }
+ self::$defaultInstance=new OC_FilesystemView($root);
+ self::$loaded=true;
+ }
+
+ /**
+ * get the default filesystem view
+ * @return OC_FilesystemView
+ */
+ static public function getView(){
+ return self::$defaultInstance;
}
/**
@@ -177,13 +228,9 @@ class OC_Filesystem{
* @param array arguments
* @return OC_Filestorage
*/
- static private function createStorage($type,$arguments){
- if(!self::hasStorageType($type)){
- return false;
- }
- $className=self::$storageTypes[$type]['classname'];
- if(class_exists($className)){
- return new $className($arguments);
+ static private function createStorage($class,$arguments){
+ if(class_exists($class)){
+ return new $class($arguments);
}else{
return false;
}
@@ -195,36 +242,27 @@ class OC_Filesystem{
* @return bool
*/
static public function chroot($fakeRoot){
- if(!$fakeRoot==''){
- if($fakeRoot[0]!=='/'){
- $fakeRoot='/'.$fakeRoot;
- }
- }
- self::$fakeRoot=$fakeRoot;
+ return self::$defaultInstance->chroot($path);
}
-
+
/**
- * get the part of the path relative to the mountpoint of the storage it's stored in
- * @param string path
- * @return bool
- */
- static public function getInternalPath($path){
- $mountPoint=self::getMountPoint($path);
- $path=self::$fakeRoot.$path;
- $internalPath=substr($path,strlen($mountPoint));
- return $internalPath;
+ * get the fake root
+ * @return string
+ */
+ static public function getRoot(){
+ return self::$defaultInstance->getRoot();
}
-
+
/**
* mount an OC_Filestorage in our virtual filesystem
* @param OC_Filestorage storage
* @param string mountpoint
*/
- static public function mount($type,$arguments,$mountpoint){
+ static public function mount($class,$arguments,$mountpoint){
if(substr($mountpoint,0,1)!=='/'){
$mountpoint='/'.$mountpoint;
}
- self::$mounts[$mountpoint]=array('type'=>$type,'arguments'=>$arguments);
+ self::$mounts[$mountpoint]=array('class'=>$class,'arguments'=>$arguments);
}
/**
@@ -239,65 +277,13 @@ class OC_Filesystem{
}
/**
- * get the storage object for a path
- * @param string path
- * @return OC_Filestorage
- */
- static public function getStorage($path){
- $mountpoint=self::getMountPoint($path);
- if($mountpoint){
- if(!isset(self::$storages[$mountpoint])){
- $mount=self::$mounts[$mountpoint];
- self::$storages[$mountpoint]=self::createStorage($mount['type'],$mount['arguments']);
- }
- return self::$storages[$mountpoint];
- }
- }
-
- /**
- * get the mountpoint of the storage object for a path
- ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
- *
- * @param string path
- * @return string
- */
- static public function getMountPoint($path){
- if(!$path){
- $path='/';
- }
- if(substr($path,0,1)!=='/'){
- $path='/'.$path;
- }
- if(substr($path,-1)!=='/'){
- $path=$path.'/';
- }
- $path=self::$fakeRoot.$path;
- $foundMountPoint='';
- foreach(self::$mounts as $mountpoint=>$storage){
- if(substr($mountpoint,-1)!=='/'){
- $mountpoint=$mountpoint.'/';
- }
- if($mountpoint==$path){
- return $mountpoint;
- }
- if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)){
- $foundMountPoint=$mountpoint;
- }
- }
- return $foundMountPoint;
- }
-
- /**
* return the path to a local version of the file
* we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed
* @param string path
* @return string
*/
static public function getLocalFile($path){
- $parent=substr($path,0,strrpos($path,'/'));
- if(self::isValidPath($parent) and $storage=self::getStorage($path)){
- return $storage->getLocalFile(self::getInternalPath($path));
- }
+ return self::$defaultInstance->getLocalFile($path);
}
/**
@@ -314,262 +300,93 @@ class OC_Filesystem{
}
return true;
}
-
+ /**
+ * following functions are equivilent to their php buildin equivilents for arguments/return values.
+ */
static public function mkdir($path){
- return self::basicOperation('mkdir',$path,array('create','write'));
+ return self::$defaultInstance->mkdir($path);
}
static public function rmdir($path){
- return self::basicOperation('rmdir',$path,array('delete'));
+ return self::$defaultInstance->rmdir($path);
}
static public function opendir($path){
- return self::basicOperation('opendir',$path,array('read'));
+ return self::$defaultInstance->opendir($path);
}
static public function is_dir($path){
- if($path=='/'){
- return true;
- }
- return self::basicOperation('is_dir',$path);
+ return self::$defaultInstance->is_dir($path);
}
static public function is_file($path){
- if($path=='/'){
- return false;
- }
- return self::basicOperation('is_file',$path);
+ return self::$defaultInstance->is_file($path);
}
static public function stat($path){
- return self::basicOperation('stat',$path);
+ return self::$defaultInstance->stat($path);
}
static public function filetype($path){
- return self::basicOperation('filetype',$path);
+ return self::$defaultInstance->filetype($path);
}
static public function filesize($path){
- return self::basicOperation('filesize',$path);
+ return self::$defaultInstance->filesize($path);
}
static public function readfile($path){
- return self::basicOperation('readfile',$path,array('read'));
+ return self::$defaultInstance->readfile($path);
}
static public function is_readable($path){
- return self::basicOperation('is_readable',$path);
+ return self::$defaultInstance->is_readable($path);
}
- static public function is_writeable($path){
- return self::basicOperation('is_writeable',$path);
+ static public function is_writable($path){
+ return self::$defaultInstance->is_writable($path);
}
static public function file_exists($path){
- if($path=='/'){
- return true;
- }
- return self::basicOperation('file_exists',$path);
+ return self::$defaultInstance->file_exists($path);
}
static public function filectime($path){
- return self::basicOperation('filectime',$path);
+ return self::$defaultInstance->filectime($path);
}
static public function filemtime($path){
- return self::basicOperation('filemtime',$path);
+ return self::$defaultInstance->filemtime($path);
}
- static public function fileatime($path){
- return self::basicOperation('fileatime',$path);
+ static public function setFileMtime($path, $mtime){
+ return self::$defaultInstance->setFileMtime($path, $mtime);
}
static public function file_get_contents($path){
- return self::basicOperation('file_get_contents',$path,array('read'));
+ return self::$defaultInstance->file_get_contents($path);
}
static public function file_put_contents($path,$data){
- return self::basicOperation('file_put_contents',$path,array('create','write'),$data);
+ return self::$defaultInstance->file_put_contents($path,$data);
}
static public function unlink($path){
- return self::basicOperation('unlink',$path,array('delete'));
+ return self::$defaultInstance->unlink($path);
}
static public function rename($path1,$path2){
- if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and self::is_writeable($path1) and self::isValidPath($path2)){
- $run=true;
- OC_Hook::emit( self::CLASSNAME, self::signal_rename, array( self::signal_param_oldpath => $path1 , self::signal_param_newpath=>$path2, self::signal_param_run => &$run));
- if($run){
- $mp1=self::getMountPoint($path1);
- $mp2=self::getMountPoint($path2);
- if($mp1==$mp2){
- if($storage=self::getStorage($path1)){
- $result=$storage->rename(self::getInternalPath($path1),self::getInternalPath($path2));
- }
- }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){
- $tmpFile=$storage1->toTmpFile(self::getInternalPath($path1));
- $result=$storage2->fromTmpFile($tmpFile,self::getInternalPath($path2));
- $storage1->unlink(self::getInternalPath($path1));
- }
- OC_Hook::emit( self::CLASSNAME, self::signal_post_rename, array( self::signal_param_oldpath => $path1, self::signal_param_newpath=>$path2));
- return $result;
- }
- }
+ return self::$defaultInstance->rename($path1,$path2);
}
static public function copy($path1,$path2){
- if(OC_FileProxy::runPreProxies('copy',$path1,$path2) and self::is_readable($path1) and self::isValidPath($path2)){
- $run=true;
- OC_Hook::emit( self::CLASSNAME, self::signal_copy, array( self::signal_param_oldpath => $path1 , self::signal_param_newpath=>$path2, self::signal_param_run => &$run));
- $exists=self::file_exists($path2);
- if($run and !$exists){
- OC_Hook::emit( self::CLASSNAME, self::signal_create, array( self::signal_param_path => $path2, self::signal_param_run => &$run));
- }
- if($run){
- OC_Hook::emit( self::CLASSNAME, self::signal_write, array( self::signal_param_path => $path2, self::signal_param_run => &$run));
- }
- if($run){
- $mp1=self::getMountPoint($path1);
- $mp2=self::getMountPoint($path2);
- if($mp1==$mp2){
- if($storage=self::getStorage($path1)){
- $result=$storage->copy(self::getInternalPath($path1),self::getInternalPath($path2));
- }
- }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){
- $tmpFile=$storage1->toTmpFile(self::getInternalPath($path1));
- $result=$storage2->fromTmpFile($tmpFile,self::getInternalPath($path2));
- }
- OC_Hook::emit( self::CLASSNAME, self::signal_post_copy, array( self::signal_param_oldpath => $path1 , self::signal_param_newpath=>$path2));
- if(!$exists){
- OC_Hook::emit( self::CLASSNAME, self::signal_post_create, array( self::signal_param_path => $path2));
- }
- OC_Hook::emit( self::CLASSNAME, self::signal_post_write, array( self::signal_param_path => $path2));
- return $result;
- }
- }
+ return self::$defaultInstance->copy($path1,$path2);
}
static public function fopen($path,$mode){
- $hooks=array();
- switch($mode){
- case 'r':
- $hooks[]='read';
- break;
- case 'r+':
- case 'w+':
- case 'x+':
- case 'a+':
- $hooks[]='read';
- $hooks[]='write';
- break;
- case 'w':
- case 'x':
- case 'a':
- $hooks[]='write';
- break;
- default:
- OC_Log::write('core','invalid mode ('.$mode.') for '.$path,OC_Log::ERROR);
- }
-
- return self::basicOperation('fopen',$path,$hooks,$mode);
+ return self::$defaultInstance->fopen($path,$mode);
}
static public function toTmpFile($path){
- if(OC_FileProxy::runPreProxies('toTmpFile',$path) and self::isValidPath($path) and $storage=self::getStorage($path)){
- OC_Hook::emit( self::CLASSNAME, self::signal_read, array( self::signal_param_path => $path));
- return $storage->toTmpFile(self::getInternalPath($path));
- }
+ return self::$defaultInstance->toTmpFile($path);
}
static public function fromTmpFile($tmpFile,$path){
- if(OC_FileProxy::runPreProxies('copy',$tmpFile,$path) and self::isValidPath($path) and $storage=self::getStorage($path)){
- $run=true;
- $exists=self::file_exists($path);
- if(!$exists){
- OC_Hook::emit( self::CLASSNAME, self::signal_create, array( self::signal_param_path => $path, self::signal_param_run => &$run));
- }
- if($run){
- OC_Hook::emit( self::CLASSNAME, self::signal_write, array( self::signal_param_path => $path, self::signal_param_run => &$run));
- }
- if($run){
- $result=$storage->fromTmpFile($tmpFile,self::getInternalPath($path));
- if(!$exists){
- OC_Hook::emit( self::CLASSNAME, self::signal_post_create, array( self::signal_param_path => $path));
- }
- OC_Hook::emit( self::CLASSNAME, self::signal_post_write, array( self::signal_param_path => $path));
- return $result;
- }
- }
- }
- static public function fromUploadedFile($tmpFile,$path){
- if(OC_FileProxy::runPreProxies('fromUploadedFile',$tmpFile,$path) and self::isValidPath($path) and $storage=self::getStorage($path)){
- $run=true;
- $exists=self::file_exists($path);
- if(!$exists){
- OC_Hook::emit( self::CLASSNAME, self::signal_create, array( self::signal_param_path => $path, self::signal_param_run => &$run));
- }
- if($run){
- OC_Hook::emit( self::CLASSNAME, self::signal_write, array( self::signal_param_path => $path, self::signal_param_run => &$run));
- }
- if($run){
- $result=$storage->fromUploadedFile($tmpFile,self::getInternalPath($path));
- if(!$exists){
- OC_Hook::emit( self::CLASSNAME, self::signal_post_create, array( self::signal_param_path => $path));
- }
- OC_Hook::emit( self::CLASSNAME, self::signal_post_write, array( self::signal_param_path => $path));
- return $result;
- }
- }
+ return self::$defaultInstance->fromTmpFile($tmpFile,$path);
}
+
static public function getMimeType($path){
- return self::basicOperation('getMimeType',$path);
+ return self::$defaultInstance->getMimeType($path);
}
static public function hash($type,$path){
- return self::basicOperation('hash',$path,array('read'));
+ return self::$defaultInstance->hash($type,$path);
}
static public function free_space($path='/'){
- return self::basicOperation('free_space',$path);
+ return self::$defaultInstance->free_space($path);
}
static public function search($query){
- self::mountAll();
- $files=array();
- $fakeRoot=self::$fakeRoot;
- $fakeRootLength=strlen($fakeRoot);
- foreach(self::$storages as $mountpoint=>$storage){
- $results=$storage->search($query);
- if(is_array($results)){
- foreach($results as $result){
- $file=str_replace('//','/',$mountpoint.$result);
- if(substr($file,0,$fakeRootLength)==$fakeRoot){
- $file=substr($file,$fakeRootLength);
- $files[]=$file;
- }
- }
- }
- }
- return $files;
-
- }
-
- static public function update_session_file_hash($sessionname,$sessionvalue){
- $_SESSION[$sessionname] = $sessionvalue;
- }
-
- /**
- * abstraction for running most basic operations
- * @param string $operation
- * @param string #path
- * @param array (optional) hooks
- * @param mixed (optional) $extraParam
- * @return mixed
- */
- private static function basicOperation($operation,$path,$hooks=array(),$extraParam=null){
- if(OC_FileProxy::runPreProxies($operation,$path, $extraParam) and self::isValidPath($path) and $storage=self::getStorage($path)){
- $interalPath=self::getInternalPath($path);
- $run=true;
- foreach($hooks as $hook){
- if($hook!='read'){
- OC_Hook::emit( self::CLASSNAME, $hook, array( self::signal_param_path => $path, self::signal_param_run => &$run));
- }else{
- OC_Hook::emit( self::CLASSNAME, $hook, array( self::signal_param_path => $path));
- }
- }
- if($run){
- if($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( self::CLASSNAME, 'post_'.$hook, array( self::signal_param_path => $path));
- }
- }
- return $result;
- }
- }
- return null;
+ return OC_FileCache::search($query);
}
}
+
+require_once('filecache.php');
diff --git a/lib/filesystemview.php b/lib/filesystemview.php
new file mode 100644
index 00000000000..0f1c546f4c5
--- /dev/null
+++ b/lib/filesystemview.php
@@ -0,0 +1,321 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Frank Karlitschek
+* @copyright 2010 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 Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+class OC_FilesystemView {
+ private $fakeRoot='';
+
+ public function __construct($root){
+ $this->fakeRoot=$root;
+ }
+
+ public function getAbsolutePath($path){
+ if(!$path){
+ $path='/';
+ }
+ if(substr($path,0,1)!=='/'){
+ $path='/'.$path;
+ }
+ return $this->fakeRoot.$path;
+ }
+
+
+ /**
+ * change the root to a fake toor
+ * @param string fakeRoot
+ * @return bool
+ */
+ public function chroot($fakeRoot){
+ if(!$fakeRoot==''){
+ if($fakeRoot[0]!=='/'){
+ $fakeRoot='/'.$fakeRoot;
+ }
+ }
+ $this->fakeRoot=$fakeRoot;
+ }
+
+ /**
+ * get the fake root
+ * @return string
+ */
+ public function getRoot(){
+ return $this->fakeRoot;
+ }
+
+ /**
+ * get the part of the path relative to the mountpoint of the storage it's stored in
+ * @param string path
+ * @return bool
+ */
+ public function getInternalPath($path){
+ return OC_Filesystem::getInternalPath($this->getAbsolutePath($path));
+ }
+ /**
+ * get the storage object for a path
+ * @param string path
+ * @return OC_Filestorage
+ */
+ public function getStorage($path){
+ return OC_Filesystem::getStorage($this->getAbsolutePath($path));
+ }
+
+ /**
+ * get the mountpoint of the storage object for a path
+ ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
+ *
+ * @param string path
+ * @return string
+ */
+ public function getMountPoint($path){
+ return OC_Filesystem::getMountPoint($this->getAbsolutePath($path));
+ }
+
+ /**
+ * return the path to a local version of the file
+ * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed
+ * @param string path
+ * @return string
+ */
+ public function getLocalFile($path){
+ $parent=substr($path,0,strrpos($path,'/'));
+ if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)){
+ return $storage->getLocalFile($this->getInternalPath($path));
+ }
+ }
+
+ /**
+ * following functions are equivilent to their php buildin equivilents for arguments/return values.
+ */
+ public function mkdir($path){
+ return $this->basicOperation('mkdir',$path,array('create','write'));
+ }
+ public function rmdir($path){
+ return $this->basicOperation('rmdir',$path,array('delete'));
+ }
+ public function opendir($path){
+ return $this->basicOperation('opendir',$path,array('read'));
+ }
+ public function is_dir($path){
+ if($path=='/'){
+ return true;
+ }
+ return $this->basicOperation('is_dir',$path);
+ }
+ public function is_file($path){
+ if($path=='/'){
+ return false;
+ }
+ return $this->basicOperation('is_file',$path);
+ }
+ public function stat($path){
+ return $this->basicOperation('stat',$path);
+ }
+ public function filetype($path){
+ return $this->basicOperation('filetype',$path);
+ }
+ public function filesize($path){
+ return $this->basicOperation('filesize',$path);
+ }
+ public function readfile($path){
+ return $this->basicOperation('readfile',$path,array('read'));
+ }
+ public function is_readable($path){
+ return $this->basicOperation('is_readable',$path);
+ }
+ public function is_writable($path){
+ return $this->basicOperation('is_writable',$path);
+ }
+ public function file_exists($path){
+ if($path=='/'){
+ return true;
+ }
+ return $this->basicOperation('file_exists',$path);
+ }
+ public function filectime($path){
+ return $this->basicOperation('filectime',$path);
+ }
+ public function filemtime($path){
+ return $this->basicOperation('filemtime',$path);
+ }
+ public function setFileMtime($path, $mtime){
+ return $this->basicOperation('setFileMtime',$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);
+ }
+ public function unlink($path){
+ return $this->basicOperation('unlink',$path,array('delete'));
+ }
+ public function rename($path1,$path2){
+ if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and $this->is_writable($path1) and OC_Filesystem::isValidPath($path2)){
+ $run=true;
+ OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run));
+ if($run){
+ $mp1=$this->getMountPoint($path1);
+ $mp2=$this->getMountPoint($path2);
+ if($mp1==$mp2){
+ 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));
+ $storage1->unlink($this->getInternalPath($path1));
+ }
+ 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;
+ }
+ }
+ }
+ public function copy($path1,$path2){
+ if(OC_FileProxy::runPreProxies('copy',$path1,$path2) and $this->is_readable($path1) and OC_Filesystem::isValidPath($path2)){
+ $run=true;
+ OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run));
+ $exists=$this->file_exists($path2);
+ if($run and !$exists){
+ OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run));
+ }
+ if($run){
+ OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run));
+ }
+ if($run){
+ $mp1=$this->getMountPoint($path1);
+ $mp2=$this->getMountPoint($path2);
+ if($mp1==$mp2){
+ 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));
+ }
+ 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){
+ OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path2));
+ }
+ OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path2));
+ return $result;
+ }
+ }
+ }
+ public function fopen($path,$mode){
+ $hooks=array();
+ switch($mode){
+ case 'r':
+ $hooks[]='read';
+ break;
+ case 'r+':
+ case 'w+':
+ case 'x+':
+ case 'a+':
+ $hooks[]='read';
+ $hooks[]='write';
+ break;
+ case 'w':
+ case 'x':
+ case 'a':
+ $hooks[]='write';
+ break;
+ default:
+ OC_Log::write('core','invalid mode ('.$mode.') for '.$path,OC_Log::ERROR);
+ }
+
+ 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));
+ }
+ }
+ 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($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;
+ }
+ }
+ }
+
+ public function getMimeType($path){
+ return $this->basicOperation('getMimeType',$path);
+ }
+ public function hash($type,$path){
+ return $this->basicOperation('hash',$path,array('read'));
+ }
+
+ public function free_space($path='/'){
+ return $this->basicOperation('free_space',$path);
+ }
+
+ /**
+ * abstraction for running most basic operations
+ * @param string $operation
+ * @param string #path
+ * @param array (optional) hooks
+ * @param mixed (optional) $extraParam
+ * @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)){
+ $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($run){
+ if($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));
+ }
+ }
+ return $result;
+ }
+ }
+ return null;
+ }
+}
diff --git a/lib/image.php b/lib/image.php
index 6de3ed9104d..97d30f56517 100644
--- a/lib/image.php
+++ b/lib/image.php
@@ -44,34 +44,24 @@ function ellipsis($str, $maxlen) {
*
*/
class OC_Image {
- static private $resource = false; // tmp resource.
- static private $destroy = false; // if the resource is created withing the object.
- static private $imagetype = IMAGETYPE_PNG; // Default to png if file type isn't evident.
- static private $filepath = null;
+ protected $resource = false; // tmp resource.
+ protected $imagetype = IMAGETYPE_PNG; // Default to png if file type isn't evident.
+ protected $filepath = null;
+
/**
* @brief Constructor.
* @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function.
- * If a resource is passed it is the job of the caller to destroy it using imagedestroy($var)
* @returns bool False on error
*/
- function __construct($imageref = null) {
- //OC_Log::write('core','OC_Image::__construct, start', OC_Log::DEBUG);
+ public function __construct($imageref = null) {
+ //OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG);
if(!extension_loaded('gd') || !function_exists('gd_info')) {
//if(!function_exists('imagecreatefromjpeg')) {
- OC_Log::write('core','OC_Image::__construct, GD module not installed', OC_Log::ERROR);
+ OC_Log::write('core',__METHOD__.'(): GD module not installed', OC_Log::ERROR);
return false;
}
if(!is_null($imageref)) {
- self::load($imageref);
- }
- }
-
- /**
- * @brief Destructor.
- */
- function __destruct() {
- if(is_resource(self::$resource) && self::$destroy) {
- imagedestroy(self::$resource); // Why does this issue a warning.
+ $this->load($imageref);
}
}
@@ -80,8 +70,7 @@ class OC_Image {
* @returns bool
*/
public function valid() { // apparently you can't name a method 'empty'...
- $ret = is_resource(self::$resource);
- return $ret;
+ return is_resource($this->resource);
}
/**
@@ -89,7 +78,7 @@ class OC_Image {
* @returns int
*/
public function mimeType() {
- return is_resource(self::$resource) ? image_type_to_mime_type(self::$imagetype) : '';
+ return is_resource($this->resource) ? image_type_to_mime_type($this->imagetype) : '';
}
/**
@@ -97,7 +86,7 @@ class OC_Image {
* @returns int
*/
public function width() {
- return is_resource(self::$resource) ? imagesx(self::$resource) : -1;
+ return is_resource($this->resource) ? imagesx($this->resource) : -1;
}
/**
@@ -105,7 +94,7 @@ class OC_Image {
* @returns int
*/
public function height() {
- return is_resource(self::$resource) ? imagesy(self::$resource) : -1;
+ return is_resource($this->resource) ? imagesy($this->resource) : -1;
}
/**
@@ -122,8 +111,8 @@ class OC_Image {
*/
public function save($filepath=null) {
- if($filepath === null && self::$filepath === null) {
- OC_Log::write('core','OC_Image::save. save() called with no path.', OC_Log::ERROR);
+ if($filepath === null && $this->filepath === null) {
+ OC_Log::write('core',__METHOD__.'(): called with no path.', OC_Log::ERROR);
return false;
} elseif($filepath === null && $this->filepath !== null) {
$filepath = $this->filepath;
@@ -136,37 +125,41 @@ class OC_Image {
*/
private function _output($filepath=null, $really=false) {
if($really === false) {
- header('Content-Type: '.self::mimeType());
+ header('Content-Type: '.$this->mimeType());
$filepath = null; // Just being cautious ;-)
} else {
if(!is_writable(dirname($filepath))) {
- OC_Log::write('core','OC_Image::_output. Directory \''.dirname($filepath).'\' is not writable.', OC_Log::ERROR);
+ OC_Log::write('core',__METHOD__.'(): Directory \''.dirname($filepath).'\' is not writable.', OC_Log::ERROR);
return false;
} elseif(is_writable(dirname($filepath)) && file_exists($filepath) && !is_writable($filepath)) {
- OC_Log::write('core','OC_Image::_output. File \''.$filepath.'\' is not writable.', OC_Log::ERROR);
+ OC_Log::write('core',__METHOD__.'(): File \''.$filepath.'\' is not writable.', OC_Log::ERROR);
return false;
}
}
+ if (!$this->valid()) {
+ return false;
+ }
+
$retval = false;
- switch(self::$imagetype) {
+ switch($this->imagetype) {
case IMAGETYPE_GIF:
- $retval = imagegif(self::$resource, $filepath);
+ $retval = imagegif($this->resource, $filepath);
break;
case IMAGETYPE_JPEG:
- $retval = imagejpeg(self::$resource, $filepath);
+ $retval = imagejpeg($this->resource, $filepath);
break;
case IMAGETYPE_PNG:
- $retval = imagepng(self::$resource, $filepath);
+ $retval = imagepng($this->resource, $filepath);
break;
case IMAGETYPE_XBM:
- $retval = imagexbm(self::$resource, $filepath);
+ $retval = imagexbm($this->resource, $filepath);
break;
case IMAGETYPE_WBMP:
case IMAGETYPE_BMP:
- $retval = imagewbmp(self::$resource, $filepath);
+ $retval = imagewbmp($this->resource, $filepath);
break;
default:
- $retval = imagepng(self::$resource, $filepath);
+ $retval = imagepng($this->resource, $filepath);
}
return $retval;
}
@@ -175,14 +168,14 @@ class OC_Image {
* @brief Prints the image when called as $image().
*/
public function __invoke() {
- return self::show();
+ return $this->show();
}
/**
* @returns Returns the image resource in any.
*/
public function resource() {
- return self::$resource;
+ return $this->resource;
}
/**
@@ -190,9 +183,9 @@ class OC_Image {
*/
function __toString() {
ob_start();
- $res = imagepng(self::$resource);
+ $res = imagepng($this->resource);
if (!$res) {
- OC_Log::write('core','OC_Image::_string. Error writing image',OC_Log::ERROR);
+ OC_Log::write('core','OC_Image->__toString. Error writing image',OC_Log::ERROR);
}
return base64_encode(ob_get_clean());
}
@@ -204,18 +197,18 @@ class OC_Image {
*/
public function fixOrientation() {
if(!is_callable('exif_read_data')){
- OC_Log::write('core','OC_Image::fixOrientation() Exif module not enabled.', OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->fixOrientation() Exif module not enabled.', OC_Log::DEBUG);
return false;
}
- if(!is_resource(self::$resource)) {
- OC_Log::write('core','OC_Image::fixOrientation() No image loaded.', OC_Log::DEBUG);
+ if(!is_resource($this->resource)) {
+ OC_Log::write('core','OC_Image->fixOrientation() No image loaded.', OC_Log::DEBUG);
return false;
}
- if(is_null(self::$filepath) || !is_readable(self::$filepath)) {
- OC_Log::write('core','OC_Image::fixOrientation() No readable file path set.', OC_Log::DEBUG);
+ if(is_null($this->filepath) || !is_readable($this->filepath)) {
+ OC_Log::write('core','OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG);
return false;
}
- $exif = exif_read_data(self::$filepath, 'IFD0');
+ $exif = exif_read_data($this->filepath, 'IFD0');
if(!$exif) {
return false;
}
@@ -223,7 +216,7 @@ class OC_Image {
return true; // Nothing to fix
}
$o = $exif['Orientation'];
- OC_Log::write('core','OC_Image::fixOrientation() Orientation: '.$o, OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG);
$rotate = 0;
$flip = false;
switch($o) {
@@ -257,27 +250,27 @@ class OC_Image {
$flip = true;
break;
case 8:
- $rotate = 270;
+ $rotate = 90;
$flip = false;
break;
}
if($rotate) {
- $res = imagerotate(self::$resource, $rotate, -1);
+ $res = imagerotate($this->resource, $rotate, -1);
if($res) {
if(imagealphablending($res, true)) {
if(imagesavealpha($res, true)) {
- self::$resource = $res;
+ $this->resource = $res;
return true;
} else {
- OC_Log::write('core','OC_Image::fixOrientation() Error during alphasaving.', OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->fixOrientation() Error during alphasaving.', OC_Log::DEBUG);
return false;
}
} else {
- OC_Log::write('core','OC_Image::fixOrientation() Error during alphablending.', OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->fixOrientation() Error during alphablending.', OC_Log::DEBUG);
return false;
}
} else {
- OC_Log::write('core','OC_Image::fixOrientation() Error during oriention fixing.', OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->fixOrientation() Error during oriention fixing.', OC_Log::DEBUG);
return false;
}
}
@@ -286,20 +279,19 @@ class OC_Image {
/**
* @brief Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
* @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function.
- * If a resource is passed it is the job of the caller to destroy it using imagedestroy($var)
* @returns An image resource or false on error
*/
public function load($imageref) {
- if(self::loadFromFile($imageref) !== false) {
- return self::$resource;
- } elseif(self::loadFromBase64($imageref) !== false) {
- return self::$resource;
- } elseif(self::loadFromData($imageref) !== false) {
- return self::$resource;
- } elseif(self::loadFromResource($imageref) !== false) {
- return self::$resource;
+ if($this->loadFromFile($imageref) !== false) {
+ return $this->resource;
+ } elseif($this->loadFromBase64($imageref) !== false) {
+ return $this->resource;
+ } elseif($this->loadFromData($imageref) !== false) {
+ return $this->resource;
+ } elseif($this->loadFromResource($imageref) !== false) {
+ return $this->resource;
} else {
- OC_Log::write('core','OC_Image::load, couldn\'t load anything. Giving up!', OC_Log::DEBUG);
+ OC_Log::write('core',__METHOD__.'(): couldn\'t load anything. Giving up!', OC_Log::DEBUG);
return false;
}
}
@@ -312,45 +304,45 @@ class OC_Image {
public function loadFromFile($imagepath=false) {
if(!is_file($imagepath) || !file_exists($imagepath) || !is_readable($imagepath)) {
// Debug output disabled because this method is tried before loadFromBase64?
- OC_Log::write('core','OC_Image::loadFromFile, couldn\'t load: '.ellipsis($imagepath, 50), OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->loadFromFile, couldn\'t load: '.ellipsis($imagepath, 50), OC_Log::DEBUG);
return false;
}
$itype = exif_imagetype($imagepath);
switch($itype) {
case IMAGETYPE_GIF:
if (imagetypes() & IMG_GIF) {
- self::$resource = imagecreatefromgif($imagepath);
+ $this->resource = imagecreatefromgif($imagepath);
} else {
- OC_Log::write('core','OC_Image::loadFromFile, GIF images not supported: '.$imagepath, OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->loadFromFile, GIF images not supported: '.$imagepath, OC_Log::DEBUG);
}
break;
case IMAGETYPE_JPEG:
if (imagetypes() & IMG_JPG) {
- self::$resource = imagecreatefromjpeg($imagepath);
+ $this->resource = imagecreatefromjpeg($imagepath);
} else {
- OC_Log::write('core','OC_Image::loadFromFile, JPG images not supported: '.$imagepath, OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->loadFromFile, JPG images not supported: '.$imagepath, OC_Log::DEBUG);
}
break;
case IMAGETYPE_PNG:
if (imagetypes() & IMG_PNG) {
- self::$resource = imagecreatefrompng($imagepath);
+ $this->resource = imagecreatefrompng($imagepath);
} else {
- OC_Log::write('core','OC_Image::loadFromFile, PNG images not supported: '.$imagepath, OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->loadFromFile, PNG images not supported: '.$imagepath, OC_Log::DEBUG);
}
break;
case IMAGETYPE_XBM:
if (imagetypes() & IMG_XPM) {
- self::$resource = imagecreatefromxbm($imagepath);
+ $this->resource = imagecreatefromxbm($imagepath);
} else {
- OC_Log::write('core','OC_Image::loadFromFile, XBM/XPM images not supported: '.$imagepath, OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->loadFromFile, XBM/XPM images not supported: '.$imagepath, OC_Log::DEBUG);
}
break;
case IMAGETYPE_WBMP:
case IMAGETYPE_BMP:
if (imagetypes() & IMG_WBMP) {
- self::$resource = imagecreatefromwbmp($imagepath);
+ $this->resource = imagecreatefromwbmp($imagepath);
} else {
- OC_Log::write('core','OC_Image::loadFromFile, (W)BMP images not supported: '.$imagepath, OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->loadFromFile, (W)BMP images not supported: '.$imagepath, OC_Log::DEBUG);
}
break;
/*
@@ -378,17 +370,16 @@ class OC_Image {
break;
*/
default:
- self::$resource = imagecreatefromstring(file_get_contents($imagepath));
+ $this->resource = imagecreatefromstring(file_get_contents($imagepath));
$itype = IMAGETYPE_PNG;
- OC_Log::write('core','OC_Image::loadFromFile, Default', OC_Log::DEBUG);
+ OC_Log::write('core','OC_Image->loadFromFile, Default', OC_Log::DEBUG);
break;
}
if($this->valid()) {
- self::$imagetype = $itype;
- self::$filepath = $imagepath;
- self::$destroy = true;
+ $this->imagetype = $itype;
+ $this->filepath = $imagepath;
}
- return self::$resource;
+ return $this->resource;
}
/**
@@ -400,13 +391,12 @@ class OC_Image {
if(is_resource($str)) {
return false;
}
- self::$resource = imagecreatefromstring($str);
- if(!self::$resource) {
- OC_Log::write('core','OC_Image::loadFromData, couldn\'t load', OC_Log::DEBUG);
+ $this->resource = imagecreatefromstring($str);
+ if(!$this->resource) {
+ OC_Log::write('core','OC_Image->loadFromData, couldn\'t load', OC_Log::DEBUG);
return false;
}
- self::$destroy = true;
- return self::$resource;
+ return $this->resource;
}
/**
@@ -420,20 +410,19 @@ class OC_Image {
}
$data = base64_decode($str);
if($data) { // try to load from string data
- self::$resource = imagecreatefromstring($data);
- if(!self::$resource) {
- OC_Log::write('core','OC_Image::loadFromBase64, couldn\'t load', OC_Log::DEBUG);
+ $this->resource = imagecreatefromstring($data);
+ if(!$this->resource) {
+ OC_Log::write('core','OC_Image->loadFromBase64, couldn\'t load', OC_Log::DEBUG);
return false;
}
- self::$destroy = true;
- return self::$resource;
+ return $this->resource;
} else {
return false;
}
}
/**
- * @brief Checks if image resource is valid and assigns it to self::$resource.
+ * @brief Checks if image resource is valid and assigns it to $this->resource.
* @param $res An image resource.
* @returns An image resource or false on error
*/
@@ -441,7 +430,7 @@ class OC_Image {
if(!is_resource($res)) {
return false;
}
- self::$resource = $res;
+ $this->resource = $res;
}
/**
@@ -450,12 +439,12 @@ class OC_Image {
* @returns bool
*/
public function resize($maxsize) {
- if(!self::$resource) {
- OC_Log::write('core','OC_Image::resize, No image loaded', OC_Log::ERROR);
+ if(!$this->resource) {
+ OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR);
return false;
}
- $width_orig=imageSX(self::$resource);
- $height_orig=imageSY(self::$resource);
+ $width_orig=imageSX($this->resource);
+ $height_orig=imageSY($this->resource);
$ratio_orig = $width_orig/$height_orig;
if ($ratio_orig > 1) {
@@ -468,18 +457,18 @@ class OC_Image {
$process = imagecreatetruecolor(round($new_width), round($new_height));
if ($process == false) {
- OC_Log::write('core','OC_Image::resize. Error creating true color image',OC_Log::ERROR);
+ OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR);
imagedestroy($process);
return false;
}
- imagecopyresampled($process, self::$resource, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
+ imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
if ($process == false) {
- OC_Log::write('core','OC_Image::resize. Error resampling process image '.$new_width.'x'.$new_height,OC_Log::ERROR);
+ OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$new_width.'x'.$new_height,OC_Log::ERROR);
imagedestroy($process);
return false;
}
- self::$resource = $process;
+ $this->resource = $process;
return true;
}
@@ -488,12 +477,12 @@ class OC_Image {
* @returns bool for success or failure
*/
public function centerCrop() {
- if(!self::$resource) {
- OC_Log::write('core','OC_Image::centerCrop, No image loaded', OC_Log::ERROR);
+ if(!$this->resource) {
+ OC_Log::write('core','OC_Image->centerCrop, No image loaded', OC_Log::ERROR);
return false;
}
- $width_orig=imageSX(self::$resource);
- $height_orig=imageSY(self::$resource);
+ $width_orig=imageSX($this->resource);
+ $height_orig=imageSY($this->resource);
if($width_orig === $height_orig) {
return true;
}
@@ -509,17 +498,17 @@ class OC_Image {
}
$process = imagecreatetruecolor($width, $height);
if ($process == false) {
- OC_Log::write('core','OC_Image::centerCrop. Error creating true color image',OC_Log::ERROR);
+ OC_Log::write('core','OC_Image->centerCrop. Error creating true color image',OC_Log::ERROR);
imagedestroy($process);
return false;
}
- imagecopyresampled($process, self::$resource, 0, 0, $x, $y, $width, $height, $width, $height);
+ imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $width, $height, $width, $height);
if ($process == false) {
- OC_Log::write('core','OC_Image::centerCrop. Error resampling process image '.$width.'x'.$height,OC_Log::ERROR);
+ OC_Log::write('core','OC_Image->centerCrop. Error resampling process image '.$width.'x'.$height,OC_Log::ERROR);
imagedestroy($process);
return false;
}
- self::$resource = $process;
+ $this->resource = $process;
return true;
}
@@ -532,26 +521,26 @@ class OC_Image {
* @returns bool for success or failure
*/
public function crop($x, $y, $w, $h) {
- if(!self::$resource) {
- OC_Log::write('core','OC_Image::crop, No image loaded', OC_Log::ERROR);
+ if(!$this->resource) {
+ OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR);
return false;
}
- $width_orig=imageSX(self::$resource);
- $height_orig=imageSY(self::$resource);
- //OC_Log::write('core','OC_Image::crop. Original size: '.$width_orig.'x'.$height_orig, OC_Log::DEBUG);
+ $width_orig=imageSX($this->resource);
+ $height_orig=imageSY($this->resource);
+ //OC_Log::write('core',__METHOD__.'(): Original size: '.$width_orig.'x'.$height_orig, OC_Log::DEBUG);
$process = imagecreatetruecolor($w, $h);
if ($process == false) {
- OC_Log::write('core','OC_Image::crop. Error creating true color image',OC_Log::ERROR);
+ OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR);
imagedestroy($process);
return false;
}
- imagecopyresampled($process, self::$resource, 0, 0, $x, $y, $w, $h, $w, $h);
+ imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
if ($process == false) {
- OC_Log::write('core','OC_Image::crop. Error resampling process image '.$w.'x'.$h,OC_Log::ERROR);
+ OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$w.'x'.$h,OC_Log::ERROR);
imagedestroy($process);
return false;
}
- self::$resource = $process;
+ $this->resource = $process;
return true;
}
}
diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php
index 5fd35fa3e52..c3dc2942aef 100644
--- a/lib/search/provider/file.php
+++ b/lib/search/provider/file.php
@@ -2,14 +2,15 @@
class OC_Search_Provider_File extends OC_Search_Provider{
function search($query){
- $files=OC_Filesystem::search($query);
+ $files=OC_FileCache::search($query,true);
$results=array();
- foreach($files as $file){
- if(OC_Filesystem::is_dir($file)){
+ foreach($files as $fileData){
+ $file=$fileData['path'];
+ if($fileData['mime']=='httpd/unix-directory'){
$results[]=new OC_Search_Result(basename($file),'',OC_Helper::linkTo( 'files', 'index.php?dir='.$file ),'Files');
}else{
- $mime=OC_Filesystem::getMimeType($file);
- $mimeBase=substr($mime,0,strpos($mime,'/'));
+ $mime=$fileData['mime'];
+ $mimeBase=$fileData['mimepart'];
switch($mimeBase){
case 'audio':
break;
diff --git a/lib/util.php b/lib/util.php
index 2fba1206bff..43fb4413f04 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -37,7 +37,7 @@ class OC_Util {
if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem
//first set up the local "root" storage
- OC_Filesystem::mount('local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/');
+ OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/');
OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root";
if( !is_dir( OC::$CONFIG_DATADIRECTORY )){
@@ -45,7 +45,7 @@ class OC_Util {
}
//jail the user into his "home" directory
- OC_Filesystem::chroot("/$user/$root");
+ OC_Filesystem::init('/'.$user.'/'.$root);
$quotaProxy=new OC_FileProxy_Quota();
OC_FileProxy::register($quotaProxy);
self::$fsSetup=true;
@@ -62,7 +62,7 @@ class OC_Util {
* @return array
*/
public static function getVersion(){
- return array(3,00,0);
+ return array(3,00,1);
}
/**
@@ -110,7 +110,7 @@ class OC_Util {
/**
* @brief Add a custom element to the header
* @param string tag tag name of the element
- * @param array $attributes array of attrobutes for the element
+ * @param array $attributes array of attributes for the element
* @param string $text the text content for the element
*/
public static function addHeader( $tag, $attributes, $text=''){
@@ -226,7 +226,7 @@ class OC_Util {
$errors[]=array('error'=>'PHP module ctype is not installed.<br/>','hint'=>'Please ask your server administrator to install the module.');
}
- if(file_exists(OC::$SERVERROOT."/config/config.php") and !is_writeable(OC::$SERVERROOT."/config/config.php")){
+ if(file_exists(OC::$SERVERROOT."/config/config.php") and !is_writable(OC::$SERVERROOT."/config/config.php")){
$errors[]=array('error'=>"Can't write into config directory 'config'",'hint'=>"You can usually fix this by giving the webserver use write access to the config directory in owncloud");
}
diff --git a/owncloud.db.filesystem b/owncloud.db.filesystem
new file mode 100644
index 00000000000..082977a37ef
--- /dev/null
+++ b/owncloud.db.filesystem
Binary files differ
diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php
index 5c07285cfca..d4e3c58ac11 100644
--- a/settings/ajax/setquota.php
+++ b/settings/ajax/setquota.php
@@ -6,10 +6,13 @@ require_once('../../lib/base.php');
OC_JSON::checkAdminUser();
$username = $_POST["username"];
+
+//make sure the quota is in the expected format
$quota= OC_Helper::computerFileSize($_POST["quota"]);
+$quota=OC_Helper::humanFileSize($quota);
// Return Success story
OC_Preferences::setValue($username,'files','quota',$quota);
-OC_JSON::success(array("data" => array( "username" => $username ,'quota'=>OC_Helper::humanFileSize($quota))));
+OC_JSON::success(array("data" => array( "username" => $username ,'quota'=>$quota)));
?>
diff --git a/settings/personal.php b/settings/personal.php
index 3b90827ed99..07030109de7 100755
--- a/settings/personal.php
+++ b/settings/personal.php
@@ -16,7 +16,8 @@ OC_Util::addStyle( '3rdparty', 'chosen' );
OC_App::setActiveNavigationEntry( 'personal' );
// calculate the disc space
-$used=OC_Filesystem::filesize('/');
+$rootInfo=OC_FileCache::get('');
+$used=$rootInfo['size'];
$free=OC_Filesystem::free_space();
$total=$free+$used;
$relative=round(($used/$total)*10000)/100;
diff --git a/settings/users.php b/settings/users.php
index 686c4b6a9bf..e5dcc049481 100644
--- a/settings/users.php
+++ b/settings/users.php
@@ -18,7 +18,7 @@ $users = array();
$groups = array();
foreach( OC_User::getUsers() as $i ){
- $users[] = array( "name" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ),'quota'=>OC_Helper::humanFileSize(OC_Preferences::getValue($i,'files','quota',0)));
+ $users[] = array( "name" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ),'quota'=>OC_Preferences::getValue($i,'files','quota',0));
}
foreach( OC_Group::getGroups() as $i ){