diff options
109 files changed, 8463 insertions, 1322 deletions
@@ -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> <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> <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> <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> <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]{ '—'[ 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 Binary files differnew file mode 100644 index 00000000000..72ea7ccb532 --- /dev/null +++ b/apps/contacts/css/Jcrop.gif 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 Binary files differnew file mode 100644 index 00000000000..f57511e1000 --- /dev/null +++ b/apps/contacts/img/person_large.png 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; ?>&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 Binary files differnew file mode 100644 index 00000000000..db1b1970f17 --- /dev/null +++ b/core/img/actions/add.png 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 Binary files differnew file mode 100644 index 00000000000..ea811ac5453 --- /dev/null +++ b/core/img/actions/mail.png 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"> <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 Binary files differnew file mode 100644 index 00000000000..082977a37ef --- /dev/null +++ b/owncloud.db.filesystem 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 ){ |