]> source.dussan.org Git - nextcloud-server.git/commitdiff
Contacts: UI updates and ajax methods for categories.
authorThomas Tanghus <thomas@tanghus.net>
Wed, 7 Mar 2012 15:39:56 +0000 (16:39 +0100)
committerThomas Tanghus <thomas@tanghus.net>
Wed, 7 Mar 2012 15:39:56 +0000 (16:39 +0100)
14 files changed:
apps/contacts/ajax/addproperty.php
apps/contacts/ajax/categories/add.php [new file with mode: 0644]
apps/contacts/ajax/categories/checksumfor.php [new file with mode: 0644]
apps/contacts/ajax/categories/delete.php [new file with mode: 0644]
apps/contacts/ajax/categories/edit.php [new file with mode: 0644]
apps/contacts/ajax/categories/list.php [new file with mode: 0644]
apps/contacts/ajax/contactdetails.php
apps/contacts/ajax/saveproperty.php
apps/contacts/css/contacts.css
apps/contacts/index.php
apps/contacts/js/contacts.js
apps/contacts/lib/vcard.php
apps/contacts/templates/part.contact.php
lib/vcategories.php

index 028974e1c66b1bc38dea45ec7512127a3a1e07aa..a28aed34d2ca86e4405b6602cc13f483178c0972 100644 (file)
@@ -27,14 +27,16 @@ require_once('../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('contacts');
 
-$id = $_POST['id'];
-$vcard = OC_Contacts_App::getContactVCard( $id );
+$id = isset($_POST['id'])?$_POST['id']:null;
+$name = isset($_POST['name'])?$_POST['name']:null;
+$value = isset($_POST['value'])?$_POST['value']:null;
+$parameters = isset($_POST['parameters'])?$_POST['parameters']:array();
+
+$vcard = OC_Contacts_App::getContactVCard($id);
 
-$name = $_POST['name'];
-$value = $_POST['value'];
 if(!is_array($value)){
        $value = trim($value);
-       if(!$value && in_array($name, array('TEL', 'EMAIL', 'ORG', 'BDAY', 'NICKNAME'))) {
+       if(!$value && in_array($name, array('TEL', 'EMAIL', 'ORG', 'BDAY', 'NICKNAME', 'NOTE'))) {
                OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Cannot add empty property.'))));
                exit();
        }
@@ -51,7 +53,6 @@ if(!is_array($value)){
                exit();
        }
 }
-$parameters = isset($_POST['parameters']) ? $_POST['parameters'] : array();
 
 // Prevent setting a duplicate entry
 $current = $vcard->select($name);
@@ -82,7 +83,9 @@ switch($name) {
                }
        case 'N':
        case 'ORG':
+       case 'NOTE':
        case 'NICKNAME':
+               // TODO: Escape commas and semicolons.
                break;
        case 'EMAIL':
                $value = strtolower($value);
diff --git a/apps/contacts/ajax/categories/add.php b/apps/contacts/ajax/categories/add.php
new file mode 100644 (file)
index 0000000..9b6c262
--- /dev/null
@@ -0,0 +1,39 @@
+<?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.
+ */
+
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+
+function bailOut($msg) {
+       OC_JSON::error(array('data' => array('message' => $msg)));
+       OC_Log::write('contacts','ajax/categories/add.php: '.$msg, OC_Log::DEBUG);
+       exit();
+}
+function debug($msg) {
+       OC_Log::write('contacts','ajax/categories/add.php: '.$msg, OC_Log::DEBUG);
+}
+
+$category = isset($_GET['category'])?strip_tags($_GET['category']):null;
+
+if(is_null($category)) {
+       bailOut(OC_Contacts_App::$l10n->t('No category to add?'));
+}
+
+debug(print_r($category, true));
+
+$categories = new OC_VCategories('contacts');
+if($categories->hasCategory($category)) {
+       bailOut(OC_Contacts_App::$l10n->t('This category already exists: '.$category));
+} else {
+       $categories->add($category, true);
+}
+
+OC_JSON::success(array('data' => array('categories'=>$categories->categories())));
+
+?>
diff --git a/apps/contacts/ajax/categories/checksumfor.php b/apps/contacts/ajax/categories/checksumfor.php
new file mode 100644 (file)
index 0000000..ff53586
--- /dev/null
@@ -0,0 +1,28 @@
+<?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.
+ */
+
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+
+$id = isset($_GET['id'])?$_GET['id']:null;
+if(is_null($id)) {
+       OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('No ID provided'))));
+       exit();
+}
+$vcard = OC_Contacts_App::getContactVCard( $id );
+foreach($vcard->children as $property){
+       //OC_Log::write('contacts','ajax/categories/checksumfor.php: '.$property->name, OC_Log::DEBUG);
+       if($property->name == 'CATEGORIES') {
+               $checksum = md5($property->serialize());
+               OC_JSON::success(array('data' => array('checksum'=>$checksum)));
+               exit();
+       }
+}
+OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error setting checksum.'))));
+?>
diff --git a/apps/contacts/ajax/categories/delete.php b/apps/contacts/ajax/categories/delete.php
new file mode 100644 (file)
index 0000000..3ba5aa1
--- /dev/null
@@ -0,0 +1,60 @@
+<?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.
+ */
+
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+
+foreach ($_POST as $key=>$element) {
+       debug('_POST: '.$key.'=>'.print_r($element, true));
+}
+
+function bailOut($msg) {
+       OC_JSON::error(array('data' => array('message' => $msg)));
+       OC_Log::write('contacts','ajax/categories/delete.php: '.$msg, OC_Log::DEBUG);
+       exit();
+}
+function debug($msg) {
+       OC_Log::write('contacts','ajax/categories/delete.php: '.$msg, OC_Log::DEBUG);
+}
+
+$categories = isset($_POST['categories'])?$_POST['categories']:null;
+
+if(is_null($categories)) {
+       bailOut(OC_Contacts_App::$l10n->t('No categories selected for deletion.'));
+}
+
+debug(print_r($categories, true));
+
+$addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser());
+if(count($addressbooks) == 0) {
+       bailOut(OC_Contacts_App::$l10n->t('No address books found.'));
+}
+$addressbookids = array();
+foreach($addressbooks as $addressbook) {
+       $addressbookids[] = $addressbook['id'];
+} 
+$contacts = OC_Contacts_VCard::all($addressbookids);
+if(count($contacts) == 0) {
+       bailOut(OC_Contacts_App::$l10n->t('No contacts found.'));
+}
+
+$cards = array();
+foreach($contacts as $contact) {
+       $cards[] = array($contact['id'], $contact['carddata']);
+} 
+
+debug('Before delete: '.print_r($categories, true));
+
+$catman = new OC_VCategories('contacts');
+$catman->delete($categories, $cards);
+debug('After delete: '.print_r($catman->categories(), true));
+OC_Contacts_VCard::updateDataByID($cards);
+OC_JSON::success(array('data' => array('categories'=>$catman->categories())));
+
+?>
diff --git a/apps/contacts/ajax/categories/edit.php b/apps/contacts/ajax/categories/edit.php
new file mode 100644 (file)
index 0000000..8ecc354
--- /dev/null
@@ -0,0 +1,28 @@
+<?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.
+ */
+
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+function bailOut($msg) {
+       OC_JSON::error(array('data' => array('message' => $msg)));
+       OC_Log::write('contacts','ajax/categories/edit.php: '.$msg, OC_Log::DEBUG);
+       exit();
+}
+function debug($msg) {
+       OC_Log::write('contacts','ajax/categories/edit.php: '.$msg, OC_Log::DEBUG);
+}
+
+$tmpl = new OC_TEMPLATE("contacts", "part.edit_categories_dialog");
+
+$categories = OC_Contacts_App::$categories->categories();
+debug(print_r($categories, true));
+$tmpl->assign('categories',$categories);
+$tmpl->printpage();
+
+?>
diff --git a/apps/contacts/ajax/categories/list.php b/apps/contacts/ajax/categories/list.php
new file mode 100644 (file)
index 0000000..3b41b7b
--- /dev/null
@@ -0,0 +1,17 @@
+<?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.
+ */
+
+require_once('../../../../lib/base.php');
+OC_JSON::checkLoggedIn();
+OC_JSON::checkAppEnabled('contacts');
+
+$categories = OC_Contacts_App::$categories->categories();
+
+OC_JSON::success(array('data' => array('categories'=>$categories)));
+
+?>
index f35fd595c5667d9e6547a51ea7de46f0860a5ae2..03895c862aab3ed195a885a962e6b54506e96737 100644 (file)
@@ -71,5 +71,5 @@ if(isset($details['PHOTO'])) {
        $details['PHOTO'] = false;
 }
 $details['id'] = $id;
-
+OC_Contacts_App::setLastModifiedHeader($vcard);
 OC_JSON::success(array('data' => $details));
index 6f8366243fe59377fe7dfce0be076102ee9b8f1c..e31c4b7c2384be57e303fddcc3b57b8c2acf5184 100644 (file)
@@ -36,7 +36,7 @@ function debug($msg) {
        OC_Log::write('contacts','ajax/saveproperty.php: '.$msg, OC_Log::DEBUG);
 }
 foreach ($_POST as $key=>$element) {
-       debug('_POST: '.$key.'=>'.$element);
+       debug('_POST: '.$key.'=>'.print_r($element, true));
 }
 
 $id = isset($_POST['id'])?$_POST['id']:null;
@@ -51,12 +51,8 @@ $checksum = isset($_POST['checksum'])?$_POST['checksum']:null;
 //     }
 // }
 
-if(is_array($value)){
-       $value = array_map('strip_tags', $value);
-       ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form!
-       $value = OC_VObject::escapeSemicolons($value);
-} else {
-       $value = trim(strip_tags($value));
+if(!$name) {
+       bailOut(OC_Contacts_App::$l10n->t('element name is not set.'));
 }
 if(!$id) {
        bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
@@ -64,14 +60,22 @@ if(!$id) {
 if(!$checksum) {
        bailOut(OC_Contacts_App::$l10n->t('checksum is not set.'));
 }
-if(!$name) {
-       bailOut(OC_Contacts_App::$l10n->t('element name is not set.'));
+if(is_array($value)){
+       $value = array_map('strip_tags', $value);
+       ksort($value); // NOTE: Important, otherwise the compound value will be set in the order the fields appear in the form!
+       if($name == 'CATEGORIES') {
+               $value = OC_Contacts_VCard::escapeDelimiters($value, ',');
+       } else {
+               $value = OC_Contacts_VCard::escapeDelimiters($value, ';');
+       }
+} else {
+       $value = trim(strip_tags($value));
 }
 
 $vcard = OC_Contacts_App::getContactVCard( $id );
 $line = OC_Contacts_App::getPropertyLineByChecksum($vcard, $checksum);
 if(is_null($line)) {
-       bailOut(OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page.'.$checksum.' "'.$line.'"'));
+       bailOut(OC_Contacts_App::$l10n->t('Information about vCard is incorrect. Please reload the page: ').$checksum);
 }
 $element = $vcard->children[$line]->name;
 
@@ -91,7 +95,9 @@ switch($element) {
                }
        case 'N':
        case 'ORG':
+       case 'NOTE':
        case 'NICKNAME':
+       case 'CATEGORIES':
                debug('Setting string:'.$name.' '.$value);
                $vcard->setString($name, $value);
                break;
@@ -123,12 +129,8 @@ $checksum = md5($vcard->children[$line]->serialize());
 debug('New checksum: '.$checksum);
 
 if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
-       OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error updating contact property.'))));
-       OC_Log::write('contacts','ajax/setproperty.php: Error updating contact property: '.$value, OC_Log::ERROR);
+       bailOut(OC_Contacts_App::$l10n->t('Error updating contact property.'));
        exit();
 }
 
-//$adr_types = OC_Contacts_App::getTypesOfProperty('ADR');
-//$phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
-
 OC_JSON::success(array('data' => array( 'line' => $line, 'checksum' => $checksum, 'oldchecksum' => $_POST['checksum'] )));
index 384541f37518cc751b1b48b30f4360adec5d94ad..65a6eb24809745d0c741ff8b19ea732f6e814d1e 100644 (file)
@@ -20,7 +20,8 @@
 #firstrun { /*border: thin solid lightgray;*/ width: 80%; margin: 5em auto auto auto; text-align: center; font-weight:bold; font-size:1.5em;  color:#777;}
 #firstrun #selections { /*border: thin solid lightgray;*/ font-size:0.8em;  width: 100%; margin: 2em auto auto auto; clear: both; }
 
-#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 16em; }
+#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; }
+.categories { float: left; width: 16em; }
 #card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #f8f8f8; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none  !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; }
 #card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; float: left; }
 input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { background-color: #ffc0c0 !important; }
@@ -68,7 +69,7 @@ dl.form
        /*background-color: yellow;*/
 }
 
-.loading { background: url('../../../core/img/loading.gif') no-repeat center !important;}
+.loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; }
 
 /*.add { cursor: pointer;  width: 25px; height: 25px; margin: 0px; float: right; position:relative; content: "\+"; font-weight: bold; color: #666; font-size: large; bottom: 0px; right: 0px; clear: both; text-align: center; vertical-align: bottom; display: none; }*/
 
@@ -185,4 +186,12 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; }
 .propertylist li > input[type="checkbox"],input[type="radio"] { float: left; clear: left; width: 20px; height: 20px; vertical-align: middle; }
 .propertylist li > select { float: left; max-width: 8em; }
 .typelist { float: left; max-width: 10em; } /* for multiselect */
-.addresslist { clear: both; }
\ No newline at end of file
+.addresslist { clear: both; }
+
+#categoryform .scrollarea { position: absolute; left: 10px; top: 10px; right: 10px; bottom: 50px; overflow: auto; border:1px solid #ddd; background: #f8f8f8; }
+#categoryform .bottombuttons { position: absolute; bottom: 10px;}
+#categoryform .bottombuttons * { float: left;}
+/*#categorylist { border:1px solid #ddd;}*/
+#categorylist li { background:#f8f8f8; padding:.3em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; }
+#categorylist li:hover, li:active { background:#eee; }
+#category_addinput { width: 10em; }
\ No newline at end of file
index 0a21ddd04b6ac2ae70ec5ea40db81696ae623d95..a7817d35e586bc62113a75718cc7e41d3a6b3d7d 100644 (file)
@@ -34,6 +34,7 @@ if(!is_null($id)) {
 }
 $property_types = OC_Contacts_App::getAddPropertyOptions();
 $phone_types = OC_Contacts_App::getTypesOfProperty('TEL');
+$categories = OC_Contacts_App::$categories->categories();
 
 $upload_max_filesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize'));
 $post_max_size = OC_Helper::computerFileSize(ini_get('post_max_size'));
@@ -59,6 +60,7 @@ $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
 $tmpl->assign('uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
 $tmpl->assign('property_types',$property_types);
 $tmpl->assign('phone_types',$phone_types);
+$tmpl->assign('categories',$categories);
 $tmpl->assign('addressbooks', $addressbooks);
 $tmpl->assign('contacts', $contacts);
 $tmpl->assign('details', $details );
index 11661320c5901d237f3953eb49f9cac7fd57e776..7306e5714c91f47f425b88e2d580148e1be5b276 100644 (file)
@@ -4,13 +4,105 @@ function ucwords (str) {
        });
 }
 
-String.prototype.strip_tags = function(){
-       tags = this;
-       stripped = tags.replace(/[\<\>]/gi, "");
-       return stripped;
+Categories={
+       edit:function(){
+               console.log('Categories.edit');
+               $('body').append('<div id="category_dialog"></div>');
+               $('#category_dialog').load(OC.filePath('contacts', 'ajax', 'categories/edit.php'), function(response, status, xhr){
+                       try {
+                               var response = jQuery.parseJSON(response);
+                               console.log('status: ' + status + ', response: ' + response + ', response.status:' + response.status);
+                               if(response.status == 'error'){
+                                       alert(response.data.message);
+                               } else {
+                                       alert(response);
+                               }
+                       } catch(e) {
+                               $('#edit_categories_dialog').dialog({
+                                               modal: true,
+                                               height: 350, minHeight:200, width: 250, minWidth: 200,
+                                               buttons: {
+                                                       'Delete':function() {
+                                                               Categories.delete();
+                                                       },
+                                                       'Rescan':function() {
+                                                               Categories.rescan();
+                                                       }
+                                               },
+                                               close : function(event, ui) {
+                                                       //alert('close');
+                                                       $(this).dialog('destroy').remove();
+                                                       $('#category_dialog').remove();
+                                               },
+                                               open : function(event, ui) {
+                                                       $('#category_addinput').live('input',function(){
+                                                               if($(this).val().length > 0) {
+                                                                       $('#category_addbutton').removeAttr('disabled');
+                                                               }
+                                                       });
+                                                       $('#categoryform').submit(function() {
+                                                               Categories.add($('#category_addinput').val());
+                                                               $('#category_addinput').val('');
+                                                               $('#category_addbutton').attr('disabled', 'disabled');
+                                                               return false;
+                                                       });
+                                                       $('#category_addbutton').live('click',function(e){
+                                                               e.preventDefault();
+                                                               if($('#category_addinput').val().length > 0) {
+                                                                       Categories.add($('#category_addinput').val());
+                                                                       $('#category_addinput').val('');
+                                                               }
+                                                       });
+                                               }
+                               });
+                       }
+               });
+       },
+       delete:function(){
+               var categories = $('#categorylist').find('input[type="checkbox"]').serialize();
+               console.log('Categories.delete: ' + categories);
+               $.post(OC.filePath('contacts', 'ajax', 'categories/delete.php'),categories,function(jsondata){
+                       if(jsondata.status == 'success'){
+                               Categories._update(jsondata.data.categories);
+                       } else {
+                               alert(jsondata.data.message);
+                       }
+               });
+       },
+       add:function(category){
+               console.log('Categories.add ' + category);
+               $.getJSON(OC.filePath('contacts', 'ajax', 'categories/add.php'),{'category':category},function(jsondata){
+                       if(jsondata.status == 'success'){
+                               Categories._update(jsondata.data.categories);
+                       } else {
+                               alert(jsondata.data.message);
+                       }
+               });
+               return false;
+       },
+       rescan:function(){
+               console.log('Categories.rescan');
+               $.getJSON(OC.filePath('contacts', 'ajax', 'categories/rescan.php'),{},function(jsondata){
+                       if(jsondata.status == 'success'){
+                               Categories._update(jsondata.data.categories);
+                       } else {
+                               alert(jsondata.data.message);
+                       }
+               });
+       },
+       _update:function(categories){
+               var categorylist = $('#categorylist');
+               categorylist.find('li').remove();
+               for(var category in categories) {
+                       var item = '<li><input type="checkbox" name="categories" value="' + categories[category] + '" />' + categories[category] + '</li>';
+                       $(item).appendTo(categorylist);
+               }
+               if(Categories.changed != undefined) {
+                       Categories.changed(categories);
+               }
+       }
 }
 
-
 Contacts={
        UI:{
                notImplemented:function() {
@@ -125,22 +217,26 @@ Contacts={
                                // NOTE: Do we ever get here?
                                $('#messagebox').dialog('moveToTop');
                        }else{
-                               $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'messagebox.php'), function(){
-                                       $('#messagebox').dialog(
-                                               {
-                                                       autoOpen: true,
-                                                       title: title,
-                                                       buttons: [{
-                                                                               text: "Ok",
-                                                                               click: function() { $(this).dialog("close"); }
-                                                                       }],
-                                                       close: function(event, ui) {
-                                                               $(this).dialog('destroy').remove();
-                                                       },
-                                                       open: function(event, ui) {
-                                                               $('#messagebox_msg').html(msg);
-                                                       }
-                                       });
+                               $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'messagebox.php'), function(jsondata){
+                                       if(jsondata.status != 'error'){
+                                               $('#messagebox').dialog(
+                                                       {
+                                                               autoOpen: true,
+                                                               title: title,
+                                                               buttons: [{
+                                                                                       text: "Ok",
+                                                                                       click: function() { $(this).dialog("close"); }
+                                                                               }],
+                                                               close: function(event, ui) {
+                                                                       $(this).dialog('destroy').remove();
+                                                               },
+                                                               open: function(event, ui) {
+                                                                       $('#messagebox_msg').html(msg);
+                                                               }
+                                               });
+                                       } else {
+                                               alert(jsondata.data.message);
+                                       }
                                });
                        }
                },
@@ -182,6 +278,12 @@ Contacts={
                        $('#bday').datepicker({
                                                dateFormat : 'dd-mm-yy'
                        });
+                       $('#categories_value').find('select').multiselect({
+                                                                               noneSelectedText: t('contacts', 'Select categories'),
+                                                                               header: false,
+                                                                               selectedList: 6,
+                                                                               classes: 'categories'
+                                                                       });
                        // Style phone types
                        $('#phonelist').find('select[class*="contacts_property"]').multiselect({
                                                                                                        noneSelectedText: t('contacts', 'Select type'),
@@ -361,7 +463,7 @@ Contacts={
                                this.data = jsondata;
                                this.id = this.data.id;
                                $('#rightcontent').data('id',this.id);
-                               //console.log('loaded: ' + this.data.FN[0]['value']);
+                               console.log('loaded: ' + this.data.FN[0]['value']);
                                this.populateNameFields();
                                this.loadCategories();
                                this.loadPhoto();
@@ -369,6 +471,16 @@ Contacts={
                                this.loadPhones();
                                this.loadAddresses();
                                this.loadSingleProperties();
+                               // TODO: load NOTE ;-)
+                               if(this.data.NOTE) {
+                                       $('#note').data('checksum', this.data.NOTE[0]['checksum']);
+                                       $('#note').find('textarea').val(this.data.NOTE[0]['value']);
+                                       $('#note').show();
+                               } else {
+                                       $('#note').data('checksum', '');
+                                       $('#note').find('textarea').val('');
+                                       $('#note').hide();
+                               }
                        },
                        loadSingleProperties:function() {
                                var props = ['BDAY', 'NICKNAME', 'ORG'];
@@ -457,21 +569,64 @@ Contacts={
                                $('#contact_identity').find('*[data-element="FN"]').data('checksum', this.data.FN[0]['checksum']);
                                $('#contact_identity').show();
                        },
-                       loadCategories:function(){
+                       hasCategory:function(category) {
+                               if(this.data.CATEGORIES) {
+                                       for(var c in this.data.CATEGORIES[0]['value']) {
+                                               var cat = this.data.CATEGORIES[0]['value'][c];
+                                               //console.log('hasCategory: ' + cat + ' === ' + category + '?');
+                                               if(typeof cat === 'string' && (cat.toUpperCase() === category.toUpperCase())) {
+                                                       //console.log('Yes');
+                                                       return true;
+                                               }
+                                       }
+                               }
+                               return false;
+                       },
+                       categoriesChanged:function(categories) { // Categories added/deleted.
+                               console.log('categoriesChanged for ' + Contacts.UI.Card.id + ' : ' + categories);
+                               var categorylist = $('#categories_value').find('select');
+                               categorylist.find('option').remove();
+                               for(var category in categories) {
+                                       console.log('categoriesChanged: ' + categories[category]);
+                                       var selected = Contacts.UI.Card.hasCategory(categories[category]) ? ' selected="selected"' : '';
+                                       var item = '<option value="' + categories[category] + '"' + selected + '>' + categories[category] + '</option>';
+                                       $(item).appendTo(categorylist);
+                               }
+                               $('#categories_value').find('select').multiselect('refresh');
+                               $.getJSON(OC.filePath('contacts', 'ajax', 'categories/checksumfor.php'),{'id':Contacts.UI.Card.id},function(jsondata){
+                                       if(jsondata.status == 'success'){
+                                               console.log('Setting checksum: ' + jsondata.data.checksum);
+                                               $('#categories_value').data('checksum', jsondata.data.checksum);
+                                       } else {
+                                               Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
+                                       }
+                               });
+                       },
+                       loadCategories:function(){ // On loading contact.
+                               var categories = $('#categories_value').find('select');
                                if(this.data.CATEGORIES) {
-                                       //
+                                       $('#categories_value').data('checksum', this.data.CATEGORIES[0]['checksum']);
+                               } else {
+                                       $('#categories_value').data('checksum', '');
                                }
+                               categories.find('option').each(function(){ 
+                                       if(Contacts.UI.Card.hasCategory($(this).val())) {
+                                               $(this).attr('selected', 'selected');
+                                       } else {
+                                               $(this).removeAttr('selected');
+                                       }
+                               });
+                               categories.multiselect('refresh');
                        },
                        editNew:function(){ // add a new contact
                                this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = '';
-                               $.getJSON('ajax/newcontact.php',{},function(jsondata){
+                               $.getJSON(OC.filePath('contacts', 'ajax', 'newcontact.php'),{},function(jsondata){
                                        if(jsondata.status == 'success'){
                                                id = '';
                                                $('#rightcontent').data('id','');
                                                $('#rightcontent').html(jsondata.data.page);
                                                Contacts.UI.Card.editName();
-                                       }
-                                       else{
+                                       } else {
                                                Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
                                                //alert(jsondata.data.message);
                                        }
@@ -479,14 +634,19 @@ Contacts={
                        },
                        savePropertyInternal:function(name, fields, oldchecksum, checksum){
                                // TODO: Add functionality for new fields.
-                               //console.log('savePropertyInternal: ' + name + ', checksum: ' + checksum);
-                               //console.log('savePropertyInternal: ' + this.data[name]);
+                               console.log('savePropertyInternal: ' + name + ', fields: ' + fields + 'checksum: ' + checksum);
+                               console.log('savePropertyInternal: ' + this.data[name]);
+                               var multivalue = ['CATEGORIES'];
                                var params = {};
-                               var value = undefined;
+                               var value = multivalue.indexOf(name) != -1 ? new Array() : undefined;
                                jQuery.each(fields, function(i, field){
                                        //.substring(11,'parameters[TYPE][]'.indexOf(']'))
                                        if(field.name.substring(0, 5) === 'value') {
-                                               value = field.value;
+                                               if(multivalue.indexOf(name) != -1) {
+                                                       value.push(field.value);
+                                               } else {
+                                                       value = field.value;
+                                               }
                                        } else if(field.name.substring(0, 10) === 'parameters') {
                                                var p = field.name.substring(11,'parameters[TYPE][]'.indexOf(']'));
                                                if(!(p in params)) {
@@ -506,7 +666,7 @@ Contacts={
                        saveProperty:function(obj){
                                // I couldn't get the selector to filter on 'contacts_property' so I filter by hand here :-/
                                if(!$(obj).hasClass('contacts_property')) {
-                                       //console.log('Filtering out object.' + obj);
+                                       console.log('Filtering out object.' + obj);
                                        return false;
                                }
                                if($(obj).hasClass('nonempty') && $(obj).val().trim() == '') {
@@ -529,32 +689,38 @@ Contacts={
                                if(checksum != undefined && checksum != '') { // save
                                        q = q + '&checksum=' + checksum;
                                        console.log('Saving: ' + q);
+                                       $(obj).attr('disabled', 'disabled');
                                        $.post('ajax/saveproperty.php',q,function(jsondata){
                                                if(jsondata.status == 'success'){
                                                        container.data('checksum', jsondata.data.checksum);
                                                        Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum);
                                                        Contacts.UI.loading(container, false);
+                                                       $(obj).removeAttr('disabled');
                                                        return true;
                                                }
                                                else{
                                                        Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
                                                        Contacts.UI.loading(container, false);
+                                                       $(obj).removeAttr('disabled');
                                                        return false;
                                                }
                                        },'json');
                                } else { // add
                                        console.log('Adding: ' + q);
+                                       $(obj).attr('disabled', 'disabled');
                                        $.post('ajax/addproperty.php',q,function(jsondata){
                                                if(jsondata.status == 'success'){
                                                        container.data('checksum', jsondata.data.checksum);
                                                        // TODO: savePropertyInternal doesn't know about new fields
                                                        //Contacts.UI.Card.savePropertyInternal(name, fields, checksum, jsondata.data.checksum);
                                                        Contacts.UI.loading(container, false);
+                                                       $(obj).removeAttr('disabled');
                                                        return true;
                                                }
                                                else{
                                                        Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message);
                                                        Contacts.UI.loading(container, false);
+                                                       $(obj).removeAttr('disabled');
                                                        return false;
                                                }
                                        },'json');
@@ -565,10 +731,14 @@ Contacts={
                                console.log('addProperty:' + type);
                                switch (type) {
                                        case 'PHOTO':
-                                               this.loadPhoto();
+                                               this.loadPhoto(true);
                                                $('#file_upload_form').show();
                                                $('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide();
                                                break;
+                                       case 'NOTE':
+                                               $('#note').show();
+                                               $('#contacts_propertymenu a[data-type="'+type+'"]').parent().hide();
+                                               break;
                                        case 'EMAIL':
                                                if($('#emaillist>li').length == 1) {
                                                        $('#emails').show();
@@ -647,8 +817,9 @@ Contacts={
                                if($('#edit_name_dialog').dialog('isOpen') == true){
                                        $('#edit_name_dialog').dialog('moveToTop');
                                }else{ // TODO: If id=='' call addcontact.php (or whatever name) instead and reload view with id.
-                                       $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editname.php')+'?id='+this.id, function(){
-                                               $('#edit_name_dialog' ).dialog({
+                                       $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editname.php')+'?id='+this.id, function(jsondata){
+                                               if(jsondata.status != 'error'){
+                                                       $('#edit_name_dialog' ).dialog({
                                                                modal: (isnew && true || false),
                                                                closeOnEscape: (isnew == '' && false || true),
                                                                title:  (isnew && t('contacts', 'Add contact') || t('contacts', 'Edit name')),
@@ -667,7 +838,10 @@ Contacts={
                                                                open : function(event, ui) {
                                                                        // load 'N' property - maybe :-P
                                                                }*/
-                                               });
+                                                       });
+                                               } else {
+                                                       alert(jsondata.data.message);
+                                               }
                                        });
                                }
                        },
@@ -692,7 +866,14 @@ Contacts={
 
                                $('#fn_select option').remove();
                                //$('#fn_select').combobox('value', this.fn);
-                               var names = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname];
+                               var tmp = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname];
+                               var names = new Array();
+                               for(var name in tmp) {
+                                       console.log('idx: ' + names.indexOf(tmp[name]));
+                                       if(names.indexOf(tmp[name]) == -1) {
+                                               names.push(tmp[name]);
+                                       }
+                               }
                                $.each(names, function(key, value) {
                                        $('#fn_select')
                                                .append($('<option></option>')
@@ -771,8 +952,9 @@ Contacts={
                                if($('#edit_address_dialog').dialog('isOpen') == true){
                                        $('#edit_address_dialog').dialog('moveToTop');
                                }else{
-                                       $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editaddress.php')+q, function(){
-                                               $('#edit_address_dialog' ).dialog({
+                                       $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'editaddress.php')+q, function(jsondata){
+                                               if(jsondata.status != 'error'){
+                                                       $('#edit_address_dialog' ).dialog({
                                                                /*modal: true,*/
                                                                height: 'auto', width: 'auto',
                                                                buttons: {
@@ -803,7 +985,10 @@ Contacts={
                                                                open : function(event, ui) {
                                                                        // load 'ADR' property - maybe :-P
                                                                }*/
-                                               });
+                                                       });
+                                               } else {
+                                                       alert(jsondata.data.message);
+                                               }
                                        });
                                }
                        },
@@ -867,8 +1052,8 @@ Contacts={
                                        form.submit();
                                }
                        },
-                       loadPhoto:function(){
-                               if(this.data.PHOTO) {
+                       loadPhoto:function(force){
+                               if(this.data.PHOTO||force==true) {
                                        $.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){
                                                if(jsondata.status == 'success'){
                                                        //alert(jsondata.data.page);
@@ -1006,13 +1191,17 @@ Contacts={
                                if($('#chooseaddressbook_dialog').dialog('isOpen') == true){
                                        $('#chooseaddressbook_dialog').dialog('moveToTop');
                                }else{
-                                       $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(){
-                                               $('#chooseaddressbook_dialog').dialog({
-                                                       width : 600,
-                                                       close : function(event, ui) {
-                                                               $(this).dialog('destroy').remove();
-                                                       }
-                                               });
+                                       $('#dialog_holder').load(OC.filePath('contacts', 'ajax', 'chooseaddressbook.php'), function(jsondata){
+                                               if(jsondata.status != 'error'){
+                                                       $('#chooseaddressbook_dialog').dialog({
+                                                               width : 600,
+                                                               close : function(event, ui) {
+                                                                       $(this).dialog('destroy').remove();
+                                                               }
+                                                       });
+                                               } else {
+                                                       alert(jsondata.data.message);
+                                               }
                                        });
                                }
                        },
@@ -1121,6 +1310,7 @@ Contacts={
 $(document).ready(function(){
 
        Contacts.UI.loadHandlers();
+       Categories.changed = Contacts.UI.Card.categoriesChanged;
 
        /**
         * Show the Addressbook chooser
@@ -1189,7 +1379,8 @@ $(document).ready(function(){
        
        // NOTE: For some reason the selector doesn't work when I select by '.contacts_property' too...
        // I do the filtering in the event handler instead.
-       $('input[type="text"],input[type="checkbox"],input[type="email"],input[type="tel"],input[type="date"], select').live('change', function(){
+       //$('input[type="text"],input[type="checkbox"],input[type="email"],input[type="tel"],input[type="date"], select').live('change', function(){
+       $('.contacts_property').live('change', function(){
                Contacts.UI.Card.saveProperty(this);
        });
 
@@ -1298,11 +1489,29 @@ $(document).ready(function(){
                xhr.send(file);
        }
 
+       $('body').live('click',function(e){
+               if(!$(e.target).is('#contacts_propertymenu_button')) {
+                       $('#contacts_propertymenu').hide();
+               }
+       });
        $('#contacts_propertymenu_button').live('click',function(){
-               $('#contacts_propertymenu').is(':hidden') && $('#contacts_propertymenu').slideDown() || $('#contacts_propertymenu').slideUp();
+               var menu = $('#contacts_propertymenu');
+               if(menu.is(':hidden')) {
+                       menu.show();
+                       menu.find('ul').focus();
+               } else {
+                       menu.hide();
+               }
        });
        $('#contacts_propertymenu a').live('click',function(){
                Contacts.UI.Card.addProperty(this);
                $('#contacts_propertymenu').hide();
        });
 });
+
+String.prototype.strip_tags = function(){
+       tags = this;
+       stripped = tags.replace(/[\<\>]/gi, "");
+       return stripped;
+}
+
index 99b1edd656f888eab758e89dcb25ff3f40440616..3c95dd19abff3fb2ab4a6fee340bd8e41395e10c 100644 (file)
@@ -4,6 +4,7 @@
  *
  * @author Jakob Sack
  * @copyright 2011 Jakob Sack mail@jakobsack.de
+ * @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
@@ -275,6 +276,29 @@ class OC_Contacts_VCard{
                return $newid;
        }
 
+       /**
+        * @brief Mass updates an array of cards
+        * @param array $objects  An array of [id, carddata].
+        */
+       public static function updateDataByID($objects){
+               $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET carddata = ?, lastmodified = ? WHERE id = ?' );
+               $now = new DateTime;
+               foreach($objects as $object) {
+                       $vcard = OC_VObject::parse($object[1]);
+                       if(!is_null($vcard)){
+                               $vcard->setString('REV', $now->format(DateTime::W3C));
+                               $data = $vcard->serialize();
+                               try {
+                                       $result = $stmt->execute(array($data,time(),$object[0]));
+                                       //OC_Log::write('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0].': '.$object[1],OC_Log::DEBUG);
+                               } catch(Exception $e) {
+                                       OC_Log::write('contacts','OC_Contacts_VCard::updateDataByID:, exception: '.$e->getMessage(),OC_Log::DEBUG);
+                                       OC_Log::write('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0],OC_Log::DEBUG);
+                               }
+                       }
+               }
+       }
+
        /**
         * @brief edits a card
         * @param integer $id id of card
@@ -378,6 +402,43 @@ class OC_Contacts_VCard{
                return true;
        }
 
+       /**
+        * @brief Escapes delimiters from an array and returns a string.
+        * @param array $value
+        * @param char $delimiter
+        * @return string
+        */
+       public static function escapeDelimiters($value, $delimiter=';') {
+               foreach($value as &$i ) {
+                       $i = implode("\\$delimiter", explode($delimiter, $i));
+               }
+               return implode($delimiter, $value);
+       }
+
+
+       /**
+        * @brief Creates an array out of a multivalue property
+        * @param string $value
+        * @param char $delimiter
+        * @return array
+        */
+       public static function unescapeDelimiters($value, $delimiter=';') {
+               $array = explode($delimiter,$value);
+               for($i=0;$i<count($array);$i++) {
+                       if(substr($array[$i],-1,1)=="\\") {
+                               if(isset($array[$i+1])) {
+                                       $array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter.$array[$i+1];
+                                       unset($array[$i+1]);
+                               } else {
+                                       $array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter;
+                               }
+                               $i = $i - 1;
+                       }
+               }
+               $array = array_map('trim', $array);
+               return $array;
+       }
+
        /**
         * @brief Data structure of vCard
         * @param object $property
@@ -416,7 +477,9 @@ class OC_Contacts_VCard{
                $value = $property->value;
                //$value = htmlspecialchars($value);
                if($property->name == 'ADR' || $property->name == 'N'){
-                       $value = OC_VObject::unescapeSemicolons($value);
+                       $value = self::unescapeDelimiters($value);
+               } elseif($property->name == 'CATEGORIES') {
+                       $value = self::unescapeDelimiters($value, ',');
                }
                $temp = array(
                        'name' => $property->name,
index 5be20964f4bb153f10c3d053ca34db60c2826025..59bb6c2cc2911cd3141b8775424527b090a85ffc 100644 (file)
@@ -13,6 +13,7 @@ $id = isset($_['id']) ? $_['id'] : '';
                <li><a data-type="TEL"><?php echo $l->t('Phone'); ?></a></li>
                <li><a data-type="EMAIL"><?php echo $l->t('Email'); ?></a></li>
                <li><a data-type="ADR"><?php echo $l->t('Address'); ?></a></li>
+               <li><a data-type="NOTE"><?php echo $l->t('Note'); ?></a></li>
        </ul>
        </div>
        <img  onclick="Contacts.UI.Card.export();" class="svg action" id="contacts_downloadcard" src="<?php echo image_path('', 'actions/download.svg'); ?>" title="<?php echo $l->t('Download contact');?>" />
@@ -45,7 +46,7 @@ $id = isset($_['id']) ? $_['id'] : '';
                <dt><label for="fn"><?php echo $l->t('Display name'); ?></label></dt>
                <dd class="propertycontainer" data-element="FN">
                <select id="fn_select" title="<?php echo $l->t('Format custom, Short name, Full name, Reverse or Reverse with comma'); ?>" style="width:16em;">
-               </select><a id="edit_name" class="edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
+               </select><a id="edit_name" class="action edit" title="<?php echo $l->t('Edit name details'); ?>"></a>
                </dd>
                <dt style="display:none;" id="org_label" data-element="ORG"><label for="org"><?php echo $l->t('Organization'); ?></label></dt>
                <dd style="display:none;" class="propertycontainer" id="org_value" data-element="ORG"><input id="org"  required="required" name="value[ORG]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Organization'); ?>" /><a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
@@ -53,8 +54,19 @@ $id = isset($_['id']) ? $_['id'] : '';
                <dd style="display:none;" class="propertycontainer" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" name="value[NICKNAME]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
                <dt style="display:none;" id="bday_label" data-element="BDAY"><label for="bday"><?php echo $l->t('Birthday'); ?></label></dt>
                <dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday"  required="required" name="value" type="text" class="contacts_property" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
+               <dt id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Categories'); ?></label></dt>
+               <dd class="propertycontainer" id="categories_value" data-element="CATEGORIES">
+                       <select class="contacts_property" multiple="multiple" id="categories" name="value[]">
+                               <?php echo html_select_options($_['categories'], array(), array('combine'=>true)) ?>
+                       </select>
+                       <a class="action edit" onclick="$(this).tipsy('hide');Categories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a>
+               </dd>
        </dl>
        </fieldset>
+       <fieldset id="note" class="formfloat propertycontainer" style="display:none;" data-element="NOTE">
+       <legend><?php echo $l->t('Note'); ?></legend>
+       <textarea class="contacts_property note" name="value"></textarea>
+       </fieldset>
        </form>
        </div>
 
index 250aa608c1df96d20092e8ce0372d195aac1c2ab..7a31a5268d1d37a609828077471d24ebfd9cb846 100644 (file)
@@ -49,12 +49,14 @@ class OC_VCategories {
        * @param $user The user whos data the object will operate on. This
        *   parameter should normally be omitted but to make an app able to
        *   update categories for all users it is made possible to provide it.
+       * @param $defcategories An array of default categories to be used if none is stored.
+       * NOTE: Not implemented.
        */
-       public function __construct($app, $user=null) {
+       public function __construct($app, $user=null, $defcategories=null) {
                $this->app = $app;
                $this->user = is_null($user) ? OC_User::getUser() : $user;
                $categories = trim(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, ''));
-               $this->categories = $categories != '' ? OC_VObject::unescapeSemicolons($categories) : array();
+               $this->categories = $categories != '' ? unserialize($categories) : array();
        }
 
        /**
@@ -62,6 +64,7 @@ class OC_VCategories {
        * @returns array containing the categories as strings.
        */
        public function categories() {
+               OC_Log::write('core','OC_VCategories::categories: '.print_r($this->categories, true), OC_Log::DEBUG);
                return $this->categories;
        }
 
@@ -81,7 +84,7 @@ class OC_VCategories {
        * @param $sync bool When true, save the categories
        * @returns bool Returns false on error.
        */
-       public function add($names, $sync=true) {
+       public function add($names, $sync=false) {
                if(!is_array($names)) {
                        $names = array($names);
                }
@@ -95,7 +98,7 @@ class OC_VCategories {
                if(count($newones) > 0) {
                        $this->categories = array_merge($this->categories, $newones);
                        natcasesort($this->categories); // Dunno if this is necessary
-                       if($sync) {
+                       if($sync === true) {
                                $this->save();
                        }
                }
@@ -106,7 +109,7 @@ class OC_VCategories {
        * @brief Extracts categories from a vobject and add the ones not already present.
        * @param $vobject The instance of OC_VObject to load the categories from.
        */
-       public function loadFromVObject($vobject, $sync=true) {
+       public function loadFromVObject($vobject, $sync=false) {
                $this->add($vobject->getAsArray('CATEGORIES'), $sync);
        }
 
@@ -126,14 +129,15 @@ class OC_VCategories {
        *       }
        *       $categories->rescan($objects);
        */
-       public function rescan($objects) {
+       public function rescan($objects, $sync=true) {
                $this->categories = array();
                foreach($objects as $object) {
+                       //OC_Log::write('core','OC_VCategories::rescan: '.substr($object, 0, 100).'(...)', OC_Log::DEBUG);
                        $vobject = OC_VObject::parse($object);
                        if(!is_null($vobject)) {
-                               $this->loadFromVObject($vobject, false);
+                               $this->loadFromVObject($vobject, $sync);
                        } else {
-                               OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 20).'(...)', OC_Log::DEBUG);
+                               OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 50).'(...)', OC_Log::DEBUG);
                        }
                }
                $this->save();
@@ -142,35 +146,51 @@ class OC_VCategories {
        /**
         * @brief Save the list with categories
         */
-       public function save() {
-               $escaped_categories = OC_VObject::escapeSemicolons($this->categories);
+       private function save() {
+               $escaped_categories = serialize($this->categories);
+               OC_Log::write('core','OC_VCategories::save: '.print_r($this->categories, true), OC_Log::DEBUG);
                OC_Preferences::setValue($this->user, $this->app, self::PREF_CATEGORIES_LABEL, $escaped_categories);
        }
 
        /**
-       * @brief Delete a category from the db and from all the vobject supplied
-       * @param $name
+       * @brief Delete categories from the db and from all the vobject supplied
+       * @param $names An array of categories to delete
        * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table.
        */
-       public function delete($name, array &$objects) {
-               if(!$this->hasCategory($name)) {
-                       return;
+       public function delete($names, array &$objects) {
+               if(!is_array($names)) {
+                       $names = array($names);
+               }
+               OC_Log::write('core','OC_VCategories::delete, before: '.print_r($this->categories, true), OC_Log::DEBUG);
+               foreach($names as $name) {
+                       OC_Log::write('core','OC_VCategories::delete: '.$name, OC_Log::DEBUG);
+                       if($this->hasCategory($name)) {
+                               OC_Log::write('core','OC_VCategories::delete: '.$name.' got it', OC_Log::DEBUG);
+                               unset($this->categories[$this->array_searchi($name, $this->categories)]);
+                       }
                }
-               unset($this->categories[$this->array_searchi($name, $this->categories)]);
                $this->save();
+               OC_Log::write('core','OC_VCategories::delete, after: '.print_r($this->categories, true), OC_Log::DEBUG);
                foreach($objects as $key=>&$value) {
                        $vobject = OC_VObject::parse($value[1]);
                        if(!is_null($vobject)){
                                $categories = $vobject->getAsArray('CATEGORIES');
-                               $idx = $this->array_searchi($name, $categories);
-                               if($idx) {
-                                       unset($categories[$this->array_searchi($name, $categories)]);
-                                       $vobject->setString('CATEGORIES', implode(',', $categories));
-                                       $value[1] = $vobject->serialize();
-                                       $objects[$key] = $value;
+                               //OC_Log::write('core','OC_VCategories::delete, before: '.$key.': '.print_r($categories, true), OC_Log::DEBUG);
+                               foreach($names as $name) {
+                                       $idx = $this->array_searchi($name, $categories);
+                                       OC_Log::write('core','OC_VCategories::delete, loop: '.$name.', '.print_r($idx, true), OC_Log::DEBUG);
+                                       if($idx !== false) {
+                                               OC_Log::write('core','OC_VCategories::delete, unsetting: '.$categories[$this->array_searchi($name, $categories)], OC_Log::DEBUG);
+                                               unset($categories[$this->array_searchi($name, $categories)]);
+                                               //unset($categories[$idx]);
+                                       }
                                }
+                               OC_Log::write('core','OC_VCategories::delete, after: '.$key.': '.print_r($categories, true), OC_Log::DEBUG);
+                               $vobject->setString('CATEGORIES', implode(',', $categories));
+                               $value[1] = $vobject->serialize();
+                               $objects[$key] = $value;
                        } else {
-                               OC_Log::write('core','OC_VCategories::delete, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 20).'(...)', OC_Log::DEBUG);
+                               OC_Log::write('core','OC_VCategories::delete, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 50).'(...)', OC_Log::DEBUG);
                        }
                }
        }