- ${object.props.map(prop)}
+ ${object.props.map(prop).join("")}
diff --git a/js/dav/lib/template/href.js b/js/dav/lib/template/href.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/js/dav/lib/template/index.js b/js/dav/lib/template/index.js
index 005a0f823..3258fb1d5 100644
--- a/js/dav/lib/template/index.js
+++ b/js/dav/lib/template/index.js
@@ -1,4 +1,5 @@
exports.addressBookQuery = require('./address_book_query');
+exports.addressBookMultiget = require('./address_book_multiget');
exports.calendarQuery = require('./calendar_query');
exports.propfind = require('./propfind');
exports.syncCollection = require('./sync_collection');
diff --git a/js/dav/lib/template/prop.js b/js/dav/lib/template/prop.js
index def9e9bc8..54c6a5fd5 100644
--- a/js/dav/lib/template/prop.js
+++ b/js/dav/lib/template/prop.js
@@ -36,17 +36,25 @@ import * as ns from '../namespace';
* }
*/
export default function prop(item) {
+ var tagName = `${xmlnsPrefix(item.namespace)}:${item.name}`;
+ var attrs = (item.attrs || []).map(makeAttr).join(' ');
if (!item.children || !item.children.length) {
if (typeof item.value === "undefined") {
- return `<${xmlnsPrefix(item.namespace)}:${item.name} />`;
+ return `<${tagName} ${attrs}/>`;
}
- return `<${xmlnsPrefix(item.namespace)}:${item.name}>${item.value}${xmlnsPrefix(item.namespace)}:${item.name}>`;
+ return `<${tagName} ${attrs}>${item.value}${tagName}>`;
}
let children = item.children.map(prop);
- return `<${xmlnsPrefix(item.namespace)}:${item.name}>
- ${children}
- ${xmlnsPrefix(item.namespace)}:${item.name}>`;
+ return `<${tagName} ${attrs}>
+ ${children.join('')}
+ ${tagName}>`;
+}
+
+function makeAttr(attr) {
+ if (!attr.name) return '';
+ if (!attr.value) return attr.name;
+ return `${attr.name}="${attr.value}"`;
}
function xmlnsPrefix(namespace) {
diff --git a/js/filters/counterFormatter_filter.js b/js/filters/counterFormatter_filter.js
new file mode 100644
index 000000000..a73402653
--- /dev/null
+++ b/js/filters/counterFormatter_filter.js
@@ -0,0 +1,12 @@
+// from https://docs.nextcloud.com/server/11/developer_manual/app/css.html#menus
+angular.module('contactsApp')
+.filter('counterFormatter', function () {
+ 'use strict';
+ return function (count) {
+ if (count > 999) {
+ return '999+';
+ }
+ return count;
+ };
+});
+
diff --git a/js/models/contact_model.js b/js/models/contact_model.js
index 53d0ab841..5d96414d0 100644
--- a/js/models/contact_model.js
+++ b/js/models/contact_model.js
@@ -417,6 +417,7 @@ angular.module('contactsApp')
var property = this.getProperty('categories');
if(!property) {
+ // categories should always have the same type (an array)
this.categories([]);
} else {
if (angular.isString(property.value)) {
diff --git a/js/services/contact_service.js b/js/services/contact_service.js
index 5cd7c6621..98ec7c32c 100644
--- a/js/services/contact_service.js
+++ b/js/services/contact_service.js
@@ -4,6 +4,7 @@ angular.module('contactsApp')
var cacheFilled = false;
var contacts = CacheFactory('contacts');
+ var urlsByDisplayname = CacheFactory('urlsByDisplayname');
var observerCallbacks = [];
@@ -24,6 +25,32 @@ angular.module('contactsApp')
});
};
+ this.getFullContacts = function getFullContacts(names) {
+ AddressBookService.getAll().then(function (enabledAddressBooks) {
+ var promises = [];
+ enabledAddressBooks.forEach(function (addressBook) {
+ var urlLists = names.map(function (name) { return urlsByDisplayname.get(name); });
+ var urls = [].concat.apply([], urlLists);
+ var promise = DavClient.getContacts(addressBook, {}, urls)
+ .then(
+ function (vcards) {
+ return vcards.map(function (vcard) {
+ return new Contact(addressBook, vcard);
+ });
+ })
+ .then(function (contacts_) {
+ contacts_.map(function (contact) {
+ contacts.put(contact.uid(), contact);
+ });
+ });
+ promises.push(promise);
+ });
+ $q.all(promises).then(function () {
+ notifyObservers('getFullContacts', '');
+ });
+ });
+ };
+
this.fillCache = function() {
if (_.isUndefined(loadPromise)) {
loadPromise = AddressBookService.getAll().then(function (enabledAddressBooks) {
@@ -35,8 +62,10 @@ angular.module('contactsApp')
if (addressBook.objects[i].addressData) {
var contact = new Contact(addressBook, addressBook.objects[i]);
contacts.put(contact.uid(), contact);
+ var oldList = urlsByDisplayname.get(contact.displayName()) || [];
+ urlsByDisplayname.put(contact.displayName(), oldList.concat(contact.data.url));
} else {
- // custom console
+ // eslint-disable-next-line no-console
console.log('Invalid contact received: ' + addressBook.objects[i].url);
}
}
@@ -61,6 +90,39 @@ angular.module('contactsApp')
}
};
+ // get list of groups and the count of contacts in said groups
+ this.getGroupList = function () {
+ return this.getAll().then(function(contacts) {
+ // the translated names for all and not-grouped are used in filtering, they must be exactly like this
+ var allContacts = [t('contacts', 'All contacts'), contacts.length];
+ var notGrouped =
+ [t('contacts', 'Not grouped'),
+ contacts.filter(
+ function (contact) {
+ return contact.categories().length === 0;
+ }).length
+ ];
+
+ // allow groups with names such as toString
+ var otherGroups = Object.create(null);
+
+ // collect categories and their associated counts
+ contacts.forEach(function (contact) {
+ contact.categories().forEach(function (category) {
+ otherGroups[category] = otherGroups[category] ? otherGroups[category] + 1 : 1;
+ });
+ });
+
+ return [allContacts, notGrouped]
+ .concat(_.keys(otherGroups).map(
+ function (key) {
+ return [key, otherGroups[key]];
+ }));
+
+
+ });
+ };
+
this.getGroups = function () {
return this.getAll().then(function(contacts) {
return _.uniq(contacts.map(function (element) {
@@ -71,14 +133,29 @@ angular.module('contactsApp')
});
};
- this.getById = function(uid) {
- if(cacheFilled === false) {
- return this.fillCache().then(function() {
- return contacts.get(uid);
+ this.getById = function(addressBooks, uid) {
+ return (function () {
+ if(cacheFilled === false) {
+ return this.fillCache().then(function() {
+ return contacts.get(uid);
+ });
+ } else {
+ return $q.when(contacts.get(uid));
+ }
+ }).call(this)
+ .then(function (contact) {
+ var addressBook = _.find(addressBooks, function(book) {
+ return book.displayName === contact.addressBookId;
+ });
+ return addressBook
+ ? DavClient.getContacts(addressBook, {}, [ contact.data.url ]).then(
+ function (vcards) { return new Contact(addressBook, vcards[0]); }
+ ).then(function (contact) {
+ contacts.put(contact.uid(), contact);
+ notifyObservers('getFullContacts', contact.uid());
+ return contact;
+ }) : contact;
});
- } else {
- return $q.when(contacts.get(uid));
- }
};
this.create = function(newContact, addressBook, uid) {
diff --git a/js/tests/filters/counterFormatter_filter.js b/js/tests/filters/counterFormatter_filter.js
new file mode 100644
index 000000000..f9cc490fe
--- /dev/null
+++ b/js/tests/filters/counterFormatter_filter.js
@@ -0,0 +1,21 @@
+// from https://github.com/owncloud/contacts/blob/31e403af1db859f85e84c2337134e20e5719c825/js/tests/filters/counterFormatter_filter.js by @skjnldsv
+describe('counterFormatter filter', function() {
+
+ var $filter;
+
+ beforeEach(module('contactsApp'));
+
+ beforeEach(inject(function(_$filter_){
+ $filter = _$filter_;
+ }));
+
+ it('should return the same number or 999+ if greater than 999', function() {
+ var counterFormatter = $filter('counterFormatter');
+ expect(counterFormatter(Number.NaN)).to.be.Nan;
+ expect(counterFormatter(15)).to.equal(15);
+ expect(counterFormatter(0)).to.equal(0);
+ expect(counterFormatter(-5)).to.equal(-5);
+ expect(counterFormatter(999)).to.equal(999);
+ expect(counterFormatter(1000)).to.equal('999+');
+ });
+});
diff --git a/templates/contactList.html b/templates/contactList.html
index 47d300673..fa9969a07 100644
--- a/templates/contactList.html
+++ b/templates/contactList.html
@@ -1,6 +1,6 @@
-
- {{ getCountString(filtered) }}
-
diff --git a/templates/group.html b/templates/group.html
index 694a29fa0..320259c2a 100644
--- a/templates/group.html
+++ b/templates/group.html
@@ -1 +1,7 @@
-{{ctrl.group}}
+{{ ctrl.group }}
+
+
+ - {{ctrl.groupCount | counterFormatter}}
+
+
+
diff --git a/templates/groupList.html b/templates/groupList.html
index 840a18226..9c999d769 100644
--- a/templates/groupList.html
+++ b/templates/groupList.html
@@ -1 +1 @@
-
+