[Pkg-owncloud-commits] [owncloud] 29/32: Imported Upstream version 6.0.3~rc1+dfsg
David Prévot
taffit at moszumanska.debian.org
Wed Apr 23 18:59:02 UTC 2014
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository owncloud.
commit 61eba845ee81c057f24fe191e2817a703362b7d5
Merge: e84bf62 53df9c9
Author: David Prévot <taffit at debian.org>
Date: Wed Apr 23 13:04:41 2014 -0400
Imported Upstream version 6.0.3~rc1+dfsg
3rdparty/doctrine/common/bin/travis-setup.php | 141 ---------------
apps/activity/lib/data.php | 2 +-
apps/activity/lib/search.php | 2 +-
apps/calendar/lib/export.php | 10 --
apps/contacts/lib/backend/database.php | 6 +-
apps/contacts/lib/backend/shared.php | 14 +-
apps/contacts/lib/dispatcher.php | 6 +-
apps/contacts/lib/share/addressbook.php | 5 +-
apps/files_external/ajax/addMountPoint.php | 7 +-
.../3rdparty/Zend/Search/Lucene/Document/Docx.php | 1 +
.../Zend/Search/Lucene/Document/OpenXml.php | 1 +
.../3rdparty/Zend/Search/Lucene/Document/Xlsx.php | 1 +
apps/search_lucene/document/Ods.php | 1 +
apps/search_lucene/document/Odt.php | 1 +
apps/search_lucene/document/OpenDocument.php | 1 +
apps/user_ldap/group_ldap.php | 21 +--
apps/user_ldap/group_proxy.php | 16 --
apps/user_ldap/js/ldapFilter.js | 100 +++++++++++
apps/user_ldap/js/settings.js | 100 +++--------
apps/user_ldap/lib/access.php | 15 ++
apps/user_ldap/settings.php | 1 +
.../doc/admin/_sources/configuration/auth_ldap.txt | 65 ++++---
core/doc/admin/configuration/auth_ldap.html | 65 ++++---
core/skeleton/ownCloudUserManual.pdf | Bin 1572014 -> 1571999 bytes
lib/base.php | 29 ++++
.../dependencyinjection/dicontainer.php | 12 +-
lib/private/appframework/http/request.php | 87 ++++------
lib/private/connector/sabre/file.php | 7 +-
lib/private/eventsource.php | 3 +-
lib/private/files/type/detection.php | 36 +++-
lib/private/group.php | 12 +-
lib/private/group/backend.php | 22 +--
lib/private/group/database.php | 26 ---
lib/private/group/group.php | 6 +-
lib/private/group/manager.php | 34 ++++
lib/private/helper.php | 10 ++
lib/private/json.php | 2 -
lib/private/mimetypes.list.php | 192 ++++++++++++---------
lib/private/server.php | 9 +-
lib/private/template.php | 23 ---
lib/private/user/manager.php | 4 +-
lib/public/appframework/iappcontainer.php | 4 +-
version.php | 8 +-
43 files changed, 517 insertions(+), 591 deletions(-)
diff --cc apps/activity/lib/data.php
index 409be24,0000000..4bfa5d8
mode 100644,000000..100644
--- a/apps/activity/lib/data.php
+++ b/apps/activity/lib/data.php
@@@ -1,286 -1,0 +1,286 @@@
+<?php
+
+/**
+ * ownCloud - Activity App
+ *
+ * @author Frank Karlitschek
+ * @copyright 2013 Frank Karlitschek frank at owncloud.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\Activity;
+
+
+/**
+ * @brief Class for managing the data in the activities
+ */
+class Data
+{
+ const PRIORITY_VERYLOW = 10;
+ const PRIORITY_LOW = 20;
+ const PRIORITY_MEDIUM = 30;
+ const PRIORITY_HIGH = 40;
+ const PRIORITY_VERYHIGH = 50;
+
+ /**
+ * @brief Send an event into the activity stream
+ * @param string $app The app where this event is associated with
+ * @param string $subject A short description of the event
+ * @param string $message A longer description of the event
+ * @param string $file The file including path where this event is associated with. (optional)
+ * @param string $link A link where this event is associated with (optional)
+ * @return boolean
+ */
+ public static function send($app, $subject, $subjectparams = array(), $message = '', $messageparams = array(), $file = '', $link = '', $affecteduser = '', $type = 0, $prio = Data::PRIORITY_MEDIUM)
+ {
+
+ $timestamp = time();
+ $user = \OCP\User::getUser();
+
+ if($affecteduser === '') {
+ $auser = \OCP\User::getUser();
+ } else{
+ $auser = $affecteduser;
+ }
+
+ // store in DB
+ $query = \OCP\DB::prepare('INSERT INTO `*PREFIX*activity`(`app`, `subject`, `subjectparams`, `message`, `messageparams`, `file`, `link`, `user`, `affecteduser`, `timestamp`, `priority`, `type`)' . ' VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )');
+ $query->execute(array($app, $subject, serialize($subjectparams), $message, serialize($messageparams), $file, $link, $user, $auser, $timestamp, $prio, $type));
+
+ // call the expire function only every 1000x time to preserve performance.
+ if (rand(0, 1000) == 0) {
+ Data::expire();
+ }
+
+ // fire a hook so that other apps like notification systems can connect
+ // todo translations
+ \OCP\Util::emitHook('OC_Activity', 'post_event', array('app' => $app, 'subject' => $subject, 'user' => $user, 'affecteduser' => $affecteduser, 'message' => $message, 'file' => $file, 'link'=> $link, 'prio' => $prio, 'type' => $type));
+
+ return true;
+ }
+
+ /**
+ * @brief Translate an event string with the translations from the app where it was send from
+ * @param string $app The app where this event comes from
+ * @param string $text The text including placeholders
+ * @param array $params The parameter for the placeholder
+ * @return string translated
+ */
+ public static function translation($app, $text, $params)
+ {
+ $l = \OCP\Util::getL10N($app);
+ $result = $l->t($text, $params);
+ unset($l);
+ return($result);
+ }
+
+ /**
+ * @brief Read a list of events from the activity stream
+ * @param int $start The start entry
+ * @param int $count The number of statements to read
+ * @return array
+ */
+ public static function read($start, $count)
+ {
+ // get current user
+ $user = \OCP\User::getUser();
+
+ // fetch from DB
+ $query = \OCP\DB::prepare('SELECT `activity_id`, `app`, `subject`, `subjectparams`, `message`, `messageparams`, `file`, `link`, `timestamp`, `priority`, `type`, `user`, `affecteduser` FROM `*PREFIX*activity` WHERE `affecteduser` = ? ORDER BY `timestamp` desc', $count, $start);
+ $result = $query->execute(array($user));
+
+ $activity = array();
+ while ($row = $result->fetchRow()) {
+ $row['subject'] = Data::translation($row['app'],$row['subject'],unserialize($row['subjectparams']));
+ $row['message'] = Data::translation($row['app'],$row['message'],unserialize($row['messageparams']));
+ $activity[] = $row;
+ }
+ return $activity;
+
+ }
+
+ /**
+ * @brief Get a list of events which contain the query string
+ * @param string $txt The query string
+ * @param int $count The number of statements to read
+ * @return array
+ */
+ public static function search($txt, $count)
+ {
+
+ // get current user
+ $user = \OCP\User::getUser();
+
+ // search in DB
- $query = \OCP\DB::prepare('SELECT `activity_id`, `app`, `subject`, `message`, `file`, `link`, `timestamp`, `priority`, `type`, `user`, `affecteduser` FROM `*PREFIX*activity` WHERE `affecteduser` = ? AND ((`subject` LIKE ?) OR (`message` LIKE ?) OR (`file` LIKE ?)) ORDER BY `timestamp` desc', $count);
++ $query = \OCP\DB::prepare('SELECT `activity_id`, `app`, `subject`, `subjectparams`, `message`, `messageparams`, `file`, `link`, `timestamp`, `priority`, `type`, `user`, `affecteduser` FROM `*PREFIX*activity` WHERE `affecteduser` = ? AND ((`subject` LIKE ?) OR (`message` LIKE ?) OR (`file` LIKE ?)) ORDER BY `timestamp` desc', $count);
+ $result = $query->execute(array($user, '%' . $txt . '%', '%' . $txt . '%', '%' . $txt . '%')); //$result = $query->execute(array($user,'%'.$txt.''));
+
+ $activity = array();
+ while ($row = $result->fetchRow()) {
+ $row['subject'] = Data::translation($row['app'],$row['subject'],unserialize($row['subjectparams']));
+ $row['message'] = Data::translation($row['app'],$row['message'],unserialize($row['messageparams']));
+ $activity[] = $row;
+ }
+ return $activity;
+
+ }
+
+ /**
+ * @brief Show a specific event in the activities
+ * @param array $event An array with all the event data in it
+ */
+ public static function show($event)
+ {
+ $l = \OC_L10N::get('lib');
+ $user = $event['user'];
+ if (!isset($event['isGrouped'])){
+ $event['isGrouped'] = false;
+ }
+
+ $formattedDate = \OCP\Util::formatDate($event['timestamp']);
+ $formattedTimestamp = \OCP\relative_modified_date($event['timestamp']);
+ $displayName = \OCP\User::getDisplayName($user);
+
+ // TODO: move into template?
+ echo('<div class="box">');
+
+ echo('<div class="header">');
+ echo('<span class="avatar" data-user="' . \OC_Util::sanitizeHTML($user) . '"></span>');
+ echo('<span>');
+ echo('<span class="user">' . \OC_Util::sanitizeHTML($displayName) . '</span>');
+ echo('<span class="activitytime tooltip" title="' . \OC_Util::sanitizeHTML($formattedDate) . '">' . \OC_Util::sanitizeHTML($formattedTimestamp) . '</span>');
+ echo('<span class="appname">' . \OC_Util::sanitizeHTML($event['app']) . '</span>');
+ echo('</span>');
+ echo('</div>');
+ echo('<div class="messagecontainer">');
+
+ if ($event['isGrouped']){
+ $count = 0;
+ echo('<ul class="activitysubject grouped">');
+ foreach($event['events'] as $subEvent){
+ echo('<li>');
+ if ($subEvent['link'] <> '') echo('<a href="' . $subEvent['link'] . '">');
+ echo(\OC_Util::sanitizeHTML($subEvent['subject']));
+ if ($subEvent['link'] <> '') echo('</a>');
+ echo('</li>');
+ $count++;
+ if ($count > 5){
+ echo('<li class="more">' . $l->n('%n more...', '%n more...', count($event['events']) - $count) . '</li>');
+ break;
+ }
+ }
+ echo('</ul>');
+ }
+ else{
+ if ($event['link'] <> '') echo('<a href="' . $event['link'] . '">');
+ echo('<div class="activitysubject">' . \OC_Util::sanitizeHTML($event['subject']) . '</div>');
+ echo('<div class="activitymessage">' . \OC_Util::sanitizeHTML($event['message']) . '</div>');
+ }
+
+ $rootView = new \OC\Files\View('');
+ if ($event['file'] !== null){
+ $exist = $rootView->file_exists('/' . $user . '/files' . $event['file']);
+ unset($rootView);
+ // show a preview image if the file still exists
+ if ($exist) {
+ echo('<img class="preview" src="' . \OCP\Util::linkToRoute('core_ajax_preview', array('file' => $event['file'], 'x' => 150, 'y' => 150)) . '" />');
+ }
+ }
+
+ if (!$event['isGrouped'] && $event['link'] <> '') echo('</a>');
+ echo('</div>'); // end messagecontainer
+ echo('</div>'); // end box
+
+ }
+
+
+ /**
+ * @brief Expire old events
+ */
+ public static function expire()
+ {
+ // keep activity feed entries for one year
+ $ttl = (60 * 60 * 24 * 365);
+
+ $timelimit = time() - $ttl;
+ $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*activity` where `timestamp`<?');
+ $query->execute(array($timelimit));
+ }
+
+
+ /**
+ * @brief Generate an RSS feed
+ * @param string $link
+ * @param string $content
+ */
+ public static function generaterss($link, $content)
+ {
+
+ $writer = xmlwriter_open_memory();
+ xmlwriter_set_indent($writer, 4);
+ xmlwriter_start_document($writer, '1.0', 'utf-8');
+
+ xmlwriter_start_element($writer, 'rss');
+ xmlwriter_write_attribute($writer, 'version', '2.0');
+ xmlwriter_write_attribute($writer, 'xmlns:atom', 'http://www.w3.org/2005/Atom');
+ xmlwriter_start_element($writer, 'channel');
+
+ xmlwriter_write_element($writer, 'title', 'my ownCloud');
+ xmlwriter_write_element($writer, 'language', 'en-us');
+ xmlwriter_write_element($writer, 'link', $link);
+ xmlwriter_write_element($writer, 'description', 'A personal ownCloud activities');
+ xmlwriter_write_element($writer, 'pubDate', date('r'));
+ xmlwriter_write_element($writer, 'lastBuildDate', date('r'));
+
+ xmlwriter_start_element($writer, 'atom:link');
+ xmlwriter_write_attribute($writer, 'href', $link);
+ xmlwriter_write_attribute($writer, 'rel', 'self');
+ xmlwriter_write_attribute($writer, 'type', 'application/rss+xml');
+ xmlwriter_end_element($writer);
+
+ // items
+ for ($i = 0; $i < count($content); $i++) {
+ xmlwriter_start_element($writer, 'item');
+ if (isset($content[$i]['subject'])) {
+ xmlwriter_write_element($writer, 'title', $content[$i]['subject']);
+ }
+
+ if (isset($content[$i]['link'])) xmlwriter_write_element($writer, 'link', $content[$i]['link']);
+ if (isset($content[$i]['link'])) xmlwriter_write_element($writer, 'guid', $content[$i]['link']);
+ if (isset($content[$i]['timestamp'])) xmlwriter_write_element($writer, 'pubDate', date('r', $content[$i]['timestamp']));
+
+ if (isset($content[$i]['message'])) {
+ xmlwriter_start_element($writer, 'description');
+ xmlwriter_start_cdata($writer);
+ xmlwriter_text($writer, $content[$i]['message']);
+ xmlwriter_end_cdata($writer);
+ xmlwriter_end_element($writer);
+ }
+ xmlwriter_end_element($writer);
+ }
+
+ xmlwriter_end_element($writer);
+ xmlwriter_end_element($writer);
+
+ xmlwriter_end_document($writer);
+ $entry = xmlwriter_output_memory($writer);
+ unset($writer);
+ return ($entry);
+ }
+
+
+}
diff --cc apps/activity/lib/search.php
index 70ba2c1,0000000..f4003ac
mode 100644,000000..100644
--- a/apps/activity/lib/search.php
+++ b/apps/activity/lib/search.php
@@@ -1,54 -1,0 +1,54 @@@
+<?php
+
+/**
+ * ownCloud - Activities App
+ *
+ * @author Frank Karlitschek
+ * @copyright 2013 Frank Karlitschek frank at owncloud.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\Activity;
+
+
+/**
+* @brief Class for provide search results for the system search
+*/
+class Search extends \OC_Search_Provider{
+
+ /**
+ * @brief Search in the activities and return search results
+ * @param $query
+ * @return search results
+ */
+ function search($query){
+
+ $data = Data::search($query, 100);
+
+ $results = array();
+ foreach($data as $d){
+ $file = $d['file'];
+ $results[] = new \OC_Search_Result(
+ basename($file),
+ $d['subject'].' ('.\OCP\Util::formatDate($d['timestamp']).')',
- \OC_Helper::linkTo( 'activity', 'index.php' ), 'Activity');
++ \OC_Helper::linkTo( 'activity', 'index.php' ), 'Activity', dirname($file));
+ }
+
+ return $results;
+ }
+
+}
diff --cc apps/calendar/lib/export.php
index 9797498,0000000..0db9e5b
mode 100644,000000..100644
--- a/apps/calendar/lib/export.php
+++ b/apps/calendar/lib/export.php
@@@ -1,115 -1,0 +1,105 @@@
+<?php
+/**
+ * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite.de>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+/**
+ * This class does export and converts all times to UTC
+ */
+class OC_Calendar_Export{
+ /**
+ * @brief Use one of these constants as second parameter if you call OC_Calendar_Export::export()
+ */
+ const CALENDAR = 'calendar';
+ const EVENT = 'event';
+
+ /**
+ * @brief export a calendar or an event
+ * @param integer $id id of calendar / event
+ * @param string $type use OC_Calendar_Export constants
+ * @return string
+ */
+ public static function export($id, $type) {
+ if($type == self::EVENT) {
+ $return = self::event($id);
+ }else{
+ $return = self::calendar($id);
+ }
+ return self::fixLineBreaks($return);
+ }
+
+ /**
+ * @brief exports a calendar and convert all times to UTC
+ * @param integer $id id of the calendar
+ * @return string
+ */
+ private static function calendar($id) {
+ $events = OC_Calendar_Object::all($id);
+ $calendar = OC_Calendar_Calendar::find($id);
+ $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\nX-WR-CALNAME:" . $calendar['displayname'] . "\n";
+ foreach($events as $event) {
+ $return .= self::generateEvent($event);
+ }
+ $return .= "END:VCALENDAR";
+ return $return;
+ }
+
+ /**
+ * @brief exports an event and convert all times to UTC
+ * @param integer $id id of the event
+ * @return string
+ */
+ private static function event($id) {
+ $event = OC_Calendar_Object::find($id);
+ $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\nX-WR-CALNAME:" . $event['summary'] . "\n";
+ $return .= self::generateEvent($event);
+ $return .= "END:VCALENDAR";
+ return $return;
+ }
+
+ /**
+ * @brief generates the VEVENT/VTODO/VJOURNAL with UTC dates
+ * @param array $event
+ * @return string
+ */
+ private static function generateEvent($event) {
+ $object = OC_VObject::parse($event['calendardata']);
+ if(!$object){
+ return false;
+ }
+
+ $sharedAccessClassPermissions = OC_Calendar_Object::getAccessClassPermissions($object);
+ if(OC_Calendar_Object::getowner($event['id']) !== OCP\User::getUser()){
+ if (!($sharedAccessClassPermissions & OCP\PERMISSION_READ)) {
+ return '';
+ }
+ }
+ $object = OC_Calendar_Object::cleanByAccessClass($event['id'], $object);
+
+ if($object->VEVENT){
- $dtstart = $object->VEVENT->DTSTART;
- $start_dt = $dtstart->getDateTime();
- $dtend = OC_Calendar_Object::getDTEndFromVEvent($object->VEVENT);
- $end_dt = $dtend->getDateTime();
- if($dtstart->getDateType() !== Sabre\VObject\Property\DateTime::DATE) {
- $start_dt->setTimezone(new DateTimeZone('UTC'));
- $end_dt->setTimezone(new DateTimeZone('UTC'));
- $object->VEVENT->setDateTime('DTSTART', $start_dt, Sabre\VObject\Property\DateTime::UTC);
- $object->VEVENT->setDateTime('DTEND', $end_dt, Sabre\VObject\Property\DateTime::UTC);
- }
+ return $object->VEVENT->serialize();
+ }
+ if($object->VTODO){
+ return $object->VTODO->serialize();
+ }
+ if($object->VJOURNAL){
+ return $object->VJOURNAL->serialize();
+ }
+ return '';
+ }
+
+ /**
+ * @brief fixes new line breaks
+ * (fixes problems with Apple iCal)
+ * @param string $string to fix
+ * @return string
+ */
+ private static function fixLineBreaks($string) {
+ $string = str_replace("\r\n", "\n", $string);
+ $string = str_replace("\r", "\n", $string);
+ $string = str_replace("\n", "\r\n", $string);
+ return $string;
+ }
+}
diff --cc apps/contacts/lib/backend/database.php
index a890169,0000000..1283cdc
mode 100644,000000..100644
--- a/apps/contacts/lib/backend/database.php
+++ b/apps/contacts/lib/backend/database.php
@@@ -1,812 -1,0 +1,812 @@@
+<?php
+/**
+ * ownCloud - Database backend for Contacts
+ *
+ * @author Thomas Tanghus
+ * @copyright 2013 Thomas Tanghus (thomas at 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/>.
+ *
+ */
+
+namespace OCA\Contacts\Backend;
+
+use OCA\Contacts\Contact,
+ OCA\Contacts\VObject\VCard,
+ OCA\Contacts\Utils\Properties,
+ Sabre\VObject\Reader;
+
+/**
+ * Subclass this class for Cantacts backends
+ */
+
+class Database extends AbstractBackend {
+
+ public $name = 'local';
+ static private $preparedQueries = array();
+
+ /**
+ * Sets up the backend
+ *
+ * @param string $addressBooksTableName
+ * @param string $cardsTableName
+ */
+ public function __construct(
+ $userid = null,
+ $options = array(
+ 'addressBooksTableName' => '*PREFIX*contacts_addressbooks',
+ 'cardsTableName' => '*PREFIX*contacts_cards',
+ 'indexTableName' => '*PREFIX*contacts_cards_properties'
+ )
+ ) {
+ $this->userid = $userid ? $userid : \OCP\User::getUser();
+ $this->addressBooksTableName = $options['addressBooksTableName'];
+ $this->cardsTableName = $options['cardsTableName'];
+ $this->indexTableName = $options['indexTableName'];
+ $this->addressbooks = array();
+ }
+
+ /**
+ * Returns the list of addressbooks for a specific user.
+ *
+ * @see AbstractBackend::getAddressBooksForUser
+ * @return array
+ */
+ public function getAddressBooksForUser(array $options = array()) {
+
+ try {
+ if(!isset(self::$preparedQueries['addressbooksforuser'])) {
+ $sql = 'SELECT `id`, `displayname`, `description`, `ctag` AS `lastmodified`, `userid` AS `owner`, `uri` FROM `'
+ . $this->addressBooksTableName
+ . '` WHERE `userid` = ? ORDER BY `displayname`';
+ self::$preparedQueries['addressbooksforuser'] = \OCP\DB::prepare($sql);
+ }
+ $result = self::$preparedQueries['addressbooksforuser']->execute(array($this->userid));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return $this->addressbooks;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.' exception: ' . $e->getMessage(), \OCP\Util::ERROR);
+ return $this->addressbooks;
+ }
+
+ while($row = $result->fetchRow()) {
+ $row['permissions'] = \OCP\PERMISSION_ALL;
+ $this->addressbooks[$row['id']] = $row;
+ }
+ return $this->addressbooks;
+ }
+
+ public function getAddressBook($addressbookid, array $options = array()) {
- //\OCP\Util::writeLog('contacts', __METHOD__.' id: '
- // . $addressbookid, \OCP\Util::DEBUG);
++ \OCP\Util::writeLog('contacts', __METHOD__.' id: ' . $addressbookid, \OCP\Util::DEBUG);
++ $owner = isset($options['shared_by']) ? $options['shared_by'] : $this->userid;
+ if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
+ //print(__METHOD__ . ' ' . __LINE__ .' addressBookInfo: ' . print_r($this->addressbooks[$addressbookid], true));
+ return $this->addressbooks[$addressbookid];
+ }
+ // Hmm, not found. Lets query the db.
+ try {
+ $query = 'SELECT `id`, `displayname`, `description`, `userid` AS `owner`, `ctag` AS `lastmodified`, `uri` FROM `'
+ . $this->addressBooksTableName
+ . '` WHERE `id` = ? AND `userid` = ?';
+ if(!isset(self::$preparedQueries['getaddressbook'])) {
+ self::$preparedQueries['getaddressbook'] = \OCP\DB::prepare($query);
+ }
- $result = self::$preparedQueries['getaddressbook']->execute(array($addressbookid, $this->userid));
++ $result = self::$preparedQueries['getaddressbook']->execute(array($addressbookid, $owner));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
+ . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return null;
+ }
+ if((int)$result->numRows() === 0) {
+ return null;
+ }
+ $row = $result->fetchRow();
+ $row['permissions'] = \OCP\PERMISSION_ALL;
+ $row['backend'] = $this->name;
+ $this->addressbooks[$addressbookid] = $row;
+ return $row;
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.' exception: '
+ . $e->getMessage(), \OCP\Util::ERROR);
+ return null;
+ }
+ return null;
+ }
+
+ public function hasAddressBook($addressbookid, array $options = array()) {
+ // First check if it's already cached
+ if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
+ return true;
+ }
+ return count($this->getAddressBook($addressbookid)) > 0;
+ }
+
+ /**
+ * Updates an addressbook's properties
+ *
+ * @param string $addressbookid
+ * @param array $changes
+ * @return bool
+ */
+ public function updateAddressBook($addressbookid, array $changes, array $options = array()) {
+ if(count($changes) === 0) {
+ return false;
+ }
+
+ $query = 'UPDATE `' . $this->addressBooksTableName . '` SET ';
+
+ $updates = array();
+
+ if(isset($changes['displayname'])) {
+ $query .= '`displayname` = ?, ';
+ $updates[] = $changes['displayname'];
+ if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
+ $this->addressbooks[$addressbookid]['displayname'] = $changes['displayname'];
+ }
+ }
+
+ if(isset($changes['description'])) {
+ $query .= '`description` = ?, ';
+ $updates[] = $changes['description'];
+ if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
+ $this->addressbooks[$addressbookid]['description'] = $changes['description'];
+ }
+ }
+
+ $query .= '`ctag` = ? + 1 WHERE `id` = ?';
+ $updates[] = time();
+ $updates[] = $addressbookid;
+
+ try {
+ $stmt = \OCP\DB::prepare($query);
+ $result = $stmt->execute($updates);
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts',
+ __METHOD__. 'DB error: '
+ . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return false;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts',
+ __METHOD__ . ', exception: '
+ . $e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Creates a new address book
+ *
+ * Supported properties are 'displayname', 'description' and 'uri'.
+ * 'uri' is supported to allow to add from CardDAV requests, and MUST
+ * be used for the 'uri' database field if present.
+ * 'displayname' MUST be present.
+ *
+ * @param array $properties
+ * @param array $options - Optional (backend specific options)
+ * @return string|false The ID if the newly created AddressBook or false on error.
+ */
+ public function createAddressBook(array $properties, array $options = array()) {
+
+ if(count($properties) === 0 || !isset($properties['displayname'])) {
+ return false;
+ }
+
+ $query = 'INSERT INTO `' . $this->addressBooksTableName . '` '
+ . '(`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)';
+
+ $updates = array($this->userid, $properties['displayname']);
+ $updates[] = isset($properties['uri'])
+ ? $properties['uri']
+ : $this->createAddressBookURI($properties['displayname']);
+ $updates[] = isset($properties['description']) ? $properties['description'] : '';
+ $ctag = time();
+ $updates[] = $ctag;
+
+ try {
+ if(!isset(self::$preparedQueries['createaddressbook'])) {
+ self::$preparedQueries['createaddressbook'] = \OCP\DB::prepare($query);
+ }
+ $result = self::$preparedQueries['createaddressbook']->execute($updates);
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return false;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__ . ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+
+ $newid = \OCP\DB::insertid($this->addressBooksTableName);
+ if($this->addressbooks) {
+ $updates['id'] = $newid;
+ $updates['ctag'] = $ctag;
+ $updates['lastmodified'] = $ctag;
+ $updates['permissions'] = \OCP\PERMISSION_ALL;
+ $this->addressbooks[$newid] = $updates;
+ }
+ return $newid;
+ }
+
+ /**
+ * Deletes an entire addressbook and all its contents
+ *
+ * NOTE: For efficience this method bypasses the cleanup hooks and deletes
+ * property indexes and category/group relations by itself.
+ *
+ * @param string $addressbookid
+ * @param array $options - Optional (backend specific options)
+ * @return bool
+ */
+ public function deleteAddressBook($addressbookid, array $options = array()) {
+
+ // Get all contact ids for this address book
+ $ids = array();
+ $result = null;
+ $stmt = \OCP\DB::prepare('SELECT `id` FROM `' . $this->cardsTableName . '`'
+ . ' WHERE `addressbookid` = ?');
+ try {
+ $result = $stmt->execute(array($addressbookid));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
+ . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return false;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.
+ ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+
+ if(!is_null($result)) {
+ while($id = $result->fetchOne()) {
+ $ids[] = $id;
+ }
+ }
+
+ \OCP\Util::emitHook('OCA\Contacts', 'pre_deleteAddressBook',
+ array('addressbookid' => $addressbookid, 'contactids' => $ids)
+ );
+
+ // Delete contacts in address book.
+ if(!isset(self::$preparedQueries['deleteaddressbookcontacts'])) {
+ self::$preparedQueries['deleteaddressbookcontacts'] =
+ \OCP\DB::prepare('DELETE FROM `' . $this->cardsTableName
+ . '` WHERE `addressbookid` = ?');
+ }
+ try {
+ self::$preparedQueries['deleteaddressbookcontacts']
+ ->execute(array($addressbookid));
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.
+ ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+
+ // Delete the address book.
+ if(!isset(self::$preparedQueries['deleteaddressbook'])) {
+ self::$preparedQueries['deleteaddressbook'] =
+ \OCP\DB::prepare('DELETE FROM `'
+ . $this->addressBooksTableName . '` WHERE `id` = ?');
+ }
+ try {
+ self::$preparedQueries['deleteaddressbook']
+ ->execute(array($addressbookid));
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.
+ ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+
+ if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
+ unset($this->addressbooks[$addressbookid]);
+ }
+
+ return true;
+ }
+
+ /**
+ * @brief Updates ctag for addressbook
+ * @param integer $id
+ * @return boolean
+ */
+ public function setModifiedAddressBook($id) {
+ $query = 'UPDATE `' . $this->addressBooksTableName
+ . '` SET `ctag` = ? + 1 WHERE `id` = ?';
+ if(!isset(self::$preparedQueries['touchaddressbook'])) {
+ self::$preparedQueries['touchaddressbook'] = \OCP\DB::prepare($query);
+ }
+ $ctag = time();
+ self::$preparedQueries['touchaddressbook']->execute(array($ctag, $id));
+
+ return true;
+ }
+
+ public function lastModifiedAddressBook($addressbookid) {
+ if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
+ return $this->addressbooks[$addressbookid]['lastmodified'];
+ }
+ $addressBook = $this->getAddressBook($addressbookid);
+ return $addressBook ? $addressBook['lastmodified'] : null;
+ }
+
+ /**
+ * Returns the number of contacts in a specific address book.
+ *
+ * @param string $addressbookid
+ * @param bool $omitdata Don't fetch the entire carddata or vcard.
+ * @return array
+ */
+ public function numContacts($addressbookid) {
+ $query = 'SELECT COUNT(*) AS `count` FROM `' . $this->cardsTableName . '` WHERE '
+ . '`addressbookid` = ?';
+
+ if(!isset(self::$preparedQueries['count'])) {
+ self::$preparedQueries['count'] = \OCP\DB::prepare($query);
+ }
+ $result = self::$preparedQueries['count']->execute(array($addressbookid));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return null;
+ }
+ return (int)$result->fetchOne();
+ }
+
+ /**
+ * Returns all contacts for a specific addressbook id.
+ *
+ * @param string $addressbookid
+ * @param array $options - Optional (backend specific options)
+ * @param bool $omitdata Don't fetch the entire carddata or vcard.
+ * @return array
+ */
+ public function getContacts($addressbookid, array $options = array() ) {
+ //\OCP\Util::writeLog('contacts', __METHOD__.' addressbookid: ' . $addressbookid, \OCP\Util::DEBUG);
+ $cards = array();
+ try {
+ $omitdata = isset($options['omitdata']) ? $options['omitdata'] : false;
+ $qfields = $omitdata ? '`id`, `fullname` AS `displayname`' : '*';
+ $query = 'SELECT ' . $qfields . ' FROM `' . $this->cardsTableName
+ . '` WHERE `addressbookid` = ? ORDER BY `fullname`';
+ $stmt = \OCP\DB::prepare(
+ $query,
+ isset($options['limit']) ? $options['limit'] : null,
+ isset($options['offset']) ? $options['offset'] : null
+ );
+ $result = $stmt->execute(array($addressbookid));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return $cards;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
+ return $cards;
+ }
+
+ if(!is_null($result)) {
+ while($row = $result->fetchRow()) {
+ $row['permissions'] = \OCP\PERMISSION_ALL;
+ $cards[] = $row;
+ }
+ }
+
+ return $cards;
+ }
+
+ /**
+ * Returns a specific contact.
+ *
+ * The $id for Database and Shared backends can be an array containing
+ * either 'id' or 'uri' to be able to play seamlessly with the
+ * CardDAV backend.
+ * FIXME: $addressbookid isn't used in the query, so there's no access control.
+ * OTOH the groups backend - OC_VCategories - doesn't no about parent collections
+ * only object IDs. Hmm.
+ * I could make a hack and add an optional, not documented 'nostrict' argument
+ * so it doesn't look for addressbookid.
+ *
+ * @param string $addressbookid
+ * @param mixed $id Contact ID
+ * @param array $options - Optional (backend specific options)
+ * @return array|null
+ */
+ public function getContact($addressbookid, $id, array $options = array()) {
+ //\OCP\Util::writeLog('contacts', __METHOD__.' identifier: ' . $addressbookid . ' ' . $id['uri'], \OCP\Util::DEBUG);
+
+ $noCollection = isset($options['noCollection']) ? $options['noCollection'] : false;
+
+ $where_query = '`id` = ?';
+ if(is_array($id)) {
+ $where_query = '';
+ if(isset($id['id'])) {
+ $id = $id['id'];
+ } elseif(isset($id['uri'])) {
+ $where_query = '`uri` = ?';
+ $id = $id['uri'];
+ } else {
+ throw new \Exception(
+ __METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
+ );
+ return null;
+ }
+ }
+ $ids = array($id);
+
+ if(!$noCollection) {
+ $where_query .= ' AND `addressbookid` = ?';
+ $ids[] = $addressbookid;
+ }
+
+ try {
+ $query = 'SELECT `id`, `uri`, `carddata`, `lastmodified`, `addressbookid` AS `parent`, `fullname` AS `displayname` FROM `'
+ . $this->cardsTableName . '` WHERE ' . $where_query;
+ $stmt = \OCP\DB::prepare($query);
+ $result = $stmt->execute($ids);
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return null;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
+ \OCP\Util::writeLog('contacts', __METHOD__.', id: '. $id, \OCP\Util::DEBUG);
+ return null;
+ }
+
+ $row = $result->fetchRow();
+ if(!$row) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', Not found, id: '. $id, \OCP\Util::DEBUG);
+ return null;
+ }
+ $row['permissions'] = \OCP\PERMISSION_ALL;
+ return $row;
+ }
+
+ public function hasContact($addressbookid, $id) {
+ return $this->getContact($addressbookid, $id) !== false;
+ }
+
+ /**
+ * Creates a new contact
+ *
+ * In the Database and Shared backends contact be either a Contact object or a string
+ * with carddata to be able to play seamlessly with the CardDAV backend.
+ * If this method is called by the CardDAV backend, the carddata is already validated.
+ * NOTE: It's assumed that this method is called either from the CardDAV backend, the
+ * import script, or from the ownCloud web UI in which case either the uri parameter is
+ * set, or the contact has a UID. If neither is set, it will fail.
+ *
+ * @param string $addressbookid
+ * @param VCard|string $contact
+ * @param array $options - Optional (backend specific options)
+ * @return string|bool The identifier for the new contact or false on error.
+ */
+ public function createContact($addressbookid, $contact, array $options = array()) {
+
+ $qname = 'createcontact';
+ $uri = isset($options['uri']) ? $options['uri'] : null;
+
+ if(!$contact instanceof VCard) {
+ try {
+ $contact = Reader::read($contact);
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+ }
+
+ try {
+ $contact->validate(VCard::REPAIR|VCard::UPGRADE);
+ } catch (\Exception $e) {
+ OCP\Util::writeLog('contacts', __METHOD__ . ' ' .
+ 'Error validating vcard: ' . $e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+
+ $uri = is_null($uri) ? $this->uniqueURI($addressbookid, $contact->UID . '.vcf') : $uri;
+ $now = new \DateTime;
+ $contact->REV = $now->format(\DateTime::W3C);
+
+ $appinfo = \OCP\App::getAppInfo('contacts');
+ $appversion = \OCP\App::getAppVersion('contacts');
+ $prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN';
+ $contact->PRODID = $prodid;
+
+ $data = $contact->serialize();
+ if(!isset(self::$preparedQueries[$qname])) {
+ self::$preparedQueries[$qname] = \OCP\DB::prepare('INSERT INTO `'
+ . $this->cardsTableName
+ . '` (`addressbookid`,`fullname`,`carddata`,`uri`,`lastmodified`) VALUES(?,?,?,?,?)' );
+ }
+ try {
+ $result = self::$preparedQueries[$qname]
+ ->execute(
+ array(
+ $addressbookid,
+ (string)$contact->FN,
+ $contact->serialize(),
+ $uri,
+ time()
+ )
+ );
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return false;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+ $newid = \OCP\DB::insertid($this->cardsTableName);
+
+ $this->setModifiedAddressBook($addressbookid);
+ \OCP\Util::emitHook('OCA\Contacts', 'post_createContact',
+ array('id' => $newid, 'parent' => $addressbookid, 'backend' => $this->name, 'contact' => $contact)
+ );
+ return (string)$newid;
+ }
+
+ /**
+ * Updates a contact
+ *
+ * @param string $addressbookid
+ * @param mixed $id Contact ID
+ * @param VCard|string $contact
+ * @param array $options - Optional (backend specific options)
+ * @see getContact
+ * @return bool
+ */
+ public function updateContact($addressbookid, $id, $contact, array $options = array()) {
+ $noCollection = isset($options['noCollection']) ? $options['noCollection'] : false;
+ $isBatch = isset($options['isBatch']) ? $options['isBatch'] : false;
+ $qname = 'updatecontact';
+
+ $updateRevision = true;
+ $isCardDAV = false;
+ if(!$contact instanceof VCard) {
+ try {
+ $contact = Reader::read($contact);
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
+ return false;
+ }
+ }
+
+ if(is_array($id)) {
+ $where_query = '';
+ if(isset($id['id'])) {
+ $id = $id['id'];
+ } elseif(isset($id['uri'])) {
+ $updateRevision = false;
+ $isCardDAV = true;
+ $id = $this->getIdFromUri($id['uri']);
+ if(is_null($id)) {
+ \OCP\Util::writeLog('contacts', __METHOD__ . ' Couldn\'t find contact', \OCP\Util::ERROR);
+ return false;
+ }
+ } else {
+ throw new \Exception(
+ __METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
+ );
+ }
+ }
+
+ if($updateRevision || !isset($contact->REV)) {
+ $now = new \DateTime;
+ $contact->REV = $now->format(\DateTime::W3C);
+ }
+
+ $data = $contact->serialize();
+
+ if($noCollection) {
+ $me = $this->getContact(null, $id, $options);
+ $addressbookid = $me['parent'];
+ }
+
+ $updates = array($contact->FN, $data, time(), $id, $addressbookid);
+
+ $query = 'UPDATE `' . $this->cardsTableName
+ . '` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ? WHERE `id` = ? AND `addressbookid` = ?';
+ if(!isset(self::$preparedQueries[$qname])) {
+ self::$preparedQueries[$qname] = \OCP\DB::prepare($query);
+ }
+ try {
+ $result = self::$preparedQueries[$qname]->execute($updates);
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return false;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', exception: '
+ . $e->getMessage(), \OCP\Util::ERROR);
+ \OCP\Util::writeLog('contacts', __METHOD__.', id' . $id, \OCP\Util::DEBUG);
+ return false;
+ }
+
+ $this->setModifiedAddressBook($addressbookid);
+ if(!$isBatch) {
+ \OCP\Util::emitHook('OCA\Contacts', 'post_updateContact',
+ array(
+ 'backend' => $this->name,
+ 'addressBookId' => $addressbookid,
+ 'contactId' => $id,
+ 'contact' => $contact,
+ 'carddav' => $isCardDAV
+ )
+ );
+ }
+ return true;
+ }
+
+ /**
+ * Deletes a contact
+ *
+ * @param string $addressbookid
+ * @param string $id
+ * @param array $options - Optional (backend specific options)
+ * @see getContact
+ * @return bool
+ */
+ public function deleteContact($addressbookid, $id, array $options = array()) {
+ // TODO: pass the uri in $options instead.
+
+ $qname = 'deletecontact';
+ $noCollection = isset($options['noCollection']) ? $options['noCollection'] : false;
+ $isBatch = isset($options['isBatch']) ? $options['isBatch'] : false;
+
+ if(is_array($id)) {
+ if(isset($id['id'])) {
+ $id = $id['id'];
+ } elseif(isset($id['uri'])) {
+ $id = $this->getIdFromUri($id['uri']);
+ if(is_null($id)) {
+ \OCP\Util::writeLog('contacts', __METHOD__ . ' Couldn\'t find contact', \OCP\Util::ERROR);
+ return false;
+ }
+ } else {
+ throw new Exception(
+ __METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
+ );
+ }
+ }
+
+ if(!$isBatch) {
+ \OCP\Util::emitHook('OCA\Contacts', 'pre_deleteContact',
+ array('id' => $id)
+ );
+ }
+
+ if($noCollection) {
+ $me = $this->getContact(null, $id, $options);
+ $addressbookid = $me['parent'];
+ }
+
+ if(!isset(self::$preparedQueries[$qname])) {
+ self::$preparedQueries[$qname] = \OCP\DB::prepare('DELETE FROM `'
+ . $this->cardsTableName
+ . '` WHERE `id` = ? AND `addressbookid` = ?');
+ }
+ \OCP\Util::writeLog('contacts', __METHOD__ . ' updates: ' . $id . '/' . $addressbookid, \OCP\Util::DEBUG);
+ try {
+ $result = self::$preparedQueries[$qname]->execute(array($id, $addressbookid));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
+ . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return false;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__.
+ ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
+ \OCP\Util::writeLog('contacts', __METHOD__.', id: '
+ . $id, \OCP\Util::DEBUG);
+ return false;
+ }
+ $this->setModifiedAddressBook($addressbookid);
+ return true;
+ }
+
+ /**
+ * @brief Get the last modification time for a contact.
+ *
+ * Must return a UNIX time stamp or null if the backend
+ * doesn't support it.
+ *
+ * @param string $addressbookid
+ * @param mixed $id
+ * @returns int | null
+ */
+ public function lastModifiedContact($addressbookid, $id) {
+ $contact = $this->getContact($addressbookid, $id);
+ return ($contact ? $contact['lastmodified'] : null);
+ }
+
+ /**
+ * @brief Get the contact id from the uri.
+ *
+ * @param mixed $id
+ * @returns int | null
+ */
+ public function getIdFromUri($uri) {
+ $query = 'SELECT `id` FROM `'. $this->cardsTableName . '` WHERE `uri` = ?';
+ $stmt = \OCP\DB::prepare($query);
+ $result = $stmt->execute(array($uri));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return null;
+ }
+ $one = $result->fetchOne();
+ if(!$one) {
+ \OCP\Util::writeLog('contacts', __METHOD__.', Not found, uri: '. $uri, \OCP\Util::DEBUG);
+ return null;
+ }
+ return $one;
+ }
+
+ private function createAddressBookURI($displayname, $userid = null) {
+ $userid = $userid ? $userid : \OCP\User::getUser();
+ $name = str_replace(' ', '_', strtolower($displayname));
+ try {
+ $stmt = \OCP\DB::prepare('SELECT `uri` FROM `' . $this->addressBooksTableName . '` WHERE `userid` = ? ');
+ $result = $stmt->execute(array($userid));
+ if (\OCP\DB::isError($result)) {
+ \OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+ return $name;
+ }
+ } catch(\Exception $e) {
+ \OCP\Util::writeLog('contacts', __METHOD__ . ' exception: ' . $e->getMessage(), \OCP\Util::ERROR);
+ return $name;
+ }
+ $uris = array();
+ while($row = $result->fetchRow()) {
+ $uris[] = $row['uri'];
+ }
+
+ $newname = $name;
+ $i = 1;
+ while(in_array($newname, $uris)) {
+ $newname = $name.$i;
+ $i = $i + 1;
+ }
+ return $newname;
+ }
+
+ /**
+ * @brief Checks if a contact with the same URI already exist in the address book.
+ * @param string $addressBookId Address book ID.
+ * @param string $uri
+ * @returns string Unique URI
+ */
+ protected function uniqueURI($addressBookId, $uri) {
+ $stmt = \OCP\DB::prepare( 'SELECT * FROM `' . $this->cardsTableName . '` WHERE `addressbookid` = ? AND `uri` = ?' );
+
+ $result = $stmt->execute(array($addressBookId, $uri));
+
+ if($result->numRows() > 0) {
+ while(true) {
+ $uri = Properties::generateUID() . '.vcf';
+ $result = $stmt->execute(array($addressBookId, $uri));
+ if($result->numRows() > 0) {
+ continue;
+ } else {
+ return $uri;
+ }
+ }
+ } else {
+ return $uri;
+ }
+ }
+}
diff --cc apps/contacts/lib/backend/shared.php
index 4896405,0000000..e4213b0
mode 100644,000000..100644
--- a/apps/contacts/lib/backend/shared.php
+++ b/apps/contacts/lib/backend/shared.php
@@@ -1,131 -1,0 +1,131 @@@
+<?php
+/**
+ * ownCloud - Backend for Shared contacts
+ *
+ * @author Thomas Tanghus
+ * @copyright 2013 Thomas Tanghus (thomas at 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/>.
+ *
+ */
+
+namespace OCA\Contacts\Backend;
+
+use OCA\Contacts;
+
+/**
+ * Subclass this class for Cantacts backends
+ */
+
+class Shared extends Database {
+
+ public $name = 'shared';
+ public $addressbooks = array();
+
+ /**
+ * Returns the list of addressbooks for a specific user.
+ *
+ * @param string $principaluri
+ * @return array
+ */
+ public function getAddressBooksForUser(array $options = array()) {
+
+ // workaround for https://github.com/owncloud/core/issues/2814
+ $maybeSharedAddressBook = \OCP\Share::getItemsSharedWith(
+ 'addressbook',
+ Contacts\Share\Addressbook::FORMAT_ADDRESSBOOKS
+ );
- foreach($maybeSharedAddressBook as $sharedAddressbook) {
- if(isset($sharedAddressbook['id'])) {
- $this->addressbooks[] = $this->getAddressBook($sharedAddressbook['id']);
++
++ foreach ($maybeSharedAddressBook as $sharedAddressbook) {
++
++ if (isset($sharedAddressbook['id'])) {
++ $this->addressBooks[$sharedAddressbook['id']] = $sharedAddressbook;
++ $this->addressBooks[$sharedAddressbook['id']]['backend'] = $this->name;
+ }
+ }
+
- foreach($this->addressbooks as &$addressBook) {
- $addressBook['backend'] = $this->name;
- }
- return $this->addressbooks;
++ return $this->addressBooks;
+ }
+
+ /**
+ * Returns a specific address book.
+ *
+ * @param string $addressbookid
+ * @param mixed $id Contact ID
+ * @return mixed
+ */
+ public function getAddressBook($addressbookid, array $options = array()) {
+ $addressBook = \OCP\Share::getItemSharedWithBySource(
+ 'addressbook',
+ $addressbookid,
+ Contacts\Share\Addressbook::FORMAT_ADDRESSBOOKS
+ );
+ // Not sure if I'm doing it wrongly, or if its supposed to return
+ // the info in an array?
+ $addressBook = (isset($addressBook['permissions']) ? $addressBook : $addressBook[0]);
+ $addressBook['backend'] = $this->name;
+ return $addressBook;
+ }
+
+ /**
+ * Returns all contacts for a specific addressbook id.
+ *
+ * @param string $addressbookid
+ * @param bool $omitdata Don't fetch the entire carddata or vcard.
+ * @return array
+ */
+ public function getContacts($addressbookid, array $options = array()) {
+
+ $addressBook = $this->getAddressBook($addressbookid);
+ if(!$addressBook) {
+ throw new \Exception('Shared Address Book not found: ' . $addressbookid, 404);
+ }
+ $permissions = $addressBook['permissions'];
+
+ $cards = parent::getContacts($addressbookid, $options);
+
+ foreach($cards as &$card) {
+ $card['permissions'] = $permissions;
+ }
+
+ return $cards;
+ }
+
+ /**
+ * Returns a specific contact.
+ *
+ * The $id for Database and Shared backends can be an array containing
+ * either 'id' or 'uri' to be able to play seamlessly with the
+ * CardDAV backend.
+ * @see \Database\getContact
+ *
+ * @param string $addressbookid
+ * @param mixed $id Contact ID
+ * @return array|false
+ */
+ public function getContact($addressbookid, $id, array $options = array()) {
+ $addressBook = $this->getAddressBook($addressbookid);
+ if(!$addressBook) {
+ throw new \Exception('Shared Address Book not found: ' . $addressbookid, 404);
+ }
+ $permissions = $addressBook['permissions'];
+
+ $card = parent::getContact($addressbookid, $id, $options);
+ if(!$card) {
+ throw new \Exception('Shared Contact not found: ' . implode(',', $id), 404);
+ }
+ $card['permissions'] = $permissions;
+ return $card;
+ }
+}
diff --cc apps/contacts/lib/dispatcher.php
index a85432e,0000000..021256b
mode 100644,000000..100644
--- a/apps/contacts/lib/dispatcher.php
+++ b/apps/contacts/lib/dispatcher.php
@@@ -1,72 -1,0 +1,76 @@@
+<?php
+/**
+ * Copyright (c) 2013 Thomas Tanghus (thomas at tanghus.net)
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OCA\Contacts;
+
+use OCP\AppFramework\App as MainApp,
+ OCP\AppFramework\IAppContainer,
+ OCA\Contacts\App,
+ OCA\Contacts\Middleware\Http as HttpMiddleware,
+ OCA\Contacts\Controller\PageController,
+ OCA\Contacts\Controller\AddressBookController,
+ OCA\Contacts\Controller\GroupController,
+ OCA\Contacts\Controller\ContactController,
+ OCA\Contacts\Controller\ContactPhotoController,
+ OCA\Contacts\Controller\SettingsController,
+ OCA\Contacts\Controller\ImportController,
+ OCA\Contacts\Controller\ExportController;
+
+/**
+ * This class manages our app actions
+ *
+ * TODO: Merge with App
+ */
+
+class Dispatcher extends MainApp {
+ /**
+ * @var App
+ */
+ protected $app;
+
+ public function __construct($params) {
+ parent::__construct('contacts', $params);
+ $this->container = $this->getContainer();
- $this->container->registerMiddleware(new HttpMiddleware($this->container));
+ $this->app = new App($this->container->query('API')->getUserId());
+ $this->registerServices();
++ $this->container->registerMiddleware('HttpMiddleware');
+ }
+
++
+ public function registerServices() {
+ $app = $this->app;
++ $this->container->registerService('HttpMiddleware', function(IAppContainer $container) use($app) {
++ return new HttpMiddleware($container);
++ });
+ $this->container->registerService('PageController', function(IAppContainer $container) use($app) {
+ return new PageController($container, $app);
+ });
+ $this->container->registerService('AddressBookController', function(IAppContainer $container) use($app) {
+ return new AddressBookController($container, $app);
+ });
+ $this->container->registerService('GroupController', function(IAppContainer $container) use($app) {
+ return new GroupController($container, $app);
+ });
+ $this->container->registerService('ContactController', function(IAppContainer $container) use($app) {
+ return new ContactController($container, $app);
+ });
+ $this->container->registerService('ContactPhotoController', function(IAppContainer $container) use($app) {
+ return new ContactPhotoController($container, $app);
+ });
+ $this->container->registerService('SettingsController', function(IAppContainer $container) use($app) {
+ return new SettingsController($container, $app);
+ });
+ $this->container->registerService('ImportController', function(IAppContainer $container) use($app) {
+ return new ImportController($container, $app);
+ });
+ $this->container->registerService('ExportController', function(IAppContainer $container) use($app) {
+ return new ExportController($container, $app);
+ });
+ }
+
+}
diff --cc apps/contacts/lib/share/addressbook.php
index 6a5ddf0,0000000..0de637f
mode 100644,000000..100644
--- a/apps/contacts/lib/share/addressbook.php
+++ b/apps/contacts/lib/share/addressbook.php
@@@ -1,127 -1,0 +1,126 @@@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv at thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OCA\Contacts\Share;
+use OCA\Contacts\App;
+
+class Addressbook implements \OCP\Share_Backend_Collection {
+ const FORMAT_ADDRESSBOOKS = 1;
+ const FORMAT_COLLECTION = 2;
+
+ /**
+ * @var \OCA\Contacts\App;
+ */
+ public $app;
+
+ public function __construct() {
+ $this->app = new App(\OCP\User::getUser());
+ }
+
+ /**
+ * @brief Get the source of the item to be stored in the database
+ * @param string Item
+ * @param string Owner of the item
+ * @return mixed|array|false Source
+ *
+ * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file'
+ * Return false if the item does not exist for the user
+ *
+ * The formatItems() function will translate the source returned back into the item
+ */
+ public function isValidSource($itemSource, $uidOwner) {
+ $app = new App($uidOwner);
+
+ try {
+ $app->getAddressBook('local', $itemSource);
+ } catch(\Exception $e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @brief Get a unique name of the item for the specified user
+ * @param string Item
+ * @param string|false User the item is being shared with
+ * @param array|null List of similar item names already existing as shared items
+ * @return string Target name
+ *
+ * This function needs to verify that the user does not already have an item with this name.
+ * If it does generate a new name e.g. name_#
+ */
+ public function generateTarget($itemSource, $shareWith, $exclude = null) {
+ // Get app for the sharee
+ $app = new App($shareWith);
+ $backend = $app->getBackend('local');
+
+ // Get address book for the owner
+ $addressBook = $this->app->getBackend('local')->getAddressBook($itemSource);
+
+ $userAddressBooks = array();
+
+ foreach($backend->getAddressBooksForUser() as $userAddressBook) {
+ $userAddressBooks[] = $userAddressBook['displayname'];
+ }
+ $name = $addressBook['displayname'] . '(' . $addressBook['owner'] . ')';
+ $suffix = '';
+ while (in_array($name.$suffix, $userAddressBooks)) {
+ $suffix++;
+ }
+
+ $suffix = $suffix ? ' ' . $suffix : '';
+ return $name.$suffix;
+ }
+
+ /**
+ * @brief Converts the shared item sources back into the item in the specified format
+ * @param array Shared items
+ * @param int Format
+ * @return ?
+ *
+ * The items array is a 3-dimensional array with the item_source as the first key and the share id as the second key to an array with the share info.
+ * The key/value pairs included in the share info depend on the function originally called:
+ * If called by getItem(s)Shared: id, item_type, item, item_source, share_type, share_with, permissions, stime, file_source
+ * If called by getItem(s)SharedWith: id, item_type, item, item_source, item_target, share_type, share_with, permissions, stime, file_source, file_target
+ * This function allows the backend to control the output of shared items with custom formats.
+ * It is only called through calls to the public getItem(s)Shared(With) functions.
+ */
+ public function formatItems($items, $format, $parameters = null, $include = false) {
+ //\OCP\Util::writeLog('contacts', __METHOD__
+ // . ' ' . $include . ' ' . print_r($items, true), \OCP\Util::DEBUG);
+ $addressBooks = array();
+ $backend = $this->app->getBackend('local');
+
+ if ($format === self::FORMAT_ADDRESSBOOKS) {
+ foreach ($items as $item) {
- //\OCP\Util::writeLog('contacts', __METHOD__.' item_source: ' . $item['item_source'] . ' include: '
- // . (int)$include, \OCP\Util::DEBUG);
- $addressBook = $backend->getAddressBook($item['item_source']);
++ //\OCP\Util::writeLog('contacts', __METHOD__.' item owner: ' . print_r($item['uid_owner'], true) , \OCP\Util::DEBUG);
++ $addressBook = $backend->getAddressBook($item['item_source'], array('shared_by' => $item['uid_owner']));
+ if ($addressBook) {
+ $addressBook['displayname'] = $addressBook['displayname'] . ' (' . $addressBook['owner'] . ')';
+ $addressBook['permissions'] = $item['permissions'];
+ $addressBooks[] = $addressBook;
+ }
+ }
+ } elseif ($format === self::FORMAT_COLLECTION) {
+ foreach ($items as $item) {
+ }
+ }
+ return $addressBooks;
+ }
+
+ public function getChildren($itemSource) {
+ \OCP\Util::writeLog('contacts', __METHOD__.' item_source: ' . $itemSource, \OCP\Util::DEBUG);
+ $contacts = $this->app->getBackend('local')->getContacts($itemSource);
+ $children = array();
+ foreach($contacts as $contact) {
+ $children[] = array('source' => $contact['id'], 'target' => $contact['displayname']);
+ }
+ return $children;
+ }
+
+}
diff --cc apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/Docx.php
index f3c3e11,0000000..f5d74a6
mode 100644,000000..100644
--- a/apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/Docx.php
+++ b/apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/Docx.php
@@@ -1,151 -1,0 +1,152 @@@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license at zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Search_Lucene
+ * @subpackage Document
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Docx.php 24593 2012-01-05 20:35:02Z matthew $
+ */
+
+/** Zend_Search_Lucene_Document_OpenXml */
+require_once 'Zend/Search/Lucene/Document/OpenXml.php';
+
+/**
+ * Docx document.
+ *
+ * @category Zend
+ * @package Zend_Search_Lucene
+ * @subpackage Document
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Search_Lucene_Document_Docx extends Zend_Search_Lucene_Document_OpenXml {
+ /**
+ * Xml Schema - WordprocessingML
+ *
+ * @var string
+ */
+ const SCHEMA_WORDPROCESSINGML = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
+
+ /**
+ * Object constructor
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @throws Zend_Search_Lucene_Exception
+ */
+ private function __construct($fileName, $storeContent) {
+ if (!class_exists('ZipArchive', false)) {
+ require_once 'Zend/Search/Lucene/Exception.php';
+ throw new Zend_Search_Lucene_Exception('MS Office documents processing functionality requires Zip extension to be loaded');
+ }
+
+ // Document data holders
+ $documentBody = array();
+ $coreProperties = array();
+
+ // Open OpenXML package
+ $package = new ZipArchive();
+ $package->open($fileName);
+
+ // Read relations and search for officeDocument
+ $relationsXml = $package->getFromName('_rels/.rels');
+ if ($relationsXml === false) {
+ require_once 'Zend/Search/Lucene/Exception.php';
+ throw new Zend_Search_Lucene_Exception('Invalid archive or corrupted .docx file.');
+ }
++ libxml_disable_entity_loader(true);
+ $relations = simplexml_load_string($relationsXml);
+ foreach($relations->Relationship as $rel) {
+ if ($rel ["Type"] == Zend_Search_Lucene_Document_OpenXml::SCHEMA_OFFICEDOCUMENT) {
+ // Found office document! Read in contents...
+ $contents = simplexml_load_string($package->getFromName(
+ $this->absoluteZipPath(dirname($rel['Target'])
+ . '/'
+ . basename($rel['Target']))
+ ));
+
+ $contents->registerXPathNamespace('w', Zend_Search_Lucene_Document_Docx::SCHEMA_WORDPROCESSINGML);
+ $paragraphs = $contents->xpath('//w:body/w:p');
+
+ foreach ($paragraphs as $paragraph) {
+ $runs = $paragraph->xpath('.//w:r/*[name() = "w:t" or name() = "w:br"]');
+
+ if ($runs === false) {
+ // Paragraph doesn't contain any text or breaks
+ continue;
+ }
+
+ foreach ($runs as $run) {
+ if ($run->getName() == 'br') {
+ // Break element
+ $documentBody[] = ' ';
+ } else {
+ $documentBody[] = (string)$run;
+ }
+ }
+
+ // Add space after each paragraph. So they are not bound together.
+ $documentBody[] = ' ';
+ }
+
+ break;
+ }
+ }
+
+ // Read core properties
+ $coreProperties = $this->extractMetaData($package);
+
+ // Close file
+ $package->close();
+
+ // Store filename
+ $this->addField(Zend_Search_Lucene_Field::Text('filename', $fileName, 'UTF-8'));
+
+ // Store contents
+ if ($storeContent) {
+ $this->addField(Zend_Search_Lucene_Field::Text('body', implode('', $documentBody), 'UTF-8'));
+ } else {
+ $this->addField(Zend_Search_Lucene_Field::UnStored('body', implode('', $documentBody), 'UTF-8'));
+ }
+
+ // Store meta data properties
+ foreach ($coreProperties as $key => $value) {
+ $this->addField(Zend_Search_Lucene_Field::Text($key, $value, 'UTF-8'));
+ }
+
+ // Store title (if not present in meta data)
+ if (! isset($coreProperties['title'])) {
+ $this->addField(Zend_Search_Lucene_Field::Text('title', $fileName, 'UTF-8'));
+ }
+ }
+
+ /**
+ * Load Docx document from a file
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @return Zend_Search_Lucene_Document_Docx
+ * @throws Zend_Search_Lucene_Document_Exception
+ */
+ public static function loadDocxFile($fileName, $storeContent = false) {
+ if (!is_readable($fileName)) {
+ require_once 'Zend/Search/Lucene/Document/Exception.php';
+ throw new Zend_Search_Lucene_Document_Exception('Provided file \'' . $fileName . '\' is not readable.');
+ }
+
+ return new Zend_Search_Lucene_Document_Docx($fileName, $storeContent);
+ }
+}
diff --cc apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/OpenXml.php
index 89772f1,0000000..871b75e
mode 100644,000000..100644
--- a/apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/OpenXml.php
+++ b/apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/OpenXml.php
@@@ -1,129 -1,0 +1,130 @@@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license at zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Search_Lucene
+ * @subpackage Document
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: OpenXml.php 24593 2012-01-05 20:35:02Z matthew $
+ */
+
+
+/** Zend_Search_Lucene_Document */
+require_once 'Zend/Search/Lucene/Document.php';
+
+
+/**
+ * OpenXML document.
+ *
+ * @category Zend
+ * @package Zend_Search_Lucene
+ * @subpackage Document
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+abstract class Zend_Search_Lucene_Document_OpenXml extends Zend_Search_Lucene_Document
+{
+ /**
+ * Xml Schema - Relationships
+ *
+ * @var string
+ */
+ const SCHEMA_RELATIONSHIP = 'http://schemas.openxmlformats.org/package/2006/relationships';
+
+ /**
+ * Xml Schema - Office document
+ *
+ * @var string
+ */
+ const SCHEMA_OFFICEDOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument';
+
+ /**
+ * Xml Schema - Core properties
+ *
+ * @var string
+ */
+ const SCHEMA_COREPROPERTIES = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
+
+ /**
+ * Xml Schema - Dublin Core
+ *
+ * @var string
+ */
+ const SCHEMA_DUBLINCORE = 'http://purl.org/dc/elements/1.1/';
+
+ /**
+ * Xml Schema - Dublin Core Terms
+ *
+ * @var string
+ */
+ const SCHEMA_DUBLINCORETERMS = 'http://purl.org/dc/terms/';
+
+ /**
+ * Extract metadata from document
+ *
+ * @param ZipArchive $package ZipArchive OpenXML package
+ * @return array Key-value pairs containing document meta data
+ */
+ protected function extractMetaData(ZipArchive $package)
+ {
+ // Data holders
+ $coreProperties = array();
+
+ // Read relations and search for core properties
++ libxml_disable_entity_loader(true);
+ $relations = simplexml_load_string($package->getFromName("_rels/.rels"));
+ foreach ($relations->Relationship as $rel) {
+ if ($rel["Type"] == Zend_Search_Lucene_Document_OpenXml::SCHEMA_COREPROPERTIES) {
+ // Found core properties! Read in contents...
+ $contents = simplexml_load_string(
+ $package->getFromName(dirname($rel["Target"]) . "/" . basename($rel["Target"]))
+ );
+
+ foreach ($contents->children(Zend_Search_Lucene_Document_OpenXml::SCHEMA_DUBLINCORE) as $child) {
+ $coreProperties[$child->getName()] = (string)$child;
+ }
+ foreach ($contents->children(Zend_Search_Lucene_Document_OpenXml::SCHEMA_COREPROPERTIES) as $child) {
+ $coreProperties[$child->getName()] = (string)$child;
+ }
+ foreach ($contents->children(Zend_Search_Lucene_Document_OpenXml::SCHEMA_DUBLINCORETERMS) as $child) {
+ $coreProperties[$child->getName()] = (string)$child;
+ }
+ }
+ }
+
+ return $coreProperties;
+ }
+
+ /**
+ * Determine absolute zip path
+ *
+ * @param string $path
+ * @return string
+ */
+ protected function absoluteZipPath($path) {
+ $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
+ $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
+ $absolutes = array();
+ foreach ($parts as $part) {
+ if ('.' == $part) continue;
+ if ('..' == $part) {
+ array_pop($absolutes);
+ } else {
+ $absolutes[] = $part;
+ }
+ }
+ return implode('/', $absolutes);
+ }
+}
diff --cc apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/Xlsx.php
index 4bccb13,0000000..dcdd14f
mode 100644,000000..100644
--- a/apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/Xlsx.php
+++ b/apps/search_lucene/3rdparty/Zend/Search/Lucene/Document/Xlsx.php
@@@ -1,263 -1,0 +1,264 @@@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license at zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Search_Lucene
+ * @subpackage Document
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Xlsx.php 24593 2012-01-05 20:35:02Z matthew $
+ */
+
+
+/** Zend_Search_Lucene_Document_OpenXml */
+require_once 'Zend/Search/Lucene/Document/OpenXml.php';
+
+/**
+ * Xlsx document.
+ *
+ * @category Zend
+ * @package Zend_Search_Lucene
+ * @subpackage Document
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Search_Lucene_Document_Xlsx extends Zend_Search_Lucene_Document_OpenXml
+{
+ /**
+ * Xml Schema - SpreadsheetML
+ *
+ * @var string
+ */
+ const SCHEMA_SPREADSHEETML = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
+
+ /**
+ * Xml Schema - DrawingML
+ *
+ * @var string
+ */
+ const SCHEMA_DRAWINGML = 'http://schemas.openxmlformats.org/drawingml/2006/main';
+
+ /**
+ * Xml Schema - Shared Strings
+ *
+ * @var string
+ */
+ const SCHEMA_SHAREDSTRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
+
+ /**
+ * Xml Schema - Worksheet relation
+ *
+ * @var string
+ */
+ const SCHEMA_WORKSHEETRELATION = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet';
+
+ /**
+ * Xml Schema - Slide notes relation
+ *
+ * @var string
+ */
+ const SCHEMA_SLIDENOTESRELATION = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide';
+
+ /**
+ * Object constructor
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @throws Zend_Search_Lucene_Exception
+ */
+ private function __construct($fileName, $storeContent)
+ {
+ if (!class_exists('ZipArchive', false)) {
+ require_once 'Zend/Search/Lucene/Exception.php';
+ throw new Zend_Search_Lucene_Exception('MS Office documents processing functionality requires Zip extension to be loaded');
+ }
+
+ // Document data holders
+ $sharedStrings = array();
+ $worksheets = array();
+ $documentBody = array();
+ $coreProperties = array();
+
+ // Open OpenXML package
+ $package = new ZipArchive();
+ $package->open($fileName);
+
+ // Read relations and search for officeDocument
+ $relationsXml = $package->getFromName('_rels/.rels');
+ if ($relationsXml === false) {
+ require_once 'Zend/Search/Lucene/Exception.php';
+ throw new Zend_Search_Lucene_Exception('Invalid archive or corrupted .xlsx file.');
+ }
++ libxml_disable_entity_loader(true);
+ $relations = simplexml_load_string($relationsXml);
+ foreach ($relations->Relationship as $rel) {
+ if ($rel["Type"] == Zend_Search_Lucene_Document_OpenXml::SCHEMA_OFFICEDOCUMENT) {
+ // Found office document! Read relations for workbook...
+ $workbookRelations = simplexml_load_string($package->getFromName( $this->absoluteZipPath(dirname($rel["Target"]) . "/_rels/" . basename($rel["Target"]) . ".rels")) );
+ $workbookRelations->registerXPathNamespace("rel", Zend_Search_Lucene_Document_OpenXml::SCHEMA_RELATIONSHIP);
+
+ // Read shared strings
+ $sharedStringsPath = $workbookRelations->xpath("rel:Relationship[@Type='" . Zend_Search_Lucene_Document_Xlsx::SCHEMA_SHAREDSTRINGS . "']");
+ $sharedStringsPath = (string)$sharedStringsPath[0]['Target'];
+ $xmlStrings = simplexml_load_string($package->getFromName( $this->absoluteZipPath(dirname($rel["Target"]) . "/" . $sharedStringsPath)) );
+ if (isset($xmlStrings) && isset($xmlStrings->si)) {
+ foreach ($xmlStrings->si as $val) {
+ if (isset($val->t)) {
+ $sharedStrings[] = (string)$val->t;
+ } elseif (isset($val->r)) {
+ $sharedStrings[] = $this->_parseRichText($val);
+ }
+ }
+ }
+
+ // Loop relations for workbook and extract worksheets...
+ foreach ($workbookRelations->Relationship as $workbookRelation) {
+ if ($workbookRelation["Type"] == Zend_Search_Lucene_Document_Xlsx::SCHEMA_WORKSHEETRELATION) {
+ $worksheets[ str_replace( 'rId', '', (string)$workbookRelation["Id"]) ] = simplexml_load_string(
+ $package->getFromName( $this->absoluteZipPath(dirname($rel["Target"]) . "/" . dirname($workbookRelation["Target"]) . "/" . basename($workbookRelation["Target"])) )
+ );
+ }
+ }
+
+ break;
+ }
+ }
+
+ // Sort worksheets
+ ksort($worksheets);
+
+ // Extract contents from worksheets
+ foreach ($worksheets as $sheetKey => $worksheet) {
+ foreach ($worksheet->sheetData->row as $row) {
+ foreach ($row->c as $c) {
+ // Determine data type
+ $dataType = (string)$c["t"];
+ switch ($dataType) {
+ case "s":
+ // Value is a shared string
+ if ((string)$c->v != '') {
+ $value = $sharedStrings[intval($c->v)];
+ } else {
+ $value = '';
+ }
+
+ break;
+
+ case "b":
+ // Value is boolean
+ $value = (string)$c->v;
+ if ($value == '0') {
+ $value = false;
+ } else if ($value == '1') {
+ $value = true;
+ } else {
+ $value = (bool)$c->v;
+ }
+
+ break;
+
+ case "inlineStr":
+ // Value is rich text inline
+ $value = $this->_parseRichText($c->is);
+
+ break;
+
+ case "e":
+ // Value is an error message
+ if ((string)$c->v != '') {
+ $value = (string)$c->v;
+ } else {
+ $value = '';
+ }
+
+ break;
+
+ default:
+ // Value is a string
+ $value = (string)$c->v;
+
+ // Check for numeric values
+ if (is_numeric($value) && $dataType != 's') {
+ if ($value == (int)$value) $value = (int)$value;
+ elseif ($value == (float)$value) $value = (float)$value;
+ elseif ($value == (double)$value) $value = (double)$value;
+ }
+ }
+
+ $documentBody[] = $value;
+ }
+ }
+ }
+
+ // Read core properties
+ $coreProperties = $this->extractMetaData($package);
+
+ // Close file
+ $package->close();
+
+ // Store filename
+ $this->addField(Zend_Search_Lucene_Field::Text('filename', $fileName, 'UTF-8'));
+
+ // Store contents
+ if ($storeContent) {
+ $this->addField(Zend_Search_Lucene_Field::Text('body', implode(' ', $documentBody), 'UTF-8'));
+ } else {
+ $this->addField(Zend_Search_Lucene_Field::UnStored('body', implode(' ', $documentBody), 'UTF-8'));
+ }
+
+ // Store meta data properties
+ foreach ($coreProperties as $key => $value)
+ {
+ $this->addField(Zend_Search_Lucene_Field::Text($key, $value, 'UTF-8'));
+ }
+
+ // Store title (if not present in meta data)
+ if (!isset($coreProperties['title']))
+ {
+ $this->addField(Zend_Search_Lucene_Field::Text('title', $fileName, 'UTF-8'));
+ }
+ }
+
+ /**
+ * Parse rich text XML
+ *
+ * @param SimpleXMLElement $is
+ * @return string
+ */
+ private function _parseRichText($is = null) {
+ $value = array();
+
+ if (isset($is->t)) {
+ $value[] = (string)$is->t;
+ } else {
+ foreach ($is->r as $run) {
+ $value[] = (string)$run->t;
+ }
+ }
+
+ return implode('', $value);
+ }
+
+ /**
+ * Load Xlsx document from a file
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @return Zend_Search_Lucene_Document_Xlsx
+ */
+ public static function loadXlsxFile($fileName, $storeContent = false)
+ {
+ return new Zend_Search_Lucene_Document_Xlsx($fileName, $storeContent);
+ }
+}
diff --cc apps/search_lucene/document/Ods.php
index e5419f2,0000000..8ba3c1a
mode 100644,000000..100644
--- a/apps/search_lucene/document/Ods.php
+++ b/apps/search_lucene/document/Ods.php
@@@ -1,87 -1,0 +1,88 @@@
+<?php
+
+namespace OCA\Search_Lucene\Document;
+/**
+ * Ods document.
+ * @see http://en.wikipedia.org/wiki/OpenDocument_technical_specification
+ */
+class Ods extends OpenDocument {
+
+ const SCHEMA_ODTABLE = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0';
+
+ /**
+ * Object constructor
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @throws \Zend_Search_Lucene_Exception
+ */
+ private function __construct($fileName, $storeContent) {
+ if (!class_exists('ZipArchive', false)) {
+ throw new \Zend_Search_Lucene_Exception('Open Document Spreadsheet processing functionality requires Zip extension to be loaded');
+ }
+
+ // Document data holders
+ $documentTables = array();
+ $documentCells = array();
+
+ // Open OpenXML package
+ $package = new \ZipArchive();
+ $package->open($fileName);
+
+ // Read relations and search for officeDocument
+ $content = $package->getFromName('content.xml');
+ if ($content === false) {
+ throw new \Zend_Search_Lucene_Exception('Invalid archive or corrupted .ods file.');
+ }
++ libxml_disable_entity_loader(true);
+ $sxe = simplexml_load_string($content, 'SimpleXMLElement', LIBXML_NOBLANKS | LIBXML_COMPACT);
+
+ foreach ($sxe->xpath('//table:table[@table:name]') as $table) {
+ $documentTables[] = (string)$table->attributes($this::SCHEMA_ODTABLE)->name;
+ }
+ foreach ($sxe->xpath('//text:p') as $cell) {
+ $documentCells[] = (string)$cell;
+ }
+
+ // Read core properties
+ $coreProperties = $this->extractMetaData($package);
+
+ // Close file
+ $package->close();
+
+ // Store contents
+ if ($storeContent) {
+ $this->addField(\Zend_Search_Lucene_Field::Text('sheets', implode(' ', $documentTables), 'UTF-8'));
+ $this->addField(\Zend_Search_Lucene_Field::Text('body', implode(' ', $documentCells), 'UTF-8'));
+ } else {
+ $this->addField(\Zend_Search_Lucene_Field::UnStored('sheets', implode(' ', $documentTables), 'UTF-8'));
+ $this->addField(\Zend_Search_Lucene_Field::UnStored('body', implode(' ', $documentCells), 'UTF-8'));
+ }
+
+ // Store meta data properties
+ foreach ($coreProperties as $key => $value) {
+ $this->addField(\Zend_Search_Lucene_Field::Text($key, $value, 'UTF-8'));
+ }
+
+ // Store title (if not present in meta data)
+ if (! isset($coreProperties['title'])) {
+ $this->addField(\Zend_Search_Lucene_Field::Text('title', $fileName, 'UTF-8'));
+ }
+ }
+
+ /**
+ * Load Ods document from a file
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @return Ods
+ * @throws \Zend_Search_Lucene_Document_Exception
+ */
+ public static function loadOdsFile($fileName, $storeContent = false) {
+ if (!is_readable($fileName)) {
+ throw new \Zend_Search_Lucene_Document_Exception('Provided file \'' . $fileName . '\' is not readable.');
+ }
+
+ return new Ods($fileName, $storeContent);
+ }
+}
diff --cc apps/search_lucene/document/Odt.php
index ea36313,0000000..c850c71
mode 100644,000000..100644
--- a/apps/search_lucene/document/Odt.php
+++ b/apps/search_lucene/document/Odt.php
@@@ -1,85 -1,0 +1,86 @@@
+<?php
+
+namespace OCA\Search_Lucene\Document;
+/**
+ * Odt document.
+ * @see http://en.wikipedia.org/wiki/OpenDocument_technical_specification
+ */
+class Odt extends OpenDocument {
+ /**
+ * Object constructor
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @throws \Zend_Search_Lucene_Exception
+ */
+ private function __construct($fileName, $storeContent) {
+ if (!class_exists('ZipArchive', false)) {
+ throw new \Zend_Search_Lucene_Exception('Open Document Text processing functionality requires Zip extension to be loaded');
+ }
+
+ // Document data holders
+ $documentHeadlines = array();
+ $documentParagraphs = array();
+
+ // Open OpenXML package
+ $package = new \ZipArchive();
+ $package->open($fileName);
+
+ // Read relations and search for officeDocument
+ $content = $package->getFromName('content.xml');
+ if ($content === false) {
+ throw new \Zend_Search_Lucene_Exception('Invalid archive or corrupted .odt file.');
+ }
++ libxml_disable_entity_loader(true);
+ $sxe = simplexml_load_string($content, 'SimpleXMLElement', LIBXML_NOBLANKS | LIBXML_COMPACT);
+
+ foreach ($sxe->xpath('//text:h') as $headline) {
+ $documentHeadlines[] = (string)$headline;
+ }
+
+ foreach ($sxe->xpath('//text:p') as $paragraph) {
+ $documentParagraphs[] = (string)$paragraph;
+ }
+
+ // Read core properties
+ $coreProperties = $this->extractMetaData($package);
+
+ // Close file
+ $package->close();
+
+ // Store contents
+ if ($storeContent) {
+ $this->addField(\Zend_Search_Lucene_Field::Text('headlines', implode(' ', $documentHeadlines), 'UTF-8'));
+ $this->addField(\Zend_Search_Lucene_Field::Text('body', implode(' ', $documentParagraphs), 'UTF-8'));
+ } else {
+ $this->addField(\Zend_Search_Lucene_Field::UnStored('headlines', implode(' ', $documentHeadlines), 'UTF-8'));
+ $this->addField(\Zend_Search_Lucene_Field::UnStored('body', implode(' ', $documentParagraphs), 'UTF-8'));
+ }
+
+ // Store meta data properties
+ foreach ($coreProperties as $key => $value) {
+ $this->addField(\Zend_Search_Lucene_Field::Text($key, $value, 'UTF-8'));
+ }
+
+ // Store title (if not present in meta data)
+ if (! isset($coreProperties['title'])) {
+ $this->addField(\Zend_Search_Lucene_Field::Text('title', $fileName, 'UTF-8'));
+ }
+ }
+
+ /**
+ * Load Odt document from a file
+ *
+ * @param string $fileName
+ * @param boolean $storeContent
+ * @return Odt
+ * @throws \Zend_Search_Lucene_Document_Exception
+ */
+ public static function loadOdtFile($fileName, $storeContent = false) {
+ if (!is_readable($fileName)) {
+ throw new \Zend_Search_Lucene_Document_Exception('Provided file \'' . $fileName . '\' is not readable.');
+ }
+
+ return new Odt($fileName, $storeContent);
+ }
+}
diff --cc apps/search_lucene/document/OpenDocument.php
index d944902,0000000..d2a14d3
mode 100644,000000..100644
--- a/apps/search_lucene/document/OpenDocument.php
+++ b/apps/search_lucene/document/OpenDocument.php
@@@ -1,81 -1,0 +1,82 @@@
+<?php
+
+namespace OCA\Search_Lucene\Document;
+/**
+ * OpenDocument document.
+ */
+abstract class OpenDocument extends \Zend_Search_Lucene_Document
+{
+ const OASIS_XPATH_TITLE = '//dc:title';
+ const OASIS_XPATH_SUBJECT = '//dc:subject';
+ const OASIS_XPATH_CREATOR = '//meta:initial-creator';
+ const OASIS_XPATH_KEYWORDS = '//meta:keyword';
+ const OASIS_XPATH_CREATED = '//meta:creation-date';
+ const OASIS_XPATH_MODIFIED = '//dc:date';
+
+ /**
+ * Extract metadata from document
+ *
+ * @param ZipArchive $package ZipArchive OpenDocument package
+ * @return array Key-value pairs containing document meta data
+ */
+ protected function extractMetaData(\ZipArchive $package)
+ {
+ // Data holders
+ $coreProperties = array();
+
+ // Read relations and search for core properties
++ libxml_disable_entity_loader(true);
+ $sxe = simplexml_load_string($package->getFromName("meta.xml"));
+
+ if (is_object($sxe) && $sxe instanceof \SimpleXMLElement) {
+
+ $coreProperties['title'] = $this->extractTermsFromMetadata($sxe, $this::OASIS_XPATH_TITLE);
+
+ $coreProperties['subject'] = $this->extractTermsFromMetadata($sxe, $this::OASIS_XPATH_SUBJECT);
+
+ $coreProperties['creator'] = $this->extractTermsFromMetadata($sxe, $this::OASIS_XPATH_CREATOR);
+
+ $coreProperties['keywords'] = $this->extractTermsFromMetadata($sxe, $this::OASIS_XPATH_KEYWORDS);
+
+ //replace T in date string with ' '
+ $coreProperties['created'] = str_replace('T', ' ', $this->extractTermsFromMetadata($sxe, $this::OASIS_XPATH_CREATED));
+
+ $coreProperties['modified'] = str_replace('T', ' ', $this->extractTermsFromMetadata($sxe, $this::OASIS_XPATH_MODIFIED));
+ }
+
+ return $coreProperties;
+ }
+
+ private function extractTermsFromMetadata(\SimpleXMLElement $sxe, $path) {
+
+ $terms = array();
+
+ foreach ($sxe->xpath($path) as $value) {
+ $terms[] = (string)$value;
+ }
+
+ return (implode(' ', $terms));
+
+ }
+
+ /**
+ * Determine absolute zip path
+ *
+ * @param string $path
+ * @return string
+ */
+ protected function absoluteZipPath($path) {
+ $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
+ $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
+ $absolutes = array();
+ foreach ($parts as $part) {
+ if ('.' == $part) continue;
+ if ('..' == $part) {
+ array_pop($absolutes);
+ } else {
+ $absolutes[] = $part;
+ }
+ }
+ return implode('/', $absolutes);
+ }
+}
diff --cc core/doc/admin/_sources/configuration/auth_ldap.txt
index 06af732,0000000..fac83b3
mode 100644,000000..100644
--- a/core/doc/admin/_sources/configuration/auth_ldap.txt
+++ b/core/doc/admin/_sources/configuration/auth_ldap.txt
@@@ -1,616 -1,0 +1,615 @@@
+LDAP Authentication
+===================
+
- ownCloud ships an LDAP backend, which allows full use of ownCloud for user
++ownCloud ships an LDAP backend, which allows full use of ownCloud for users
+logging in with LDAP credentials including:
+
+* LDAP group support
+* File sharing with users and groups
+* Access via WebDAV and of course ownCloud Desktop Client
+* Versioning, external Storages and all other ownCloud goodies
+
+To connect to an LDAP server the configuration needs to be set up properly.
+Once the LDAP backend is activated (Apps Sidebar→Apps, choose **LDAP user and
+group backend**, click on **Enable**) the configuration can be found on
+Settings→Admin. Read on for a detailed description of the configuration fields.
+
+Configuration
+-------------
+
- The LDAP backend follows a wizard-like approach, splitted into four tabs. A
++The LDAP backend configuration follows a wizard-like approach, split into four tabs. A
+correctly completed first tab ("Server") is mandatory to access the other tabs.
- Also, the other tabs need to be reviewed by the admin, however the necessary
- settings are detected automatically. An indicator will show whether the
- configuration is incomplete, incorrect or OK.
++The settings in the other tabs are detected automatically, but should be reviewed by the
++admin. An indicator will show whether the configuration is incomplete, incorrect or OK.
+
- The settings are changed automatically, as soon as a input element looses the
++The settings are changed automatically, as soon as a input element loses the
+focus, i.e. the cursor is taken away by clicking somewhere else or pressing the
- tabulator key.
++Tab key.
+
+The other tabs can be navigated by clicking the tabs or by using the *Continue*
+and *Back* buttons. They are located on the lower right, next to the status
+indicator.
+
+Server
+~~~~~~
+
- The server tab contains the basic information on the LDAP server. They make sure
++The Server tab contains the basic information on the LDAP server. They make sure
+that ownCloud will be able to connect to LDAP and be able to read data from
+there. The admin at least needs to provide a hostname. If anonymous access is
+not possible he will need to provide an account DN and a password, too. ownCloud
+attempts to auto-detect the port and the base DN.
+
+.. figure:: ../images/ldap-wizard-1-server.png
+
+Server configuration:
+ ownCloud can be configured to connect to multiple LDAP servers. Using this
+ control you can pick a configuration you want to edit or add a new one. The
+ button **Delete Configuration** deletes the current configuration.
+
+Host:
+ The host name of the LDAP server. It can also be a **ldaps://** URI, for
+ instance.
+
+ It is also possible to pass a port number, which speeds up port detection. It
+ is especially useful, if a custom port is used. ownCloud will move the value
+ to the port field subsequently.
+
+ Examples:
+
+ * *directory.my-company.com*
+ * *ldaps://directory.my-company.com*
+ * *directory.my-company.com:9876*
+
+Port:
+ The port on which to connect to the LDAP server. The field is disabled in the
+ beginning of a new configuration. The port will be detected automatically,
+ if the LDAP server is running on a standard port. After ownCloud attempted to
+ determine the port, the field will be enabled for user input. A successfully
+ found port will be inserted by ownCloud, of course.
+
+ Example:
+
+ * *389*
+
+User DN:
+ The name as DN of a user who is able to do searches in the LDAP
+ directory. Leave it empty for anonymous access. It is recommended to have a
+ special system user for ownCloud.
+
+ Example:
+
+ * *uid=owncloudsystemuser,cn=sysusers,dc=my-company,dc=com*
+
+Password:
+ The password for the user given above. Empty for anonymous access.
+
+Base DN:
+ The base DN of LDAP, from where all users and groups can be reached. Separated
+ Base DNs for users and groups can be set in the Advanced tab. Nevertheless,
+ this field is mandatory. ownCloud attempts to determine the Base DN according
+ to the provided User DN or the provided Host.
+
+ Example:
+
+ * *dc=my-company,dc=com*
+
+User Filter
+~~~~~~~~~~~
+
- The settings in the user filter tab determine which LDAP users will appear and
++The settings in the User Filter tab determine which LDAP users will appear and
+are allowed to log in into ownCloud. It is also possible to enter a raw LDAP
+filter.
+
+.. figure:: ../images/ldap-wizard-2-user.png
+
+only those object classes:
- ownCloud will determine the object classes that are typically availalble for
++ ownCloud will determine the object classes that are typically available for
+ (ideally only) user objects in your LDAP. ownCloud will automatically select
+ the object class that returns the highest amount of users. You can select
+ multiple object classes.
+
+only from those groups:
+ If your LDAP server supports the member-of-overlay in LDAP filters, you can
+ define that only users from one or more certain groups are allowed to
+ appear and log in into ownCloud. By default, no value will be selected. You
+ can select multiple groups.
+
+ If your LDAP server does not support the member-of-overlay in LDAP filters,
+ the input field is disabled. Please contact your LDAP administrator.
+
+Edit raw filter instead:
+ Clicking on this text will toggle the filter mode. Instead of the assisted
+ approach, you can enter the raw LDAP filter directly in the appearing field.
+
+ Example:
+
+ * *objectClass=inetOrgPerson*
+
+x users found:
+ This is an indicator that tells you approximately how many users will be
+ allowed to access ownCloud. The number will update after any change you do.
+
+Login Filter
+~~~~~~~~~~~~
+
- The settings in the login filter tab determine which user detail will be
++The settings in the Login Filter tab determine which user detail will be
+compared to the login value entered by the user. It is possible to allow
+multiple user details. It is also possible to enter a raw LDAP filter.
+
+The user limitation as set up in the previous tab is in effect, unless you
+manually configure the filter in raw mode.
+
+.. figure:: ../images/ldap-wizard-3-login.png
+
+LDAP Username:
+ If this value is checked, the login value will be compared to the username in
+ the LDAP directory. The corresponding attribute, usually *uid* or
+ *samaccountname* will be detected automatically by ownCloud.
+
+LDAP Email Address:
+ If this value is checked, the login value will be compared to an email address
+ in the LDAP directory. The email address will be looked for in the
+ *mailPrimaryAddress* and *mail* attributes.
+
+Other Attributes:
+ This multiselect box allows you to select other attributes for the comparison.
+ The list is generated automatically based on the attributes that a user object
+ contains in your LDAP server.
+
+Edit raw filter instead:
+ Clicking on this text will toggle the filter mode. Instead of the assisted
+ approach, you can enter the raw LDAP filter directly in the appearing field.
+
+ The **%uid** placeholder will be replaced with the login name entered by the user
+ upon login. When you enter the filter manually.
+
+ Examples:
+
+ * only username: *uid=%uid*
+ * username or email address: *(|(uid=%uid)(mail=$uid))*
+
+Group Filter
+~~~~~~~~~~~~
+
- The settings in the group filter tab determine which groups will be availalble
- in ownCloud. It does not have any restrictions on logins, this has been dealed
++The settings in the Group Filter tab determine which groups will be available
++in ownCloud. It does not have any restrictions on logins, this has been dealt
+with in the prior tabs. It is also possible to enter a raw LDAP
+filter.
+
- By default, no groups will be availalble in ownCloud. You actively need to
++By default, no groups will be available in ownCloud. You actively need to
+enable groups.
+
+.. figure:: ../images/ldap-wizard-4-group.png
+
+only those object classes:
- ownCloud will determine the object classes that are typically availalble for
++ ownCloud will determine the object classes that are typically available for
+ (ideally only) group objects in your LDAP. ownCloud will only list object
+ classes that return at least one group object. You can select multiple
+ object classes. A typical object class is "group", or "posixGroup".
+
+only from those groups:
- This setting lets you pick certain groups that shall be availalble in
++ This setting lets you pick certain groups that shall be available in
+ ownCloud. This field follows a whitelist approach. ownCloud will generate a
+ list of available groups found in your LDAP server. You can select multiple
+ groups.
+
+Edit raw filter instead:
+ Clicking on this text will toggle the filter mode. Instead of the assisted
+ approach, you can enter the raw LDAP filter directly in the appearing field.
+
+ Example:
+
+ * *objectClass=group*
+ * *objectClass=posixGroup*
+
+y groups found:
+ This is an indicator that tells you approximately how many groups will be
+ available in ownCloud. The number will update after any change you do.
+
+
+Advanced Settings
+-----------------
+
- In the LDAP Advanced settings section you can define options, that are less
++In the LDAP Advanced Settings section you can define options, that are less
+common to set. They are not needed for a working connection. It can also have a
+positive effect on the performance to specify distinguished bases for user and
+group searches.
+
+The Advanced Settings are structured into three parts:
+
+* Connection Settings
+* Directory Settings
+* Special Attributes
+
+Connection Settings
+~~~~~~~~~~~~~~~~~~~
+
+.. figure:: ../images/ldap-advanced-1-connection.png
+
+ LDAP Advanced Settings, section Connection Settings
+
+Configuration Active:
+ Enables or Disables the current configuration. Disabled configuration will not
+ connect to the LDAP server.
+
+ By default, it is turned off. It will be automatically turned on, when using
+ the wizard and the configuration is OK and a test connection successful.
+
+Backup (Replica) Host:
+ A backup server can be defined here. ownCloud tries to connect to the backup
+ server automatically, when the main host (as specified in basic settings)
- cannot be reached. It is import that the backup server is a replica of the
++ cannot be reached. It is important that the backup server is a replica of the
+ main server, because the object UUIDs must match.
+
+ Example:
+
+ * *directory2.my-company.com*
+
+Backup (Replica) Port:
+ The port on which to connect to the backup LDAP server. If no port is given,
+ but a host, then the main port (as specified above) will be used.
+
+ Example:
+
+ * *389*
+
+Disable Main Server:
+ You can manually override the main server and make ownCloud only connect to
- the backup server. It may be handy for planned downtimes.
++ the backup server. This may be handy for planned downtimes.
+
+Case insensitive LDAP server (Windows):
- Whether the LDAP server is running on a Windows Host. Usually, it is not
++ Whether the LDAP server is running on a Windows host. Usually, it is not
+ necessary to check it, however.
+
+Turn off SSL certificate validation:
- Turns of check of valid SSL certificates. Use it – if needed –
++ Turns off check of valid SSL certificates. Use it – if needed –
+ for testing, only!
+
+Cache Time-To-Live:
+ A cache is introduced to avoid unnecessary LDAP traffic,
- for example lookups check whether the users exists on every page request or
++ for example lookups check whether the users exist on every page request or
+ WebDAV interaction. It is also supposed to speed up the Admin → User page or
+ list of users to share with, once it is populated. Saving the configuration
+ empties the cache (changes are not necessary). The time is given in seconds.
+
+ Note that almost every PHP request would require to build up a new connection
- to the LDAP server. If you require a most up-to-dateness it is recommended not
++ to the LDAP server. If you require most up-to-dateness it is recommended not
+ to totally switch off the cache, but define a minimum life time of 15s.
+
+ Examples:
+
+ * ten minutes: *600*
+ * one hour: *3600*
+
+Directory Settings
+~~~~~~~~~~~~~~~~~~~
+
+.. figure:: ../images/ldap-advanced-2-directory.png
+
+ LDAP Advanced Settings, section Directory Settings
+
+User Display Name Field:
+ The attribute that should be used as display name in ownCloud.
+
+ * Example: *displayName*
+
+Base User Tree:
+ The base DN of LDAP, from where all users can be reached. It needs to be given
+ completely despite to the Base DN from the Basic settings. You can specify
+ multiple base trees, one in each line.
+
+ * Example:
+
+ | *cn=programmers,dc=my-company,dc=com*
+ | *cn=designers,dc=my-company,dc=com*
+
+User Search Attributes:
+ These attributes are used when a search for users is done. This happens, for
+ instance, in the share dialogue. By default the user display name attribute as
+ specified above is being used. Multiple attributes can be given, one in each
+ line.
+
+ Beware that if an attribute is not available on a user object, the user will
+ neither be listed (e.g. in the share dialogue) nor be able to login. This also
+ affects the display name attribute as specified above. If you override the
+ default, the display name attribute will not be taken into account, unless you
+ specify it as well.
+
+ * Example:
+
+ | *displayName*
+ | *mail*
+
+Group Display Name Field:
+ The attribute that should be used as ownCloud group name. ownCloud allows a
+ limited set of characters (a-zA-Z0-9.-_@), every other character will be
+ replaced in ownCloud. Once a group name is assigned, it will not be changed,
+ i.e. changing this value will only have effect to new LDAP groups.
+
+ * Example: *cn*
+
+Base Group Tree:
+ The base DN of LDAP, from where all groups can be reached.
+ It needs to be given completely despite to the Base DN from the Basic
+ settings. You can specify multiple base trees, one in each line.
+
+ * Example:
+
+ | *cn=barcelona,dc=my-company,dc=com*
+ | *cn=madrid,dc=my-company,dc=com*
+
+Group Search Attributes:
+ These attributes are used when a search for groups is done. This happens, for
+ instance, in the share dialogue. By default the group display name attribute
+ as specified above is being used. Multiple attributes can be given, one in
+ each line.
+
+ If you override the default, the group display name attribute will not be
+ taken into account, unless you specify it as well.
+
+ * Example:
+
+ | *cn*
+ | *description*
+
+Group Member association:
+ The attribute that is used to indicate group memberships, i.e. the attribute
+ used by LDAP groups to refer to their users.
+
- ownCloud detects the value automatically, you should only change it, if you
++ ownCloud detects the value automatically. You should only change it if you
+ have a very valid reason and know what you are doing.
+
+ * Example: *uniquemember*
+
+Special Attributes
+~~~~~~~~~~~~~~~~~~
+
+.. figure:: ../images/ldap-advanced-3-attributes.png
+
+ LDAP Advanced Settings, section Special Attributes
+
+Quota Field:
+ ownCloud can read an LDAP attribute and set the user quota according to its
+ value. Specify the attribute here, otherwise keep it empty. The attribute
+ shall return human readable values, e.g. "2 GB".
+
+ * Example: *ownCloudQuota*
+
+Quota Default:
+ Override ownCloud default quota for LDAP users who do not
+ have a quota set in the attribute given above.
+
+ * Example: *15 GB*
+
+Email Field:
+ ownCloud can read an LDAP attribute and set the user email
+ there from. Specify the attribute here, otherwise keep it empty.
+
+ Although the wizard offers you to check login by email, the correct email
+ attribute is not detected and you need to specify it manually.
+
+ * Example: *mail*
+
+User Home Folder Naming Rule:
+ By default, the ownCloud creates the user
+ directory, where all files and meta data are kept, according to the ownCloud
+ user name. You may want to override this setting and name it after an
+ attribute value. The attribute given can also return an absolute path, e.g.
+ ``/mnt/storage43/alice``. Leave it empty for default behavior.
+
+ * Example: *cn*
+
+Expert Settings
+---------------
+
+.. figure:: ../images/ldap-expert.png
+
+In the Expert Settings fundamental behavior can be adjusted to your needs. The
+configuration should be done before starting production use or when testing the
+installation.
+
+Internal Username:
+ The internal username is the identifier in ownCloud for LDAP users. By default
+ it will be created from the UUID attribute. By using the UUID attribute it is
+ made sure that the username is unique and characters do not need to be
+ converted. The internal username has the restriction that only these
+ characters are allowed: [\a-\zA-\Z0-\9_. at -]. Other characters are replaced with
+ their ASCII correspondence or are simply omitted.
+
+ The LDAP backend ensures that there are no duplicate internal usernames in
+ ownCloud, i.e. that it is checking all other activated user backends
+ (including local ownCloud users). On collisions a random number (between 1000
+ and 9999) will be attached to the retrieved value. For example, if "alice"
+ exists, the next username may be "alice_1337".
+
+ The internal username is also the default name for the user home folder in
+ ownCloud. It is also a part of remote URLs, for instance for all \*DAV services.
+ With this setting the default behaviour can be overridden.
+
+ Leave it empty for default behaviour. Changes will have effect only on newly
+ mapped (added) LDAP users.
+
+ * Example: *uid*
+
+Override UUID detection
+ By default, ownCloud auto-detects the UUID attribute. The UUID attribute is
+ used to doubtlessly identify LDAP users and groups. Also, the internal
+ username will be created based on the UUID, if not specified otherwise above.
+
+ You can override the setting and pass an attribute of your choice. You must
+ make sure that the attribute of your choice can be fetched for both users and
+ groups and it is unique. Leave it empty for default behaviour. Changes will
+ have effect only on newly mapped (added) LDAP users and groups. It also will
- have effect when a user's or group's DN changes and an old UUID was cached: It
++ have effect when a user's or group's DN changes and an old UUID was cached: it
+ will result in a new user. Because of this, the setting should be applied
+ before putting ownCloud in production use and cleaning the bindings
+ (see below).
+
+ * Example: *cn*
+
+Username-LDAP User Mapping
+ ownCloud uses the usernames as key to store and assign data. In order to
+ precisely identify and recognize users, each LDAP user will have a internal
+ username in ownCloud. This requires a mapping from ownCloud username to LDAP
+ user. The created username is mapped to the UUID of the LDAP user.
+ Additionally the DN is cached as well to reduce LDAP interaction, but it is
+ not used for identification. If the DN changes, the change will be detected by
+ ownCloud by checking the UUID value.
+
+ The same is valid for groups.
+
+ The internal ownCloud name is used all over in ownCloud. Clearing the Mappings
- will have leftovers everywhere. Do never clear the mappings
++ will have leftovers everywhere. Never clear the mappings
+ in a production environment. Only clear mappings in a testing or experimental
+ stage.
+
+ **Clearing the Mappings is not configuration sensitive, it affects all LDAP
+ configurations!**
+
+Testing the configuration
+-------------------------
+
- In this version we introduced the **Test Configuration** button on the bottom
- of the LDAP settings section. It will always check the values as currently
++The **Test Configuration** button on the bottom
++of the LDAP settings section will always check the values as currently
+given in the input fields. You do not need to save before testing. By clicking
+on the button, ownCloud will try to bind to the ownCloud server with the
+settings currently given in the input fields. The response will look like this:
+
+.. figure:: ../images/ldap-settings-invalid-oc45.png
+
+ Failure
+
+In case the configuration fails, you can see details in ownCloud's log, which
+is in the data directory and called **owncloud.log** or on the bottom the
+**Settings → Admin page**. Unfortunately it requires a reload – sorry for the
+inconvenience.
+
+.. figure:: ../images/ldap-settings-valid-oc45.png
+
+ Success
+
+In this case, Save the settings. You can check if the users and groups are
+fetched correctly on the Settings → Users page.
+
+ownCloud Avatar integration
+---------------------------
+
+ownCloud 6 incorporates a user profile picture feature, called Avatar. If a user
+has a photo stored in the *jpegPhoto* or, since 6.0.2, *thumbnailPhoto*
+attribute, it will be used as Avatar. The user then is not able to change his
+avatar in the personal settings. It must be done within LDAP. *jpegPhoto* is
+preferred over *thumbnailPhoto*.
+
+.. figure:: ../images/ldap-fetched-avatar.png
+
+ Profile picture fetched from LDAP, Personal Settings
+
+If the *jpegPhoto* or *thumbnailPhoto* attribute is not set or empty, the
+default ownCloud behaviour is active, i.e. the user will be able to set and
+change his profile picture in the personal settings. If the user sets a profile
+picture within ownCloud it will _not_ be stored in LDAP.
+
+The *jpegPhoto* or *thumbnailPhoto* attribute will be fetched once a day to make
+sure the current photo from LDAP is used in ownCloud. If a picture is added
+later, a possibly set profile picture will be overridden with the LDAP one. If a
+photo stored in the *jpegPhoto* and/or *thumbnailPhoto* attribute is deleted
+later, the last profile picture in ownCloud will still be used.
+
+The photo taken from LDAP will be adjusted to the requirements of the ownCloud
- avatar automatically. I.e. it will be transformed into a square. If the photo
++avatar automatically, i.e. it will be transformed into a square. If the photo
+needs to be cut, it will be done equally from both affected sides. The original
+photo stored in LDAP will stay the same, of course.
+
+Troubleshooting, Tips and Tricks
+--------------------------------
+
+SSL Certificate Verification (LDAPS, TLS)
+-----------------------------------------
+
+A common mistake with SSL certificates is that they may not be known to PHP.
+If you have trouble with certificate validation make sure that
+
+* you have the certificate of the server installed on the ownCloud server
+* the certificate is announced in the system's LDAP configuration file (usually
+ */etc/ldap/ldap.conf* on Linux, *C:\\openldap\\sysconf\\ldap.conf* or
+ *C:\\ldap.conf* on Windows) using a **TLS_CACERT /path/to/cert** line.
+* Using LDAPS, also make sure that the port is correctly configured (by default
+ 686)
+
+Microsoft Active Directory
+--------------------------
+
+Compared to earlier ownCloud versions, no further tweaks need to be done to
+make ownCloud work with Active Directory. ownCloud will automatically find the
+correct configuration in the wizard-like set up process.
+
+Duplicating Server Configurations
+---------------------------------
+
+In case you have a working configuration and want to create a similar one or
+"snapshot" configurations before modifying them you can do the following:
+
+#. Go to the **Server** tab
+#. On **Server Configuration** choose *Add Server Configuration*
+#. Answer the question *Take over settings from recent server configuration?*
+ with *yes*.
+#. (optional) Switch to **Advanced** tab and uncheck **Configuration Active**
+ in the *Connection Settings*, so the new configuration is not used on Save
+#. Click on **Save**
+
+Now you can modify the configuration and enable it if you wish.
+
+ownCloud LDAP Internals
+-----------------------
+
+Some parts of how the LDAP backend works are described here. May it be helpful.
+
+Groups
+------
+
+At the moment, only secondary groups are read. That means that only the groups
+are retrieved, which are returned by the attribute auto-detected (or manually
- chosen) in Group-Member association. Primary groups are not being taken into
++chosen) in Group-Member association. Primary groups are not taken into
+account.
+
+User and Group Mapping
+----------------------
+
+In ownCloud the user or group name is used to have all relevant information in
+the database assigned. To work reliably a permanent internal user name and
+group name is created and mapped to the LDAP DN and UUID. If the DN changes in
+LDAP it will be detected, there will be no conflicts.
+
+Those mappings are done in the database table ldap_user_mapping and
+ldap_group_mapping. The user name is also used for the user's folder (except
+something else is specified in *User Home Folder Naming Rule*), which
+contains files and meta data.
+
+As of ownCloud 5 internal user name and a visible display name are separated.
+This is not the case for group names, yet, i.e. group cannot be altered.
+
+That means that your LDAP configuration should be good and ready before putting
+it into production. The mapping tables are filled early, but as long as you are
+testing, you can empty the tables any time. Do not do this in production. If you
+want to rename a group, be very careful. Do not rename the user's internal name.
+
+Caching
+-------
+
- For performance reasons a cache has been introduced to ownCloud. He we store
++For performance reasons a cache has been introduced to ownCloud. The cache stores
+all users and groups, group memberships or internal userExists-requests. Since
+ownCloud is written in PHP and each and every page request (also done by Ajax)
+loads ownCloud and would execute one or more LDAP queries again, you do want to
+have some of those queries cached and save those requests and traffic. It is
+highly recommended to have the cache filled for a small amount of time, which
+comes also very handy when using the sync client, as it is yet another request
+for PHP.
+
+Handling with Backup Server
+---------------------------
+
- When ownCloud is not able to contact the main server, he will be treated as
++When ownCloud is not able to contact the main server, it will be treated as
+offline and no connection attempts will be done for the time specified in
+**Cache Time-To-Live**. If a backup server is configured, it will be connected
- instead. If you plan a maintained downtime, check **Disable Main Server** for
++instead. For planned downtime, check **Disable Main Server** for
+the time being to avoid unnecessary connection attempts every now and then.
diff --cc core/doc/admin/configuration/auth_ldap.html
index abd280e,0000000..6bac9db
mode 100644,000000..100644
--- a/core/doc/admin/configuration/auth_ldap.html
+++ b/core/doc/admin/configuration/auth_ldap.html
@@@ -1,785 -1,0 +1,784 @@@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>LDAP Authentication — ownCloud Administrators Manual 6.0 documentation</title>
+
+ <link rel="stylesheet" href="../_static/style.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/style.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/bootstrap-sphinx.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '6.0',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <script type="text/javascript" src="../_static/bootstrap.js"></script>
+ <link rel="top" title="ownCloud Administrators Manual 6.0 documentation" href="../index.html" />
+ <link rel="up" title="Configuration" href="index.html" />
+ <link rel="next" title="Background Jobs" href="background_jobs.html" />
+ <link rel="prev" title="User Management" href="configuration_users.html" />
+<script type="text/javascript">
+(function () {
+ /**
+ * Patch TOC list.
+ *
+ * Will mutate the underlying span to have a correct ul for nav.
+ *
+ * @param $span: Span containing nested UL's to mutate.
+ * @param minLevel: Starting level for nested lists. (1: global, 2: local).
+ */
+ var patchToc = function ($ul, minLevel) {
+ var findA;
+
+ // Find all a "internal" tags, traversing recursively.
+ findA = function ($elem, level) {
+ var level = level || 0,
+ $items = $elem.find("> li > a.internal, > ul, > li > ul");
+
+ // Iterate everything in order.
+ $items.each(function (index, item) {
+ var $item = $(item),
+ tag = item.tagName.toLowerCase(),
+ pad = 15 + ((level - minLevel) * 10);
+
+ if (tag === 'a' && level >= minLevel) {
+ // Add to existing padding.
+ $item.css('padding-left', pad + "px");
+ console.log(level, $item, 'padding-left', pad + "px");
+ } else if (tag === 'ul') {
+ // Recurse.
+ findA($item, level + 1);
+ }
+ });
+ };
+
+ console.log("HERE");
+ findA($ul);
+ };
+
+ $(document).ready(function () {
+ // Add styling, structure to TOC's.
+ $(".dropdown-menu").each(function () {
+ $(this).find("ul").each(function (index, item){
+ var $item = $(item);
+ $item.addClass('unstyled');
+ });
+ $(this).find("li").each(function () {
+ $(this).parent().append(this);
+ });
+ });
+
+ // Patch in level.
+ patchToc($("ul.globaltoc"), 2);
+ patchToc($("ul.localtoc"), 2);
+
+ // Enable dropdown.
+ $('.dropdown-toggle').dropdown();
+ });
+}());
+</script>
+
+ </head>
+ <body>
+
+
+<div class="container">
+ <div class="content">
+ <div class="page-header">
+ <h1><a href="../contents.html">ownCloud Administrators Manual</a></h1>
+
+ </div>
+
+ <div class="row">
+ <div class="span3">
+ <div class="sidebar">
+ <div class="well">
+ <div class="menu-support-container">
+ <ul id="menu-support" class="menu">
+ <ul>
+ <li><a href="../contents.html">Overview</a></li>
+ </ul>
+ <ul>
+<li class="toctree-l1"><a class="reference internal" href="../index.html">ownCloud 6.0 Admin Documentation</a></li>
+</ul>
+<ul class="current">
+<li class="toctree-l1"><a class="reference internal" href="../installation/index.html">Installation</a></li>
+<li class="toctree-l1 current"><a class="reference internal" href="index.html">Configuration</a><ul class="current">
+<li class="toctree-l2"><a class="reference internal" href="configuration_apps.html">Managing Apps</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_users.html">User Management</a></li>
+<li class="toctree-l2 current"><a class="current reference internal" href="">LDAP Authentication</a><ul>
+<li class="toctree-l3"><a class="reference internal" href="#configuration">Configuration</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#advanced-settings">Advanced Settings</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#expert-settings">Expert Settings</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#testing-the-configuration">Testing the configuration</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#owncloud-avatar-integration">ownCloud Avatar integration</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#troubleshooting-tips-and-tricks">Troubleshooting, Tips and Tricks</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#ssl-certificate-verification-ldaps-tls">SSL Certificate Verification (LDAPS, TLS)</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#microsoft-active-directory">Microsoft Active Directory</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#duplicating-server-configurations">Duplicating Server Configurations</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#owncloud-ldap-internals">ownCloud LDAP Internals</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#groups">Groups</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#user-and-group-mapping">User and Group Mapping</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#caching">Caching</a></li>
+<li class="toctree-l3"><a class="reference internal" href="#handling-with-backup-server">Handling with Backup Server</a></li>
+</ul>
+</li>
+<li class="toctree-l2"><a class="reference internal" href="background_jobs.html">Background Jobs</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_3rdparty.html">Find Third-Party Libraries</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_automation.html">Automatic Configuration</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_custom_clients.html">Customizing Client Download Links</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_database.html">MySQL/Postgres/SQLite Support</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_encryption.html">Using Server-Side Encryption</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_knowledgebase.html">Disable Knowledge Base</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_language.html">Setting the Default Language</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_logging.html">Configure Logging</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_mail.html">Sending Mail Notifications</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_maintenance.html">Enable Maintenance Mode</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_preview.html">Enabling File Previews</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuration_reverseproxy.html">Reverse Proxy Configuration</a></li>
+<li class="toctree-l2"><a class="reference internal" href="configuring_big_file_upload.html">Dealing with Big File Uploads</a></li>
+<li class="toctree-l2"><a class="reference internal" href="custom_mount_config_gui.html">Custom Mount Configuration Web-GUI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="custom_mount_config.html">Custom Mount Configuration</a></li>
+<li class="toctree-l2"><a class="reference internal" href="custom_user_backend.html">Custom User Backend Configuration</a></li>
+<li class="toctree-l2"><a class="reference internal" href="xsendfile.html">Serving static files via web server</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="../apps/index.html">Apps</a></li>
+<li class="toctree-l1"><a class="reference internal" href="../maintenance/index.html">Maintenance</a></li>
+<li class="toctree-l1"><a class="reference internal" href="../config/index.html">The Configuration File</a></li>
+<li class="toctree-l1"><a class="reference internal" href="../issues/index.html">Issues</a></li>
+</ul>
+
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+
+
+ <div class="span9">
+ <div class="page-content">
+
+ <div class="section" id="ldap-authentication">
+<h1>LDAP Authentication<a class="headerlink" href="#ldap-authentication" title="Permalink to this headline">¶</a></h1>
- <p>ownCloud ships an LDAP backend, which allows full use of ownCloud for user
++<p>ownCloud ships an LDAP backend, which allows full use of ownCloud for users
+logging in with LDAP credentials including:</p>
+<ul class="simple">
+<li>LDAP group support</li>
+<li>File sharing with users and groups</li>
+<li>Access via WebDAV and of course ownCloud Desktop Client</li>
+<li>Versioning, external Storages and all other ownCloud goodies</li>
+</ul>
+<p>To connect to an LDAP server the configuration needs to be set up properly.
+Once the LDAP backend is activated (Apps Sidebar→Apps, choose <strong>LDAP user and
+group backend</strong>, click on <strong>Enable</strong>) the configuration can be found on
+Settings→Admin. Read on for a detailed description of the configuration fields.</p>
+<div class="section" id="configuration">
+<h2>Configuration<a class="headerlink" href="#configuration" title="Permalink to this headline">¶</a></h2>
- <p>The LDAP backend follows a wizard-like approach, splitted into four tabs. A
++<p>The LDAP backend configuration follows a wizard-like approach, split into four tabs. A
+correctly completed first tab (“Server”) is mandatory to access the other tabs.
- Also, the other tabs need to be reviewed by the admin, however the necessary
- settings are detected automatically. An indicator will show whether the
- configuration is incomplete, incorrect or OK.</p>
- <p>The settings are changed automatically, as soon as a input element looses the
++The settings in the other tabs are detected automatically, but should be reviewed by the
++admin. An indicator will show whether the configuration is incomplete, incorrect or OK.</p>
++<p>The settings are changed automatically, as soon as a input element loses the
+focus, i.e. the cursor is taken away by clicking somewhere else or pressing the
- tabulator key.</p>
++Tab key.</p>
+<p>The other tabs can be navigated by clicking the tabs or by using the <em>Continue</em>
+and <em>Back</em> buttons. They are located on the lower right, next to the status
+indicator.</p>
+<div class="section" id="server">
+<h3>Server<a class="headerlink" href="#server" title="Permalink to this headline">¶</a></h3>
- <p>The server tab contains the basic information on the LDAP server. They make sure
++<p>The Server tab contains the basic information on the LDAP server. They make sure
+that ownCloud will be able to connect to LDAP and be able to read data from
+there. The admin at least needs to provide a hostname. If anonymous access is
+not possible he will need to provide an account DN and a password, too. ownCloud
+attempts to auto-detect the port and the base DN.</p>
+<div class="figure">
+<img alt="../_images/ldap-wizard-1-server.png" src="../_images/ldap-wizard-1-server.png" />
+</div>
+<dl class="docutils">
+<dt>Server configuration:</dt>
+<dd>ownCloud can be configured to connect to multiple LDAP servers. Using this
+control you can pick a configuration you want to edit or add a new one. The
+button <strong>Delete Configuration</strong> deletes the current configuration.</dd>
+<dt>Host:</dt>
+<dd><p class="first">The host name of the LDAP server. It can also be a <strong>ldaps://</strong> URI, for
+instance.</p>
+<p>It is also possible to pass a port number, which speeds up port detection. It
+is especially useful, if a custom port is used. ownCloud will move the value
+to the port field subsequently.</p>
+<p>Examples:</p>
+<ul class="last simple">
+<li><em>directory.my-company.com</em></li>
+<li><em>ldaps://directory.my-company.com</em></li>
+<li><em>directory.my-company.com:9876</em></li>
+</ul>
+</dd>
+<dt>Port:</dt>
+<dd><p class="first">The port on which to connect to the LDAP server. The field is disabled in the
+beginning of a new configuration. The port will be detected automatically,
+if the LDAP server is running on a standard port. After ownCloud attempted to
+determine the port, the field will be enabled for user input. A successfully
+found port will be inserted by ownCloud, of course.</p>
+<p>Example:</p>
+<ul class="last simple">
+<li><em>389</em></li>
+</ul>
+</dd>
+<dt>User DN:</dt>
+<dd><p class="first">The name as DN of a user who is able to do searches in the LDAP
+directory. Leave it empty for anonymous access. It is recommended to have a
+special system user for ownCloud.</p>
+<p>Example:</p>
+<ul class="last simple">
+<li><em>uid=owncloudsystemuser,cn=sysusers,dc=my-company,dc=com</em></li>
+</ul>
+</dd>
+<dt>Password:</dt>
+<dd>The password for the user given above. Empty for anonymous access.</dd>
+<dt>Base DN:</dt>
+<dd><p class="first">The base DN of LDAP, from where all users and groups can be reached. Separated
+Base DNs for users and groups can be set in the Advanced tab. Nevertheless,
+this field is mandatory. ownCloud attempts to determine the Base DN according
+to the provided User DN or the provided Host.</p>
+<p>Example:</p>
+<ul class="last simple">
+<li><em>dc=my-company,dc=com</em></li>
+</ul>
+</dd>
+</dl>
+</div>
+<div class="section" id="user-filter">
+<h3>User Filter<a class="headerlink" href="#user-filter" title="Permalink to this headline">¶</a></h3>
- <p>The settings in the user filter tab determine which LDAP users will appear and
++<p>The settings in the User Filter tab determine which LDAP users will appear and
+are allowed to log in into ownCloud. It is also possible to enter a raw LDAP
+filter.</p>
+<div class="figure">
+<img alt="../_images/ldap-wizard-2-user.png" src="../_images/ldap-wizard-2-user.png" />
+</div>
+<dl class="docutils">
+<dt>only those object classes:</dt>
- <dd>ownCloud will determine the object classes that are typically availalble for
++<dd>ownCloud will determine the object classes that are typically available for
+(ideally only) user objects in your LDAP. ownCloud will automatically select
+the object class that returns the highest amount of users. You can select
+multiple object classes.</dd>
+<dt>only from those groups:</dt>
+<dd><p class="first">If your LDAP server supports the member-of-overlay in LDAP filters, you can
+define that only users from one or more certain groups are allowed to
+appear and log in into ownCloud. By default, no value will be selected. You
+can select multiple groups.</p>
+<p class="last">If your LDAP server does not support the member-of-overlay in LDAP filters,
+the input field is disabled. Please contact your LDAP administrator.</p>
+</dd>
+<dt>Edit raw filter instead:</dt>
+<dd><p class="first">Clicking on this text will toggle the filter mode. Instead of the assisted
+approach, you can enter the raw LDAP filter directly in the appearing field.</p>
+<p>Example:</p>
+<ul class="last simple">
+<li><em>objectClass=inetOrgPerson</em></li>
+</ul>
+</dd>
+<dt>x users found:</dt>
+<dd>This is an indicator that tells you approximately how many users will be
+allowed to access ownCloud. The number will update after any change you do.</dd>
+</dl>
+</div>
+<div class="section" id="login-filter">
+<h3>Login Filter<a class="headerlink" href="#login-filter" title="Permalink to this headline">¶</a></h3>
- <p>The settings in the login filter tab determine which user detail will be
++<p>The settings in the Login Filter tab determine which user detail will be
+compared to the login value entered by the user. It is possible to allow
+multiple user details. It is also possible to enter a raw LDAP filter.</p>
+<p>The user limitation as set up in the previous tab is in effect, unless you
+manually configure the filter in raw mode.</p>
+<div class="figure">
+<img alt="../_images/ldap-wizard-3-login.png" src="../_images/ldap-wizard-3-login.png" />
+</div>
+<dl class="docutils">
+<dt>LDAP Username:</dt>
+<dd>If this value is checked, the login value will be compared to the username in
+the LDAP directory. The corresponding attribute, usually <em>uid</em> or
+<em>samaccountname</em> will be detected automatically by ownCloud.</dd>
+<dt>LDAP Email Address:</dt>
+<dd>If this value is checked, the login value will be compared to an email address
+in the LDAP directory. The email address will be looked for in the
+<em>mailPrimaryAddress</em> and <em>mail</em> attributes.</dd>
+<dt>Other Attributes:</dt>
+<dd>This multiselect box allows you to select other attributes for the comparison.
+The list is generated automatically based on the attributes that a user object
+contains in your LDAP server.</dd>
+<dt>Edit raw filter instead:</dt>
+<dd><p class="first">Clicking on this text will toggle the filter mode. Instead of the assisted
+approach, you can enter the raw LDAP filter directly in the appearing field.</p>
+<p>The <strong>%uid</strong> placeholder will be replaced with the login name entered by the user
+upon login. When you enter the filter manually.</p>
+<p>Examples:</p>
+<ul class="last simple">
+<li>only username: <em>uid=%uid</em></li>
+<li>username or email address: <em>(|(uid=%uid)(mail=$uid))</em></li>
+</ul>
+</dd>
+</dl>
+</div>
+<div class="section" id="group-filter">
+<h3>Group Filter<a class="headerlink" href="#group-filter" title="Permalink to this headline">¶</a></h3>
- <p>The settings in the group filter tab determine which groups will be availalble
- in ownCloud. It does not have any restrictions on logins, this has been dealed
++<p>The settings in the Group Filter tab determine which groups will be available
++in ownCloud. It does not have any restrictions on logins, this has been dealt
+with in the prior tabs. It is also possible to enter a raw LDAP
+filter.</p>
- <p>By default, no groups will be availalble in ownCloud. You actively need to
++<p>By default, no groups will be available in ownCloud. You actively need to
+enable groups.</p>
+<div class="figure">
+<img alt="../_images/ldap-wizard-4-group.png" src="../_images/ldap-wizard-4-group.png" />
+</div>
+<dl class="docutils">
+<dt>only those object classes:</dt>
- <dd>ownCloud will determine the object classes that are typically availalble for
++<dd>ownCloud will determine the object classes that are typically available for
+(ideally only) group objects in your LDAP. ownCloud will only list object
+classes that return at least one group object. You can select multiple
+object classes. A typical object class is “group”, or “posixGroup”.</dd>
+<dt>only from those groups:</dt>
- <dd>This setting lets you pick certain groups that shall be availalble in
++<dd>This setting lets you pick certain groups that shall be available in
+ownCloud. This field follows a whitelist approach. ownCloud will generate a
+list of available groups found in your LDAP server. You can select multiple
+groups.</dd>
+<dt>Edit raw filter instead:</dt>
+<dd><p class="first">Clicking on this text will toggle the filter mode. Instead of the assisted
+approach, you can enter the raw LDAP filter directly in the appearing field.</p>
+<p>Example:</p>
+<ul class="last simple">
+<li><em>objectClass=group</em></li>
+<li><em>objectClass=posixGroup</em></li>
+</ul>
+</dd>
+<dt>y groups found:</dt>
+<dd>This is an indicator that tells you approximately how many groups will be
+available in ownCloud. The number will update after any change you do.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="advanced-settings">
+<h2>Advanced Settings<a class="headerlink" href="#advanced-settings" title="Permalink to this headline">¶</a></h2>
- <p>In the LDAP Advanced settings section you can define options, that are less
++<p>In the LDAP Advanced Settings section you can define options, that are less
+common to set. They are not needed for a working connection. It can also have a
+positive effect on the performance to specify distinguished bases for user and
+group searches.</p>
+<p>The Advanced Settings are structured into three parts:</p>
+<ul class="simple">
+<li>Connection Settings</li>
+<li>Directory Settings</li>
+<li>Special Attributes</li>
+</ul>
+<div class="section" id="connection-settings">
+<h3>Connection Settings<a class="headerlink" href="#connection-settings" title="Permalink to this headline">¶</a></h3>
+<div class="figure">
+<img alt="../_images/ldap-advanced-1-connection.png" src="../_images/ldap-advanced-1-connection.png" />
+<p class="caption">LDAP Advanced Settings, section Connection Settings</p>
+</div>
+<dl class="docutils">
+<dt>Configuration Active:</dt>
+<dd><p class="first">Enables or Disables the current configuration. Disabled configuration will not
+connect to the LDAP server.</p>
+<p class="last">By default, it is turned off. It will be automatically turned on, when using
+the wizard and the configuration is OK and a test connection successful.</p>
+</dd>
+<dt>Backup (Replica) Host:</dt>
+<dd><p class="first">A backup server can be defined here. ownCloud tries to connect to the backup
+server automatically, when the main host (as specified in basic settings)
- cannot be reached. It is import that the backup server is a replica of the
++cannot be reached. It is important that the backup server is a replica of the
+main server, because the object UUIDs must match.</p>
+<p>Example:</p>
+<ul class="last simple">
+<li><em>directory2.my-company.com</em></li>
+</ul>
+</dd>
+<dt>Backup (Replica) Port:</dt>
+<dd><p class="first">The port on which to connect to the backup LDAP server. If no port is given,
+but a host, then the main port (as specified above) will be used.</p>
+<p>Example:</p>
+<ul class="last simple">
+<li><em>389</em></li>
+</ul>
+</dd>
+<dt>Disable Main Server:</dt>
+<dd>You can manually override the main server and make ownCloud only connect to
- the backup server. It may be handy for planned downtimes.</dd>
++the backup server. This may be handy for planned downtimes.</dd>
+<dt>Case insensitive LDAP server (Windows):</dt>
- <dd>Whether the LDAP server is running on a Windows Host. Usually, it is not
++<dd>Whether the LDAP server is running on a Windows host. Usually, it is not
+necessary to check it, however.</dd>
+<dt>Turn off SSL certificate validation:</dt>
- <dd>Turns of check of valid SSL certificates. Use it – if needed –
++<dd>Turns off check of valid SSL certificates. Use it – if needed –
+for testing, only!</dd>
+<dt>Cache Time-To-Live:</dt>
+<dd><p class="first">A cache is introduced to avoid unnecessary LDAP traffic,
- for example lookups check whether the users exists on every page request or
++for example lookups check whether the users exist on every page request or
+WebDAV interaction. It is also supposed to speed up the Admin → User page or
+list of users to share with, once it is populated. Saving the configuration
+empties the cache (changes are not necessary). The time is given in seconds.</p>
+<p>Note that almost every PHP request would require to build up a new connection
- to the LDAP server. If you require a most up-to-dateness it is recommended not
++to the LDAP server. If you require most up-to-dateness it is recommended not
+to totally switch off the cache, but define a minimum life time of 15s.</p>
+<p>Examples:</p>
+<ul class="last simple">
+<li>ten minutes: <em>600</em></li>
+<li>one hour: <em>3600</em></li>
+</ul>
+</dd>
+</dl>
+</div>
+<div class="section" id="directory-settings">
+<h3>Directory Settings<a class="headerlink" href="#directory-settings" title="Permalink to this headline">¶</a></h3>
+<div class="figure">
+<img alt="../_images/ldap-advanced-2-directory.png" src="../_images/ldap-advanced-2-directory.png" />
+<p class="caption">LDAP Advanced Settings, section Directory Settings</p>
+</div>
+<dl class="docutils">
+<dt>User Display Name Field:</dt>
+<dd><p class="first">The attribute that should be used as display name in ownCloud.</p>
+<ul class="last simple">
+<li>Example: <em>displayName</em></li>
+</ul>
+</dd>
+<dt>Base User Tree:</dt>
+<dd><p class="first">The base DN of LDAP, from where all users can be reached. It needs to be given
+completely despite to the Base DN from the Basic settings. You can specify
+multiple base trees, one in each line.</p>
+<ul class="last">
+<li><p class="first">Example:</p>
+<div class="line-block">
+<div class="line"><em>cn=programmers,dc=my-company,dc=com</em></div>
+<div class="line"><em>cn=designers,dc=my-company,dc=com</em></div>
+</div>
+</li>
+</ul>
+</dd>
+<dt>User Search Attributes:</dt>
+<dd><p class="first">These attributes are used when a search for users is done. This happens, for
+instance, in the share dialogue. By default the user display name attribute as
+specified above is being used. Multiple attributes can be given, one in each
+line.</p>
+<p>Beware that if an attribute is not available on a user object, the user will
+neither be listed (e.g. in the share dialogue) nor be able to login. This also
+affects the display name attribute as specified above. If you override the
+default, the display name attribute will not be taken into account, unless you
+specify it as well.</p>
+<ul class="last">
+<li><p class="first">Example:</p>
+<div class="line-block">
+<div class="line"><em>displayName</em></div>
+<div class="line"><em>mail</em></div>
+</div>
+</li>
+</ul>
+</dd>
+<dt>Group Display Name Field:</dt>
+<dd><p class="first">The attribute that should be used as ownCloud group name. ownCloud allows a
+limited set of characters (a-zA-Z0-9.-_@), every other character will be
+replaced in ownCloud. Once a group name is assigned, it will not be changed,
+i.e. changing this value will only have effect to new LDAP groups.</p>
+<ul class="last simple">
+<li>Example: <em>cn</em></li>
+</ul>
+</dd>
+<dt>Base Group Tree:</dt>
+<dd><p class="first">The base DN of LDAP, from where all groups can be reached.
+It needs to be given completely despite to the Base DN from the Basic
+settings. You can specify multiple base trees, one in each line.</p>
+<ul class="last">
+<li><p class="first">Example:</p>
+<div class="line-block">
+<div class="line"><em>cn=barcelona,dc=my-company,dc=com</em></div>
+<div class="line"><em>cn=madrid,dc=my-company,dc=com</em></div>
+</div>
+</li>
+</ul>
+</dd>
+<dt>Group Search Attributes:</dt>
+<dd><p class="first">These attributes are used when a search for groups is done. This happens, for
+instance, in the share dialogue. By default the group display name attribute
+as specified above is being used. Multiple attributes can be given, one in
+each line.</p>
+<p>If you override the default, the group display name attribute will not be
+taken into account, unless you specify it as well.</p>
+<ul class="last">
+<li><p class="first">Example:</p>
+<div class="line-block">
+<div class="line"><em>cn</em></div>
+<div class="line"><em>description</em></div>
+</div>
+</li>
+</ul>
+</dd>
+<dt>Group Member association:</dt>
+<dd><p class="first">The attribute that is used to indicate group memberships, i.e. the attribute
+used by LDAP groups to refer to their users.</p>
- <p>ownCloud detects the value automatically, you should only change it, if you
++<p>ownCloud detects the value automatically. You should only change it if you
+have a very valid reason and know what you are doing.</p>
+<ul class="last simple">
+<li>Example: <em>uniquemember</em></li>
+</ul>
+</dd>
+</dl>
+</div>
+<div class="section" id="special-attributes">
+<h3>Special Attributes<a class="headerlink" href="#special-attributes" title="Permalink to this headline">¶</a></h3>
+<div class="figure">
+<img alt="../_images/ldap-advanced-3-attributes.png" src="../_images/ldap-advanced-3-attributes.png" />
+<p class="caption">LDAP Advanced Settings, section Special Attributes</p>
+</div>
+<dl class="docutils">
+<dt>Quota Field:</dt>
+<dd><p class="first">ownCloud can read an LDAP attribute and set the user quota according to its
+value. Specify the attribute here, otherwise keep it empty. The attribute
+shall return human readable values, e.g. “2 GB”.</p>
+<ul class="last simple">
+<li>Example: <em>ownCloudQuota</em></li>
+</ul>
+</dd>
+<dt>Quota Default:</dt>
+<dd><p class="first">Override ownCloud default quota for LDAP users who do not
+have a quota set in the attribute given above.</p>
+<ul class="last simple">
+<li>Example: <em>15 GB</em></li>
+</ul>
+</dd>
+<dt>Email Field:</dt>
+<dd><p class="first">ownCloud can read an LDAP attribute and set the user email
+there from. Specify the attribute here, otherwise keep it empty.</p>
+<p>Although the wizard offers you to check login by email, the correct email
+attribute is not detected and you need to specify it manually.</p>
+<ul class="last simple">
+<li>Example: <em>mail</em></li>
+</ul>
+</dd>
+<dt>User Home Folder Naming Rule:</dt>
+<dd><p class="first">By default, the ownCloud creates the user
+directory, where all files and meta data are kept, according to the ownCloud
+user name. You may want to override this setting and name it after an
+attribute value. The attribute given can also return an absolute path, e.g.
+<tt class="docutils literal"><span class="pre">/mnt/storage43/alice</span></tt>. Leave it empty for default behavior.</p>
+<ul class="last simple">
+<li>Example: <em>cn</em></li>
+</ul>
+</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="expert-settings">
+<h2>Expert Settings<a class="headerlink" href="#expert-settings" title="Permalink to this headline">¶</a></h2>
+<div class="figure">
+<img alt="../_images/ldap-expert.png" src="../_images/ldap-expert.png" />
+</div>
+<p>In the Expert Settings fundamental behavior can be adjusted to your needs. The
+configuration should be done before starting production use or when testing the
+installation.</p>
+<dl class="docutils">
+<dt>Internal Username:</dt>
+<dd><p class="first">The internal username is the identifier in ownCloud for LDAP users. By default
+it will be created from the UUID attribute. By using the UUID attribute it is
+made sure that the username is unique and characters do not need to be
+converted. The internal username has the restriction that only these
+characters are allowed: [a-zA-Z0-9_.@-]. Other characters are replaced with
+their ASCII correspondence or are simply omitted.</p>
+<p>The LDAP backend ensures that there are no duplicate internal usernames in
+ownCloud, i.e. that it is checking all other activated user backends
+(including local ownCloud users). On collisions a random number (between 1000
+and 9999) will be attached to the retrieved value. For example, if “alice”
+exists, the next username may be “alice_1337”.</p>
+<p>The internal username is also the default name for the user home folder in
+ownCloud. It is also a part of remote URLs, for instance for all *DAV services.
+With this setting the default behaviour can be overridden.</p>
+<p>Leave it empty for default behaviour. Changes will have effect only on newly
+mapped (added) LDAP users.</p>
+<ul class="last simple">
+<li>Example: <em>uid</em></li>
+</ul>
+</dd>
+<dt>Override UUID detection</dt>
+<dd><p class="first">By default, ownCloud auto-detects the UUID attribute. The UUID attribute is
+used to doubtlessly identify LDAP users and groups. Also, the internal
+username will be created based on the UUID, if not specified otherwise above.</p>
+<p>You can override the setting and pass an attribute of your choice. You must
+make sure that the attribute of your choice can be fetched for both users and
+groups and it is unique. Leave it empty for default behaviour. Changes will
+have effect only on newly mapped (added) LDAP users and groups. It also will
- have effect when a user’s or group’s DN changes and an old UUID was cached: It
++have effect when a user’s or group’s DN changes and an old UUID was cached: it
+will result in a new user. Because of this, the setting should be applied
+before putting ownCloud in production use and cleaning the bindings
+(see below).</p>
+<ul class="last simple">
+<li>Example: <em>cn</em></li>
+</ul>
+</dd>
+<dt>Username-LDAP User Mapping</dt>
+<dd><p class="first">ownCloud uses the usernames as key to store and assign data. In order to
+precisely identify and recognize users, each LDAP user will have a internal
+username in ownCloud. This requires a mapping from ownCloud username to LDAP
+user. The created username is mapped to the UUID of the LDAP user.
+Additionally the DN is cached as well to reduce LDAP interaction, but it is
+not used for identification. If the DN changes, the change will be detected by
+ownCloud by checking the UUID value.</p>
+<p>The same is valid for groups.</p>
+<p>The internal ownCloud name is used all over in ownCloud. Clearing the Mappings
- will have leftovers everywhere. Do never clear the mappings
++will have leftovers everywhere. Never clear the mappings
+in a production environment. Only clear mappings in a testing or experimental
+stage.</p>
+<p class="last"><strong>Clearing the Mappings is not configuration sensitive, it affects all LDAP
+configurations!</strong></p>
+</dd>
+</dl>
+</div>
+<div class="section" id="testing-the-configuration">
+<h2>Testing the configuration<a class="headerlink" href="#testing-the-configuration" title="Permalink to this headline">¶</a></h2>
- <p>In this version we introduced the <strong>Test Configuration</strong> button on the bottom
- of the LDAP settings section. It will always check the values as currently
++<p>The <strong>Test Configuration</strong> button on the bottom
++of the LDAP settings section will always check the values as currently
+given in the input fields. You do not need to save before testing. By clicking
+on the button, ownCloud will try to bind to the ownCloud server with the
+settings currently given in the input fields. The response will look like this:</p>
+<div class="figure">
+<img alt="../_images/ldap-settings-invalid-oc45.png" src="../_images/ldap-settings-invalid-oc45.png" />
+<p class="caption">Failure</p>
+</div>
+<p>In case the configuration fails, you can see details in ownCloud’s log, which
+is in the data directory and called <strong>owncloud.log</strong> or on the bottom the
+<strong>Settings → Admin page</strong>. Unfortunately it requires a reload – sorry for the
+inconvenience.</p>
+<div class="figure">
+<img alt="../_images/ldap-settings-valid-oc45.png" src="../_images/ldap-settings-valid-oc45.png" />
+<p class="caption">Success</p>
+</div>
+<p>In this case, Save the settings. You can check if the users and groups are
+fetched correctly on the Settings → Users page.</p>
+</div>
+<div class="section" id="owncloud-avatar-integration">
+<h2>ownCloud Avatar integration<a class="headerlink" href="#owncloud-avatar-integration" title="Permalink to this headline">¶</a></h2>
+<p>ownCloud 6 incorporates a user profile picture feature, called Avatar. If a user
+has a photo stored in the <em>jpegPhoto</em> or, since 6.0.2, <em>thumbnailPhoto</em>
+attribute, it will be used as Avatar. The user then is not able to change his
+avatar in the personal settings. It must be done within LDAP. <em>jpegPhoto</em> is
+preferred over <em>thumbnailPhoto</em>.</p>
+<div class="figure">
+<img alt="../_images/ldap-fetched-avatar.png" src="../_images/ldap-fetched-avatar.png" />
+<p class="caption">Profile picture fetched from LDAP, Personal Settings</p>
+</div>
+<p>If the <em>jpegPhoto</em> or <em>thumbnailPhoto</em> attribute is not set or empty, the
+default ownCloud behaviour is active, i.e. the user will be able to set and
+change his profile picture in the personal settings. If the user sets a profile
+picture within ownCloud it will _not_ be stored in LDAP.</p>
+<p>The <em>jpegPhoto</em> or <em>thumbnailPhoto</em> attribute will be fetched once a day to make
+sure the current photo from LDAP is used in ownCloud. If a picture is added
+later, a possibly set profile picture will be overridden with the LDAP one. If a
+photo stored in the <em>jpegPhoto</em> and/or <em>thumbnailPhoto</em> attribute is deleted
+later, the last profile picture in ownCloud will still be used.</p>
+<p>The photo taken from LDAP will be adjusted to the requirements of the ownCloud
- avatar automatically. I.e. it will be transformed into a square. If the photo
++avatar automatically, i.e. it will be transformed into a square. If the photo
+needs to be cut, it will be done equally from both affected sides. The original
+photo stored in LDAP will stay the same, of course.</p>
+</div>
+<div class="section" id="troubleshooting-tips-and-tricks">
+<h2>Troubleshooting, Tips and Tricks<a class="headerlink" href="#troubleshooting-tips-and-tricks" title="Permalink to this headline">¶</a></h2>
+</div>
+<div class="section" id="ssl-certificate-verification-ldaps-tls">
+<h2>SSL Certificate Verification (LDAPS, TLS)<a class="headerlink" href="#ssl-certificate-verification-ldaps-tls" title="Permalink to this headline">¶</a></h2>
+<p>A common mistake with SSL certificates is that they may not be known to PHP.
+If you have trouble with certificate validation make sure that</p>
+<ul class="simple">
+<li>you have the certificate of the server installed on the ownCloud server</li>
+<li>the certificate is announced in the system’s LDAP configuration file (usually
+<em>/etc/ldap/ldap.conf</em> on Linux, <em>C:\openldap\sysconf\ldap.conf</em> or
+<em>C:\ldap.conf</em> on Windows) using a <strong>TLS_CACERT /path/to/cert</strong> line.</li>
+<li>Using LDAPS, also make sure that the port is correctly configured (by default
+686)</li>
+</ul>
+</div>
+<div class="section" id="microsoft-active-directory">
+<h2>Microsoft Active Directory<a class="headerlink" href="#microsoft-active-directory" title="Permalink to this headline">¶</a></h2>
+<p>Compared to earlier ownCloud versions, no further tweaks need to be done to
+make ownCloud work with Active Directory. ownCloud will automatically find the
+correct configuration in the wizard-like set up process.</p>
+</div>
+<div class="section" id="duplicating-server-configurations">
+<h2>Duplicating Server Configurations<a class="headerlink" href="#duplicating-server-configurations" title="Permalink to this headline">¶</a></h2>
+<p>In case you have a working configuration and want to create a similar one or
+“snapshot” configurations before modifying them you can do the following:</p>
+<ol class="arabic simple">
+<li>Go to the <strong>Server</strong> tab</li>
+<li>On <strong>Server Configuration</strong> choose <em>Add Server Configuration</em></li>
+<li>Answer the question <em>Take over settings from recent server configuration?</em>
+with <em>yes</em>.</li>
+<li>(optional) Switch to <strong>Advanced</strong> tab and uncheck <strong>Configuration Active</strong>
+in the <em>Connection Settings</em>, so the new configuration is not used on Save</li>
+<li>Click on <strong>Save</strong></li>
+</ol>
+<p>Now you can modify the configuration and enable it if you wish.</p>
+</div>
+<div class="section" id="owncloud-ldap-internals">
+<h2>ownCloud LDAP Internals<a class="headerlink" href="#owncloud-ldap-internals" title="Permalink to this headline">¶</a></h2>
+<p>Some parts of how the LDAP backend works are described here. May it be helpful.</p>
+</div>
+<div class="section" id="groups">
+<h2>Groups<a class="headerlink" href="#groups" title="Permalink to this headline">¶</a></h2>
+<p>At the moment, only secondary groups are read. That means that only the groups
+are retrieved, which are returned by the attribute auto-detected (or manually
- chosen) in Group-Member association. Primary groups are not being taken into
++chosen) in Group-Member association. Primary groups are not taken into
+account.</p>
+</div>
+<div class="section" id="user-and-group-mapping">
+<h2>User and Group Mapping<a class="headerlink" href="#user-and-group-mapping" title="Permalink to this headline">¶</a></h2>
+<p>In ownCloud the user or group name is used to have all relevant information in
+the database assigned. To work reliably a permanent internal user name and
+group name is created and mapped to the LDAP DN and UUID. If the DN changes in
+LDAP it will be detected, there will be no conflicts.</p>
+<p>Those mappings are done in the database table ldap_user_mapping and
+ldap_group_mapping. The user name is also used for the user’s folder (except
+something else is specified in <em>User Home Folder Naming Rule</em>), which
+contains files and meta data.</p>
+<p>As of ownCloud 5 internal user name and a visible display name are separated.
+This is not the case for group names, yet, i.e. group cannot be altered.</p>
+<p>That means that your LDAP configuration should be good and ready before putting
+it into production. The mapping tables are filled early, but as long as you are
+testing, you can empty the tables any time. Do not do this in production. If you
+want to rename a group, be very careful. Do not rename the user’s internal name.</p>
+</div>
+<div class="section" id="caching">
+<h2>Caching<a class="headerlink" href="#caching" title="Permalink to this headline">¶</a></h2>
- <p>For performance reasons a cache has been introduced to ownCloud. He we store
++<p>For performance reasons a cache has been introduced to ownCloud. The cache stores
+all users and groups, group memberships or internal userExists-requests. Since
+ownCloud is written in PHP and each and every page request (also done by Ajax)
+loads ownCloud and would execute one or more LDAP queries again, you do want to
+have some of those queries cached and save those requests and traffic. It is
+highly recommended to have the cache filled for a small amount of time, which
+comes also very handy when using the sync client, as it is yet another request
+for PHP.</p>
+</div>
+<div class="section" id="handling-with-backup-server">
+<h2>Handling with Backup Server<a class="headerlink" href="#handling-with-backup-server" title="Permalink to this headline">¶</a></h2>
- <p>When ownCloud is not able to contact the main server, he will be treated as
++<p>When ownCloud is not able to contact the main server, it will be treated as
+offline and no connection attempts will be done for the time specified in
+<strong>Cache Time-To-Live</strong>. If a backup server is configured, it will be connected
- instead. If you plan a maintained downtime, check <strong>Disable Main Server</strong> for
++instead. For planned downtime, check <strong>Disable Main Server</strong> for
+the time being to avoid unnecessary connection attempts every now and then.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+
+ </div>
+</div>
+ </body>
+</html>
diff --cc core/skeleton/ownCloudUserManual.pdf
index f883ee7,0000000..628f538
mode 100644,000000..100644
Binary files differ
diff --cc version.php
index 31c9a1b,6a900f8..d55a56e
--- a/version.php
+++ b/version.php
@@@ -1,6 -1,17 +1,6 @@@
-<?php
-
-// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel when updating major/minor version number.
-$OC_Version=array(6, 0, 3, 0);
-
-// The human readable string
-$OC_VersionString='6.0.3 RC1';
-
-// The ownCloud edition
-$OC_Edition='';
-
-// The ownCloud channel
-$OC_Channel='git';
-
-// The build number
-$OC_Build='';
-
+<?php
- $OC_Version = array(6,0,2,5);
- $OC_VersionString = '6.0.2';
++$OC_Version = array(6,0,3,0);
++$OC_VersionString = '6.0.3 RC1';
+$OC_Edition = '';
- $OC_Channel = 'daily';
- $OC_Build = '2014-04-18T03:13:40+00:00';
++$OC_Channel = 'testing';
++$OC_Build = '2014-04-23T16:23:29+00:00';
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud.git
More information about the Pkg-owncloud-commits
mailing list