1
0
mirror of https://github.com/owncloudarchive/contacts.git synced 2025-01-30 19:52:17 +01:00

merge master into gallery

This commit is contained in:
Robin Appelman 2012-11-24 12:36:57 +01:00
commit 231b897fcf
122 changed files with 7812 additions and 4000 deletions

View File

@ -13,20 +13,33 @@ OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
$id = $_POST['id'];
$book = OC_Contacts_App::getAddressbook($id);// is owner access check
if(!OC_Contacts_Addressbook::setActive($id, $_POST['active'])) {
try {
$book = OCA\Contacts\Addressbook::find($id); // is owner access check
} catch(Exception $e) {
OCP\JSON::error(
array(
'data' => array(
'message' => $e->getMessage(),
'file'=>$_POST['file']
)
)
);
exit();
}
if(!OCA\Contacts\Addressbook::setActive($id, $_POST['active'])) {
OCP\Util::writeLog('contacts',
'ajax/activation.php: Error activating addressbook: '. $id,
OCP\Util::ERROR);
OCP\JSON::error(array(
'data' => array(
'message' => OC_Contacts_App::$l10n->t('Error (de)activating addressbook.'))));
'message' => OCA\Contacts\App::$l10n->t('Error (de)activating addressbook.'))));
exit();
}
OCP\JSON::success(array(
'active' => OC_Contacts_Addressbook::isActive($id),
'active' => OCA\Contacts\Addressbook::isActive($id),
'id' => $id,
'addressbook' => $book,
));

View File

@ -25,13 +25,13 @@ $description = isset($_POST['description'])
if(is_null($name)) {
bailOut('Cannot add addressbook with an empty name.');
}
$bookid = OC_Contacts_Addressbook::add($userid, $name, $description);
$bookid = OCA\Contacts\Addressbook::add($userid, $name, $description);
if(!$bookid) {
bailOut('Error adding addressbook: '.$name);
}
if(!OC_Contacts_Addressbook::setActive($bookid, 1)) {
if(!OCA\Contacts\Addressbook::setActive($bookid, 1)) {
bailOut('Error activating addressbook.');
}
$addressbook = OC_Contacts_App::getAddressbook($bookid);
$addressbook = OCA\Contacts\Addressbook::find($bookid);
OCP\JSON::success(array('data' => array('addressbook' => $addressbook)));

View File

@ -28,11 +28,11 @@ require_once __DIR__.'/../loghandler.php';
$id = $_POST['id'];
if(!$id) {
bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
bailOut(OCA\Contacts\App::$l10n->t('id is not set.'));
}
try {
OC_Contacts_Addressbook::delete($id);
OCA\Contacts\Addressbook::delete($id);
} catch(Exception $e) {
bailOut($e->getMessage());
}

View File

@ -16,24 +16,24 @@ $name = trim(strip_tags($_POST['name']));
$description = trim(strip_tags($_POST['description']));
if(!$id) {
bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
bailOut(OCA\Contacts\App::$l10n->t('id is not set.'));
}
if(!$name) {
bailOut(OC_Contacts_App::$l10n->t('Cannot update addressbook with an empty name.'));
bailOut(OCA\Contacts\App::$l10n->t('Cannot update addressbook with an empty name.'));
}
try {
OC_Contacts_Addressbook::edit($id, $name, $description);
OCA\Contacts\Addressbook::edit($id, $name, $description);
} catch(Exception $e) {
bailOut($e->getMessage());
}
if(!OC_Contacts_Addressbook::setActive($id, $_POST['active'])) {
bailOut(OC_Contacts_App::$l10n->t('Error (de)activating addressbook.'));
if(!OCA\Contacts\Addressbook::setActive($id, $_POST['active'])) {
bailOut(OCA\Contacts\App::$l10n->t('Error (de)activating addressbook.'));
}
$addressbook = OC_Contacts_App::getAddressbook($id);
$addressbook = OCA\Contacts\Addressbook::find($id);
OCP\JSON::success(array(
'data' => array('addressbook' => $addressbook),
));

29
ajax/categories/add.php Normal file
View File

@ -0,0 +1,29 @@
<?php
/**
* Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
require_once __DIR__.'/../loghandler.php';
$category = isset($_POST['category']) ? trim(strip_tags($_POST['category'])) : null;
if(is_null($category) || $category === "") {
bailOut(OCA\Contacts\App::$l10n->t('No category name given.'));
}
$catman = new OC_VCategories('contact');
$id = $catman->add($category);
if($id !== false) {
OCP\JSON::success(array('data' => array('id'=>$id)));
} else {
bailOut(OCA\Contacts\App::$l10n->t('Error adding group.'));
}

34
ajax/categories/addto.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/**
* Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
require_once __DIR__.'/../loghandler.php';
$categoryid = isset($_POST['categoryid']) ? $_POST['categoryid'] : null;
$contactids = isset($_POST['contactids']) ? $_POST['contactids'] : null;
if(is_null($categoryid)) {
bailOut(OCA\Contacts\App::$l10n->t('Group ID missing from request.'));
}
if(is_null($contactids)) {
bailOut(OCA\Contacts\App::$l10n->t('Contact ID missing from request.'));
}
$catmgr = OCA\Contacts\App::getVCategories();
foreach($contactids as $contactid) {
debug('contactid: ' . $contactid . ', categoryid: ' . $categoryid);
$catmgr->addToCategory($contactid, $categoryid);
}
OCP\JSON::success();

View File

@ -14,10 +14,11 @@ $id = isset($_GET['id'])?$_GET['id']:null;
if(is_null($id)) {
OCP\JSON::error(array(
'data' => array(
'message' => OC_Contacts_App::$l10n->t('No ID provided'))));
'message' => OCA\Contacts\App::$l10n->t('No ID provided'))));
exit();
}
$vcard = OC_Contacts_App::getContactVCard( $id );
$vcard = OCA\Contacts\App::getContactVCard( $id );
foreach($vcard->children as $property) {
if($property->name == 'CATEGORIES') {
$checksum = md5($property->serialize());
@ -31,4 +32,4 @@ foreach($vcard->children as $property) {
}
OCP\JSON::error(array(
'data' => array(
'message' => OC_Contacts_App::$l10n->t('Error setting checksum.'))));
'message' => OCA\Contacts\App::$l10n->t('Error setting checksum.'))));

View File

@ -13,36 +13,39 @@ OCP\JSON::callCheck();
require_once __DIR__.'/../loghandler.php';
$categories = isset($_POST['categories'])?$_POST['categories']:null;
$categories = isset($_POST['categories']) ? $_POST['categories'] : null;
$fromobjects = (isset($_POST['fromobjects'])
&& ($_POST['fromobjects'] === 'true' || $_POST['fromobjects'] === '1')) ? true : false;
if(is_null($categories)) {
bailOut(OC_Contacts_App::$l10n->t('No categories selected for deletion.'));
bailOut(OCA\Contacts\App::$l10n->t('No categories selected for deletion.'));
}
debug(print_r($categories, true));
if($fromobjects) {
$addressbooks = OCA\Contacts\Addressbook::all(OCP\USER::getUser());
if(count($addressbooks) == 0) {
bailOut(OCA\Contacts\App::$l10n->t('No address books found.'));
}
$addressbookids = array();
foreach($addressbooks as $addressbook) {
$addressbookids[] = $addressbook['id'];
}
$contacts = OCA\Contacts\VCard::all($addressbookids);
if(count($contacts) == 0) {
bailOut(OCA\Contacts\App::$l10n->t('No contacts found.'));
}
$addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser());
if(count($addressbooks) == 0) {
bailOut(OC_Contacts_App::$l10n->t('No address books found.'));
}
$addressbookids = array();
foreach($addressbooks as $addressbook) {
$addressbookids[] = $addressbook['id'];
}
$contacts = OC_Contacts_VCard::all($addressbookids);
if(count($contacts) == 0) {
bailOut(OC_Contacts_App::$l10n->t('No contacts found.'));
$cards = array();
foreach($contacts as $contact) {
$cards[] = array($contact['id'], $contact['carddata']);
}
}
$cards = array();
foreach($contacts as $contact) {
$cards[] = array($contact['id'], $contact['carddata']);
}
debug('Before delete: '.print_r($categories, true));
$catman = new OC_VCategories('contacts');
$catman = new OC_VCategories('contact');
$catman->delete($categories, $cards);
debug('After delete: '.print_r($catman->categories(), true));
OC_Contacts_VCard::updateDataByID($cards);
OCP\JSON::success(array('data' => array('categories'=>$catman->categories())));
if($fromobjects) {
OCA\Contacts\VCard::updateDataByID($cards);
}
OCP\JSON::success();

View File

@ -10,6 +10,37 @@
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
$categories = OC_Contacts_App::getCategories();
$catmgr = OCA\Contacts\App::getVCategories();
$categories = $catmgr->categories(OC_VCategories::FORMAT_MAP);
foreach($categories as &$category) {
$ids = array();
$contacts = $catmgr->itemsForCategory(
$category['name'],
array(
'tablename' => '*PREFIX*contacts_cards',
'fields' => array('id',),
));
foreach($contacts as $contact) {
$ids[] = $contact['id'];
}
$category['contacts'] = $ids;
}
OCP\JSON::success(array('data' => array('categories'=>$categories)));
$favorites = $catmgr->getFavorites();
OCP\JSON::success(array(
'data' => array(
'categories' => $categories,
'favorites' => $favorites,
'shared' => OCP\Share::getItemsSharedWith('addressbook', OCA\Contacts\Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS),
'lastgroup' => OCP\Config::getUserValue(
OCP\User::getUser(),
'contacts',
'lastgroup', 'all'),
'sortorder' => OCP\Config::getUserValue(
OCP\User::getUser(),
'contacts',
'groupsort', ''),
)
)
);

View File

@ -0,0 +1,34 @@
<?php
/**
* Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
require_once __DIR__.'/../loghandler.php';
$categoryid = isset($_POST['categoryid']) ? $_POST['categoryid'] : null;
$contactids = isset($_POST['contactids']) ? $_POST['contactids'] : null;
if(is_null($categoryid)) {
bailOut(OCA\Contacts\App::$l10n->t('Group ID missing from request.'));
}
if(is_null($contactids)) {
bailOut(OCA\Contacts\App::$l10n->t('Contact ID missing from request.'));
}
$catmgr = OCA\Contacts\App::getVCategories();
foreach($contactids as $contactid) {
debug('id: ' . $contactid .', categoryid: ' . $categoryid);
$catmgr->removeFromCategory($contactid, $categoryid);
}
OCP\JSON::success();

View File

@ -11,7 +11,7 @@ OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
OC_Contacts_App::scanCategories();
$categories = OC_Contacts_App::getCategories();
OCA\Contacts\App::scanCategories();
$categories = OCA\Contacts\App::getCategories();
OCP\JSON::success(array('data' => array('categories'=>$categories)));

View File

@ -27,23 +27,23 @@ OCP\JSON::callCheck();
require_once __DIR__.'/../loghandler.php';
$aid = isset($_POST['aid'])?$_POST['aid']:null;
$aid = isset($_POST['aid']) ? $_POST['aid'] : null;
if(!$aid) {
$aid = min(OC_Contacts_Addressbook::activeIds()); // first active addressbook.
$aid = min(OCA\Contacts\Addressbook::activeIds()); // first active addressbook.
}
$isnew = isset($_POST['isnew'])?$_POST['isnew']:false;
$fn = trim($_POST['fn']);
$n = trim($_POST['n']);
debug('Adding new contact to: ' . $aid);
$vcard = new OC_VObject('VCARD');
$vcard->setUID();
$vcard->setString('FN', $fn);
$vcard->setString('N', $n);
$isnew = isset($_POST['isnew']) ? $_POST['isnew'] : false;
$vcard = Sabre\VObject\Component::create('VCARD');
$uid = substr(md5(rand().time()), 0, 10);
$vcard->add('UID', $uid);
debug('vobject: ', print_r($vcard->serialize(), true));
$id = null;
try {
$id = OC_Contacts_VCard::add($aid, $vcard, null, $isnew);
$id = OCA\Contacts\VCard::add($aid, $vcard, null, $isnew);
} catch(Exception $e) {
bailOut($e->getMessage());
}
@ -52,7 +52,7 @@ if(!$id) {
bailOut('There was an error adding the contact.');
}
$lastmodified = OC_Contacts_App::lastModified($vcard);
$lastmodified = OCA\Contacts\App::lastModified($vcard);
if(!$lastmodified) {
$lastmodified = new DateTime();
}
@ -60,6 +60,7 @@ OCP\JSON::success(array(
'data' => array(
'id' => $id,
'aid' => $aid,
'details' => OCA\Contacts\VCard::structureContact($vcard),
'lastmodified' => $lastmodified->format('U')
)
));

View File

@ -1,168 +0,0 @@
<?php
/**
* ownCloud - Addressbook
*
* @author Jakob Sack
* @copyright 2011 Jakob Sack mail@jakobsack.de
*
* 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/>.
*
*/
// Check if we are a user
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
require_once __DIR__.'/../loghandler.php';
$id = isset($_POST['id'])?$_POST['id']:null;
$name = isset($_POST['name'])?$_POST['name']:null;
$value = isset($_POST['value'])?$_POST['value']:null;
$parameters = isset($_POST['parameters'])?$_POST['parameters']:array();
$vcard = OC_Contacts_App::getContactVCard($id);
$l10n = OC_Contacts_App::$l10n;
if(!$name) {
bailOut($l10n->t('element name is not set.'));
}
if(!$id) {
bailOut($l10n->t('id is not set.'));
}
if(!$vcard) {
bailOut($l10n->t('Could not parse contact: ').$id);
}
if(!is_array($value)) {
$value = trim($value);
if(!$value
&& in_array(
$name,
array('TEL', 'EMAIL', 'ORG', 'BDAY', 'URL', 'NICKNAME', 'NOTE'))
) {
bailOut($l10n->t('Cannot add empty property.'));
}
} elseif($name === 'ADR') { // only add if non-empty elements.
$empty = true;
foreach($value as $part) {
if(trim($part) != '') {
$empty = false;
break;
}
}
if($empty) {
bailOut($l10n->t('At least one of the address fields has to be filled out.'));
}
}
// Prevent setting a duplicate entry
$current = $vcard->select($name);
foreach($current as $item) {
$tmpvalue = (is_array($value)?implode(';', $value):$value);
if($tmpvalue == $item->value) {
bailOut($l10n->t('Trying to add duplicate property: '.$name.': '.$tmpvalue));
}
}
if(is_array($value)) {
// NOTE: Important, otherwise the compound value will
// be set in the order the fields appear in the form!
ksort($value);
$value = array_map('strip_tags', $value);
} else {
$value = strip_tags($value);
}
/* preprocessing 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 'NOTE':
$value = str_replace('\n', ' \\n', $value);
break;
case 'NICKNAME':
// TODO: Escape commas and semicolons.
break;
case 'EMAIL':
$value = strtolower($value);
break;
case 'TEL':
case 'ADR':
break;
case 'IMPP':
if(is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) {
bailOut(OC_Contacts_App::$l10n->t('Missing IM parameter.'));
}
$impp = OC_Contacts_App::getIMOptions($parameters['X-SERVICE-TYPE']);
if(is_null($impp)) {
bailOut(OC_Contacts_App::$l10n->t('Unknown IM: '.$parameters['X-SERVICE-TYPE']));
}
$value = $impp['protocol'] . ':' . $value;
break;
}
switch($name) {
case 'NOTE':
$vcard->setString('NOTE', $value);
break;
default:
$property = $vcard->addProperty($name, $value); //, $parameters);
break;
}
$line = count($vcard->children) - 1;
// Apparently Sabre\VObject\Parameter doesn't do well with
// multiple values or I don't know how to do it. Tanghus.
foreach ($parameters as $key=>$element) {
if(is_array($element) /*&& strtoupper($key) == 'TYPE'*/) {
// NOTE: Maybe this doesn't only apply for TYPE?
// And it probably shouldn't be done here anyways :-/
foreach($element as $e) {
if($e != '' && !is_null($e)) {
if(trim($e)) {
$vcard->children[$line]->parameters[] = new Sabre\VObject\Parameter($key, $e);
}
}
}
} else {
if(trim($element)) {
$vcard->children[$line]->parameters[] = new Sabre\VObject\Parameter($key, $element);
}
}
}
$checksum = md5($vcard->children[$line]->serialize());
try {
OC_Contacts_VCard::edit($id, $vcard);
} catch(Exception $e) {
bailOut($e->getMessage());
}
OCP\JSON::success(array(
'data' => array(
'checksum' => $checksum,
'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U'))
)
);

View File

@ -29,11 +29,11 @@ require_once __DIR__.'/../loghandler.php';
$id = isset($_POST['id']) ? $_POST['id'] : null;
if(!$id) {
bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
bailOut(OCA\Contacts\App::$l10n->t('id is not set.'));
}
try {
OC_Contacts_VCard::delete($id);
OCA\Contacts\VCard::delete($id);
} catch(Exception $e) {
bailOut($e->getMessage());
}

View File

@ -27,21 +27,40 @@ OCP\JSON::callCheck();
require_once __DIR__.'/../loghandler.php';
$id = $_POST['id'];
$checksum = $_POST['checksum'];
$l10n = OC_Contacts_App::$l10n;
$id = isset($_POST['id']) ? $_POST['id'] : null;
$name = isset($_POST['name']) ? $_POST['name'] : null;
$checksum = isset($_POST['checksum']) ? $_POST['checksum'] : null;
$l10n = OCA\Contacts\App::$l10n;
$vcard = OC_Contacts_App::getContactVCard( $id );
$line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
if(is_null($line)) {
bailOut($l10n->t('Information about vCard is incorrect. Please reload the page.'));
exit();
$multi_properties = array('EMAIL', 'TEL', 'IMPP', 'ADR', 'URL');
if(!$id) {
bailOut(OCA\Contacts\App::$l10n->t('id is not set.'));
}
unset($vcard->children[$line]);
if(!$name) {
bailOut(OCA\Contacts\App::$l10n->t('element name is not set.'));
}
if(!$checksum && in_array($name, $multi_properties)) {
bailOut(OCA\Contacts\App::$l10n->t('checksum is not set.'));
}
$vcard = OCA\Contacts\App::getContactVCard( $id );
if(!is_null($checksum)) {
$line = OCA\Contacts\App::getPropertyLineByChecksum($vcard, $checksum);
if(is_null($line)) {
bailOut($l10n->t('Information about vCard is incorrect. Please reload the page.'));
exit();
}
unset($vcard->children[$line]);
} else {
unset($vcard->{$name});
}
try {
OC_Contacts_VCard::edit($id, $vcard);
OCA\Contacts\VCard::edit($id, $vcard);
} catch(Exception $e) {
bailOut($e->getMessage());
}
@ -49,6 +68,6 @@ try {
OCP\JSON::success(array(
'data' => array(
'id' => $id,
'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U'),
'lastmodified' => OCA\Contacts\App::lastModified($vcard)->format('U'),
)
));

View File

@ -1,76 +0,0 @@
<?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/>.
*
*/
require_once __DIR__.'/../loghandler.php';
// Check if we are a user
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
$id = isset($_GET['id'])?$_GET['id']:null;
if(is_null($id)) {
bailOut(OC_Contacts_App::$l10n->t('Missing ID'));
}
$card = OC_Contacts_VCard::find($id);
$vcard = OC_VObject::parse($card['carddata']);
if(is_null($vcard)) {
bailOut(OC_Contacts_App::$l10n->t('Error parsing VCard for ID: "'.$id.'"'));
}
$details = OC_Contacts_VCard::structureContact($vcard);
// 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;
}
$lastmodified = OC_Contacts_App::lastModified($vcard);
if(!$lastmodified) {
$lastmodified = new DateTime();
}
$permissions = OCP\Share::PERMISSION_CREATE | OCP\Share::PERMISSION_READ
| OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE
| OCP\Share::PERMISSION_SHARE;
$addressbook = OC_Contacts_Addressbook::find($card['addressbookid']);
if ($addressbook['userid'] != OCP\User::getUser()) {
$sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $card['addressbookid']);
if($sharedAddressbook) {
$permissions = $sharedAddressbook['permissions'];
}
}
$details['id'] = $id;
$details['displayname'] = $card['fullname'];
$details['addressbookid'] = $card['addressbookid'];
$details['lastmodified'] = $lastmodified->format('U');
$details['permissions'] = $permissions;
OC_Contacts_App::setLastModifiedHeader($vcard);
OCP\JSON::success(array('data' => $details));

View File

@ -8,24 +8,25 @@
function cmp($a, $b)
{
if ($a['displayname'] == $b['displayname']) {
if ($a['fullname'] == $b['fullname']) {
return 0;
}
return ($a['displayname'] < $b['displayname']) ? -1 : 1;
return ($a['fullname'] < $b['fullname']) ? -1 : 1;
}
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
$start = isset($_GET['startat'])?$_GET['startat']:0;
$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;
$aid = isset($_GET['aid'])?$_GET['aid']:null;
$active_addressbooks = array();
if(is_null($aid)) {
// Called initially to get the active addressbooks.
$active_addressbooks = OC_Contacts_Addressbook::active(OCP\USER::getUser());
$active_addressbooks = OCA\Contacts\Addressbook::active(OCP\USER::getUser());
} else {
// called each time more contacts has to be shown.
$active_addressbooks = array(OC_Contacts_Addressbook::find($aid));
$active_addressbooks = array(OCA\Contacts\Addressbook::find($aid));
}
@ -36,43 +37,66 @@ $contacts_addressbook = array();
$ids = array();
foreach($active_addressbooks as $addressbook) {
$ids[] = $addressbook['id'];
if(!isset($contacts_addressbook[$addressbook['id']])) {
/*if(!isset($contacts_addressbook[$addressbook['id']])) {
$contacts_addressbook[$addressbook['id']]
= array('contacts' => array('type' => 'book',));
$contacts_addressbook[$addressbook['id']]['displayname']
= $addressbook['displayname'];
$contacts_addressbook[$addressbook['id']]['description']
= $addressbook['description'];
$contacts_addressbook[$addressbook['id']]['permissions']
= $addressbook['permissions'];
$contacts_addressbook[$addressbook['id']]['owner']
= $addressbook['userid'];
}
}*/
}
$contacts_alphabet = array();
// get next 50 for each addressbook.
foreach($ids as $id) {
$contacts_alphabet = array_merge(
$contacts_alphabet,
OCA\Contacts\VCard::all($ids)
);
/*foreach($ids as $id) {
if($id) {
$contacts_alphabet = array_merge(
$contacts_alphabet,
OC_Contacts_VCard::all($id, $start, 50)
OCA\Contacts\VCard::all($id, $offset, 50)
);
}
}
}*/
uasort($contacts_alphabet, 'cmp');
$contacts = array();
// Our new array for the contacts sorted by addressbook
if($contacts_alphabet) {
foreach($contacts_alphabet as $contact) {
try {
$vcard = Sabre\VObject\Reader::read($contact['carddata']);
$details = OCA\Contacts\VCard::structureContact($vcard);
$contacts[] = array(
'id' => $contact['id'],
'aid' => $contact['addressbookid'],
'data' => $details,
);
} catch (Exception $e) {
continue;
}
// This should never execute.
if(!isset($contacts_addressbook[$contact['addressbookid']])) {
/*if(!isset($contacts_addressbook[$contact['addressbookid']])) {
$contacts_addressbook[$contact['addressbookid']] = array(
'contacts' => array('type' => 'book',)
);
}
$display = trim($contact['fullname']);
if(!$display) {
$vcard = OC_Contacts_App::getContactVCard($contact['id']);
$vcard = OCA\Contacts\App::getContactVCard($contact['id']);
if(!is_null($vcard)) {
$struct = OC_Contacts_VCard::structureContact($vcard);
$struct = OCA\Contacts\VCard::structureContact($vcard);
$display = isset($struct['EMAIL'][0])
? $struct['EMAIL'][0]['value']
: '[UNKNOWN]';
@ -87,10 +111,15 @@ if($contacts_alphabet) {
isset($contacts_addressbook[$contact['addressbookid']]['permissions'])
? $contacts_addressbook[$contact['addressbookid']]['permissions']
: '0',
);
);*/
}
}
unset($contacts_alphabet);
uasort($contacts_addressbook, 'cmp');
//unset($contacts_alphabet);
//uasort($contacts, 'cmp');
OCP\JSON::success(array('data' => array('entries' => $contacts_addressbook)));
OCP\JSON::success(array(
'data' => array(
'contacts' => $contacts,
'addressbooks' => $active_addressbooks,
)
));

View File

@ -0,0 +1,61 @@
<?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.
*/
function cmpcategories($a, $b)
{
if (strtolower($a['name']) == strtolower($b['name'])) {
return 0;
}
return (strtolower($a['name']) < strtolower($b['name'])) ? -1 : 1;
}
function cmpcontacts($a, $b)
{
if (strtolower($a['fullname']) == strtolower($b['fullname'])) {
return 0;
}
return (strtolower($a['fullname']) < strtolower($b['fullname'])) ? -1 : 1;
}
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
$offset = isset($_GET['offset']) ? $_GET['offset'] : null;
$category = isset($_GET['category']) ? $_GET['category'] : null;
$list = array();
$catmgr = OC_Contacts_App::getVCategories();
if(is_null($category)) {
$categories = $catmgr->categories(OC_VCategories::FORMAT_MAP);
uasort($categories, 'cmpcategories');
foreach($categories as $category) {
$list[] = array(
'name' => $category['name'],
'contacts' => $catmgr->itemsForCategory(
$category['name'],
array(
'tablename' => '*PREFIX*contacts_cards',
'fields' => array('id',),
))
);
}
uasort($list['contacts'], 'cmpcontacts');
} else {
$list[$category] = $catmgr->itemsForCategory(
$category,
'*PREFIX*contacts_cards',
50,
$offset);
uasort($list[$category], 'cmpcontacts');
}
session_write_close();
OCP\JSON::success(array('data' => array('categories' => $list)));

View File

@ -17,9 +17,21 @@ $aid = intval($_POST['aid']);
$isaddressbook = isset($_POST['isaddressbook']) ? true: false;
// Ownership checking
OC_Contacts_App::getAddressbook($aid);
try {
OC_Contacts_VCard::moveToAddressBook($aid, $id, $isaddressbook);
OCA\Contacts\Addressbook::find($id); // is owner access check
} catch(Exception $e) {
OCP\JSON::error(
array(
'data' => array(
'message' => $e->getMessage(),
)
)
);
exit();
}
try {
OCA\Contacts\VCard::moveToAddressBook($aid, $id, $isaddressbook);
} catch (Exception $e) {
$msg = $e->getMessage();
OCP\Util::writeLog('contacts', 'Error moving contacts "'.implode(',', $id).'" to addressbook "'.$aid.'"'.$msg, OCP\Util::ERROR);

View File

@ -20,26 +20,64 @@
*
*/
namespace OCA\Contacts;
use Sabre\VObject as VObject;
require_once __DIR__.'/../loghandler.php';
function setParameters($property, $parameters, $reset = false) {
if(!$parameters) {
return;
}
if($reset) {
$property->parameters = array();
}
debug('Setting parameters: ' . print_r($parameters, true));
foreach($parameters as $key => $parameter) {
debug('Adding parameter: ' . $key);
if(is_array($parameter)) {
foreach($parameter as $val) {
if(is_array($val)) {
foreach($val as $val2) {
if(trim($key) && trim($val2)) {
debug('Adding parameter: '.$key.'=>'.print_r($val2, true));
$property->add($key, strip_tags($val2));
}
}
} else {
if(trim($key) && trim($val)) {
debug('Adding parameter: '.$key.'=>'.print_r($val, true));
$property->add($key, strip_tags($val));
}
}
}
}
}
}
// Check if we are a user
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
\OCP\JSON::checkLoggedIn();
\OCP\JSON::checkAppEnabled('contacts');
\OCP\JSON::callCheck();
$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;
debug('value: ' . print_r($value, 1));
$multi_properties = array('EMAIL', 'TEL', 'IMPP', 'ADR', 'URL');
if(!$name) {
bailOut(OC_Contacts_App::$l10n->t('element name is not set.'));
bailOut(App::$l10n->t('element name is not set.'));
}
if(!$id) {
bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
bailOut(App::$l10n->t('id is not set.'));
}
if(!$checksum) {
bailOut(OC_Contacts_App::$l10n->t('checksum is not set.'));
if(!$checksum && in_array($name, $multi_properties)) {
bailOut(App::$l10n->t('checksum is not set.'));
}
if(is_array($value)) {
$value = array_map('strip_tags', $value);
@ -47,33 +85,82 @@ if(is_array($value)) {
// set in the order the fields appear in the form!
ksort($value);
//if($name == 'CATEGORIES') {
// $value = OC_Contacts_VCard::escapeDelimiters($value, ',');
// $value = VCard::escapeDelimiters($value, ',');
//} else {
$value = OC_Contacts_VCard::escapeDelimiters($value, ';');
// $value = VCard::escapeDelimiters($value, ';');
//}
} else {
$value = trim(strip_tags($value));
}
$vcard = OC_Contacts_App::getContactVCard( $id );
$line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
if(is_null($line)) {
bailOut(OC_Contacts_App::$l10n->t(
'Information about vCard is incorrect. Please reload the page: ').$checksum
);
$vcard = App::getContactVCard($id);
if(!$vcard) {
bailOut(App::$l10n->t('Couldn\'t find vCard for %d.', array($id)));
}
$element = $vcard->children[$line]->name;
if($element != $name) {
bailOut(OC_Contacts_App::$l10n->t(
'Something went FUBAR. ').$name.' != '.$element
);
$property = null;
if(in_array($name, $multi_properties)) {
if($checksum !== 'new') {
$line = App::getPropertyLineByChecksum($vcard, $checksum);
if(is_null($line)) {
bailOut(App::$l10n->t(
'Information about vCard is incorrect. Please reload the page: ').$checksum
);
}
$property = $vcard->children[$line];
$element = $property->name;
if($element != $name) {
bailOut(App::$l10n->t(
'Something went FUBAR. ').$name.' != '.$element
);
}
} else {
// Add new property
$element = $name;
if (!is_scalar($value)) {
$property = VObject\Property::create($name);
if(in_array($name, array('ADR',))) {
$property->setParts($value);
} else {
bailOut(App::$l10n->t(
'Cannot save property of type "%s" as array', array($name,)
));
}
} else {
$property = VObject\Property::create($name, $value, $parameters);
}
setParameters($property, $parameters);
$vcard->add($property);
$checksum = substr(md5($property->serialize()), 0, 8);
try {
VCard::edit($id, $vcard);
} catch(Exception $e) {
bailOut($e->getMessage());
}
\OCP\JSON::success(array('data' => array(
'checksum' => $checksum,
'oldchecksum' => $_POST['checksum'],
)));
exit();
}
} else {
$element = $name;
$property = $vcard->select($name);
debug('propertylist: ' . get_class($property));
if(count($property) === 0) {
$property = VObject\Property::create($name);
$vcard->add($property);
} else {
$property = array_shift($property);
}
}
/* preprocessing value */
switch($element) {
case 'BDAY':
$date = New DateTime($value);
$date = New \DateTime($value);
$value = $date->format('Y-m-d');
break;
case 'FN':
@ -90,86 +177,80 @@ switch($element) {
break;
case 'IMPP':
if(is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) {
bailOut(OC_Contacts_App::$l10n->t('Missing IM parameter.'));
bailOut(App::$l10n->t('Missing IM parameter.'));
}
$impp = OC_Contacts_App::getIMOptions($parameters['X-SERVICE-TYPE']);
$impp = App::getIMOptions($parameters['X-SERVICE-TYPE']);
if(is_null($impp)) {
bailOut(OC_Contacts_App::$l10n->t('Unknown IM: '.$parameters['X-SERVICE-TYPE']));
bailOut(App::$l10n->t('Unknown IM: '.$parameters['X-SERVICE-TYPE']));
}
$value = $impp['protocol'] . ':' . $value;
break;
}
// If empty remove the property
if(!$value) {
unset($vcard->children[$line]);
$checksum = '';
if(in_array($name, $multi_properties)) {
unset($vcard->children[$line]);
$checksum = '';
} else {
unset($vcard->{$name});
}
} else {
/* setting value */
switch($element) {
case 'BDAY':
// I don't use setDateTime() because that formats it as YYYYMMDD instead
// of YYYY-MM-DD which is what the RFC recommends.
$vcard->children[$line]->setValue($value);
$vcard->children[$line]->parameters = array();
$vcard->children[$line]->add(
new Sabre\VObject\Parameter('VALUE', 'DATE')
);
debug('Setting value:'.$name.' '.$vcard->children[$line]);
$vcard->BDAY = $value;
if(!isset($vcard->BDAY['VALUE'])) {
$vcard->BDAY->add('VALUE', 'DATE');
} else {
$vcard->BDAY->VALUE = 'DATE';
}
break;
case 'CATEGORIES':
debug('Setting string:'.$name.' '.$value);
$vcard->children[$line]->setValue($value);
case 'ADR':
case 'N':
if(is_array($value)) {
$property->setParts($value);
} else {
debug('Saving N ' . $value);
$vcard->N = $value;
}
break;
case 'EMAIL':
case 'TEL':
case 'ADR':
case 'IMPP':
case 'URL':
debug('Setting element: (EMAIL/TEL/ADR)'.$element);
$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);
if(is_array($parameter)) {
foreach($parameter as $val) {
if(trim($val)) {
debug('Adding parameter: '.$key.'=>'.$val);
$vcard->children[$line]->add(new Sabre\VObject\Parameter(
$key,
strtoupper(strip_tags($val)))
);
}
}
} else {
if(trim($parameter)) {
$vcard->children[$line]->add(new Sabre\VObject\Parameter(
$key,
strtoupper(strip_tags($parameter)))
);
}
}
}
}
$property->setValue($value);
break;
default:
$vcard->setString($name, $value);
$vcard->{$name} = $value;
break;
}
setParameters($property, $parameters, true);
// Do checksum and be happy
$checksum = md5($vcard->children[$line]->serialize());
if(in_array($name, $multi_properties)) {
$checksum = substr(md5($property->serialize()), 0, 8);
}
}
//debug('New checksum: '.$checksum);
//$vcard->children[$line] = $property; ???
try {
OC_Contacts_VCard::edit($id, $vcard);
VCard::edit($id, $vcard);
} catch(Exception $e) {
bailOut($e->getMessage());
}
OCP\JSON::success(array('data' => array(
'line' => $line,
'checksum' => $checksum,
'oldchecksum' => $_POST['checksum'],
'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U'),
)));
if(in_array($name, $multi_properties)) {
\OCP\JSON::success(array('data' => array(
'line' => $line,
'checksum' => $checksum,
'oldchecksum' => $_POST['checksum'],
'lastmodified' => App::lastModified($vcard)->format('U'),
)));
} else {
\OCP\JSON::success(array('data' => array(
'lastmodified' => App::lastModified($vcard)->format('U'),
)));
}

View File

@ -27,27 +27,29 @@ OCP\JSON::checkAppEnabled('contacts');
require_once 'loghandler.php';
if (!isset($_GET['id'])) {
bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.'));
bailOut(OCA\Contacts\App::$l10n->t('No contact ID was submitted.'));
}
$contact = OC_Contacts_App::getContactVCard($_GET['id']);
$contact = OCA\Contacts\App::getContactVCard($_GET['id']);
// invalid vcard
if( is_null($contact)) {
bailOut(OC_Contacts_App::$l10n->t('Error reading contact photo.'));
bailOut(OCA\Contacts\App::$l10n->t('Error reading contact photo.'));
} else {
$image = new OC_Image();
if(!$image->loadFromBase64($contact->getAsString('PHOTO'))) {
$image->loadFromBase64($contact->getAsString('LOGO'));
if(!isset($contact->PHOTO) || !$image->loadFromBase64((string)$contact->PHOTO)) {
if(isset($contact->LOGO)) {
$image->loadFromBase64((string)$contact->LOGO);
}
}
if($image->valid()) {
$tmpkey = 'contact-photo-'.$contact->getAsString('UID');
$tmpkey = 'contact-photo-'.$contact->UID;
if(OC_Cache::set($tmpkey, $image->data(), 600)) {
OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey)));
exit();
} else {
bailOut(OC_Contacts_App::$l10n->t('Error saving temporary file.'));
bailOut(OCA\Contacts\App::$l10n->t('Error saving temporary file.'));
}
} else {
bailOut(OC_Contacts_App::$l10n->t('The loading photo is not valid.'));
bailOut(OCA\Contacts\App::$l10n->t('The loading photo is not valid.'));
}
}

View File

@ -12,14 +12,14 @@ OCP\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');
$vcard = OCA\Contacts\App::getContactVCard($id);
$adr_types = OCA\Contacts\App::getTypesOfProperty('ADR');
$tmpl = new OCP\Template("contacts", "part.edit_address_dialog");
if($checksum) {
$line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
$line = OCA\Contacts\App::getPropertyLineByChecksum($vcard, $checksum);
$element = $vcard->children[$line];
$adr = OC_Contacts_VCard::structureProperty($element);
$adr = OCA\Contacts\VCard::structureProperty($element);
$types = array();
if(isset($adr['parameters']['TYPE'])) {
if(is_array($adr['parameters']['TYPE'])) {

View File

@ -16,19 +16,19 @@ $tmpl = new OCP\Template("contacts", "part.edit_name_dialog");
$id = isset($_GET['id'])?$_GET['id']:'';
if($id) {
$vcard = OC_Contacts_App::getContactVCard($id);
$vcard = OCA\Contacts\App::getContactVCard($id);
$name = array('', '', '', '', '');
if($vcard->__isset('N')) {
$property = $vcard->__get('N');
if($property) {
$name = OC_Contacts_VCard::structureProperty($property);
$name = OCA\Contacts\VCard::structureProperty($property);
}
}
$name = array_map('htmlspecialchars', $name['value']);
$tmpl->assign('name', $name, false);
$tmpl->assign('id', $id, false);
} else {
bailOut(OC_Contacts_App::$l10n->t('Contact ID is missing.'));
bailOut(OCA\Contacts\App::$l10n->t('Contact ID is missing.'));
}
$page = $tmpl->fetchPage();
OCP\JSON::success(array('data' => array('page'=>$page)));

View File

@ -25,26 +25,26 @@ OCP\JSON::checkAppEnabled('contacts');
require_once 'loghandler.php';
if(!isset($_GET['id'])) {
bailOut(OC_Contacts_App::$l10n->t('No contact ID was submitted.'));
bailOut(OCA\Contacts\App::$l10n->t('No contact ID was submitted.'));
}
if(!isset($_GET['path'])) {
bailOut(OC_Contacts_App::$l10n->t('No photo path was submitted.'));
bailOut(OCA\Contacts\App::$l10n->t('No photo path was submitted.'));
}
$localpath = OC_Filesystem::getLocalFile($_GET['path']);
$tmpkey = 'contact-photo-'.$_GET['id'];
if(!file_exists($localpath)) {
bailOut(OC_Contacts_App::$l10n->t('File doesn\'t exist:').$localpath);
bailOut(OCA\Contacts\App::$l10n->t('File doesn\'t exist:').$localpath);
}
$image = new OC_Image();
if(!$image) {
bailOut(OC_Contacts_App::$l10n->t('Error loading image.'));
bailOut(OCA\Contacts\App::$l10n->t('Error loading image.'));
}
if(!$image->loadFromFile($localpath)) {
bailOut(OC_Contacts_App::$l10n->t('Error loading image.'));
bailOut(OCA\Contacts\App::$l10n->t('Error loading image.'));
}
if($image->width() > 400 || $image->height() > 400) {
$image->resize(400); // Prettier resizing than with browser and saves bandwidth.

View File

@ -63,10 +63,10 @@ if($data) {
if(($image->width() <= 200 && $image->height() <= 200)
|| $image->resize(200)) {
$vcard = OC_Contacts_App::getContactVCard($id);
$vcard = OCA\Contacts\App::getContactVCard($id);
if(!$vcard) {
OC_Cache::remove($tmpkey);
bailOut(OC_Contacts_App::$l10n
bailOut(OCA\Contacts\App::$l10n
->t('Error getting contact object.'));
}
if($vcard->__isset('PHOTO')) {
@ -76,7 +76,7 @@ if($data) {
$property = $vcard->__get('PHOTO');
if(!$property) {
OC_Cache::remove($tmpkey);
bailOut(OC_Contacts_App::$l10n
bailOut(OCA\Contacts\App::$l10n
->t('Error getting PHOTO property.'));
}
$property->setValue($image->__toString());
@ -89,34 +89,41 @@ if($data) {
OCP\Util::writeLog('contacts',
'savecrop.php: files: Adding PHOTO property.',
OCP\Util::DEBUG);
$vcard->addProperty('PHOTO',
// For vCard 3.0 the type must be e.g. JPEG or PNG
// For version 4.0 the full mimetype should be used.
// https://tools.ietf.org/html/rfc2426#section-3.1.4
$type = $vcard->VERSION == '4.0'
? $image->mimeType()
: strtoupper(array_pop(explode('/', $image->mimeType())));
$vcard->add('PHOTO',
$image->__toString(), array('ENCODING' => 'b',
'TYPE' => $image->mimeType()));
'TYPE' => $type));
}
$now = new DateTime;
$vcard->setString('REV', $now->format(DateTime::W3C));
if(!OC_Contacts_VCard::edit($id, $vcard)) {
bailOut(OC_Contacts_App::$l10n->t('Error saving contact.'));
$vcard->{'REV'} = $now->format(DateTime::W3C);
if(!OCA\Contacts\VCard::edit($id, $vcard)) {
bailOut(OCA\Contacts\App::$l10n->t('Error saving contact.'));
}
OCA\Contacts\App::cacheThumbnail($id, $image);
OCP\JSON::success(array(
'data' => array(
'id' => $id,
'width' => $image->width(),
'height' => $image->height(),
'lastmodified' => OC_Contacts_App::lastModified($vcard)->format('U')
'lastmodified' => OCA\Contacts\App::lastModified($vcard)->format('U')
)
));
} else {
bailOut(OC_Contacts_App::$l10n->t('Error resizing image'));
bailOut(OCA\Contacts\App::$l10n->t('Error resizing image'));
}
} else {
bailOut(OC_Contacts_App::$l10n->t('Error cropping image'));
bailOut(OCA\Contacts\App::$l10n->t('Error cropping image'));
}
} else {
bailOut(OC_Contacts_App::$l10n->t('Error creating temporary image'));
bailOut(OCA\Contacts\App::$l10n->t('Error creating temporary image'));
}
} else {
bailOut(OC_Contacts_App::$l10n->t('Error finding image: ').$tmpkey);
bailOut(OCA\Contacts\App::$l10n->t('Error finding image: ').$tmpkey);
}
OC_Cache::remove($tmpkey);

View File

@ -9,7 +9,7 @@
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
$books = OC_Contacts_Addressbook::all(OCP\USER::getUser());
$books = OCA\Contacts\Addressbook::all(OCP\USER::getUser());
$tmpl = new OCP\Template("contacts", "part.selectaddressbook");
$tmpl->assign('addressbooks', $books);
$page = $tmpl->fetchPage();

55
ajax/setpreference.php Normal file
View File

@ -0,0 +1,55 @@
<?php
/**
* ownCloud - Contacts
*
* @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/>.
*
*/
/**
* @brief Set user preference.
* @param $key
* @param $value
*/
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
require_once 'loghandler.php';
$key = isset($_POST['key'])?$_POST['key']:null;
$value = isset($_POST['value'])?$_POST['value']:null;
if(is_null($key)) {
bailOut(OCA\Contacts\App::$l10n->t('Key is not set for: '.$value));
}
if(is_null($value)) {
bailOut(OCA\Contacts\App::$l10n->t('Value is not set for: '.$key));
}
if(OCP\Config::setUserValue(OCP\USER::getUser(), 'contacts', $key, $value)) {
OCP\JSON::success(array(
'data' => array(
'key' => $key,
'value' => $value)
)
);
} else {
bailOut(OCA\Contacts\App::$l10n->t(
'Could not set preference: ' . $key . ':' . $value)
);
}

View File

@ -26,7 +26,7 @@ OCP\JSON::checkAppEnabled('contacts');
OCP\JSON::callCheck();
require_once 'loghandler.php';
$l10n = OC_Contacts_App::$l10n;
$l10n = OCA\Contacts\App::$l10n;
$view = OCP\Files::getStorage('contacts');
if(!$view->file_exists('imports')) {

View File

@ -28,7 +28,7 @@ OCP\JSON::callCheck();
// Firefox and Konqueror tries to download application/json for me. --Arthur
OCP\JSON::setContentTypeHeader('text/plain; charset=utf-8');
require_once 'loghandler.php';
$l10n = OC_Contacts_App::$l10n;
$l10n = OCA\Contacts\App::$l10n;
// If it is a Drag'n'Drop transfer it's handled here.
$fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
if ($fn) {

View File

@ -1,20 +1,20 @@
<?php
OC::$CLASSPATH['OC_Contacts_App'] = 'contacts/lib/app.php';
OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'contacts/lib/addressbook.php';
OC::$CLASSPATH['OC_Contacts_VCard'] = 'contacts/lib/vcard.php';
OC::$CLASSPATH['OC_Contacts_Hooks'] = 'contacts/lib/hooks.php';
OC::$CLASSPATH['OC_Share_Backend_Contact'] = 'contacts/lib/share/contact.php';
OC::$CLASSPATH['OC_Share_Backend_Addressbook'] = 'contacts/lib/share/addressbook.php';
OC::$CLASSPATH['OCA\Contacts\App'] = 'contacts/lib/app.php';
OC::$CLASSPATH['OCA\Contacts\Addressbook'] = 'contacts/lib/addressbook.php';
OC::$CLASSPATH['OCA\Contacts\VCard'] = 'contacts/lib/vcard.php';
OC::$CLASSPATH['OCA\Contacts\Hooks'] = 'contacts/lib/hooks.php';
OC::$CLASSPATH['OCA\Contacts\Share_Backend_Contact'] = 'contacts/lib/share/contact.php';
OC::$CLASSPATH['OCA\Contacts\Share_Backend_Addressbook'] = 'contacts/lib/share/addressbook.php';
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'contacts/lib/sabre/backend.php';
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBookRoot'] = 'contacts/lib/sabre/addressbookroot.php';
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_UserAddressBooks'] = 'contacts/lib/sabre/useraddressbooks.php';
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBook'] = 'contacts/lib/sabre/addressbook.php';
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_Card'] = 'contacts/lib/sabre/card.php';
OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'contacts/lib/search.php';
OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Contacts_Hooks', 'createUser');
OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser');
OCP\Util::connectHook('OC_Calendar', 'getEvents', 'OC_Contacts_Hooks', 'getBirthdayEvents');
OCP\Util::connectHook('OC_Calendar', 'getSources', 'OC_Contacts_Hooks', 'getCalenderSources');
OC::$CLASSPATH['OCA\\Contacts\\SearchProvider'] = 'contacts/lib/search.php';
OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Contacts\Hooks', 'createUser');
OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OCA\Contacts\Hooks', 'deleteUser');
OCP\Util::connectHook('OC_Calendar', 'getEvents', 'OCA\Contacts\Hooks', 'getBirthdayEvents');
OCP\Util::connectHook('OC_Calendar', 'getSources', 'OCA\Contacts\Hooks', 'getCalenderSources');
OCP\App::addNavigationEntry( array(
'id' => 'contacts_index',
@ -24,6 +24,6 @@ OCP\App::addNavigationEntry( array(
'name' => OC_L10N::get('contacts')->t('Contacts') ));
OCP\Util::addscript('contacts', 'loader');
OC_Search::registerProvider('OC_Search_Provider_Contacts');
OCP\Share::registerBackend('contact', 'OC_Share_Backend_Contact');
OCP\Share::registerBackend('addressbook', 'OC_Share_Backend_Addressbook', 'contact');
OC_Search::registerProvider('OCA\Contacts\SearchProvider');
OCP\Share::registerBackend('contact', 'OCA\Contacts\Share_Backend_Contact');
OCP\Share::registerBackend('addressbook', 'OCA\Contacts\Share_Backend_Addressbook', 'contact');

View File

@ -1,138 +1,213 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<database>
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
<charset>utf8</charset>
<charset>utf8</charset>
<table>
<table>
<name>*dbprefix*contacts_addressbooks</name>
<name>*dbprefix*contacts_addressbooks</name>
<declaration>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>userid</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>255</length>
</field>
<field>
<name>userid</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>255</length>
</field>
<field>
<name>displayname</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>displayname</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>uri</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>200</length>
</field>
<field>
<name>uri</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>200</length>
</field>
<field>
<name>description</name>
<type>text</type>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>description</name>
<type>text</type>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>ctag</name>
<type>integer</type>
<default>1</default>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>ctag</name>
<type>integer</type>
<default>1</default>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>active</name>
<type>integer</type>
<default>1</default>
<notnull>true</notnull>
<length>4</length>
</field>
<field>
<name>active</name>
<type>integer</type>
<default>1</default>
<notnull>true</notnull>
<length>4</length>
</field>
</declaration>
</declaration>
</table>
</table>
<table>
<table>
<name>*dbprefix*contacts_cards</name>
<name>*dbprefix*contacts_cards</name>
<declaration>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>addressbookid</name>
<type>integer</type>
<default></default>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>addressbookid</name>
<type>integer</type>
<default></default>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>fullname</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>fullname</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>carddata</name>
<type>clob</type>
<notnull>false</notnull>
</field>
<field>
<name>carddata</name>
<type>clob</type>
<notnull>false</notnull>
</field>
<field>
<name>uri</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>200</length>
</field>
<field>
<name>uri</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>200</length>
</field>
<field>
<name>lastmodified</name>
<type>integer</type>
<default></default>
<notnull>false</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>lastmodified</name>
<type>integer</type>
<default></default>
<notnull>false</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>
</declaration>
</declaration>
</table>
</table>
<table>
<name>*dbprefix*contacts_cards_properties</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>userid</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>255</length>
</field>
<field>
<name>contactid</name>
<type>integer</type>
<default></default>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>4</length>
</field>
<field>
<name>name</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>64</length>
</field>
<field>
<name>value</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>preferred</name>
<type>integer</type>
<default>1</default>
<notnull>true</notnull>
<length>4</length>
</field>
<index>
<name>name_index</name>
<field>
<name>name</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>value_index</name>
<field>
<name>value</name>
<sorting>ascending</sorting>
</field>
</index>
</declaration>
</table>
</database>

View File

@ -3,7 +3,7 @@
<id>contacts</id>
<name>Contacts</name>
<licence>AGPL</licence>
<author>Jakob Sack</author>
<author>Jakob Sack,Thomas Tanghus</author>
<require>4.9</require>
<shipped>true</shipped>
<description>Address book with CardDAV support.</description>
@ -12,5 +12,6 @@
<remote>
<contacts>appinfo/remote.php</contacts>
<carddav>appinfo/remote.php</carddav>
<contactthumbnail>thumbnail.php</contactthumbnail>
</remote>
</info>

View File

@ -44,7 +44,7 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
// Map the id
$idmap[$row['id']] = OCP\DB::insertid('*PREFIX*contacts_addressbooks');
// Make the addressbook active
OC_Contacts_Addressbook::setActive($idmap[$row['id']], true);
OCA\Contacts\Addressbook::setActive($idmap[$row['id']], true);
}
// Now tags
foreach($idmap as $oldid => $newid) {

View File

@ -1 +1 @@
0.2.4
0.2.5

View File

@ -1,145 +1,167 @@
/*dl > dt {
font-weight: bold;
}*/
/* General element settings */
#content li { cursor: default; }
#content input[type=checkbox] {
height: 14px; width: 14px;
border: 1px solid #fff;
-moz-appearance:none; -webkit-appearance: none;
-moz-box-sizing:none; box-sizing:none;
-moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none;
-moz-border-radius: 2px; -webkit-border-radius: 2px; border-radius: 2px;
}
#content input[type=radio] { width: 12px; height: 12px; }
#content input[type=checkbox]:hover { border: 1px solid #D4D4D4 !important; }
#content input[type=checkbox]:checked::after {
content: url('%appswebroot%/contacts/img/checkmark.png');
display: block;
position: relative;
top: -8px;
left: -6px;
}
#content input[type=radio]:checked::after {
content: url('%appswebroot%/contacts/img/checkmark.png');
margin: 0; padding: 0;
display: inline-block;
position: relative;
top: -8px;
left: -6px;
}
#content textarea { font-family: inherit; }
#content input:-moz-placeholder { color: #aaa; }
#content input::-webkit-input-placeholder { color: #aaa; }
#content input:-ms-input-placeholder { color: #aaa; }
#content input:placeholder { color: #aaa; }
/* Left content */
#leftcontent { top: 3.5em !important; padding: 0; margin: 0; }
#leftcontent a { padding: 0 0 0 25px; }
#rightcontent { top: 3.5em !important; padding-top: 5px; }
#leftcontent a { display: inline-block; }
#leftcontent h3 { cursor: pointer; -moz-transition: background 300ms ease 0s; background: none no-repeat scroll 1em center #eee; border-bottom: 1px solid #ddd; border-top: 1px solid #fff; display: block; max-width: 100%; padding: 0.5em 0.8em; color: #666; text-shadow: 0 1px 0 #f8f8f8; font-size: 1.2em; }
#leftcontent h3:hover,#leftcontent h3:active,#leftcontent h3.active { background-color: #DBDBDB; border-bottom: 1px solid #CCCCCC; border-top: 1px solid #D4D4D4; color: #333333; font-weight: bold; }
#leftcontent h3 img.shared { float: right; opacity: 0.4; }
#leftcontent h3 img.shared { float: right; opacity: 0.4; margin: 0 .5em; }
#leftcontent h3 img.shared:hover { opacity: 1; }
#contacts { position: fixed; background: #fff; max-width: 100%; width: 20em; left: 12.5em; top: 3.7em; bottom: 3em; overflow: auto; padding: 0; margin: 0; }
.contacts a { height: 23px; display: block; left: 12.5em; margin: 0 0 0 0; padding: 0 0 0 25px; }
.contacts li.ui-draggable { height: 23px; }
.ui-draggable-dragging { width: 17em; cursor: move; }
.ui-state-hover { border: 1px solid dashed; }
#bottomcontrols { padding: 0; bottom:0px; height:2.8em; width: 20em; margin:0; background:#eee; border-top:1px solid #ccc; position:fixed; -moz-box-shadow: 0 -3px 3px -3px #000; -webkit-box-shadow: 0 -3px 3px -3px #000; box-shadow: 0 -3px 3px -3px #000;}
#bottomcontrols img { margin-top: 0.35em; }
#uploadprogressbar { display: none; padding: 0; bottom: 3em; height:2em; width: 20em; margin:0; background:#eee; border:1px solid #ccc; position:fixed; }
button.control { float: left; margin: 0.2em 0 0 1em; height: 2.4em; width: 2.4em; /* border: 0 none; border-radius: 0; -moz-box-shadow: none; box-shadow: none; outline: 0 none;*/ }
.settings { background:url('%webroot%/core/img/actions/settings.svg') no-repeat center; float: right !important; margin: 0.2em 1em 0 0 !important; }
.import { background:url('%webroot%/core/img/actions/upload.svg') no-repeat center; }
.newcontact { background:url('%appswebroot%/contacts/img/contact-new.svg') no-repeat center; }
#actionbar { clear: both; height: 30px;}
#contacts_deletecard {position:relative; float:left; background:url('%webroot%/core/img/actions/delete.svg') no-repeat center; }
#contacts_downloadcard {position:relative; float:left; background:url('%webroot%/core/img/actions/download.svg') no-repeat center; }
#contacts_propertymenu { clear: left; float:left; max-width: 15em; margin: 2em; }
#contacts_propertymenu_button { position:relative;top:0;left:0; margin: 0; }
#contacts_propertymenu_dropdown { background-color: #fff; position:relative; right:0; overflow:hidden; text-overflow:ellipsis; border: thin solid #1d2d44; box-shadow: 0 3px 5px #bbb; /* -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: 3px; }
#contacts_propertymenu li { display: block; font-weight: bold; height: 20px; }
#contacts_propertymenu li a { padding: 3px; display: block }
#contacts_propertymenu li:hover { background-color: #1d2d44; }
#contacts_propertymenu li a:hover { color: #fff }
#card { width: auto; font-size: 10px; /*max-width: 70em; border: thin solid lightgray; display: block;*/ }
#groupactions { box-sizing: border-box; height: 4em; border-bottom: 1px solid #DDDDDD; }
#groupactions > button, .addcontact {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
border-color: #51A351 #419341 #387038;
border-image: none;
border-style: solid;
border-width: 1px;
box-shadow: 0 1px 1px #F8F8F8, 1px 1px 1px #AADDAA inset;
background-color: #5BB75B;
color: #fff;
border-right: medium none;
margin: .7em 2em;
}
#grouplist { z-index: 100; }
#grouplist h3 .action { float: right; display: none; padding: 0; margin: auto; }
#grouplist h3:not([data-type="shared"]):hover .action.numcontacts, #grouplist h3:not([data-type="shared"]) .active.action.numcontacts { display: inline-block; }
#grouplist h3[data-type="category"]:hover .action.delete { display: inline-block; }
#grouplist h3 .action.delete { width: 20px; height: 20px; }
/* First run */
#firstrun { position: relative; top: 25%; left: 20%; right: 20%; width: 50%; font-weight:bold; text-align: center; color: #777; }
#firstrun h3 { font-size:1.5em; text-align: center; margin-bottom: 1em; }
#firstrun p { font-size:1.2em; text-align:center; }
#firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; }
#card input[type="text"].contacts_property,input[type="email"].contacts_property,input[type="url"].contacts_property { width: 14em; float: left; font-weight: bold; }
.categories { float: left; width: 16em; }
#card input[type="checkbox"].contacts_property, #card input[type="text"], #card input[type="email"], #card input[type="url"], #card input[type="tel"], #card input[type="date"], #card select, #card textarea { background-color: #fefefe; border: 0 !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, #card input[type="text"]:focus, #card input[type="text"]:active, input[type="email"]:hover, #card input[type="url"]:hover, #card input[type="tel"]:hover, #card input[type="date"]:hover, #card input[type="date"], #card input[type="date"]:hover, #card input[type="date"]:active, #card input[type="date"]:active, #card input[type="date"]:active, #card input[type="email"]:active, #card input[type="url"]:active, #card input[type="tel"]:active, #card textarea:focus, #card textarea:hover { 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 #ddd, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #ddd, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #ddd, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; }
#card textarea { width: 80%; min-height: 5em; min-width: 30em; margin: 0 !important; padding: 0 !important; outline: 0 !important;}
dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; cursor: normal; }
#content input:not([type="checkbox"]), #content select, #content textarea {
background-color: #fefefe; border: 1px solid #fff !important;
-moz-appearance:none !important; -webkit-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;
}
#content input:invalid, #content input:hover:not([type="checkbox"]), #content input:active:not([type="checkbox"]), #content input:focus:not([type="checkbox"]), #content textarea:focus, #content textarea:hover {
border: 1px solid silver !important;
-moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em;
outline:none; float: left;
}
#contact { margin: 1em; }
#contact textarea { min-height: 5em; min-width: 30em; margin: 0 !important; padding: 0 !important; outline: 0 !important;}
#contact input[type="checkbox"] { margin-top: 10px; vertical-align: bottom; float: left; }
dl.form { display: inline-block; width: auto; margin: 0; padding: 0; cursor: normal; }
.form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom; color: #bbb;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ }
.form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; }
label:hover, dt:hover { color: #333; }
/*::-webkit-input-placeholder { color: #bbb; }
:-moz-placeholder { color: #bbb; }
:-ms-input-placeholder { color: #bbb; }*/
.droptarget { margin: 0.5em; padding: 0.5em; border: thin solid #ccc; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; }
.droppable { margin: 0.5em; padding: 0.5em; border: thin dashed #333; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; }
.loading { background: url('%webroot%/core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; }
.ui-autocomplete-loading { background: url('%webroot%/core/img/loading.gif') right center no-repeat; }
.ui-autocomplete-input { margin-top: 0.5em; } /* Ugly hack */
.float { float: left; }
.svg { border: inherit; background: inherit; }
.listactions { height: 1em; width:60px; float: left; clear: right; }
.add,.edit,.delete,.mail, .globe, .upload, .download, .cloud, .share { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; opacity: 0.1; }
.add:hover,.edit:hover,.delete:hover,.mail:hover, .globe:hover, .upload:hover, .download:hover .cloud:hover { opacity: 1.0 }
.form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0; white-space: nowrap; vertical-align: text-bottom; }
.action { display: inline-block; width: 20px; height: 20px; }
.add { background:url('%webroot%/core/img/actions/add.svg') no-repeat center; clear: both; }
.delete { background:url('%webroot%/core/img/actions/delete.svg') no-repeat center; }
.delete { background:url('%webroot%/core/img/actions/delete.png') no-repeat center; }
.edit { background:url('%webroot%/core/img/actions/rename.svg') no-repeat center; }
.share { background:url('%webroot%/core/img/actions/share.svg') no-repeat center; }
.mail { background:url('%webroot%/core/img/actions/mail.svg') no-repeat center; }
.upload { background:url('%webroot%/core/img/actions/upload.svg') no-repeat center; }
.download { background:url('%webroot%/core/img/actions/download.svg') no-repeat center; }
.cloud { background:url('%webroot%/core/img/places/picture.svg') no-repeat center; }
/*.globe { background:url('../img/globe.svg') no-repeat center; }*/
.globe { background:url('%webroot%/core/img/actions/public.svg') no-repeat center; }
.starred { display: inline-block; height: 22px; width: 22px; padding: 0; margin: 0; background:url('%appswebroot%/contacts/img/starred.png') no-repeat center; }
.transparent{ opacity: 0.6; }
#edit_name_dialog { padding:0; }
#edit_name_dialog > input { width: 15em; }
#edit_address_dialog { /*width: 30em;*/ }
#edit_address_dialog > input { width: 15em; }
#edit_photo_dialog_img { display: block; min-width: 150; min-height: 200; }
#fn { float: left !important; width: 18em !important; }
#name { /*position: absolute; top: 0px; left: 0px;*/ min-width: 25em; height: 2em; clear: right; display: block; }
#identityprops { /*position: absolute; top: 2.5em; left: 0px;*/ }
#contact_photo { float: left; margin: 1em; }
#contact_identity { min-width: 30em; padding: 0.5em;}
.contactsection { position: relative; float: left; width: 35em; padding: 0.5em; height: auto; }
.float { float: left; display: inline-block; width: auto; }
.break { clear: both; }
.loading { background: url('%webroot%/core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; }
.wait { opacity: cursor: wait; }
.control {
border: 1px solid #DDDDDD;
border-radius: 0.3em;
color: #555;
cursor: pointer;
font-weight: bold;
font-size: 1em;
width: auto;
}
.control > * { background: none repeat scroll 0 0 #F8F8F8; color: #555 !important; font-size: 100%; margin: 0px; }
#cropbox { margin: auto; }
#contacts_details_photo_wrapper { width: 150px; }
#contacts_details_photo_wrapper.wait { opacity: 0.6; filter:alpha(opacity=0.6); z-index:1000; background: url('%webroot%/core/img/loading.gif') no-repeat center center; cursor: wait; }
.contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; margin: 0.3em; background: url('%webroot%/core/img/loading.gif') no-repeat center center; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; opacity: 1; }
.contacts_details_photo:hover { background: #fff; cursor: default; }
#phototools { position:absolute; margin: 5px 0 0 10px; width:auto; height:22px; padding:0px; background-color:#fff; list-style-type:none; border-radius: 0.5em; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; }
#phototools li { display: inline; }
#phototools li a { float:left; cursor:pointer; width:22px; height:22px; opacity: 0.6; }
#phototools li a:hover { opacity: 0.8; }
.ui-draggable { height: 3em; z-index: 1000; }
.ui-draggable-dragging { width: 70%; cursor: move; }
.ui-state-hover { border: 1px solid dashed; z-index: 1; }
/* Address editor */
#addressdisplay { padding: 0.5em; }
dl.addresscard { background-color: #fff; float: left; width: auto; margin: 0 0.3em 0.3em 0.3em; padding: 0; border: 0; }
dl.addresscard dd {}
dl.addresscard dt { padding: 0.3em; font-weight: bold; clear: both; color: #aaa; }
dl.addresscard dt:hover { color:#777; }
dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; }
dl.addresscard .action { float: right; }
#address dt { width: 30%; white-space:nowrap; }
#address dd { width: 66%; }
#address input { width: 12em; padding: 0.6em 0.5em 0.4em; }
#address input:-moz-placeholder { color: #aaa; }
#address input::-webkit-input-placeholder { color: #aaa; }
#address input:-ms-input-placeholder { color: #aaa; }
#address input:placeholder { color: #aaa; }
#adr_type {} /* Select */
#adr_pobox {}
#adr_extended {}
#adr_street {}
#adr_city {}
#adr_region {}
#adr_zipcode {}
#adr_country {}
/* Properties */
#file_upload_form { width: 0; height: 0; }
#file_upload_target, #import_upload_target, #crop_target { display:none; }
#file_upload_start, #import_upload_start { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index:1001; width:0; height:0;}
input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
.big { font-weight:bold; font-size:1.2em; }
.huge { font-weight:bold; font-size:1.5em; }
.propertycontainer dd { float: left; width: 25em; }
/*.propertylist { clear: none; max-width: 33em; }*/
.propertylist li.propertycontainer { white-space: nowrap; min-width: 35em; display: block; clear: both; }
.propertycontainer[data-element="EMAIL"] > input[type="email"],.propertycontainer[data-element="TEL"] > input[type="text"] { min-width: 12em !important; float: left; }
.propertylist li > input[type="checkbox"],input[type="radio"] { float: left; clear: left; width: 16px; height: 16px; vertical-align: middle; padding: 0; }
.fullname { font-weight:bold; font-size:1.5em; width: 17em; }
.singleproperties { display: inline-block; float: left; width: 30em;}
.singleproperties input.value { font-weight: bold; }
.singleproperties .action { float: left; width: 20px; height: 20px; }
.singleproperties .n input { width: 17em}
.singleproperties dl { min-width: 24em; }
.propertylist li.propertycontainer { white-space: nowrap; min-width: 38em; display: block; clear: both; }
.propertylist li.propertycontainer > .listactions { display: inline-block; position: absolute; clear: none; opacity: 0; float: right; }
.propertylist li.propertycontainer .listactions a { display: inline-block; float: left; clear: none; width: 20px; height: 20px; }
.propertylist { float: left; }
/*.propertylist li > a { display: block; }}*/
.propertylist li > input[type="checkbox"],input[type="radio"] { display: inline-block; }
.propertylist input.value:not([type="checkbox"]) { width: 16em; display: inline-block; font-weight: bold; }
.propertylist li > select { float: left; max-width: 8em; }
.propertylist li > .select_wrapper { float: left; overflow: hidden; color: #bbb; font-size: 0.8em; }
.propertylist li > .select_wrapper select { float: left; overflow: hidden; color: #bbb; }
.propertylist li > .select_wrapper select option { color: #777; }
.propertylist li > .select_wrapper select:hover,.propertylist li > select:focus,.propertylist li > select:active { color: #777; }
.propertylist li > .select_wrapper select.impp { margin-left: -23px; direction: rtl; }
.propertylist li > .select_wrapper select.types { margin-right: -23px; }
.select_wrapper { float: left; overflow: hidden; color: #bbb; font-size: 0.8em; }
.select_wrapper select { float: left; overflow: hidden; text-overflow: ellipsis; color: #bbb; width: 8em; }
.select_wrapper select:hover { overflow: inherit; text-overflow: inherit; }
.select_wrapper select option { color: #777; }
.select_wrapper select:hover,.propertylist li > select:focus,.propertylist li > select:active { color: #777; }
.select_wrapper select.rtl { margin-left: -24px; direction: rtl; }
.select_wrapper select.ltr { margin-right: -23px; }
.propertylist li > input[type="checkbox"].impp { clear: none; }
.propertylist li > label.xab { display: block; color: #bbb; float:left; clear: both; padding: 0.5em 0 0 2.5em; }
.propertylist li > label.xab:hover { color: #777; }
#rightcontent label, #rightcontent dt, #rightcontent th, #rightcontent .label { float: left; font-size: 0.7em; font-weight: bold; color: #bbb !important; max-width: 7em !important; border: 0; }
#rightcontent label:hover, .form dt:hover, #rightcontent input.label:hover { color: #777 !important; }
#rightcontent input.label:hover, #rightcontent input.label:active { border: 0 none !important; border-radius: 0; cursor: pointer; }
.typelist[type="button"] { float: left; max-width: 8em; border: 0; background-color: #fff; color: #bbb; box-shadow: none; } /* for multiselect */
.typelist[type="button"]:hover { color: #777; } /* for multiselect */
.addresslist { clear: both; font-weight: bold; }
#ninjahelp { position: absolute; bottom: 0; left: 0; right: 0; padding: 1em; margin: 1em; opacity: 0.9; }
/* Help section */
#ninjahelp { position: relative; bottom: 0; left: 0; right: 0; padding: 1em; margin: 1em; opacity: 0.9; }
#ninjahelp .close { position: absolute; top: 5px; right: 5px; height: 20px; width: 20px; }
#ninjahelp h2, .help-section h3 { width: 100%; font-weight: bold; text-align: center; }
#ninjahelp h2 { font-size: 1.4em; }
@ -148,12 +170,169 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
.help-section dl { width: 100%; float: left; clear: right; margin: 0; padding: 0; cursor: normal; }
.help-section dt { display: table-cell; clear: left; float: left; width: 35%; margin: 0; padding: 0.2em; text-align: right; text-overflow: ellipsis; vertical-align: text-bottom; font-weight: bold; }
.help-section dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0.2em; white-space: nowrap; vertical-align: text-bottom; }
/* Settings */
.contacts-settings dl { width: 100%; }
.addressbooks-settings table { width: 100%; }
.addressbooks-settings .actions { width: 100%; white-space: nowrap; }
.addressbooks-settings .actions * { float: left; }
.addressbooks-settings .actions input.name { width: 5em; }
.addressbooks-settings .actions input.name { width: 7em; }
.addressbooks-settings a.action { opacity: 0.2; }
.addressbooks-settings a.action { opacity: 0.5; }
.addressbooks-settings a.action:hover { opacity: 1; }
.addressbooks-settings td.active, .addressbooks-settings td.action { width: 20px; }
#contacts-settings .settings {
width: 20px; height: 20px;
float: right;
background:url('%webroot%/core/img/actions/settings.svg') no-repeat center;
}
#contacts-settings.open {
height: auto;
}
#contacts-settings {
-moz-box-sizing: border-box;
box-sizing: border-box;
background: none repeat scroll 0 0 #EEEEEE;
border-right: 1px solid #CCCCCC;
border-top: 1px solid #CCCCCC;
bottom: 0;
height: 2.8em;
margin: 0;
overflow: visible;
padding: 0;
position: fixed;
width: 20em;
z-index: 2;
}
#contacts-settings li,#contacts-settings li:hover { background-color: transparent; }
/* Single elements */
#file_upload_target, #import_upload_target, #crop_target { display:none; }
#toggle_all { position: absolute; bottom: .5em; left: .8em; }
input.propertytype { float: left; font-size: .8em; width: 8em !important; direction: rtl;}
.contactphoto { float: left; display: inline-block; border-radius: 0.3em; border: thin solid #bbb; margin: 0.5em; background: url('%webroot%/core/img/loading.gif') no-repeat center center; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; opacity: 1; }
.contactphoto:hover { background: #fff; cursor: default; }
#photowrapper { display: inline-block; float: left; width: 150px; }
#photowrapper.wait { opacity: 0.6; filter:alpha(opacity=0.6); z-index:1000; background: url('%webroot%/core/img/loading.gif') no-repeat center; cursor: wait; }
#phototools { position:absolute; margin: 5px 0 0 10px; width:auto; height:22px; padding:0px; background-color:#fff; list-style-type:none; border-radius: 0.3em; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; }
#phototools li { display: inline; }
#phototools li a { float:left; cursor:pointer; width:22px; height:22px; opacity: 0.6; }
#phototools li a:hover { opacity: 0.8; }
#contactphoto_fileupload, #import_fileupload { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index:1001; width:0; height:0;}
.favorite { display: inline-block; float: left; height: 20px; width: 20px; background:url('%appswebroot%/contacts/img/inactive_star.png') no-repeat center; }
.favorite.active, .favorite:hover { background:url('%appswebroot%/contacts/img/active_star.png') no-repeat center; }
.favorite.inactive { background:url('%appswebroot%/contacts/img/inactive_star.png') no-repeat center; }
/* Header */
#contactsheader { position: fixed; box-sizing: border-box; padding: 0; margin:0; top:3.5em; left: 32.5em; right: 0; height: 4em; border-bottom: 1px solid #DDDDDD; z-index: 50; }
#contactsheader div.actions { padding: 0 0.5em; margin: 0 auto; height: 100%; width: 90%; }
#contactsheader button, #contactsheader select { position: relative; float: left; min-width: 26px; height: 26px; margin: .7em; padding: .2em; clear: none; }
#contactsheader .back { }
#contactsheader .import { background:url('%webroot%/core/img/actions/upload.svg') no-repeat center; }
#contactsheader .delete { background:url('%webroot%/core/img/actions/delete.svg') no-repeat center; }
#contactsheader .list.add { margin-left: 5em; }
/* Right content layout */
#rightcontent, .rightcontent { position:fixed; top: 7.5em; left: 32.5em; overflow-x:hidden; overflow-y: auto; }
/* Contact layout */
#contact > ul.propertylist {
font-size: 10px;
/*display: table;
border-spacing: 1em;
border: thin solid black;*/
}
#contact > ul.propertylist > li {
display: inline-block;
padding: 1em;
/*display: table-cell;*/
}
.adr.edit {
width: 20em;
border: 1px solid silver; -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; outline:none;
}
.adr.edit ul {
-moz-column-count: 1;
-webkit-columns: 1;
-o-columns: 1;
columns: 1;
}
.adr.edit input.value { border: none; }
.adr.edit input.value:hover { border: none; }
.adr.edit input.value.street, ul.adr.edit input.value.country, ul.adr.edit input.value.region { width: 19em;}
.adr.edit input.value.zip { width: 5em; }
.adr.edit input.value.city { width: 10em; }
#rightcontent footer { padding: 1em; width: 100%; box-sizing: border-box; clear: both; }
#rightcontent footer > { display: inline-block; }
/* contact list */
#contactlist { position: relative; top: 0; left: 0; right: 0; width: 100%; }
#contactlist tr { height: 3em; display: none; }
#contactlist tr.active, #contactlist tr:hover { background-color: #eee; }
#contactlist tr > td { border-bottom: 1px solid #DDDDDD; font-weight: normal; text-align: left; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; white-space: nowrap; }
#contactlist tr > td:hover { overflow: inherit; text-overflow: inherit; background-color: #fff; z-index: 200; }
#contactlist tr > td:not(.adr) { width: 15%; }
#contactlist tr > td.name>input[type=checkbox]:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; float:left; margin:.5em 0 0 1.2em; -webkit-transition:opacity 200ms; -moz-transition:opacity 200ms; -o-transition:opacity 200ms; transition:opacity 200ms; }
#contactlist tr > td.name>input[type="checkbox"]:hover:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; }
#contactlist tr > td.name>input[type="checkbox"]:checked:first-child { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; }
#contactlist tr > td.name { font-weight: bold; text-indent: 1.6em; -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms; position:relative; background-position:1em .5em !important; background-repeat:no-repeat !important; }
#contactlist tr > td a.mailto { position: absolute; float: right; clear: none; cursor:pointer; width:22px; height:22px; z-index: 200; opacity: 0.6; background:url('%webroot%/core/img/actions/mail.svg') no-repeat center; }
#contactlist tr > td a.mailto:hover { opacity: 0.8; }
#contact figure img { -moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em; border: thin solid #bbb; margin: 0.3em; background: url('%webroot%/core/img/loading.gif') no-repeat center center; -moz-box-shadow: 0 1px 3px #777; -webkit-box-shadow: 0 1px 3px #777; box-shadow: 0 1px 3px #777; opacity: 1; }
#contact span.adr {
font-weight: bold;
float: left;
width: 20em;
padding-top: .5em;
overflow: hidden; text-overflow: ellipsis; text-align: bottom; white-space: nowrap;
}
#contact span.adr:hover { /*overflow: inherit;*/ white-space: pre-wrap; }
@media screen and (min-width: 1400px) {
#contact > ul.propertylist {
-moz-column-count: 3;
-webkit-columns: 3;
-o-columns: 3;
columns: 3;
}
}
@media screen and (min-width: 800px) and (max-width: 1400) {
#singlevalues { max-width: 50%; }
#contact > ul.propertylist {
-moz-column-count: 2;
-webkit-columns: 2;
-o-columns: 2;
columns: 2;
}
}
@media screen and (max-width: 400px) {
#contact > ul.propertylist {
-moz-column-count: 1;
-webkit-columns: 1;
-o-columns: 1;
columns: 1;
}
}
@media screen and (max-width: 1500px) {
#contactlist tr td.categories { display: none; }
}
@media screen and (max-width: 1400px) {
#contactlist tr td.adr { display: none; }
}
@media screen and (max-width: 1200px) {
#contactlist tr td.tel { display: none; }
}
@media screen and (max-width: 900px) {
#contactlist tr td.email { display: none; }
}

View File

@ -12,9 +12,20 @@ OCP\App::checkAppEnabled('contacts');
$bookid = isset($_GET['bookid']) ? $_GET['bookid'] : null;
$contactid = isset($_GET['contactid']) ? $_GET['contactid'] : null;
$nl = "\n";
if(isset($bookid)) {
$addressbook = OC_Contacts_App::getAddressbook($bookid);
//$cardobjects = OC_Contacts_VCard::all($bookid);
if(!is_null($bookid)) {
try {
$addressbook = OCA\Contacts\Addressbook::find($bookid); // is owner access check
} catch(Exception $e) {
OCP\JSON::error(
array(
'data' => array(
'message' => $e->getMessage(),
)
)
);
exit();
}
//$cardobjects = OCA\Contacts\VCard::all($bookid);
header('Content-Type: text/directory');
header('Content-Disposition: inline; filename='
. str_replace(' ', '_', $addressbook['displayname']) . '.vcf');
@ -23,14 +34,25 @@ if(isset($bookid)) {
$batchsize = OCP\Config::getUserValue(OCP\User::getUser(),
'contacts',
'export_batch_size', 20);
while($cardobjects = OC_Contacts_VCard::all($bookid, $start, $batchsize)) {
while($cardobjects = OCA\Contacts\VCard::all($bookid, $start, $batchsize)) {
foreach($cardobjects as $card) {
echo $card['carddata'] . $nl;
}
$start += $batchsize;
}
}elseif(isset($contactid)) {
$data = OC_Contacts_App::getContactObject($contactid);
} elseif(!is_null($contactid)) {
try {
$data = OCA\Contacts\VCard::find($contactid);
} catch(Exception $e) {
OCP\JSON::error(
array(
'data' => array(
'message' => $e->getMessage(),
)
)
);
exit();
}
header('Content-Type: text/vcard');
header('Content-Disposition: inline; filename='
. str_replace(' ', '_', $data['fullname']) . '.vcf');

BIN
img/active_star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

BIN
img/checkmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

BIN
img/inactive_star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

BIN
img/starred.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

View File

@ -39,7 +39,7 @@ if(!$file) {
exit();
}
if(isset($_POST['method']) && $_POST['method'] == 'new') {
$id = OC_Contacts_Addressbook::add(OCP\USER::getUser(),
$id = OCA\Contacts\Addressbook::add(OCP\USER::getUser(),
$_POST['addressbookname']);
if(!$id) {
OCP\JSON::error(
@ -49,7 +49,7 @@ if(isset($_POST['method']) && $_POST['method'] == 'new') {
);
exit();
}
OC_Contacts_Addressbook::setActive($id, 1);
OCA\Contacts\Addressbook::setActive($id, 1);
}else{
$id = $_POST['id'];
if(!$id) {
@ -63,7 +63,19 @@ if(isset($_POST['method']) && $_POST['method'] == 'new') {
);
exit();
}
OC_Contacts_App::getAddressbook($id); // is owner access check
try {
OCA\Contacts\Addressbook::find($id); // is owner access check
} catch(Exception $e) {
OCP\JSON::error(
array(
'data' => array(
'message' => $e->getMessage(),
'file'=>$_POST['file']
)
)
);
exit();
}
}
//analyse the contacts file
writeProgress('40');
@ -110,20 +122,21 @@ if(!count($parts) > 0) {
exit();
}
foreach($parts as $part) {
$card = OC_VObject::parse($part);
if (!$card) {
try {
$vcard = Sabre\VObject\Reader::read($part);
} catch (Exception $e) {
$failed += 1;
OCP\Util::writeLog('contacts',
'Import: skipping card. Error parsing VCard: ' . $part,
'Import: skipping card. Error parsing VCard: ' . $e->getMessage(),
OCP\Util::ERROR);
continue; // Ditch cards that can't be parsed by Sabre.
}
try {
OC_Contacts_VCard::add($id, $card);
OCA\Contacts\VCard::add($id, $vcard);
$imported += 1;
} catch (Exception $e) {
OCP\Util::writeLog('contacts',
'Error importing vcard: ' . $e->getMessage() . $nl . $card,
'Error importing vcard: ' . $e->getMessage() . $nl . $vcard,
OCP\Util::ERROR);
$failed += 1;
}

View File

@ -13,8 +13,8 @@ OCP\User::checkLoggedIn();
OCP\App::checkAppEnabled('contacts');
// Get active address books. This creates a default one if none exists.
$ids = OC_Contacts_Addressbook::activeIds(OCP\USER::getUser());
$has_contacts = (count(OC_Contacts_VCard::all($ids, 0, 1)) > 0
$ids = OCA\Contacts\Addressbook::activeIds(OCP\USER::getUser());
$has_contacts = (count(OCA\Contacts\VCard::all($ids, 0, 1)) > 0
? true
: false); // just to check if there are any contacts.
if($has_contacts === false) {
@ -28,15 +28,16 @@ OCP\App::setActiveNavigationEntry('contacts_index');
// Load a specific user?
$id = isset( $_GET['id'] ) ? $_GET['id'] : null;
$impp_types = OC_Contacts_App::getTypesOfProperty('IMPP');
$phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
$email_types = OC_Contacts_App::getTypesOfProperty('EMAIL');
$ims = OC_Contacts_App::getIMOptions();
$impp_types = OCA\Contacts\App::getTypesOfProperty('IMPP');
$adr_types = OCA\Contacts\App::getTypesOfProperty('ADR');
$phone_types = OCA\Contacts\App::getTypesOfProperty('TEL');
$email_types = OCA\Contacts\App::getTypesOfProperty('EMAIL');
$ims = OCA\Contacts\App::getIMOptions();
$im_protocols = array();
foreach($ims as $name => $values) {
$im_protocols[$name] = $values['displayname'];
}
$categories = OC_Contacts_App::getCategories();
$categories = OCA\Contacts\App::getCategories();
$upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize'));
$post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
@ -48,7 +49,10 @@ $maxUploadFilesize = min($maxUploadFilesize, $freeSpace);
OCP\Util::addscript('', 'jquery.multiselect');
OCP\Util::addscript('', 'oc-vcategories');
OCP\Util::addscript('contacts', 'app');
OCP\Util::addscript('contacts', 'contacts');
OCP\Util::addscript('contacts', 'modernizr');
OCP\Util::addscript('contacts', 'placeholder.polyfill.jquery');
OCP\Util::addscript('contacts', 'expanding');
OCP\Util::addscript('contacts', 'jquery.combobox');
OCP\Util::addscript('files', 'jquery.fileupload');
@ -60,15 +64,18 @@ OCP\Util::addStyle('contacts', 'jquery.combobox');
OCP\Util::addStyle('contacts', 'jquery.Jcrop');
OCP\Util::addStyle('contacts', 'contacts');
$tmpl = new OCP\Template( "contacts", "index", "user" );
$tmpl = new OCP\Template( "contacts", "contacts", "user" );
$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize, false);
$tmpl->assign('uploadMaxHumanFilesize',
OCP\Util::humanFileSize($maxUploadFilesize), false);
$tmpl->assign('addressbooks', OCA\Contacts\Addressbook::all(OCP\USER::getUser()), false);
$tmpl->assign('phone_types', $phone_types, false);
$tmpl->assign('email_types', $email_types, false);
$tmpl->assign('adr_types', $adr_types, false);
$tmpl->assign('impp_types', $impp_types, false);
$tmpl->assign('categories', $categories, false);
$tmpl->assign('im_protocols', $im_protocols, false);
$tmpl->assign('has_contacts', $has_contacts, false);
$tmpl->assign('id', $id);
$tmpl->printPage();
$tmpl->assign('is_indexed', OCP\Config::getUserValue(OCP\User::getUser(), 'contacts', 'contacts_indexed', 'no'));
$tmpl->printPage();

1709
js/app.js Normal file
View File

@ -0,0 +1,1709 @@
var utils = {};
/**
* utils.isArray
*
* Best guess if object is an array.
*/
utils.isArray = function(obj) {
// do an instanceof check first
if (obj instanceof Array) {
return true;
}
// then check for obvious falses
if (typeof obj !== 'object') {
return false;
}
if (utils.type(obj) === 'array') {
return true;
}
return false;
};
utils.isInt = function(s) {
return typeof s === 'number' && (s.toString().search(/^-?[0-9]+$/) === 0);
}
utils.isUInt = function(s) {
return typeof s === 'number' && (s.toString().search(/^[0-9]+$/) === 0);
}
/**
* utils.type
*
* Attempt to ascertain actual object type.
*/
utils.type = function(obj) {
if (obj === null || typeof obj === 'undefined') {
return String (obj);
}
return Object.prototype.toString.call(obj)
.replace(/\[object ([a-zA-Z]+)\]/, '$1').toLowerCase();
};
utils.moveCursorToEnd = function(el) {
if (typeof el.selectionStart === 'number') {
el.selectionStart = el.selectionEnd = el.value.length;
} else if (typeof el.createTextRange !== 'undefined') {
el.focus();
var range = el.createTextRange();
range.collapse(false);
range.select();
}
}
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
Array.prototype.clone = function() {
return this.slice(0);
};
Array.prototype.clean = function(deleteValue) {
var arr = this.clone();
for (var i = 0; i < arr.length; i++) {
if (arr[i] == deleteValue) {
arr.splice(i, 1);
i--;
}
}
return arr;
};
// Keep it DRY ;)
var wrongKey = function(event) {
return (event.type === 'keydown' && (event.keyCode !== 32 && event.keyCode !== 13));
}
/**
* Simply notifier
* Arguments:
* @param message The text message to show.
* @param timeout The timeout in seconds before the notification disappears. Default 10.
* @param timeouthandler A function to run on timeout.
* @param clickhandler A function to run on click. If a timeouthandler is given it will be cancelled on click.
* @param data An object that will be passed as argument to the timeouthandler and clickhandler functions.
* @param cancel If set cancel all ongoing timer events and hide the notification.
*/
OC.notify = function(params) {
var self = this;
if(!self.notifier) {
self.notifier = $('#notification');
if(!self.notifier.length) {
$('#content').prepend('<div id="notification" />');
self.notifier = $('#notification');
}
}
if(params.cancel) {
self.notifier.off('click');
for(var id in self.notifier.data()) {
if($.isNumeric(id)) {
clearTimeout(parseInt(id));
}
}
self.notifier.text('').fadeOut().removeData();
return;
}
self.notifier.text(params.message);
self.notifier.fadeIn();
self.notifier.on('click', function() { $(this).fadeOut();});
var timer = setTimeout(function() {
/*if(!self || !self.notifier) {
var self = OC.Contacts;
self.notifier = $('#notification');
}*/
self.notifier.fadeOut();
if(params.timeouthandler && $.isFunction(params.timeouthandler)) {
params.timeouthandler(self.notifier.data(dataid));
self.notifier.off('click');
self.notifier.removeData(dataid);
}
}, params.timeout && $.isNumeric(params.timeout) ? parseInt(params.timeout)*1000 : 10000);
var dataid = timer.toString();
if(params.data) {
self.notifier.data(dataid, params.data);
}
if(params.clickhandler && $.isFunction(params.clickhandler)) {
self.notifier.on('click', function() {
/*if(!self || !self.notifier) {
var self = OC.Contacts;
self.notifier = $(this);
}*/
clearTimeout(timer);
self.notifier.off('click');
params.clickhandler(self.notifier.data(dataid));
self.notifier.removeData(dataid);
});
}
}
var GroupList = function(groupList, listItemTmpl) {
this.$groupList = groupList;
var self = this;
var numtypes = ['category', 'fav', 'all'];
this.$groupList.on('click', 'h3', function(event) {
$('.tipsy').remove();
if(wrongKey(event)) {
return;
}
console.log($(event.target));
if($(event.target).is('.action.delete')) {
var id = $(event.target).parents('h3').first().data('id');
self.deleteGroup(id, function(response) {
if(response.status !== 'success') {
OC.notify({message:response.data.message});
}
})
} else {
self.selectGroup({element:$(this)});
}
});
this.$groupListItemTemplate = listItemTmpl;
this.categories = [];
}
GroupList.prototype.nameById = function(id) {
return this.findById(id).contents().filter(function(){ return(this.nodeType == 3); }).text().trim()
}
GroupList.prototype.findById = function(id) {
return this.$groupList.find('h3[data-id="' + id + '"]');
}
GroupList.prototype.isFavorite = function(contactid) {
return this.inGroup(contactid, 'fav');
}
GroupList.prototype.selectGroup = function(params) {
var id, $elem;
if(typeof params.id !== 'undefined') {
id = params.id;
$elem = this.findById(id);
} else if(typeof params.element !== 'undefined') {
id = params.element.data('id');
$elem = params.element;
}
if(!$elem) {
self.selectGroup('all');
return;
}
console.log('selectGroup', id, $elem);
this.$groupList.find('h3').removeClass('active');
$elem.addClass('active');
this.lastgroup = id;
$(document).trigger('status.group.selected', {
id: this.lastgroup,
type: $elem.data('type'),
contacts: $elem.data('contacts'),
});
}
GroupList.prototype.inGroup = function(contactid, groupid) {
var $groupelem = this.findById(groupid);
var contacts = $groupelem.data('contacts');
return (contacts.indexOf(contactid) !== -1);
}
GroupList.prototype.setAsFavorite = function(contactid, state, cb) {
contactid = parseInt(contactid);
var $groupelem = this.findById('fav');
var contacts = $groupelem.data('contacts');
if(state) {
OCCategories.addToFavorites(contactid, 'contact', function(jsondata) {
if(jsondata.status === 'success') {
contacts.push(contactid);
$groupelem.data('contacts', contacts);
$groupelem.find('.numcontacts').text(contacts.length);
if(contacts.length > 0 && $groupelem.is(':hidden')) {
$groupelem.show();
}
}
if(typeof cb === 'function') {
cb(jsondata);
} else if(jsondata.status !== 'success') {
OC.notify({message:t('contacts', jsondata.data.message)});
}
});
} else {
OCCategories.removeFromFavorites(contactid, 'contact', function(jsondata) {
if(jsondata.status === 'success') {
contacts.splice(contacts.indexOf(contactid), 1);
//console.log('contacts', contacts, contacts.indexOf(id), contacts.indexOf(String(id)));
$groupelem.data('contacts', contacts);
$groupelem.find('.numcontacts').text(contacts.length);
if(contacts.length === 0 && $groupelem.is(':visible')) {
$groupelem.hide();
}
}
if(typeof cb === 'function') {
cb(jsondata);
} else if(jsondata.status !== 'success') {
OC.notify({message:t('contacts', jsondata.data.message)});
}
});
}
}
/**
* Add one or more contact ids to a group
* @param contactid An integer id or an array of integer ids.
* @param groupid The integer id of the group
* @param cb Optional call-back function
*/
GroupList.prototype.addTo = function(contactid, groupid, cb) {
console.log('GroupList.addTo', contactid, groupid);
var $groupelem = this.findById(groupid);
var contacts = $groupelem.data('contacts');
var ids = [];
if(!contacts) {
console.log('Contacts not found, adding list!!!');
contacts = [];
}
var self = this;
var doPost = false;
if(typeof contactid === 'number') {
if(contacts.indexOf(contactid) === -1) {
ids.push(contactid);
doPost = true;
} else {
if(typeof cb == 'function') {
cb({status:'error', message:t('contacts', 'Contact is already in this group.')});
}
}
} else if(utils.isArray(contactid)) {
$.each(contactid, function(i, id) {
if(contacts.indexOf(id) === -1) {
ids.push(id);
}
});
if(ids.length > 0) {
doPost = true;
} else {
if(typeof cb == 'function') {
cb({status:'error', message:t('contacts', 'Contacts are already in this group.')});
}
}
}
if(doPost) {
$.post(OC.filePath('contacts', 'ajax', 'categories/addto.php'), {contactids: ids, categoryid: groupid},function(jsondata) {
if(!jsondata) {
if(typeof cb === 'function') {
cb({status:'error', message:'Network or server error. Please inform administrator.'});
}
return;
}
if(jsondata.status === 'success') {
contacts = contacts.concat(ids).sort();
$groupelem.data('contacts', contacts);
var $numelem = $groupelem.find('.numcontacts');
$numelem.text(contacts.length).switchClass('', 'active', 200);
setTimeout(function() {
$numelem.switchClass('active', '', 1000);
}, 2000);
if(typeof cb === 'function') {
cb({status:'success', ids:ids});
} else {
$(document).trigger('status.group.contactadded', {
contactid: contactid,
groupid: groupid,
groupname: self.nameById(groupid),
});
}
} else {
if(typeof cb == 'function') {
cb({status:'error', message:jsondata.data.message});
}
}
});
}
}
GroupList.prototype.removeFrom = function(contactid, groupid, cb) {
console.log('GroupList.removeFrom', contactid, groupid);
var $groupelem = this.findById(groupid);
var contacts = $groupelem.data('contacts');
var ids = [];
// If the contact is in the category remove it from internal list.
if(!contacts) {
if(typeof cb === 'function') {
cb({status:'error', message:t('contacts', 'Couldn\'t get contact list.')});
}
return;
}
var doPost = false;
if(typeof contactid === 'number') {
if(contacts.indexOf(contactid) !== -1) {
ids.push(contactid);
doPost = true;
} else {
if(typeof cb == 'function') {
cb({status:'error', message:t('contacts', 'Contact is not in this group.')});
}
}
} else if(utils.isArray(contactid)) {
$.each(contactid, function(i, id) {
if(contacts.indexOf(id) !== -1) {
ids.push(id);
}
});
if(ids.length > 0) {
doPost = true;
} else {
console.log(contactid, 'not in', contacts);
if(typeof cb == 'function') {
cb({status:'error', message:t('contacts', 'Contacts are not in this group.')});
}
}
}
if(doPost) {
$.post(OC.filePath('contacts', 'ajax', 'categories/removefrom.php'), {contactids: ids, categoryid: groupid},function(jsondata) {
if(!jsondata) {
if(typeof cb === 'function') {
cb({status:'error', message:'Network or server error. Please inform administrator.'});
}
return;
}
if(jsondata.status === 'success') {
$.each(ids, function(idx, id) {
contacts.splice(contacts.indexOf(id), 1);
});
//console.log('contacts', contacts, contacts.indexOf(id), contacts.indexOf(String(id)));
$groupelem.data('contacts', contacts);
var $numelem = $groupelem.find('.numcontacts');
$numelem.text(contacts.length).switchClass('', 'active', 200);
setTimeout(function() {
$numelem.switchClass('active', '', 1000);
}, 2000);
if(typeof cb === 'function') {
cb({status:'success', ids:ids});
}
} else {
if(typeof cb == 'function') {
cb({status:'error', message:jsondata.data.message});
}
}
});
}
}
GroupList.prototype.removeFromAll = function(contactid, alsospecial) {
var self = this;
var selector = alsospecial ? 'h3' : 'h3[data-type="category"]';
$.each(this.$groupList.find(selector), function(i, group) {
self.removeFrom(contactid, $(this).data('id'));
});
}
GroupList.prototype.categoriesChanged = function(newcategories) {
console.log('GroupList.categoriesChanged, I should do something');
}
GroupList.prototype.contactDropped = function(event, ui) {
var dragitem = ui.draggable, droptarget = $(this);
console.log('dropped', dragitem);
if(dragitem.is('tr')) {
console.log('tr dropped', dragitem.data('id'), 'on', $(this).data('id'));
if($(this).data('type') === 'fav') {
$(this).data('obj').setAsFavorite(dragitem.data('id'), true);
} else {
$(this).data('obj').addTo(dragitem.data('id'), $(this).data('id'));
}
}
}
GroupList.prototype.deleteGroup = function(groupid, cb) {
var $elem = this.findById(groupid);
var $newelem = $elem.prev('h3');
var name = this.nameById(groupid);
var contacts = $elem.data('contacts');
var self = this;
console.log('delete group', groupid, contacts);
$.post(OC.filePath('contacts', 'ajax', 'categories/delete.php'), {categories: name}, function(jsondata) {
if (jsondata && jsondata.status == 'success') {
$(document).trigger('status.group.groupremoved', {
groupid: groupid,
newgroupid: parseInt($newelem.data('id')),
groupname: self.nameById(groupid),
contacts: contacts,
});
$elem.remove();
self.selectGroup({element:$newelem});
} else {
//
}
if(typeof cb === 'function') {
cb(jsondata);
}
});
}
GroupList.prototype.addGroup = function(name, cb) {
console.log('GroupList.addGroup', name);
contacts = []; // $.map(contacts, function(c) {return parseInt(c)});
var self = this, exists = false;
self.$groupList.find('h3[data-type="category"]').each(function() {
if ($(this).data('name').toLowerCase() === name.toLowerCase()) {
exists = true;
return false; //break out of loop
}
});
if(exists) {
if(typeof cb === 'function') {
cb({status:'error', message:t('contacts', 'A group named {group} already exists', {group: name})});
}
return;
}
$.post(OC.filePath('contacts', 'ajax', 'categories/add.php'), {category: name}, function(jsondata) {
if (jsondata && jsondata.status == 'success') {
var tmpl = self.$groupListItemTemplate;
var $elem = (tmpl).octemplate({
id: jsondata.data.id,
type: 'category',
num: contacts.length,
name: name,
})
self.categories.push({id: jsondata.data.id, name: name});
$elem.data('obj', self);
$elem.data('contacts', contacts);
$elem.data('name', name);
$elem.data('id', jsondata.data.id);
var added = false;
self.$groupList.find('h3.group[data-type="category"]').each(function() {
if ($(this).data('name').toLowerCase().localeCompare(name.toLowerCase()) > 0) {
$(this).before($elem);
added = true;
return false;
}
});
if(!added) {
$elem.insertAfter(self.$groupList.find('h3.group[data-type="category"]').last());
}
if(typeof cb === 'function') {
cb({status:'success', id:parseInt(jsondata.data.id), name:name});
}
} else {
if(typeof cb === 'function') {
cb({status:'error', message:jsondata.data.message});
}
}
});
}
GroupList.prototype.loadGroups = function(numcontacts, cb) {
var self = this;
var acceptdrop = 'tr.contact';
var $groupList = this.$groupList;
var tmpl = this.$groupListItemTemplate;
tmpl.octemplate({id: 'all', type: 'all', num: numcontacts, name: t('contacts', 'All')}).appendTo($groupList);
$.getJSON(OC.filePath('contacts', 'ajax', 'categories/list.php'), {}, function(jsondata) {
if (jsondata && jsondata.status == 'success') {
self.lastgroup = jsondata.data.lastgroup;
self.sortorder = jsondata.data.sortorder.length > 0
? $.map(jsondata.data.sortorder.split(','), function(c) {return parseInt(c)})
: [];
console.log('sortorder', self.sortorder);
// Favorites
var contacts = $.map(jsondata.data.favorites, function(c) {return parseInt(c)});
var $elem = tmpl.octemplate({
id: 'fav',
type: 'fav',
num: contacts.length,
name: t('contacts', 'Favorites')
}).appendTo($groupList);
$elem.data('obj', self);
$elem.data('contacts', contacts).find('.numcontacts').before('<span class="starred" />');
$elem.droppable({
drop: self.contactDropped,
activeClass: 'ui-state-active',
hoverClass: 'ui-state-hover',
accept: acceptdrop
});
if(contacts.length === 0) {
$elem.hide();
}
console.log('favorites', $elem.data('contacts'));
// Normal groups
$.each(jsondata.data.categories, function(c, category) {
var contacts = $.map(category.contacts, function(c) {return parseInt(c)});
var $elem = (tmpl).octemplate({
id: category.id,
type: 'category',
num: contacts.length,
name: category.name,
});
self.categories.push({id: category.id, name: category.name});
$elem.data('obj', self);
$elem.data('contacts', contacts);
$elem.data('name', category.name);
$elem.data('id', category.id);
$elem.droppable({
drop: self.contactDropped,
activeClass: 'ui-state-hover',
accept: acceptdrop
});
$elem.appendTo($groupList);
});
var elems = $groupList.find('h3[data-type="category"]').get();
elems.sort(function(a, b) {
return self.sortorder.indexOf(parseInt($(a).data('id'))) > self.sortorder.indexOf(parseInt($(b).data('id')));
});
$.each(elems, function(index, elem) {
$groupList.append(elem);
});
// Shared addressbook
$.each(jsondata.data.shared, function(c, shared) {
var sharedindicator = '<img class="shared svg" src="' + OC.imagePath('core', 'actions/shared') + '"'
+ 'title="' + t('contacts', 'Shared by {owner}', {owner:shared.userid}) + '" />'
var $elem = (tmpl).octemplate({
id: shared.id,
type: 'shared',
num: '', //jsondata.data.shared.length,
name: shared.displayname,
});
$elem.find('.numcontacts').after(sharedindicator);
$elem.data('obj', self);
$elem.data('name', shared.displayname);
$elem.data('id', shared.id);
$elem.appendTo($groupList);
});
$groupList.sortable({
items: 'h3[data-type="category"]',
stop: function() {
console.log('stop sorting', $(this));
var ids = [];
$.each($(this).children('h3[data-type="category"]'), function(i, elem) {
ids.push($(elem).data('id'))
})
self.sortorder = ids;
$(document).trigger('status.groups.sorted', {
sortorder: self.sortorder.join(','),
});
},
});
var $elem = self.findById(self.lastgroup);
$elem.addClass('active');
$(document).trigger('status.group.selected', {
id: self.lastgroup,
type: $elem.data('type'),
contacts: $elem.data('contacts'),
});
} // TODO: else
if(typeof cb === 'function') {
cb();
}
});
}
OC.Contacts = OC.Contacts || {
init:function(id) {
if(oc_debug === true) {
$(document).ajaxError(function(e, xhr, settings, exception) {
// Don't try to get translation because it's likely a network error.
OC.notify({
message: 'error in: ' + settings.url + ', '+'error: ' + xhr.responseText,
});
});
}
//if(id) {
this.currentid = parseInt(id);
console.log('init, id:', id);
//}
// Holds an array of {id,name} maps
this.scrollTimeoutMiliSecs = 100;
this.isScrolling = false;
this.cacheElements();
this.Contacts = new OC.Contacts.ContactList(
this.$contactList,
this.$contactListItemTemplate,
this.$contactFullTemplate,
this.detailTemplates
);
this.Groups = new GroupList(this.$groupList, this.$groupListItemTemplate);
OCCategories.changed = this.Groups.categoriesChanged;
OCCategories.app = 'contacts';
OCCategories.type = 'contact';
this.bindEvents();
this.$toggleAll.show();
this.showActions(['addcontact']);
// Wait 2 mins then check if contacts are indexed.
setTimeout(function() {
if(!is_indexed) {
OC.notify({message:t('contacts', 'Indexing contacts'), timeout:20});
$.post(OC.filePath('contacts', 'ajax', 'indexproperties.php'));
} else {
console.log('contacts are indexed.');
}
}, 10000);
},
loading:function(obj, state) {
$(obj).toggleClass('loading', state);
},
/**
* Show/hide elements in the header
* @param act An array of actions to show based on class name e.g ['add', 'delete']
*/
hideActions:function() {
this.showActions(false);
},
showActions:function(act) {
this.$headeractions.children().hide();
if(act && act.length > 0) {
this.$headeractions.children('.'+act.join(',.')).show();
}
},
showAction:function(act, show) {
this.$headeractions.find('.' + act).toggle(show);
},
cacheElements: function() {
var self = this;
this.detailTemplates = {};
// Load templates for contact details.
// The weird double loading is because jquery apparently doesn't
// create a searchable object from a script element.
$.each($($('#contactDetailsTemplate').html()), function(idx, node) {
if(node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'DIV') {
var $tmpl = $(node.innerHTML);
self.detailTemplates[$tmpl.data('element')] = $(node.outerHTML);
}
});
this.$groupListItemTemplate = $('#groupListItemTemplate');
this.$contactListItemTemplate = $('#contactListItemTemplate');
this.$contactFullTemplate = $('#contactFullTemplate');
this.$contactDetailsTemplate = $('#contactDetailsTemplate');
this.$rightContent = $('#rightcontent');
this.$header = $('#contactsheader');
this.$headeractions = this.$header.find('div.actions');
this.$groupList = $('#grouplist');
this.$contactList = $('#contactlist');
this.$contactListHeader = $('#contactlistheader');
this.$toggleAll = $('#toggle_all');
this.$groups = this.$headeractions.find('.groups');
this.$ninjahelp = $('#ninjahelp');
this.$firstRun = $('#firstrun');
this.$settings = $('#contacts-settings');
},
// Build the select to add/remove from groups.
buildGroupSelect: function() {
// If a contact is open we know which categories it's in
if(this.currentid) {
var contact = this.Contacts.contacts[this.currentid];
this.$groups.find('optgroup,option:not([value="-1"])').remove();
var addopts = '', rmopts = '';
$.each(this.Groups.categories, function(i, category) {
if(contact.inGroup(category.name)) {
rmopts += '<option value="' + category.id + '">' + category.name + '</option>';
} else {
addopts += '<option value="' + category.id + '">' + category.name + '</option>';
}
});
if(addopts.length) {
$(addopts).appendTo(this.$groups)
.wrapAll('<optgroup data-action="add" label="' + t('contacts', 'Add to...') + '"/>');
}
if(rmopts.length) {
$(rmopts).appendTo(this.$groups)
.wrapAll('<optgroup data-action="remove" label="' + t('contacts', 'Remove from...') + '"/>');
}
} else if(this.Contacts.getSelectedContacts().length > 0) { // Otherwise add all categories to both add and remove
this.$groups.find('optgroup,option:not([value="-1"])').remove();
var addopts = '', rmopts = '';
$.each(this.Groups.categories, function(i, category) {
rmopts += '<option value="' + category.id + '">' + category.name + '</option>';
addopts += '<option value="' + category.id + '">' + category.name + '</option>';
});
$(addopts).appendTo(this.$groups)
.wrapAll('<optgroup data-action="add" label="' + t('contacts', 'Add to...') + '"/>');
$(rmopts).appendTo(this.$groups)
.wrapAll('<optgroup data-action="remove" label="' + t('contacts', 'Remove from...') + '"/>');
} else {
// 3rd option: No contact open, none checked, just show "Add group..."
this.$groups.find('optgroup,option:not([value="-1"])').remove();
}
$('<option value="add">' + t('contacts', 'Add group...') + '</option>').appendTo(this.$groups);
this.$groups.val(-1);
},
bindEvents: function() {
var self = this;
// Should fix Opera check for delayed delete.
$(window).unload(function (){
$(window).trigger('beforeunload');
});
// App specific events
$(document).bind('status.contact.deleted', function(e, data) {
var id = parseInt(data.id);
console.log('contact', data.id, 'deleted');
// update counts on group lists
self.Groups.removeFromAll(data.id, true)
});
$(document).bind('status.contact.added', function(e, data) {
self.currentid = parseInt(data.id);
self.buildGroupSelect();
self.showActions(['back', 'download', 'delete', 'groups', 'favorite']);
});
$(document).bind('status.contact.error', function(e, data) {
OC.notify({message:data.message});
});
$(document).bind('status.contact.enabled', function(e, enabled) {
console.log('status.contact.enabled', enabled)
if(enabled) {
self.showActions(['back', 'download', 'delete', 'groups', 'favorite']);
} else {
self.showActions(['back']);
}
if(self.Groups.isFavorite(self.currentid)) {
self.$header.find('.favorite').switchClass('inactive', 'active');
} else {
self.$header.find('.favorite').switchClass('active', 'inactive');
}
});
$(document).bind('status.contacts.loaded', function(e, result) {
console.log('status.contacts.loaded', result);
if(result.status !== true) {
alert('Error loading contacts!');
} else {
self.numcontacts = result.numcontacts;
self.loading(self.$rightContent, false);
self.Groups.loadGroups(self.numcontacts, function() {
self.loading($('#leftcontent'), false);
console.log('Groups loaded, currentid', self.currentid);
if(self.currentid) {
self.openContact(self.currentid);
}
});
}
});
$(document).bind('status.contact.currentlistitem', function(e, result) {
//console.log('status.contact.currentlistitem', result, self.$rightContent.height());
if(self.dontScroll !== true) {
if(result.pos > self.$rightContent.height()) {
self.$rightContent.scrollTop(result.pos - self.$rightContent.height() + result.height);
}
else if(result.pos < self.$rightContent.offset().top) {
self.$rightContent.scrollTop(result.pos);
}
} else {
setTimeout(function() {
self.dontScroll = false;
}, 100);
}
self.currentlistid = result.id
});
$(document).bind('status.nomorecontacts', function(e, result) {
console.log('status.nomorecontacts', result);
self.$contactList.hide();
self.$firstRun.show();
// TODO: Show a first-run page.
});
$(document).bind('status.visiblecontacts', function(e, result) {
console.log('status.visiblecontacts', result);
// TODO: To be decided.
});
// A contact id was in the request
$(document).bind('request.loadcontact', function(e, result) {
console.log('request.loadcontact', result);
if(self.numcontacts) {
self.openContact(result.id);
} else {
// Contacts are not loaded yet, try again.
console.log('waiting for contacts to load');
setTimeout(function() {
$(document).trigger('request.loadcontact', {
id: result.id,
});
}, 1000);
}
});
$(document).bind('request.select.contactphoto.fromlocal', function(e, result) {
console.log('request.select.contactphoto.fromlocal', result);
$('#contactphoto_fileupload').trigger('click');
});
$(document).bind('request.select.contactphoto.fromcloud', function(e, result) {
console.log('request.select.contactphoto.fromcloud', result);
OC.dialogs.filepicker(t('contacts', 'Select photo'), function(path) {
self.cloudPhotoSelected(self.currentid, path);
}, false, 'image', true);
});
$(document).bind('request.edit.contactphoto', function(e, result) {
console.log('request.edit.contactphoto', result);
self.editCurrentPhoto(result.id);
});
$(document).bind('request.addressbook.activate', function(e, result) {
console.log('request.addressbook.activate', result);
self.Contacts.showFromAddressbook(result.id, result.activate);
});
$(document).bind('status.contact.removedfromgroup', function(e, result) {
console.log('status.contact.removedfromgroup', result);
if(self.currentgroup == result.groupid) {
self.Contacts.hideContact(result.contactid);
self.closeContact(result.contactid);
}
});
$(document).bind('status.group.groupremoved', function(e, result) {
console.log('status.group.groupremoved', result);
if(parseInt(result.groupid) === parseInt(self.currentgroup)) {
console.time('hiding');
self.Contacts.showContacts([]);
console.timeEnd('hiding');
self.currentgroup = 'all';
}
$.each(result.contacts, function(idx, contactid) {
var contact = self.Contacts.findById(contactid);
console.log('contactid', contactid, contact);
self.Contacts.findById(contactid).removeFromGroup(result.groupname);
});
});
$(document).bind('status.group.contactadded', function(e, result) {
console.log('status.group.contactadded', result);
self.Contacts.contacts[parseInt(result.contactid)].addToGroup(result.groupname);
});
// Group sorted, save the sort order
$(document).bind('status.groups.sorted', function(e, result) {
console.log('status.groups.sorted', result);
$.post(OC.filePath('contacts', 'ajax', 'setpreference.php'), {'key':'groupsort', 'value':result.sortorder}, function(jsondata) {
if(jsondata.status !== 'success') {
OC.notify({message: jsondata ? jsondata.data.message : t('contacts', 'Network or server error. Please inform administrator.')});
}
});
});
// Group selected, only show contacts from that group
$(document).bind('status.group.selected', function(e, result) {
console.log('status.group.selected', result);
self.currentgroup = result.id;
// Close any open contact.
if(self.currentid) {
var id = self.currentid;
self.closeContact(id);
self.Contacts.jumpToContact(id);
}
self.$contactList.show();
self.$toggleAll.show();
self.showActions(['addcontact']);
if(result.type === 'category' || result.type === 'fav') {
self.Contacts.showContacts(result.contacts);
} else if(result.type === 'shared') {
self.Contacts.showFromAddressbook(self.currentgroup, true, true);
} else {
self.Contacts.showContacts(self.currentgroup);
}
$.post(OC.filePath('contacts', 'ajax', 'setpreference.php'), {'key':'lastgroup', 'value':self.currentgroup}, function(jsondata) {
if(!jsondata || jsondata.status !== 'success') {
OC.notify({message: (jsondata && jsondata.data) ? jsondata.data.message : t('contacts', 'Network or server error. Please inform administrator.')});
}
});
self.$rightContent.scrollTop(0);
});
// mark items whose title was hid under the top edge as read
/*this.$rightContent.scroll(function() {
// prevent too many scroll requests;
if(!self.isScrolling) {
self.isScrolling = true;
var num = self.$contactList.find('tr').length;
//console.log('num', num);
var offset = self.$contactList.find('tr:eq(' + (num-20) + ')').offset().top;
if(offset < self.$rightContent.height()) {
console.log('load more');
self.Contacts.loadContacts(num, function() {
self.isScrolling = false;
});
} else {
setTimeout(function() {
self.isScrolling = false;
}, self.scrollTimeoutMiliSecs);
}
//console.log('scroll, unseen:', offset, self.$rightContent.height());
}
});*/
this.$settings.find('.settings').on('click keydown',function(event) {
if(wrongKey(event)) {
return;
}
var bodyListener = function(e) {
if(self.$settings.find($(e.target)).length == 0) {
self.$settings.switchClass('open', '');
}
}
if(self.$settings.hasClass('open')) {
self.$settings.switchClass('open', '');
$('body').unbind('click', bodyListener);
} else {
self.$settings.switchClass('', 'open');
$('body').bind('click', bodyListener);
}
});
$('#contactphoto_fileupload').on('change', function() {
self.uploadPhoto(this.files);
});
$('#groupactions > .addgroup').on('click keydown',function(event) {
if(wrongKey(event)) {
return;
}
self.addGroup();
});
this.$ninjahelp.find('.close').on('click keydown',function(event) {
if(wrongKey(event)) {
return;
}
self.$ninjahelp.hide();
});
this.$toggleAll.on('change', function() {
var isChecked = $(this).is(':checked');
self.setAllChecked(isChecked);
if(self.$groups.find('option').length === 1) {
self.buildGroupSelect();
}
if(isChecked) {
self.showActions(['addcontact', 'groups', 'delete']);
} else {
self.showActions(['addcontact']);
}
});
this.$contactList.on('change', 'input:checkbox', function(event) {
if($(this).is(':checked')) {
if(self.$groups.find('option').length === 1) {
self.buildGroupSelect();
}
self.showActions(['addcontact', 'groups', 'delete']);
} else if(self.Contacts.getSelectedContacts().length === 0) {
self.showActions(['addcontact']);
}
});
this.$groups.on('change', function() {
var $opt = $(this).find('option:selected');
var action = $opt.parent().data('action');
var ids, groupName, groupId, buildnow = false;
// If a contact is open the action is only applied to that,
// otherwise on all selected items.
if(self.currentid) {
ids = [self.currentid,];
buildnow = true
} else {
ids = self.Contacts.getSelectedContacts();
}
self.setAllChecked(false);
self.$toggleAll.prop('checked', false);
if(!self.currentid) {
self.showActions(['addcontact']);
}
if($opt.val() === 'add') { // Add new group
action = 'add';
console.log('add group...');
self.$groups.val(-1);
self.addGroup(function(response) {
if(response.status === 'success') {
groupId = response.id;
groupName = response.name;
self.Groups.addTo(ids, groupId, function(result) {
if(result.status === 'success') {
$.each(ids, function(idx, id) {
// Delay each contact to not trigger too many ajax calls
// at a time.
setTimeout(function() {
self.Contacts.contacts[id].addToGroup(groupName);
// I don't think this is used...
if(buildnow) {
self.buildGroupSelect();
}
$(document).trigger('status.contact.addedtogroup', {
contactid: id,
groupid: groupId,
groupname: groupName,
});
}, 1000);
});
} else {
// TODO: Use message return from Groups object.
OC.notify({message:t('contacts', t('contacts', 'Error adding to group.'))});
}
});
} else {
OC.notify({message: response.message});
}
});
return;
}
groupName = $opt.text(), groupId = $opt.val();
console.log('trut', groupName, groupId);
if(action === 'add') {
self.Groups.addTo(ids, $opt.val(), function(result) {
console.log('after add', result);
if(result.status === 'success') {
$.each(result.ids, function(idx, id) {
// Delay each contact to not trigger too many ajax calls
// at a time.
setTimeout(function() {
console.log('adding', id, 'to', groupName);
self.Contacts.contacts[id].addToGroup(groupName);
// I don't think this is used...
if(buildnow) {
self.buildGroupSelect();
}
$(document).trigger('status.contact.addedtogroup', {
contactid: id,
groupid: groupId,
groupname: groupName,
});
}, 1000);
});
} else {
var msg = result.message ? result.message : t('contacts', 'Error adding to group.');
OC.notify({message:msg});
}
});
if(!buildnow) {
self.$groups.val(-1).hide().find('optgroup,option:not([value="-1"])').remove();
}
} else if(action === 'remove') {
self.Groups.removeFrom(ids, $opt.val(), function(result) {
console.log('after remove', result);
if(result.status === 'success') {
var groupname = $opt.text(), groupid = $opt.val();
$.each(result.ids, function(idx, id) {
self.Contacts.contacts[id].removeFromGroup(groupname);
if(buildnow) {
self.buildGroupSelect();
}
// If a group is selected the contact has to be removed from the list
$(document).trigger('status.contact.removedfromgroup', {
contactid: id,
groupid: groupId,
groupname: groupName,
});
});
} else {
var msg = result.message ? result.message : t('contacts', 'Error removing from group.');
OC.notify({message:msg});
}
});
if(!buildnow) {
self.$groups.val(-1).hide().find('optgroup,option:not([value="-1"])').remove();
}
} // else something's wrong ;)
self.setAllChecked(false);
});
// Contact list. Either open a contact or perform an action (mailto etc.)
this.$contactList.on('click', 'tr', function(event) {
if($(event.target).is('input')) {
return;
}
if(event.ctrlKey) {
event.stopPropagation();
event.preventDefault();
console.log('select', event);
self.dontScroll = true;
self.Contacts.select($(this).data('id'), true);
return;
}
if($(event.target).is('a.mailto')) {
var mailto = 'mailto:' + $(this).find('.email').text().trim();
console.log('mailto', mailto);
try {
window.location.href=mailto;
} catch(e) {
alert(t('contacts', 'There was an error opening a mail composer.'));
}
return;
}
self.openContact($(this).data('id'));
});
$('.addcontact').on('click keydown', function(event) {
if(wrongKey(event)) {
return;
}
console.log('add');
self.$contactList.hide();
self.$toggleAll.hide();
$(this).hide();
self.currentid = 'new';
self.tmpcontact = self.Contacts.addContact();
self.$rightContent.prepend(self.tmpcontact);
self.showActions(['back']);
});
$('.import').on('click keydown', function(event) {
// NOTE: Test if document title changes. If so there's a fix in
// https://github.com/owncloud/apps/pull/212#issuecomment-10516723
if(wrongKey(event)) {
return;
}
console.log('import');
self.hideActions();
});
$('.export,.import').on('click keydown', function(event) {
if(wrongKey(event)) {
return;
}
console.log('export');
$(this).next('ul').toggle('slow');
});
this.$header.on('click keydown', '.back', function(event) {
if(wrongKey(event)) {
return;
}
console.log('back');
self.closeContact(self.currentid);
self.$toggleAll.show();
self.showActions(['addcontact']);
});
this.$header.on('click keydown', '.delete', function(event) {
if(wrongKey(event)) {
return;
}
console.log('delete');
if(self.currentid) {
console.assert(utils.isUInt(self.currentid), 'self.currentid is not an integer');
self.Contacts.delayedDelete(self.currentid);
} else {
self.Contacts.delayedDelete(self.Contacts.getSelectedContacts());
}
self.showActions(['addcontact']);
});
this.$header.on('click keydown', '.download', function(event) {
if(wrongKey(event)) {
return;
}
console.log('download');
if(self.currentid) {
document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + self.currentid;
} else {
console.log('currentid is not set');
}
});
this.$header.on('click keydown', '.favorite', function(event) {
if(wrongKey(event)) {
return;
}
if(!utils.isUInt(self.currentid)) {
return;
}
var state = self.Groups.isFavorite(self.currentid);
console.log('Favorite?', this, state);
self.Groups.setAsFavorite(self.currentid, !state, function(jsondata) {
if(jsondata.status === 'success') {
if(state) {
self.$header.find('.favorite').switchClass('active', 'inactive');
} else {
self.$header.find('.favorite').switchClass('inactive', 'active');
}
} else {
OC.notify({message:t('contacts', jsondata.data.message)});
}
});
});
this.$contactList.on('mouseenter', 'td.email', function(event) {
if($(this).text().trim().length > 3) {
$(this).find('.mailto').css('display', 'inline-block'); //.fadeIn(100);
}
});
this.$contactList.on('mouseleave', 'td.email', function(event) {
$(this).find('.mailto').fadeOut(100);
});
$(document).on('keyup', function(event) {
if(event.target.nodeName.toUpperCase() != 'BODY') {
return;
}
// TODO: This should go in separate method
console.log(event.which + ' ' + event.target.nodeName);
/**
* To add:
* Shift-a: add addressbook
* u (85): hide/show leftcontent
* f (70): add field
*/
switch(event.which) {
case 13: // Enter?
console.log('Enter?');
if(!self.currentid && self.currentlistid) {
self.openContact(self.currentlistid);
}
break;
case 27: // Esc
if(self.$ninjahelp.is(':visible')) {
self.$ninjahelp.hide();
} else if(self.currentid) {
self.closeContact(self.currentid);
}
break;
case 46: // Delete
if(event.shiftKey) {
self.Contacts.delayedDelete(self.currentid);
}
break;
case 40: // down
case 74: // j
console.log('next');
if(!self.currentid && self.currentlistid) {
self.Contacts.contacts[self.currentlistid].next();
}
break;
case 65: // a
if(event.shiftKey) {
console.log('add group?');
break;
}
self.addContact();
break;
case 38: // up
case 75: // k
console.log('previous');
if(!self.currentid && self.currentlistid) {
self.Contacts.contacts[self.currentlistid].prev();
}
break;
case 34: // PageDown
case 78: // n
console.log('page down')
break;
case 79: // o
console.log('open contact?');
break;
case 33: // PageUp
case 80: // p
// prev addressbook
//OC.Contacts.Contacts.previousAddressbook();
break;
case 82: // r
console.log('refresh - what?');
break;
case 63: // ? German.
if(event.shiftKey) {
self.$ninjahelp.toggle('fast');
}
break;
case 171: // ? Danish
case 191: // ? Standard qwerty
self.$ninjahelp.toggle('fast').position({my: "center",at: "center",of: "#content"});
break;
}
});
$('#content > [title]').tipsy(); // find all with a title attribute and tipsy them
},
addGroup: function(cb) {
var self = this;
$('body').append('<div id="add_group_dialog"></div>');
if(!this.$addGroupTmpl) {
this.$addGroupTmpl = $('#addGroupTemplate');
}
var $dlg = this.$addGroupTmpl.octemplate();
$('#add_group_dialog').html($dlg).dialog({
modal: true,
closeOnEscape: true,
title: t('contacts', 'Add group'),
height: 'auto', width: 'auto',
buttons: {
'Ok':function() {
self.Groups.addGroup(
$dlg.find('input:text').val(),
function(response) {
if(typeof cb === 'function') {
cb(response);
} else {
if(response.status !== 'success') {
OC.notify({message: response.message});
}
}
});
$(this).dialog('close');
},
'Cancel':function() {
$(this).dialog('close');
return false;
}
},
close: function(event, ui) {
$(this).dialog('destroy').remove();
$('#add_group_dialog').remove();
},
open: function(event, ui) {
$dlg.find('input').focus();
},
});
},
setAllChecked: function(checked) {
var selector = checked ? 'input:checkbox:visible:not(checked)' : 'input:checkbox:visible:checked';
$.each(self.$contactList.find(selector), function() {
$(this).prop('checked', checked);
});
},
jumpToContact: function(id) {
this.$rightContent.scrollTop(this.Contacts.contactPos(id));
},
closeContact: function(id) {
if(typeof this.currentid === 'number') {
if(this.Contacts.findById(id).close()) {
this.$contactList.show();
this.jumpToContact(id);
}
} else if(this.currentid === 'new') {
this.tmpcontact.remove();
this.$contactList.show();
}
delete this.currentid;
this.$groups.find('optgroup,option:not([value="-1"])').remove();
},
openContact: function(id) {
console.log('Contacts.openContact', id);
if(this.currentid) {
this.closeContact(this.currentid);
}
this.currentid = parseInt(id);
this.setAllChecked(false);
this.$contactList.hide();
this.$toggleAll.hide();
var $contactelem = this.Contacts.showContact(this.currentid);
this.$rightContent.prepend($contactelem);
this.buildGroupSelect();
},
update: function() {
console.log('update');
},
uploadPhoto:function(filelist) {
var self = this;
if(!filelist) {
OC.notify({message:t('contacts','No files selected for upload.')});
return;
}
var file = filelist[0];
var target = $('#file_upload_target');
var form = $('#file_upload_form');
var totalSize=0;
if(file.size > $('#max_upload').val()){
OC.notify({
message: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') {
console.log('response', response);
self.editPhoto(self.currentid, response.data.tmp);
//alert('File: ' + file.tmp + ' ' + file.name + ' ' + file.mime);
} else {
OC.notify({message:response.data.message});
}
});
form.submit();
}
},
cloudPhotoSelected:function(id, path) {
var self = this;
console.log('cloudPhotoSelected, id', id)
$.getJSON(OC.filePath('contacts', 'ajax', 'oc_photo.php'),
{path: path, id: id},function(jsondata) {
if(jsondata.status == 'success') {
//alert(jsondata.data.page);
self.editPhoto(jsondata.data.id, jsondata.data.tmp)
$('#edit_photo_dialog_img').html(jsondata.data.page);
}
else{
OC.notify({message: jsondata.data.message});
}
});
},
editCurrentPhoto:function(id) {
var self = this;
$.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),
{id: id}, function(jsondata) {
if(jsondata.status == 'success') {
//alert(jsondata.data.page);
self.editPhoto(jsondata.data.id, jsondata.data.tmp)
$('#edit_photo_dialog_img').html(jsondata.data.page);
}
else{
OC.notify({message: jsondata.data.message});
}
});
},
editPhoto:function(id, tmpkey) {
console.log('editPhoto', id, tmpkey)
$('.tipsy').remove();
// Simple event handler, called from onChange and onSelect
// event handlers, as per the Jcrop invocation above
var showCoords = function(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);
};
var clearCoords = function() {
$('#coords input').val('');
};
var self = this;
if(!this.$cropBoxTmpl) {
this.$cropBoxTmpl = $('#cropBoxTemplate');
}
$('body').append('<div id="edit_photo_dialog"></div>');
var $dlg = this.$cropBoxTmpl.octemplate({id: id, tmpkey: tmpkey});
var cropphoto = new Image();
$(cropphoto).load(function () {
$(this).attr('id', 'cropbox');
$(this).prependTo($dlg).fadeIn();
$(this).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
});
$('#edit_photo_dialog').html($dlg).dialog({
modal: true,
closeOnEscape: true,
title: t('contacts', 'Edit profile picture'),
height: 'auto', width: 'auto',
buttons: {
'Ok':function() {
self.savePhoto($(this));
$(this).dialog('close');
},
'Cancel':function() { $(this).dialog('close'); }
},
close: function(event, ui) {
$(this).dialog('destroy').remove();
$('#edit_photo_dialog').remove();
},
open: function(event, ui) {
// Jcrop maybe?
}
});
}).error(function () {
OC.notify({message:t('contacts','Error loading profile picture.')});
}).attr('src', OC.linkTo('contacts', 'tmpphoto.php')+'?tmpkey='+tmpkey);
},
savePhoto:function($dlg) {
var form = $dlg.find('#cropform');
q = form.serialize();
console.log('savePhoto', q);
$.post(OC.filePath('contacts', 'ajax', 'savecrop.php'), q, function(response) {
var jsondata = $.parseJSON(response);
console.log('savePhoto, jsondata', typeof jsondata);
if(jsondata && jsondata.status === 'success') {
// load cropped photo.
$(document).trigger('status.contact.photoupdated', {
id: jsondata.data.id,
});
} else {
if(!jsondata) {
OC.notify({message:t('contacts', 'Network or server error. Please inform administrator.')});
} else {
OC.notify({message: jsondata.data.message});
}
}
});
},
addAddressbook:function(data, cb) {
$.post(OC.filePath('contacts', 'ajax', 'addressbook/add.php'), { name: data.name, description: data.description },
function(jsondata) {
if(jsondata.status == 'success') {
if(typeof cb === 'function') {
cb({
status:'success',
addressbook: jsondata.data.addressbook,
});
}
} else {
if(typeof cb === 'function') {
cb({status:'error'});
}
}
});
},
selectAddressbook:function(cb) {
var self = this;
var jqxhr = $.get(OC.filePath('contacts', 'templates', 'selectaddressbook.html'), function(data) {
$('body').append('<div id="addressbook_dialog"></div>');
var $dlg = $('#addressbook_dialog').html(data).octemplate({
nameplaceholder: t('contacts', 'Enter name'),
descplaceholder: t('contacts', 'Enter description'),
}).dialog({
modal: true, height: 'auto', width: 'auto',
title: t('contacts', 'Select addressbook'),
buttons: {
'Ok':function() {
aid = $(this).find('input:checked').val();
if(aid == 'new') {
var displayname = $(this).find('input.name').val();
var description = $(this).find('input.desc').val();
if(!displayname.trim()) {
OC.dialogs.alert(t('contacts', 'The address book name cannot be empty.'), t('contacts', 'Error'));
return false;
}
console.log('ID, name and desc', aid, displayname, description);
if(typeof cb === 'function') {
// TODO: Create addressbook
var data = {name:displayname, description:description};
self.addAddressbook(data, function(data) {
if(data.status === 'success') {
cb({
status:'success',
addressbook:data.addressbook,
});
} else {
cb({status:'error'});
}
});
}
$(this).dialog('close');
} else {
console.log('aid ' + aid);
if(typeof cb === 'function') {
cb({
status:'success',
addressbook:self.Contacts.addressbooks[parseInt(aid)],
});
}
$(this).dialog('close');
}
},
'Cancel':function() {
$(this).dialog('close');
}
},
close: function(event, ui) {
$(this).dialog('destroy').remove();
$('#addressbook_dialog').remove();
},
open: function(event, ui) {
console.log('open', $(this));
var $lastrow = $(this).find('tr.new');
$.each(self.Contacts.addressbooks, function(i, book) {
console.log('book', i, book);
if(book.owner === OC.currentUser
|| (book.permissions & OC.PERMISSION_UPDATE
|| book.permissions & OC.PERMISSION_CREATE
|| book.permissions & OC.PERMISSION_DELETE)) {
var row = '<tr><td><input id="book_{id}" name="book" type="radio" value="{id}"</td>'
+ '<td><label for="book_{id}">{displayname}</label></td>'
+ '<td>{description}</td></tr>'
var $row = $(row).octemplate({
id:book.id,
displayname:book.displayname,
description:book.description
});
$lastrow.before($row);
}
});
$(this).find('input[type="radio"]').first().prop('checked', true);
$lastrow.find('input.name,input.desc').on('focus', function(e) {
$lastrow.find('input[type="radio"]').prop('checked', true);
});
},
});
}).error(function() {
OC.notify({message: t('contacts', 'Network or server error. Please inform administrator.')});
});
},
};
(function( $ ) {
/**
* Object Template
* Inspired by micro templating done by e.g. underscore.js
*/
var Template = {
init: function(options, elem) {
// Mix in the passed in options with the default options
this.options = $.extend({},this.options,options);
// Save the element reference, both as a jQuery
// reference and a normal reference
this.elem = elem;
this.$elem = $(elem);
var _html = this._build(this.options);
//console.log('html', this.$elem.html());
return $(_html);
},
// From stackoverflow.com/questions/1408289/best-way-to-do-variable-interpolation-in-javascript
_build: function(o){
var data = this.$elem.attr('type') === 'text/template'
? this.$elem.html() : this.$elem.get(0).outerHTML;
return data.replace(/{([^{}]*)}/g,
function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
}
);
},
options: {
},
};
$.fn.octemplate = function(options) {
if ( this.length ) {
var _template = Object.create(Template);
return _template.init(options, this);
}
};
})( jQuery );
$(document).ready(function() {
OC.Contacts.init(id);
});

View File

@ -1,2267 +1,1435 @@
function ucwords (str) {
return (str + '').replace(/^([a-z])|\s+([a-z])/g, function ($1) {
return $1.toUpperCase();
});
}
OC.Contacts = OC.Contacts || {};
String.prototype.strip_tags = function(){
tags = this;
stripped = tags.replace(/<(.|\n)*?>/g, '');
return stripped;
};
OC.Contacts={
(function($) {
/**
* Arguments:
* message: The text message to show.
* timeout: The timeout in seconds before the notification disappears. Default 10.
* timeouthandler: A function to run on timeout.
* clickhandler: A function to run on click. If a timeouthandler is given it will be cancelled.
* data: An object that will be passed as argument to the timeouthandler and clickhandler functions.
* cancel: If set cancel all ongoing timer events and hide the notification.
*/
notify:function(params) {
var self = this;
if(!self.notifier) {
self.notifier = $('#notification');
}
if(params.cancel) {
self.notifier.off('click');
for(var id in self.notifier.data()) {
if($.isNumeric(id)) {
clearTimeout(parseInt(id));
}
}
self.notifier.text('').fadeOut().removeData();
* An item which binds the appropriate html and event handlers
* @param parent the parent ContactList
* @param id The integer contact id.
* @param access An access object containing and 'owner' string variable and an integer 'permissions' variable.
* @param data the data used to populate the contact
* @param listtemplate the jquery object used to render the contact list item
* @param fulltemplate the jquery object used to render the entire contact
* @param detailtemplates A map of jquery objects used to render the contact parts e.g. EMAIL, TEL etc.
*/
var Contact = function(parent, id, access, data, listtemplate, fulltemplate, detailtemplates) {
//console.log('contact:', id, access); //parent, id, data, listtemplate, fulltemplate);
this.parent = parent,
this.id = id,
this.access = access,
this.data = data,
this.$listTemplate = listtemplate,
this.$fullTemplate = fulltemplate;
this.detailTemplates = detailtemplates;
this.multi_properties = ['EMAIL', 'TEL', 'IMPP', 'ADR', 'URL'];
}
Contact.prototype.setAsSaving = function(obj, state) {
if(!obj) {
return;
}
self.notifier.text(params.message);
self.notifier.fadeIn();
self.notifier.on('click', function() { $(this).fadeOut();});
var timer = setTimeout(function() {
if(!self || !self.notifier) {
var self = OC.Contacts;
self.notifier = $('#notification');
}
self.notifier.fadeOut();
if(params.timeouthandler && $.isFunction(params.timeouthandler)) {
params.timeouthandler(self.notifier.data(dataid));
self.notifier.off('click');
self.notifier.removeData(dataid);
}
}, params.timeout && $.isNumeric(params.timeout) ? parseInt(params.timeout)*1000 : 10000);
var dataid = timer.toString();
if(params.data) {
self.notifier.data(dataid, params.data);
}
if(params.clickhandler && $.isFunction(params.clickhandler)) {
self.notifier.on('click', function() {
if(!self || !self.notifier) {
var self = OC.Contacts;
self.notifier = $(this);
}
clearTimeout(timer);
self.notifier.off('click');
params.clickhandler(self.notifier.data(dataid));
self.notifier.removeData(dataid);
});
}
},
notImplemented:function() {
OC.dialogs.alert(t('contacts', 'Sorry, this functionality has not been implemented yet'), t('contacts', 'Not implemented'));
},
searchOSM:function(obj) {
var adr = OC.Contacts.propertyContainerFor(obj).find('.adr').val();
if(adr == undefined) {
OC.dialogs.alert(t('contacts', 'Couldn\'t get a valid address.'), t('contacts', 'Error'));
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();
}
adrstr = encodeURIComponent(adrstr);
var uri = 'http://open.mapquestapi.com/nominatim/v1/search.php?q=' + adrstr + '&limit=10&addressdetails=1&polygon=1&zoom=';
var newWindow = window.open(uri,'_blank');
newWindow.focus();
},
mailTo:function(obj) {
var adr = OC.Contacts.propertyContainerFor($(obj)).find('input[type="email"]').val().trim();
if(adr == '') {
OC.dialogs.alert(t('contacts', 'Please enter an email address.'), t('contacts', 'Error'));
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');
},
loading:function(obj, state) {
if(state) {
$(obj).prop('disabled', state);
$(obj).toggleClass('loading', state);
/*if(state) {
$(obj).addClass('loading');
} else {
$(obj).removeClass('loading');
}
},
showCardDAVUrl:function(username, bookname){
$('#carddav_url').val(totalurl + '/' + username + '/' + decodeURIComponent(bookname));
$('#carddav_url').show();
$('#carddav_url_close').show();
},
loadListHandlers:function() {
$('.propertylist li a.delete').unbind('click');
$('.propertylist li a.delete').unbind('keydown');
var deleteItem = function(obj) {
obj.tipsy('hide');
OC.Contacts.Card.deleteProperty(obj, 'list');
}
$('.propertylist li a.delete, .addresscard .delete').click(function() { deleteItem($(this)) });
$('.propertylist li a.delete, .addresscard .delete').keydown(function() { deleteItem($(this)) });
$('.addresscard .globe').click(function() { $(this).tipsy('hide');OC.Contacts.searchOSM(this); });
$('.addresscard .globe').keydown(function() { $(this).tipsy('hide');OC.Contacts.searchOSM(this); });
$('.addresscard .edit').click(function() { $(this).tipsy('hide');OC.Contacts.Card.editAddress(this, false); });
$('.addresscard .edit').keydown(function() { $(this).tipsy('hide');OC.Contacts.Card.editAddress(this, false); });
$('.addresscard,.propertylist li,.propertycontainer').hover(
function () {
$(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 1.0 }, 200, function() {});
},
function () {
$(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 0.1 }, 200, function() {});
}
);
},
loadHandlers:function() {
var deleteItem = function(obj) {
obj.tipsy('hide');
OC.Contacts.Card.deleteProperty(obj, 'single');
}
var goToUrl = function(obj) {
var url = OC.Contacts.propertyContainerFor(obj).find('#url').val().toString();
// Check if the url is valid
if(new RegExp("[a-zA-Z0-9]+://([a-zA-Z0-9_]+:[a-zA-Z0-9_]+@)?([a-zA-Z0-9.-]+\\.[A-Za-z]{2,4})(:[0-9]+)?(/.*)?").test(url)) {
var newWindow = window.open(url,'_blank');
newWindow.focus();
}
}
}*/
}
$('#identityprops a.delete').click( function() { deleteItem($(this)) });
$('#identityprops a.delete').keydown( function() { deleteItem($(this)) });
$('#categories_value a.edit').click( function() { $(this).tipsy('hide');OCCategories.edit(); } );
$('#categories_value a.edit').keydown( function() { $(this).tipsy('hide');OCCategories.edit(); } );
$('#url_value a.globe').click( function() { $(this).tipsy('hide');goToUrl($(this)); } );
$('#url_value a.globe').keydown( function() { $(this).tipsy('hide');goToUrl($(this)); } );
$('#fn_select').combobox({
'id': 'fn',
'name': 'value',
'classes': ['contacts_property', 'nonempty', 'huge', 'tip', 'float'],
'attributes': {'placeholder': t('contacts', 'Enter name')},
'title': t('contacts', 'Format custom, Short name, Full name, Reverse or Reverse with comma')});
$('#bday').datepicker({
dateFormat : 'dd-mm-yy'
});
// Style phone types
$('#phonelist').find('select.contacts_property').multiselect({
noneSelectedText: t('contacts', 'Select type'),
header: false,
selectedList: 4,
classes: 'typelist'
});
$('#edit_name').click(function(){OC.Contacts.Card.editName()});
$('#edit_name').keydown(function(){OC.Contacts.Card.editName()});
$('#phototools li a').click(function() {
$(this).tipsy('hide');
});
$('#contacts_details_photo_wrapper').hover(
function () {
$('#phototools').slideDown(200);
},
function () {
$('#phototools').slideUp(200);
}
);
$('#phototools').hover(
function () {
$(this).removeClass('transparent');
},
function () {
$(this).addClass('transparent');
}
);
$('#phototools .upload').click(function() {
$('#file_upload_start').trigger('click');
});
$('#phototools .cloud').click(function() {
OC.dialogs.filepicker(t('contacts', 'Select photo'), OC.Contacts.Card.cloudPhotoSelected, false, 'image', true);
});
/* 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() {
OC.Contacts.Card.savePhoto(this);
$(this).dialog('close');
}
},
{
text: "Cancel",
click: function() { $(this).dialog('close'); }
}
] );
// Name has changed. Update it and reorder.
$('#fn').change(function(){
var name = $('#fn').val().strip_tags();
var item = $('.contacts li[data-id="'+OC.Contacts.Card.id+'"]').detach();
$(item).find('a').html(name);
OC.Contacts.Card.fn = name;
OC.Contacts.Contacts.insertContact({contact:item});
OC.Contacts.Contacts.scrollTo(OC.Contacts.Card.id);
});
$('#contacts_deletecard').click( function() { OC.Contacts.Card.delayedDelete();return false;} );
$('#contacts_deletecard').keydown( function(event) {
if(event.which == 13 || event.which == 32) {
OC.Contacts.Card.delayedDelete();
}
return false;
});
$('#contacts_downloadcard').click( function() { OC.Contacts.Card.doExport();return false;} );
$('#contacts_downloadcard').keydown( function(event) {
if(event.which == 13 || event.which == 32) {
OC.Contacts.Card.doExport();
}
return false;
});
// Profile picture upload handling
// New profile picture selected
$('#file_upload_start').change(function(){
OC.Contacts.Card.uploadPhoto(this.files);
});
$('#contacts_details_photo_wrapper').bind('dragover',function(event){
$(event.target).addClass('droppable');
event.stopPropagation();
event.preventDefault();
});
$('#contacts_details_photo_wrapper').bind('dragleave',function(event){
$(event.target).removeClass('droppable');
});
$('#contacts_details_photo_wrapper').bind('drop',function(event){
event.stopPropagation();
event.preventDefault();
$(event.target).removeClass('droppable');
$.fileUpload(event.originalEvent.dataTransfer.files);
});
$('#categories').multiple_autocomplete({source: categories});
$('#contacts_deletecard').tipsy({gravity: 'ne'});
$('#contacts_downloadcard').tipsy({gravity: 'ne'});
$('#contacts_propertymenu_button').tipsy();
$('#bottomcontrols button').tipsy({gravity: 'sw'});
$('body').click(function(e){
if(!$(e.target).is('#contacts_propertymenu_button')) {
$('#contacts_propertymenu_dropdown').hide();
}
});
function propertyMenu(){
var menu = $('#contacts_propertymenu_dropdown');
if(menu.is(':hidden')) {
menu.show();
menu.find('li').first().focus();
} else {
menu.hide();
}
}
$('#contacts_propertymenu_button').click(propertyMenu);
$('#contacts_propertymenu_button').keydown(propertyMenu);
function propertyMenuItem(){
var type = $(this).data('type');
OC.Contacts.Card.addProperty(type);
$('#contacts_propertymenu_dropdown').hide();
}
$('#contacts_propertymenu_dropdown a').click(propertyMenuItem);
$('#contacts_propertymenu_dropdown a').keydown(propertyMenuItem);
},
Card:{
update:function(params) { // params {cid:int, aid:int}
if(!params) { params = {}; }
$('#contacts li,#contacts h3').removeClass('active');
//console.log('Card, cid: ' + params.cid + ' aid: ' + params.aid);
var newid, bookid, firstitem;
if(!parseInt(params.cid) && !parseInt(params.aid)) {
firstitem = $('#contacts ul').find('li:first-child');
if(firstitem.length > 0) {
if(firstitem.length > 1) {
firstitem = firstitem.first();
}
newid = parseInt(firstitem.data('id'));
bookid = parseInt(firstitem.data('bookid'));
}
} else if(!parseInt(params.cid) && parseInt(params.aid)) {
bookid = parseInt(params.aid);
newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id'));
} else if(parseInt(params.cid) && !parseInt(params.aid)) {
newid = parseInt(params.cid);
var listitem = OC.Contacts.Contacts.getContact(newid); //$('#contacts li[data-id="'+newid+'"]');
//console.log('Is contact in list? ' + listitem.length);
if(listitem.length) {
//bookid = parseInt($('#contacts li[data-id="'+newid+'"]').data('bookid'));
bookid = parseInt(OC.Contacts.Contacts.getContact(newid).data('bookid'));
} else { // contact isn't in list yet.
bookid = 'unknown';
}
} else {
newid = parseInt(params.cid);
bookid = parseInt(params.aid);
}
if(!bookid || !newid) {
bookid = parseInt($('#contacts h3').first().data('id'));
newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id'));
}
//console.log('newid: ' + newid + ' bookid: ' +bookid);
var localLoadContact = function(newid, bookid) {
if($('.contacts li').length > 0) {
$.getJSON(OC.filePath('contacts', 'ajax', 'contact/details.php'),{'id':newid},function(jsondata){
if(jsondata.status == 'success'){
if(bookid == 'unknown') {
bookid = jsondata.data.addressbookid;
var contact = OC.Contacts.Contacts.insertContact({
contactlist:$('#contacts ul[data-id="'+bookid+'"]'),
data:jsondata.data
});
}
$('#contacts li[data-id="'+newid+'"],#contacts h3[data-id="'+bookid+'"]').addClass('active');
$('#contacts ul[data-id="'+bookid+'"]').slideDown(300);
OC.Contacts.Card.loadContact(jsondata.data, bookid);
OC.Contacts.Contacts.scrollTo(newid);
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
}
}
// Make sure proper DOM is loaded.
if(newid) {
//console.log('Loading card DOM');
localLoadContact(newid, bookid);
$('#firstrun').hide();
$('#card').show();
} else if(!newid) {
//console.log('Loading intro');
// show intro page
$('#firstrun').show();
$('#card').hide();
}
$('#contacts h3[data-id="'+bookid+'"]').addClass('active');
},
setEnabled:function(enabled) {
//console.log('setEnabled', enabled);
$('.contacts_property,.action').each(function () {
$(this).prop('disabled', !enabled);
OC.Contacts.Card.enabled = enabled;
});
},
doExport:function() {
document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + this.id;
},
editNew:function(){ // add a new contact
var book = $('#contacts h3.active');
var permissions = parseInt(book.data('permissions'));
if(permissions == 0
|| permissions & OC.PERMISSION_UPDATE
|| permissions & OC.PERMISSION_DELETE) {
with(this) {
delete id; delete fn; delete fullname; delete givname; delete famname;
delete addname; delete honpre; delete honsuf;
}
this.bookid = book.data('id');
OC.Contacts.Card.add(';;;;;', '', '', true);
} else {
OC.dialogs.alert(t('contacts', 'You do not have permission to add contacts to ')
+ book.text() + '. ' + t('contacts', 'Please select one of your own address books.'), t('contacts', 'Permission error'));
}
return false;
},
add:function(n, fn, aid, isnew) { // add a new contact
//console.log('Adding ' + fn);
$('#firstrun').hide();
$('#card').show();
aid = aid?aid:$('#contacts h3.active').first().data('id');
$.post(OC.filePath('contacts', 'ajax', 'contact/add.php'), { n: n, fn: fn, aid: aid, isnew: isnew },
function(jsondata) {
if (jsondata.status == 'success'){
$('#rightcontent').data('id',jsondata.data.id);
var id = jsondata.data.id;
var aid = jsondata.data.aid;
$.getJSON(OC.filePath('contacts', 'ajax', 'contact/details.php'),{'id':id},function(jsondata){
if(jsondata.status == 'success'){
OC.Contacts.Card.loadContact(jsondata.data, aid);
var item = OC.Contacts.Contacts.insertContact({data:jsondata.data});
$('#contacts li').removeClass('active');
item.addClass('active');
if(isnew) { // add some default properties
OC.Contacts.Card.addProperty('EMAIL');
OC.Contacts.Card.addProperty('TEL');
$('#fn').focus();
}
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
$('#contact_identity').show();
$('#actionbar').show();
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
},
delayedDelete:function() {
$('#contacts_deletecard').tipsy('hide');
var newid = '', bookid;
var curlistitem = OC.Contacts.Contacts.getContact(this.id);
curlistitem.removeClass('active');
var newlistitem = curlistitem.prev('li');
if(!newlistitem) {
newlistitem = curlistitem.next('li');
}
curlistitem.detach();
if($(newlistitem).is('li')) {
newid = newlistitem.data('id');
bookid = newlistitem.data('bookid');
}
$('#rightcontent').data('id', newid);
OC.Contacts.Contacts.deletionQueue.push(parseInt(this.id));
if(!window.onbeforeunload) {
window.onbeforeunload = OC.Contacts.Contacts.warnNotDeleted;
}
with(this) {
delete id; delete fn; delete fullname; delete shortname; delete famname;
delete givname; delete addname; delete honpre; delete honsuf; delete data;
}
if($('.contacts li').length > 0) {
OC.Contacts.Card.update({cid:newid, aid:bookid});
} else {
// load intro page
$('#firstrun').show();
$('#card').hide();
}
OC.Contacts.notify({
data:curlistitem,
message:t('contacts','Click to undo deletion of "') + curlistitem.find('a').text() + '"',
//timeout:5,
timeouthandler:function(contact) {
//console.log('timeout');
OC.Contacts.Card.doDelete(contact.data('id'), true, function(res) {
if(!res) {
OC.Contacts.Contacts.insertContact({contact:contact});
} else {
delete contact;
}
});
},
clickhandler:function(contact) {
OC.Contacts.Contacts.insertContact({contact:contact});
OC.Contacts.notify({message:t('contacts', 'Cancelled deletion of: "') + curlistitem.find('a').text() + '"'});
window.onbeforeunload = null;
}
});
},
doDelete:function(id, removeFromQueue, cb) {
var updateQueue = function(id, remove) {
if(removeFromQueue) {
OC.Contacts.Contacts.deletionQueue.splice(OC.Contacts.Contacts.deletionQueue.indexOf(parseInt(id)), 1);
}
if(OC.Contacts.Contacts.deletionQueue.length == 0) {
window.onbeforeunload = null;
}
}
if(OC.Contacts.Contacts.deletionQueue.indexOf(parseInt(id)) == -1 && removeFromQueue) {
//console.log('returning');
updateQueue(id, removeFromQueue);
if(typeof cb == 'function') {
cb(true);
}
return;
}
$.post(OC.filePath('contacts', 'ajax', 'contact/delete.php'), {'id':id},function(jsondata) {
if(jsondata.status == 'error'){
OC.Contacts.notify({message:jsondata.data.message});
if(typeof cb == 'function') {
cb(false);
}
}
updateQueue(id, removeFromQueue);
});
if(typeof cb == 'function') {
cb(true);
}
},
loadContact:function(jsondata, bookid){
this.data = jsondata;
this.id = this.data.id;
this.bookid = bookid;
$('#rightcontent').data('id',this.id);
this.populateNameFields();
this.loadPhoto();
this.loadMails();
this.loadPhones();
this.loadIMs();
this.loadAddresses();
this.loadSingleProperties();
OC.Contacts.loadListHandlers();
var note = $('#note');
if(this.data.NOTE) {
note.data('checksum', this.data.NOTE[0]['checksum']);
var textarea = note.find('textarea');
var txt = this.data.NOTE[0]['value'];
var nheight = txt.split('\n').length > 4 ? txt.split('\n').length+2 : 5;
textarea.css('min-height', nheight+'em');
textarea.attr('rows', nheight);
textarea.val(txt);
$('#contact_note').show();
textarea.expandingTextarea();
$('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().hide();
} else {
note.removeData('checksum');
note.find('textarea').val('');
$('#contact_note').hide();
$('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().show();
}
var permissions = OC.Contacts.Card.permissions = parseInt(this.data.permissions);
//console.log('permissions', permissions);
this.setEnabled(permissions == 0
|| permissions & OC.PERMISSION_UPDATE
|| permissions & OC.PERMISSION_DELETE);
},
loadSingleProperties:function() {
var props = ['BDAY', 'NICKNAME', 'ORG', 'URL', 'CATEGORIES'];
// Clear all elements
$('#ident .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) {
var propname = props[prop];
if(this.data[propname] != undefined) {
$('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().hide();
var property = this.data[propname][0];
var value = property['value'], checksum = property['checksum'];
if(propname == 'BDAY') {
var val = $.datepicker.parseDate('yy-mm-dd', value.substring(0, 10));
value = $.datepicker.formatDate('dd-mm-yy', val);
}
var identcontainer = $('#contact_identity');
identcontainer.find('#'+propname.toLowerCase()).val(value);
identcontainer.find('#'+propname.toLowerCase()+'_value').data('checksum', checksum);
identcontainer.find('#'+propname.toLowerCase()+'_label').show();
identcontainer.find('#'+propname.toLowerCase()+'_value').show();
} else {
$('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().show();
}
}
},
populateNameFields:function() {
var props = ['FN', 'N'];
// Clear all elements
$('#ident .propertycontainer').each(function(){
if(props.indexOf($(this).data('element')) > -1) {
$(this).data('checksum', '');
$(this).find('input').val('');
}
});
with(this) {
delete fn; delete fullname; delete givname; delete famname;
delete addname; delete honpre; delete honsuf;
}
if(this.data.FN) {
this.fn = this.data.FN[0]['value'];
}
else {
this.fn = '';
}
if(this.data.N == undefined) {
narray = [this.fn,'','','','']; // Checking for non-existing 'N' property :-P
} 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').val(narray.join(';'));
$('#fn_select option').remove();
var names = [this.fn, this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname];
if(this.data.ORG) {
names[names.length]=this.data.ORG[0].value;
}
$.each(names, function(key, value) {
$('#fn_select')
.append($('<option></option>')
.text(value));
});
$('#fn_select').combobox('value', this.fn);
$('#contact_identity').find('*[data-element="N"]').data('checksum', this.data.N[0]['checksum']);
if(this.data.FN) {
$('#contact_identity').find('*[data-element="FN"]').data('checksum', this.data.FN[0]['checksum']);
}
$('#contact_identity').show();
},
hasCategory:function(category) {
if(this.data.CATEGORIES) {
var categories = this.data.CATEGORIES[0]['value'].split(/,\s*/);
for(var c in categories) {
var cat = this.data.CATEGORIES[0]['value'][c];
if(typeof cat === 'string' && (cat.toUpperCase() === category.toUpperCase())) {
return true;
}
}
}
return false;
},
categoriesChanged:function(newcategories) { // Categories added/deleted.
categories = $.map(newcategories, function(v) {return v;});
$('#categories').multiple_autocomplete('option', 'source', categories);
var categorylist = $('#categories_value').find('input');
$.getJSON(OC.filePath('contacts', 'ajax', 'categories/categoriesfor.php'),{'id':OC.Contacts.Card.id},function(jsondata){
if(jsondata.status == 'success'){
$('#categories_value').data('checksum', jsondata.data.checksum);
categorylist.val(jsondata.data.value);
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
},
savePropertyInternal:function(name, fields, oldchecksum, checksum) {
// TODO: Add functionality for new fields.
//console.log('savePropertyInternal: ' + name + ', fields: ' + fields + 'checksum: ' + checksum);
//console.log('savePropertyInternal: ' + this.data[name]);
var multivalue = ['CATEGORIES'];
var params = {};
var value = multivalue.indexOf(name) != -1 ? new Array() : undefined;
jQuery.each(fields, function(i, field){
//.substring(11,'parameters[TYPE][]'.indexOf(']'))
if(field.name.substring(0, 5) === 'value') {
if(multivalue.indexOf(name) != -1) {
value.push(field.value);
} else {
value = field.value;
}
} else if(field.name.substring(0, 10) === 'parameters') {
var p = field.name.substring(11,'parameters[TYPE][]'.indexOf(']'));
if(!(p in params)) {
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) {
if(!$(obj).hasClass('contacts_property')) {
return false;
}
if($(obj).hasClass('nonempty') && $(obj).val().trim() == '') {
OC.dialogs.alert(t('contacts', 'This property has to be non-empty.'), t('contacts', 'Error'));
return false;
}
container = $(obj).parents('.propertycontainer').first(); // get the parent holding the metadata.
OC.Contacts.loading(obj, true);
var checksum = container.data('checksum');
var name = container.data('element');
var fields = container.find('input.contacts_property,select.contacts_property').serializeArray();
switch(name) {
case 'FN':
var nempty = true;
for(var i in OC.Contacts.Card.data.N[0]['value']) {
if(OC.Contacts.Card.data.N[0]['value'][i] != '') {
nempty = false;
break;
}
}
if(nempty) {
$('#n').val(fields[0].value + ';;;;');
OC.Contacts.Card.data.N[0]['value'] = Array(fields[0].value, '', '', '', '');
setTimeout(function() {OC.Contacts.Card.saveProperty($('#n'))}, 500);
}
break;
}
var q = container.find('input.contacts_property,select.contacts_property,textarea.contacts_property').serialize();
if(q == '' || q == undefined) {
OC.dialogs.alert(t('contacts', 'Couldn\'t serialize elements.'), t('contacts', 'Error'));
OC.Contacts.loading(obj, false);
return false;
}
q = q + '&id=' + this.id + '&name=' + name;
if(checksum != undefined && checksum != '') { // save
q = q + '&checksum=' + checksum;
//console.log('Saving: ' + q);
$(obj).attr('disabled', 'disabled');
$.post(OC.filePath('contacts', 'ajax', 'contact/saveproperty.php'),q,function(jsondata){
if(!jsondata) {
OC.dialogs.alert(t('contacts', 'Unknown error. Please check logs.'), t('contacts', 'Error'));
OC.Contacts.loading(obj, false);
$(obj).removeAttr('disabled');
OC.Contacts.Card.update({cid:OC.Contacts.Card.id});
return false;
}
if(jsondata.status == 'success'){
container.data('checksum', jsondata.data.checksum);
OC.Contacts.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum);
OC.Contacts.loading(obj, false);
$(obj).removeAttr('disabled');
return true;
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
OC.Contacts.loading(obj, false);
$(obj).removeAttr('disabled');
OC.Contacts.Card.update({cid:OC.Contacts.Card.id});
return false;
}
},'json');
} else { // add
//console.log('Adding: ' + q);
$(obj).attr('disabled', 'disabled');
$.post(OC.filePath('contacts', 'ajax', 'contact/addproperty.php'),q,function(jsondata){
if(jsondata.status == 'success'){
container.data('checksum', jsondata.data.checksum);
// TODO: savePropertyInternal doesn't know about new fields
//OC.Contacts.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum);
OC.Contacts.loading(obj, false);
$(obj).removeAttr('disabled');
return true;
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
OC.Contacts.loading(obj, false);
$(obj).removeAttr('disabled');
OC.Contacts.Card.update({cid:OC.Contacts.Card.id});
return false;
}
},'json');
}
},
addProperty:function(type) {
if(!this.enabled) {
return;
}
switch (type) {
case 'NOTE':
$('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide();
$('#note').find('textarea').expandingTextarea().show().focus();
$('#contact_note').show();
break;
case 'EMAIL':
if($('#emaillist>li').length == 1) {
$('#emails').show();
}
OC.Contacts.Card.addMail();
break;
case 'TEL':
if($('#phonelist>li').length == 1) {
$('#phones').show();
}
OC.Contacts.Card.addPhone();
break;
case 'IMPP':
if($('#imlist>li').length == 1) {
$('#ims').show();
}
OC.Contacts.Card.addIM();
break;
case 'ADR':
if($('addresses>dl').length == 1) {
$('#addresses').show();
}
OC.Contacts.Card.editAddress('new', true);
break;
case 'NICKNAME':
case 'URL':
case 'ORG':
case 'BDAY':
case 'CATEGORIES':
$('dl dt[data-element="'+type+'"],dd[data-element="'+type+'"]').show();
$('dd[data-element="'+type+'"]').find('input').focus();
$('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide();
break;
}
},
deleteProperty:function(obj, type) {
//console.log('deleteProperty');
if(!this.enabled) {
return;
}
OC.Contacts.loading(obj, true);
var checksum = OC.Contacts.checksumFor(obj);
if(checksum) {
$.post(OC.filePath('contacts', 'ajax', 'contact/deleteproperty.php'),{'id': this.id, 'checksum': checksum },function(jsondata){
if(jsondata.status == 'success'){
if(type == 'list') {
OC.Contacts.propertyContainerFor(obj).remove();
} else if(type == 'single') {
var proptype = OC.Contacts.propertyTypeFor(obj);
OC.Contacts.Card.data[proptype] = null;
var othertypes = ['NOTE', 'PHOTO'];
if(othertypes.indexOf(proptype) != -1) {
OC.Contacts.propertyContainerFor(obj).data('checksum', '');
if(proptype == 'PHOTO') {
OC.Contacts.Contacts.refreshThumbnail(OC.Contacts.Card.id);
OC.Contacts.Card.loadPhoto();
} else if(proptype == 'NOTE') {
$('#note').find('textarea').val('');
$('#contact_note').hide();
OC.Contacts.propertyContainerFor(obj).hide();
}
} else {
$('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide();
$('dl dd[data-element="'+proptype+'"]').data('checksum', '').find('input').val('');
}
$('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show();
OC.Contacts.loading(obj, false);
} else {
OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error'));
OC.Contacts.loading(obj, false);
}
}
else{
OC.Contacts.loading(obj, false);
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
} else { // Property hasn't been saved so there's nothing to delete.
if(type == 'list') {
OC.Contacts.propertyContainerFor(obj).remove();
} else if(type == 'single') {
var proptype = OC.Contacts.propertyTypeFor(obj);
$('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide();
$('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show();
OC.Contacts.loading(obj, false);
} else {
OC.dialogs.alert(t('contacts', '\'deleteProperty\' called without type argument. Please report at bugs.owncloud.org'), t('contacts', 'Error'));
}
}
},
editName:function() {
if(!this.enabled) {
return;
}
var params = {id: this.id};
/* Initialize the name edit dialog */
if($('#edit_name_dialog').dialog('isOpen') == true) {
$('#edit_name_dialog').dialog('moveToTop');
} else {
$.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata) {
if(jsondata.status == 'success') {
$('body').append('<div id="name_dialog"></div>');
$('#name_dialog').html(jsondata.data.page).find('#edit_name_dialog' ).dialog({
modal: true,
closeOnEscape: true,
title: t('contacts', 'Edit name'),
height: 'auto', width: 'auto',
buttons: {
'Ok':function() {
OC.Contacts.Card.saveName(this);
$(this).dialog('close');
},
'Cancel':function() { $(this).dialog('close'); }
},
close: function(event, ui) {
$(this).dialog('destroy').remove();
$('#name_dialog').remove();
},
open: function(event, ui) {
// load 'N' property - maybe :-P
}
});
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
}
},
saveName:function(dlg) {
if(!this.enabled) {
return;
}
//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];
this.fullname = '';
$('#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];
}
$('#fn_select option').remove();
//$('#fn_select').combobox('value', this.fn);
var tmp = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname];
var names = new Array();
for(var name in tmp) {
if(names.indexOf(tmp[name]) == -1) {
names.push(tmp[name]);
}
}
$.each(names, function(key, value) {
$('#fn_select')
.append($('<option></option>')
.text(value));
});
if(this.id == '') {
var aid = $(dlg).find('#aid').val();
OC.Contacts.Card.add(n.join(';'), $('#short').text(), aid);
} else {
OC.Contacts.Card.saveProperty($('#n'));
}
},
loadAddresses:function() {
$('#addresses').hide();
$('#addresses dl.propertycontainer').remove();
var addresscontainer = $('#addresses');
for(var adr in this.data.ADR) {
addresscontainer.find('dl').first().clone().insertAfter($('#addresses dl').last()).show();
addresscontainer.find('dl').last().removeClass('template').addClass('propertycontainer');
addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']);
var adrarray = this.data.ADR[adr]['value'];
var adrtxt = '';
if(adrarray[0] && adrarray[0].length > 0) {
adrtxt = adrtxt + '<li>' + adrarray[0].strip_tags() + '</li>';
}
if(adrarray[1] && adrarray[1].length > 0) {
adrtxt = adrtxt + '<li>' + adrarray[1].strip_tags() + '</li>';
}
if(adrarray[2] && adrarray[2].length > 0) {
adrtxt = adrtxt + '<li>' + adrarray[2].strip_tags() + '</li>';
}
if((3 in adrarray && 5 in adrarray) && adrarray[3].length > 0 || adrarray[5].length > 0) {
adrtxt = adrtxt + '<li>' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + '</li>';
}
if(adrarray[4] && adrarray[4].length > 0) {
adrtxt = adrtxt + '<li>' + adrarray[4].strip_tags() + '</li>';
}
if(adrarray[6] && adrarray[6].length > 0) {
adrtxt = adrtxt + '<li>' + adrarray[6].strip_tags() + '</li>';
}
addresscontainer.find('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]);
}
}
addresscontainer.find('dl').last().find('.adr_type_label').text(types.join('/'));
addresscontainer.find('dl').last().find('.adr_type').val(ttypes.join(','));
addresscontainer.find('dl').last().find('.adr').val(adrarray.join(';'));
addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']);
}
if(addresscontainer.find('dl').length > 1) {
$('#addresses').show();
}
return false;
},
editAddress:function(obj, isnew){
if(!this.enabled) {
return;
}
var container = undefined;
var params = {id: this.id};
if(obj === 'new') {
isnew = true;
$('#addresses dl').first().clone(true).insertAfter($('#addresses dl').last()).show();
container = $('#addresses dl').last();
container.removeClass('template').addClass('propertycontainer');
} else {
params['checksum'] = OC.Contacts.checksumFor(obj);
}
/* Initialize the address edit dialog */
if($('#edit_address_dialog').dialog('isOpen') == true){
$('#edit_address_dialog').dialog('moveToTop');
}else{
$.getJSON(OC.filePath('contacts', 'ajax', 'editaddress.php'),params,function(jsondata){
if(jsondata.status == 'success'){
$('body').append('<div id="address_dialog"></div>');
$('#address_dialog').html(jsondata.data.page).find('#edit_address_dialog' ).dialog({
height: 'auto', width: 'auto',
buttons: {
'Ok':function() {
if(isnew) {
OC.Contacts.Card.saveAddress(this, $('#addresses dl:last-child').find('input').first(), isnew);
} else {
OC.Contacts.Card.saveAddress(this, obj, isnew);
}
$(this).dialog('close');
},
'Cancel':function() {
$(this).dialog('close');
if(isnew) {
container.remove();
}
}
},
close : function(event, ui) {
$(this).dialog('destroy').remove();
$('#address_dialog').remove();
},
open : function(event, ui) {
$( "#adr_city" ).autocomplete({
source: function( request, response ) {
$.ajax({
url: "http://ws.geonames.org/searchJSON",
dataType: "jsonp",
data: {
featureClass: "P",
style: "full",
maxRows: 12,
lang: lang,
name_startsWith: request.term
},
success: function( data ) {
response( $.map( data.geonames, function( item ) {
return {
label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
value: item.name,
country: item.countryName
}
}));
}
});
},
minLength: 2,
select: function( event, ui ) {
if(ui.item && $('#adr_country').val().trim().length == 0) {
$('#adr_country').val(ui.item.country);
}
},
open: function() {
$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
$('#adr_country').autocomplete({
source: function( request, response ) {
$.ajax({
url: "http://ws.geonames.org/searchJSON",
dataType: "jsonp",
data: {
/*featureClass: "A",*/
featureCode: "PCLI",
/*countryBias: "true",*/
/*style: "full",*/
lang: lang,
maxRows: 12,
name_startsWith: request.term
},
success: function( data ) {
response( $.map( data.geonames, function( item ) {
return {
label: item.name,
value: item.name
}
}));
}
});
},
minLength: 2,
select: function( event, ui ) {
/*if(ui.item) {
$('#adr_country').val(ui.item.country);
}
log( ui.item ?
"Selected: " + ui.item.label :
"Nothing selected, input was " + this.value);*/
},
open: function() {
$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
}
});
} else {
alert(jsondata.data.message);
}
});
}
},
saveAddress:function(dlg, obj, isnew){
if(!this.enabled) {
return;
}
if(isnew) {
container = $('#addresses dl').last();
obj = container.find('input').first();
} else {
checksum = OC.Contacts.checksumFor(obj);
container = OC.Contacts.propertyContainerFor(obj);
}
var adr = new Array(
$(dlg).find('#adr_pobox').val().strip_tags(),
$(dlg).find('#adr_extended').val().strip_tags(),
$(dlg).find('#adr_street').val().strip_tags(),
$(dlg).find('#adr_city').val().strip_tags(),
$(dlg).find('#adr_region').val().strip_tags(),
$(dlg).find('#adr_zipcode').val().strip_tags(),
$(dlg).find('#adr_country').val().strip_tags()
);
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())));
OC.Contacts.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);
$('#addresses').show();
container.show();
},
uploadPhoto:function(filelist) {
if(!this.enabled) {
return;
}
if(!filelist) {
OC.dialogs.alert(t('contacts','No files selected for upload.'), t('contacts', 'Error'));
return;
}
var file = filelist[0];
var target = $('#file_upload_target');
var form = $('#file_upload_form');
var totalSize=0;
if(file.size > $('#max_upload').val()){
OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts', 'Error'));
return;
} else {
target.load(function(){
var response=jQuery.parseJSON(target.contents().text());
if(response != undefined && response.status == 'success'){
OC.Contacts.Card.editPhoto(response.data.id, response.data.tmp);
//alert('File: ' + file.tmp + ' ' + file.name + ' ' + file.mime);
}else{
OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
}
});
form.submit();
}
},
loadPhotoHandlers:function() {
var phototools = $('#phototools');
phototools.find('li a').tipsy('hide');
phototools.find('li a').tipsy();
if(this.data.PHOTO) {
phototools.find('.delete').click(function() {
$(this).tipsy('hide');
OC.Contacts.Card.deleteProperty($('#contacts_details_photo'), 'single');
$(this).hide();
});
phototools.find('.edit').click(function() {
$(this).tipsy('hide');
OC.Contacts.Card.editCurrentPhoto();
});
phototools.find('.delete').show();
phototools.find('.edit').show();
} else {
phototools.find('.delete').hide();
phototools.find('.edit').hide();
}
},
cloudPhotoSelected:function(path){
$.getJSON(OC.filePath('contacts', 'ajax', 'oc_photo.php'),{'path':path,'id':OC.Contacts.Card.id},function(jsondata){
if(jsondata.status == 'success'){
//alert(jsondata.data.page);
OC.Contacts.Card.editPhoto(jsondata.data.id, jsondata.data.tmp)
$('#edit_photo_dialog_img').html(jsondata.data.page);
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
},
loadPhoto:function(){
var self = this;
var refreshstr = '&refresh='+Math.random();
$('#phototools li a').tipsy('hide');
var wrapper = $('#contacts_details_photo_wrapper');
wrapper.addClass('loading').addClass('wait');
delete this.photo;
this.photo = new Image();
$(this.photo).load(function () {
$('img.contacts_details_photo').remove()
$(this).addClass('contacts_details_photo');
wrapper.css('width', $(this).get(0).width + 10);
wrapper.removeClass('loading').removeClass('wait');
$(this).insertAfter($('#phototools')).fadeIn();
}).error(function () {
// notify the user that the image could not be loaded
OC.Contacts.notify({message:t('contacts','Error loading profile picture.')});
}).attr('src', OC.linkTo('contacts', 'photo.php')+'?id='+self.id+refreshstr);
this.loadPhotoHandlers()
},
editCurrentPhoto:function(){
if(!this.enabled) {
return;
}
$.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),{'id':this.id},function(jsondata){
if(jsondata.status == 'success'){
//alert(jsondata.data.page);
OC.Contacts.Card.editPhoto(jsondata.data.id, jsondata.data.tmp)
$('#edit_photo_dialog_img').html(jsondata.data.page);
}
else{
wrapper.removeClass('wait');
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
},
editPhoto:function(id, tmpkey){
if(!this.enabled) {
return;
}
//alert('editPhoto: ' + tmpkey);
$.getJSON(OC.filePath('contacts', 'ajax', 'cropphoto.php'),{'tmpkey':tmpkey,'id':this.id, 'requesttoken':oc_requesttoken},function(jsondata){
if(jsondata.status == 'success'){
//alert(jsondata.data.page);
$('#edit_photo_dialog_img').html(jsondata.data.page);
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
if($('#edit_photo_dialog').dialog('isOpen') == true){
$('#edit_photo_dialog').dialog('moveToTop');
} else {
$('#edit_photo_dialog').dialog('open');
}
},
savePhoto:function() {
if(!this.enabled) {
return;
}
var target = $('#crop_target');
var form = $('#cropform');
var wrapper = $('#contacts_details_photo_wrapper');
var self = this;
wrapper.addClass('wait');
form.submit();
target.load(function(){
var response=jQuery.parseJSON(target.contents().text());
if(response != undefined && response.status == 'success'){
// load cropped photo.
self.loadPhoto();
OC.Contacts.Card.data.PHOTO = true;
}else{
OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
wrapper.removeClass('wait');
}
});
OC.Contacts.Contacts.refreshThumbnail(this.id);
},
addIM:function() {
//alert('addMail');
var imlist = $('#imlist');
imlist.find('li.template:first-child').clone(true).appendTo(imlist).show().find('a .tip').tipsy();
imlist.find('li.template:last-child').find('select').addClass('contacts_property');
imlist.find('li.template:last-child').removeClass('template').addClass('propertycontainer');
imlist.find('li:last-child').find('input[type="text"]').focus();
return false;
},
loadIMs:function() {
//console.log('loadIMs');
$('#ims').hide();
$('#imlist li.propertycontainer').remove();
var imlist = $('#imlist');
for(var im in this.data.IMPP) {
this.addIM();
var curim = imlist.find('li.propertycontainer:last-child');
if(typeof this.data.IMPP[im].label != 'undefined') {
curim.prepend('<label class="xab">'+this.data.IMPP[im].label+'</label>');
}
curim.data('checksum', this.data.IMPP[im]['checksum'])
curim.find('input[type="text"]').val(this.data.IMPP[im]['value'].split(':').pop());
for(var param in this.data.IMPP[im]['parameters']) {
if(param.toUpperCase() == 'PREF') {
curim.find('input[type="checkbox"]').attr('checked', 'checked')
}
else if(param.toUpperCase() == 'TYPE') {
if(typeof this.data.IMPP[im]['parameters'][param] == 'string') {
var found = false;
var imt = this.data.IMPP[im]['parameters'][param];
curim.find('select.types option').each(function(){
if($(this).val().toUpperCase() == imt.toUpperCase()) {
$(this).attr('selected', 'selected');
found = true;
}
});
if(!found) {
curim.find('select.type option:last-child').after('<option value="'+imt+'" selected="selected">'+imt+'</option>');
}
} else if(typeof this.data.IMPP[im]['parameters'][param] == 'object') {
for(imtype in this.data.IMPP[im]['parameters'][param]) {
var found = false;
var imt = this.data.IMPP[im]['parameters'][param][imtype];
curim.find('select.types option').each(function(){
if($(this).val().toUpperCase() == imt.toUpperCase().split(',')) {
$(this).attr('selected', 'selected');
found = true;
}
});
if(!found) {
curim.find('select.type option:last-child').after('<option value="'+imt+'" selected="selected">'+imt+'</option>');
}
}
}
}
else if(param.toUpperCase() == 'X-SERVICE-TYPE') {
curim.find('select.impp').val(this.data.IMPP[im]['parameters'][param].toLowerCase());
}
}
}
if($('#imlist li').length > 1) {
$('#ims').show();
}
return false;
},
addMail:function() {
var emaillist = $('#emaillist');
emaillist.find('li.template:first-child').clone(true).appendTo(emaillist).show().find('a .tip').tipsy();
emaillist.find('li.template:last-child').find('select').addClass('contacts_property');
emaillist.find('li.template:last-child').removeClass('template').addClass('propertycontainer');
var current = emaillist.find('li.propertycontainer:last-child');
current.find('input[type="email"]').focus();
current.find('select').multiselect({
noneSelectedText: t('contacts', 'Select type'),
header: false,
selectedList: 4,
classes: 'typelist'
});
current.find('a.mail').click(function() { OC.Contacts.mailTo(this) });
current.find('a.mail').keydown(function() { OC.Contacts.mailTo(this) });
return false;
},
loadMails:function() {
$('#emails').hide();
$('#emaillist li.propertycontainer').remove();
var emaillist = $('#emaillist');
for(var mail in this.data.EMAIL) {
this.addMail();
emaillist.find('li:last-child').find('select').multiselect('destroy');
var curemail = emaillist.find('li.propertycontainer:last-child');
if(typeof this.data.EMAIL[mail].label != 'undefined') {
curemail.prepend('<label class="xab">'+this.data.EMAIL[mail].label+'</label>');
}
curemail.data('checksum', this.data.EMAIL[mail]['checksum'])
curemail.find('input[type="email"]').val(this.data.EMAIL[mail]['value']);
for(var param in this.data.EMAIL[mail]['parameters']) {
if(param.toUpperCase() == 'PREF') {
curemail.find('input[type="checkbox"]').attr('checked', 'checked')
}
else if(param.toUpperCase() == 'TYPE') {
for(etype in this.data.EMAIL[mail]['parameters'][param]) {
var found = false;
var et = this.data.EMAIL[mail]['parameters'][param][etype];
curemail.find('select option').each(function(){
if($.inArray($(this).val().toUpperCase(), et.toUpperCase().split(',')) > -1) {
$(this).attr('selected', 'selected');
found = true;
}
});
if(!found) {
curemail.find('select option:last-child').after('<option value="'+et+'" selected="selected">'+et+'</option>');
}
}
}
}
curemail.find('select').multiselect({
noneSelectedText: t('contacts', 'Select type'),
header: false,
selectedList: 4,
classes: 'typelist'
});
}
if($('#emaillist li').length > 1) {
$('#emails').show();
}
$('#emaillist li:last-child').find('input[type="text"]').focus();
return false;
},
addPhone:function() {
var phonelist = $('#phonelist');
phonelist.find('li.template:first-child').clone(true).appendTo(phonelist); //.show();
phonelist.find('li.template:last-child').find('select').addClass('contacts_property');
phonelist.find('li.template:last-child').removeClass('template').addClass('propertycontainer');
phonelist.find('li:last-child').find('input[type="text"]').focus();
phonelist.find('li:last-child').find('select').multiselect({
noneSelectedText: t('contacts', 'Select type'),
header: false,
selectedList: 4,
classes: 'typelist'
});
phonelist.find('li:last-child').show();
return false;
},
loadPhones:function() {
$('#phones').hide();
$('#phonelist li.propertycontainer').remove();
var phonelist = $('#phonelist');
for(var phone in this.data.TEL) {
this.addPhone();
var curphone = phonelist.find('li.propertycontainer:last-child');
if(typeof this.data.TEL[phone].label != 'undefined') {
curphone.prepend('<label class="xab">'+this.data.TEL[phone].label+'</label>');
}
curphone.find('select').multiselect('destroy');
curphone.data('checksum', this.data.TEL[phone]['checksum'])
curphone.find('input[type="text"]').val(this.data.TEL[phone]['value']);
for(var param in this.data.TEL[phone]['parameters']) {
if(param.toUpperCase() == 'PREF') {
curphone.find('input[type="checkbox"]').attr('checked', 'checked');
}
else if(param.toUpperCase() == 'TYPE') {
for(ptype in this.data.TEL[phone]['parameters'][param]) {
var found = false;
var pt = this.data.TEL[phone]['parameters'][param][ptype];
curphone.find('select option').each(function() {
//if ($(this).val().toUpperCase() == pt.toUpperCase()) {
if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) {
$(this).attr('selected', 'selected');
found = true;
}
});
if(!found) {
curphone.find('select option:last-child').after('<option class="custom" value="'+pt+'" selected="selected">'+pt+'</option>');
}
}
}
}
curphone.find('select').multiselect({
noneSelectedText: t('contacts', 'Select type'),
header: false,
selectedList: 4,
classes: 'typelist'
});
}
if(phonelist.find('li').length > 1) {
$('#phones').show();
}
return false;
},
},
Contacts:{
contacts:{},
deletionQueue:[],
batchnum:50,
warnNotDeleted:function(e) {
e = e || window.event;
var warn = t('contacts', 'Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted.');
if (e) {
e.returnValue = String(warn);
}
if(OC.Contacts.Contacts.deletionQueue.length > 0) {
setTimeout(OC.Contacts.Contacts.deleteFilesInQueue, 1);
}
return warn;
},
deleteFilesInQueue:function() {
var queue = OC.Contacts.Contacts.deletionQueue;
if(queue.length > 0) {
OC.Contacts.notify({cancel:true});
while(queue.length > 0) {
var id = queue.pop();
if(id) {
OC.Contacts.Card.doDelete(id, false);
}
}
}
},
getContact:function(id) {
if(!this.contacts[id]) {
this.contacts[id] = $('#contacts li[data-id="'+id+'"]');
if(!this.contacts[id]) {
self = this;
$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){
if(jsondata.status == 'success'){
self.contacts[id] = self.insertContact({data:jsondata.data});
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
}
}
return this.contacts[id];
},
drop:function(event, ui) {
var dragitem = ui.draggable, droptarget = $(this);
if(dragitem.is('li')) {
OC.Contacts.Contacts.dropContact(event, dragitem, droptarget);
} else {
OC.Contacts.Contacts.dropAddressbook(event, dragitem, droptarget);
}
},
dropContact:function(event, dragitem, droptarget) {
if(dragitem.data('bookid') == droptarget.data('id')) {
return false;
}
var droplist = (droptarget.is('ul')) ? droptarget : droptarget.next();
$.post(OC.filePath('contacts', 'ajax', 'contact/move.php'),
{
id: dragitem.data('id'),
aid: droptarget.data('id')
},
function(jsondata){
if(jsondata.status == 'success'){
dragitem.attr('data-bookid', droptarget.data('id'))
dragitem.data('bookid', droptarget.data('id'));
OC.Contacts.Contacts.insertContact({
contactlist:droplist,
contact:dragitem.detach()
});
OC.Contacts.Contacts.scrollTo(dragitem.data('id'));
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
},
dropAddressbook:function(event, dragitem, droptarget) {
if(confirm(t('contacts', 'Do you want to merge these address books?'))) {
if(dragitem.data('bookid') == droptarget.data('id')) {
return false;
}
var droplist = (droptarget.is('ul')) ? droptarget : droptarget.next();
$.post(OC.filePath('contacts', 'ajax', 'contact/move.php'),
{
id: dragitem.data('id'),
aid: droptarget.data('id'),
isaddressbook: 1
},
function(jsondata){
if(jsondata.status == 'success'){
OC.Contacts.Contacts.update(); // Easier to refresh the whole bunch.
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
} else {
return false;
}
},
/**
* @params params An object with the properties 'contactlist':a jquery object of the ul to insert into,
* 'contacts':a jquery object of all items in the list and either 'data': an object with the properties
* id, addressbookid and displayname or 'contact': a listitem to be inserted directly.
* If 'contactlist' or 'contacts' aren't defined they will be search for based in the properties in 'data'.
*/
insertContact:function(params) {
var id, bookid;
if(!params.contactlist) {
// FIXME: Check if contact really exists.
bookid = params.data ? params.data.addressbookid : params.contact.data('bookid');
id = params.data ? params.data.id : params.contact.data('id');
params.contactlist = $('#contacts ul[data-id="'+bookid+'"]');
}
if(!params.contacts) {
bookid = params.data ? params.data.addressbookid : params.contact.data('bookid');
id = params.data ? params.data.id : params.contact.data('id');
params.contacts = $('#contacts ul[data-id="'+bookid+'"] li');
}
var contact = params.data
? $('<li data-id="'+params.data.id+'" data-bookid="'+params.data.addressbookid
+ '" role="button"><a href="'+OC.linkTo('contacts', 'index.php')+'?id='
+ params.data.id+'" style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')
+ '?id='+params.data.id+') no-repeat scroll 0% 0% transparent;">'
+ params.data.displayname+'</a></li>')
: params.contact;
var added = false;
var name = params.data ? params.data.displayname.toLowerCase() : contact.find('a').text().toLowerCase();
if(params.contacts) {
params.contacts.each(function() {
if ($(this).text().toLowerCase().localeCompare(name) > 0) {
$(this).before(contact);
added = true;
return false;
}
});
}
if(!added || !params.contacts) {
params.contactlist.append(contact);
}
//this.contacts[id] = contact;
return contact;
},
addAddressbook:function(name, description, cb) {
$.post(OC.filePath('contacts', 'ajax/addressbook', 'add.php'), { name: name, description: description, active: true },
function(jsondata) {
if(jsondata.status == 'success'){
if(cb && typeof cb == 'function') {
cb(jsondata.data.addressbook);
}
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
return false;
}
});
},
doImport:function(file, aid, cb) {
$.post(OC.filePath('contacts', '', 'import.php'), { id: aid, file: file, fstype: 'OC_FilesystemView' },
function(jsondata) {
if(jsondata.status != 'success'){
OC.Contacts.notify({message:jsondata.data.message});
}
if(typeof cb == 'function') {
cb();
}
});
return false;
},
next:function(reverse) {
var curlistitem = this.getContact(OC.Contacts.Card.id);
var newlistitem = reverse ? curlistitem.prev('li') : curlistitem.next('li');
if(newlistitem) {
curlistitem.removeClass('active');
OC.Contacts.Card.update({
cid:newlistitem.data('id'),
aid:newlistitem.data('bookid')
});
}
},
previous:function() {
this.next(true);
},
nextAddressbook:function(reverse) {
//console.log('nextAddressbook', reverse);
var curlistitem = this.getContact(OC.Contacts.Card.id);
var parent = curlistitem.parent('ul');
var newparent = reverse
? parent.prevAll('ul').first()
: parent.nextAll('ul').first();
if(newparent) {
newlistitem = newparent.find('li:first-child');
if(newlistitem) {
parent.slideUp().prev('h3').removeClass('active');
newparent.slideDown().prev('h3').addClass('active');
curlistitem.removeClass('active');
OC.Contacts.Card.update({
cid:newlistitem.data('id'),
aid:newlistitem.data('bookid')
});
}
}
},
previousAddressbook:function() {
//console.log('previousAddressbook');
this.nextAddressbook(true);
},
// Reload the contacts list.
update:function(params){
if(!params) { params = {}; }
if(!params.start) {
if(params.aid) {
$('#contacts h3[data-id="'+params.aid+'"],#contacts ul[data-id="'+params.aid+'"]').remove();
} else {
$('#contacts').empty();
}
}
self = this;
//console.log('update: ' + params.cid + ' ' + params.aid + ' ' + params.start);
var firstrun = false;
var opts = {};
opts['startat'] = (params.start?params.start:0);
if(params.aid) {
opts['aid'] = params.aid;
}
$.getJSON(OC.filePath('contacts', 'ajax', 'contact/list.php'),opts,function(jsondata){
if(jsondata.status == 'success'){
var books = jsondata.data.entries;
$.each(books, function(b, book) {
if($('#contacts h3[data-id="'+b+'"]').length == 0) {
firstrun = true;
var sharedindicator = book.owner == OC.currentUser ? ''
: '<img class="shared svg" src="'+OC.imagePath('core', 'actions/shared')+'" title="'+t('contacts', 'Shared by ')+book.owner+'" />'
if($('#contacts h3').length == 0) {
$('#contacts').html('<h3 class="addressbook" data-id="'
+ b + '" data-permissions="' + book.permissions + '">' + book.displayname
+ sharedindicator + '</h3><ul class="contacts hidden" data-id="'+b+'" data-permissions="'
+ book.permissions + '"></ul>');
} else {
if(!$('#contacts h3[data-id="' + b + '"]').length) {
var item = $('<h3 class="addressbook" data-id="'
+ b + '" data-permissions="' + book.permissions + '">'
+ book.displayname+sharedindicator+'</h3><ul class="contacts hidden" data-id="' + b
+ '" data-permissions="' + book.permissions + '"></ul>');
var added = false;
$('#contacts h3').each(function(){
if ($(this).text().toLowerCase().localeCompare(book.displayname.toLowerCase()) > 0) {
$(this).before(item).fadeIn('fast');
added = true;
return false;
}
});
if(!added) {
$('#contacts').append(item);
}
}
}
$('#contacts h3[data-id="'+b+'"]').on('click', function(event) {
$('#contacts h3').removeClass('active');
$(this).addClass('active');
$('#contacts ul[data-id="'+b+'"]').slideToggle(300);
return false;
});
var accept = 'li:not([data-bookid="'+b+'"]),h3:not([data-id="'+b+'"])';
$('#contacts h3[data-id="'+b+'"],#contacts ul[data-id="'+b+'"]').droppable({
drop: OC.Contacts.Contacts.drop,
activeClass: 'ui-state-hover',
accept: accept
});
}
var contactlist = $('#contacts ul[data-id="'+b+'"]');
var contacts = $('#contacts ul[data-id="'+b+'"] li');
for(var c in book.contacts) {
if(book.contacts[c].id == undefined) { continue; }
if(!$('#contacts li[data-id="'+book.contacts[c]['id']+'"]').length) {
var contact = OC.Contacts.Contacts.insertContact({contactlist:contactlist, contacts:contacts, data:book.contacts[c]});
if(c == self.batchnum-10) {
contact.bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
$(this).unbind(event);
var bookid = $(this).data('bookid');
var numsiblings = $('.contacts li[data-bookid="'+bookid+'"]').length;
if (isInView && numsiblings >= self.batchnum) {
//console.log('This would be a good time to load more contacts.');
OC.Contacts.Contacts.update({cid:params.cid, aid:bookid, start:$('#contacts li[data-bookid="'+bookid+'"]').length});
}
});
}
}
}
});
$('#contacts h3 img.shared').tipsy()
if($('#contacts h3').length > 1) {
$('#contacts li,#contacts h3').draggable({
distance: 10,
revert: 'invalid',
axis: 'y', containment: '#contacts',
scroll: true, scrollSensitivity: 40,
opacity: 0.7, helper: 'clone'
});
} else {
$('#contacts h3').first().addClass('active');
}
if(opts['startat'] == 0) { // only update card on first load.
OC.Contacts.Card.update(params);
}
} else {
OC.Contacts.notify({message:t('contacts', 'Error')+': '+jsondata.data.message});
}
});
},
refreshThumbnail:function(id){
var item = $('.contacts li[data-id="'+id+'"]').find('a');
item.html(OC.Contacts.Card.fn);
item.css('background','url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+id+'&refresh=1'+Math.random()+') no-repeat');
},
scrollTo:function(id){
var item = $('#contacts li[data-id="'+id+'"]');
if(item && $.isNumeric(item.offset().top)) {
//console.log('scrollTo ' + parseInt(item.offset().top));
$('#contacts').animate({
scrollTop: parseInt(item.offset()).top-40}, 'slow','swing');
}
Contact.prototype.addProperty = function($option, name) {
console.log('Contact.addProperty', name)
switch(name) {
case 'NICKNAME':
case 'TITLE':
case 'ORG':
case 'BDAY':
this.$fullelem.find('[data-element="' + name.toLowerCase() + '"]').show();
$option.prop('disabled', true);
break;
case 'TEL':
case 'URL':
case 'EMAIL':
$elem = this.renderStandardProperty(name.toLowerCase());
var $list = this.$fullelem.find('ul.' + name.toLowerCase());
$list.show();
$list.append($elem);
break;
case 'ADR':
$elem = this.renderAddressProperty();
var $list = this.$fullelem.find('ul.' + name.toLowerCase());
$list.show();
$list.append($elem);
$elem.find('.adr.display').trigger('click');
break;
case 'IMPP':
$elem = this.renderIMProperty();
var $list = this.$fullelem.find('ul.' + name.toLowerCase());
$list.show();
$list.append($elem);
break;
}
}
}
$(document).ready(function(){
OCCategories.changed = OC.Contacts.Card.categoriesChanged;
OCCategories.app = 'contacts';
var ninjahelp = $('#ninjahelp');
$('#bottomcontrols .settings').on('click keydown', function() {
try {
ninjahelp.hide();
OC.appSettings({appid:'contacts', loadJS:true, cache:false});
} catch(e) {
console.log('error:', e.message);
}
});
$('#bottomcontrols .import').click(function() {
$('#import_upload_start').trigger('click');
});
$('#contacts_newcontact').on('click keydown', OC.Contacts.Card.editNew);
ninjahelp.find('.close').on('click keydown',function() {
ninjahelp.hide();
});
$(document).on('keyup', function(event) {
if(event.target.nodeName.toUpperCase() != 'BODY'
|| $('#contacts li').length == 0
|| !OC.Contacts.Card.id) {
Contact.prototype.deleteProperty = function(params) {
var obj = params.obj;
if(!this.enabled) {
return;
}
//console.log(event.which + ' ' + event.target.nodeName);
/**
* To add:
* Shift-a: add addressbook
* u (85): hide/show leftcontent
* f (70): add field
*/
switch(event.which) {
case 27: // Esc
ninjahelp.hide();
break;
case 46: // Delete
if(event.shiftKey) {
OC.Contacts.Card.delayedDelete();
}
break;
case 40: // down
case 74: // j
OC.Contacts.Contacts.next();
break;
case 65: // a
if(event.shiftKey) {
// add addressbook
OC.Contacts.notImplemented();
break;
}
OC.Contacts.Card.editNew();
break;
case 38: // up
case 75: // k
OC.Contacts.Contacts.previous();
break;
case 34: // PageDown
case 78: // n
// next addressbook
OC.Contacts.Contacts.nextAddressbook();
break;
case 79: // o
var aid = $('#contacts h3.active').first().data('id');
if(aid) {
$('#contacts ul[data-id="'+aid+'"]').slideToggle(300);
}
break;
case 33: // PageUp
case 80: // p
// prev addressbook
OC.Contacts.Contacts.previousAddressbook();
break;
case 82: // r
OC.Contacts.Contacts.update({cid:OC.Contacts.Card.id});
break;
case 63: // ? German.
if(event.shiftKey) {
ninjahelp.toggle('fast');
}
break;
case 171: // ? Danish
case 191: // ? Standard qwerty
ninjahelp.toggle('fast');
break;
var element = this.propertyTypeFor(obj);
var $container = this.propertyContainerFor(obj);
console.log('Contact.deleteProperty, element', element, $container);
var params = {
name: element,
id: this.id
};
if(this.multi_properties.indexOf(element) !== -1) {
params['checksum'] = this.checksumFor(obj);
}
this.setAsSaving(obj, true);
var self = this;
$.post(OC.filePath('contacts', 'ajax', 'contact/deleteproperty.php'), params, function(jsondata) {
if(!jsondata) {
$(document).trigger('status.contact.error', {
status: 'error',
message: t('contacts', 'Network or server error. Please inform administrator.'),
});
self.setAsSaving(obj, false);
return false;
}
if(jsondata.status == 'success') {
// TODO: Test if removing from internal data structure works
if(self.multi_properties.indexOf(element) !== -1) {
// First find out if an existing element by looking for checksum
var checksum = self.checksumFor(obj);
if(checksum) {
for(var i in self.data[element]) {
if(self.data[element][i].checksum === checksum) {
// Found it
var prop = self.data[element][i];
self.data[element].splice(self.data[element].indexOf(prop), 1);
delete prop;
break;
}
}
}
$container.remove();
} else {
self.setAsSaving(obj, false);
self.$fullelem.find('[data-element="' + element.toLowerCase() + '"]').hide();
$container.find('input.value').val('');
self.$addMenu.find('option[value="' + element.toUpperCase() + '"]').prop('disabled', false);
}
return true;
} else {
$(document).trigger('status.contact.error', {
status: 'error',
message: jsondata.data.message,
});
self.setAsSaving(obj, false);
return false;
}
},'json');
}
});
//$(window).on('beforeunload', OC.Contacts.Contacts.deleteFilesInQueue);
// Load a contact.
$('.contacts').keydown(function(event) {
if(event.which == 13 || event.which == 32) {
$('.contacts').click();
/**
* @brief Act on change of a property.
* If this is a new contact it will first be saved to the datastore and a
* new datastructure will be added to the object. FIXME: Not implemented yet.
* If the obj argument is not provided 'name' and 'value' MUST be provided
* and this is only allowed for single elements like N, FN, CATEGORIES.
* @param obj. The form form field that has changed.
* @param name. The optional name of the element.
* @param value. The optional value.
*/
Contact.prototype.saveProperty = function(params) {
console.log('Contact.saveProperty', params);
if(!this.id) {
var self = this;
this.add({isnew:true}, function(response) {
if(!response || response.status === 'error') {
console.log('No response object');
return false;
}
console.log('Contact added.' + self.id);
self.saveProperty(params);
});
return;
}
});
$(document).on('click', '#contacts', function(event){
var $tgt = $(event.target);
if ($tgt.is('li') || $tgt.is('a')) {
var item = $tgt.is('li')?$($tgt):($tgt).parent();
var id = item.data('id');
var bookid = item.data('bookid');
item.addClass('active');
var oldid = $('#rightcontent').data('id');
if(oldid != 0){
var olditem = $('.contacts li[data-id="'+oldid+'"]');
var oldbookid = olditem.data('bookid');
olditem.removeClass('active');
if(oldbookid != bookid) {
$('#contacts h3[data-id="'+oldbookid+'"]').removeClass('active');
$('#contacts h3[data-id="'+bookid+'"]').addClass('active');
var obj = null;
var element = null;
var q = '';
if(params.obj) {
obj = params.obj;
q = this.queryStringFor(obj);
element = this.propertyTypeFor(obj);
} else {
element = params.name;
var value = utils.isArray(params.value)
? $.param(params.value)
: encodeURIComponent(params.value);
q = 'id=' + this.id + '&value=' + value + '&name=' + element;
}
console.log('q', q);
var self = this;
this.setAsSaving(obj, true);
$.post(OC.filePath('contacts', 'ajax', 'contact/saveproperty.php'), q, function(jsondata){
if(!jsondata) {
$(document).trigger('status.contact.error', {
status: 'error',
message: t('contacts', 'Network or server error. Please inform administrator.'),
});
self.setAsSaving(obj, false);
return false;
}
if(jsondata.status == 'success') {
if(!self.data[element]) {
self.data[element] = [];
}
if(self.multi_properties.indexOf(element) !== -1) {
// First find out if an existing element by looking for checksum
var checksum = self.checksumFor(obj);
if(checksum) {
for(var i in self.data[element]) {
if(self.data[element][i].checksum === checksum) {
self.data[element][i] = {
name: element,
value: self.valueFor(obj),
parameters: self.parametersFor(obj),
checksum: jsondata.data.checksum,
}
break;
}
}
} else {
self.data[element].push({
name: element,
value: self.valueFor(obj),
parameters: self.parametersFor(obj),
checksum: jsondata.data.checksum,
});
}
self.propertyContainerFor(obj).data('checksum', jsondata.data.checksum);
} else {
// Save value and parameters internally
var value = obj ? self.valueFor(obj) : params.value;
switch(element) {
case 'CATEGORIES':
// We deal with this in addToGroup()
break;
case 'N':
if(!utils.isArray(value)) {
value = value.split(';');
// Then it is auto-generated from FN.
var $nelems = self.$fullelem.find('.n.edit input');
console.log('nelems', $nelems);
$.each(value, function(idx, val) {
console.log('nval', val);
self.$fullelem.find('#n_' + idx).val(val);
});
}
case 'FN':
// Update the list element
self.$listelem.find('.nametext').text(value);
var nempty = true;
if(!self.data.N) {
// TODO: Maybe add a method for constructing new elements?
self.data.N = [{name:'N',value:['', '', '', '', ''],parameters:[]}];
}
$.each(self.data.N[0]['value'], function(idx, val) {
if(val) {
nempty = false;
return false;
}
});
if(nempty) {
self.data.N[0]['value'] = ['', '', '', '', ''];
nvalue = value.split(' ');
// Very basic western style parsing. I'm not gonna implement
// https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;)
self.data.N[0]['value'][0] = nvalue.length > 2 && nvalue.slice(nvalue.length-1).toString() || nvalue[1] || '';
self.data.N[0]['value'][1] = nvalue[0] || '';
self.data.N[0]['value'][2] = nvalue.length > 2 && nvalue.slice(1, nvalue.length-1).join(' ') || '';
setTimeout(function() {
// TODO: Hint to user to check if name is properly formatted
console.log('auto creating N', self.data.N[0].value)
self.saveProperty({name:'N', value:self.data.N[0].value.join(';')});
setTimeout(function() {
self.$fullelem.find('.fullname').next('.action.edit').trigger('click');
OC.notify({message:t('contacts', 'Is this correct?')});
}
, 1000);
}
, 500);
}
$(document).trigger('status.contact.renamed', {
id: self.id,
contact: self,
});
case 'NICKNAME':
case 'BDAY':
case 'ORG':
case 'TITLE':
self.data[element][0] = {
name: element,
value: value,
parameters: self.parametersFor(obj),
checksum: jsondata.data.checksum,
};
break;
default:
break;
}
}
self.setAsSaving(obj, false);
return true;
} else {
$(document).trigger('status.contact.error', {
status: 'error',
message: jsondata.data.message,
});
self.setAsSaving(obj, false);
return false;
}
},'json');
}
/**
* Hide contact list element.
*/
Contact.prototype.hide = function() {
this.getListItemElement().hide();
}
/**
* Remove any open contact from the DOM.
*/
Contact.prototype.close = function() {
console.log('Contact.close', this);
if(this.$fullelem) {
this.$fullelem.remove();
return true;
} else {
return false;
}
}
/**
* Remove any open contact from the DOM and detach it's list
* element from the DOM.
* @returns The contact object.
*/
Contact.prototype.detach = function() {
if(this.$fullelem) {
this.$fullelem.remove();
}
if(this.$listelem) {
this.$listelem.detach();
return this;
}
}
/**
* Set a contacts list element as (un)checked
* @returns The contact object.
*/
Contact.prototype.setChecked = function(checked) {
if(this.$listelem) {
this.$listelem.find('input:checkbox').prop('checked', checked);
return this;
}
}
/**
* Set a contact to en/disabled depending on its permissions.
* @param boolean enabled
*/
Contact.prototype.setEnabled = function(enabled) {
console.log('setEnabled', enabled);
if(enabled) {
this.$fullelem.find('#addproperty').show();
} else {
this.$fullelem.find('#addproperty').hide();
}
this.enabled = enabled;
this.$fullelem.find('.value,.action').each(function () {
$(this).prop('disabled', !enabled);
});
$(document).trigger('status.contact.enabled', enabled);
}
/**
* Add a contact from data store and remove it from the DOM
* @params params. An object which can contain the optional properties:
* aid: The id of the addressbook to add the contact to. Per default it will be added to the first.
* fn: The formatted name of the contact.
* @param cb Optional callback function which
* @returns The callback gets an object as argument with a variable 'status' of either 'success'
* or 'error'. On success the 'data' property of that object contains the contact id as 'id', the
* addressbook id as 'aid' and the contact data structure as 'details'.
*/
Contact.prototype.add = function(params, cb) {
var self = this;
$.post(OC.filePath('contacts', 'ajax', 'contact/add.php'),
params, function(jsondata) {
if(!jsondata) {
$(document).trigger('status.contact.error', {
status: 'error',
message: t('contacts', 'Network or server error. Please inform administrator.'),
});
return false;
}
if(jsondata.status === 'success') {
self.id = parseInt(jsondata.data.id);
self.access.id = parseInt(jsondata.data.aid);
self.data = jsondata.data.details;
$(document).trigger('status.contact.added', {
id: self.id,
contact: self,
});
}
if(typeof cb == 'function') {
cb(jsondata);
}
});
}
/**
* Delete contact from data store and remove it from the DOM
* @param cb Optional callback function which
* @returns An object with a variable 'status' of either success
* or 'error'
*/
Contact.prototype.destroy = function(cb) {
var self = this;
$.post(OC.filePath('contacts', 'ajax', 'contact/delete.php'),
{id: this.id}, function(jsondata) {
if(jsondata && jsondata.status === 'success') {
if(self.$listelem) {
self.$listelem.remove();
}
if(self.$fullelem) {
self.$fullelem.remove();
}
}
$.getJSON(OC.filePath('contacts', 'ajax', 'contact/details.php'),{'id':id},function(jsondata){
if(jsondata.status == 'success'){
OC.Contacts.Card.loadContact(jsondata.data, bookid);
if(typeof cb == 'function') {
var retval = {status: jsondata ? jsondata.status : 'error'};
if(jsondata) {
if(jsondata.status === 'success') {
retval['id'] = jsondata.id;
} else {
retval['message'] = jsondata.message;
}
} else {
retval['message'] = t('contacts', 'There was an unknown error when trying to delete this contact');
retval['id'] = self.id;
}
else{
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
cb(retval);
}
});
}
Contact.prototype.queryStringFor = function(obj) {
var q = 'id=' + this.id;
var ptype = this.propertyTypeFor(obj);
q += '&name=' + ptype;
if(this.multi_properties.indexOf(ptype) !== -1) {
q += '&checksum=' + this.checksumFor(obj);
}
if($(obj).hasClass('propertycontainer')) {
q += '&value=' + encodeURIComponent($(obj).val());
} else {
q += '&' + this.propertyContainerFor(obj)
.find('input.value,select.value,textarea.value,.parameter').serialize();
}
return q;
}
Contact.prototype.propertyContainerFor = function(obj) {
return $(obj).hasClass('propertycontainer')
? $(obj)
: $(obj).parents('.propertycontainer').first();
}
Contact.prototype.checksumFor = function(obj) {
return this.propertyContainerFor(obj).data('checksum');
}
Contact.prototype.valueFor = function(obj) {
var $container = this.propertyContainerFor(obj);
console.assert($container.length > 0, 'Couldn\'t find container for ' + $(obj))
return $container.is('input')
? $container.val()
: (function() {
var $elem = $container.find('input.value:not(:checkbox)');
console.assert($elem.length > 0, 'Couldn\'t find value for ' + $container.data('element'));
if($elem.length === 1) {
return $elem.val;
} else if($elem.length > 1) {
var retval = [];
$.each($elem, function(idx, e) {
retval.push($(e).val());
});
return retval;
}
})();
}
Contact.prototype.parametersFor = function(obj, asText) {
var parameters = [];
$.each(this.propertyContainerFor(obj).find('select.parameter,input:checkbox:checked.parameter,textarea'), function(i, elem) {
var $elem = $(elem);
var paramname = $elem.data('parameter');
if(!parameters[paramname]) {
parameters[paramname] = [];
}
var val;
if(asText) {
if($elem.is(':checkbox')) {
val = $elem.attr('title');
} else if($elem.is('select')) {
val = $elem.find(':selected').text();
}
} else {
val = $elem.val();
}
parameters[paramname].push(val);
});
console.log('Contact.parametersFor', parameters);
return parameters;
}
Contact.prototype.propertyTypeFor = function(obj) {
var ptype = this.propertyContainerFor(obj).data('element');
return ptype ? ptype.toUpperCase() : null;
}
/**
* Render the list item
* @return A jquery object to be inserted in the DOM
*/
Contact.prototype.renderListItem = function() {
this.$listelem = this.$listTemplate.octemplate({
id: this.id,
name: this.getPreferredValue('FN', ''),
email: this.getPreferredValue('EMAIL', ''),
tel: this.getPreferredValue('TEL', ''),
adr: this.getPreferredValue('ADR', []).clean('').join(', '),
categories: this.getPreferredValue('CATEGORIES', [])
.clean('').join(' / '),
});
if(this.access.owner !== OC.currentUser
&& !(this.access.permissions & OC.PERMISSION_UPDATE
|| this.access.permissions & OC.PERMISSION_DELETE)) {
this.$listelem.find('input:checkbox').prop('disabled', true).css('opacity', '0');
}
return this.$listelem;
}
/**
* Render the full contact
* @return A jquery object to be inserted in the DOM
*/
Contact.prototype.renderContact = function() {
var self = this;
var n = this.getPreferredValue('N', ['', '', '', '', '']);
console.log('renderContact', this.data);
var values = this.data
? {
id: this.id,
name: this.getPreferredValue('FN', ''),
n0: n[0], n1: n[1], n2: n[2], n3: n[3], n4: n[4],
nickname: this.getPreferredValue('NICKNAME', ''),
title: this.getPreferredValue('TITLE', ''),
org: this.getPreferredValue('ORG', []).clean('').join(', '), // TODO Add parts if more than one.
bday: this.getPreferredValue('BDAY', '').length >= 10
? $.datepicker.formatDate('dd-mm-yy',
$.datepicker.parseDate('yy-mm-dd',
this.getPreferredValue('BDAY', '').substring(0, 10)))
: '',
}
: {id: '', name: '', nickname: '', title: '', org: '', bday: '', n0: '', n1: '', n2: '', n3: '', n4: ''};
this.$fullelem = this.$fullTemplate.octemplate(values).data('contactobject', this);
this.$addMenu = this.$fullelem.find('#addproperty');
this.$addMenu.on('change', function(event) {
console.log('add', $(this).val());
var $opt = $(this).find('option:selected');
self.addProperty($opt, $(this).val());
$(this).val('');
});
var $fullname = this.$fullelem.find('.fullname');
this.$fullelem.find('.singleproperties').on('mouseenter', function() {
$fullname.next('.edit').css('opacity', '1');
}).on('mouseleave', function() {
$fullname.next('.edit').css('opacity', '0');
});
$fullname.next('.edit').on('click keydown', function(event) {
console.log('edit name', event);
$('.tipsy').remove();
if(wrongKey(event)) {
return;
}
$(this).css('opacity', '0');
var $editor = $(this).next('.n.edit').first();
var bodyListener = function(e) {
if($editor.find($(e.target)).length == 0) {
console.log('this', $(this));
$editor.toggle('blind');
$('body').unbind('click', bodyListener);
}
}
$editor.toggle('blind', function() {
$('body').bind('click', bodyListener);
});
});
var $singleelements = this.$fullelem.find('dd.propertycontainer');
$singleelements.find('.action').css('opacity', '0');
$singleelements.on('mouseenter', function() {
$(this).find('.action').css('opacity', '1');
}).on('mouseleave', function() {
$(this).find('.action').css('opacity', '0');
});
this.$fullelem.on('click keydown', '.delete', function(event) {
console.log('delete', event);
$('.tipsy').remove();
if(wrongKey(event)) {
return;
}
self.deleteProperty({obj:event.target});
});
this.$fullelem.on('change', '.value,.parameter', function(event) {
console.log('change', event);
self.saveProperty({obj:event.target});
});
this.$fullelem.find('form').on('submit', function(event) {
console.log('submit', this, event);
return false;
});
this.$fullelem.find('[data-element="bday"]')
.find('input').datepicker({
dateFormat : 'dd-mm-yy'
});
this.loadPhoto();
if(!this.data) {
// A new contact
this.setEnabled(true);
return this.$fullelem;
}
// Loop thru all single occurrence values. If not set hide the
// element, if set disable the add menu entry.
for(var value in values) {
if(this.multi_properties.indexOf(value.toUpperCase()) === -1) {
if(!values[value].length) {
console.log('hiding', value);
this.$fullelem.find('[data-element="' + value + '"]').hide();
} else {
this.$addMenu.find('option[value="' + value.toUpperCase() + '"]').prop('disabled', true);
}
}
}
$.each(this.multi_properties, function(idx, name) {
if(self.data[name]) {
var $list = self.$fullelem.find('ul.' + name.toLowerCase());
$list.show();
for(var p in self.data[name]) {
if(typeof self.data[name][p] === 'object') {
var property = self.data[name][p];
//console.log(name, p, property);
$property = null;
switch(name) {
case 'TEL':
case 'URL':
case 'EMAIL':
$property = self.renderStandardProperty(name.toLowerCase(), property);
if(self.data[name].length >= 1) {
$property.find('input:checkbox[value="PREF"]').hide();
}
break;
case 'ADR':
$property = self.renderAddressProperty(idx, property);
break;
case 'IMPP':
$property = self.renderIMProperty(property);
break;
}
if(!$property) {
continue;
}
//console.log('$property', $property);
var meta = [];
if(property.label) {
if(!property.parameters['TYPE']) {
property.parameters['TYPE'] = [];
}
property.parameters['TYPE'].push(property.label);
meta.push(property.label);
}
for(var param in property.parameters) {
//console.log('param', param);
if(param.toUpperCase() == 'PREF') {
var $cb = $property.find('input[type="checkbox"]');
$cb.attr('checked', 'checked')
meta.push($cb.attr('title'));
}
else if(param.toUpperCase() == 'TYPE') {
for(etype in property.parameters[param]) {
var found = false;
var et = property.parameters[param][etype];
if(typeof et !== 'string') {
continue;
}
//console.log('et', et);
if(et.toUpperCase() === 'INTERNET') {
continue;
}
$property.find('select.type option').each(function() {
if($(this).val().toUpperCase() === et.toUpperCase()) {
$(this).attr('selected', 'selected');
meta.push($(this).text());
found = true;
}
});
if(!found) {
$property.find('select.type option:last-child').after('<option value="'+et+'" selected="selected">'+et+'</option>');
}
}
}
else if(param.toUpperCase() == 'X-SERVICE-TYPE') {
//console.log('setting', $property.find('select.impp'), 'to', property.parameters[param].toLowerCase());
$property.find('select.impp').val(property.parameters[param].toLowerCase());
}
}
var $meta = $property.find('.meta');
if($meta.length) {
$meta.html(meta.join('/'));
}
if(self.access.owner === OC.currentUser
|| self.access.permissions & OC.PERMISSION_UPDATE
|| self.access.permissions & OC.PERMISSION_DELETE) {
$property.find('select.type[name="parameters[TYPE][]"]')
.combobox({
singleclick: true,
classes: ['propertytype', 'float', 'label'],
});
$property.on('mouseenter', function() {
$(this).find('.listactions').css('opacity', '1');
}).on('mouseleave', function() {
$(this).find('.listactions').css('opacity', '0');
});
}
$list.append($property);
}
}
}
});
if(this.access.owner !== OC.currentUser
&& !(this.access.permissions & OC.PERMISSION_UPDATE
|| this.access.permissions & OC.PERMISSION_DELETE)) {
this.setEnabled(false);
} else {
this.setEnabled(true);
}
return this.$fullelem;
}
Contact.prototype.isEditable = function() {
return ((this.access.owner === OC.currentUser)
|| (this.access.permissions & OC.PERMISSION_UPDATE
|| this.access.permissions & OC.PERMISSION_DELETE));
}
/**
* Render a simple property. Used for EMAIL and TEL.
* @return A jquery object to be injected in the DOM
*/
Contact.prototype.renderStandardProperty = function(name, property) {
if(!this.detailTemplates[name]) {
console.log('No template for', name);
return;
}
var values = property
? { value: property.value, checksum: property.checksum }
: { value: '', checksum: 'new' };
$elem = this.detailTemplates[name].octemplate(values);
return $elem;
}
/**
* Render an ADR (address) property.
* @return A jquery object to be injected in the DOM
*/
Contact.prototype.renderAddressProperty = function(idx, property) {
console.log('Contact.renderAddressProperty', property)
if(!this.detailTemplates['adr']) {
console.log('No template for adr', this.detailTemplates);
return;
}
if(typeof idx === 'undefined') {
if(this.data && this.data.ADR && this.data.ADR.length > 0) {
idx = this.data.ADR.length - 1;
} else {
idx = 0;
}
}
var values = property ? {
value: property.value.clean('').join(', '),
checksum: property.checksum,
adr0: property.value[0] || '',
adr1: property.value[1] || '',
adr2: property.value[2] || '',
adr3: property.value[3] || '',
adr4: property.value[4] || '',
adr5: property.value[5] || '',
adr6: property.value[6] || '',
idx: idx,
}
: {value:'', checksum:'new', adr0:'', adr1:'', adr2:'', adr3:'', adr4:'', adr5:'', adr6:'', idx: idx};
var $elem = this.detailTemplates['adr'].octemplate(values);
var self = this;
$elem.find('.display').on('click', function() {
$(this).next('.listactions').hide();
var $editor = $(this).siblings('.adr.edit').first();
var $viewer = $(this);
var bodyListener = function(e) {
if($editor.find($(e.target)).length == 0) {
console.log('this', $(this));
$editor.toggle('blind');
$viewer.slideDown(400, function() {
var input = $editor.find('input').first();
console.log('input', input);
var val = self.valueFor(input);
var params = self.parametersFor(input, true);
console.log('val', val, 'params', params);
$(this).find('.meta').html(params['TYPE'].join('/'));
$(this).find('.adr').html(escapeHTML(self.valueFor($editor.find('input').first()).clean('').join(', ')));
$(this).next('.listactions').css('display', 'inline-block');
$('body').unbind('click', bodyListener);
});
}
}
$viewer.slideUp();
$editor.toggle('blind', function() {
$('body').bind('click', bodyListener);
});
});
return $elem;
}
/**
* Render an IMPP (Instant Messaging) property.
* @return A jquery object to be injected in the DOM
*/
Contact.prototype.renderIMProperty = function(property) {
if(!this.detailTemplates['impp']) {
console.log('No template for impp', this.detailTemplates);
return;
}
var values = property ? {
value: property.value,
checksum: property.checksum,
} : {value: '', checksum: 'new'};
$elem = this.detailTemplates['impp'].octemplate(values);
return $elem;
}
/**
* Render the PHOTO property.
*/
Contact.prototype.loadPhoto = function(dontloadhandlers) {
var self = this;
var id = this.id || 'new';
var refreshstr = '&refresh='+Math.random();
this.$photowrapper = this.$fullelem.find('#photowrapper');
this.$photowrapper.addClass('loading').addClass('wait');
var $phototools = this.$fullelem.find('#phototools');
console.log('photowrapper', this.$photowrapper.length);
delete this.photo;
$('img.contactphoto').remove()
this.photo = new Image();
$(this.photo).load(function () {
$(this).addClass('contactphoto');
self.$photowrapper.css('width', $(this).get(0).width + 10);
self.$photowrapper.removeClass('loading').removeClass('wait');
$(this).insertAfter($phototools).fadeIn();
}).error(function () {
OC.notify({message:t('contacts','Error loading profile picture.')});
}).attr('src', OC.linkTo('contacts', 'photo.php')+'?id='+id+refreshstr);
if(!dontloadhandlers && this.isEditable()) {
this.$photowrapper.on('mouseenter', function() {
$phototools.slideDown(200);
}).on('mouseleave', function() {
$phototools.slideUp(200);
});
$phototools.hover( function () {
$(this).removeClass('transparent');
}, function () {
$(this).addClass('transparent');
});
$phototools.find('li a').tipsy();
$phototools.find('.edit').on('click', function() {
console.log('TODO: edit photo');
$(document).trigger('request.edit.contactphoto', {
id: self.id,
});
});
$phototools.find('.cloud').on('click', function() {
console.log('select photo from cloud');
$(document).trigger('request.select.contactphoto.fromcloud', {
id: self.id,
});
});
$phototools.find('.upload').on('click', function() {
console.log('select photo from local');
$(document).trigger('request.select.contactphoto.fromlocal', {
id: self.id,
});
});
if(this.data && this.data.PHOTO) {
$phototools.find('.delete').show();
$phototools.find('.edit').show();
} else {
$phototools.find('.delete').hide();
$phototools.find('.edit').hide();
}
$(document).bind('status.contact.photoupdated', function(e, result) {
console.log('Contact - photoupdated')
self.loadPhoto(true);
var refreshstr = '&refresh='+Math.random();
self.getListItemElement().find('td.name')
.css('background', 'url(' + OC.filePath('', '', 'remote.php')+'/contactthumbnail?id='+self.id+refreshstr + ')');
});
}
}
/**
* Get the jquery element associated with this object
*/
Contact.prototype.getListItemElement = function() {
if(!this.$listelem) {
this.renderListItem();
}
return this.$listelem;
}
/**
* Get the preferred value for a property.
* If a preferred value is not found the first one will be returned.
* @param string name The name of the property like EMAIL, TEL or ADR.
* @param def A default value to return if nothing is found.
*/
Contact.prototype.getPreferredValue = function(name, def) {
var pref = def, found = false;
if(this.data && this.data[name]) {
var props = this.data[name];
//console.log('props', props);
$.each(props, function( i, prop ) {
//console.log('prop:', i, prop);
if(i === 0) { // Choose first to start with
pref = prop.value;
}
for(var param in prop.parameters) {
if(param.toUpperCase() == 'PREF') {
found = true; //
break;
}
}
if(found) {
return false; // break out of loop
}
});
}
return false;
});
$('.contacts_property').live('change', function(){
OC.Contacts.Card.saveProperty(this);
});
$(function() {
// Upload function for dropped contact photos files. Should go in the Contacts class/object.
$.fileUpload = function(files){
var file = files[0];
if(file.size > $('#max_upload').val()){
OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
return;
}
if (file.type.indexOf("image") != 0) {
OC.dialogs.alert(t('contacts','Only image files can be used as profile picture.'), t('contacts','Wrong file type'));
return;
}
var xhr = new XMLHttpRequest();
if (!xhr.upload) {
OC.dialogs.alert(t('contacts', 'Your browser doesn\'t support AJAX upload. Please click on the profile picture to select a photo to upload.'), t('contacts', 'Error'))
}
fileUpload = xhr.upload,
xhr.onreadystatechange = function() {
if (xhr.readyState == 4){
response = $.parseJSON(xhr.responseText);
if(response.status == 'success') {
if(xhr.status == 200) {
OC.Contacts.Card.editPhoto(response.data.id, response.data.tmp);
} else {
OC.dialogs.alert(xhr.status + ': ' + xhr.responseText, t('contacts', 'Error'));
}
} else {
OC.dialogs.alert(response.data.message, t('contacts', 'Error'));
}
}
};
fileUpload.onprogress = function(e){
if (e.lengthComputable){
var _progress = Math.round((e.loaded * 100) / e.total);
//if (_progress != 100){
//}
}
};
xhr.open('POST', OC.filePath('contacts', 'ajax', 'uploadphoto.php')+'?id='+OC.Contacts.Card.id+'&requesttoken='+oc_requesttoken+'&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-File-Size', file.size);
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
}
});
$(document).bind('drop dragover', function (e) {
e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone
});
//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
if(navigator.userAgent.search(/konqueror/i)==-1){
$('#import_upload_start').attr('multiple','multiple')
return pref;
}
// Import using jquery.fileupload
$(function() {
var uploadingFiles = {}, numfiles = 0, uploadedfiles = 0, retries = 0;
var aid;
$('#import_upload_start').fileupload({
dropZone: $('#contacts'), // restrict dropZone to contacts list.
acceptFileTypes: /^text\/(directory|vcard|x-vcard)$/i,
add: function(e, data) {
var files = data.files;
var totalSize=0;
if(files) {
numfiles += files.length; uploadedfiles = 0;
for(var i=0;i<files.length;i++) {
if(files[i].size ==0 && files[i].type== '') {
OC.dialogs.alert(t('files', 'Unable to upload your file as it is a directory or has 0 bytes'), t('files', 'Upload Error'));
return;
}
totalSize+=files[i].size;
}
/**
* Returns true/false depending on the contact being in the
* specified group.
* @param String name The group name (not case-sensitive)
* @returns Boolean
*/
Contact.prototype.inGroup = function(name) {
if(!this.data.CATEGORIES) {
return false;
}
categories = this.data.CATEGORIES[0].value;
for(var i in categories) {
if(typeof categories[i] === 'string' && (name.toLowerCase() === categories[i].toLowerCase())) {
return true;
}
};
return false;
}
/**
* Add this contact to a group
* @param String name The group name
*/
Contact.prototype.addToGroup = function(name) {
console.log('addToGroup', name);
if(!this.data.CATEGORIES) {
this.data.CATEGORIES = [{value:[name]},];
} else {
var found = false;
$.each(this.data.CATEGORIES[0].value, function(idx, category) {
if(name.toLowerCase() === category.toLowerCase()) {
found = true;
return false;
}
if(totalSize>$('#max_upload').val()){
OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
numfiles = uploadedfiles = retries = aid = 0;
uploadingFiles = {};
return;
}else{
if($.support.xhrFileUpload) {
for(var i=0;i<files.length;i++){
var fileName = files[i].name;
var dropTarget;
if($(e.originalEvent.target).is('h3')) {
dropTarget = $(e.originalEvent.target).next('ul');
} else {
dropTarget = $(e.originalEvent.target).closest('ul');
}
if(dropTarget && dropTarget.hasClass('contacts')) { // TODO: More thorough check for where we are.
aid = dropTarget.attr('data-id');
} else {
aid = undefined;
}
var jqXHR = $('#import_upload_start').fileupload('send', {files: files[i],
formData: function(form) {
var formArray = form.serializeArray();
formArray['aid'] = aid;
return formArray;
}})
.success(function(result, textStatus, jqXHR) {
if(result.status == 'success') {
// import the file
uploadedfiles += 1;
} else {
OC.Contacts.notify({message:jsondata.data.message});
}
return false;
})
.error(function(jqXHR, textStatus, errorThrown) {
//console.log(textStatus);
OC.Contacts.notify({message:errorThrown + ': ' + textStatus,});
});
uploadingFiles[fileName] = jqXHR;
}
} else {
data.submit().success(function(data, status) {
response = jQuery.parseJSON(data[0].body.innerText);
if(response[0] != undefined && response[0].status == 'success') {
var file=response[0];
delete uploadingFiles[file.name];
$('tr').filterAttr('data-file',file.name).data('mime',file.mime);
var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text();
if(size==t('files','Pending')){
$('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size);
}
FileList.loadingDone(file.name);
} else {
OC.Contacts.notify({message:response.data.message});
}
});
}
}
},
fail: function(e, data) {
//console.log('fail');
OC.Contacts.notify({message:data.errorThrown + ': ' + data.textStatus});
// TODO: Remove file from upload queue.
},
progressall: function(e, data) {
var progress = (data.loaded/data.total)*50;
$('#uploadprogressbar').progressbar('value',progress);
},
start: function(e, data) {
$('#uploadprogressbar').progressbar({value:0});
$('#uploadprogressbar').fadeIn();
if(data.dataType != 'iframe ') {
$('#upload input.stop').show();
}
},
stop: function(e, data) {
// stop only gets fired once so we collect uploaded items here.
var waitForImport = function() {
if(numfiles == 0 && uploadedfiles == 0) {
$('#uploadprogressbar').progressbar('value',100);
OC.Contacts.notify({message:t('contacts', 'Import done')});
OC.Contacts.Contacts.update({aid:aid});
retries = aid = 0;
$('#uploadprogressbar').fadeOut();
} else {
setTimeout(function() { //
waitForImport();
}, 1000);
}
}
var importFiles = function(aid, fileList) {
// Create a closure that can be called from different places.
if(numfiles != uploadedfiles) {
OC.Contacts.notify({message:t('contacts', 'Not all files uploaded. Retrying...')});
retries += 1;
if(retries > 3) {
numfiles = uploadedfiles = retries = aid = 0;
uploadingFiles = {};
$('#uploadprogressbar').fadeOut();
OC.dialogs.alert(t('contacts', 'Something went wrong with the upload, please retry.'), t('contacts', 'Error'));
return;
}
setTimeout(function() { // Just to let any uploads finish
importFiles(aid, uploadingFiles);
}, 1000);
}
$('#uploadprogressbar').progressbar('value',50);
var todo = uploadedfiles;
$.each(fileList, function(fileName, data) {
OC.Contacts.Contacts.doImport(fileName, aid, function() {
delete fileList[fileName];
numfiles -= 1; uploadedfiles -= 1;
$('#uploadprogressbar').progressbar('value',50+(50/(todo-uploadedfiles)));
});
})
OC.Contacts.notify({message:t('contacts', 'Importing...'), timeout:20});
waitForImport();
}
if(!aid) {
// Either selected with filepicker or dropped outside of an address book.
$.getJSON(OC.filePath('contacts', 'ajax', 'selectaddressbook.php'),{},function(jsondata) {
if(jsondata.status == 'success') {
if($('#selectaddressbook_dialog').dialog('isOpen') == true) {
$('#selectaddressbook_dialog').dialog('moveToTop');
} else {
$('#dialog_holder').html(jsondata.data.page).ready(function($) {
var select_dlg = $('#selectaddressbook_dialog');
select_dlg.dialog({
modal: true, height: 'auto', width: 'auto',
buttons: {
'Ok':function() {
aid = select_dlg.find('input:checked').val();
if(aid == 'new') {
var displayname = select_dlg.find('input.name').val();
var description = select_dlg.find('input.desc').val();
if(!displayname.trim()) {
OC.dialogs.alert(t('contacts', 'The address book name cannot be empty.'), t('contacts', 'Error'));
return false;
}
$(this).dialog('close');
OC.Contacts.Contacts.addAddressbook(displayname, description, function(addressbook) {
aid = addressbook.id;
setTimeout(function() {
importFiles(aid, uploadingFiles);
}, 500);
//console.log('aid ' + aid);
});
} else {
setTimeout(function() {
importFiles(aid, uploadingFiles);
}, 500);
//console.log('aid ' + aid);
$(this).dialog('close');
}
},
'Cancel':function() {
$(this).dialog('close');
numfiles = uploadedfiles = retries = aid = 0;
uploadingFiles = {};
$('#uploadprogressbar').fadeOut();
}
},
close: function(event, ui) {
// TODO: If numfiles != 0 delete tmp files after a timeout.
$(this).dialog('destroy').remove();
}
});
});
}
} else {
$('#uploadprogressbar').fadeOut();
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
});
});
if(found) {
return;
}
this.data.CATEGORIES[0].value.push(name);
console.log('listelem categories', this.getPreferredValue('CATEGORIES', []).clean('').join(' / '));
if(this.$listelem) {
this.$listelem.find('td.categories')
.text(this.getPreferredValue('CATEGORIES', []).clean('').join(' / '));
}
}
this.saveProperty({name:'CATEGORIES', value:this.data.CATEGORIES[0].value.join(',') });
}
/**
* Remove this contact to a group
* @param String name The group name
*/
Contact.prototype.removeFromGroup = function(name) {
console.log('removeFromGroup', name);
if(!this.data.CATEGORIES) {
return;
} else {
var found = false;
var categories = [];
$.each(this.data.CATEGORIES[0].value, function(idx, category) {
if(name.toLowerCase() === category.toLowerCase()) {
found = true;
} else {
// Dropped on an address book or it's list.
setTimeout(function() { // Just to let any uploads finish
importFiles(aid, uploadingFiles);
}, 1000);
categories.push(category);
}
if(data.dataType != 'iframe ') {
$('#upload input.stop').hide();
});
if(!found) {
return;
}
this.data.CATEGORIES[0].value = categories;
//this.data.CATEGORIES[0].value.splice(this.data.CATEGORIES[0].value.indexOf(name), 1);
if(this.$listelem) {
this.$listelem.find('td.categories')
.text(categories.join(' / '));
}
}
this.saveProperty({name:'CATEGORIES', value:this.data.CATEGORIES[0].value.join(',') });
}
Contact.prototype.setCurrent = function(on) {
if(on) {
this.$listelem.addClass('active');
} else {
this.$listelem.removeClass('active');
}
$(document).trigger('status.contact.currentlistitem', {
id: this.id,
pos: Math.round(this.$listelem.position().top),
height: Math.round(this.$listelem.height()),
});
}
Contact.prototype.next = function() {
var $next = this.$listelem.next('tr');
if($next.length > 0) {
this.$listelem.removeClass('active');
$next.addClass('active');
$(document).trigger('status.contact.currentlistitem', {
id: parseInt($next.data('id')),
pos: Math.round($next.position().top),
height: Math.round($next.height()),
});
}
}
Contact.prototype.prev = function() {
var $prev = this.$listelem.prev('tr');
if($prev.length > 0) {
this.$listelem.removeClass('active');
$prev.addClass('active');
$(document).trigger('status.contact.currentlistitem', {
id: parseInt($prev.data('id')),
pos: Math.round($prev.position().top),
height: Math.round($prev.height()),
});
}
}
var ContactList = function(contactlist, contactlistitemtemplate, contactfulltemplate, contactdetailtemplates) {
//console.log('ContactList', contactlist, contactlistitemtemplate, contactfulltemplate, contactdetailtemplates);
var self = this;
this.length = 0;
this.contacts = {};
this.deletionQueue = [];
this.$contactList = contactlist;
this.$contactListItemTemplate = contactlistitemtemplate;
this.$contactFullTemplate = contactfulltemplate;
this.contactDetailTemplates = contactdetailtemplates;
this.$contactList.scrollTop(0);
this.loadContacts(0);
$(document).bind('status.contact.added', function(e, data) {
self.contacts[parseInt(data.id)] = data.contact;
self.insertContact(data.contact.renderListItem());
});
$(document).bind('status.contact.renamed', function(e, data) {
self.insertContact(data.contact.getListItemElement().detach());
});
}
/**
* Show/hide contacts belonging to an addressbook.
* @param int aid. Addressbook id.
* @param boolean show. Whether to show or hide.
* @param boolean hideothers. Used when showing shared addressbook as a group.
*/
ContactList.prototype.showFromAddressbook = function(aid, show, hideothers) {
console.log('ContactList.showFromAddressbook', aid, show);
aid = parseInt(aid);
for(var contact in this.contacts) {
if(this.contacts[contact].access.id === aid) {
this.contacts[contact].getListItemElement().toggle(show);
} else if(hideothers) {
this.contacts[contact].getListItemElement().hide();
}
}
}
/**
* Show/hide contacts belonging to shared addressbooks.
* @param boolean show. Whether to show or hide.
*/
ContactList.prototype.showSharedAddressbooks = function(show) {
console.log('ContactList.showSharedAddressbooks', show);
for(var contact in this.contacts) {
if(this.contacts[contact].access.owner !== OC.currentUser) {
if(show) {
this.contacts[contact].getListItemElement().show();
} else {
this.contacts[contact].getListItemElement().hide();
}
}
})
});
}
}
OC.Contacts.loadHandlers();
OC.Contacts.Contacts.update({cid:id});
});
/**
* Show contacts in list
* @param Array contacts. A list of contact ids.
*/
ContactList.prototype.showContacts = function(contacts) {
if(contacts.length === 0) {
// ~5 times faster
$('tr:visible.contact').hide();
return;
}
for(var contact in this.contacts) {
if(contacts === 'all') {
this.contacts[contact].getListItemElement().show();
} else {
contact = parseInt(contact);
if(contacts.indexOf(contact) === -1) {
this.contacts[contact].getListItemElement().hide();
} else {
this.contacts[contact].getListItemElement().show();
}
}
}
}
ContactList.prototype.contactPos = function(id) {
if(!id) {
console.log('id missing');
return false;
}
var $elem = this.contacts[parseInt(id)].getListItemElement();
var pos = $elem.offset().top - this.$contactList.offset().top + this.$contactList.scrollTop();
console.log('pos', pos);
return pos;
}
ContactList.prototype.hideContact = function(id) {
this.contacts[parseInt(id)].hide();
}
ContactList.prototype.closeContact = function(id) {
this.contacts[parseInt(id)].close();
}
/**
* Jumps to an element in the contact list
* @param number the number of the item starting with 0
*/
ContactList.prototype.jumpToContact = function(id) {
var pos = this.contactPos(id);
console.log('scrollTop', pos);
this.$contactList.scrollTop(pos);
};
/**
* Returns a Contact object by searching for its id
* @param id the id of the node
* @return the Contact object or undefined if not found.
* FIXME: If continious loading is reintroduced this will have
* to load the requested contact if not in list.
*/
ContactList.prototype.findById = function(id) {
return this.contacts[parseInt(id)];
};
ContactList.prototype.delayedDelete = function(id) {
var self = this;
if(utils.isUInt(id)) {
this.currentContact = null;
self.$contactList.show();
this.deletionQueue.push(id);
} else if(utils.isArray(id)) {
$.extend(this.deletionQueue, id);
} else {
throw { name: 'WrongParameterType', message: 'ContactList.delayedDelete only accept integers or arrays.'}
}
$.each(this.deletionQueue, function(idx, id) {
self.contacts[id].detach().setChecked(false);
});
console.log('deletionQueue', this.deletionQueue);
if(!window.onbeforeunload) {
window.onbeforeunload = function(e) {
e = e || window.event;
var warn = t('contacts', 'Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted.');
if (e) {
e.returnValue = String(warn);
}
return warn;
}
}
if(this.$contactList.find('tr:visible').length === 0) {
$(document).trigger('status.visiblecontacts');
}
OC.notify({
message:t('contacts','Click to undo deletion of {num} contacts', {num: self.deletionQueue.length}),
//timeout:5,
timeouthandler:function() {
console.log('timeout');
// Don't fire all deletes at once
self.deletionTimer = setInterval('self.deleteContacts()', 500);
},
clickhandler:function() {
console.log('clickhandler');
$.each(self.deletionQueue, function(idx, id) {
self.insertContact(self.contacts[id].getListItemElement());
});
OC.notify({cancel:true});
OC.notify({message:t('contacts', 'Cancelled deletion of {num}', {num: self.deletionQueue.length})});
self.deletionQueue = [];
window.onbeforeunload = null;
}
});
}
/**
* Delete a contact with this id
* @param id the id of the contact
*/
ContactList.prototype.deleteContacts = function() {
var self = this;
console.log('ContactList.deleteContacts, deletionQueue', this.deletionQueue);
if(typeof this.deletionTimer === 'undefined') {
console.log('No deletion timer!');
window.onbeforeunload = null;
return;
}
var id = this.deletionQueue.shift();
if(typeof id === 'undefined') {
clearInterval(this.deletionTimer);
delete this.deletionTimer;
window.onbeforeunload = null;
return;
}
// Let contact remove itself.
self.contacts[id].destroy(function(response) {
console.log('deleteContact', response);
if(response.status === 'success') {
delete self.contacts[id];
$(document).trigger('status.contact.deleted', {
id: id,
});
self.length -= 1;
if(self.length === 0) {
$(document).trigger('status.nomorecontacts');
}
} else {
OC.notify({message:response.message});
}
});
}
/**
* Opens the contact with this id in edit mode
* @param id the id of the contact
* @returns A jquery object to be inserted in the DOM.
*/
ContactList.prototype.showContact = function(id) {
console.assert(typeof id === 'number', 'ContactList.showContact called with a non-number');
this.currentContact = id;
console.log('Contacts.showContact', id, this.contacts[this.currentContact], this.contacts)
return this.contacts[this.currentContact].renderContact();
};
/**
* Insert a rendered contact list item into the list
* @param contact jQuery object.
*/
ContactList.prototype.insertContact = function($contact) {
console.log('insertContact', $contact);
$contact.draggable({
distance: 10,
revert: 'invalid',
//containment: '#content',
opacity: 0.8, helper: 'clone',
zIndex: 1000,
});
var name = $contact.find('.nametext').text().toLowerCase();
var added = false
this.$contactList.find('tr').each(function() {
if ($(this).find('.nametext').text().toLowerCase().localeCompare(name) > 0) {
$(this).before($contact);
added = true;
return false;
}
});
if(!added) {
this.$contactList.append($contact);
}
$contact.show();
return $contact;
}
/**
* Add contact
*/
ContactList.prototype.addContact = function() {
var contact = new Contact(
this,
null,
{owner:OC.currentUser, permissions: 31},
null,
this.$contactListItemTemplate,
this.$contactFullTemplate,
this.contactDetailTemplates
);
if(this.currentContact) {
console.assert(typeof this.currentContact == 'number', 'this.currentContact is not a number');
this.contacts[this.currentContact].close();
}
return contact.renderContact();
}
/**
* Get contacts selected in list
*
* @returns array of integer contact ids.
*/
ContactList.prototype.getSelectedContacts = function() {
var contacts = [];
$.each(this.$contactList.find('tr > td > input:checkbox:visible:checked'), function(a, b) {
contacts.push(parseInt($(b).parents('tr').first().data('id')));
});
return contacts;
}
ContactList.prototype.setCurrent = function(id, deselect_other) {
self = this;
if(deselect_other === true) {
$.each(this.contacts, function(contact) {
self.contacts[contact].setCurrent(false);
});
}
this.contacts[parseInt(id)].setCurrent(true);
}
// Should only be neccesary with progressive loading, but it's damn fast, so... ;)
ContactList.prototype.doSort = function() {
var self = this;
var rows = this.$contactList.find('tr').get();
rows.sort(function(a, b) {
return $(a).find('td.name').text().toUpperCase().localeCompare($(b).find('td.name').text().toUpperCase());
});
$.each(rows, function(index, row) {
self.$contactList.append(row);
});
}
/**
* Load contacts
* @param int offset
*/
ContactList.prototype.loadContacts = function(offset, cb) {
var self = this;
// Should the actual ajax call be in the controller?
$.getJSON(OC.filePath('contacts', 'ajax', 'contact/list.php'), {offset: offset}, function(jsondata) {
if (jsondata && jsondata.status == 'success') {
console.log('ContactList.loadContacts', jsondata.data);
self.addressbooks = {};
$.each(jsondata.data.addressbooks, function(i, book) {
self.addressbooks[parseInt(book.id)] = {
owner: book.userid,
permissions: parseInt(book.permissions),
id: parseInt(book.id),
displayname: book.displayname,
description: book.description,
active: Boolean(parseInt(book.active)),
};
});
$.each(jsondata.data.contacts, function(c, contact) {
self.contacts[parseInt(contact.id)]
= new Contact(
self,
contact.id,
self.addressbooks[parseInt(contact.aid)],
contact.data,
self.$contactListItemTemplate,
self.$contactFullTemplate,
self.contactDetailTemplates
);
self.length +=1;
var $item = self.contacts[parseInt(contact.id)].renderListItem();
$item.draggable({
distance: 10,
revert: 'invalid',
//containment: '#content',
opacity: 0.8, helper: 'clone',
zIndex: 1000,
});
self.$contactList.append($item);
//self.insertContact(item);
});
self.doSort();
$(document).trigger('status.contacts.loaded', {
status: true,
numcontacts: jsondata.data.contacts.length
});
self.setCurrent(self.$contactList.find('tr:first-child').data('id'), false);
}
if(typeof cb === 'function') {
cb();
}
});
}
OC.Contacts.ContactList = ContactList;
})( jQuery );

View File

@ -6,18 +6,21 @@
$.widget('ui.combobox', {
options: {
id: null,
name: null,
showButton: false,
editable: true
editable: true,
singleclick: false,
},
_create: function() {
var self = this,
select = this.element.hide(),
selected = select.children(':selected'),
value = selected.val() ? selected.text() : '';
var input = this.input = $('<input type="text">')
var name = this.element.attr('name');
//this.element.attr('name', 'old_' + name)
var input = this.input = $('<input type="text" />')
.insertAfter( select )
.val( value )
//.attr('name', name)
.autocomplete({
delay: 0,
minLength: 0,
@ -80,10 +83,20 @@
self._setOption(key, value);
});
input.dblclick(function() {
// pass empty string as value to search for, displaying all results
input.autocomplete('search', '');
});
var clickHandler = function(e) {
var w = self.input.autocomplete('widget');
if(w.is(':visible')) {
self.input.autocomplete('close');
} else {
input.autocomplete('search', '');
}
}
if(this.options['singleclick'] === true) {
input.click(clickHandler);
} else {
input.dblclick(clickHandler);
}
if(this.options['showButton']) {
this.button = $('<button type="button">&nbsp;</button>')
@ -128,10 +141,6 @@
this.options['id'] = value;
this.input.attr('id', value);
break;
case 'name':
this.options['name'] = value;
this.input.attr('name', value);
break;
case 'attributes':
var input = this.input;
$.each(this.options['attributes'], function(key, value) {

View File

@ -76,6 +76,15 @@ Contacts_Import={
});
}
}
var openContact = function(id) {
if(typeof OC.Contacts !== 'undefined') {
OC.Contacts.openContact(id);
} else {
window.location.href = OC.linkTo('contacts', 'index.php') + '?id=' + id;
}
}
$(document).ready(function(){
if(typeof FileActions !== 'undefined'){
FileActions.register('text/vcard','importaddressbook', OC.PERMISSION_READ, '', Contacts_Import.importdialog);

1394
js/modernizr.js Normal file
View File

@ -0,0 +1,1394 @@
/*!
* Modernizr v2.6.3pre
* www.modernizr.com
*
* Copyright (c) Faruk Ates, Paul Irish, Alex Sexton
* Available under the BSD and MIT licenses: www.modernizr.com/license/
*/
/*
* Modernizr tests which native CSS3 and HTML5 features are available in
* the current UA and makes the results available to you in two ways:
* as properties on a global Modernizr object, and as classes on the
* <html> element. This information allows you to progressively enhance
* your pages with a granular level of control over the experience.
*
* Modernizr has an optional (not included) conditional resource loader
* called Modernizr.load(), based on Yepnope.js (yepnopejs.com).
* To get a build that includes Modernizr.load(), as well as choosing
* which tests to include, go to www.modernizr.com/download/
*
* Authors Faruk Ates, Paul Irish, Alex Sexton
* Contributors Ryan Seddon, Ben Alman
*/
window.Modernizr = (function( window, document, undefined ) {
var version = '2.6.3pre',
Modernizr = {},
/*>>cssclasses*/
// option for enabling the HTML classes to be added
enableClasses = true,
/*>>cssclasses*/
docElement = document.documentElement,
/**
* Create our "modernizr" element that we do most feature tests on.
*/
mod = 'modernizr',
modElem = document.createElement(mod),
mStyle = modElem.style,
/**
* Create the input element for various Web Forms feature tests.
*/
inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ ,
/*>>smile*/
smile = ':)',
/*>>smile*/
toString = {}.toString,
// TODO :: make the prefixes more granular
/*>>prefixes*/
// List of property values to set for css tests. See ticket #21
prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
/*>>prefixes*/
/*>>domprefixes*/
// Following spec is to expose vendor-specific style properties as:
// elem.style.WebkitBorderRadius
// and the following would be incorrect:
// elem.style.webkitBorderRadius
// Webkit ghosts their properties in lowercase but Opera & Moz do not.
// Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
// erik.eae.net/archives/2008/03/10/21.48.10/
// More here: github.com/Modernizr/Modernizr/issues/issue/21
omPrefixes = 'Webkit Moz O ms',
cssomPrefixes = omPrefixes.split(' '),
domPrefixes = omPrefixes.toLowerCase().split(' '),
/*>>domprefixes*/
/*>>ns*/
ns = {'svg': 'http://www.w3.org/2000/svg'},
/*>>ns*/
tests = {},
inputs = {},
attrs = {},
classes = [],
slice = classes.slice,
featureName, // used in testing loop
/*>>teststyles*/
// Inject element with style element and some CSS rules
injectElementWithStyles = function( rule, callback, nodes, testnames ) {
var style, ret, node, docOverflow,
div = document.createElement('div'),
// After page load injecting a fake body doesn't work so check if body exists
body = document.body,
// IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it.
fakeBody = body || document.createElement('body');
if ( parseInt(nodes, 10) ) {
// In order not to give false positives we create a node for each test
// This also allows the method to scale for unspecified uses
while ( nodes-- ) {
node = document.createElement('div');
node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
div.appendChild(node);
}
}
// <style> elements in IE6-9 are considered 'NoScope' elements and therefore will be removed
// when injected with innerHTML. To get around this you need to prepend the 'NoScope' element
// with a 'scoped' element, in our case the soft-hyphen entity as it won't mess with our measurements.
// msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx
// Documents served as xml will throw if using &shy; so use xml friendly encoded version. See issue #277
style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join('');
div.id = mod;
// IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.
// Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
(body ? div : fakeBody).innerHTML += style;
fakeBody.appendChild(div);
if ( !body ) {
//avoid crashing IE8, if background image is used
fakeBody.style.background = '';
//Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
fakeBody.style.overflow = 'hidden';
docOverflow = docElement.style.overflow;
docElement.style.overflow = 'hidden';
docElement.appendChild(fakeBody);
}
ret = callback(div, rule);
// If this is done after page load we don't want to remove the body so check if body exists
if ( !body ) {
fakeBody.parentNode.removeChild(fakeBody);
docElement.style.overflow = docOverflow;
} else {
div.parentNode.removeChild(div);
}
return !!ret;
},
/*>>teststyles*/
/*>>mq*/
// adapted from matchMedia polyfill
// by Scott Jehl and Paul Irish
// gist.github.com/786768
testMediaQuery = function( mq ) {
var matchMedia = window.matchMedia || window.msMatchMedia;
if ( matchMedia ) {
return matchMedia(mq).matches;
}
var bool;
injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
bool = (window.getComputedStyle ?
getComputedStyle(node, null) :
node.currentStyle)['position'] == 'absolute';
});
return bool;
},
/*>>mq*/
/*>>hasevent*/
//
// isEventSupported determines if a given element supports the given event
// kangax.github.com/iseventsupported/
//
// The following results are known incorrects:
// Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative
// Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333
// ...
isEventSupported = (function() {
var TAGNAMES = {
'select': 'input', 'change': 'input',
'submit': 'form', 'reset': 'form',
'error': 'img', 'load': 'img', 'abort': 'img'
};
function isEventSupported( eventName, element ) {
element = element || document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
// When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
var isSupported = eventName in element;
if ( !isSupported ) {
// If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
if ( !element.setAttribute ) {
element = document.createElement('div');
}
if ( element.setAttribute && element.removeAttribute ) {
element.setAttribute(eventName, '');
isSupported = is(element[eventName], 'function');
// If property was created, "remove it" (by setting value to `undefined`)
if ( !is(element[eventName], 'undefined') ) {
element[eventName] = undefined;
}
element.removeAttribute(eventName);
}
}
element = null;
return isSupported;
}
return isEventSupported;
})(),
/*>>hasevent*/
// TODO :: Add flag for hasownprop ? didn't last time
// hasOwnProperty shim by kangax needed for Safari 2.0 support
_hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
hasOwnProp = function (object, property) {
return _hasOwnProperty.call(object, property);
};
}
else {
hasOwnProp = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
};
}
// Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
// es5.github.com/#x15.3.4.5
if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) {
var target = this;
if (typeof target != "function") {
throw new TypeError();
}
var args = slice.call(arguments, 1),
bound = function () {
if (this instanceof bound) {
var F = function(){};
F.prototype = target.prototype;
var self = new F();
var result = target.apply(
self,
args.concat(slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return self;
} else {
return target.apply(
that,
args.concat(slice.call(arguments))
);
}
};
return bound;
};
}
/**
* setCss applies given styles to the Modernizr DOM node.
*/
function setCss( str ) {
mStyle.cssText = str;
}
/**
* setCssAll extrapolates all vendor-specific css strings.
*/
function setCssAll( str1, str2 ) {
return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
}
/**
* is returns a boolean for if typeof obj is exactly type.
*/
function is( obj, type ) {
return typeof obj === type;
}
/**
* contains returns a boolean for if substr is found within str.
*/
function contains( str, substr ) {
return !!~('' + str).indexOf(substr);
}
/*>>testprop*/
// testProps is a generic CSS / DOM property test.
// In testing support for a given CSS property, it's legit to test:
// `elem.style[styleName] !== undefined`
// If the property is supported it will return an empty string,
// if unsupported it will return undefined.
// We'll take advantage of this quick test and skip setting a style
// on our modernizr element, but instead just testing undefined vs
// empty string.
// Because the testing of the CSS property names (with "-", as
// opposed to the camelCase DOM properties) is non-portable and
// non-standard but works in WebKit and IE (but not Gecko or Opera),
// we explicitly reject properties with dashes so that authors
// developing in WebKit or IE first don't end up with
// browser-specific content by accident.
function testProps( props, prefixed ) {
for ( var i in props ) {
var prop = props[i];
if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
return prefixed == 'pfx' ? prop : true;
}
}
return false;
}
/*>>testprop*/
// TODO :: add testDOMProps
/**
* testDOMProps is a generic DOM property test; if a browser supports
* a certain property, it won't return undefined for it.
*/
function testDOMProps( props, obj, elem ) {
for ( var i in props ) {
var item = obj[props[i]];
if ( item !== undefined) {
// return the property name as a string
if (elem === false) return props[i];
// let's bind a function (and it has a bind method -- certain native objects that report that they are a
// function don't [such as webkitAudioContext])
if (is(item, 'function') && 'bind' in item){
// default to autobind unless override
return item.bind(elem || obj);
}
// return the unbound function or obj or value
return item;
}
}
return false;
}
/*>>testallprops*/
/**
* testPropsAll tests a list of DOM properties we want to check against.
* We specify literally ALL possible (known and/or likely) properties on
* the element including the non-vendor prefixed one, for forward-
* compatibility.
*/
function testPropsAll( prop, prefixed, elem ) {
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
// did they call .prefixed('boxSizing') or are we just testing a prop?
if(is(prefixed, "string") || is(prefixed, "undefined")) {
return testProps(props, prefixed);
// otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
} else {
props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
return testDOMProps(props, prefixed, elem);
}
}
/*>>testallprops*/
/**
* Tests
* -----
*/
// The *new* flexbox
// dev.w3.org/csswg/css3-flexbox
tests['flexbox'] = function() {
return testPropsAll('flexWrap');
};
// The *old* flexbox
// www.w3.org/TR/2009/WD-css3-flexbox-20090723/
tests['flexboxlegacy'] = function() {
return testPropsAll('boxDirection');
};
// On the S60 and BB Storm, getContext exists, but always returns undefined
// so we actually have to call getContext() to verify
// github.com/Modernizr/Modernizr/issues/issue/97/
tests['canvas'] = function() {
var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d'));
};
tests['canvastext'] = function() {
return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
};
// webk.it/70117 is tracking a legit WebGL feature detect proposal
// We do a soft detect which may false positive in order to avoid
// an expensive context creation: bugzil.la/732441
tests['webgl'] = function() {
return !!window.WebGLRenderingContext;
};
/*
* The Modernizr.touch test only indicates if the browser supports
* touch events, which does not necessarily reflect a touchscreen
* device, as evidenced by tablets running Windows 7 or, alas,
* the Palm Pre / WebOS (touch) phones.
*
* Additionally, Chrome (desktop) used to lie about its support on this,
* but that has since been rectified: crbug.com/36415
*
* We also test for Firefox 4 Multitouch Support.
*
* For more info, see: modernizr.github.com/Modernizr/touch.html
*/
tests['touch'] = function() {
var bool;
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
bool = true;
} else {
injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
bool = node.offsetTop === 9;
});
}
return bool;
};
// geolocation is often considered a trivial feature detect...
// Turns out, it's quite tricky to get right:
//
// Using !!navigator.geolocation does two things we don't want. It:
// 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513
// 2. Disables page caching in WebKit: webk.it/43956
//
// Meanwhile, in Firefox < 8, an about:config setting could expose
// a false positive that would throw an exception: bugzil.la/688158
tests['geolocation'] = function() {
return 'geolocation' in navigator;
};
tests['postmessage'] = function() {
return !!window.postMessage;
};
// Chrome incognito mode used to throw an exception when using openDatabase
// It doesn't anymore.
tests['websqldatabase'] = function() {
return !!window.openDatabase;
};
// Vendors had inconsistent prefixing with the experimental Indexed DB:
// - Webkit's implementation is accessible through webkitIndexedDB
// - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
// For speed, we don't test the legacy (and beta-only) indexedDB
tests['indexedDB'] = function() {
return !!testPropsAll("indexedDB", window);
};
// documentMode logic from YUI to filter out IE8 Compat Mode
// which false positives.
tests['hashchange'] = function() {
return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
};
// Per 1.6:
// This used to be Modernizr.historymanagement but the longer
// name has been deprecated in favor of a shorter and property-matching one.
// The old API is still available in 1.6, but as of 2.0 will throw a warning,
// and in the first release thereafter disappear entirely.
tests['history'] = function() {
return !!(window.history && history.pushState);
};
tests['draganddrop'] = function() {
var div = document.createElement('div');
return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
};
// FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10
// will be supported until FF19 (2/12/13), at which time, ESR becomes FF17.
// FF10 still uses prefixes, so check for it until then.
// for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/
tests['websockets'] = function() {
return 'WebSocket' in window || 'MozWebSocket' in window;
};
// css-tricks.com/rgba-browser-support/
tests['rgba'] = function() {
// Set an rgba() color and check the returned value
setCss('background-color:rgba(150,255,150,.5)');
return contains(mStyle.backgroundColor, 'rgba');
};
tests['hsla'] = function() {
// Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,
// except IE9 who retains it as hsla
setCss('background-color:hsla(120,40%,100%,.5)');
return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
};
tests['multiplebgs'] = function() {
// Setting multiple images AND a color on the background shorthand property
// and then querying the style.background property value for the number of
// occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
setCss('background:url(https://),url(https://),red url(https://)');
// If the UA supports multiple backgrounds, there should be three occurrences
// of the string "url(" in the return value for elemStyle.background
return (/(url\s*\(.*?){3}/).test(mStyle.background);
};
// this will false positive in Opera Mini
// github.com/Modernizr/Modernizr/issues/396
tests['backgroundsize'] = function() {
return testPropsAll('backgroundSize');
};
tests['borderimage'] = function() {
return testPropsAll('borderImage');
};
// Super comprehensive table about all the unique implementations of
// border-radius: muddledramblings.com/table-of-css3-border-radius-compliance
tests['borderradius'] = function() {
return testPropsAll('borderRadius');
};
// WebOS unfortunately false positives on this test.
tests['boxshadow'] = function() {
return testPropsAll('boxShadow');
};
// FF3.0 will false positive on this test
tests['textshadow'] = function() {
return document.createElement('div').style.textShadow === '';
};
tests['opacity'] = function() {
// Browsers that actually have CSS Opacity implemented have done so
// according to spec, which means their return values are within the
// range of [0.0,1.0] - including the leading zero.
setCssAll('opacity:.55');
// The non-literal . in this regex is intentional:
// German Chrome returns this value as 0,55
// github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
return (/^0.55$/).test(mStyle.opacity);
};
// Note, Android < 4 will pass this test, but can only animate
// a single property at a time
// daneden.me/2011/12/putting-up-with-androids-bullshit/
tests['cssanimations'] = function() {
return testPropsAll('animationName');
};
tests['csscolumns'] = function() {
return testPropsAll('columnCount');
};
tests['cssgradients'] = function() {
/**
* For CSS Gradients syntax, please see:
* webkit.org/blog/175/introducing-css-gradients/
* developer.mozilla.org/en/CSS/-moz-linear-gradient
* developer.mozilla.org/en/CSS/-moz-radial-gradient
* dev.w3.org/csswg/css3-images/#gradients-
*/
var str1 = 'background-image:',
str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
str3 = 'linear-gradient(left top,#9f9, white);';
setCss(
// legacy webkit syntax (FIXME: remove when syntax not in use anymore)
(str1 + '-webkit- '.split(' ').join(str2 + str1) +
// standard syntax // trailing 'background-image:'
prefixes.join(str3 + str1)).slice(0, -str1.length)
);
return contains(mStyle.backgroundImage, 'gradient');
};
tests['cssreflections'] = function() {
return testPropsAll('boxReflect');
};
tests['csstransforms'] = function() {
return !!testPropsAll('transform');
};
tests['csstransforms3d'] = function() {
var ret = !!testPropsAll('perspective');
// Webkit's 3D transforms are passed off to the browser's own graphics renderer.
// It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
// some conditions. As a result, Webkit typically recognizes the syntax but
// will sometimes throw a false positive, thus we must do a more thorough check:
if ( ret && 'webkitPerspective' in docElement.style ) {
// Webkit allows this media query to succeed only if the feature is enabled.
// `@media (transform-3d),(-webkit-transform-3d){ ... }`
injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
ret = node.offsetLeft === 9 && node.offsetHeight === 3;
});
}
return ret;
};
tests['csstransitions'] = function() {
return testPropsAll('transition');
};
/*>>fontface*/
// @font-face detection routine by Diego Perini
// javascript.nwbox.com/CSSSupport/
// false positives:
// WebOS github.com/Modernizr/Modernizr/issues/342
// WP7 github.com/Modernizr/Modernizr/issues/538
tests['fontface'] = function() {
var bool;
injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) {
var style = document.getElementById('smodernizr'),
sheet = style.sheet || style.styleSheet,
cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
});
return bool;
};
/*>>fontface*/
// CSS generated content detection
tests['generatedcontent'] = function() {
var bool;
injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) {
bool = node.offsetHeight >= 3;
});
return bool;
};
// These tests evaluate support of the video/audio elements, as well as
// testing what types of content they support.
//
// We're using the Boolean constructor here, so that we can extend the value
// e.g. Modernizr.video // true
// Modernizr.video.ogg // 'probably'
//
// Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
// thx to NielsLeenheer and zcorpan
// Note: in some older browsers, "no" was a return value instead of empty string.
// It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
// It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
tests['video'] = function() {
var elem = document.createElement('video'),
bool = false;
// IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
try {
if ( bool = !!elem.canPlayType ) {
bool = new Boolean(bool);
bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,'');
// Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
}
} catch(e) { }
return bool;
};
tests['audio'] = function() {
var elem = document.createElement('audio'),
bool = false;
try {
if ( bool = !!elem.canPlayType ) {
bool = new Boolean(bool);
bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,'');
// Mimetypes accepted:
// developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
// bit.ly/iphoneoscodecs
bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,'');
bool.m4a = ( elem.canPlayType('audio/x-m4a;') ||
elem.canPlayType('audio/aac;')) .replace(/^no$/,'');
}
} catch(e) { }
return bool;
};
// In FF4, if disabled, window.localStorage should === null.
// Normally, we could not test that directly and need to do a
// `('localStorage' in window) && ` test first because otherwise Firefox will
// throw bugzil.la/365772 if cookies are disabled
// Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
// will throw the exception:
// QUOTA_EXCEEDED_ERRROR DOM Exception 22.
// Peculiarly, getItem and removeItem calls do not throw.
// Because we are forced to try/catch this, we'll go aggressive.
// Just FWIW: IE8 Compat mode supports these features completely:
// www.quirksmode.org/dom/html5.html
// But IE8 doesn't support either with local files
tests['localstorage'] = function() {
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
} catch(e) {
return false;
}
};
tests['sessionstorage'] = function() {
try {
sessionStorage.setItem(mod, mod);
sessionStorage.removeItem(mod);
return true;
} catch(e) {
return false;
}
};
tests['webworkers'] = function() {
return !!window.Worker;
};
tests['applicationcache'] = function() {
return !!window.applicationCache;
};
// Thanks to Erik Dahlstrom
tests['svg'] = function() {
return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
};
// specifically for SVG inline in HTML, not within XHTML
// test page: paulirish.com/demo/inline-svg
tests['inlinesvg'] = function() {
var div = document.createElement('div');
div.innerHTML = '<svg/>';
return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
};
// SVG SMIL animation
tests['smil'] = function() {
return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
};
// This test is only for clip paths in SVG proper, not clip paths on HTML content
// demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg
// However read the comments to dig into applying SVG clippaths to HTML content here:
// github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491
tests['svgclippaths'] = function() {
return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
};
/*>>webforms*/
// input features and input types go directly onto the ret object, bypassing the tests loop.
// Hold this guy to execute in a moment.
function webforms() {
/*>>input*/
// Run through HTML5's new input attributes to see if the UA understands any.
// We're using f which is the <input> element created early on
// Mike Taylr has created a comprehensive resource for testing these attributes
// when applied to all input types:
// miketaylr.com/code/input-type-attr.html
// spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
// Only input placeholder is tested while textarea's placeholder is not.
// Currently Safari 4 and Opera 11 have support only for the input placeholder
// Both tests are available in feature-detects/forms-placeholder.js
Modernizr['input'] = (function( props ) {
for ( var i = 0, len = props.length; i < len; i++ ) {
attrs[ props[i] ] = !!(props[i] in inputElem);
}
if (attrs.list){
// safari false positive's on datalist: webk.it/74252
// see also github.com/Modernizr/Modernizr/issues/146
attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
}
return attrs;
})('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
/*>>input*/
/*>>inputtypes*/
// Run through HTML5's new input types to see if the UA understands any.
// This is put behind the tests runloop because it doesn't return a
// true/false like all the other tests; instead, it returns an object
// containing each input type with its corresponding true/false value
// Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/
Modernizr['inputtypes'] = (function(props) {
for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
inputElem.setAttribute('type', inputElemType = props[i]);
bool = inputElem.type !== 'text';
// We first check to see if the type we give it sticks..
// If the type does, we feed it a textual value, which shouldn't be valid.
// If the value doesn't stick, we know there's input sanitization which infers a custom UI
if ( bool ) {
inputElem.value = smile;
inputElem.style.cssText = 'position:absolute;visibility:hidden;';
if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
docElement.appendChild(inputElem);
defaultView = document.defaultView;
// Safari 2-4 allows the smiley as a value, despite making a slider
bool = defaultView.getComputedStyle &&
defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
// Mobile android web browser has false positive, so must
// check the height to see if the widget is actually there.
(inputElem.offsetHeight !== 0);
docElement.removeChild(inputElem);
} else if ( /^(search|tel)$/.test(inputElemType) ){
// Spec doesn't define any special parsing or detectable UI
// behaviors so we pass these through as true
// Interestingly, opera fails the earlier test, so it doesn't
// even make it here.
} else if ( /^(url|email)$/.test(inputElemType) ) {
// Real url and email support comes with prebaked validation.
bool = inputElem.checkValidity && inputElem.checkValidity() === false;
} else {
// If the upgraded input compontent rejects the :) text, we got a winner
bool = inputElem.value != smile;
}
}
inputs[ props[i] ] = !!bool;
}
return inputs;
})('search tel url email datetime date month week time datetime-local number range color'.split(' '));
/*>>inputtypes*/
}
/*>>webforms*/
// End of test definitions
// -----------------------
// Run through all tests and detect their support in the current UA.
// todo: hypothetically we could be doing an array of tests and use a basic loop here.
for ( var feature in tests ) {
if ( hasOwnProp(tests, feature) ) {
// run the test, throw the return value into the Modernizr,
// then based on that boolean, define an appropriate className
// and push it into an array of classes we'll join later.
featureName = feature.toLowerCase();
Modernizr[featureName] = tests[feature]();
classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
}
}
/*>>webforms*/
// input tests need to run.
Modernizr.input || webforms();
/*>>webforms*/
/**
* addTest allows the user to define their own feature tests
* the result will be added onto the Modernizr object,
* as well as an appropriate className set on the html element
*
* @param feature - String naming the feature
* @param test - Function returning true if feature is supported, false if not
*/
Modernizr.addTest = function ( feature, test ) {
if ( typeof feature == 'object' ) {
for ( var key in feature ) {
if ( hasOwnProp( feature, key ) ) {
Modernizr.addTest( key, feature[ key ] );
}
}
} else {
feature = feature.toLowerCase();
if ( Modernizr[feature] !== undefined ) {
// we're going to quit if you're trying to overwrite an existing test
// if we were to allow it, we'd do this:
// var re = new RegExp("\\b(no-)?" + feature + "\\b");
// docElement.className = docElement.className.replace( re, '' );
// but, no rly, stuff 'em.
return Modernizr;
}
test = typeof test == 'function' ? test() : test;
if (typeof enableClasses !== "undefined" && enableClasses) {
docElement.className += ' ' + (test ? '' : 'no-') + feature;
}
Modernizr[feature] = test;
}
return Modernizr; // allow chaining.
};
// Reset modElem.cssText to nothing to reduce memory footprint.
setCss('');
modElem = inputElem = null;
/*>>shiv*/
/*! HTML5 Shiv v3.6.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed */
;(function(window, document) {
/*jshint evil:true */
/** Preset options */
var options = window.html5 || {};
/** Used to skip problem elements */
var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
/** Not all elements can be cloned in IE **/
var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
/** Detect whether the browser supports default html5 styles */
var supportsHtml5Styles;
/** Name of the expando, to work with multiple documents or to re-shiv one document */
var expando = '_html5shiv';
/** The id for the the documents expando */
var expanID = 0;
/** Cached data for each document */
var expandoData = {};
/** Detect whether the browser supports unknown elements */
var supportsUnknownElements;
(function() {
try {
var a = document.createElement('a');
a.innerHTML = '<xyz></xyz>';
//if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
supportsHtml5Styles = ('hidden' in a);
supportsUnknownElements = a.childNodes.length == 1 || (function() {
// assign a false positive if unable to shiv
(document.createElement)('a');
var frag = document.createDocumentFragment();
return (
typeof frag.cloneNode == 'undefined' ||
typeof frag.createDocumentFragment == 'undefined' ||
typeof frag.createElement == 'undefined'
);
}());
} catch(e) {
supportsHtml5Styles = true;
supportsUnknownElements = true;
}
}());
/*--------------------------------------------------------------------------*/
/**
* Creates a style sheet with the given CSS text and adds it to the document.
* @private
* @param {Document} ownerDocument The document.
* @param {String} cssText The CSS text.
* @returns {StyleSheet} The style element.
*/
function addStyleSheet(ownerDocument, cssText) {
var p = ownerDocument.createElement('p'),
parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
p.innerHTML = 'x<style>' + cssText + '</style>';
return parent.insertBefore(p.lastChild, parent.firstChild);
}
/**
* Returns the value of `html5.elements` as an array.
* @private
* @returns {Array} An array of shived element node names.
*/
function getElements() {
var elements = html5.elements;
return typeof elements == 'string' ? elements.split(' ') : elements;
}
/**
* Returns the data associated to the given document
* @private
* @param {Document} ownerDocument The document.
* @returns {Object} An object of data.
*/
function getExpandoData(ownerDocument) {
var data = expandoData[ownerDocument[expando]];
if (!data) {
data = {};
expanID++;
ownerDocument[expando] = expanID;
expandoData[expanID] = data;
}
return data;
}
/**
* returns a shived element for the given nodeName and document
* @memberOf html5
* @param {String} nodeName name of the element
* @param {Document} ownerDocument The context document.
* @returns {Object} The shived element.
*/
function createElement(nodeName, ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createElement(nodeName);
}
if (!data) {
data = getExpandoData(ownerDocument);
}
var node;
if (data.cache[nodeName]) {
node = data.cache[nodeName].cloneNode();
} else if (saveClones.test(nodeName)) {
node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
} else {
node = data.createElem(nodeName);
}
// Avoid adding some elements to fragments in IE < 9 because
// * Attributes like `name` or `type` cannot be set/changed once an element
// is inserted into a document/fragment
// * Link elements with `src` attributes that are inaccessible, as with
// a 403 response, will cause the tab/window to crash
// * Script elements appended to fragments will execute when their `src`
// or `text` property is set
return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;
}
/**
* returns a shived DocumentFragment for the given document
* @memberOf html5
* @param {Document} ownerDocument The context document.
* @returns {Object} The shived DocumentFragment.
*/
function createDocumentFragment(ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createDocumentFragment();
}
data = data || getExpandoData(ownerDocument);
var clone = data.frag.cloneNode(),
i = 0,
elems = getElements(),
l = elems.length;
for(;i<l;i++){
clone.createElement(elems[i]);
}
return clone;
}
/**
* Shivs the `createElement` and `createDocumentFragment` methods of the document.
* @private
* @param {Document|DocumentFragment} ownerDocument The document.
* @param {Object} data of the document.
*/
function shivMethods(ownerDocument, data) {
if (!data.cache) {
data.cache = {};
data.createElem = ownerDocument.createElement;
data.createFrag = ownerDocument.createDocumentFragment;
data.frag = data.createFrag();
}
ownerDocument.createElement = function(nodeName) {
//abort shiv
if (!html5.shivMethods) {
return data.createElem(nodeName);
}
return createElement(nodeName, ownerDocument, data);
};
ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
'var n=f.cloneNode(),c=n.createElement;' +
'h.shivMethods&&(' +
// unroll the `createElement` calls
getElements().join().replace(/\w+/g, function(nodeName) {
data.createElem(nodeName);
data.frag.createElement(nodeName);
return 'c("' + nodeName + '")';
}) +
');return n}'
)(html5, data.frag);
}
/*--------------------------------------------------------------------------*/
/**
* Shivs the given document.
* @memberOf html5
* @param {Document} ownerDocument The document to shiv.
* @returns {Document} The shived document.
*/
function shivDocument(ownerDocument) {
if (!ownerDocument) {
ownerDocument = document;
}
var data = getExpandoData(ownerDocument);
if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
data.hasCSS = !!addStyleSheet(ownerDocument,
// corrects block display not defined in IE6/7/8/9
'article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}' +
// adds styling not present in IE6/7/8/9
'mark{background:#FF0;color:#000}'
);
}
if (!supportsUnknownElements) {
shivMethods(ownerDocument, data);
}
return ownerDocument;
}
/*--------------------------------------------------------------------------*/
/**
* The `html5` object is exposed so that more elements can be shived and
* existing shiving can be detected on iframes.
* @type Object
* @example
*
* // options can be changed before the script is included
* html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
*/
var html5 = {
/**
* An array or space separated string of node names of the elements to shiv.
* @memberOf html5
* @type Array|String
*/
'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video',
/**
* A flag to indicate that the HTML5 style sheet should be inserted.
* @memberOf html5
* @type Boolean
*/
'shivCSS': (options.shivCSS !== false),
/**
* Is equal to true if a browser supports creating unknown/HTML5 elements
* @memberOf html5
* @type boolean
*/
'supportsUnknownElements': supportsUnknownElements,
/**
* A flag to indicate that the document's `createElement` and `createDocumentFragment`
* methods should be overwritten.
* @memberOf html5
* @type Boolean
*/
'shivMethods': (options.shivMethods !== false),
/**
* A string to describe the type of `html5` object ("default" or "default print").
* @memberOf html5
* @type String
*/
'type': 'default',
// shivs the document according to the specified `html5` object options
'shivDocument': shivDocument,
//creates a shived element
createElement: createElement,
//creates a shived documentFragment
createDocumentFragment: createDocumentFragment
};
/*--------------------------------------------------------------------------*/
// expose html5
window.html5 = html5;
// shiv the document
shivDocument(document);
}(this, document));
/*>>shiv*/
// Assign private properties to the return object with prefix
Modernizr._version = version;
// expose these for the plugin API. Look in the source for how to join() them against your input
/*>>prefixes*/
Modernizr._prefixes = prefixes;
/*>>prefixes*/
/*>>domprefixes*/
Modernizr._domPrefixes = domPrefixes;
Modernizr._cssomPrefixes = cssomPrefixes;
/*>>domprefixes*/
/*>>mq*/
// Modernizr.mq tests a given media query, live against the current state of the window
// A few important notes:
// * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false
// * A max-width or orientation query will be evaluated against the current state, which may change later.
// * You must specify values. Eg. If you are testing support for the min-width media query use:
// Modernizr.mq('(min-width:0)')
// usage:
// Modernizr.mq('only screen and (max-width:768)')
Modernizr.mq = testMediaQuery;
/*>>mq*/
/*>>hasevent*/
// Modernizr.hasEvent() detects support for a given event, with an optional element to test on
// Modernizr.hasEvent('gesturestart', elem)
Modernizr.hasEvent = isEventSupported;
/*>>hasevent*/
/*>>testprop*/
// Modernizr.testProp() investigates whether a given style property is recognized
// Note that the property names must be provided in the camelCase variant.
// Modernizr.testProp('pointerEvents')
Modernizr.testProp = function(prop){
return testProps([prop]);
};
/*>>testprop*/
/*>>testallprops*/
// Modernizr.testAllProps() investigates whether a given style property,
// or any of its vendor-prefixed variants, is recognized
// Note that the property names must be provided in the camelCase variant.
// Modernizr.testAllProps('boxSizing')
Modernizr.testAllProps = testPropsAll;
/*>>testallprops*/
/*>>teststyles*/
// Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards
// Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })
Modernizr.testStyles = injectElementWithStyles;
/*>>teststyles*/
/*>>prefixed*/
// Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input
// Modernizr.prefixed('boxSizing') // 'MozBoxSizing'
// Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.
// Return values will also be the camelCase variant, if you need to translate that to hypenated style use:
//
// str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
// If you're trying to ascertain which transition end event to bind to, you might do something like...
//
// var transEndEventNames = {
// 'WebkitTransition' : 'webkitTransitionEnd',
// 'MozTransition' : 'transitionend',
// 'OTransition' : 'oTransitionEnd',
// 'msTransition' : 'MSTransitionEnd',
// 'transition' : 'transitionend'
// },
// transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
Modernizr.prefixed = function(prop, obj, elem){
if(!obj) {
return testPropsAll(prop, 'pfx');
} else {
// Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
return testPropsAll(prop, obj, elem);
}
};
/*>>prefixed*/
/*>>cssclasses*/
// Remove "no-js" class from <html> element, if it exists:
docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
// Add the new classes to the <html> element.
(enableClasses ? ' js ' + classes.join(' ') : '');
/*>>cssclasses*/
return Modernizr;
})(this, this.document);

View File

@ -0,0 +1,212 @@
/**
* HTML5 placeholder polyfill
* @requires jQuery - tested with 1.6.2 but might as well work with older versions
*
* code: https://github.com/ginader/HTML5-placeholder-polyfill
* please report issues at: https://github.com/ginader/HTML5-placeholder-polyfill/issues
*
* Copyright (c) 2012 Dirk Ginader (ginader.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Version: 2.0.3
*
* History:
* * 1.0 initial release
* * 1.1 added support for multiline placeholders in textareas
* * 1.2 Allow label to wrap the input element by noah https://github.com/ginader/HTML5-placeholder-polyfill/pull/1
* * 1.3 New option to read placeholder to Screenreaders. Turned on by default
* * 1.4 made placeholder more rubust to allow labels being offscreen + added minified version of the 3rd party libs
* * 1.5 emptying the native placeholder to prevent double rendering in Browsers with partial support
* * 1.6 optional reformat when a textarea is being resized - requires http://benalman.com/projects/jquery-resize-plugin/
* * 1.7 feature detection is now included in the polyfill so you can simply include it without the need for Modernizr
* * 1.8 replacing the HTML5 Boilerplate .visuallyhidden technique with one that still allows the placeholder to be rendered
* * 1.8.1 bugfix for implicit labels
* * 1.9 New option "hideOnFocus" which, if set to false will mimic the behavior of mobile safari and chrome (remove label when typed instead of onfocus)
* * 1.9.1 added reformat event on window resize
* * 1.9.2 more flexible way to "fix" labels that are hidden using clip() thanks to grahambates: https://github.com/ginader/HTML5-placeholder-polyfill/issues/12
* * 2.0 new easier configuration technique and new options forceApply and AutoInit and support for setters and getters
* * 2.0.1 changed check for empty field so a space character is no longer ignored
* * 2.0.2 allow rerun of the placeholder() to cover generated elements - existing polyfilled placeholder will be repositioned. Fixing: https://github.com/ginader/HTML5-placeholder-polyfill/issues/15
* * 2.0.3 turn debugging of for production. fix https://github.com/ginader/HTML5-placeholder-polyfill/issues/18
*/
(function($) {
var debug = false,
animId;
function showPlaceholderIfEmpty(input,options) {
if( input.val() === '' ){
input.data('placeholder').removeClass(options.hideClass);
}else{
input.data('placeholder').addClass(options.hideClass);
}
}
function hidePlaceholder(input,options){
input.data('placeholder').addClass(options.hideClass);
}
function positionPlaceholder(placeholder,input){
var ta = input.is('textarea');
placeholder.css({
width : input.innerWidth()-(ta ? 20 : 4),
height : input.innerHeight()-6,
lineHeight : input.css('line-height'),
whiteSpace : ta ? 'normal' : 'nowrap',
overflow : 'hidden'
}).offset(input.offset());
}
function startFilledCheckChange(input,options){
var input = input,
val = input.val();
(function checkloop(){
animId = requestAnimationFrame(checkloop);
if(input.val() != val){
hidePlaceholder(input,options);
stopCheckChange();
startEmptiedCheckChange(input,options);
}
})();
}
function startEmptiedCheckChange(input,options){
var input = input,
val = input.val();
(function checkloop(){
animId = requestAnimationFrame(checkloop);
showPlaceholderIfEmpty(input,options);
})();
}
function stopCheckChange(){
cancelAnimationFrame(animId);
}
function log(msg){
if(debug && window.console && window.console.log){
window.console.log(msg);
}
}
$.fn.placeHolder = function(config) {
log('init placeHolder');
var o = this;
var l = $(this).length;
this.options = $.extend({
className: 'placeholder', // css class that is used to style the placeholder
visibleToScreenreaders : true, // expose the placeholder text to screenreaders or not
visibleToScreenreadersHideClass : 'placeholder-hide-except-screenreader', // css class is used to visually hide the placeholder
visibleToNoneHideClass : 'placeholder-hide', // css class used to hide the placeholder for all
hideOnFocus : false, // either hide the placeholder on focus or on type
removeLabelClass : 'visuallyhidden', // remove this class from a label (to fix hidden labels)
hiddenOverrideClass : 'visuallyhidden-with-placeholder', // replace the label above with this class
forceHiddenOverride : true, // allow the replace of the removeLabelClass with hiddenOverrideClass or not
forceApply : false, // apply the polyfill even for browser with native support
autoInit : true // init automatically or not
}, config);
this.options.hideClass = this.options.visibleToScreenreaders ? this.options.visibleToScreenreadersHideClass : this.options.visibleToNoneHideClass;
return $(this).each(function(index) {
var input = $(this),
text = input.attr('placeholder'),
id = input.attr('id'),
label,placeholder,titleNeeded,polyfilled;
label = input.closest('label');
input.removeAttr('placeholder');
if(!label.length && !id){
log('the input element with the placeholder needs an id!');
return;
}
label = label.length ? label : $('label[for="'+id+'"]').first();
if(!label.length){
log('the input element with the placeholder needs a label!');
return;
}
polyfilled = $(label).find('.placeholder');
if(polyfilled.length) {
//log('the input element already has a polyfilled placeholder!');
positionPlaceholder(polyfilled,input);
return input;
}
if(label.hasClass(o.options.removeLabelClass)){
label.removeClass(o.options.removeLabelClass)
.addClass(o.options.hiddenOverrideClass);
}
placeholder = $('<span class="'+o.options.className+'">'+text+'</span>').appendTo(label);
titleNeeded = (placeholder.width() > input.width());
if(titleNeeded){
placeholder.attr('title',text);
}
positionPlaceholder(placeholder,input);
input.data('placeholder',placeholder);
placeholder.data('input',placeholder);
placeholder.click(function(){
$(this).data('input').focus();
});
input.focusin(function() {
if(!o.options.hideOnFocus && window.requestAnimationFrame){
startFilledCheckChange(input,o.options);
}else{
hidePlaceholder(input,o.options);
}
});
input.focusout(function(){
showPlaceholderIfEmpty($(this),o.options);
if(!o.options.hideOnFocus && window.cancelAnimationFrame){
stopCheckChange();
}
});
showPlaceholderIfEmpty(input,o.options);
// reformat on window resize and optional reformat on font resize - requires: http://www.tomdeater.com/jquery/onfontresize/
$(document).bind("fontresize resize", function(){
positionPlaceholder(placeholder,input);
});
// optional reformat when a textarea is being resized - requires http://benalman.com/projects/jquery-resize-plugin/
if($.event.special.resize){
$("textarea").bind("resize", function(e){
positionPlaceholder(placeholder,input);
});
}else{
// we simply disable the resizeablilty of textareas when we can't react on them resizing
$("textarea").css('resize','none');
}
if(index >= l-1){
$.attrHooks.placeholder = {
get: function(elem) {
if (elem.nodeName.toLowerCase() == 'input' || elem.nodeName.toLowerCase() == 'textarea') {
if( $(elem).data('placeholder') ){
// has been polyfilled
return $( $(elem).data('placeholder') ).text();
}else{
// native / not yet polyfilled
return $(elem)[0].placeholder;
}
}else{
return undefined;
}
},
set: function(elem, value){
return $( $(elem).data('placeholder') ).text(value);
}
};
}
});
};
$(function(){
var config = window.placeHolderConfig || {};
if(config.autoInit === false){
log('placeholder:abort because autoInit is off');
return
}
if('placeholder' in $('<input>')[0] && !config.forceApply){ // don't run the polyfill when the browser has native support
log('placeholder:abort because browser has native support');
return;
}
$('input[placeholder], textarea[placeholder]').placeHolder(config);
});
})(jQuery);

View File

@ -15,12 +15,11 @@ OC.Contacts.Settings = OC.Contacts.Settings || {
var active = tgt.is(':checked');
//console.log('doActivate: ', id, active);
$.post(OC.filePath('contacts', 'ajax', 'addressbook/activate.php'), {id: id, active: Number(active)}, function(jsondata) {
if (jsondata.status == 'success'){
if(!active) {
$('#contacts h3[data-id="'+id+'"],#contacts ul[data-id="'+id+'"]').remove();
} else {
OC.Contacts.Contacts.update();
}
if (jsondata.status == 'success') {
$(document).trigger('request.addressbook.activate', {
id: id,
activate: active,
});
} else {
//console.log('Error:', jsondata.data.message);
OC.Contacts.notify(t('contacts', 'Error') + ': ' + jsondata.data.message);
@ -41,7 +40,7 @@ OC.Contacts.Settings = OC.Contacts.Settings || {
$('#contacts h3[data-id="'+id+'"],#contacts ul[data-id="'+id+'"]').remove();
row.remove()
OC.Contacts.Settings.Addressbook.showActions(['new',]);
OC.Contacts.Contacts.update();
OC.Contacts.update();
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
@ -108,7 +107,7 @@ OC.Contacts.Settings = OC.Contacts.Settings || {
row.find('td.name').text(jsondata.data.addressbook.displayname);
row.find('td.description').text(jsondata.data.addressbook.description);
}
OC.Contacts.Contacts.update();
OC.Contacts.update();
} else {
OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
}
@ -157,11 +156,9 @@ $(document).ready(function() {
event.preventDefault();
if(OC.Contacts.Settings.Addressbook.adrsettings.is(':visible')) {
OC.Contacts.Settings.Addressbook.adrsettings.slideUp();
OC.Contacts.Settings.Addressbook.adrsettings.prev('dt').hide();
moreless.text(t('contacts', 'More...'));
} else {
OC.Contacts.Settings.Addressbook.adrsettings.slideDown();
OC.Contacts.Settings.Addressbook.adrsettings.prev('dt').show();
moreless.text(t('contacts', 'Less...'));
}
});

View File

@ -3,7 +3,14 @@
"Cannot add empty property." => "لا يمكنك اضافه صفه خاليه.",
"At least one of the address fields has to be filled out." => "يجب ملء على الاقل خانه واحده من العنوان.",
"Information about vCard is incorrect. Please reload the page." => "المعلومات الموجودة في ال vCard غير صحيحة. الرجاء إعادة تحديث الصفحة.",
"There is no error, the file uploaded with success" => "تم ترفيع الملفات بنجاح.",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "حجم الملف الذي تريد ترفيعه أعلى مما upload_max_filesize يسمح به في ملف php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "حجم الملف الذي تريد ترفيعه أعلى مما MAX_FILE_SIZE يسمح به في واجهة ال HTML.",
"The uploaded file was only partially uploaded" => "تم ترفيع جزء من الملفات الذي تريد ترفيعها فقط",
"No file was uploaded" => "لم يتم ترفيع أي من الملفات",
"Missing a temporary folder" => "المجلد المؤقت غير موجود",
"Contacts" => "المعارف",
"Upload too large" => "حجم الترفيع أعلى من المسموح",
"Download" => "انزال",
"Edit" => "تعديل",
"Delete" => "حذف",
@ -12,16 +19,21 @@
"Contact could not be found." => "لم يتم العثور على الشخص.",
"Work" => "الوظيفة",
"Home" => "البيت",
"Other" => "شيء آخر",
"Mobile" => "الهاتف المحمول",
"Text" => "معلومات إضافية",
"Voice" => "صوت",
"Fax" => "الفاكس",
"Video" => "الفيديو",
"Pager" => "الرنان",
"Birthday" => "تاريخ الميلاد",
"Contact" => "معرفه",
"Add Contact" => "أضف شخص ",
"Import" => "إدخال",
"Settings" => "اعدادات",
"Close" => "اغلق",
"Organization" => "المؤسسة",
"Birthday" => "تاريخ الميلاد",
"Groups" => "مجموعات",
"Preferred" => "مفضل",
"Phone" => "الهاتف",
"Email" => "البريد الالكتروني",
@ -36,7 +48,12 @@
"Zipcode" => "رقم المنطقة",
"Country" => "البلد",
"Addressbook" => "كتاب العناوين",
"more info" => "مزيد من المعلومات",
"Primary address (Kontact et al)" => "العنوان الرئيسي (جهات الإتصال)",
"iOS/OS X" => "ط ن ت/ ن ت 10",
"Addressbooks" => "كتب العناوين",
"Share" => "شارك",
"New Address Book" => "كتاب عناوين جديد",
"Name" => "اسم",
"Save" => "حفظ"
);

23
l10n/bg_BG.php Normal file
View File

@ -0,0 +1,23 @@
<?php $TRANSLATIONS = array(
"No categories selected for deletion." => "Няма избрани категории за изтриване",
"There is no error, the file uploaded with success" => "Файлът е качен успешно",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Файлът който се опитвате да качите, надвишава зададените стойности в upload_max_filesize в PHP.INI",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Файлът който се опитвате да качите надвишава стойностите в MAX_FILE_SIZE в HTML формата.",
"The uploaded file was only partially uploaded" => "Файлът е качен частично",
"No file was uploaded" => "Фахлът не бе качен",
"Missing a temporary folder" => "Липсва временната папка",
"Error" => "Грешка",
"Upload Error" => "Грешка при качване",
"Download" => "Изтегляне",
"Delete" => "Изтриване",
"Cancel" => "Отказ",
"Work" => "Работа",
"Other" => "Друго",
"Import" => "Внасяне",
"Birthday" => "Роджен ден",
"Groups" => "Групи",
"Email" => "Е-поща",
"Address" => "Адрес",
"Share" => "Споделяне",
"Save" => "Запис"
);

View File

@ -126,19 +126,8 @@
"Video" => "Vídeo",
"Pager" => "Paginador",
"Internet" => "Internet",
"Birthday" => "Aniversari",
"Business" => "Negocis",
"Call" => "Trucada",
"Clients" => "Clients",
"Deliverer" => "Emissari",
"Holidays" => "Vacances",
"Ideas" => "Idees",
"Journey" => "Viatge",
"Jubilee" => "Aniversari",
"Meeting" => "Reunió",
"Personal" => "Personal",
"Projects" => "Projectes",
"Questions" => "Preguntes",
"Friends" => "Amics",
"Family" => "Familia",
"{name}'s Birthday" => "Aniversari de {name}",
"Contact" => "Contacte",
"You do not have the permissions to add contacts to this addressbook." => "No teniu permisos per afegir contactes a aquesta llibreta d'adreces.",
@ -176,6 +165,7 @@
"Web site" => "Adreça web",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Vés a la web",
"Birthday" => "Aniversari",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Grups",
"Separate groups with commas" => "Separeu els grups amb comes",

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Narozeniny",
"Business" => "Pracovní",
"Call" => "Volat",
"Clients" => "Klienti",
"Deliverer" => "Dodavatel",
"Holidays" => "Svátky",
"Ideas" => "Nápady",
"Journey" => "Cestování",
"Jubilee" => "Jubileum",
"Meeting" => "Schůze",
"Personal" => "Osobní",
"Projects" => "Projekty",
"Questions" => "Otázky",
"Friends" => "Přátelé",
"Family" => "Rodina",
"{name}'s Birthday" => "Narozeniny {name}",
"Contact" => "Kontakt",
"You do not have the permissions to add contacts to this addressbook." => "Nemáte práva pro přidání kontaktů do této knihy adres.",
@ -176,6 +165,7 @@
"Web site" => "Webová stránka",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Přejít na webovou stránku",
"Birthday" => "Narozeniny",
"dd-mm-yyyy" => "dd. mm. yyyy",
"Groups" => "Skupiny",
"Separate groups with commas" => "Oddělte skupiny čárkami",

View File

@ -126,19 +126,6 @@
"Video" => "Video",
"Pager" => "Personsøger",
"Internet" => "Internet",
"Birthday" => "Fødselsdag",
"Business" => "Firma",
"Call" => "Ring op",
"Clients" => "Klienter",
"Deliverer" => "Udbringer",
"Holidays" => "Ferie",
"Ideas" => "Ideer",
"Journey" => "Rejse",
"Jubilee" => "Jubilæum",
"Meeting" => "Møde",
"Personal" => "Personligt",
"Projects" => "Projekter",
"Questions" => "Spørgsmål",
"{name}'s Birthday" => "{name}s fødselsdag",
"Contact" => "Kontaktperson",
"You do not have the permissions to add contacts to this addressbook." => "Du har ikke rettigheder til at tilføje kontaktpersoner til denne adressebog",
@ -176,6 +163,7 @@
"Web site" => "Hjemmeside",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Gå til web site",
"Birthday" => "Fødselsdag",
"dd-mm-yyyy" => "dd-mm-åååå",
"Groups" => "Grupper",
"Separate groups with commas" => "Opdel gruppenavne med kommaer",

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Geburtstag",
"Business" => "Geschäftlich",
"Call" => "Anruf",
"Clients" => "Kunden",
"Deliverer" => "Lieferant",
"Holidays" => "Feiertage",
"Ideas" => "Ideen",
"Journey" => "Reise",
"Jubilee" => "Jubiläum",
"Meeting" => "Besprechung",
"Personal" => "Persönlich",
"Projects" => "Projekte",
"Questions" => "Fragen",
"Friends" => "Freunde",
"Family" => "Familie",
"{name}'s Birthday" => "Geburtstag von {name}",
"Contact" => "Kontakt",
"You do not have the permissions to add contacts to this addressbook." => "Du besitzt nicht die erforderlichen Rechte, diesem Adressbuch Kontakte hinzuzufügen.",
@ -176,6 +165,7 @@
"Web site" => "Webseite",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Webseite aufrufen",
"Birthday" => "Geburtstag",
"dd-mm-yyyy" => "dd.mm.yyyy",
"Groups" => "Gruppen",
"Separate groups with commas" => "Gruppen mit Komma getrennt",

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Geburtstag",
"Business" => "Geschäftlich",
"Call" => "Anruf",
"Clients" => "Kunden",
"Deliverer" => "Lieferant",
"Holidays" => "Feiertage",
"Ideas" => "Ideen",
"Journey" => "Reise",
"Jubilee" => "Jubiläum",
"Meeting" => "Besprechung",
"Personal" => "Persönlich",
"Projects" => "Projekte",
"Questions" => "Fragen",
"Friends" => "Freunde",
"Family" => "Familie",
"{name}'s Birthday" => "Geburtstag von {name}",
"Contact" => "Kontakt",
"You do not have the permissions to add contacts to this addressbook." => "Sie besitzen nicht die erforderlichen Rechte, diesem Adressbuch Kontakte hinzuzufügen.",
@ -176,6 +165,7 @@
"Web site" => "Webseite",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Webseite aufrufen",
"Birthday" => "Geburtstag",
"dd-mm-yyyy" => "dd.mm.yyyy",
"Groups" => "Gruppen",
"Separate groups with commas" => "Gruppen mit Komma getrennt",

View File

@ -126,19 +126,8 @@
"Video" => "Βίντεο",
"Pager" => "Βομβητής",
"Internet" => "Διαδίκτυο",
"Birthday" => "Γενέθλια",
"Business" => "Επιχείρηση",
"Call" => "Κάλεσε",
"Clients" => "Πελάτες",
"Deliverer" => "Προμηθευτής",
"Holidays" => "Διακοπές",
"Ideas" => "Ιδέες",
"Journey" => "Ταξίδι",
"Jubilee" => "Ιωβηλαίο",
"Meeting" => "Συνάντηση",
"Personal" => "Προσωπικό",
"Projects" => "Έργα",
"Questions" => "Ερωτήσεις",
"Friends" => "Φίλοι",
"Family" => "Οικογένεια",
"{name}'s Birthday" => "Τα Γεννέθλια του/της {name}",
"Contact" => "Επαφή",
"You do not have the permissions to add contacts to this addressbook." => "Δεν έχετε δικαιώματα να προσθέσετε επαφές σε αυτό το βιβλίο διευθύνσεων.",
@ -176,6 +165,7 @@
"Web site" => "Ιστότοπος",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Πήγαινε στον ιστότοπο",
"Birthday" => "Γενέθλια",
"dd-mm-yyyy" => "ΗΗ-ΜΜ-ΕΕΕΕ",
"Groups" => "Ομάδες",
"Separate groups with commas" => "Διαχώρισε τις ομάδες με κόμμα ",

View File

@ -73,6 +73,7 @@
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "Via foliumilo ne kongruas kun AJAX-alŝutado. Bonvolu klaki la profilbildon por elekti foton alŝutotan.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Ne eblis alŝuti vian dosieron ĉar ĝi estas dosierujo aŭ havas 0 duumokojn",
"Upload Error" => "Eraro dum alŝuto",
"Pending" => "Traktotaj",
"Import done" => "Enporto plenumiĝis",
"Not all files uploaded. Retrying..." => "Ne ĉiuj dosieroj alŝutiĝis. Reprovante...",
"Something went wrong with the upload, please retry." => "Io malsukcesis dum alŝuto, bonvolu reprovi.",
@ -81,6 +82,7 @@
"Result: " => "Rezulto: ",
" imported, " => " enportoj, ",
" failed." => "malsukcesoj.",
"Displayname cannot be empty." => "Montronomo devas ne esti malplena.",
"Show CardDav link" => "Montri CardDav-ligilon",
"Show read-only VCF link" => "Montri nur legeblan VCF-ligilon",
"Download" => "Elŝuti",
@ -120,19 +122,6 @@
"Video" => "Videaĵo",
"Pager" => "Televokilo",
"Internet" => "Interreto",
"Birthday" => "Naskiĝotago",
"Business" => "Negoco",
"Call" => "Voko",
"Clients" => "Klientoj",
"Deliverer" => "Liveranto",
"Holidays" => "Ferioj",
"Ideas" => "Ideoj",
"Journey" => "Vojaĝo",
"Jubilee" => "Jubileo",
"Meeting" => "Kunveno",
"Personal" => "Persona",
"Projects" => "Projektoj",
"Questions" => "Demandoj",
"{name}'s Birthday" => "Naskiĝtago de {name}",
"Contact" => "Kontakto",
"You do not have the permissions to add contacts to this addressbook." => "Vi ne havas la permeson aldoni kontaktojn al ĉi tiu adresaro.",
@ -167,6 +156,7 @@
"Web site" => "TTT-ejo",
"http://www.somesite.com" => "http://www.iuejo.com",
"Go to web site" => "Iri al TTT-ejon",
"Birthday" => "Naskiĝotago",
"dd-mm-yyyy" => "yyyy-mm-dd",
"Groups" => "Grupoj",
"Separate groups with commas" => "Disigi grupojn per komoj",

View File

@ -126,19 +126,8 @@
"Video" => "Vídeo",
"Pager" => "Localizador",
"Internet" => "Internet",
"Birthday" => "Cumpleaños",
"Business" => "Negocio",
"Call" => "Llamada",
"Clients" => "Clientes",
"Deliverer" => "Mensajero",
"Holidays" => "Vacaciones",
"Ideas" => "Ideas",
"Journey" => "Jornada",
"Jubilee" => "Aniversario",
"Meeting" => "Reunión",
"Personal" => "Personal",
"Projects" => "Proyectos",
"Questions" => "Preguntas",
"Friends" => "Amigos",
"Family" => "Familia",
"{name}'s Birthday" => "Cumpleaños de {name}",
"Contact" => "Contacto",
"You do not have the permissions to add contacts to this addressbook." => "No tiene permisos para añadir contactos a esta libreta de direcciones.",
@ -176,6 +165,7 @@
"Web site" => "Sitio Web",
"http://www.somesite.com" => "http://www.unsitio.com",
"Go to web site" => "Ir al sitio Web",
"Birthday" => "Cumpleaños",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Grupos",
"Separate groups with commas" => "Separa los grupos con comas",

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Cumpleaños",
"Business" => "Negocio",
"Call" => "Llamar",
"Clients" => "Clientes",
"Deliverer" => "Distribuidor",
"Holidays" => "Vacaciones",
"Ideas" => "Ideas",
"Journey" => "Viaje",
"Jubilee" => "Aniversario",
"Meeting" => "Reunión",
"Personal" => "Personal",
"Projects" => "Proyectos",
"Questions" => "Preguntas",
"Friends" => "Amigos",
"Family" => "Familia",
"{name}'s Birthday" => "Cumpleaños de {name}",
"Contact" => "Contacto",
"You do not have the permissions to add contacts to this addressbook." => "No tenés permisos para agregar contactos a esta agenda.",
@ -176,6 +165,7 @@
"Web site" => "Página web",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Ir al sitio web",
"Birthday" => "Cumpleaños",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Grupos",
"Separate groups with commas" => "Separá los grupos con comas",

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Piipar",
"Internet" => "Internet",
"Birthday" => "Sünnipäev",
"Business" => "Ettevõte",
"Call" => "Helista",
"Clients" => "Kliendid",
"Deliverer" => "Kohaletoimetaja",
"Holidays" => "Puhkused",
"Ideas" => "Ideed",
"Journey" => "Teekond",
"Jubilee" => "Juubel",
"Meeting" => "Kohtumine",
"Personal" => "Isiklik",
"Projects" => "Projektid",
"Questions" => "Küsimused",
"Friends" => "Sõbrad",
"Family" => "Pereliikmed",
"{name}'s Birthday" => "{name} sünnipäev",
"Contact" => "Kontakt",
"You do not have the permissions to add contacts to this addressbook." => "Sul pole õigusi sellesse aadressiraamatusse kontaktide lisamiseks.",
@ -176,6 +165,7 @@
"Web site" => "Veebisait",
"http://www.somesite.com" => "http://www.mingisait.ee",
"Go to web site" => "Mine veebisaidile",
"Birthday" => "Sünnipäev",
"dd-mm-yyyy" => "dd.mm.yyyy",
"Groups" => "Grupid",
"Separate groups with commas" => "Eralda grupid komadega",

View File

@ -124,19 +124,6 @@
"Video" => "Bideoa",
"Pager" => "Bilagailua",
"Internet" => "Internet",
"Birthday" => "Jaioteguna",
"Business" => "Negozioak",
"Call" => "Deia",
"Clients" => "Bezeroak",
"Deliverer" => "Banatzailea",
"Holidays" => "Oporrak",
"Ideas" => "Ideiak",
"Journey" => "Bidaia",
"Jubilee" => "Urteurrenak",
"Meeting" => "Bilera",
"Personal" => "Pertsonala",
"Projects" => "Proiektuak",
"Questions" => "Galderak",
"{name}'s Birthday" => "{name}ren jaioteguna",
"Contact" => "Kontaktua",
"You do not have the permissions to add contacts to this addressbook." => "Ez duzu helbide-liburu honetara kontaktuak gehitzeko baimenik.",
@ -174,6 +161,7 @@
"Web site" => "Web orria",
"http://www.somesite.com" => "http://www.webgunea.com",
"Go to web site" => "Web orrira joan",
"Birthday" => "Jaioteguna",
"dd-mm-yyyy" => "yyyy-mm-dd",
"Groups" => "Taldeak",
"Separate groups with commas" => "Banatu taldeak komekin",

View File

@ -49,15 +49,24 @@
"Error" => "خطا",
"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format custom, Short name, Full name, Reverse or Reverse with comma",
"Select type" => "نوع را انتخاب کنید",
"Select photo" => "تصویر را انتخاب کنید",
"This property has to be non-empty." => "این ویژگی باید به صورت غیر تهی عمل کند",
"Couldn't serialize elements." => "قابلیت مرتب سازی عناصر وجود ندارد",
"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "پاک کردن ویژگی بدون استدلال انجام شده.لطفا این مورد را گزارش دهید:bugs.owncloud.org",
"Edit name" => "نام تغییر",
"No files selected for upload." => "هیچ فایلی برای آپلود انتخاب نشده است",
"The file you are trying to upload exceed the maximum size for file uploads on this server." => "حجم فایل بسیار بیشتر از حجم تنظیم شده در تنظیمات سرور است",
"Upload too large" => "سایز فایل برای آپلود زیاد است(م.تنظیمات در php.ini)",
"Only image files can be used as profile picture." => "تنها تصاویر می توانند برای تصویر پروفایل انتخاب شوند",
"Wrong file type" => "خطا در نوع فایل",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "مرورگر شما از آژاکس پشتیبانی نمی کند. لطفا از مرورگر بهتری استفاده کرده و یا بر روی تصویر پروفایل کلیک کنید تا تصوبر دیگری را جهت بارگذاری برگزینید",
"Unable to upload your file as it is a directory or has 0 bytes" => "ناتوان در بارگذاری یا فایل یک پوشه است یا 0بایت دارد",
"Upload Error" => "خطا در بار گذاری",
"Pending" => "در انتظار",
"Result: " => "نتیجه:",
" imported, " => "وارد شد،",
" failed." => "ناموفق",
"Displayname cannot be empty." => "اسم نمایشی نمی تواند خالی باشد",
"Download" => "بارگیری",
"Edit" => "ویرایش",
"Delete" => "پاک کردن",
@ -66,6 +75,7 @@
"Contact could not be found." => "اتصال ویا تماسی یافت نشد",
"Work" => "کار",
"Home" => "خانه",
"Other" => "دیگر",
"Mobile" => "موبایل",
"Text" => "متن",
"Voice" => "صدا",
@ -74,11 +84,11 @@
"Video" => "رسانه تصویری",
"Pager" => "صفحه",
"Internet" => "اینترنت",
"Birthday" => "روزتولد",
"{name}'s Birthday" => "روز تولد {name} است",
"Contact" => "اشخاص",
"Add Contact" => "افزودن اطلاعات شخص مورد نظر",
"Import" => "وارد کردن",
"Settings" => "تنظیمات",
"Close" => "بستن",
"Drop photo to upload" => "تصویر را به اینجا بکشید تا بار گذازی شود",
"Delete current photo" => "پاک کردن تصویر کنونی",
@ -89,6 +99,7 @@
"Organization" => "نهاد(ارگان)",
"Nickname" => "نام مستعار",
"Enter nickname" => "یک نام مستعار وارد کنید",
"Birthday" => "روزتولد",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "گروه ها",
"Separate groups with commas" => "جدا کردن گروه ها به وسیله درنگ نما",
@ -150,6 +161,8 @@
"Primary address (Kontact et al)" => "نشانی اولیه",
"iOS/OS X" => "iOS/OS X ",
"Addressbooks" => "کتابچه ی نشانی ها",
"Share" => "اشتراک‌گزاری",
"New Address Book" => "کتابچه نشانه های جدید",
"Name" => "نام",
"Save" => "ذخیره سازی"
);

View File

@ -123,19 +123,8 @@
"Video" => "Video",
"Pager" => "Hakulaite",
"Internet" => "Internet",
"Birthday" => "Syntymäpäivä",
"Business" => "Työ",
"Call" => "Kutsu",
"Clients" => "Asiakkaat",
"Deliverer" => "Toimittaja",
"Holidays" => "Vapaapäivät",
"Ideas" => "Ideat",
"Journey" => "Matka",
"Jubilee" => "Juhla",
"Meeting" => "Kokous",
"Personal" => "Henkilökohtainen",
"Projects" => "Projektit",
"Questions" => "Kysymykset",
"Friends" => "Kaverit",
"Family" => "Perhe",
"{name}'s Birthday" => "Henkilön {name} syntymäpäivä",
"Contact" => "Yhteystieto",
"You do not have the permissions to add contacts to this addressbook." => "Sinulla ei ole oikeuksia lisätä yhteystietoja tähän osoitekirjaan.",
@ -173,6 +162,7 @@
"Web site" => "Verkkosivu",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Siirry verkkosivulle",
"Birthday" => "Syntymäpäivä",
"dd-mm-yyyy" => "pp-kk-vvvv",
"Groups" => "Ryhmät",
"Separate groups with commas" => "Erota ryhmät pilkuilla",
@ -236,6 +226,7 @@
"create a new addressbook" => "luo uusi osoitekirja",
"Name of new addressbook" => "Uuden osoitekirjan nimi",
"Importing contacts" => "Tuodaan yhteystietoja",
"<h3>You have no contacts in your addressbook.</h3><p>You can import VCF files by dragging them to the contacts list and either drop them on an addressbook to import into it, or on an empty spot to create a new addressbook and import into that.<br />You can also import by clicking on the import button at the bottom of the list.</p>" => "<h3>Osoitekirjassasi ei ole yhteystietoja.</h3><p>Voit tuoda VCF-tiedostoja vetämällä ne yhteystietoluetteloon ja pudottamalla ne haluamaasi osoitekirjaan, tai lisätä yhteystiedon uuteen osoitekirjaan pudottamalla sen tyhjään tilaan.<br />Vaihtoehtoisesti voit myös napsauttaa Tuo-painiketta luettelon alaosassa.</p>",
"Add contact" => "Lisää yhteystieto",
"Select Address Books" => "Valitse osoitekirjat",
"Enter description" => "Anna kuvaus",

View File

@ -126,19 +126,6 @@
"Video" => "Vidéo",
"Pager" => "Bipeur",
"Internet" => "Internet",
"Birthday" => "Anniversaire",
"Business" => "Business",
"Call" => "Appel",
"Clients" => "Clients",
"Deliverer" => "Livreur",
"Holidays" => "Vacances",
"Ideas" => "Idées",
"Journey" => "Trajet",
"Jubilee" => "Jubilé",
"Meeting" => "Rendez-vous",
"Personal" => "Personnel",
"Projects" => "Projets",
"Questions" => "Questions",
"{name}'s Birthday" => "Anniversaire de {name}",
"Contact" => "Contact",
"You do not have the permissions to add contacts to this addressbook." => "Vous n'avez pas les droits suffisants pour ajouter des contacts à ce carnet d'adresses.",
@ -176,6 +163,7 @@
"Web site" => "Page web",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Allez à la page web",
"Birthday" => "Anniversaire",
"dd-mm-yyyy" => "jj-mm-aaaa",
"Groups" => "Groupes",
"Separate groups with commas" => "Séparer les groupes avec des virgules",

View File

@ -1,21 +1,25 @@
<?php $TRANSLATIONS = array(
"Error (de)activating addressbook." => "Produciuse un erro (des)activando a axenda.",
"id is not set." => "non se estableceu o id.",
"Cannot update addressbook with an empty name." => "Non se pode actualizar a libreta de enderezos sen completar o nome.",
"No ID provided" => "Non se proveeu ID",
"Cannot update addressbook with an empty name." => "Non se pode actualizar a caderno de enderezos sen completar o nome.",
"No ID provided" => "Non se deu o ID ",
"Error setting checksum." => "Erro establecendo a suma de verificación",
"No categories selected for deletion." => "Non se seleccionaron categorías para borrado.",
"No address books found." => "Non se atoparon libretas de enderezos.",
"No address books found." => "Non se atoparon cadernos de enderezos.",
"No contacts found." => "Non se atoparon contactos.",
"element name is not set." => "non se nomeou o elemento.",
"Could not parse contact: " => "Non se puido engadir o contacto: ",
"Cannot add empty property." => "Non se pode engadir unha propiedade baleira.",
"At least one of the address fields has to be filled out." => "Polo menos un dos campos do enderezo ten que ser cuberto.",
"Trying to add duplicate property: " => "Tentando engadir propiedade duplicada: ",
"Information about vCard is incorrect. Please reload the page." => "A información sobre a vCard é incorrecta. Por favor volva cargar a páxina.",
"Missing IM parameter." => "Falta un parámetro do MI.",
"Unknown IM: " => "MI descoñecido:",
"Information about vCard is incorrect. Please reload the page." => "A información sobre a vCard é incorrecta. Volva cargar a páxina.",
"Missing ID" => "ID perdido",
"Error parsing VCard for ID: \"" => "Erro procesando a VCard para o ID: \"",
"checksum is not set." => "non se estableceu a suma de verificación.",
"Information about vCard is incorrect. Please reload the page: " => "A información sobre a vCard é incorrecta. Por favor, recargue a páxina: ",
"Information about vCard is incorrect. Please reload the page: " => "A información sobre a vCard é incorrecta. Recargue a páxina: ",
"Something went FUBAR. " => "Algo se escangallou.",
"No contact ID was submitted." => "Non se enviou ningún ID de contacto.",
"Error reading contact photo." => "Erro lendo a fotografía do contacto.",
"Error saving temporary file." => "Erro gardando o ficheiro temporal.",
@ -24,7 +28,7 @@
"No photo path was submitted." => "Non se enviou a ruta a unha foto.",
"File doesn't exist:" => "O ficheiro non existe:",
"Error loading image." => "Erro cargando imaxe.",
"Error getting contact object." => "Erro obtendo o obxeto contacto.",
"Error getting contact object." => "Erro obtendo o obxecto contacto.",
"Error getting PHOTO property." => "Erro obtendo a propiedade PHOTO.",
"Error saving contact." => "Erro gardando o contacto.",
"Error resizing image" => "Erro cambiando o tamaño da imaxe",
@ -32,39 +36,88 @@
"Error creating temporary image" => "Erro creando a imaxe temporal",
"Error finding image: " => "Erro buscando a imaxe: ",
"Error uploading contacts to storage." => "Erro subindo os contactos ao almacén.",
"There is no error, the file uploaded with success" => "Non houbo erros, o ficheiro subeuse con éxito",
"There is no error, the file uploaded with success" => "Non houbo erros, o ficheiro subiuse con éxito",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "O ficheiro subido supera a directiva upload_max_filesize no php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "O ficheiro subido supera a directiva MAX_FILE_SIZE especificada no formulario HTML",
"The uploaded file was only partially uploaded" => "O ficheiro so foi parcialmente subido",
"No file was uploaded" => "Non se subeu ningún ficheiro",
"No file was uploaded" => "Non se subiu ningún ficheiro",
"Missing a temporary folder" => "Falta o cartafol temporal",
"Couldn't save temporary image: " => "Non se puido gardar a imaxe temporal: ",
"Couldn't load temporary image: " => "Non se puido cargar a imaxe temporal: ",
"No file was uploaded. Unknown error" => "Non se subeu ningún ficheiro. Erro descoñecido.",
"No file was uploaded. Unknown error" => "Non se subiu ningún ficheiro. Erro descoñecido.",
"Contacts" => "Contactos",
"Sorry, this functionality has not been implemented yet" => "Sentímolo, esta función aínda non foi implementada.",
"Sorry, this functionality has not been implemented yet" => "Esta función aínda non foi implementada.",
"Not implemented" => "Non implementada.",
"Couldn't get a valid address." => "Non se puido obter un enderezo de correo válido.",
"Error" => "Erro",
"Please enter an email address." => "Introduce unha dirección de correo electrónico.",
"Enter name" => "Indique o nome",
"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formato personalizado, Nome corto, Nome completo, Inverso ou Inverso con coma",
"Select type" => "Seleccione tipo",
"Select photo" => "Seleccione fotografía",
"You do not have permission to add contacts to " => "Non tes permisos para engadir contactos a",
"Please select one of your own address books." => "Escolle un das túas axenda.",
"Permission error" => " Erro nos permisos ",
"Click to undo deletion of \"" => "Fai clic para desfacer que se eliminara \"",
"Cancelled deletion of: \"" => "Cancelar a eliminación de: \"",
"This property has to be non-empty." => "Esta propiedade non pode quedar baldeira.",
"Couldn't serialize elements." => "Non se puido serializar os elementos.",
"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' chamado sen argumento. Por favor, informe en bugs.owncloud.org",
"Couldn't serialize elements." => "Non se puideron poñer os elementos en serie",
"Unknown error. Please check logs." => "Erro descoñecido. Comproba os rexistros log.",
"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' chamado sen argumento. Informe en bugs.owncloud.org",
"Edit name" => "Editar nome",
"No files selected for upload." => "Sen ficheiros escollidos para subir.",
"The file you are trying to upload exceed the maximum size for file uploads on this server." => "O ficheiro que tenta subir supera o tamaño máximo permitido neste servidor.",
"Error loading profile picture." => "Erro ao cargar a imaxe de perfil.",
"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "Algúns contactos están marcados para ser eliminados máis aínda non se eliminaron. Espera a que se eliminen.",
"Do you want to merge these address books?" => "Queres combinar estes cadernos de enderezos?",
"Shared by " => "Compartido por",
"Upload too large" => "Subida demasiado grande",
"Only image files can be used as profile picture." => "Só se poden usar ficheiros de imaxe como foto de perfil.",
"Wrong file type" => "Tipo de ficheiro incorrecto",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "O teu navegador non soporta a subida AJAX. Fai clic na foto de perfil para seleccionar unha foto para subir.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Non se puido subir o ficheiro pois ou é un directorio ou ten 0 bytes",
"Upload Error" => "Erro na subida",
"Pending" => "Pendentes",
"Import done" => "Importación realizada",
"Not all files uploaded. Retrying..." => "Non se subiron todos os ficheiros. Intentándoo de novo...",
"Something went wrong with the upload, please retry." => "Algo fallou na subida de ficheiros. Inténtao de novo.",
"Importing..." => "Importando...",
"The address book name cannot be empty." => "Non se pode deixar baleiro o nome do caderno de enderezos.",
"Result: " => "Resultado: ",
" imported, " => " importado, ",
" failed." => " fallou.",
"Displayname cannot be empty." => "Displayname non pode estar baldeiro.",
"Show CardDav link" => "Mostrar a ligazón de CardDav",
"Show read-only VCF link" => "Mostrar as ligazóns a VCF de só lectura",
"Download" => "Descargar",
"Edit" => "Editar",
"Delete" => "Eliminar",
"Cancel" => "Cancelar",
"This is not your addressbook." => "Esta non é a súa axenda.",
"More..." => "Máis...",
"Less..." => "Menos...",
"You do not have the permissions to read this addressbook." => "Non tes permisos para ler este caderno de enderezos.",
"You do not have the permissions to update this addressbook." => "Non tes permisos para actualizar este caderno de enderezos.",
"There was an error updating the addressbook." => "Houbo un erro actualizando o caderno de enderezos.",
"You do not have the permissions to delete this addressbook." => "Non tes permisos para eliminar este caderno de enderezos.",
"There was an error deleting this addressbook." => "Houbo un erro borrando este caderno de enderezos.",
"Addressbook not found: " => "Non se atopou o caderno de enderezos:",
"This is not your addressbook." => "Esta non é a túa axenda.",
"Contact could not be found." => "Non se atopou o contacto.",
"Jabber" => "Jabber",
"AIM" => "AIM",
"MSN" => "MSN",
"Twitter" => "Twitter",
"GoogleTalk" => "Google Talk",
"Facebook" => "Facebook",
"XMPP" => "XMPP",
"ICQ" => "ICQ",
"Yahoo" => "Yahoo",
"Skype" => "Skype",
"QQ" => "QQ",
"GaduGadu" => "GaduGadu",
"Work" => "Traballo",
"Home" => "Casa",
"Other" => "Outro",
"Mobile" => "Móbil",
"Text" => "Texto",
"Voice" => "Voz",
@ -73,12 +126,33 @@
"Video" => "Vídeo",
"Pager" => "Paxinador",
"Internet" => "Internet",
"Birthday" => "Aniversario",
"{name}'s Birthday" => "Cumpleanos de {name}",
"Friends" => "Amigos",
"Family" => "Familia",
"{name}'s Birthday" => "Aniversario de {name}",
"Contact" => "Contacto",
"You do not have the permissions to add contacts to this addressbook." => "Non tes permisos para engadir contactos a este caderno de enderezos.",
"Could not find the vCard with ID." => "Non se atopa a vCard coa ID.",
"You do not have the permissions to edit this contact." => "Non tes permisos para editar este contacto.",
"Could not find the vCard with ID: " => "Non se atopa a vCard co ID:",
"Could not find the Addressbook with ID: " => "Non se pode atopar o caderno de enderezos coa ID:",
"You do not have the permissions to delete this contact." => "Non tes permisos para eliminar este contacto.",
"There was an error deleting this contact." => "Houbo un erro eliminando este contacto.",
"Add Contact" => "Engadir contacto",
"Import" => "Importar",
"Settings" => "Preferencias",
"Close" => "Pechar",
"Keyboard shortcuts" => "Atallos de teclado",
"Navigation" => "Navegación",
"Next contact in list" => "Seguinte contacto na lista",
"Previous contact in list" => "Contacto anterior na lista",
"Expand/collapse current addressbook" => "Expandir/contraer o caderno de enderezos actual",
"Next addressbook" => "Seguinte caderno de enderezos",
"Previous addressbook" => "Anterior caderno de enderezos",
"Actions" => "Accións",
"Refresh contacts list" => "Anovar a lista de contactos",
"Add new contact" => "Engadir un contacto novo",
"Add new addressbook" => "Engadir un novo caderno de enderezos",
"Delete current contact" => "Eliminar o contacto actual",
"Drop photo to upload" => "Solte a foto a subir",
"Delete current photo" => "Borrar foto actual",
"Edit current photo" => "Editar a foto actual",
@ -86,39 +160,51 @@
"Select photo from ownCloud" => "Escoller foto desde ownCloud",
"Edit name details" => "Editar detalles do nome",
"Organization" => "Organización",
"Nickname" => "Apodo",
"Enter nickname" => "Introuza apodo",
"Nickname" => "Alcume",
"Enter nickname" => "Introduza o alcume",
"Web site" => "Sitio web",
"http://www.somesite.com" => "http://www.unhaligazon.net",
"Go to web site" => "Ir ao sitio web",
"Birthday" => "Aniversario",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Grupos",
"Separate groups with commas" => "Separe grupos con comas",
"Edit groups" => "Editar grupos",
"Preferred" => "Preferido",
"Please specify a valid email address." => "Por favor indique un enderezo de correo electrónico válido.",
"Enter email address" => "Introduza enderezo de correo electrónico",
"Mail to address" => "Correo ao enderezo",
"Delete email address" => "Borrar enderezo de correo electrónico",
"Please specify a valid email address." => "Indica unha dirección de correo electrónico válida.",
"Enter email address" => "Introduza unha dirección de correo electrónico",
"Mail to address" => "Enviar correo ao enderezo",
"Delete email address" => "Borrar o enderezo de correo electrónico",
"Enter phone number" => "Introducir número de teléfono",
"Delete phone number" => "Borrar número de teléfono",
"Instant Messenger" => "Mensaxería instantánea",
"Delete IM" => "Eliminar o MI",
"View on map" => "Ver no mapa",
"Edit address details" => "Editar detalles do enderezo",
"Edit address details" => "Editar os detalles do enderezo",
"Add notes here." => "Engadir aquí as notas.",
"Add field" => "Engadir campo",
"Phone" => "Teléfono",
"Email" => "Correo electrónico",
"Instant Messaging" => "Mensaxería instantánea",
"Address" => "Enderezo",
"Note" => "Nota",
"Download contact" => "Descargar contacto",
"Delete contact" => "Borrar contacto",
"The temporary image has been removed from cache." => "A imaxe temporal foi eliminada da caché.",
"Edit address" => "Editar enderezo",
"Edit address" => "Editar o enderezo",
"Type" => "Escribir",
"PO Box" => "Apartado de correos",
"Street address" => "Enderezo da rúa",
"Street and number" => "Rúa e número",
"Extended" => "Ampliado",
"Apartment number etc." => "Número de apartamento etc.",
"City" => "Cidade",
"Region" => "Autonomía",
"E.g. state or province" => "P.ex estado ou provincia",
"Zipcode" => "Código postal",
"Postal code" => "Código Postal",
"Country" => "País",
"Addressbook" => "Axenda",
"Addressbook" => "Caderno de enderezos",
"Hon. prefixes" => "Prefixos honoríficos",
"Miss" => "Srta",
"Ms" => "Sra/Srta",
@ -126,7 +212,7 @@
"Sir" => "Sir",
"Mrs" => "Sra",
"Dr" => "Dr",
"Given name" => "Apodo",
"Given name" => "Alcume",
"Additional names" => "Nomes adicionais",
"Family name" => "Nome familiar",
"Hon. suffixes" => "Sufixos honorarios",
@ -139,16 +225,22 @@
"Jr." => "Jr.",
"Sn." => "Sn.",
"Import a contacts file" => "Importar un ficheiro de contactos",
"Please choose the addressbook" => "Por favor escolla unha libreta de enderezos",
"create a new addressbook" => "crear unha nova libreta de enderezos",
"Name of new addressbook" => "Nome da nova libreta de enderezos",
"Please choose the addressbook" => "Escolle o caderno de enderezos",
"create a new addressbook" => "crear un novo caderno de enderezos",
"Name of new addressbook" => "Nome do novo caderno de enderezos",
"Importing contacts" => "Importando contactos",
"<h3>You have no contacts in your addressbook.</h3><p>You can import VCF files by dragging them to the contacts list and either drop them on an addressbook to import into it, or on an empty spot to create a new addressbook and import into that.<br />You can also import by clicking on the import button at the bottom of the list.</p>" => "<h3>Non tes contactos no teu caderno de enderezos.</h3><p>Podes importar ficheiros VCF arrastrándoos á lista de contactos ou ben tirándoos enriba do caderno de enderezos para importalos alí. Tamén arrastrándoos e deixándoos nun punto baleiro créase un novo caderno de enderezos e impórtanse alí.<br/>Igualmente podes empregar o botón de importar que tes no fondo da lista.</p>",
"Add contact" => "Engadir contacto",
"Select Address Books" => "Escoller o cadernos de enderezos",
"Enter description" => "Introducir a descrición",
"CardDAV syncing addresses" => "Enderezos CardDAV a sincronizar",
"more info" => "máis información",
"Primary address (Kontact et al)" => "Enderezo primario (Kontact et al)",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Axendas",
"New Address Book" => "Nova axenda",
"Addressbooks" => "Caderno de enderezos",
"Share" => "Compartir",
"New Address Book" => "Novo caderno de enderezos",
"Name" => "Nome",
"Description" => "Descrición",
"Save" => "Gardar"
);

View File

@ -37,6 +37,11 @@
"No file was uploaded" => "שום קובץ לא הועלה",
"Missing a temporary folder" => "תקיה זמנית חסרה",
"Contacts" => "אנשי קשר",
"Error" => "שגיאה",
"Upload too large" => "העלאה גדולה מידי",
"Unable to upload your file as it is a directory or has 0 bytes" => "לא יכול להעלות את הקובץ מכיוון שזו תקיה או שמשקל הקובץ 0 בתים",
"Upload Error" => "שגיאת העלאה",
"Pending" => "ממתין",
"Download" => "הורדה",
"Edit" => "עריכה",
"Delete" => "מחיקה",
@ -45,6 +50,7 @@
"Contact could not be found." => "לא ניתן לאתר איש קשר",
"Work" => "עבודה",
"Home" => "בית",
"Other" => "אחר",
"Mobile" => "נייד",
"Text" => "טקסט",
"Voice" => "קולי",
@ -53,11 +59,12 @@
"Video" => "וידאו",
"Pager" => "זימונית",
"Internet" => "אינטרנט",
"Birthday" => "יום הולדת",
"{name}'s Birthday" => "יום ההולדת של {name}",
"Contact" => "איש קשר",
"Add Contact" => "הוספת איש קשר",
"Import" => "יבא",
"Settings" => "הגדרות",
"Close" => "סגירה",
"Drop photo to upload" => "גרור ושחרר תמונה בשביל להעלות",
"Delete current photo" => "מחק תמונה נוכחית",
"Edit current photo" => "ערוך תמונה נוכחית",
@ -67,6 +74,7 @@
"Organization" => "ארגון",
"Nickname" => "כינוי",
"Enter nickname" => "הכנס כינוי",
"Birthday" => "יום הולדת",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "קבוצות",
"Separate groups with commas" => "הפרד קבוצות עם פסיקים",
@ -127,6 +135,8 @@
"Primary address (Kontact et al)" => "כתובת ראשית",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "פנקסי כתובות",
"Share" => "שתף",
"New Address Book" => "פנקס כתובות חדש",
"Name" => "שם",
"Save" => "שמירה"
);

View File

@ -33,6 +33,11 @@
"No file was uploaded" => "Datoteka nije poslana",
"Missing a temporary folder" => "Nedostaje privremeni direktorij",
"Contacts" => "Kontakti",
"Error" => "Greška",
"Upload too large" => "Prijenos je preobiman",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nemoguće poslati datoteku jer je prazna ili je direktorij",
"Upload Error" => "Pogreška pri slanju",
"Pending" => "U tijeku",
"Download" => "Preuzimanje",
"Edit" => "Uredi",
"Delete" => "Obriši",
@ -52,6 +57,7 @@
"GaduGadu" => "GaduGadu",
"Work" => "Posao",
"Home" => "Kuća",
"Other" => "ostali",
"Mobile" => "Mobitel",
"Text" => "Tekst",
"Voice" => "Glasovno",
@ -60,11 +66,11 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Rođendan",
"{name}'s Birthday" => "{name} Rođendan",
"Contact" => "Kontakt",
"Add Contact" => "Dodaj kontakt",
"Import" => "Uvezi",
"Settings" => "Postavke",
"Close" => "Zatvori",
"Drop photo to upload" => "Dovucite fotografiju za slanje",
"Delete current photo" => "Izbriši trenutnu sliku",
@ -75,6 +81,7 @@
"Nickname" => "Nadimak",
"Enter nickname" => "Unesi nadimank",
"http://www.somesite.com" => "http://www.somesite.com",
"Birthday" => "Rođendan",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Grupe",
"Separate groups with commas" => "Razdvoji grupe sa zarezom",
@ -111,6 +118,8 @@
"more info" => "više informacija",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Adresari",
"Share" => "Podijeli",
"New Address Book" => "Novi adresar",
"Name" => "Ime",
"Save" => "Spremi"
);

View File

@ -49,15 +49,24 @@
"Error" => "Hiba",
"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formátum egyedi, Rövid név, Teljes név, Visszafelé vagy Visszafelé vesszővel",
"Select type" => "Típus kiválasztása",
"Select photo" => "Fotó kiválasztása",
"This property has to be non-empty." => "Ezt a tulajdonságot muszáj kitölteni",
"Couldn't serialize elements." => "Sorbarakás sikertelen",
"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "A 'deleteProperty' argumentum nélkül lett meghívva. Kérjük, jelezze a hibát.",
"Edit name" => "Név szerkesztése",
"No files selected for upload." => "Nincs kiválasztva feltöltendő fájl",
"The file you are trying to upload exceed the maximum size for file uploads on this server." => "A feltöltendő fájl mérete meghaladja a megengedett mértéket",
"Upload too large" => "A feltöltési méret túl nagy",
"Only image files can be used as profile picture." => "Csak képfájl használható profilképnek",
"Wrong file type" => "Rossz fájltípus",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "Az Ön böngészője nem támogatja az AJAX feltöltést. Kérjük, kattintson a profilképre, hogy kiválaszthassa a feltöltendő képet.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nem tölthető fel, mert mappa volt, vagy 0 byte méretű",
"Upload Error" => "Feltöltési hiba",
"Pending" => "Folyamatban",
"Result: " => "Eredmény: ",
" imported, " => " beimportálva, ",
" failed." => " sikertelen",
"Displayname cannot be empty." => "Megjelenített név kitöltendő",
"Download" => "Letöltés",
"Edit" => "Szerkesztés",
"Delete" => "Törlés",
@ -66,6 +75,7 @@
"Contact could not be found." => "Kapcsolat nem található.",
"Work" => "Munkahelyi",
"Home" => "Otthoni",
"Other" => "Egyéb",
"Mobile" => "Mobiltelefonszám",
"Text" => "Szöveg",
"Voice" => "Hang",
@ -74,11 +84,11 @@
"Video" => "Video",
"Pager" => "Személyhívó",
"Internet" => "Internet",
"Birthday" => "Születésnap",
"{name}'s Birthday" => "{name} születésnapja",
"Contact" => "Kapcsolat",
"Add Contact" => "Kapcsolat hozzáadása",
"Import" => "Import",
"Settings" => "Beállítások",
"Close" => "Bezár",
"Drop photo to upload" => "Húzza ide a feltöltendő képet",
"Delete current photo" => "Aktuális kép törlése",
@ -89,6 +99,7 @@
"Organization" => "Szervezet",
"Nickname" => "Becenév",
"Enter nickname" => "Becenév megadása",
"Birthday" => "Születésnap",
"dd-mm-yyyy" => "yyyy-mm-dd",
"Groups" => "Csoportok",
"Separate groups with commas" => "Vesszővel válassza el a csoportokat",
@ -150,6 +161,8 @@
"Primary address (Kontact et al)" => "Elsődleges cím",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Címlisták",
"Share" => "Megosztás",
"New Address Book" => "Új címlista",
"Name" => "Név",
"Save" => "Mentés"
);

View File

@ -4,9 +4,11 @@
"Cannot add empty property." => "Non pote adder proprietate vacue.",
"Error saving temporary file." => "Error durante le scriptura in le file temporari",
"Error loading image." => "Il habeva un error durante le cargamento del imagine.",
"The uploaded file was only partially uploaded" => "Le file incargate solmente esseva incargate partialmente",
"No file was uploaded" => "Nulle file esseva incargate.",
"Missing a temporary folder" => "Manca un dossier temporari",
"Contacts" => "Contactos",
"Upload too large" => "Incargamento troppo longe",
"Download" => "Discargar",
"Edit" => "Modificar",
"Delete" => "Deler",
@ -15,6 +17,7 @@
"Contact could not be found." => "Contacto non poterea esser legite",
"Work" => "Travalio",
"Home" => "Domo",
"Other" => "Altere",
"Mobile" => "Mobile",
"Text" => "Texto",
"Voice" => "Voce",
@ -23,10 +26,11 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Anniversario",
"Contact" => "Contacto",
"Add Contact" => "Adder contacto",
"Import" => "Importar",
"Settings" => "Configurationes",
"Close" => "Clauder",
"Delete current photo" => "Deler photo currente",
"Edit current photo" => "Modificar photo currente",
"Upload new photo" => "Incargar nove photo",
@ -34,6 +38,7 @@
"Organization" => "Organisation",
"Nickname" => "Pseudonymo",
"Enter nickname" => "Inserer pseudonymo",
"Birthday" => "Anniversario",
"Groups" => "Gruppos",
"Edit groups" => "Modificar gruppos",
"Preferred" => "Preferite",
@ -76,6 +81,8 @@
"more info" => "plus info",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Adressarios",
"Share" => "Compartir",
"New Address Book" => "Nove adressario",
"Name" => "Nomine",
"Save" => "Salveguardar"
);

View File

@ -1,8 +1,21 @@
<?php $TRANSLATIONS = array(
"No categories selected for deletion." => "Tidak ada kategori terpilih untuk penghapusan.",
"No contacts found." => "kontak tidak ditemukan",
"Cannot add empty property." => "tidak dapat menambahkan properti kosong",
"At least one of the address fields has to be filled out." => "setidaknya satu dari alamat wajib di isi",
"File doesn't exist:" => "file tidak ditemukan:",
"There is no error, the file uploaded with success" => "Tidak ada galat, berkas sukses diunggah",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "File yang diunggah melampaui directive upload_max_filesize di php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "File yang diunggah melampaui directive MAX_FILE_SIZE yang disebutan dalam form HTML.",
"The uploaded file was only partially uploaded" => "Berkas hanya diunggah sebagian",
"No file was uploaded" => "Tidak ada berkas yang diunggah",
"Missing a temporary folder" => "Kehilangan folder temporer",
"Contacts" => "kontak",
"Error" => "kesalahan",
"Unable to upload your file as it is a directory or has 0 bytes" => "Gagal mengunggah berkas anda karena berupa direktori atau mempunyai ukuran 0 byte",
"Upload Error" => "Terjadi Galat Pengunggahan",
"Pending" => "Menunggu",
"Importing..." => "mengimpor...",
"Download" => "unduh",
"Edit" => "ubah",
"Delete" => "hapus",
@ -10,6 +23,7 @@
"Contact could not be found." => "kontak tidak dapat ditemukan",
"Work" => "pekerjaan",
"Home" => "rumah",
"Other" => "Lainnya",
"Mobile" => "ponsel",
"Text" => "teks",
"Voice" => "suara",
@ -18,14 +32,17 @@
"Video" => "video",
"Pager" => "pager",
"Internet" => "internet",
"Birthday" => "tanggal lahir",
"{name}'s Birthday" => "hari ulang tahun {name}",
"Contact" => "kontak",
"Add Contact" => "tambah kontak",
"Import" => "impor",
"Settings" => "pengaturan",
"Close" => "tutup",
"Edit name details" => "ubah detail nama",
"Organization" => "organisasi",
"Nickname" => "nama panggilan",
"Enter nickname" => "masukkan nama panggilan",
"Birthday" => "tanggal lahir",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "grup",
"Separate groups with commas" => "pisahkan grup dengan tanda koma",
@ -42,6 +59,10 @@
"Zipcode" => "kodepos",
"Country" => "negara",
"Addressbook" => "buku alamat",
"more info" => "lebih lanjut",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "buku alamat",
"Share" => "berbagi",
"Name" => "nama",
"Save" => "simpan"
);

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Cercapersone",
"Internet" => "Internet",
"Birthday" => "Compleanno",
"Business" => "Lavoro",
"Call" => "Chiama",
"Clients" => "Client",
"Deliverer" => "Corriere",
"Holidays" => "Festività",
"Ideas" => "Idee",
"Journey" => "Viaggio",
"Jubilee" => "Anniversario",
"Meeting" => "Riunione",
"Personal" => "Personale",
"Projects" => "Progetti",
"Questions" => "Domande",
"Friends" => "Amici",
"Family" => "Famiglia",
"{name}'s Birthday" => "Data di nascita di {name}",
"Contact" => "Contatto",
"You do not have the permissions to add contacts to this addressbook." => "Non hai i permessi per aggiungere contatti a questa rubrica.",
@ -176,6 +165,7 @@
"Web site" => "Sito web",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Vai al sito web",
"Birthday" => "Compleanno",
"dd-mm-yyyy" => "gg-mm-aaaa",
"Groups" => "Gruppi",
"Separate groups with commas" => "Separa i gruppi con virgole",

View File

@ -75,7 +75,7 @@
"Only image files can be used as profile picture." => "画像ファイルのみがプロファイル写真として使用することができます。",
"Wrong file type" => "誤ったファイルタイプ",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "あなたのブラウザはAJAXのアップロードをサポートしていません。プロファイル写真をクリックしてアップロードする写真を選択してください。",
"Unable to upload your file as it is a directory or has 0 bytes" => "ディレクトリ0バイトのファイルはアップロードできません",
"Unable to upload your file as it is a directory or has 0 bytes" => "ディレクトリもしくは0バイトのファイルはアップロードできません",
"Upload Error" => "アップロードエラー",
"Pending" => "中断",
"Import done" => "インポート完了",
@ -126,19 +126,8 @@
"Video" => "テレビ電話",
"Pager" => "ポケベル",
"Internet" => "インターネット",
"Birthday" => "誕生日",
"Business" => "ビジネス",
"Call" => "電話",
"Clients" => "顧客",
"Deliverer" => "運送会社",
"Holidays" => "休日",
"Ideas" => "アイデア",
"Journey" => "旅行",
"Jubilee" => "記念祭",
"Meeting" => "打ち合わせ",
"Personal" => "個人",
"Projects" => "プロジェクト",
"Questions" => "質問",
"Friends" => "友達",
"Family" => "家族",
"{name}'s Birthday" => "{name}の誕生日",
"Contact" => "連絡先",
"You do not have the permissions to add contacts to this addressbook." => "アドレスブックに連絡先を追加する権限がありません",
@ -176,6 +165,7 @@
"Web site" => "ウェブサイト",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Webサイトへ移動",
"Birthday" => "誕生日",
"dd-mm-yyyy" => "yyyy-mm-dd",
"Groups" => "グループ",
"Separate groups with commas" => "コンマでグループを分割",

View File

@ -1,11 +1,24 @@
<?php $TRANSLATIONS = array(
"No categories selected for deletion." => "სარედაქტირებელი კატეგორია არ არის არჩეული ",
"There is no error, the file uploaded with success" => "ჭოცდომა არ დაფიქსირდა, ფაილი წარმატებით აიტვირთა",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "ატვირთული ფაილი აჭარბებს upload_max_filesize დირექტივას php.ini ფაილში",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "ატვირთული ფაილი აჭარბებს MAX_FILE_SIZE დირექტივას, რომელიც მითითებულია HTML ფორმაში",
"The uploaded file was only partially uploaded" => "ატვირთული ფაილი მხოლოდ ნაწილობრივ აიტვირთა",
"No file was uploaded" => "ფაილი არ აიტვირთა",
"Missing a temporary folder" => "დროებითი საქაღალდე არ არსებობს",
"Contacts" => "კონტაქტები",
"Error" => "შეცდომა",
"Upload too large" => "ასატვირთი ფაილი ძალიან დიდია",
"Unable to upload your file as it is a directory or has 0 bytes" => "თქვენი ფაილის ატვირთვა ვერ მოხერხდა. ის არის საქაღალდე და შეიცავს 0 ბაიტს",
"Upload Error" => "შეცდომა ატვირთვისას",
"Pending" => "მოცდის რეჟიმში",
"Download" => "ჩამოტვირთვა",
"Edit" => "რედაქტირება",
"Delete" => "წაშლა",
"Cancel" => "უარყოფა",
"Work" => "სამსახური",
"Home" => "სახლი",
"Other" => "სხვა",
"Mobile" => "მობილური",
"Text" => "ტექსტი",
"Voice" => "ხმა",
@ -14,18 +27,18 @@
"Video" => "ვიდეო",
"Pager" => "პეიჯერი",
"Internet" => "ინტერნეტი",
"Birthday" => "დაბადების დრე",
"Business" => "ბიზნესი",
"Clients" => "კლიენტები",
"Contact" => "კონტაქტი",
"Add Contact" => "კონტაქტის დამატება",
"Import" => "იმპორტი",
"Settings" => "პარამეტრები",
"Close" => "დახურვა",
"Delete current photo" => "მიმდინარე სურათის წაშლა",
"Edit current photo" => "მიმდინარე სურათის რედაქტირება",
"Upload new photo" => "ახალი სურათის ატვირთვა",
"Select photo from ownCloud" => "აირჩიე სურათი ownCloud –იდან",
"Organization" => "ორგანიზაცია",
"Nickname" => "ნიკნეიმი",
"Birthday" => "დაბადების დრე",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "ჯგუფები",
"Edit groups" => "ჯგუფების რედაქტირება",
@ -52,7 +65,11 @@
"Sir" => "სერ",
"Add contact" => "კონტაქტის დამატება",
"more info" => "უფრო მეტი ინფორმაცია",
"Primary address (Kontact et al)" => "პირველადი მისამართი (Kontact et al)",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "მისამართის წიგნები",
"Share" => "გაზიარება",
"New Address Book" => "ახალი მისამართების წიგნი",
"Name" => "სახელი",
"Save" => "შენახვა"
);

View File

@ -50,14 +50,19 @@
"Not implemented" => "구현되지 않음",
"Couldn't get a valid address." => "유효한 주소를 얻을 수 없습니다.",
"Error" => "오류",
"Please enter an email address." => "이메일 주소를 입력해 주세요.",
"Enter name" => "이름을 입력",
"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format custom, Short name, Full name, Reverse or Reverse with comma",
"Select type" => "유형 선택",
"Select photo" => "사진 선택",
"You do not have permission to add contacts to " => "당신은 연락처를 추가 할 수 있는 권한이 없습니다. ",
"Please select one of your own address books." => "당신의 Own 주소록 중 하나만 선택 하세요.",
"Permission error" => "권한 에러",
"Click to undo deletion of \"" => "삭제를 되돌리기 위한 클릭",
"Cancelled deletion of: \"" => "삭제가 취소되었습니다.",
"This property has to be non-empty." => "이 속성은 비어있어서는 안됩니다.",
"Couldn't serialize elements." => "요소를 직렬화 할 수 없습니다.",
"Unknown error. Please check logs." => "알수없는 에러. 로그를 확인해주세요.",
"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty'가 문서형식이 없이 불려왔습니다. bugs.owncloud.org에 보고해주세요. ",
"Edit name" => "이름 편집",
"No files selected for upload." => "업로드를 위한 파일이 선택되지 않았습니다. ",
@ -65,6 +70,19 @@
"Error loading profile picture." => "프로필 사진 로딩 에러",
"Some contacts are marked for deletion, but not deleted yet. Please wait for them to be deleted." => "일부 연락처가 삭제 표시 되었으나 아직 삭제되지 않았습니다. 삭제가 끝날 때 까지 기다려 주세요.",
"Do you want to merge these address books?" => "이 주소록을 통합하고 싶으십니까?",
"Shared by " => "Shared by",
"Upload too large" => "업로드 용량 초과",
"Only image files can be used as profile picture." => "이미지 파일만 프로필 사진으로 사용 될 수 있습니다.",
"Wrong file type" => "옳지 않은 파일 형식",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "브라우저가 AJAX 업로드를 지원하지 않습니다. 업로드할 사진을 선택하려면 프로필 사진을 클릭하세요.",
"Unable to upload your file as it is a directory or has 0 bytes" => "이 파일은 디렉토리이거나 0 바이트이기 때문에 업로드 할 수 없습니다.",
"Upload Error" => "업로드 에러",
"Pending" => "보류 중",
"Import done" => "가져오기 완료",
"Not all files uploaded. Retrying..." => "모든파일이 업로드되지 않았습니다. 재시도중...",
"Something went wrong with the upload, please retry." => "업로드 하는데 무언가 잘못되었습니다. 재시도 해주세요.",
"Importing..." => "가져오기중...",
"The address book name cannot be empty." => "주소록 이름은 비워둘 수 없습니다.",
"Result: " => "결과:",
" imported, " => "불러오기,",
" failed." => "실패.",
@ -76,6 +94,12 @@
"Delete" => "삭제",
"Cancel" => "취소",
"More..." => "더...",
"Less..." => "그외...",
"You do not have the permissions to read this addressbook." => "당신은 이 주소록을 읽기 위한 권한이 없습니다.",
"You do not have the permissions to update this addressbook." => "당신은 이 주소록을 업데이트하기 위한 권한이 없습니다.",
"There was an error updating the addressbook." => "주소록을 업데이트 하는중에 에러가 발생하였습니다.",
"You do not have the permissions to delete this addressbook." => "당신은 이 주소록을 삭제하기 위한 권한이 없습니다.",
"There was an error deleting this addressbook." => "이 주소록을 제거하는데 에러가 발생하였습니다.",
"Addressbook not found: " => "주소록을 찾지 못하였습니다:",
"This is not your addressbook." => "내 주소록이 아닙니다.",
"Contact could not be found." => "연락처를 찾을 수 없습니다.",
@ -102,23 +126,17 @@
"Video" => "영상 번호",
"Pager" => "호출기",
"Internet" => "인터넷",
"Birthday" => "생일",
"Business" => "비즈니스",
"Call" => "전화",
"Clients" => "고객",
"Deliverer" => "운송자",
"Holidays" => "휴가",
"Ideas" => "아이디어",
"Journey" => "여행",
"Jubilee" => "축제",
"Meeting" => "미팅",
"Personal" => "개인의",
"Projects" => "프로젝트",
"Questions" => "질문",
"Friends" => "친구들",
"Family" => "가족",
"{name}'s Birthday" => "{이름}의 생일",
"Contact" => "연락처",
"You do not have the permissions to add contacts to this addressbook." => "당신은 이 주소록에 연락처를 추가하기 위한 권한이 없습니다.",
"Could not find the vCard with ID." => "ID와 vCard를 찾을수 없습니다.",
"You do not have the permissions to edit this contact." => "당신은 연락처를 수정할 권한이 없습니다. ",
"Could not find the vCard with ID: " => "ID와 vCard를 찾을수 없습니다.",
"Could not find the Addressbook with ID: " => "ID와 주소록을 찾을 수 없습니다.",
"You do not have the permissions to delete this contact." => "당신은 연락처를 삭제할 권한이 없습니다. ",
"There was an error deleting this contact." => "이 연락처를 삭제하는데 에러가 발생하였습니다.",
"Add Contact" => "연락처 추가",
"Import" => "입력",
"Settings" => "설정",
@ -147,6 +165,7 @@
"Web site" => "웹 사이트",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "웹 사이트로 가기",
"Birthday" => "생일",
"dd-mm-yyyy" => "일-월-년",
"Groups" => "그룹",
"Separate groups with commas" => "쉼표로 그룹 구분",
@ -210,6 +229,7 @@
"create a new addressbook" => "새 주소록 만들기",
"Name of new addressbook" => "새 주소록 이름",
"Importing contacts" => "연락처 입력",
"<h3>You have no contacts in your addressbook.</h3><p>You can import VCF files by dragging them to the contacts list and either drop them on an addressbook to import into it, or on an empty spot to create a new addressbook and import into that.<br />You can also import by clicking on the import button at the bottom of the list.</p>" => "<h3>당신의 주소록에 연락처가 없습니다.</h3><p>연락처 리스트에 드래그앤 드롭 함으로써 주소록에 VCF 파일을 가져올 수 있습니다, or on an empty spot to create a new addressbook and import into that.<br />당신은 리스트 상단에 위치한 가져오기 버튼을 누름으로써 가져올수 있습니다.</p>",
"Add contact" => "연락처 추가",
"Select Address Books" => "주소록 선택",
"Enter description" => "설명을 입력",

13
l10n/ku_IQ.php Normal file
View File

@ -0,0 +1,13 @@
<?php $TRANSLATIONS = array(
"File doesn't exist:" => "په‌ڕگه‌که‌ هه‌بوون نیه:",
"Error" => "هه‌ڵه",
"Importing..." => "ده‌هێنرێت...",
"Download" => "داگرتن",
"Import" => "هێنان",
"Settings" => "ده‌ستكاری",
"Close" => "داخستن",
"Email" => "ئیمه‌یل",
"Address" => "ناونیشان",
"Name" => "ناو",
"Save" => "پاشکه‌وتکردن"
);

View File

@ -17,9 +17,16 @@
"Contact ID is missing." => "Kontakt ID fehlt.",
"File doesn't exist:" => "Fichier existéiert net:",
"Error loading image." => "Fehler beim lueden vum Bild.",
"There is no error, the file uploaded with success" => "Keen Feeler, Datei ass komplett ropgelueden ginn",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Déi ropgelueden Datei ass méi grouss wei d'upload_max_filesize Eegenschaft an der php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Déi ropgelueden Datei ass méi grouss wei d'MAX_FILE_SIZE Eegenschaft déi an der HTML form uginn ass",
"The uploaded file was only partially uploaded" => "Déi ropgelueden Datei ass nëmmen hallef ropgelueden ginn",
"No file was uploaded" => "Et ass kee Fichier ropgeluede ginn",
"Missing a temporary folder" => "Et feelt en temporären Dossier",
"Contacts" => "Kontakter",
"Error" => "Fehler",
"Unable to upload your file as it is a directory or has 0 bytes" => "Kann deng Datei net eroplueden well et en Dossier ass oder 0 byte grouss ass.",
"Upload Error" => "Fehler beim eroplueden",
"Result: " => "Resultat: ",
" imported, " => " importéiert, ",
"Download" => "Download",
@ -30,6 +37,7 @@
"Contact could not be found." => "Konnt den Kontakt net fannen.",
"Work" => "Aarbecht",
"Home" => "Doheem",
"Other" => "Aner",
"Mobile" => "GSM",
"Text" => "SMS",
"Voice" => "Voice",
@ -38,14 +46,16 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Gebuertsdag",
"{name}'s Birthday" => "{name} säi Gebuertsdag",
"Contact" => "Kontakt",
"Add Contact" => "Kontakt bäisetzen",
"Import" => "Import",
"Settings" => "Astellungen",
"Close" => "Zoumaachen",
"Organization" => "Firma",
"Nickname" => "Spëtznumm",
"Enter nickname" => "Gëff e Spëtznumm an",
"Birthday" => "Gebuertsdag",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Gruppen",
"Edit groups" => "Gruppen editéieren",
@ -79,6 +89,8 @@
"Sn." => "Sn.",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Adressbicher ",
"Share" => "Deelen",
"New Address Book" => "Neit Adressbuch",
"Name" => "Numm",
"Save" => "Späicheren"
);

View File

@ -1,5 +1,6 @@
<?php $TRANSLATIONS = array(
"Error (de)activating addressbook." => "Klaida (de)aktyvuojant adresų knygą.",
"No categories selected for deletion." => "Trynimui nepasirinkta jokia kategorija.",
"No contacts found." => "Kontaktų nerasta.",
"Information about vCard is incorrect. Please reload the page." => "Informacija apie vCard yra neteisinga. ",
"Error reading contact photo." => "Klaida skaitant kontakto nuotrauką.",
@ -11,7 +12,14 @@
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Įkeliamo failo dydis viršija MAX_FILE_SIZE nustatymą, kuris naudojamas HTML formoje.",
"The uploaded file was only partially uploaded" => "Failas buvo įkeltas tik dalinai",
"No file was uploaded" => "Nebuvo įkeltas joks failas",
"Missing a temporary folder" => "Nėra laikinojo katalogo",
"Contacts" => "Kontaktai",
"Error" => "Klaida",
"Upload too large" => "Įkėlimui failas per didelis",
"Unable to upload your file as it is a directory or has 0 bytes" => "Neįmanoma įkelti failo - jo dydis gali būti 0 bitų arba tai katalogas",
"Upload Error" => "Įkėlimo klaida",
"Pending" => "Laukiantis",
"Importing..." => "Importuojama...",
"Download" => "Atsisiųsti",
"Edit" => "Keisti",
"Delete" => "Trinti",
@ -20,6 +28,7 @@
"Contact could not be found." => "Kontaktas nerastas",
"Work" => "Darbo",
"Home" => "Namų",
"Other" => "Kita",
"Mobile" => "Mobilusis",
"Text" => "Žinučių",
"Voice" => "Balso",
@ -28,12 +37,16 @@
"Video" => "Vaizdo",
"Pager" => "Pranešimų gaviklis",
"Internet" => "Internetas",
"Birthday" => "Gimtadienis",
"Contact" => "Kontaktas",
"Add Contact" => "Pridėti kontaktą",
"Import" => "Importuoti",
"Settings" => "Nustatymai",
"Close" => "Užverti",
"Organization" => "Organizacija",
"Nickname" => "Slapyvardis",
"Enter nickname" => "Įveskite slapyvardį",
"Birthday" => "Gimtadienis",
"Groups" => "Grupės",
"Phone" => "Telefonas",
"Email" => "El. paštas",
"Address" => "Adresas",
@ -47,6 +60,8 @@
"Country" => "Šalis",
"Addressbook" => "Adresų knyga",
"Addressbooks" => "Adresų knygos",
"Share" => "Dalintis",
"New Address Book" => "Nauja adresų knyga",
"Name" => "Pavadinimas",
"Save" => "Išsaugoti"
);

16
l10n/lv.php Normal file
View File

@ -0,0 +1,16 @@
<?php $TRANSLATIONS = array(
"No file was uploaded" => "Neviens fails netika augšuplādēts",
"Error" => "Kļūme",
"Upload too large" => "Fails ir par lielu lai to augšuplādetu",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nav iespējams augšuplādēt jūsu failu, jo tāds jau eksistē vai arī failam nav izmēra (0 baiti)",
"Upload Error" => "Augšuplādēšanas laikā radās kļūda",
"Pending" => "Gaida savu kārtu",
"Download" => "Lejuplādēt",
"Delete" => "Izdzēst",
"Other" => "Cits",
"Settings" => "Iestatījumi",
"Groups" => "Grupas",
"Email" => "Epasts",
"Share" => "Līdzdalīt",
"Name" => "Nosaukums"
);

View File

@ -49,15 +49,24 @@
"Error" => "Грешка",
"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Прилагоден формат, кратко име, цело име, обратно или обратно со запирка",
"Select type" => "Одбери тип",
"Select photo" => "Одбери фотографија",
"This property has to be non-empty." => "Својството не смее да биде празно.",
"Couldn't serialize elements." => "Не може да се серијализираат елементите.",
"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' повикан без тип на аргументот. Пријавете грешка/проблем на bugs.owncloud.org",
"Edit name" => "Уреди го името",
"No files selected for upload." => "Ниту еден фајл не е избран за вчитување.",
"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Датотеката која се обидувате да ја префрлите ја надминува максималната големина дефинирана за пренос на овој сервер.",
"Upload too large" => "Фајлот кој се вчитува е преголем",
"Only image files can be used as profile picture." => "Само слики можат да бидат искористени како фотографија за профилот.",
"Wrong file type" => "Погрешен тип на фајл",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "Вашиот прелистувач не подржува AJAX преземања. Ве молиме кликнете на сликата за профилот да ја изберете фотографијата за преземање.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Не може да се преземе вашата датотека бидејќи фолдерот во кој се наоѓа фајлот има големина од 0 бајти",
"Upload Error" => "Грешка при преземање",
"Pending" => "Чека",
"Result: " => "Резултат: ",
" imported, " => "увезено,",
" failed." => "неуспешно.",
"Displayname cannot be empty." => "Прикажаното име не може да биде празно.",
"Download" => "Преземи",
"Edit" => "Уреди",
"Delete" => "Избриши",
@ -66,6 +75,7 @@
"Contact could not be found." => "Контактот неможе да биде најден.",
"Work" => "Работа",
"Home" => "Дома",
"Other" => "Останато",
"Mobile" => "Мобилен",
"Text" => "Текст",
"Voice" => "Глас",
@ -74,11 +84,11 @@
"Video" => "Видео",
"Pager" => "Пејџер",
"Internet" => "Интернет",
"Birthday" => "Роденден",
"{name}'s Birthday" => "Роденден на {name}",
"Contact" => "Контакт",
"Add Contact" => "Додади контакт",
"Import" => "Внеси",
"Settings" => "Параметри",
"Close" => "Затвои",
"Drop photo to upload" => "Довлечкај фотографија за да се подигне",
"Delete current photo" => "Избриши моментална фотографија",
@ -89,6 +99,7 @@
"Organization" => "Организација",
"Nickname" => "Прекар",
"Enter nickname" => "Внеси прекар",
"Birthday" => "Роденден",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Групи",
"Separate groups with commas" => "Одвоете ги групите со запирка",
@ -150,6 +161,8 @@
"Primary address (Kontact et al)" => "Примарна адреса",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Адресари",
"Share" => "Сподели",
"New Address Book" => "Нов адресар",
"Name" => "Име",
"Save" => "Сними"
);

View File

@ -50,15 +50,24 @@
"Enter name" => "Masukkan nama",
"Format custom, Short name, Full name, Reverse or Reverse with comma" => "Format bebas, Nama pendek, Nama penuh, Unduran dengan koma",
"Select type" => "PIlih jenis",
"Select photo" => "Pilih foto",
"This property has to be non-empty." => "Nilai ini tidak boleh kosong.",
"Couldn't serialize elements." => "Tidak boleh menggabungkan elemen.",
"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' dipanggil tanpa argumen taip. Sila maklumkan di bugs.owncloud.org",
"Edit name" => "Ubah nama",
"No files selected for upload." => "Tiada fail dipilih untuk muatnaik.",
"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Fail yang ingin dimuatnaik melebihi saiz yang dibenarkan.",
"Upload too large" => "Muatnaik terlalu besar",
"Only image files can be used as profile picture." => "Hanya fail imej boleh digunakan sebagai gambar profil.",
"Wrong file type" => "Salah jenis fail",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "Pelayar web anda tidak menyokong muatnaik AJAX. Sila klik pada gambar profil untuk memilih foto untuk dimuatnail.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Tidak boleh memuatnaik fail anda kerana mungkin ianya direktori atau saiz fail 0 bytes",
"Upload Error" => "Muat naik ralat",
"Pending" => "Dalam proses",
"Result: " => "Hasil: ",
" imported, " => " import, ",
" failed." => " gagal.",
"Displayname cannot be empty." => "Nama paparan tidak boleh kosong",
"Download" => "Muat naik",
"Edit" => "Sunting",
"Delete" => "Padam",
@ -78,16 +87,6 @@
"Video" => "Video",
"Pager" => "Alat Kelui",
"Internet" => "Internet",
"Birthday" => "Hari lahir",
"Business" => "Perniagaan",
"Clients" => "klien",
"Holidays" => "Hari kelepasan",
"Ideas" => "Idea",
"Journey" => "Perjalanan",
"Jubilee" => "Jubli",
"Meeting" => "Mesyuarat",
"Personal" => "Peribadi",
"Projects" => "Projek",
"{name}'s Birthday" => "Hari Lahir {name}",
"Contact" => "Hubungan",
"Add Contact" => "Tambah kenalan",
@ -105,6 +104,7 @@
"Organization" => "Organisasi",
"Nickname" => "Nama Samaran",
"Enter nickname" => "Masukkan nama samaran",
"Birthday" => "Hari lahir",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Kumpulan",
"Separate groups with commas" => "Asingkan kumpulan dengan koma",
@ -168,6 +168,7 @@
"Primary address (Kontact et al)" => "Alamat utama",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Senarai Buku Alamat",
"Share" => "Kongsi",
"New Address Book" => "Buku Alamat Baru",
"Name" => "Nama",
"Description" => "Keterangan",

View File

@ -56,8 +56,13 @@
"No files selected for upload." => "Ingen filer valgt for opplasting.",
"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Filen du prøver å laste opp er for stor.",
"Shared by " => "Delt av",
"Upload too large" => "Filen er for stor",
"Only image files can be used as profile picture." => "Kun bildefiler kan bli brukt som profilbilde",
"Wrong file type" => "Feil filtype",
"Your browser doesn't support AJAX upload. Please click on the profile picture to select a photo to upload." => "Din nettleser støtter ikke opplasting med AJAX. Klikk på ønsket bilde for å laste opp.",
"Unable to upload your file as it is a directory or has 0 bytes" => "Kan ikke laste opp filen din siden det er en mappe eller den har 0 bytes",
"Upload Error" => "Opplasting feilet",
"Pending" => "Ventende",
"Importing..." => "Importerer...",
"Result: " => "Resultat:",
" imported, " => "importert,",
@ -84,16 +89,6 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internett",
"Birthday" => "Bursdag",
"Call" => "Ring",
"Clients" => "Klienter",
"Holidays" => "Helligdager",
"Ideas" => "Idè",
"Journey" => "Reise",
"Meeting" => "Møte",
"Personal" => "Personlig",
"Projects" => "Prosjekt",
"Questions" => "Spørsmål",
"{name}'s Birthday" => "{name}s bursdag",
"Contact" => "Kontakt",
"Could not find the vCard with ID." => "Kunne ikke finne vCard med denne IDen",
@ -102,6 +97,7 @@
"There was an error deleting this contact." => "Det oppstod en feil ved sletting av denne kontakten",
"Add Contact" => "Ny kontakt",
"Import" => "Importer",
"Settings" => "Innstillinger",
"Close" => "Lukk",
"Keyboard shortcuts" => "Tastatur snarveier",
"Navigation" => "Navigasjon",
@ -122,6 +118,7 @@
"Enter nickname" => "Skriv inn kallenavn",
"Web site" => "Hjemmeside",
"http://www.somesite.com" => "http://www.domene.no",
"Birthday" => "Bursdag",
"dd-mm-yyyy" => "dd-mm-åååå",
"Groups" => "Grupper",
"Separate groups with commas" => "Skill gruppene med komma",
@ -176,6 +173,8 @@
"Primary address (Kontact et al)" => "Primær adresse (kontakt osv)",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Adressebøker",
"Share" => "Del",
"New Address Book" => "Ny adressebok",
"Name" => "Navn",
"Save" => "Lagre"
);

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Pieper",
"Internet" => "Internet",
"Birthday" => "Verjaardag",
"Business" => "Business",
"Call" => "Bel",
"Clients" => "Klanten",
"Deliverer" => "Leverancier",
"Holidays" => "Vakanties",
"Ideas" => "Ideeën",
"Journey" => "Reis",
"Jubilee" => "Jubileum",
"Meeting" => "Vergadering",
"Personal" => "Persoonlijk",
"Projects" => "Projecten",
"Questions" => "Vragen",
"Friends" => "Vrienden",
"Family" => "Familie",
"{name}'s Birthday" => "{name}'s verjaardag",
"Contact" => "Contact",
"You do not have the permissions to add contacts to this addressbook." => "U kunt geen contact personen toevoegen aan dit adresboek.",
@ -176,6 +165,7 @@
"Web site" => "Website",
"http://www.somesite.com" => "http://www.willekeurigesite.com",
"Go to web site" => "Ga naar website",
"Birthday" => "Verjaardag",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Groepen",
"Separate groups with commas" => "Gebruik komma bij meerder groepen",

View File

@ -3,7 +3,15 @@
"Cannot add empty property." => "Kan ikkje leggja til tomt felt.",
"At least one of the address fields has to be filled out." => "Minst eit av adressefelta må fyllast ut.",
"Information about vCard is incorrect. Please reload the page." => "Informasjonen om vCard-et er feil, ver venleg og last sida på nytt.",
"There is no error, the file uploaded with success" => "Ingen feil, fila vart lasta opp",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Den opplasta fila er større enn variabelen upload_max_filesize i php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Den opplasta fila er større enn variabelen MAX_FILE_SIZE i HTML-skjemaet",
"The uploaded file was only partially uploaded" => "Fila vart berre delvis lasta opp",
"No file was uploaded" => "Ingen filer vart lasta opp",
"Missing a temporary folder" => "Manglar ei mellombels mappe",
"Contacts" => "Kotaktar",
"Error" => "Feil",
"Upload too large" => "For stor opplasting",
"Download" => "Last ned",
"Edit" => "Endra",
"Delete" => "Slett",
@ -12,16 +20,21 @@
"Contact could not be found." => "Fann ikkje kontakten.",
"Work" => "Arbeid",
"Home" => "Heime",
"Other" => "Anna",
"Mobile" => "Mobil",
"Text" => "Tekst",
"Voice" => "Tale",
"Fax" => "Faks",
"Video" => "Video",
"Pager" => "Personsøkjar",
"Birthday" => "Bursdag",
"Contact" => "Kontakt",
"Add Contact" => "Legg til kontakt",
"Import" => "Importer",
"Settings" => "Innstillingar",
"Close" => "Lukk",
"Organization" => "Organisasjon",
"Birthday" => "Bursdag",
"Groups" => "Grupper",
"Preferred" => "Føretrekt",
"Phone" => "Telefonnummer",
"Email" => "Epost",
@ -38,5 +51,6 @@
"Addressbook" => "Adressebok",
"Addressbooks" => "Adressebøker",
"New Address Book" => "Ny adressebok",
"Name" => "Namn",
"Save" => "Lagre"
);

32
l10n/oc.php Normal file
View File

@ -0,0 +1,32 @@
<?php $TRANSLATIONS = array(
"No categories selected for deletion." => "Pas de categorias seleccionadas per escafar.",
"There is no error, the file uploaded with success" => "Amontcargament capitat, pas d'errors",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Lo fichièr amontcargat es tròp bèl per la directiva «upload_max_filesize » del php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Lo fichièr amontcargat es mai gròs que la directiva «MAX_FILE_SIZE» especifiada dins lo formulari HTML",
"The uploaded file was only partially uploaded" => "Lo fichièr foguèt pas completament amontcargat",
"No file was uploaded" => "Cap de fichièrs son estats amontcargats",
"Missing a temporary folder" => "Un dorsièr temporari manca",
"Contacts" => "Contactes",
"Error" => "Error",
"Upload too large" => "Amontcargament tròp gròs",
"Unable to upload your file as it is a directory or has 0 bytes" => "Impossible d'amontcargar lo teu fichièr qu'es un repertòri o que ten pas que 0 octet.",
"Upload Error" => "Error d'amontcargar",
"Pending" => "Al esperar",
"Download" => "Avalcarga",
"Edit" => "Editar",
"Delete" => "Escafa",
"Cancel" => "Annula",
"Work" => "Trabalh",
"Other" => "Autres",
"Import" => "Importa",
"Settings" => "Configuracion",
"Birthday" => "Anniversari",
"Groups" => "Grops",
"Email" => "Corrièl",
"more info" => "mai d'entresenhes",
"Primary address (Kontact et al)" => "Adreiças primarias (Kontact et al)",
"iOS/OS X" => "iOS/OS X",
"Share" => "Parteja",
"Name" => "Nom",
"Save" => "Enregistra"
);

View File

@ -126,19 +126,8 @@
"Video" => "Połączenie wideo",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Urodziny",
"Business" => "Biznesowe",
"Call" => "Wywołanie",
"Clients" => "Klienci",
"Deliverer" => "Doręczanie",
"Holidays" => "Święta",
"Ideas" => "Pomysły",
"Journey" => "Podróż",
"Jubilee" => "Jubileusz",
"Meeting" => "Spotkanie",
"Personal" => "Osobiste",
"Projects" => "Projekty",
"Questions" => "Pytania",
"Friends" => "Przyjaciele",
"Family" => "Rodzina",
"{name}'s Birthday" => "{name} Urodzony",
"Contact" => "Kontakt",
"You do not have the permissions to add contacts to this addressbook." => "Nie masz uprawnień do dodawania kontaktów do tej książki adresowej.",
@ -176,6 +165,7 @@
"Web site" => "Strona www",
"http://www.somesite.com" => "http://www.jakasstrona.pl",
"Go to web site" => "Idż do strony www",
"Birthday" => "Urodziny",
"dd-mm-yyyy" => "dd-mm-rrrr",
"Groups" => "Grupy",
"Separate groups with commas" => "Oddziel grupy przecinkami",

View File

@ -1,3 +1,6 @@
<?php $TRANSLATIONS = array(
"<h3>You have no contacts in your addressbook.</h3><p>You can import VCF files by dragging them to the contacts list and either drop them on an addressbook to import into it, or on an empty spot to create a new addressbook and import into that.<br />You can also import by clicking on the import button at the bottom of the list.</p>" => "<h3>Nie masz kontaktów w książce adresowej.</h3><p>Możesz zaimportować pliki VCF poprzez przeciągnięcie ich do listy kontaktów i albo upuścić je na książce adresowej w celu zaimportowanie ich do niej lub na pustym miejscu, aby utworzyć nowych nową książke adresową i zaimportować je do niej.<br/>Możesz również także zaimportować, klikając przycisk Importuj na dole listy.</p>"
"Settings" => "Ustawienia",
"Email" => "Email",
"<h3>You have no contacts in your addressbook.</h3><p>You can import VCF files by dragging them to the contacts list and either drop them on an addressbook to import into it, or on an empty spot to create a new addressbook and import into that.<br />You can also import by clicking on the import button at the bottom of the list.</p>" => "<h3>Nie masz kontaktów w książce adresowej.</h3><p>Możesz zaimportować pliki VCF poprzez przeciągnięcie ich do listy kontaktów i albo upuścić je na książce adresowej w celu zaimportowanie ich do niej lub na pustym miejscu, aby utworzyć nowych nową książke adresową i zaimportować je do niej.<br/>Możesz również także zaimportować, klikając przycisk Importuj na dole listy.</p>",
"Save" => "Zapisz"
);

View File

@ -126,19 +126,6 @@
"Video" => "Vídeo",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Aniversário",
"Business" => "Trabalho",
"Call" => "Chamar",
"Clients" => "Clientes",
"Deliverer" => "Entrega",
"Holidays" => "Feriados",
"Ideas" => "Idéias",
"Journey" => "Jornada",
"Jubilee" => "Jubileu",
"Meeting" => "Reunião",
"Personal" => "Pessoal",
"Projects" => "Projetos",
"Questions" => "Perguntas",
"{name}'s Birthday" => "Aniversário de {name}",
"Contact" => "Contato",
"You do not have the permissions to add contacts to this addressbook." => "Você não tem permissões para adicionar contatos a essa agenda.",
@ -176,6 +163,7 @@
"Web site" => "Web site",
"http://www.somesite.com" => "http://www.qualquersite.com",
"Go to web site" => "Ir para web site",
"Birthday" => "Aniversário",
"dd-mm-yyyy" => "dd-mm-aaaa",
"Groups" => "Grupos",
"Separate groups with commas" => "Separe grupos por virgula",

View File

@ -126,19 +126,8 @@
"Video" => "Vídeo",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Aniversário",
"Business" => "Empresa",
"Call" => "Telefonar",
"Clients" => "Clientes",
"Deliverer" => "Fornecedor",
"Holidays" => "Férias",
"Ideas" => "Ideias",
"Journey" => "Viagem",
"Jubilee" => "Jubileu",
"Meeting" => "Encontro",
"Personal" => "Pessoal",
"Projects" => "Projectos",
"Questions" => "Questões",
"Friends" => "Amigos",
"Family" => "Familia",
"{name}'s Birthday" => "Aniversário de {name}",
"Contact" => "Contacto",
"You do not have the permissions to add contacts to this addressbook." => "Não tem permissões para acrescentar contactos a este livro de endereços.",
@ -176,6 +165,7 @@
"Web site" => "Página web",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Ir para página web",
"Birthday" => "Aniversário",
"dd-mm-yyyy" => "dd-mm-aaaa",
"Groups" => "Grupos",
"Separate groups with commas" => "Separe os grupos usando virgulas",

View File

@ -25,6 +25,8 @@
"Error loading image." => "Eroare la încărcarea imaginii.",
"Error uploading contacts to storage." => "Eroare la încărcarea contactelor.",
"There is no error, the file uploaded with success" => "Nu a apărut nici o eroare, fișierul a fost încărcat cu succes",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Fișierul are o dimensiune mai mare decât cea specificată în variabila upload_max_filesize din php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Fișierul are o dimensiune mai mare decât variabile MAX_FILE_SIZE specificată în formularul HTML",
"The uploaded file was only partially uploaded" => "Fișierul a fost încărcat doar parțial",
"No file was uploaded" => "Nu a fost încărcat nici un fișier",
"Missing a temporary folder" => "Lipsește un director temporar",
@ -34,6 +36,11 @@
"Enter name" => "Specifică nume",
"Select type" => "Selectează tip",
"Edit name" => "Editează nume",
"Upload too large" => "Fișierul încărcat este prea mare",
"Unable to upload your file as it is a directory or has 0 bytes" => "Nu s-a putut încărca fișierul tău deoarece pare să fie un director sau are 0 bytes.",
"Upload Error" => "Eroare la încărcare",
"Pending" => "În așteptare",
"Importing..." => "Se importă...",
"Result: " => "Rezultat:",
" imported, " => "importat,",
"Show CardDav link" => "Arată legătură CardDav",
@ -55,15 +62,6 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Zi de naștere",
"Business" => "Companie",
"Call" => "Sună",
"Clients" => "Clienți",
"Ideas" => "Idei",
"Meeting" => "Întâlnire",
"Personal" => "Personal",
"Projects" => "Proiecte",
"Questions" => "Întrebări",
"{name}'s Birthday" => "Ziua de naștere a {name}",
"Contact" => "Contact",
"Add Contact" => "Adaugă contact",
@ -87,6 +85,7 @@
"Enter nickname" => "Introdu pseudonim",
"Web site" => "Site web",
"Go to web site" => "Vizitează site-ul",
"Birthday" => "Zi de naștere",
"dd-mm-yyyy" => "zz-ll-aaaa",
"Groups" => "Grupuri",
"Separate groups with commas" => "Separă grupurile cu virgule",
@ -131,8 +130,11 @@
"Importing contacts" => "Se importă contactele",
"Add contact" => "Adaugă contact",
"Enter description" => "Specifică descriere",
"more info" => "mai multe informații",
"Primary address (Kontact et al)" => "Adresa primară (Kontact et al)",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "Agende",
"Share" => "Partajează",
"New Address Book" => "Agendă nouă",
"Name" => "Nume",
"Description" => "Descriere",

View File

@ -126,19 +126,8 @@
"Video" => "Видео",
"Pager" => "Пейджер",
"Internet" => "Интернет",
"Birthday" => "День рождения",
"Business" => "Бизнес",
"Call" => "Вызов",
"Clients" => "Клиенты",
"Deliverer" => "Посыльный",
"Holidays" => "Праздники",
"Ideas" => "Идеи",
"Journey" => "Поездка",
"Jubilee" => "Юбилей",
"Meeting" => "Встреча",
"Personal" => "Личный",
"Projects" => "Проекты",
"Questions" => "Вопросы",
"Friends" => "Друзья",
"Family" => "Семья",
"{name}'s Birthday" => "День рождения {name}",
"Contact" => "Контакт",
"You do not have the permissions to add contacts to this addressbook." => "У вас нет права создавать контакты в этой адресной книге.",
@ -176,6 +165,7 @@
"Web site" => "Веб-сайт",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Перейти на веб-сайт",
"Birthday" => "День рождения",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "Группы",
"Separate groups with commas" => "Разделить группы запятыми",

View File

@ -126,19 +126,8 @@
"Video" => "Видео",
"Pager" => "Пейджер",
"Internet" => "Интернет",
"Birthday" => "День рождения",
"Business" => "Бизнес",
"Call" => "Вызов",
"Clients" => "Клиенты",
"Deliverer" => "Поставщик",
"Holidays" => "Праздники",
"Ideas" => "Идеи",
"Journey" => "Путешествие",
"Jubilee" => "Юбилей",
"Meeting" => "Встреча",
"Personal" => "Персональный",
"Projects" => "Проекты",
"Questions" => "Вопросы",
"Friends" => "Друзья",
"Family" => "Семья",
"{name}'s Birthday" => "{имя} день рождения",
"Contact" => "Контакт",
"You do not have the permissions to add contacts to this addressbook." => "У Вас нет разрешения для добавления контактов в эту адресную книгу.",
@ -176,6 +165,7 @@
"Web site" => "Веб-сайт",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Перейти к веб-сайту",
"Birthday" => "День рождения",
"dd-mm-yyyy" => "дд-мм-гггг",
"Groups" => "Группы",
"Separate groups with commas" => "Разделите группы запятыми",

View File

@ -51,6 +51,7 @@
"Upload too large" => "උඩුගත කිරීම විශාල වැඩිය",
"Upload Error" => "උඩුගත කිරීමේ දෝෂයක්",
"Import done" => "ආයාත කිරීම අවසන්",
"Importing..." => "ආයාත කරමින් පවති...",
"Result: " => "ප්‍රතිඵලය:",
" imported, " => "ආයාත කරන ලදී,",
" failed." => "අසාර්ථකයි",
@ -79,18 +80,6 @@
"Video" => "වීඩියෝව",
"Pager" => "පේජරය",
"Internet" => "අන්තර්ජාලය",
"Birthday" => "උපන් දිනය",
"Business" => "ව්‍යාපාරය",
"Call" => "අමතන්න",
"Clients" => "සේවාලාභීන්",
"Deliverer" => "බාරදෙන්නා",
"Holidays" => "නිවාඩු",
"Ideas" => "අදහස්",
"Journey" => "ගමන",
"Meeting" => "රැස්වීම",
"Personal" => "පෞද්ගලික",
"Projects" => "ව්‍යාපෘති",
"Questions" => "ප්‍රශ්ණ",
"{name}'s Birthday" => "{name}ගේ උපන්දිනය",
"Contact" => "සබඳතාව",
"You do not have the permissions to add contacts to this addressbook." => "මෙම ලිපින පොතට අලුත් සම්බන්ධතා එක් කිරීමට ඔබට අවසර නැත",
@ -114,6 +103,7 @@
"Web site" => "වෙබ් අඩවිය",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "වෙබ් අඩවියට යන්න",
"Birthday" => "උපන් දිනය",
"dd-mm-yyyy" => "දිදි-මාමා-වවවව",
"Groups" => "කණ්ඩායම්",
"Separate groups with commas" => "කණ්ඩායම් කොමා භාවිතයෙන් වෙන් කරන්න",
@ -156,6 +146,7 @@
"Add contact" => "සම්බන්ධතාවක් එකතු කරන්න",
"Enter description" => "විස්තරය දෙන්න",
"more info" => "තව විස්තර",
"Primary address (Kontact et al)" => "ප්‍රාථමික ලිපිනය(හැම විටම සම්බන්ධ කරගත හැක)",
"iOS/OS X" => "iOS/OS X",
"Addressbooks" => "ලිපින පොත්",
"Share" => "බෙදා හදා ගන්න",

View File

@ -126,19 +126,6 @@
"Video" => "Video",
"Pager" => "Pager",
"Internet" => "Internet",
"Birthday" => "Narodeniny",
"Business" => "Biznis",
"Call" => "Zavolať",
"Clients" => "Klienti",
"Deliverer" => "Dodávateľ",
"Holidays" => "Prázdniny",
"Ideas" => "Nápady",
"Journey" => "Cesta",
"Jubilee" => "Jubileum",
"Meeting" => "Stretnutie",
"Personal" => "Osobné",
"Projects" => "Projekty",
"Questions" => "Otázky",
"{name}'s Birthday" => "Narodeniny {name}",
"Contact" => "Kontakt",
"You do not have the permissions to add contacts to this addressbook." => "Nemáte oprávnenie pridať kontakt do adresára.",
@ -176,6 +163,7 @@
"Web site" => "Web stránka",
"http://www.somesite.com" => "http://www.stranka.sk",
"Go to web site" => "Navštíviť web",
"Birthday" => "Narodeniny",
"dd-mm-yyyy" => "dd. mm. yyyy",
"Groups" => "Skupiny",
"Separate groups with commas" => "Oddelte skupiny čiarkami",

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Pozivnik",
"Internet" => "Internet",
"Birthday" => "Rojstni dan",
"Business" => "Poslovno",
"Call" => "Klic",
"Clients" => "Stranka",
"Deliverer" => "Dostavljalec",
"Holidays" => "Prazniki",
"Ideas" => "Ideje",
"Journey" => "Potovanje",
"Jubilee" => "Obletnica",
"Meeting" => "Srečanje",
"Personal" => "Osebno",
"Projects" => "Projekti",
"Questions" => "Vprašanja",
"Friends" => "Prijatelji",
"Family" => "Družina",
"{name}'s Birthday" => "{name} - rojstni dan",
"Contact" => "Stik",
"You do not have the permissions to add contacts to this addressbook." => "Ni ustreznih dovoljenj za dodajanje stikov v ta imenik.",
@ -176,6 +165,7 @@
"Web site" => "Spletna stran",
"http://www.somesite.com" => "http://www.spletnastran.si",
"Go to web site" => "Pojdi na spletno stran",
"Birthday" => "Rojstni dan",
"dd-mm-yyyy" => "dd. mm. yyyy",
"Groups" => "Skupine",
"Separate groups with commas" => "Skupine ločite z vejicami",

View File

@ -1,6 +1,18 @@
<?php $TRANSLATIONS = array(
"No categories selected for deletion." => "Ни једна категорија није означена за брисање.",
"Information about vCard is incorrect. Please reload the page." => "Подаци о вКарти су неисправни. Поново учитајте страницу.",
"There is no error, the file uploaded with success" => "Нема грешке, фајл је успешно послат",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Послати фајл превазилази директиву upload_max_filesize из ",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Послати фајл превазилази директиву MAX_FILE_SIZE која је наведена у ХТМЛ форми",
"The uploaded file was only partially uploaded" => "Послати фајл је само делимично отпремљен!",
"No file was uploaded" => "Ниједан фајл није послат",
"Missing a temporary folder" => "Недостаје привремена фасцикла",
"Contacts" => "Контакти",
"Error" => "Грешка",
"Upload too large" => "Пошиљка је превелика",
"Unable to upload your file as it is a directory or has 0 bytes" => "Није могуће послати датотеку или зато што је директоријуму или јој је величина 0 бајта",
"Upload Error" => "Грешка у слању",
"Pending" => "На чекању",
"Download" => "Преузимање",
"Edit" => "Уреди",
"Delete" => "Обриши",
@ -9,16 +21,21 @@
"Contact could not be found." => "Контакт се не може наћи.",
"Work" => "Посао",
"Home" => "Кућа",
"Other" => "Друго",
"Mobile" => "Мобилни",
"Text" => "Текст",
"Voice" => "Глас",
"Fax" => "Факс",
"Video" => "Видео",
"Pager" => "Пејџер",
"Birthday" => "Рођендан",
"Contact" => "Контакт",
"Add Contact" => "Додај контакт",
"Import" => "Увези",
"Settings" => "Подешавања",
"Close" => "Затвори",
"Organization" => "Организација",
"Birthday" => "Рођендан",
"Groups" => "Групе",
"Preferred" => "Пожељан",
"Phone" => "Телефон",
"Email" => "Е-маил",
@ -34,6 +51,8 @@
"Country" => "Земља",
"Addressbook" => "Адресар",
"Addressbooks" => "Адресар",
"Share" => "Дељење",
"New Address Book" => "Нови адресар",
"Name" => "Име",
"Save" => "Сними"
);

View File

@ -1,20 +1,33 @@
<?php $TRANSLATIONS = array(
"Information about vCard is incorrect. Please reload the page." => "Podaci o vKarti su neispravni. Ponovo učitajte stranicu.",
"There is no error, the file uploaded with success" => "Nema greške, fajl je uspešno poslat",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "Poslati fajl prevazilazi direktivu upload_max_filesize iz ",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "Poslati fajl prevazilazi direktivu MAX_FILE_SIZE koja je navedena u HTML formi",
"The uploaded file was only partially uploaded" => "Poslati fajl je samo delimično otpremljen!",
"No file was uploaded" => "Nijedan fajl nije poslat",
"Missing a temporary folder" => "Nedostaje privremena fascikla",
"Upload too large" => "Pošiljka je prevelika",
"Download" => "Preuzmi",
"Edit" => "Uredi",
"Delete" => "Obriši",
"Cancel" => "Otkaži",
"This is not your addressbook." => "Ovo nije vaš adresar.",
"Contact could not be found." => "Kontakt se ne može naći.",
"Work" => "Posao",
"Home" => "Kuća",
"Other" => "Drugo",
"Mobile" => "Mobilni",
"Text" => "Tekst",
"Voice" => "Glas",
"Fax" => "Faks",
"Video" => "Video",
"Pager" => "Pejdžer",
"Birthday" => "Rođendan",
"Add Contact" => "Dodaj kontakt",
"Settings" => "Podešavanja",
"Close" => "Zatvori",
"Organization" => "Organizacija",
"Birthday" => "Rođendan",
"Groups" => "Grupe",
"Phone" => "Telefon",
"Email" => "E-mail",
"Address" => "Adresa",
@ -23,5 +36,7 @@
"City" => "Grad",
"Region" => "Regija",
"Zipcode" => "Zip kod",
"Country" => "Zemlja"
"Country" => "Zemlja",
"Name" => "Ime",
"Save" => "Snimi"
);

View File

@ -126,19 +126,8 @@
"Video" => "Video",
"Pager" => "Personsökare",
"Internet" => "Internet",
"Birthday" => "Födelsedag",
"Business" => "Företag",
"Call" => "Ring",
"Clients" => "Kunder",
"Deliverer" => "Leverera",
"Holidays" => "Helgdagar",
"Ideas" => "Idéer",
"Journey" => "Resa",
"Jubilee" => "Jubileum",
"Meeting" => "Möte",
"Personal" => "Privat",
"Projects" => "Projekt",
"Questions" => "Frågor",
"Friends" => "Vänner",
"Family" => "Familj",
"{name}'s Birthday" => "{name}'s födelsedag",
"Contact" => "Kontakt",
"You do not have the permissions to add contacts to this addressbook." => "Du har inte behörighet att lägga till kontakter i denna adressbok.",
@ -176,6 +165,7 @@
"Web site" => "Webbplats",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "Gå till webbplats",
"Birthday" => "Födelsedag",
"dd-mm-yyyy" => "dd-mm-åååå",
"Groups" => "Grupper",
"Separate groups with commas" => "Separera grupperna med kommatecken",

33
l10n/ta_LK.php Normal file
View File

@ -0,0 +1,33 @@
<?php $TRANSLATIONS = array(
"No categories selected for deletion." => "நீக்குவதற்கு எந்தப் பிரிவும் தெரிவுசெய்யப்படவில்லை.",
"There is no error, the file uploaded with success" => "இங்கு வழு இல்லை, கோப்பு வெற்றிகரமாக பதிவேற்றப்பட்டது",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" => "பதிவேற்றப்பட்ட கோப்பானது php.ini இலுள்ள upload_max_filesize directive ஐ விட கூடியது",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" => "பதிவேற்றப்பட்ட கோப்பானது HTML படிவத்தில் குறிப்பிடப்பட்டுள்ள MAX_FILE_SIZE directive ஐ விட கூடியது",
"The uploaded file was only partially uploaded" => "பதிவேற்றப்பட்ட கோப்பானது பகுதியாக மட்டுமே பதிவேற்றப்பட்டுள்ளது",
"No file was uploaded" => "எந்த கோப்பும் பதிவேற்றப்படவில்லை",
"Missing a temporary folder" => "ஒரு தற்காலிகமான கோப்புறையை காணவில்லை",
"Contacts" => "தொடர்புகள்",
"Error" => "வழு",
"Upload too large" => "பதிவேற்றல் மிகப்பெரியது",
"Unable to upload your file as it is a directory or has 0 bytes" => "அடைவு அல்லது 0 bytes ஐ கொண்டுள்ளதால் உங்களுடைய கோப்பை பதிவேற்ற முடியவில்லை",
"Upload Error" => "பதிவேற்றல் வழு",
"Pending" => "நிலுவையிலுள்ள",
"Download" => "பதிவிறக்குக",
"Edit" => "தொகுக்க",
"Delete" => "அழிக்க",
"Cancel" => "இரத்து செய்க",
"Work" => "வேலை",
"Other" => "மற்றவை",
"Text" => "உரை",
"Import" => "இறக்குமதி",
"Settings" => "அமைப்புகள்",
"Close" => "மூடுக",
"Birthday" => "பிறந்த நாள்",
"Email" => "மின்னஞ்சல்",
"more info" => "மேலதிக தகவல்",
"Primary address (Kontact et al)" => "முதன்மை முகவரி (Kontact et al)",
"iOS/OS X" => "iOS/OS X",
"Share" => "பகிர்வு",
"Name" => "பெயர்",
"Save" => "சேமிக்க"
);

View File

@ -126,19 +126,8 @@
"Video" => "วีดีโอ",
"Pager" => "เพจเจอร์",
"Internet" => "อินเทอร์เน็ต",
"Birthday" => "วันเกิด",
"Business" => "ธุรกิจ",
"Call" => "โทร",
"Clients" => "ลูกค้า",
"Deliverer" => "ผู้จัดส่ง",
"Holidays" => "วันหยุด",
"Ideas" => "ไอเดีย",
"Journey" => "การเดินทาง",
"Jubilee" => "งานเฉลิมฉลอง",
"Meeting" => "ประชุม",
"Personal" => "ส่วนตัว",
"Projects" => "โปรเจค",
"Questions" => "คำถาม",
"Friends" => "เพื่อน",
"Family" => "ครอบครัว",
"{name}'s Birthday" => "วันเกิดของ {name}",
"Contact" => "ข้อมูลการติดต่อ",
"You do not have the permissions to add contacts to this addressbook." => "คุณไม่ได้รับสิทธิ์ให้เพิ่มข้อมูลผู้ติดต่อเข้าไปในสมุดบันทึกที่อยู่นี้",
@ -176,6 +165,7 @@
"Web site" => "เว็บไซต์",
"http://www.somesite.com" => "http://www.somesite.com",
"Go to web site" => "ไปที่เว็บไซต์",
"Birthday" => "วันเกิด",
"dd-mm-yyyy" => "dd-mm-yyyy",
"Groups" => "กลุ่ม",
"Separate groups with commas" => "คั่นระหว่างรายชื่อกลุ่มด้วยเครื่องหมายจุลภาีคหรือคอมม่า",

Some files were not shown because too many files have changed in this diff Show More