diff --git a/3rdparty/Sabre.includes.php b/3rdparty/Sabre.includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
old mode 100644
new mode 100755
index 1bb6b5d53fa..4bcd32cdf88
--- a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
+++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
@@ -294,6 +294,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
// in the VALARM component code, so this is a hack, and an
// expensive one too.
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
+
// Fire up the iterator!
$it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
while($it->valid()) {
@@ -304,14 +305,35 @@ class Sabre_CalDAV_CalendarQueryValidator {
// determine if we can 'give up' expanding events.
$firstAlarm = null;
foreach($expandedEvent->VALARM as $expandedAlarm) {
+
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
- if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
- $firstAlarm = $effectiveTrigger;
- }
if ($expandedAlarm->isInTimeRange($start, $end)) {
return true;
}
+ if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
+ // This is an alarm with a non-relative trigger
+ // time, likely created by a buggy client. The
+ // implication is that every alarm in this
+ // recurring event trigger at the exact same
+ // time. It doesn't make sense to traverse
+ // further.
+ } else {
+ // We store the first alarm as a means to
+ // figure out when we can stop traversing.
+ if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
+ $firstAlarm = $effectiveTrigger;
+ }
+ }
+
+ }
+ if (is_null($firstAlarm)) {
+ // No alarm was found.
+ //
+ // Or technically: No alarm that will change for
+ // every instance of the recurrence was found,
+ // which means we can assume there was no match.
+ return false;
}
if ($firstAlarm > $end) {
return false;
diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/ICalendar.php b/3rdparty/Sabre/CalDAV/ICalendar.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/ICalendarObject.php b/3rdparty/Sabre/CalDAV/ICalendarObject.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php
old mode 100644
new mode 100755
index d7d1d970518..5903968c003
--- a/3rdparty/Sabre/CalDAV/Plugin.php
+++ b/3rdparty/Sabre/CalDAV/Plugin.php
@@ -672,6 +672,42 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
}
+ if ($vobj->name !== 'VCALENDAR') {
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
+ }
+
+ $foundType = null;
+ $foundUID = null;
+ foreach($vobj->getComponents() as $component) {
+ switch($component->name) {
+ case 'VTIMEZONE' :
+ continue 2;
+ case 'VEVENT' :
+ case 'VTODO' :
+ case 'VJOURNAL' :
+ if (is_null($foundType)) {
+ $foundType = $component->name;
+ if (!isset($component->UID)) {
+ throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
+ }
+ $foundUID = (string)$component->UID;
+ } else {
+ if ($foundType !== $component->name) {
+ throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType);
+ }
+ if ($foundUID !== (string)$component->UID) {
+ throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs');
+ }
+ }
+ break;
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here');
+
+ }
+ }
+ if (!$foundType)
+ throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL');
+
}
/**
diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php
old mode 100644
new mode 100755
index 939e903c89f..289a0c83a34
--- a/3rdparty/Sabre/CalDAV/Version.php
+++ b/3rdparty/Sabre/CalDAV/Version.php
@@ -14,7 +14,7 @@ class Sabre_CalDAV_Version {
/**
* Full version number
*/
- const VERSION = '1.6.2';
+ const VERSION = '1.6.3';
/**
* Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/CalDAV/includes.php b/3rdparty/Sabre/CalDAV/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/AddressBook.php b/3rdparty/Sabre/CardDAV/AddressBook.php
old mode 100644
new mode 100755
index 3b381e1eea3..12297175a85
--- a/3rdparty/Sabre/CardDAV/AddressBook.php
+++ b/3rdparty/Sabre/CardDAV/AddressBook.php
@@ -108,7 +108,9 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca
*/
public function createFile($name,$vcardData = null) {
- $vcardData = stream_get_contents($vcardData);
+ if (is_resource($vcardData)) {
+ $vcardData = stream_get_contents($vcardData);
+ }
// Converting to UTF-8, if needed
$vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData);
diff --git a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
old mode 100644
new mode 100755
index 85a4963127b..46bb8ff18dd
--- a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
+++ b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
@@ -9,7 +9,7 @@
* @package Sabre
* @subpackage CardDAV
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CardDAV_AddressBookQueryParser {
@@ -88,12 +88,22 @@ class Sabre_CardDAV_AddressBookQueryParser {
if (is_nan($limit)) $limit = null;
$filter = $this->xpath->query('/card:addressbook-query/card:filter');
- if ($filter->length !== 1) {
+
+ // According to the CardDAV spec there needs to be exactly 1 filter
+ // element. However, KDE 4.8.2 contains a bug that will encode 0 filter
+ // elements, so this is a workaround for that.
+ //
+ // See: https://bugs.kde.org/show_bug.cgi?id=300047
+ if ($filter->length === 0) {
+ $test = null;
+ $filter = null;
+ } elseif ($filter->length === 1) {
+ $filter = $filter->item(0);
+ $test = $this->xpath->evaluate('string(@test)', $filter);
+ } else {
throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
}
- $filter = $filter->item(0);
- $test = $this->xpath->evaluate('string(@test)', $filter);
if (!$test) $test = self::TEST_ANYOF;
if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
diff --git a/3rdparty/Sabre/CardDAV/AddressBookRoot.php b/3rdparty/Sabre/CardDAV/AddressBookRoot.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Backend/Abstract.php b/3rdparty/Sabre/CardDAV/Backend/Abstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Backend/PDO.php b/3rdparty/Sabre/CardDAV/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Card.php b/3rdparty/Sabre/CardDAV/Card.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/IAddressBook.php b/3rdparty/Sabre/CardDAV/IAddressBook.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/ICard.php b/3rdparty/Sabre/CardDAV/ICard.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/IDirectory.php b/3rdparty/Sabre/CardDAV/IDirectory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Plugin.php b/3rdparty/Sabre/CardDAV/Plugin.php
old mode 100644
new mode 100755
index 9ebec243eb0..ca20e468497
--- a/3rdparty/Sabre/CardDAV/Plugin.php
+++ b/3rdparty/Sabre/CardDAV/Plugin.php
@@ -52,6 +52,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
$server->subscribeEvent('report', array($this,'report'));
$server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
$server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
+ $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
+ $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
/* Namespaces */
$server->xmlNamespaces[self::NS_CARDDAV] = 'card';
@@ -283,6 +285,81 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
}
+ /**
+ * This method is triggered before a file gets updated with new content.
+ *
+ * This plugin uses this method to ensure that Card nodes receive valid
+ * vcard data.
+ *
+ * @param string $path
+ * @param Sabre_DAV_IFile $node
+ * @param resource $data
+ * @return void
+ */
+ public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) {
+
+ if (!$node instanceof Sabre_CardDAV_ICard)
+ return;
+
+ $this->validateVCard($data);
+
+ }
+
+ /**
+ * This method is triggered before a new file is created.
+ *
+ * This plugin uses this method to ensure that Card nodes receive valid
+ * vcard data.
+ *
+ * @param string $path
+ * @param resource $data
+ * @param Sabre_DAV_ICollection $parentNode
+ * @return void
+ */
+ public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) {
+
+ if (!$parentNode instanceof Sabre_CardDAV_IAddressBook)
+ return;
+
+ $this->validateVCard($data);
+
+ }
+
+ /**
+ * Checks if the submitted iCalendar data is in fact, valid.
+ *
+ * An exception is thrown if it's not.
+ *
+ * @param resource|string $data
+ * @return void
+ */
+ protected function validateVCard(&$data) {
+
+ // If it's a stream, we convert it to a string first.
+ if (is_resource($data)) {
+ $data = stream_get_contents($data);
+ }
+
+ // Converting the data to unicode, if needed.
+ $data = Sabre_DAV_StringUtil::ensureUTF8($data);
+
+ try {
+
+ $vobj = Sabre_VObject_Reader::read($data);
+
+ } catch (Sabre_VObject_ParseException $e) {
+
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
+
+ }
+
+ if ($vobj->name !== 'VCARD') {
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
+ }
+
+ }
+
+
/**
* This function handles the addressbook-query REPORT
*
@@ -362,6 +439,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
$vcard = Sabre_VObject_Reader::read($vcardData);
+ if (!$filters) return true;
+
foreach($filters as $filter) {
$isDefined = isset($vcard->{$filter['name']});
diff --git a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/UserAddressBooks.php b/3rdparty/Sabre/CardDAV/UserAddressBooks.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/CardDAV/Version.php b/3rdparty/Sabre/CardDAV/Version.php
old mode 100644
new mode 100755
index 811b929e397..d0623f0d3e8
--- a/3rdparty/Sabre/CardDAV/Version.php
+++ b/3rdparty/Sabre/CardDAV/Version.php
@@ -16,7 +16,7 @@ class Sabre_CardDAV_Version {
/**
* Full version number
*/
- const VERSION = '1.6.1';
+ const VERSION = '1.6.3';
/**
* Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/CardDAV/includes.php b/3rdparty/Sabre/CardDAV/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/File.php b/3rdparty/Sabre/DAV/Auth/Backend/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/IBackend.php b/3rdparty/Sabre/DAV/Auth/IBackend.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Auth/Plugin.php b/3rdparty/Sabre/DAV/Auth/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/GuessContentType.php b/3rdparty/Sabre/DAV/Browser/GuessContentType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/favicon.ico b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/card.png b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/file.png b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Client.php b/3rdparty/Sabre/DAV/Client.php
old mode 100644
new mode 100755
index 075e84caa1d..9a428765e90
--- a/3rdparty/Sabre/DAV/Client.php
+++ b/3rdparty/Sabre/DAV/Client.php
@@ -11,7 +11,7 @@
* @package Sabre
* @subpackage DAVClient
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_DAV_Client {
@@ -23,6 +23,28 @@ class Sabre_DAV_Client {
protected $password;
protected $proxy;
+ /**
+ * Basic authentication
+ */
+ const AUTH_BASIC = 1;
+
+ /**
+ * Digest authentication
+ */
+ const AUTH_DIGEST = 2;
+
+ /**
+ * The authentication type we're using.
+ *
+ * This is a bitmask of AUTH_BASIC and AUTH_DIGEST.
+ *
+ * If DIGEST is used, the client makes 1 extra request per request, to get
+ * the authentication tokens.
+ *
+ * @var int
+ */
+ protected $authType;
+
/**
* Constructor
*
@@ -46,16 +68,21 @@ class Sabre_DAV_Client {
'baseUri',
'userName',
'password',
- 'proxy'
+ 'proxy',
);
-
foreach($validSettings as $validSetting) {
if (isset($settings[$validSetting])) {
$this->$validSetting = $settings[$validSetting];
}
}
+ if (isset($settings['authType'])) {
+ $this->authType = $settings['authType'];
+ } else {
+ $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST;
+ }
+
$this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
}
@@ -250,14 +277,9 @@ class Sabre_DAV_Client {
// Automatically follow redirects
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
- CURLOPT_SSL_VERIFYPEER => true,
- //CURLOPT_SSL_VERIFYPEER => false,
);
switch ($method) {
- case 'PUT':
- $curlSettings[CURLOPT_PUT] = true;
- break;
case 'HEAD' :
// do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD
@@ -288,8 +310,15 @@ class Sabre_DAV_Client {
$curlSettings[CURLOPT_PROXY] = $this->proxy;
}
- if ($this->userName) {
- $curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST;
+ if ($this->userName && $this->authType) {
+ $curlType = 0;
+ if ($this->authType & self::AUTH_BASIC) {
+ $curlType |= CURLAUTH_BASIC;
+ }
+ if ($this->authType & self::AUTH_DIGEST) {
+ $curlType |= CURLAUTH_DIGEST;
+ }
+ $curlSettings[CURLOPT_HTTPAUTH] = $curlType;
$curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
}
diff --git a/3rdparty/Sabre/DAV/Collection.php b/3rdparty/Sabre/DAV/Collection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Directory.php b/3rdparty/Sabre/DAV/Directory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception.php b/3rdparty/Sabre/DAV/Exception.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/BadRequest.php b/3rdparty/Sabre/DAV/Exception/BadRequest.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/Conflict.php b/3rdparty/Sabre/DAV/Exception/Conflict.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/FileNotFound.php b/3rdparty/Sabre/DAV/Exception/FileNotFound.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/Forbidden.php b/3rdparty/Sabre/DAV/Exception/Forbidden.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/Locked.php b/3rdparty/Sabre/DAV/Exception/Locked.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/NotFound.php b/3rdparty/Sabre/DAV/Exception/NotFound.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/NotImplemented.php b/3rdparty/Sabre/DAV/Exception/NotImplemented.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/PaymentRequired.php b/3rdparty/Sabre/DAV/Exception/PaymentRequired.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FS/Directory.php b/3rdparty/Sabre/DAV/FS/Directory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FS/File.php b/3rdparty/Sabre/DAV/FS/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FS/Node.php b/3rdparty/Sabre/DAV/FS/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FSExt/Directory.php b/3rdparty/Sabre/DAV/FSExt/Directory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FSExt/File.php b/3rdparty/Sabre/DAV/FSExt/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/FSExt/Node.php b/3rdparty/Sabre/DAV/FSExt/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/File.php b/3rdparty/Sabre/DAV/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IExtendedCollection.php b/3rdparty/Sabre/DAV/IExtendedCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IFile.php b/3rdparty/Sabre/DAV/IFile.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/INode.php b/3rdparty/Sabre/DAV/INode.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IProperties.php b/3rdparty/Sabre/DAV/IProperties.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/IQuota.php b/3rdparty/Sabre/DAV/IQuota.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/FS.php b/3rdparty/Sabre/DAV/Locks/Backend/FS.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/File.php b/3rdparty/Sabre/DAV/Locks/Backend/File.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/LockInfo.php b/3rdparty/Sabre/DAV/Locks/LockInfo.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Mount/Plugin.php b/3rdparty/Sabre/DAV/Mount/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Node.php b/3rdparty/Sabre/DAV/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/ObjectTree.php b/3rdparty/Sabre/DAV/ObjectTree.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property.php b/3rdparty/Sabre/DAV/Property.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/GetLastModified.php b/3rdparty/Sabre/DAV/Property/GetLastModified.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/Href.php b/3rdparty/Sabre/DAV/Property/Href.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/HrefList.php b/3rdparty/Sabre/DAV/Property/HrefList.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/IHref.php b/3rdparty/Sabre/DAV/Property/IHref.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/LockDiscovery.php b/3rdparty/Sabre/DAV/Property/LockDiscovery.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/ResourceType.php b/3rdparty/Sabre/DAV/Property/ResourceType.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/Response.php b/3rdparty/Sabre/DAV/Property/Response.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/ResponseList.php b/3rdparty/Sabre/DAV/Property/ResponseList.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/SupportedLock.php b/3rdparty/Sabre/DAV/Property/SupportedLock.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Property/SupportedReportSet.php b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php
old mode 100644
new mode 100755
index 50b190e8fab..0dfac8b0c71
--- a/3rdparty/Sabre/DAV/Server.php
+++ b/3rdparty/Sabre/DAV/Server.php
@@ -215,7 +215,7 @@ class Sabre_DAV_Server {
$DOM->appendChild($error);
$error->appendChild($DOM->createElement('s:exception',get_class($e)));
- $error->appendChild($DOM->createElement('s:message',htmlentities($e->getMessage())));
+ $error->appendChild($DOM->createElement('s:message',$e->getMessage()));
if ($this->debugExceptions) {
$error->appendChild($DOM->createElement('s:file',$e->getFile()));
$error->appendChild($DOM->createElement('s:line',$e->getLine()));
@@ -1784,7 +1784,14 @@ class Sabre_DAV_Server {
$etag = $node->getETag();
if ($etag===$ifMatchItem) {
$haveMatch = true;
+ } else {
+ // Evolution has a bug where it sometimes prepends the "
+ // with a \. This is our workaround.
+ if (str_replace('\\"','"', $ifMatchItem) === $etag) {
+ $haveMatch = true;
+ }
}
+
}
if (!$haveMatch) {
throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match');
diff --git a/3rdparty/Sabre/DAV/ServerPlugin.php b/3rdparty/Sabre/DAV/ServerPlugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/SimpleCollection.php b/3rdparty/Sabre/DAV/SimpleCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/SimpleDirectory.php b/3rdparty/Sabre/DAV/SimpleDirectory.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/SimpleFile.php b/3rdparty/Sabre/DAV/SimpleFile.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/StringUtil.php b/3rdparty/Sabre/DAV/StringUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Tree.php b/3rdparty/Sabre/DAV/Tree.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Tree/Filesystem.php b/3rdparty/Sabre/DAV/Tree/Filesystem.php
old mode 100644
new mode 100755
index 85a9ee317be..40580ae366f
--- a/3rdparty/Sabre/DAV/Tree/Filesystem.php
+++ b/3rdparty/Sabre/DAV/Tree/Filesystem.php
@@ -42,9 +42,9 @@ class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree {
$realPath = $this->getRealPath($path);
if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found');
if (is_dir($realPath)) {
- return new Sabre_DAV_FS_Directory($path);
+ return new Sabre_DAV_FS_Directory($realPath);
} else {
- return new Sabre_DAV_FS_File($path);
+ return new Sabre_DAV_FS_File($realPath);
}
}
diff --git a/3rdparty/Sabre/DAV/URLUtil.php b/3rdparty/Sabre/DAV/URLUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/UUIDUtil.php b/3rdparty/Sabre/DAV/UUIDUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/Version.php b/3rdparty/Sabre/DAV/Version.php
old mode 100644
new mode 100755
index 5e5d15e4039..40cfe81b34f
--- a/3rdparty/Sabre/DAV/Version.php
+++ b/3rdparty/Sabre/DAV/Version.php
@@ -14,7 +14,7 @@ class Sabre_DAV_Version {
/**
* Full version number
*/
- const VERSION = '1.6.2';
+ const VERSION = '1.6.3';
/**
* Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/DAV/XMLUtil.php b/3rdparty/Sabre/DAV/XMLUtil.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAV/includes.php b/3rdparty/Sabre/DAV/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/IPrincipal.php b/3rdparty/Sabre/DAVACL/IPrincipal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Principal.php b/3rdparty/Sabre/DAVACL/Principal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/PrincipalCollection.php b/3rdparty/Sabre/DAVACL/PrincipalCollection.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/Version.php b/3rdparty/Sabre/DAVACL/Version.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/DAVACL/includes.php b/3rdparty/Sabre/DAVACL/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/AWSAuth.php b/3rdparty/Sabre/HTTP/AWSAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/AbstractAuth.php b/3rdparty/Sabre/HTTP/AbstractAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/BasicAuth.php b/3rdparty/Sabre/HTTP/BasicAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/DigestAuth.php b/3rdparty/Sabre/HTTP/DigestAuth.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Response.php b/3rdparty/Sabre/HTTP/Response.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Util.php b/3rdparty/Sabre/HTTP/Util.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/Version.php b/3rdparty/Sabre/HTTP/Version.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/HTTP/includes.php b/3rdparty/Sabre/HTTP/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component.php b/3rdparty/Sabre/VObject/Component.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VAlarm.php b/3rdparty/Sabre/VObject/Component/VAlarm.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VCalendar.php b/3rdparty/Sabre/VObject/Component/VCalendar.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VEvent.php b/3rdparty/Sabre/VObject/Component/VEvent.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VJournal.php b/3rdparty/Sabre/VObject/Component/VJournal.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Component/VTodo.php b/3rdparty/Sabre/VObject/Component/VTodo.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/DateTimeParser.php b/3rdparty/Sabre/VObject/DateTimeParser.php
old mode 100644
new mode 100755
index 1e2d54ef3a9..23a4bb69916
--- a/3rdparty/Sabre/VObject/DateTimeParser.php
+++ b/3rdparty/Sabre/VObject/DateTimeParser.php
@@ -125,6 +125,9 @@ class Sabre_VObject_DateTimeParser {
}
+ if ($duration==='P') {
+ $duration = 'PT0S';
+ }
$iv = new DateInterval($duration);
if ($invert) $iv->invert = true;
@@ -150,6 +153,7 @@ class Sabre_VObject_DateTimeParser {
}
$newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
+ if ($newDur === '+') { $newDur = '+0 seconds'; };
return $newDur;
}
diff --git a/3rdparty/Sabre/VObject/Element.php b/3rdparty/Sabre/VObject/Element.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Element/DateTime.php b/3rdparty/Sabre/VObject/Element/DateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Element/MultiDateTime.php b/3rdparty/Sabre/VObject/Element/MultiDateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/ElementList.php b/3rdparty/Sabre/VObject/ElementList.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/FreeBusyGenerator.php b/3rdparty/Sabre/VObject/FreeBusyGenerator.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Node.php b/3rdparty/Sabre/VObject/Node.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Property.php b/3rdparty/Sabre/VObject/Property.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Property/DateTime.php b/3rdparty/Sabre/VObject/Property/DateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Property/MultiDateTime.php b/3rdparty/Sabre/VObject/Property/MultiDateTime.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Reader.php b/3rdparty/Sabre/VObject/Reader.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/RecurrenceIterator.php b/3rdparty/Sabre/VObject/RecurrenceIterator.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/Version.php b/3rdparty/Sabre/VObject/Version.php
old mode 100644
new mode 100755
index 00110febc07..2617c7b129d
--- a/3rdparty/Sabre/VObject/Version.php
+++ b/3rdparty/Sabre/VObject/Version.php
@@ -14,7 +14,7 @@ class Sabre_VObject_Version {
/**
* Full version number
*/
- const VERSION = '1.3.2';
+ const VERSION = '1.3.3';
/**
* Stability : alpha, beta, stable
diff --git a/3rdparty/Sabre/VObject/WindowsTimezoneMap.php b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/VObject/includes.php b/3rdparty/Sabre/VObject/includes.php
old mode 100644
new mode 100755
diff --git a/3rdparty/Sabre/autoload.php b/3rdparty/Sabre/autoload.php
old mode 100644
new mode 100755
diff --git a/3rdparty/miniColors/GPL-LICENSE.txt b/3rdparty/miniColors/GPL-LICENSE.txt
new file mode 100644
index 00000000000..11dddd00ef0
--- /dev/null
+++ b/3rdparty/miniColors/GPL-LICENSE.txt
@@ -0,0 +1,278 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/3rdparty/miniColors/MIT-LICENSE.txt b/3rdparty/miniColors/MIT-LICENSE.txt
new file mode 100644
index 00000000000..ec6f7581576
--- /dev/null
+++ b/3rdparty/miniColors/MIT-LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) Cory LaViska
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/3rdparty/miniColors/css/images/colors.png b/3rdparty/miniColors/css/images/colors.png
new file mode 100755
index 00000000000..1b4f819d8d9
Binary files /dev/null and b/3rdparty/miniColors/css/images/colors.png differ
diff --git a/3rdparty/miniColors/css/images/trigger.png b/3rdparty/miniColors/css/images/trigger.png
new file mode 100755
index 00000000000..8c169fd6053
Binary files /dev/null and b/3rdparty/miniColors/css/images/trigger.png differ
diff --git a/3rdparty/miniColors/css/jquery.miniColors.css b/3rdparty/miniColors/css/jquery.miniColors.css
new file mode 100755
index 00000000000..381bc1dc065
--- /dev/null
+++ b/3rdparty/miniColors/css/jquery.miniColors.css
@@ -0,0 +1,81 @@
+.miniColors-trigger {
+ height: 22px;
+ width: 22px;
+ background: url(images/trigger.png) center no-repeat;
+ vertical-align: middle;
+ margin: 0 .25em;
+ display: inline-block;
+ outline: none;
+}
+
+.miniColors-selector {
+ position: absolute;
+ width: 175px;
+ height: 150px;
+ background: #FFF;
+ border: solid 1px #BBB;
+ -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+ -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+ box-shadow: 0 0 6px rgba(0, 0, 0, .25);
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ padding: 5px;
+ z-index: 999999;
+}
+
+.miniColors-selector.black {
+ background: #000;
+ border-color: #000;
+}
+
+.miniColors-colors {
+ position: absolute;
+ top: 5px;
+ left: 5px;
+ width: 150px;
+ height: 150px;
+ background: url(images/colors.png) right no-repeat;
+ cursor: crosshair;
+}
+
+.miniColors-hues {
+ position: absolute;
+ top: 5px;
+ left: 160px;
+ width: 20px;
+ height: 150px;
+ background: url(images/colors.png) left no-repeat;
+ cursor: crosshair;
+}
+
+.miniColors-colorPicker {
+ position: absolute;
+ width: 9px;
+ height: 9px;
+ border: 1px solid #fff;
+ -moz-border-radius: 11px;
+ -webkit-border-radius: 11px;
+ border-radius: 11px;
+}
+.miniColors-colorPicker-inner {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 7px;
+ height: 7px;
+ border: 1px solid #000;
+ -moz-border-radius: 9px;
+ -webkit-border-radius: 9px;
+ border-radius: 9px;
+}
+
+.miniColors-huePicker {
+ position: absolute;
+ left: -3px;
+ width: 24px;
+ height: 1px;
+ border: 1px solid #fff;
+ border-radius: 2px;
+ background: #000;
+}
\ No newline at end of file
diff --git a/3rdparty/miniColors/js/jquery.miniColors.js b/3rdparty/miniColors/js/jquery.miniColors.js
new file mode 100755
index 00000000000..187db3fa84e
--- /dev/null
+++ b/3rdparty/miniColors/js/jquery.miniColors.js
@@ -0,0 +1,580 @@
+/*
+ * jQuery miniColors: A small color selector
+ *
+ * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/)
+ *
+ * Dual licensed under the MIT or GPL Version 2 licenses
+ *
+*/
+if(jQuery) (function($) {
+
+ $.extend($.fn, {
+
+ miniColors: function(o, data) {
+
+ var create = function(input, o, data) {
+ //
+ // Creates a new instance of the miniColors selector
+ //
+
+ // Determine initial color (defaults to white)
+ var color = expandHex(input.val());
+ if( !color ) color = 'ffffff';
+ var hsb = hex2hsb(color);
+
+ // Create trigger
+ var trigger = $('');
+ trigger.insertAfter(input);
+
+ // Set input data and update attributes
+ input
+ .addClass('miniColors')
+ .data('original-maxlength', input.attr('maxlength') || null)
+ .data('original-autocomplete', input.attr('autocomplete') || null)
+ .data('letterCase', 'uppercase')
+ .data('trigger', trigger)
+ .data('hsb', hsb)
+ .data('change', o.change ? o.change : null)
+ .data('close', o.close ? o.close : null)
+ .data('open', o.open ? o.open : null)
+ .attr('maxlength', 7)
+ .attr('autocomplete', 'off')
+ .val('#' + convertCase(color, o.letterCase));
+
+ // Handle options
+ if( o.readonly ) input.prop('readonly', true);
+ if( o.disabled ) disable(input);
+
+ // Show selector when trigger is clicked
+ trigger.bind('click.miniColors', function(event) {
+ event.preventDefault();
+ if( input.val() === '' ) input.val('#');
+ show(input);
+
+ });
+
+ // Show selector when input receives focus
+ input.bind('focus.miniColors', function(event) {
+ if( input.val() === '' ) input.val('#');
+ show(input);
+ });
+
+ // Hide on blur
+ input.bind('blur.miniColors', function(event) {
+ var hex = expandHex( hsb2hex(input.data('hsb')) );
+ input.val( hex ? '#' + convertCase(hex, input.data('letterCase')) : '' );
+ });
+
+ // Hide when tabbing out of the input
+ input.bind('keydown.miniColors', function(event) {
+ if( event.keyCode === 9 ) hide(input);
+ });
+
+ // Update when color is typed in
+ input.bind('keyup.miniColors', function(event) {
+ setColorFromInput(input);
+ });
+
+ // Handle pasting
+ input.bind('paste.miniColors', function(event) {
+ // Short pause to wait for paste to complete
+ setTimeout( function() {
+ setColorFromInput(input);
+ }, 5);
+ });
+
+ };
+
+ var destroy = function(input) {
+ //
+ // Destroys an active instance of the miniColors selector
+ //
+
+ hide();
+ input = $(input);
+
+ // Restore to original state
+ input.data('trigger').remove();
+ input
+ .attr('autocomplete', input.data('original-autocomplete'))
+ .attr('maxlength', input.data('original-maxlength'))
+ .removeData()
+ .removeClass('miniColors')
+ .unbind('.miniColors');
+ $(document).unbind('.miniColors');
+ };
+
+ var enable = function(input) {
+ //
+ // Enables the input control and the selector
+ //
+ input
+ .prop('disabled', false)
+ .data('trigger')
+ .css('opacity', 1);
+ };
+
+ var disable = function(input) {
+ //
+ // Disables the input control and the selector
+ //
+ hide(input);
+ input
+ .prop('disabled', true)
+ .data('trigger')
+ .css('opacity', 0.5);
+ };
+
+ var show = function(input) {
+ //
+ // Shows the miniColors selector
+ //
+ if( input.prop('disabled') ) return false;
+
+ // Hide all other instances
+ hide();
+
+ // Generate the selector
+ var selector = $('
+
\ No newline at end of file
diff --git a/apps/atnotes/appinfo/info.xml b/apps/atnotes/appinfo/info.xml
new file mode 100644
index 00000000000..0ea9a74200d
--- /dev/null
+++ b/apps/atnotes/appinfo/info.xml
@@ -0,0 +1,11 @@
+
+
+ atnotes
+ ATNotes
+ ATNotes for Always Take Notes. Create/Edit/Delete notes in the application. You can export notes to save them in your ownCloud filesystem and you can access them through the WebDAV protocol.
+ 1.0
+ AGPL
+ Xavier Beurois (www.djazz-lab.net)
+ 4
+ true
+
diff --git a/apps/atnotes/atnotes.php b/apps/atnotes/atnotes.php
new file mode 100644
index 00000000000..b028f0adcee
--- /dev/null
+++ b/apps/atnotes/atnotes.php
@@ -0,0 +1,29 @@
+.
+*
+*/
+
+OCP\User::checkLoggedIn();
+OCP\App::checkAppEnabled('atnotes');
+
+$tmpl = new OCP\Template('atnotes', 'main.tpl', 'user');
+$tmpl->assign('notes_list', OC_ATNotes::getNotesList());
+$tmpl->printPage();
diff --git a/apps/atnotes/css/atnotes.min.css b/apps/atnotes/css/atnotes.min.css
new file mode 100644
index 00000000000..b98a2c50a80
--- /dev/null
+++ b/apps/atnotes/css/atnotes.min.css
@@ -0,0 +1,22 @@
+/**
+* ownCloud - ATNotes plugin
+*
+* @author Xavier Beurois
+* @copyright 2012 Xavier Beurois www.djazz-lab.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 Lesser General Public
+* License along with this library. If not, see .
+*
+*/
+
+#atnotes_container{min-width:830px}#atnotes_container div#controls div.title{color:#BBB;font-style:italic;padding:.7em .5em .5em}#atnotes_container ul.atnotes-noteslist{background:none repeat scroll 0 0 #f8f8f8;border-right:1px solid #DDD;overflow:auto;position:fixed;top:6.3em;width:20em;box-shadow:-2px -3px 7px #000;z-index:2}#atnotes_container ul.atnotes-noteslist li.atnotes-elt{border-bottom:1px solid #CCC;padding:.3em .8em 1em;-moz-transition:background-color 500ms ease 0s;background:none repeat scroll 0 0 #f8f8f8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#atnotes_container ul.atnotes-noteslist li.atnotes-elt:hover,#atnotes_container ul.atnotes-noteslist li.atnotes-elt.active{background:none repeat scroll 0 0 #eee}#atnotes_container ul.atnotes-noteslist li.atnotes-elt:hover div{cursor:pointer}#atnotes_container ul.atnotes-noteslist li.atnotes-elt div.atnotes-elt-title{font-weight:bold;white-space:pre-wrap;width:18.4em}#atnotes_container ul.atnotes-noteslist li.atnotes-elt div.atnotes-elt-state{float:left;width:10em;font-style:italic;color:#AAA}#atnotes_container ul.atnotes-noteslist li.atnotes-elt div.atnotes-elt-date{color:#999;float:right;width:7em;text-align:right}#atnotes_container ul.atnotes-noteslist li.atnotes-elt div.atnotes-elt-prerender{display:none}#atnotes_container div.atnotes-notesedit{left:20em;position:absolute;top:2.9em}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-states{box-shadow:0 4px 7px #AAA}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-states div.atnotes-saved{float:right}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-title,#atnotes_container div.atnotes-notesedit div.anotes-notesedit-states{padding:.5em 2em;background-color:#FFF;border-bottom:1px solid #CCC;height:1.8em;z-index:1;min-width:350px}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-states{padding:.5em 2em .5em 22em}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-title input#note_title{box-shadow:none;background:0;-moz-border-radius:0;-webkit-border-radius:0;padding:0;margin:0;border:0 none}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-title div.atnotes-actions-btns{float:right;margin-top:.5em}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-title div.atnotes-actions-btns img{cursor:pointer}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-title div.atnotes-actions-btns img.atnotes-actions-list{margin-right:1em}#atnotes_container div.atnotes-notesedit div.anotes-notesedit-title div.atnotes-actions-btns div.atnotes-actions-ddmenu{display:none}#atnotes_container div.jqte{left:20em;position:absolute;top:5.8em;border:0 none;resize:none;min-width:350px}#save_dialog div.atnotes-explorer{background-color:#FFF;border:1px solid #AAA;height:100%;width:100%;overflow-x:auto;overflow-y:visible}#save_dialog div.atnotes-explorer table{width:100%}#save_dialog div.atnotes-explorer table tr td.filename{width:70%;background-position:0 .1em;background-repeat:no-repeat;padding-left:1.7em;cursor:pointer}#save_dialog div.atnotes-explorer table tr td.filename span{cursor:pointer}#save_dialog div.atnotes-explorer table tr td.filesize{width:10%;text-align:center}#save_dialog div.atnotes-explorer table tr td.date{width:20%;text-align:center}.ui-tooltip-content ul li:hover{color:#AAA;background-color:#1a1a1a}
diff --git a/apps/atnotes/css/jquery-te-1.0.3.min.css b/apps/atnotes/css/jquery-te-1.0.3.min.css
new file mode 100644
index 00000000000..b60e50b1590
--- /dev/null
+++ b/apps/atnotes/css/jquery-te-1.0.3.min.css
@@ -0,0 +1,11 @@
+/*!
+ * http://jqueryte.com
+ * jQuery Text Editor 1.0.2
+ * Copyright (C) 2012, Fatih Koca (fatihkoca@me.com), AUTHOR.txt (http://jqueryte.com/about)
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+This program 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 General Public License for more details.
+You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+
+.jqte{overflow:hidden}.jqte *{font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#333}.jqte_Panel{height:25px;padding:5px 8px}.jqte_Panel a{display:block;float:left;width:22px;height:22px;border:#FFF 1px solid;text-align:center;background:url('/apps/atnotes/img/jquery-te-toolbar.png') no-repeat;font-weight:bold;cursor:pointer;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.jqte_Panel a.bold{background-position:0 0}.jqte_Panel a.italic{background-position:-22px 0}.jqte_Panel a.underline{background-position:-44px 0}.jqte_Panel a.orderedlist{background-position:-66px 0}.jqte_Panel a.unorderedlist{background-position:-88px 0}.jqte_Panel a.subscript{background-position:-110px 0}.jqte_Panel a.superscript{background-position:-132px 0}.jqte_Panel a.strike{background-position:-154px 0}.jqte_Panel a.remove{background-position:-176px 0}.jqte_Panel a.rule{background-position:-198px 0}.jqte_Panel a:hover,.jqte_Active{border:#CCC 1px solid!important;box-shadow:inset 0 0 2px #999;background-color:#FFF}.jqte_Panel a:active,.jqte_Active{background-color:#DDD!important}.jqte_Content{overflow-y:auto;position:absolute;top:3.3em;left:.7em;padding-right:.3em}.jqte_Content div,.jqte_Content p{margin:7px 0}.jqte_Content ul{list-style-type:disc}.jqte_Content ol{list-style-type:decimal}.jqte_Content ol,.jqte_Content ul{list-style-position:inside}
\ No newline at end of file
diff --git a/apps/atnotes/css/jquery.qtip.min.css b/apps/atnotes/css/jquery.qtip.min.css
new file mode 100644
index 00000000000..3f5e39adc73
--- /dev/null
+++ b/apps/atnotes/css/jquery.qtip.min.css
@@ -0,0 +1 @@
+.ui-tooltip,.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;border-width:1px;border-style:solid;}.ui-tooltip-fluid{display:block;visibility:hidden;position:static!important;float:left!important;}.ui-tooltip-content{position:relative;padding:5px 9px;overflow:hidden;text-align:left;word-wrap:break-word;overflow:hidden;}.ui-tooltip-titlebar{position:relative;min-height:14px;padding:5px 35px 5px 10px;overflow:hidden;border-width:0 0 1px;font-weight:bold;}.ui-tooltip-titlebar+.ui-tooltip-content{border-top-width:0!important;}/*!Default close button class */ .ui-tooltip-titlebar .ui-state-default{position:absolute;right:4px;top:50%;margin-top:-9px;cursor:pointer;outline:medium none;border-width:1px;border-style:solid;}* html .ui-tooltip-titlebar .ui-state-default{top:16px;}.ui-tooltip-titlebar .ui-icon,.ui-tooltip-icon .ui-icon{display:block;text-indent:-1000em;}.ui-tooltip-icon,.ui-tooltip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;}.ui-tooltip-icon .ui-icon{width:18px;height:14px;text-align:center;text-indent:0;font:normal bold 10px/13px Tahoma,sans-serif;color:inherit;background:transparent none no-repeat -100em -100em;}/*!Default tooltip style */ .ui-tooltip-default{border-color:#F1D031;background-color:#FFFFA3;color:#555;}.ui-tooltip-default .ui-tooltip-titlebar{background-color:#FFEF93;}.ui-tooltip-default .ui-tooltip-icon{border-color:#CCC;background:#F1F1F1;color:#777;}.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover{border-color:#AAA;color:#111;}/*!Light tooltip style */ .ui-tooltip-light{background-color:white;border-color:#E2E2E2;color:#454545;}.ui-tooltip-light .ui-tooltip-titlebar{background-color:#f1f1f1;}/*!Dark tooltip style */ .ui-tooltip-dark{background-color:#505050;border-color:#303030;color:#f3f3f3;}.ui-tooltip-dark .ui-tooltip-titlebar{background-color:#404040;}.ui-tooltip-dark .ui-tooltip-icon{border-color:#444;}.ui-tooltip-dark .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}/*!Cream tooltip style */ .ui-tooltip-cream{background-color:#FBF7AA;border-color:#F9E98E;color:#A27D35;}.ui-tooltip-cream .ui-tooltip-titlebar{background-color:#F0DE7D;}.ui-tooltip-cream .ui-state-default .ui-tooltip-icon{background-position:-82px 0;}/*!Red tooltip style */ .ui-tooltip-red{background-color:#F78B83;border-color:#D95252;color:#912323;}.ui-tooltip-red .ui-tooltip-titlebar{background-color:#F06D65;}.ui-tooltip-red .ui-state-default .ui-tooltip-icon{background-position:-102px 0;}.ui-tooltip-red .ui-tooltip-icon{border-color:#D95252;}.ui-tooltip-red .ui-tooltip-titlebar .ui-state-hover{border-color:#D95252;}/*!Green tooltip style */ .ui-tooltip-green{background-color:#CAED9E;border-color:#90D93F;color:#3F6219;}.ui-tooltip-green .ui-tooltip-titlebar{background-color:#B0DE78;}.ui-tooltip-green .ui-state-default .ui-tooltip-icon{background-position:-42px 0;}/*!Blue tooltip style */ .ui-tooltip-blue{background-color:#E5F6FE;border-color:#ADD9ED;color:#5E99BD;}.ui-tooltip-blue .ui-tooltip-titlebar{background-color:#D0E9F5;}.ui-tooltip-blue .ui-state-default .ui-tooltip-icon{background-position:-2px 0;}/*!Add shadows to your tooltips in:FF3+,Chrome 2+,Opera 10.6+,IE6+,Safari 2+*/ .ui-tooltip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);}.ui-tooltip-shadow .ui-tooltip-titlebar,.ui-tooltip-shadow .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.Shadow(Color='gray',Direction=135,Strength=3);-ms-filter:"progid:DXImageTransform.Microsoft.Shadow(Color='gray',Direction=135,Strength=3)";_margin-bottom:-3px;.margin-bottom:-3px;}/*!Add rounded corners to your tooltips in:FF3+,Chrome 2+,Opera 10.6+,IE9+,Safari 2+*/ .ui-tooltip-rounded,.ui-tooltip-tipsy,.ui-tooltip-youtube,.ui-tooltip-youtube>div,.ui-tooltip-bootstrap{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;}/*!Youtube tooltip style */ .ui-tooltip-youtube{-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:white;border-color:#CCC;background:transparent;background:rgba(0,0,0,0.85);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000)";}.ui-tooltip-youtube .ui-tooltip-titlebar{background-color:transparent;}.ui-tooltip-youtube .ui-tooltip-icon{border-color:#222;}.ui-tooltip-youtube .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}.ui-tooltip-jtools{background:#232323;background:rgba(0,0,0,0.7);background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333;}.ui-tooltip-jtools .ui-tooltip-titlebar{background-color:transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)";}.ui-tooltip-jtools .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)";}.ui-tooltip-jtools .ui-tooltip-titlebar,.ui-tooltip-jtools .ui-tooltip-content{background:transparent;color:white;border:0 dashed transparent;}.ui-tooltip-jtools .ui-tooltip-icon{border-color:#555;}.ui-tooltip-jtools .ui-tooltip-titlebar .ui-state-hover{border-color:#333;}.ui-tooltip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,0.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,0.4);box-shadow:4px 4px 5px rgba(0,0,0,0.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent;}.ui-tooltip-cluetip .ui-tooltip-titlebar{background-color:#87876A;color:white;border:0 dashed transparent;}.ui-tooltip-cluetip .ui-tooltip-icon{border-color:#808064;}.ui-tooltip-cluetip .ui-tooltip-titlebar .ui-state-hover{border-color:#696952;color:#696952;}.ui-tooltip-tipsy{background:black;background:rgba(0,0,0,.87);color:white;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:bold;line-height:16px;text-shadow:0 1px black;}.ui-tooltip-tipsy .ui-tooltip-titlebar{padding:6px 35px 0 10;background-color:transparent;}.ui-tooltip-tipsy .ui-tooltip-content{padding:6px 10;}.ui-tooltip-tipsy .ui-tooltip-icon{border-color:#222;text-shadow:none;}.ui-tooltip-tipsy .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}.ui-tooltip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:normal;font-family:serif;}.ui-tooltip-tipped .ui-tooltip-titlebar{border-bottom-width:0;color:white;background:#3A79B8;background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";}.ui-tooltip-tipped .ui-tooltip-icon{border:2px solid #285589;background:#285589;}.ui-tooltip-tipped .ui-tooltip-icon .ui-icon{background-color:#FBFBFB;color:#555;}.ui-tooltip-bootstrap{font-size:13px;line-height:18px;color:#333;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.ui-tooltip-bootstrap .ui-tooltip-titlebar{font-size:18px;line-height:22px;border-bottom:1px solid #ccc;background-color:transparent;}.ui-tooltip-bootstrap .ui-tooltip-titlebar .ui-state-default{right:9px;top:49%;border-style:none;}.ui-tooltip-bootstrap .ui-tooltip-icon{background:white;}.ui-tooltip-bootstrap .ui-tooltip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20);}.ui-tooltip-bootstrap .ui-tooltip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40);}.ui-tooltip:not(.ie9haxors) div.ui-tooltip-content,.ui-tooltip:not(.ie9haxors) div.ui-tooltip-titlebar{filter:none;-ms-filter:none;}
\ No newline at end of file
diff --git a/apps/atnotes/img/action.png b/apps/atnotes/img/action.png
new file mode 100644
index 00000000000..5b24257a0e7
Binary files /dev/null and b/apps/atnotes/img/action.png differ
diff --git a/apps/atnotes/img/icon.png b/apps/atnotes/img/icon.png
new file mode 100644
index 00000000000..cce8f39255e
Binary files /dev/null and b/apps/atnotes/img/icon.png differ
diff --git a/apps/atnotes/img/jquery-te-toolbar.png b/apps/atnotes/img/jquery-te-toolbar.png
new file mode 100644
index 00000000000..a330302d9f1
Binary files /dev/null and b/apps/atnotes/img/jquery-te-toolbar.png differ
diff --git a/apps/atnotes/img/save.png b/apps/atnotes/img/save.png
new file mode 100644
index 00000000000..49581985227
Binary files /dev/null and b/apps/atnotes/img/save.png differ
diff --git a/apps/atnotes/js/atnotes.min.js b/apps/atnotes/js/atnotes.min.js
new file mode 100644
index 00000000000..007c53c6fdc
--- /dev/null
+++ b/apps/atnotes/js/atnotes.min.js
@@ -0,0 +1,22 @@
+/**
+* ownCloud - ATNotes plugin
+*
+* @author Xavier Beurois
+* @copyright 2012 Xavier Beurois www.djazz-lab.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 Lesser General Public
+* License along with this library. If not, see .
+*
+*/
+
+function atnotes(){if(typeof atnotes.initialized=="undefined"){atnotes.prototype.A=function(c,g){if(c.val().length!=0){if(c.attr("rel")==0){if($("ul.atnotes-noteslist li.atnotes-elt.active").length!=0){$("ul.atnotes-noteslist li.atnotes-elt.active div.atnotes-elt-title").html(c.val());$("ul.atnotes-noteslist li.atnotes-elt.active div.atnotes-elt-prerender").html(g.html())}else{this.N(1)}}else{$("ul.atnotes-noteslist li.atnotes-elt.active div.atnotes-elt-title").html(c.val());$("ul.atnotes-noteslist li.atnotes-elt.active div.atnotes-elt-state").html("Unsaved");$("ul.atnotes-noteslist li.atnotes-elt.active div.atnotes-elt-date").html(this.B());$("ul.atnotes-noteslist li.atnotes-elt.active div.atnotes-elt-prerender").html(g.html())}$(window).bind("beforeunload",function(){return false})}};atnotes.prototype.B=function(){var j=new Date(),k,b,c;k=j.getDate();if(k<10){k="0"+k}b=parseInt(j.getMonth()+1);if(b<10){b="0"+b}c=j.getFullYear();j=b+"/"+k+"/"+c;return j};atnotes.prototype.C=function(g,e){if(e.val().length==0){e.val("Untitled note")}this.A(e,g)};atnotes.prototype.D=function(){d=$(".atnotes-elt.active");a=this;if(parseInt(d.attr("rel"))>0){$.ajax({type:"POST",dataType:"json",url:OC.linkTo("atnotes","ajax/delete.php"),data:{i:parseInt(d.attr("rel"))},async:false,success:function(c){if(c.e==0){a.E(d,1)}}})}else{a.E(d,1)}};atnotes.prototype.E=function(g,e){if(e==1){g.remove()}$("#note_title").val("");$("#note_title").attr("rel","0");$("#note_path").val("");$(".jqte_Content").html("");$("#note_title").focus()};atnotes.prototype.F=function(c,g){if(c.val().length==0){c.val("Untitled note");this.A($("#note_title"),g)}$.ajax({type:"POST",dataType:"json",url:OC.linkTo("atnotes","ajax/save.php"),data:{i:c.attr("rel"),t:c.val(),c:g.html()},async:false,error:function(b){alert(b.responseText)},success:function(b){if(!b.e){$("ul.atnotes-noteslist li.atnotes-elt.active div.atnotes-elt-state").empty();$("ul.atnotes-noteslist li.atnotes-elt.active").attr("rel",b.i);$("ul.atnotes-noteslist li.atnotes-elt.active").removeClass("unsaved");c.attr("rel",b.i);$(".atnotes-saved").empty().fadeIn(400);$(".atnotes-saved").css("color","#009700");$(".atnotes-saved").html("Saved !").delay(1000).fadeOut(400);if($("ul.atnotes-noteslist li.atnotes-elt.unsaved").length==0){$(window).unbind("beforeunload")}}}})};atnotes.prototype.G=function(i,c,h){if(h.html().length==0){$(".atnotes-saved").empty().fadeIn(400);$(".atnotes-saved").css("color","#990000");$(".atnotes-saved").html("File not exported: empty content ...").delay(1000).fadeOut(400)}else{$.ajax({type:"POST",dataType:"json",url:OC.linkTo("atnotes","ajax/check.php"),data:{p:i.val(),t:c.val()},async:false,success:function(b){f=true;if(b.e){if(!confirm("The file already exists. Are you sure to override it ?")){f=false}}if(f){$.ajax({type:"POST",dataType:"json",url:OC.linkTo("atnotes","ajax/export.php"),data:{p:b.p,t:c.val(),c:h.html()},async:false,success:function(e){$("#save_dialog").dialog("close")}})}}})}};atnotes.prototype.H=function(c,b){c.css("height",$("#content").height()-b+"px")};atnotes.prototype.I=function(e,g){if(typeof g=="undefined"){g="/"}a=this;e=e.find("div.atnotes-explorer table");$.ajax({type:"POST",dataType:"json",url:OC.linkTo("atnotes","ajax/browse.php"),data:{p:g},async:false,success:function(b){e.empty();$.each(b,function(c,i){e.append(i)});t=e.find("td.filename");t.click(function(c){var i=this;setTimeout(function(){var h=parseInt($(this).data("double"),10);if(h>0){$(this).data("double",h-1)}else{$("#note_path").val($(this).attr("data-rel"))}},300)}).dblclick(function(c){$(this).data("double",2);$("#note_path").val($(this).attr("data-rel"));a.I($("#save_dialog"),$(this).attr("data-rel"))})}})};atnotes.prototype.J=function(c){if(c.val().length==0){c.val("Untitled note")}$("#save_dialog").dialog("open")};atnotes.prototype.K=function(i,c,h){c.val(i.find("div.atnotes-elt-title").text());c.attr("rel",i.attr("rel"));h.html(i.find("div.atnotes-elt-prerender").html())};atnotes.prototype.N=function(c){$(".active").removeClass("active");if(c==1){$("ul.atnotes-noteslist").prepend('
'+$("#note_title").val()+'
Unsaved
'+this.B()+'
'+$(".jqte_Content").html()+"
")}else{$("ul.atnotes-noteslist").prepend('
New note
Unsaved
'+this.B()+'
');this.E(0,0)}$("ul.atnotes-noteslist li.atnotes-elt.active").bind("click",function(){$(".active").removeClass("active");$(this).addClass("active");A.K($(this),$("#note_title"),$(".jqte_Content"))});$(window).bind("beforeunload",function(){return false})};atnotes.prototype.W=function(c,b){c.css("width",$("#content").width()-$(".atnotes-noteslist").width()-b+"px")}}}$(document).ready(function(){A=new atnotes();$(".atnotes-editor").jqte();$(window).resize(function(){A.H($(".atnotes-noteslist"),36);A.W($(".atnotes-notesedit"),0);A.H($(".jqte"),75);A.W($(".jqte"),0);A.H($(".jqte_Content"),122);A.W($(".jqte_Content"),0);$("#note_title").css("width",$(".anotes-notesedit-title").width()-70+"px")});$(window).trigger("resize");$("#save_dialog").dialog({autoOpen:false,height:300,width:550,modal:true,resizable:false,buttons:{Cancel:function(){$("#save_dialog").dialog("close")},Ok:function(){if($("#note_path").val().length==0){$("#note_path").val("/")}A.G($("#note_path"),$("#note_title"),$(".jqte_Content"))}},open:function(g,e){A.I($(this))}});$("#note_title").change(function(){A.A($(this),$(".jqte_Content"))});$("#note_title").keyup(function(){$(this).change()});$(".jqte_Content").keyup(function(){A.C($(this),$("#note_title"))});$(".atnotes-actions-btns img.atnotes-save").bind("click",function(){A.F($("#note_title"),$(".jqte_Content"))});$(".atnotes-elt").bind("click",function(){$(".active").removeClass("active");$(this).addClass("active");A.K($(this),$("#note_title"),$(".jqte_Content"))});$(".atnotes-actions-list").qtip({prerender:true,overwrite:false,content:{text:$(".atnotes-actions-ddmenu").html()},position:{my:"top right",at:"bottom right"},events:{show:function(g,e){$(".atnotes-delete").bind("click",function(){A.D()});$(".atnotes-new").bind("click",function(){A.N(0)})},hide:function(g,e){$(".atnotes-delete").unbind("click");$(".atnotes-new").unbind("click")}},hide:{delay:100,event:"unfocus mouseleave",fixed:true},style:{classes:"ui-tooltip-shadow ui-tooltip-rounded ui-tooltip-youtube",tip:false,width:150}});$("#note_title").focus()});
\ No newline at end of file
diff --git a/apps/atnotes/js/jquery-te-1.0.3.min.js b/apps/atnotes/js/jquery-te-1.0.3.min.js
new file mode 100644
index 00000000000..b94db89e8ef
--- /dev/null
+++ b/apps/atnotes/js/jquery-te-1.0.3.min.js
@@ -0,0 +1,10 @@
+/*!
+ * http://jqueryte.com
+ * jQuery TE 1.0.3
+ * Copyright (C) 2012, Fatih Koca (fatihkoca@me.com), AUTHOR.txt (http://jqueryte.com/about)
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+This program 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 General Public License for more details.
+You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+(function(a){a.fn.jqte=function(b){var c=a.extend({css:"jqte",b:true,i:true,u:true,ol:true,ul:true,sub:true,sup:true,strike:true,remove:true,rule:true},b);var d=a.extend({b:{cls:"bold",command:"Bold",key:"B",tags:["b","strong"]},i:{cls:"italic",command:"Italic",key:"I",tags:["i","em"]},u:{cls:"underline",command:"Underline",key:"U",tags:["u"]},ol:{cls:"orderedlist",command:"insertorderedlist",key:"O",tags:["ol"]},ul:{cls:"unorderedlist",command:"insertunorderedlist",key:"F",tags:["ul"]},sub:{cls:"subscript",command:"subscript",key:"(",tags:["sub"]},sup:{cls:"superscript",command:"superscript",key:"&",tags:["sup"]},strike:{cls:"strike",command:"strikeThrough",key:"S",tags:["strike"]},remove:{cls:"remove",command:"removeformat",key:".",css:false},hr:{cls:"rule",command:"inserthorizontalrule",key:"H",tags:["hr"]}},b);return this.each(function(){function i(a){var b,c,d=navigator.userAgent.toLowerCase();if(window.getSelection){c=window.getSelection();if(c.getRangeAt){b=c.getRangeAt(0)}if(b){c.removeAllRanges();c.addRange(b)}if(!d.match(/msie/))document.execCommand("StyleWithCSS",false,false);document.execCommand(a,false,null)}else if(document.selection&&document.selection.createRange&&document.selection.type!="None"){b=document.selection.createRange();b.execCommand(a,false,null)}}function k(b,c){var d=false,e=j(),f,g;a.each(b,function(b,c){f=e.prop("tagName").toLowerCase();g=e.attr("style");if(f==c)d=true;else{e.parents().each(function(){f=a(this).prop("tagName").toLowerCase();if(f==c)d=true})}});return d}function l(b){if(c.b)k(d.b.tags,d.b.css)?f.find("."+d.b.cls).addClass(h):a("."+d.b.cls).removeClass(h);if(c.i)k(d.i.tags,d.i.css)?f.find("."+d.i.cls).addClass(h):a("."+d.i.cls).removeClass(h);if(c.u)k(d.u.tags,d.u.css)?f.find("."+d.u.cls).addClass(h):a("."+d.u.cls).removeClass(h);if(c.ol)k(d.ol.tags,d.ol.css)?f.find("."+d.ol.cls).addClass(h):a("."+d.ol.cls).removeClass(h);if(c.ul)k(d.ul.tags,d.ul.css)?f.find("."+d.ul.cls).addClass(h):a("."+d.ul.cls).removeClass(h);if(c.sub)k(d.sub.tags,d.sub.css)?f.find("."+d.sub.cls).addClass(h):a("."+d.sub.cls).removeClass(h);if(c.sup)k(d.sup.tags,d.sup.css)?f.find("."+d.sup.cls).addClass(h):a("."+d.sup.cls).removeClass(h);if(c.strike)k(d.strike.tags,d.strike.css)?f.find("."+d.strike.cls).addClass(h):a("."+d.strike.cls).removeClass(h)}function m(){var a=g.html().replace(/
\ No newline at end of file
diff --git a/apps/files_sharing_log/appinfo/database.xml b/apps/files_sharing_log/appinfo/database.xml
index 92e5f0125bd..dae811f87fa 100644
--- a/apps/files_sharing_log/appinfo/database.xml
+++ b/apps/files_sharing_log/appinfo/database.xml
@@ -3,7 +3,7 @@
*dbname*truefalse
- latin1
+ utf8
*dbprefix*sharing_log
diff --git a/apps/files_versions/ajax/expireAll.php b/apps/files_versions/ajax/expireAll.php
index 4f165be0ae9..2a678c7f0a5 100644
--- a/apps/files_versions/ajax/expireAll.php
+++ b/apps/files_versions/ajax/expireAll.php
@@ -27,6 +27,7 @@
// Check user and app status
OCP\JSON::checkLoggedIn();
OCP\App::checkAppEnabled('files_versions');
+OCP\JSON::callCheck();
$versions = new OCA_Versions\Storage();
diff --git a/apps/files_versions/ajax/rollbackVersion.php b/apps/files_versions/ajax/rollbackVersion.php
index 8d1092f8b8e..24d71a914a4 100644
--- a/apps/files_versions/ajax/rollbackVersion.php
+++ b/apps/files_versions/ajax/rollbackVersion.php
@@ -1,6 +1,7 @@
.
+*/
+
+return array(
+ 'list' => array('method' => 'GET', 'class' => 'Storage', 'function' => 'getVersions',
+ 'parameters' => array(
+ 'file' => array('required' => true, 'type' => 'string')
+ )
+ ),
+ 'revert' => array('method' => 'POST', 'class' => 'Storage', 'function' => 'rollback',
+ 'parameters' => array(
+ 'file' => array('required' => true, 'type' => 'string'),
+ 'time' => array('required' => true, 'type' => 'int')
+ )
+ )
+);
+
+?>
\ No newline at end of file
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index 0ce884c3ea0..f146676757d 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -227,13 +227,13 @@ class Storage {
}
$versions = array_reverse( $versions );
-
+
foreach( $versions as $key => $value ) {
// flag the first matched file in array (which will have latest modification date) as current version
- if ( $versions[$key]['fileMatch'] ) {
+ if ( $value['fileMatch'] ) {
- $versions[$key]['cur'] = 1;
+ $value['cur'] = 1;
break;
}
diff --git a/apps/gallery/appinfo/version b/apps/gallery/appinfo/version
index 8f0916f768f..4b9fcbec101 100644
--- a/apps/gallery/appinfo/version
+++ b/apps/gallery/appinfo/version
@@ -1 +1 @@
-0.5.0
+0.5.1
diff --git a/apps/gallery/l10n/de.php b/apps/gallery/l10n/de.php
index 6c3d9fc7389..cd580cf303c 100644
--- a/apps/gallery/l10n/de.php
+++ b/apps/gallery/l10n/de.php
@@ -1,9 +1,9 @@
"Bilder",
-"Settings" => "Einstellungen",
-"Rescan" => "Erneut Scannen",
-"Stop" => "Stopp",
-"Share" => "Teilen",
+"Share gallery" => "Galerie teilen",
+"Error: " => "Fehler:",
+"Internal error" => "Interner Fehler",
+"Slideshow" => "Slideshow",
"Back" => "Zurück",
"Remove confirmation" => "Bestätigung entfernen",
"Do you want to remove album" => "Soll das Album entfernt werden",
diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php
index 39d6d3aded1..701949d4d80 100644
--- a/apps/gallery/lib/album.php
+++ b/apps/gallery/lib/album.php
@@ -58,7 +58,7 @@ class OC_Gallery_Album {
return $stmt->execute($args);
}
- public static function removeByName($owner, $name) { self::remove($ownmer, $name); }
+ public static function removeByName($owner, $name) { self::remove($owner, $name); }
public static function removeByPath($owner, $path) { self::remove($owner, null, $path); }
public static function removeByParentPath($owner, $parent) { self::remove($owner, null, null, $parent); }
diff --git a/apps/gallery/lib/managers.php b/apps/gallery/lib/managers.php
index b6ade3d1b1e..575d962dbe3 100644
--- a/apps/gallery/lib/managers.php
+++ b/apps/gallery/lib/managers.php
@@ -29,7 +29,6 @@ class DatabaseManager {
$stmt = \OCP\DB::prepare('INSERT INTO *PREFIX*pictures_images_cache (uid_owner, path, width, height) VALUES (?, ?, ?, ?)');
$stmt->execute(array(\OCP\USER::getUser(), $path, $width, $height));
$ret = array('path' => $path, 'width' => $width, 'height' => $height);
- unset($image);
$dir = dirname($path);
$this->cache[$dir][$path] = $ret;
return $ret;
diff --git a/apps/gallery/lib/share.php b/apps/gallery/lib/share.php
new file mode 100644
index 00000000000..6f3db45375f
--- /dev/null
+++ b/apps/gallery/lib/share.php
@@ -0,0 +1,42 @@
+.
+*/
+
+class OC_Share_Backend_Photo extends OCP\Share_Backend {
+
+ public $dependsOn = 'file';
+ public $supportedFileExtensions = array('jpg', 'png', 'gif');
+
+ public function getSource($item, $uid) {
+ return array('item' => 'blah.jpg', 'file' => $item);
+ }
+
+ public function generateTarget($item, $uid, $exclude = null) {
+ // TODO Make sure target path doesn't exist already
+ return $item;
+ }
+
+ public function formatItems($items, $format, $parameters = null) {
+
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/gallery/lib/tiles.php b/apps/gallery/lib/tiles.php
index 754734e609e..e36d26d3191 100644
--- a/apps/gallery/lib/tiles.php
+++ b/apps/gallery/lib/tiles.php
@@ -33,7 +33,7 @@ class TilesLine {
}
public function setAvailableSpace($space) {
- $available_space = $space;
+ $this->available_space = $space;
}
public function getTilesCount() {
diff --git a/apps/journal/.gitignore b/apps/journal/.gitignore
new file mode 100644
index 00000000000..e2ff07d14d8
--- /dev/null
+++ b/apps/journal/.gitignore
@@ -0,0 +1,51 @@
+# the default generated dir + db file
+data
+owncloud
+config/config.php
+config/mount.php
+apps/inc.php
+
+# just sane ignores
+.*.sw[po]
+*.bak
+*.BAK
+*~
+*.orig
+*.class
+.cvsignore
+Thumbs.db
+*.py[co]
+_darcs/*
+CVS/*
+.svn/*
+RCS/*
+
+# kdevelop
+.kdev
+*.kdev4
+
+# Lokalize
+*lokalize*
+
+# eclipse
+.project
+.settings
+
+# netbeans
+nbproject
+
+# geany
+*.geany
+
+# Cloud9IDE
+.settings.xml
+
+# vim ex mode
+.vimrc
+
+# Mac OS
+.DS_Store
+
+# WebFinger
+.well-known
+/.buildpath
diff --git a/apps/journal/COPYING-AGPL b/apps/journal/COPYING-AGPL
new file mode 100644
index 00000000000..dba13ed2ddf
--- /dev/null
+++ b/apps/journal/COPYING-AGPL
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/apps/journal/COPYING-README b/apps/journal/COPYING-README
new file mode 100644
index 00000000000..18f06caa3ec
--- /dev/null
+++ b/apps/journal/COPYING-README
@@ -0,0 +1,10 @@
+Files in TAL Templating System for ownCloud are licensed under the Affero General Public License version 3,
+the text of which can be found in COPYING-AGPL, or any later version of the AGPL,
+unless otherwise noted.
+
+Licensing of components:
+* PHPTAL - http://phptal.org/ : LGPL
+
+All unmodified files from these and other sources retain their original copyright
+and license notices: see the relevant individual files.
+
diff --git a/apps/journal/Changelog b/apps/journal/Changelog
new file mode 100644
index 00000000000..08b20831bed
--- /dev/null
+++ b/apps/journal/Changelog
@@ -0,0 +1,5 @@
+# Changelog
+
+### 0.1
+
+- First release.
\ No newline at end of file
diff --git a/apps/journal/README.md b/apps/journal/README.md
new file mode 100644
index 00000000000..7c06e4f0389
--- /dev/null
+++ b/apps/journal/README.md
@@ -0,0 +1,38 @@
+# Journal/Notes app for ownCloud
+
+## Features
+
+- Saves notes/journal entries as VJOURNAL records in the ownCloud Calendar.
+
+- Integrates with ownClouds search backend.
+
+- Sort entries by date/time ascending/descending or summary ascending/descending.
+
+- Plain text or rich text editing (rich text editing is still buggy and immature).
+
+- Syncs with KDEPIMs Journal part.
+
+- Completed tasks from the Task app can be automatically added as journal entries.
+
+- Stores entry data as json objects in each element for quich access and to minimize ajax calls.
+
+To install this app you will first have to install the [TAL Page Templates for ownCloud](/tanghus/tal#readme) app.
+
+## Installation from git
+
+1. Go to your ownCloud apps dir and clone the repo there:
+
+ cd owncloud/apps
+ git clone git://github.com/tanghus/journal.git
+
+2. From your browser go to the ownCloud apps page (`/settings/apps.php`) and enable the Journal app.
+
+3. After a page refresh you should see the Journal app in the main menu.
+
+
+## DISCLAIMER
+
+There's no garantee this app won't eat your data, chew it up and spit it out. It works directly on the calendar app data
+though not touching anything but VJOURNAL entries. [Always backup!](http://tanghus.net/2012/04/backup-owncloud-calendar-and-contacts/)
+
+Please report any issues at the [github issue tracker](https://github.com/tanghus/journal/issues)
\ No newline at end of file
diff --git a/apps/journal/TODO b/apps/journal/TODO
new file mode 100644
index 00000000000..28271365813
--- /dev/null
+++ b/apps/journal/TODO
@@ -0,0 +1,13 @@
+
+- Fix categories.
+
+- Hide empty elements?
+
+- Check if entered text in one mode gets transfered when changing modes.
+
+- Make edit area fit size and scrollable.
+
+- Add an "only read from this calendar" checkbox.
+
+- Test, test, test in other browser as well.
+
diff --git a/apps/journal/ajax/categories/categoriesfor.php b/apps/journal/ajax/categories/categoriesfor.php
new file mode 100644
index 00000000000..846af300de8
--- /dev/null
+++ b/apps/journal/ajax/categories/categoriesfor.php
@@ -0,0 +1,28 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('contacts');
+
+$id = isset($_GET['id'])?$_GET['id']:null;
+if(is_null($id)) {
+ OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('No ID provided'))));
+ exit();
+}
+$vcard = OC_Contacts_App::getContactVCard( $id );
+foreach($vcard->children as $property){
+ //OCP\Util::writeLog('contacts','ajax/categories/checksumfor.php: '.$property->name, OCP\Util::DEBUG);
+ if($property->name == 'CATEGORIES') {
+ $checksum = md5($property->serialize());
+ OCP\JSON::success(array('data' => array('value'=>$property->value, 'checksum'=>$checksum)));
+ exit();
+ }
+}
+OCP\JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error setting checksum.'))));
+?>
diff --git a/apps/journal/ajax/categories/delete.php b/apps/journal/ajax/categories/delete.php
new file mode 100644
index 00000000000..e68d608c5da
--- /dev/null
+++ b/apps/journal/ajax/categories/delete.php
@@ -0,0 +1,54 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('contacts');
+OCP\JSON::callCheck();
+
+foreach ($_POST as $key=>$element) {
+ debug('_POST: '.$key.'=>'.print_r($element, true));
+}
+
+require_once('../util.php');
+
+$categories = isset($_POST['categories'])?$_POST['categories']:null;
+
+if(is_null($categories)) {
+ bailOut(OC_Contacts_App::$l10n->t('No categories selected for deletion.'));
+}
+
+debug(print_r($categories, true));
+
+$addressbooks = OC_Contacts_Addressbook::all(OCP\USER::getUser());
+if(count($addressbooks) == 0) {
+ bailOut(OC_Contacts_App::$l10n->t('No address books found.'));
+}
+$addressbookids = array();
+foreach($addressbooks as $addressbook) {
+ $addressbookids[] = $addressbook['id'];
+}
+$contacts = OC_Contacts_VCard::all($addressbookids);
+if(count($contacts) == 0) {
+ bailOut(OC_Contacts_App::$l10n->t('No contacts found.'));
+}
+
+$cards = array();
+foreach($contacts as $contact) {
+ $cards[] = array($contact['id'], $contact['carddata']);
+}
+
+debug('Before delete: '.print_r($categories, true));
+
+$catman = new OC_VCategories('contacts');
+$catman->delete($categories, $cards);
+debug('After delete: '.print_r($catman->categories(), true));
+OC_Contacts_VCard::updateDataByID($cards);
+OCP\JSON::success(array('data' => array('categories'=>$catman->categories())));
+
+?>
diff --git a/apps/journal/ajax/categories/list.php b/apps/journal/ajax/categories/list.php
new file mode 100644
index 00000000000..ac8976fa0ec
--- /dev/null
+++ b/apps/journal/ajax/categories/list.php
@@ -0,0 +1,17 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('journal');
+
+$categories = OC_Journal_App::getCategories();
+
+OCP\JSON::success(array('data' => array('categories'=>$categories)));
+
+?>
diff --git a/apps/journal/ajax/categories/rescan.php b/apps/journal/ajax/categories/rescan.php
new file mode 100644
index 00000000000..d746f9179fe
--- /dev/null
+++ b/apps/journal/ajax/categories/rescan.php
@@ -0,0 +1,42 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('journal');
+
+foreach ($_POST as $key=>$element) {
+ debug('_POST: '.$key.'=>'.print_r($element, true));
+}
+
+function bailOut($msg) {
+ OCP\JSON::error(array('data' => array('message' => $msg)));
+ OCP\Util::writeLog('journal','ajax/categories/rescan.php: '.$msg, OCP\Util::DEBUG);
+ exit();
+}
+function debug($msg) {
+ OCP\Util::writeLog('journal','ajax/categories/rescan.php: '.$msg, OCP\Util::DEBUG);
+}
+
+$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser());
+if(count($calendars) == 0) {
+ bailOut(OC_Calendar_App::$l10n->t('No calendars found.'));
+}
+$events = array();
+foreach($calendars as $calendar) {
+ $calendar_events = OC_Calendar_Object::all($calendar['id']);
+ $events = $events + $calendar_events;
+}
+if(count($events) == 0) {
+ bailOut(OC_Calendar_App::$l10n->t('No events found.'));
+}
+
+OC_Calendar_App::scanCategories($events);
+$categories = OC_Calendar_App::getCategoryOptions();
+
+OCP\JSON::success(array('data' => array('categories'=>$categories)));
\ No newline at end of file
diff --git a/apps/journal/ajax/delete.php b/apps/journal/ajax/delete.php
new file mode 100644
index 00000000000..4013e4440a6
--- /dev/null
+++ b/apps/journal/ajax/delete.php
@@ -0,0 +1,37 @@
+
+ *
+ * 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 .
+ *
+ */
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('journal');
+OCP\JSON::callCheck();
+
+$id = isset($_POST['id'])?$_POST['id']:null;
+if(is_null($id)) {
+ OCP\JSON::error(array('data'=>array('message' => OC_Journal_App::$l10n->t('ID is not set!'))));
+ exit;
+}
+$journal = OC_Calendar_App::getEventObject($id);
+if($journal) {
+ OC_Calendar_Object::delete($id);
+ OCP\JSON::success(array('data' => array( 'id' => $id )));
+} else {
+ OCP\JSON::error(array('data' => array('id' => $id, 'message' => OC_Journal_App::$l10n->t('Could not find journal entry: '.$id))));
+}
\ No newline at end of file
diff --git a/apps/journal/ajax/entries.php b/apps/journal/ajax/entries.php
new file mode 100644
index 00000000000..03f2366d33b
--- /dev/null
+++ b/apps/journal/ajax/entries.php
@@ -0,0 +1,36 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+// Init owncloud
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('journal');
+
+$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
+$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+session_write_close();
+$journals = array();
+foreach( $calendars as $calendar ){
+ $calendar_journals = OC_Calendar_Object::all($calendar['id']);
+ foreach( $calendar_journals as $journal ) {
+ if($journal['objecttype']!='VJOURNAL') {
+ continue;
+ }
+ if(is_null($journal['summary'])) {
+ continue;
+ }
+ $object = OC_VObject::parse($journal['calendardata']);
+ $vjournalobj = $object->VJOURNAL;
+ try {
+ $journals[] = OC_Journal_App::arrayForJSON($journal['id'], $vjournalobj, $user_timezone);
+ } catch(Exception $e) {
+ OCP\Util::writeLog('journal', 'ajax/getentries.php. id: '.$journal['id'].' '.$e->getMessage(), OCP\Util::ERROR);
+ }
+ }
+}
+
+OCP\JSON::success(array('data' => array('entries' => $journals)));
diff --git a/apps/journal/ajax/saveproperty.php b/apps/journal/ajax/saveproperty.php
new file mode 100644
index 00000000000..016f922a9af
--- /dev/null
+++ b/apps/journal/ajax/saveproperty.php
@@ -0,0 +1,138 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+$htmlwrap = '%s';
+$divwrap = '
%s
';
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('journal');
+OCP\JSON::callCheck();
+
+$id = isset($_POST['id'])?$_POST['id']:null;
+$property = isset($_POST['type'])?$_POST['type']:null;
+$value = isset($_POST['value'])?$_POST['value']:null;
+if(is_null($id)) {
+ OCP\JSON::error(array('data'=>array('message' => OC_Journal_App::$l10n->t('ID is not set!'))));
+ exit;
+}
+if(is_null($property)) {
+ OCP\JSON::error(array('data'=>array('message' => OC_Journal_App::$l10n->t('Property name is not set!'))));
+ exit;
+}
+if(is_null($value)) {
+ OCP\JSON::error(array('data'=>array('message' => OC_Journal_App::$l10n->t('Property value is not set!'))));
+ exit;
+}
+
+foreach($_POST as $key => $val) {
+ error_log($key.': '.print_r($val, true));
+}
+
+$parameters = isset($_POST['parameters'])? $_POST['parameters']:null;
+if($id == 'new') {
+ $vcalendar = OC_Journal_App::createVCalendar();
+} else {
+ $vcalendar = OC_Calendar_App::getVCalendar( $id );
+}
+error_log('saveproperty: '.$property.': '.print_r($value, true));
+$vjournal = $vcalendar->VJOURNAL;
+switch($property) {
+ case 'DESCRIPTION':
+ $hasgenericformat = false;
+ $haskdeformat = false;
+ if(!$vjournal->DESCRIPTION) {
+ $vjournal->setString('DESCRIPTION', $value);
+ }
+ if($parameters && isset($parameters['FORMAT']) && strtoupper($parameters['FORMAT']) == 'HTML') {
+ if($value[0] != '<') { // Fugly hack coming up
+ $value = sprintf($divwrap, $value);
+ }
+ $vjournal->DESCRIPTION->value = sprintf($htmlwrap, $value);
+ foreach($vjournal->DESCRIPTION->parameters as $parameter){
+ if(stripos($parameter->name, 'X-KDE-TEXTFORMAT') !== false && stripos($parameter->value, 'HTML') !== false){
+ $haskdeformat = true;
+ }
+ if(stripos($parameter->name, 'X-TEXTFORMAT') !== false && stripos($parameter->value, 'HTML') !== false){
+ $hasgenericformat = true;
+ }
+ }
+ if(!$haskdeformat) {
+ try {
+ $vjournal->DESCRIPTION->add(new Sabre_VObject_Parameter('X-KDE-TEXTFORMAT', 'HTML'));
+ } catch (Exception $e) {
+ OCP\JSON::error(array('data'=>array('message'=>OC_Journal_App::$l10n->t('Error setting rich text format parameter: '.$e->getMessage()))));
+ exit();
+ }
+ }
+ if(!$hasgenericformat) { // Add a more generic text formatting parameter in case other clients would use VJOURNAL this way.
+ try {
+ $vjournal->DESCRIPTION->add(new Sabre_VObject_Parameter('X-TEXTFORMAT', 'HTML'));
+ } catch (Exception $e) {
+ OCP\JSON::error(array('data'=>array('message'=>OC_Journal_App::$l10n->t('Error setting rich text format parameter: '.$e->getMessage()))));
+ exit();
+ }
+ }
+ } else {
+ $vjournal->DESCRIPTION->value = $value;
+ }
+ break;
+ case 'DTSTART':
+ try {
+ $date_only = isset($_POST['date_only']) && (bool)$_POST['date_only'] == true?true:false;
+ $timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+ $timezone = new DateTimeZone($timezone);
+ //$dtstart = new DateTime($value, $timezone);
+ $dtstart = new DateTime('@'.$value);
+ $dtstart->setTimezone($timezone);
+ $type = Sabre_VObject_Property_DateTime::LOCALTZ;
+ if ($date_only) {
+ $type = Sabre_VObject_Property_DateTime::DATE;
+ }
+ $vjournal->setDateTime('DTSTART', $value, $type);
+ } catch (Exception $e) {
+ OCP\JSON::error(array('data'=>array('message'=>OC_Journal_App::$l10n->t('Invalid date/time: '.$e->getMessage()))));
+ exit();
+ }
+ break;
+ case 'ORGANIZER':
+ case 'SUMMARY':
+ case 'CATEGORIES':
+ $vobject = $vjournal->getVObject();
+ if(isset($vobject[$property])) {
+ $vobject[$property]['value'] = $value;
+ } else {
+ $vjournal->setString($property, $value);
+ }
+ break;
+ $vjournal->setString($property, $value);
+ break;
+ default:
+ OCP\JSON::error(array('data'=>array('message'=>'Unknown type: '.$property)));
+ exit();
+}
+
+$vjournal->setDateTime('LAST-MODIFIED', 'now', Sabre_VObject_Property_DateTime::UTC);
+$vjournal->setDateTime('DTSTAMP', 'now', Sabre_VObject_Property_DateTime::UTC);
+
+if($id == 'new') {
+ // TODO: Have a calendar ID parameter in request.
+ $cid = OCP\Config::getUserValue(OCP\User::getUser(), 'journal', 'default_calendar', null);
+ // Check that the calendar exists and that it's ours.
+ $cid = OC_Calendar_App::getCalendar($cid, true);
+ if(!$cid) {
+ OCP\Util::writeLog('journal', 'The default calendar '.$cid.' is either not owned by '.OCP\User::getUser().' or doesn\'t exist.', OCP\Util::WARN);
+ $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
+ $first_calendar = reset($calendars);
+ $cid = $first_calendar['id'];
+ }
+ $id = OC_Calendar_Object::add($cid, $vcalendar->serialize());
+} else {
+ OC_Calendar_Object::edit($id, $vcalendar->serialize());
+}
+$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+$journal_info = OC_Journal_App::arrayForJSON($id, $vjournal, $user_timezone);
+OCP\JSON::success(array('data' => $journal_info));
diff --git a/apps/contacts/ajax/loadphoto.php b/apps/journal/ajax/setdefaultcalendar.php
similarity index 58%
rename from apps/contacts/ajax/loadphoto.php
rename to apps/journal/ajax/setdefaultcalendar.php
index a35631055eb..a82c1e3cf1f 100644
--- a/apps/contacts/ajax/loadphoto.php
+++ b/apps/journal/ajax/setdefaultcalendar.php
@@ -2,7 +2,6 @@
/**
* ownCloud - Addressbook
*
- * @author Thomas Tanghus
* @copyright 2012 Thomas Tanghus
*
* This library is free software; you can redistribute it and/or
@@ -19,28 +18,19 @@
* License along with this library. If not, see .
*
*/
-
-// Check if we are a user
+
OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('contacts');
+OCP\JSON::checkAppEnabled('journal');
+OCP\JSON::callCheck();
-require_once('loghandler.php');
-
-$id = isset($_GET['id']) ? $_GET['id'] : '';
-$refresh = isset($_GET['refresh']) ? true : false;
-
-if($id == '') {
- bailOut(OC_Contacts_App::$l10n->t('Missing contact id.'));
+$id = isset($_POST['id'])?$_POST['id']:null;
+if(is_null($id)) {
+ OCP\JSON::error(array('data'=>array('message' => OC_Journal_App::$l10n->t('ID is not set!'))));
+ exit;
}
-
-$checksum = '';
-$vcard = OC_Contacts_App::getContactVCard( $id );
-foreach($vcard->children as $property){
- if($property->name == 'PHOTO') {
- $checksum = md5($property->serialize());
- break;
- }
-}
-
-OCP\JSON::success(array('data' => array('checksum'=>$checksum)));
-
+if(OCP\Config::setUserValue(OCP\USER::getUser(), 'journal', 'default_calendar', $id)) {
+ OC_Calendar_Object::delete($id);
+ OCP\JSON::success(array('data' => array( 'id' => $id )));
+} else {
+ OCP\JSON::error(array('data' => array('id' => $id, 'message' => OC_Journal_App::$l10n->t('Could not set default calendar: '.$id))));
+}
\ No newline at end of file
diff --git a/apps/journal/ajax/util.php b/apps/journal/ajax/util.php
new file mode 100644
index 00000000000..b690b2e1948
--- /dev/null
+++ b/apps/journal/ajax/util.php
@@ -0,0 +1,40 @@
+
+ *
+ * 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 .
+ *
+ */
+
+function bailOut($msg, $tracelevel=1, $debuglevel=OCP\Util::ERROR) {
+ OCP\JSON::error(array('data' => array('message' => $msg)));
+ debug($msg, $tracelevel, $debuglevel);
+ exit();
+}
+
+function debug($msg, $tracelevel=0, $debuglevel=OCP\Util::DEBUG) {
+ if(PHP_VERSION >= "5.4") {
+ $call = debug_backtrace(false, $tracelevel+1);
+ } else {
+ $call = debug_backtrace(false);
+ }
+ error_log('trace: '.print_r($call, true));
+ $call = $call[$tracelevel];
+ if($debuglevel !== false) {
+ OCP\Util::writeLog('journal', $call['file'].'. Line: '.$call['line'].': '.$msg, $debuglevel);
+ }
+}
diff --git a/apps/journal/appinfo/app.php b/apps/journal/appinfo/app.php
new file mode 100644
index 00000000000..5f33d40399f
--- /dev/null
+++ b/apps/journal/appinfo/app.php
@@ -0,0 +1,18 @@
+ 'journal_index',
+ 'order' => 11,
+ 'href' => OCP\Util::linkTo( 'journal', 'index.php' ),
+ 'icon' => OCP\Util::imagePath( 'journal', 'journal.png' ),
+ 'name' => $l->t('Journal')));
+
+OC_Search::registerProvider('OC_Search_Provider_Journal');
+OCP\App::registerPersonal('journal','settings');
\ No newline at end of file
diff --git a/apps/journal/appinfo/info.xml b/apps/journal/appinfo/info.xml
new file mode 100644
index 00000000000..426e38eb9f1
--- /dev/null
+++ b/apps/journal/appinfo/info.xml
@@ -0,0 +1,11 @@
+
+
+ journal
+ Journal
+ 0.1
+ AGPL
+ Thomas Tanghus
+ 5
+ true
+ Journal view from calendar
+
diff --git a/apps/journal/css/journal.css b/apps/journal/css/journal.css
new file mode 100644
index 00000000000..ac2ae74c65a
--- /dev/null
+++ b/apps/journal/css/journal.css
@@ -0,0 +1,19 @@
+#leftcontent a { font-weight: bold; }
+#metadata { position: fixed; background-color: #ccc; width: 20em; right: 0; top: 6.5em; bottom: 0; overflow: auto; padding: 1em; margin: 0; font-size: 1em; font-weight: bold; }
+#metadata :disabled.property { background-color: #ccc; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
+#entry { position: fixed; background: #fff; top: 6.5em; bottom: 0; left: 32.5em !important; right: 22em; padding: 1em; margin: 0; }
+div.propertycontainer[data-type="DESCRIPTION"] { height: 90%; }
+#description { width: 95%; height: 90%; }
+.rte-content { position: absolute; left: 1em; right: 1em; top: 7em; bottom: 1em; overflow: auto; }
+#summary { width: 95%; font-size: 1.4em; font-weight: bold; height: 1.6em; }
+.editable,.content { font-size: 1.2em; background-color: #fff !important; border: 1px solid #fff !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; cursor: text; }
+.editable:hover,.editable:focus,.editable:hover:active { border: 1px solid #1d2d44 !important; }
+#categories,#organizer,#location,#link { width: 18em; clear: right; }
+#dtstartdate { width: 10em; }
+#dtstarttime { width: 5em; }
+.loading { background: url('%webroot%/core/img/loading.gif') no-repeat center !important; cursor: wait; }
+.action { padding-left: 0.5em; }
+label,dt { color: #aaa; }
+label:hover, dt:hover { color: #333; }
+.required { color: red; border: 3px solid red; }
+input { font-size: 1em; font-weight: bold; }
\ No newline at end of file
diff --git a/apps/journal/css/rte.css b/apps/journal/css/rte.css
new file mode 100644
index 00000000000..e786599002a
--- /dev/null
+++ b/apps/journal/css/rte.css
@@ -0,0 +1,7 @@
+.rte-toolbar { margin: 0; width:100%; height:22px; padding:0; list-style-type:none; }
+.rte-toolbar li button, .rte-toolbar li a { float:left; cursor:pointer; opacity: 0.6; padding:0; margin: 0; border: 0; border-radius: 0; outline: 0; background: inherit; }
+.rte-toolbar li button:hover { opacity: 0.8; }
+.rte-content { cursor:text;position:absolute; border-color:#000;border-style:solid;white-space: pre-wrap;word-wrap:break-word; padding: 0.2em; }
+.rte-content ul { list-style: circle inside; }
+.rte-content li { padding-left: 1em; }
+
diff --git a/apps/journal/img/bold.svg b/apps/journal/img/bold.svg
new file mode 100644
index 00000000000..2b06228258a
--- /dev/null
+++ b/apps/journal/img/bold.svg
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/apps/journal/img/indent-less.svg b/apps/journal/img/indent-less.svg
new file mode 100644
index 00000000000..246089c293c
--- /dev/null
+++ b/apps/journal/img/indent-less.svg
@@ -0,0 +1,300 @@
+
+
+
diff --git a/apps/journal/img/indent-more.svg b/apps/journal/img/indent-more.svg
new file mode 100644
index 00000000000..4e137cf4528
--- /dev/null
+++ b/apps/journal/img/indent-more.svg
@@ -0,0 +1,295 @@
+
+
+
diff --git a/apps/journal/img/italic.svg b/apps/journal/img/italic.svg
new file mode 100644
index 00000000000..1eeb4fc1321
--- /dev/null
+++ b/apps/journal/img/italic.svg
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/apps/journal/img/journal.png b/apps/journal/img/journal.png
new file mode 100644
index 00000000000..f95dd8e9f3a
Binary files /dev/null and b/apps/journal/img/journal.png differ
diff --git a/apps/journal/img/journal.svg b/apps/journal/img/journal.svg
new file mode 100644
index 00000000000..2ee85ef0699
--- /dev/null
+++ b/apps/journal/img/journal.svg
@@ -0,0 +1,104 @@
+
+
+
+
diff --git a/apps/journal/img/justify-center.svg b/apps/journal/img/justify-center.svg
new file mode 100644
index 00000000000..7a3a7ffb97a
--- /dev/null
+++ b/apps/journal/img/justify-center.svg
@@ -0,0 +1,269 @@
+
+
+
diff --git a/apps/journal/img/justify-fill.svg b/apps/journal/img/justify-fill.svg
new file mode 100644
index 00000000000..f9a530bdae1
--- /dev/null
+++ b/apps/journal/img/justify-fill.svg
@@ -0,0 +1,269 @@
+
+
+
diff --git a/apps/journal/img/justify-left.svg b/apps/journal/img/justify-left.svg
new file mode 100644
index 00000000000..ed5bab46516
--- /dev/null
+++ b/apps/journal/img/justify-left.svg
@@ -0,0 +1,199 @@
+
+
+
diff --git a/apps/journal/img/justify-right.svg b/apps/journal/img/justify-right.svg
new file mode 100644
index 00000000000..affb129539f
--- /dev/null
+++ b/apps/journal/img/justify-right.svg
@@ -0,0 +1,269 @@
+
+
+
diff --git a/apps/journal/img/list.svg b/apps/journal/img/list.svg
new file mode 100644
index 00000000000..b253c0ab50d
--- /dev/null
+++ b/apps/journal/img/list.svg
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/apps/journal/img/strikethrough.svg b/apps/journal/img/strikethrough.svg
new file mode 100644
index 00000000000..528907cebe1
--- /dev/null
+++ b/apps/journal/img/strikethrough.svg
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/apps/journal/img/underline.svg b/apps/journal/img/underline.svg
new file mode 100644
index 00000000000..534bed6ea24
--- /dev/null
+++ b/apps/journal/img/underline.svg
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/apps/journal/index.php b/apps/journal/index.php
new file mode 100644
index 00000000000..bfe882376e3
--- /dev/null
+++ b/apps/journal/index.php
@@ -0,0 +1,72 @@
+
+ *
+ * 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 .
+ *
+ */
+$errors = array();
+$l = new OC_L10N('journal');
+OCP\User::checkLoggedIn();
+
+$required_apps = array(
+ array('id' => 'tal', 'name' => 'TAL Page Templates'),
+ array('id' => 'journal', 'name' => 'Journal'),
+ array('id' => 'contacts', 'name' => 'Contacts'),
+);
+foreach($required_apps as $app) {
+ if(!OCP\App::isEnabled($app['id'])) {
+ $error = (string)$l->t('The %%s app isn\'t enabled! Please enable it here: Enable %%s app');
+ $errors[] = sprintf($error, $app['name'],OCP\Util::linkTo('settings', 'apps'), $app['id'], $app['name']);
+ }
+}
+
+if(count($errors) == 0) {
+ $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
+ if( count($calendars) == 0 ) {
+ $error = (string)$l->t('You have no calendars. Please add one at the Calendar app');
+ $errors[] = sprintf($error, OCP\Util::linkTo('calendar', 'index.php'));
+ }
+ // Load a specific entry?
+ $id = isset( $_GET['id'] ) ? $_GET['id'] : null;
+
+ OCP\Util::addScript('3rdparty/timepicker', 'jquery.ui.timepicker');
+ OCP\Util::addScript('contacts','jquery.multi-autocomplete');
+ OCP\Util::addScript('','oc-vcategories');
+ OCP\Util::addScript('journal', 'jquery.rte');
+ OCP\Util::addScript('journal', 'jquery.textchange');
+ OCP\Util::addScript('journal', 'journal');
+ OCP\Util::addscript('tal','modernizr');
+ OCP\Util::addStyle('3rdparty/timepicker', 'jquery.ui.timepicker');
+ OCP\Util::addStyle('journal', 'rte');
+ OCP\Util::addStyle('journal', 'journal');
+ OCP\App::setActiveNavigationEntry('journal_index');
+
+ $categories = OC_Calendar_App::getCategoryOptions();
+}
+
+//$tmpl = new OCP\Template('journal', 'journals', 'user');
+if($errors) {
+ $tmpl = new OCP\Template( "journal", "rtfm", "user" );
+ $tmpl->assign('errors',$errors, false);
+} else {
+ $tmpl = new OC_TALTemplate('journal', 'index', 'user');
+ $tmpl->assign('categories', $categories);
+ $tmpl->assign('calendars', $calendars);
+ $tmpl->assign('id',$id);
+}
+$tmpl->printPage();
diff --git a/apps/journal/js/expanding.js b/apps/journal/js/expanding.js
new file mode 100644
index 00000000000..17139f27fff
--- /dev/null
+++ b/apps/journal/js/expanding.js
@@ -0,0 +1,118 @@
+// Expanding Textareas
+// https://github.com/bgrins/ExpandingTextareas
+
+(function(factory) {
+ // Add jQuery via AMD registration or browser globals
+ if (typeof define === 'function' && define.amd) {
+ define([ 'jquery' ], factory);
+ }
+ else {
+ factory(jQuery);
+ }
+}(function ($) {
+ $.expandingTextarea = $.extend({
+ autoInitialize: true,
+ initialSelector: "textarea.expanding",
+ opts: {
+ resize: function() { }
+ }
+ }, $.expandingTextarea || {});
+
+ var cloneCSSProperties = [
+ 'lineHeight', 'textDecoration', 'letterSpacing',
+ 'fontSize', 'fontFamily', 'fontStyle',
+ 'fontWeight', 'textTransform', 'textAlign',
+ 'direction', 'wordSpacing', 'fontSizeAdjust',
+ 'wordWrap',
+ 'borderLeftWidth', 'borderRightWidth',
+ 'borderTopWidth','borderBottomWidth',
+ 'paddingLeft', 'paddingRight',
+ 'paddingTop','paddingBottom',
+ 'marginLeft', 'marginRight',
+ 'marginTop','marginBottom',
+ 'boxSizing', 'webkitBoxSizing', 'mozBoxSizing', 'msBoxSizing'
+ ];
+
+ var textareaCSS = {
+ position: "absolute",
+ height: "100%",
+ resize: "none"
+ };
+
+ var preCSS = {
+ visibility: "hidden",
+ border: "0 solid",
+ whiteSpace: "pre-wrap"
+ };
+
+ var containerCSS = {
+ position: "relative"
+ };
+
+ function resize() {
+ $(this).closest('.expandingText').find("div").text(this.value + ' ');
+ $(this).trigger("resize.expanding");
+ }
+
+ $.fn.expandingTextarea = function(o) {
+
+ var opts = $.extend({ }, $.expandingTextarea.opts, o);
+
+ if (o === "resize") {
+ return this.trigger("input.expanding");
+ }
+
+ if (o === "destroy") {
+ this.filter(".expanding-init").each(function() {
+ var textarea = $(this).removeClass('expanding-init');
+ var container = textarea.closest('.expandingText');
+
+ container.before(textarea).remove();
+ textarea
+ .attr('style', textarea.data('expanding-styles') || '')
+ .removeData('expanding-styles');
+ });
+
+ return this;
+ }
+
+ this.filter("textarea").not(".expanding-init").addClass("expanding-init").each(function() {
+ var textarea = $(this);
+
+ textarea.wrap("");
+ textarea.after("
");
+
+ var container = textarea.parent().css(containerCSS);
+ var pre = container.find("pre").css(preCSS);
+
+ // Store the original styles in case of destroying.
+ textarea.data('expanding-styles', textarea.attr('style'));
+ textarea.css(textareaCSS);
+
+ $.each(cloneCSSProperties, function(i, p) {
+ var val = textarea.css(p);
+
+ // Only set if different to prevent overriding percentage css values.
+ if (pre.css(p) !== val) {
+ pre.css(p, val);
+ }
+ });
+
+ textarea.bind("input.expanding propertychange.expanding", resize);
+ resize.apply(this);
+
+ if (opts.resize) {
+ textarea.bind("resize.expanding", opts.resize);
+ }
+ });
+
+ return this;
+ };
+
+ $(function () {
+ if ($.expandingTextarea.autoInitialize) {
+ $($.expandingTextarea.initialSelector).expandingTextarea();
+ }
+ });
+
+}));
diff --git a/apps/journal/js/journal.js b/apps/journal/js/journal.js
new file mode 100644
index 00000000000..2611b09c550
--- /dev/null
+++ b/apps/journal/js/journal.js
@@ -0,0 +1,432 @@
+String.prototype.unEscape = function(){
+ str = this;
+ return str.replace(/\\"/g, '"');
+};
+String.prototype.stripTags = function(){
+ tags = this;
+ stripped = tags.replace(/<(.|\n)*?>/g, '');
+ return stripped;
+};
+String.prototype.zeroPad = function(digits) {
+ n = this.toString();
+ while (n.length < digits) {
+ n = '0' + n;
+ }
+ return n;
+}
+
+OC.Journal = {
+ init:function() {
+ this.setEnabled(false);
+ // Fetch journal entries. If it's a direct link 'id' will be loaded.
+ OC.Journal.Journals.update(id);
+ },
+ categoriesChanged:function(newcategories) { // Categories added/deleted.
+ categories = $.map(newcategories, function(v) {return v;});
+ $('#categories').multiple_autocomplete('option', 'source', categories);
+ var categorylist = $('#categories_value').find('input');
+ $.getJSON(OC.filePath('journal', 'ajax', 'categories/categoriesfor.php'),{'id':Contacts.UI.Card.id},function(jsondata){
+ if(jsondata.status == 'success'){
+ $('#categories_value').data('checksum', jsondata.data.checksum);
+ categorylist.val(jsondata.data.value);
+ } else {
+ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+ }
+ });
+ },
+ propertyContainerFor:function(obj) {
+ if($(obj).hasClass('propertycontainer')) {
+ return $(obj);
+ }
+ return $(obj).parents('.propertycontainer').first();
+ },
+ required:function(event){ // eventhandler for required elements
+ // FIXME: This doesn't seem to work.
+ console.log('blur on required');
+ var obj = $(event.target);
+ $(obj).addClass('required');
+ if($(this).val().trim() == '') {
+ $('This field is required!').appendTo($(obj));
+ return;
+ } else {
+ $(obj).removeClass('required');
+ $(obj).off('blur', OC.Journal.required);
+ }
+ },
+ setEnabled:function(state) {
+ if(state == undefined) { state = true; }
+ console.log('OC.Journal.setEnabled: ' + state);
+ if(state) {
+ $('#description').rte('setEnabled', true);
+ if($('#description').rte('mode') == 'html') {
+ $('#editortoolbar li').show();
+ }
+ $('#togglemode').show();
+ $('#summary').addClass('editable');
+ $('.property,#also_time').each(function () {
+ $(this).attr('disabled', false);
+ });
+ } else {
+ $('#description').rte('setEnabled', false);
+ $('#editortoolbar .richtext, #togglemode').hide();
+ $('#summary').removeClass('editable');
+ $('.property,#also_time').each(function () {
+ $(this).attr('disabled', true);
+ });
+ }
+ },
+ toggleMode:function() {
+ console.log('togglemode');
+ $('#description').rte('toggleMode');
+ $('#editortoolbar li.richtext').toggle();
+ },
+ Entry:{
+ id:'',
+ data:undefined,
+ add:function() {
+ // TODO: wrap a DIV around the summary field with a suggestion(?) to fill out this field first. See OC.Journal.required
+ // Remember to reenable all controls.
+ $('#leftcontent lidata-id="'+this.id+'"').removeClass('active');
+ this.id = 'new';
+ this.data = undefined;
+ $('.property').each(function () {
+ switch($(this).get(0).nodeName) {
+ case 'DIV':
+ $(this).html('');
+ break;
+ case 'INPUT':
+ case 'TEXTAREA':
+ $(this).val('');
+ break;
+ default:
+ console.log('OC.Journal.Entry.add. Forgot: ' + $(this).get(0).nodeName);
+ break;
+ }
+ });
+ $('#description').rte('setEnabled', false);
+ $('#editortoolbar li.richtext').hide();
+ $('#editable').attr('checked', true);
+ OC.Journal.setEnabled(true);
+ },
+ createEntry:function(data) {
+ var date = new Date(parseInt(data.dtstart)*1000);
+ var timestring = (data.only_date?'':' ' + date.toLocaleTimeString());
+ return $('
').data('entry', data);
+ },
+ loadEntry:function(id, data) {
+ //$(document).off('change', '.property');
+ console.log('loadEntry: ' + id + ': ' + data.summary);
+ this.id = id;
+ this.data = data;
+ $('#entry').data('id', id);
+ console.log('summary: ' + data.summary.unEscape());
+ $('#summary').val(data.summary.unEscape());
+ $('#organizer').val(data.organizer.value.split(':')[1]);
+ var format = data.description.format;
+ console.log('format: '+format);
+ $('#description').rte(format, data.description.value.unEscape());
+ $('#description').rte('mode', format);
+ //$('#description').expandingTextarea('resize');
+ (format=='html'&&$('#editable').get(0).checked?$('#editortoolbar li.richtext').show():$('#editortoolbar li.richtext').hide());
+ $('#location').val(data.location);
+ $('#categories').val(data.categories.join(','));
+ $('#categories').multiple_autocomplete('option', 'source', categories);
+ console.log('Trying to parse: '+data.dtstart);
+ var date = new Date(parseInt(data.dtstart)*1000);
+ //$('#dtstartdate').val(date.getDate()+'-'+date.getMonth()+'-'+date.getFullYear()); //
+ $('#dtstartdate').datepicker('setDate', date);
+ if(data.only_date) {
+ $('#dtstarttime').hide();
+ $('#also_time').attr('checked', false);
+ //$('#also_time').get(0).checked = false;
+ } else {
+ // timepicker('setTime', ...) triggers a 'change' event, so you have to jump through hoops here ;-)
+ $('#dtstarttime').val(date.getHours().toString().zeroPad(2)+':'+date.getMinutes().toString().zeroPad(2));
+ $('#dtstarttime').show();
+ $('#also_time').attr('checked', true);
+ //$('#also_time').get(0).checked = true;
+ }
+ console.log('dtstart: '+date);
+ },
+ saveproperty:function(obj) {
+ if(!this.id) { // we are adding an entry and want a response back from the server.
+ this.id = 'new';
+ console.log('OC.Journal.Entry.saveproperty: We need to add a new one.');
+ //return;
+ }
+ var container = OC.Journal.propertyContainerFor(obj);
+ var params = {'id':this.id};
+ params['type'] = container.data('type');
+ params['parameters'] = {};
+ switch(params['type']) {
+ case 'ORGANIZER':
+ case 'LOCATION':
+ case 'CATEGORIES':
+ params['value'] = $(obj).val();
+ break;
+ case 'SUMMARY':
+ if(this.id == 'new' && $(obj).val().trim() == '') {
+ $(obj).focus();
+ $(obj).addClass('required');
+ $(obj).on('blur', OC.Journal.required);
+ return;
+ }
+ params['value'] = $(obj).val();
+ break;
+ case 'DESCRIPTION':
+ // Check if we get the description from the textarea or the contenteditable.
+ var format = ($(obj).get(0).nodeName == 'DIV' ? 'html' : 'text'); // FIXME: should check rte instead.
+ var value = $('#description').rte(format); // calls either the 'text' or 'html' method of the rte.
+ //var value = ($(obj).get(0).nodeName == 'DIV' ? $(obj).html() : $(obj).text());
+ console.log('nodeName: ' + $(obj).get(0).nodeName);
+ params['value'] = value;
+ params['parameters']['FORMAT'] = format.toUpperCase();
+ break;
+ case 'DTSTART':
+ var date = $('#dtstartdate').val();
+ var time = $('#dtstarttime').val();
+ var datetime = new Date(parseInt(date.substring(6, 10)), parseInt(date.substring(3, 5)), parseInt(date.substring(0, 2)) , parseInt(time.substring(0, 2)), parseInt(time.substring(3, 5)), 0, 0);
+ params['value'] = datetime.getTime()/1000;
+ break;
+ default:
+ $.extend(1, $(obj).serializeArray(), params);
+ break;
+ }
+ self = this;
+ $.post(OC.filePath('journal', 'ajax', 'saveproperty.php'), params, function(jsondata) {
+ if(jsondata.status == 'success') {
+ if(self.id == 'new') {
+ self.loadEntry(jsondata.data.id, jsondata.data);
+ } else {
+ $('#leftcontent li[data-id="'+self.id+'"]').remove();
+ }
+ var item = self.createEntry(jsondata.data);
+ $('#leftcontent').append(item);
+ OC.Journal.Journals.doSort();
+ OC.Journal.Journals.scrollTo(self.id);
+ // add error checking
+ console.log('successful save');
+ } else {
+ OC.dialogs.alert(jsondata.data.message.text, t('contacts', 'Error'));
+ }
+ });
+ },
+ doExport:function() {
+ document.location.href = OC.linkTo('calendar', 'export.php') + '?eventid=' + this.id;
+ },
+ doDelete:function() {
+ // TODO: Do something when there are no more entries.
+ if(this.id == 'new') { return; }
+ $('#delete').tipsy('hide');
+ self = this;
+ OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this entry?'), t('journal', 'Warning'), function(answer) {
+ if(answer == true) {
+ $.post(OC.filePath('journal', 'ajax', 'delete.php'), {'id': self.id}, function(jsondata) {
+ if(jsondata.status == 'success') {
+ var curlistitem = $('#leftcontent li[data-id="'+self.id+'"]');
+ var newlistitem = curlistitem.prev('li');
+ if(!$(newlistitem).is('li')) {
+ newlistitem = curlistitem.next('li');
+ }
+ if(!$(newlistitem).is('li')) {
+ alert('No more entries. Do something!!!');
+ }
+ $(newlistitem).addClass('active');
+ console.log('newlistitem: ' + newlistitem.toString());
+ curlistitem.remove();
+ var data = newlistitem.data('entry');
+ self.loadEntry(data.id, data);
+ console.log('successful delete');
+ } else {
+ OC.dialogs.alert(jsondata.data.message.text, t('contacts', 'Error'));
+ }
+ });
+ }
+ });
+ },
+ },
+ Journals:{
+ sortmethod:undefined,
+ doSort:function(method) {
+ if(method) {
+ this.sortmethod = method;
+ } else {
+ method = this.sortmethod;
+ }
+ // Thanks to http://www.java2s.com/Tutorial/JavaScript/0220__Array/Usinganalphabeticalsortmethodonstrings.html
+ // and http://stackoverflow.com/questions/4258974/sort-list-based-on-data-attribute-using-jquery-metadata-plugin#4259074
+ // and http://stackoverflow.com/questions/8882418/jquery-sorting-lib-that-supports-multilanguage-sorting
+ compareDateTimeAsc = function(a, b){
+ return (parseInt(a.dtstart) > parseInt(b.dtstart)?-1:1);
+ }
+ compareDateTimeDesc = function(a, b){
+ return (parseInt(b.dtstart) < parseInt(a.dtstart)?-1:1);
+ }
+ compareSummaryAsc = function(a, b){
+ return b.summary.toLowerCase().localeCompare(a.summary.toLowerCase());
+ }
+ compareSummaryDesc = function(a, b){
+ return a.summary.toLowerCase().localeCompare(b.summary.toLowerCase());
+ }
+ var func;
+ switch(method) {
+ case 'dtasc':
+ func = compareDateTimeAsc;
+ break;
+ case 'dtdesc':
+ func = compareDateTimeDesc;
+ break;
+ case 'sumasc':
+ func = compareSummaryAsc;
+ break;
+ case 'sumdesc':
+ func = compareSummaryDesc;
+ break;
+ default:
+ var func = compareDateTimeDesc;
+ break;
+ }
+
+ var arr = []
+ // loop through each list item and get the metadata
+ $('#leftcontent li').each(function () {
+ var meta = $(this).data('entry');
+ meta.elem = $(this);
+ arr.push(meta);
+ });
+ arr.sort(func);
+
+ //Foreach item append it to the container. The first i arr will then end up in the top
+ $.each(arr, function(index, item){
+ item.elem.appendTo(item.elem.parent());
+ });
+ },
+ update:function(id) {
+ console.log('update: ' + id);
+ self = this;
+ $('#leftcontent').addClass('loading');
+ $.getJSON(OC.filePath('journal', 'ajax', 'entries.php'), function(jsondata) {
+ if(jsondata.status == 'success') {
+ var entries = $('#leftcontent').empty();
+ $(jsondata.data.entries).each(function(i, entry) {
+ entries.append(OC.Journal.Entry.createEntry(entry));
+ });
+ $('#leftcontent').removeClass('loading');
+ self.doSort('dtasc');
+ console.log('Count: ' + $('#leftcontent li').length);
+ if($('#leftcontent li').length > 0 ){
+ var firstitem;
+ if(id) {
+ firstitem = $('#leftcontent li[data-id="'+id+'"]');
+ } else {
+ firstitem = $('#leftcontent li').first();
+ id = firstitem.data('entry').id;
+ }
+ firstitem.addClass('active');
+ self.scrollTo(id);
+ OC.Journal.Entry.loadEntry(firstitem.data('id'), firstitem.data('entry'));
+ }
+ } else {
+ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+ }
+ });
+ },
+ scrollTo:function(id){
+ var item = $('#leftcontent li[data-id="'+id+'"]');
+ if(item) {
+ try {
+ $('#leftcontent').animate({scrollTop: $('#leftcontent li[data-id="'+id+'"]').offset().top-70}, 'slow','swing');
+ } catch(e) {}
+ }
+ }
+ }
+};
+
+$(document).ready(function(){
+ OCCategories.changed = OC.Journal.categoriesChanged;
+ OCCategories.app = 'calendar';
+
+ // Initialize controls.
+ $('#categories').multiple_autocomplete({source: categories});
+ //$('#categories').multiple_autocomplete('option', 'source', categories);
+ $('#dtstartdate').datepicker({dateFormat: 'dd-mm-yy'});
+ $('#dtstarttime').timepicker({timeFormat: 'hh:mm', showPeriodLabels:false});
+ $('#description').rte({classes: ['property','content']});
+ $('.tip').tipsy();
+
+ OC.Journal.init();
+
+ // Show the input with a direcy link the journal entry, binds an event to close
+ // it on blur and removes the binding again afterwards.
+ $('#showlink').on('click', function(event){
+ console.log('showlink');
+ $('#link').toggle('slow').val(totalurl+'&id='+OC.Journal.Entry.id).focus().
+ on('blur',function(event) {$(this).hide()}).off('blur', $(this));
+ return false;
+ });
+
+ $('#rightcontent').on('change', '.property', function(event){
+ OC.Journal.Entry.saveproperty(this);
+ });
+
+ $('#controls').on('click', '#add', function(event){
+ OC.Journal.Entry.add();
+ });
+
+ $('#metadata').on('change', '#also_time', function(event){
+ $('#dtstarttime').toggle().trigger('change');
+ });
+
+ $('#metadata').on('click', '#export', function(event){
+ OC.Journal.Entry.doExport();
+ });
+
+ $('#metadata').on('click', '#editcategories', function(event){
+ OCCategories.edit();
+ });
+
+ $('#metadata').on('click', '#delete', function(event){
+ OC.Journal.Entry.doDelete();
+ });
+
+ $('#controls').on('change', '#entrysort', function(event){
+ OC.Journal.Journals.doSort($(this).val());
+ });
+
+ // Proxy click.
+ $('#leftcontent').on('keydown', '#leftcontent', function(event){
+ if(event.which == 13) {
+ $('#leftcontent').click(event);
+ }
+ });
+ // Journal entry clicked
+ $(document).on('click', '#leftcontent', function(event){
+ var $tgt = $(event.target);
+ var item = $tgt.is('li')?$($tgt):($tgt).parents('li').first();
+ var id = item.data('id');
+ item.addClass('active');
+ var oldid = $('#entry').data('id');
+ console.log('oldid: ' + oldid);
+ if(oldid != 0){
+ $('#leftcontent li[data-id="'+oldid+'"]').removeClass('active');
+ }
+ OC.Journal.Entry.loadEntry(id, item.data('entry'));
+ return false;
+ });
+ // Editor command.
+ $('.rte-toolbar button').on('click', function(event){
+ console.log('cmd: ' + $(this).data('cmd'));
+ $('#description').rte('formatText', $(this).data('cmd'));
+ event.preventDefault();
+ return false;
+ });
+ // Toggle text/html editing mode.
+ $('#togglemode').on('click', function(event){
+ OC.Journal.toggleMode(true);
+ return false;
+ });
+ $('#editable').on('change', function(event){
+ OC.Journal.setEnabled($(this).get(0).checked);
+ });
+
+});
diff --git a/apps/journal/js/jquery.rte.js b/apps/journal/js/jquery.rte.js
new file mode 100644
index 00000000000..ca7b61d719c
--- /dev/null
+++ b/apps/journal/js/jquery.rte.js
@@ -0,0 +1,244 @@
+// http://wiki.jqueryui.com/w/page/12138135/Widget%20factory
+
+(function( $ ) {
+
+$.widget( 'ui.rte', {
+ // These options will be used as defaults
+ options: {
+ disabled: true,
+ mode: 'html'
+ },
+ // Set up the widget
+ _create: function() {
+ console.log('_create');
+ var self = this,
+ dirty = false,
+ textarea = this.element; //.hide(),
+ pos = textarea.position();
+ //this.element.text = this.text;
+ this.mirror = $('').insertAfter(textarea).show();
+ textarea.hide();
+ this.formatText('styleWithCSS', true);
+ this.validtags = ['A','P','STRONG', 'B', 'I', 'SPAN', 'DIV', 'OL', 'UL', 'LI', 'DL', 'DT', 'DD'];
+ this.enabled = true;
+
+ /*$(window).resize(function() {
+ console.log('resize: ');
+ });*/
+
+ this.mirror.keydown(function(event){
+ if(event.which == 13) {
+ self.insertAtCaret(' ');
+ }
+ });
+ this.mirror.keyup(function() {
+ console.log('keyup, set dirty.');
+ self.dirty = true;
+ });
+ this.mirror.blur(function() {
+ console.log('blur: ');
+ if(self.dirty) {
+ self.mirror.trigger('change');
+ self.dirty = false;
+ }
+ });
+
+ },
+ _init: function() {
+ console.log('_init');
+ self = this;
+ $.each(this.options, function(key, value) {
+ self._setOption(key, value);
+ });
+ },
+ text: function(str) {
+ console.log('function text');
+ if(str != undefined) {
+ this.mirror.html(str);
+ this.element.text(str);
+ } else {
+ console.log('returning: ' + this.element.val());
+ return this.element.val();
+ }
+ },
+ html: function(str) {
+ console.log('function html');
+ if(str != undefined) {
+ console.log('str: ' + str);
+ var $str;
+ try {
+ $str = $(str);
+ this.mirror.empty().html($str); // Call empty() for IE 8.
+ this.element.text($str.text());
+ } catch(e) {
+ console.log(e.message);
+ this.mirror.empty().html(str);
+ this.element.text(str);
+ }
+ //console.log('length: ' + $str.length);
+ //this.mirror.get(0).contenteditable=false;
+ } else {
+ console.log('returning: ' + this.mirror.html());
+ return this.mirror.html();
+ }
+ },
+ insertAtCaret: function(myValue){
+ // Found this at stackoverflow
+ return this.mirror.each(function(i) {
+ if (document.selection) {
+ console.log('IE');
+ //For browsers like Internet Explorer
+ this.focus();
+ sel = document.selection.createRange();
+ sel.text = myValue;
+ this.focus();
+ }
+ else if (this.selectionStart || this.selectionStart == '0') {
+ console.log('FF');
+ //For browsers like Firefox and Webkit based
+ var startPos = this.selectionStart;
+ var endPos = this.selectionEnd;
+ var scrollTop = this.scrollTop;
+ this.value = this.value.substring(0, startPos)+myValue+this.value.substring(endPos,this.value.length);
+ this.focus();
+ this.selectionStart = startPos + myValue.length;
+ this.selectionEnd = startPos + myValue.length;
+ this.scrollTop = scrollTop;
+ } else {
+ console.log('Smth.');
+ this.value += myValue;
+ this.focus();
+ }
+ })
+ },
+ showSelection: function() {
+ var textComponent = this.mirror.get(0); //document.getElementById('Editor');
+ var selectedText;
+ // IE version
+ if (document.selection != undefined) {
+ textComponent.focus();
+ var sel = document.selection.createRange();
+ selectedText = sel.text;
+ }
+ // Mozilla version
+ else if (textComponent.selectionStart != undefined) {
+ var startPos = textComponent.selectionStart;
+ var endPos = textComponent.selectionEnd;
+ selectedText = textComponent.value.substring(startPos, endPos)
+ }
+ alert("You selected: " + selectedText);
+ },
+ formatText: function(command, option) {
+ self = this, useDialog = false;
+ switch(command) {
+ case 'ulist':
+ command = 'insertUnorderedList';
+ break;
+ case 'olist':
+ command = 'insertOrderedList';
+ break;
+ case 'createlink':
+ self.showSelection();
+ option=prompt('Write the URL here')
+ useDialog = true;
+ default:
+ break;
+ }
+ try{
+ document.execCommand(command, useDialog, option);
+ self.dirty = true; // FIXME: This doesn't work because blur is triggered before dirty is set.
+ self.mirror.trigger('blur'); // Dirty hack to trigger save. Hmm, if it only worked...
+ }catch(e){
+ console.log('Error: ' + e)
+ }
+ },
+ setEnabled: function(state) {
+ console.log('function setEnabled: ' + state);
+ if(state != undefined) {
+ this._setOption('disabled', !state);
+ }
+ return this.options['disabled'];
+ },
+ mode: function(mode) {
+ if(mode != undefined) {
+ this._setOption('mode', mode);
+ }
+ return this.options['mode'];
+ },
+ /*toggle: function() {
+ this._setOption('disabled', !this.options['disabled']);
+ return !this.options['disabled'];
+ },*/
+ toggleMode: function() {
+ this._setOption('mode', (this.options['mode'] == 'html'?'text':'html'));
+ return this.options['mode'];
+ },
+ // Use the _setOption method to respond to changes to options
+ _setOption: function( key, value ) {
+ console.log('option ' + key + ': ' + value);
+ switch( key ) {
+ case 'disabled':
+ if(value) {
+ this.mirror.get(0).contenteditable = false;
+ this.mirror.attr('contenteditable', false);
+ this.mirror.removeClass('editable');
+ this.element.attr('disabled', true);
+ this.element.removeClass('editable');
+ } else {
+ this.mirror.get(0).contenteditable = true;
+ this.mirror.attr('contenteditable', true);
+ this.mirror.addClass('editable');
+ this.element.attr('disabled', false);
+ this.element.addClass('editable');
+ }
+ break;
+ case 'mode':
+ switch(value) {
+ case 'html':
+ this.mirror.show();
+ this.element.hide();
+ break;
+ case 'text':
+ this.mirror.hide();
+ this.element.show();
+ this.element.trigger('resize');
+ break;
+ default:
+ throw { name: 'UnknownMode', message: 'Invalid mode: ' + value }
+ break;
+ }
+ break;
+ case 'classes':
+ if($.isArray(value)) {
+ var mirror = this.mirror;
+ $.each(this.options['classes'], function(key, value) {
+ mirror.addClass(value);
+ });
+ } else {
+ this.mirror.addClass(value);
+ }
+ break;
+ default:
+ this.options[key] = value;
+ break;
+ }
+ // In jQuery UI 1.8, you have to manually invoke the _setOption method from the base widget
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+ // In jQuery UI 1.9 and above, you use the _super method instead
+ //this._super( "_setOption", key, value );
+ },
+ // Use the destroy method to clean up any modifications your widget has made to the DOM
+ destroy: function() {
+ this.mirror.remove();
+ this.element.show();
+ // In jQuery UI 1.8, you must invoke the destroy method from the base widget
+ $.Widget.prototype.destroy.call( this );
+ // In jQuery UI 1.9 and above, you would define _destroy instead of destroy and not call the base method
+ }
+
+});
+
+}( jQuery ) );
+
+
diff --git a/apps/journal/js/jquery.textchange.js b/apps/journal/js/jquery.textchange.js
new file mode 100644
index 00000000000..66a07fc5bcd
--- /dev/null
+++ b/apps/journal/js/jquery.textchange.js
@@ -0,0 +1,76 @@
+/*!
+ * jQuery TextChange Plugin
+ * http://www.zurb.com/playground/jquery-text-change-custom-event
+ *
+ * Copyright 2010, ZURB
+ * Released under the MIT License
+ */
+(function ($) {
+
+ $.event.special.textchange = {
+
+ setup: function (data, namespaces) {
+ $(this).data('lastValue', this.contentEditable === 'true' ? $(this).html() : $(this).val());
+ $(this).bind('keyup.textchange', $.event.special.textchange.handler);
+ $(this).bind('cut.textchange paste.textchange input.textchange', $.event.special.textchange.delayedHandler);
+ },
+
+ teardown: function (namespaces) {
+ $(this).unbind('.textchange');
+ },
+
+ handler: function (event) {
+ $.event.special.textchange.triggerIfChanged($(this));
+ },
+
+ delayedHandler: function (event) {
+ var element = $(this);
+ setTimeout(function () {
+ $.event.special.textchange.triggerIfChanged(element);
+ }, 25);
+ },
+
+ triggerIfChanged: function (element) {
+ var current = element[0].contentEditable === 'true' ? element.html() : element.val();
+ if (current !== element.data('lastValue')) {
+ element.trigger('textchange', [element.data('lastValue')]);
+ element.data('lastValue', current);
+ }
+ }
+ };
+
+ $.event.special.hastext = {
+
+ setup: function (data, namespaces) {
+ $(this).bind('textchange', $.event.special.hastext.handler);
+ },
+
+ teardown: function (namespaces) {
+ $(this).unbind('textchange', $.event.special.hastext.handler);
+ },
+
+ handler: function (event, lastValue) {
+ if ((lastValue === '') && lastValue !== $(this).val()) {
+ $(this).trigger('hastext');
+ }
+ }
+ };
+
+ $.event.special.notext = {
+
+ setup: function (data, namespaces) {
+ $(this).bind('textchange', $.event.special.notext.handler);
+ },
+
+ teardown: function (namespaces) {
+ $(this).unbind('textchange', $.event.special.notext.handler);
+ },
+
+ handler: function (event, lastValue) {
+ if ($(this).val() === '' && $(this).val() !== lastValue) {
+ $(this).trigger('notext');
+ }
+ }
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/apps/journal/js/settings.js b/apps/journal/js/settings.js
new file mode 100644
index 00000000000..8cd1b25f6e7
--- /dev/null
+++ b/apps/journal/js/settings.js
@@ -0,0 +1,15 @@
+$(document).ready(function(){
+ $('#journal_calendar').on('change', function(event){
+ $.post(OC.filePath('journal', 'ajax', 'setdefaultcalendar.php'), {'id':$('#journal_calendar option:selected').val()}, function(jsondata) {
+ var success = {padding: 0.5em, background-color:green, color: white, font-weight: bold, float: left};
+ var failure = {padding: 0.5em, background-color:red, color: white, font-weight: bold, float: left};
+ if(jsondata.status == 'success') {
+ $('#journal_status');
+ $('#journal_status').css(success).html(t('journal', 'Saved')).fadeIn().fadeOut(5000);
+ } else {
+ $('#journal_status').css(failure);
+ $('#journal_status').html(t('journal', 'Error saving: ')+jsondata.data.message).fadeIn().fadeOut(5000);
+ }
+ });
+ });
+});
diff --git a/apps/journal/lib/app.php b/apps/journal/lib/app.php
new file mode 100644
index 00000000000..d3d9b3bf114
--- /dev/null
+++ b/apps/journal/lib/app.php
@@ -0,0 +1,135 @@
+
+ * @copyright 2012 Thomas Tanghus
+ *
+ * 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 .
+ *
+ */
+
+/**
+ * This class manages our journal.
+ */
+OC_Journal_App::$l10n = new OC_L10N('journal');
+class OC_Journal_App {
+ public static $l10n;
+ /*
+ * @brief categories of the user
+ */
+ protected static $categories = null;
+
+ public static function arrayForJSON($id, $vjournal, $user_timezone) {
+ // Possible properties: URL
+ $journal = array( 'id' => $id );
+ $journal['summary'] = $vjournal->getAsString('SUMMARY');
+ $format = 'text';
+ if($vjournal->DESCRIPTION) {
+ foreach($vjournal->DESCRIPTION->parameters as $parameter){
+ if(stripos($parameter->name, 'FORMAT') !== false && stripos($parameter->value, 'HTML') !== false){
+ $format = 'html'; // an educated guess ;-)
+ break;
+ }
+ }
+ $desc = $vjournal->getAsString('DESCRIPTION');
+ $journal['description'] = array(
+ 'value' => ($format=='html'?$body = preg_replace("/.*]*>|<\/body>.*/si", "", $desc):$desc),
+ 'format' => $format,
+ 'parameters' => self::parametersForProperty($vjournal->DESCRIPTION)
+ );
+ } else {
+ $journal['description'] = array('value' => '', 'format' => 'text');
+ }
+ $journal['organizer'] = array(
+ 'value' => $vjournal->getAsString('ORGANIZER'),
+ 'parameters' => self::parametersForProperty($vjournal->ORGANIZER)
+ );
+ $journal['categories'] = $vjournal->getAsArray('CATEGORIES');
+ //error_log('DTSTART: '.print_r($vjournal->DTSTART, true));
+ $dtprop = $vjournal->DTSTART;
+ if($dtprop) {
+ $dtstart = $vjournal->DTSTART->getDateTime();
+ if($dtstart) {
+ //if(!)
+ $tz = new DateTimeZone($user_timezone);
+ if($tz->getName() != $dtstart->getTimezone()->getName() && !$vjournal->DTSTART->offsetExists('TZID')) {
+ //error_log($tz->getName().' != '.$dtstart->getTimezone()->getName());
+ //error_log('TZ offset: '.$tz->getOffset(new DateTime("now"))/60);
+ $dtstart->setTimezone($tz);
+ }
+ $journal['dtstart'] = $dtstart->format('U');
+ $journal['only_date'] = ($dtprop->getDateType() == Sabre_VObject_Property_DateTime::DATE);
+ } else {
+ OCP\Util::writeLog('journal', 'Could not get DTSTART DateTime for '.$journal['summary'], OCP\Util::ERROR);
+ }
+ } else {
+ OCP\Util::writeLog('journal', 'Could not get DTSTART for '.$journal['summary'], OCP\Util::ERROR);
+ }
+ return $journal;
+ }
+
+ /** Get a map of a properties parameters for JSON
+ * @param $property Sabre_VObject_Property
+ * @return array of parameters in { name => value, } format
+ */
+ public static function parametersForProperty($property) {
+ $temp = array();
+ if(!$property) {
+ return;
+ }
+ foreach($property->parameters as $parameter){
+ $temp[$parameter->name] = $parameter->value;
+ }
+ return $temp;
+ }
+
+ /*
+ * @brief returns the vcategories object of the user
+ * @return (object) $vcategories
+ */
+ protected static function getVCategories() {
+ if (is_null(self::$categories)) {
+ self::$categories = new OC_VCategories('calendar');
+ }
+ return self::$categories;
+ }
+
+
+ /**
+ * Create a stub for a new journal entry.
+ * @return OC_VObject The newly created stub.
+ */
+ public static function createVCalendar() {
+ $vcalendar = new OC_VObject('VCALENDAR');
+ $appinfo = OCP\App::getAppInfo('journal');
+ $appversion = OCP\App::getAppVersion('journal');
+ $prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN';
+ $vcalendar->add('PRODID', $prodid);
+ $vcalendar->add('VERSION', '2.0');
+
+ $vjournal = new OC_VObject('VJOURNAL');
+ $vjournal->setDateTime('DTSTART', 'now', Sabre_VObject_Property_DateTime::LOCALTZ);
+ $vjournal->setDateTime('CREATED', 'now', Sabre_VObject_Property_DateTime::UTC);
+ $vjournal->setUID();
+ $email = OCP\Config::getUserValue(OCP\User::getUser(), 'settings', 'email', '');
+ if($email) {
+ $vjournal->setString('ORGANIZER', 'mailto:'.$email);
+ }
+ $vcalendar->add($vjournal);
+ return $vcalendar;
+ }
+
+}
diff --git a/apps/journal/lib/hooks.php b/apps/journal/lib/hooks.php
new file mode 100644
index 00000000000..2298871148c
--- /dev/null
+++ b/apps/journal/lib/hooks.php
@@ -0,0 +1,53 @@
+
+ *
+ * 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 .
+ *
+ */
+
+/**
+ * This class manages our journal.
+ */
+class OC_Journal_Hooks {
+ /**
+ * Hook to convert a completed Task (VTODO) to a journal entry and add it to the calendar.
+ * @param $vtodo An OC_VObject of type VTODO.
+ */
+ public static function taskToJournalEntry($vtodo) {
+ if(!$vtodo) { return; }
+
+ OCP\Util::writeLog('journal', 'Completed task: '.$vtodo->getAsString('SUMMARY'), OCP\Util::DEBUG);
+ $vcalendar = OC_Journal_App::createVCalendar();
+ $vjournal = $vcalendar->VJOURNAL;
+ $vjournal->setDateTime('DTSTART',$vtodo->COMPLETED->getDateTime());
+ $vjournal->SUMMARY = $vtodo->SUMMARY;
+ $vjournal->setString('SUMMARY', OC_Journal_App::$l10n->t('Completed task: ').$vjournal->getAsString('SUMMARY'));
+ $vjournal->DESCRIPTION = $vtodo->DESCRIPTION;
+
+ $cid = OCP\Config::getUserValue(OCP\User::getUser(), 'journal', 'default_calendar', null);
+ if(!$cid) {
+ $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
+ $first_calendar = reset($calendars);
+ $cid = $first_calendar['id'];
+ }
+ try {
+ $id = OC_Calendar_Object::add($cid, $vcalendar->serialize());
+ } catch (Exception $e) {
+ OCP\Util::writeLog('journal', 'Error adding completed Task to calendar: "'.$cid.'" '. $e->getMessage(), OCP\Util::ERROR);
+ }
+ }
+}
diff --git a/apps/journal/lib/search.php b/apps/journal/lib/search.php
new file mode 100644
index 00000000000..27b9b87b031
--- /dev/null
+++ b/apps/journal/lib/search.php
@@ -0,0 +1,45 @@
+ 0) {
+ $searchquery = explode(' ', $query);
+ }else{
+ $searchquery[] = $query;
+ }
+ error_log('search');
+ $user_timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
+ $l = new OC_l10n('journal');
+ foreach($calendars as $calendar) {
+ $objects = OC_Calendar_Object::all($calendar['id']);
+ foreach($objects as $object) {
+ if($object['objecttype']!='VJOURNAL') {
+ continue;
+ }
+ if(substr_count(strtolower($object['summary']), strtolower($query)) > 0) {
+ $calendardata = OC_VObject::parse($object['calendardata']);
+ $vjournal = $calendardata->VJOURNAL;
+ $dtstart = $vjournal->DTSTART;
+ if($dtstart) {
+ continue;
+ }
+ $start_dt = $dtstart->getDateTime();
+ $start_dt->setTimezone(new DateTimeZone($user_timezone));
+ if ($dtstart->getDateType() == Sabre_VObject_Property_DateTime::DATE) {
+ $info = $l->t('Date') . ': ' . $start_dt->format('d.m.Y');
+ }else{
+ $info = $l->t('Date') . ': ' . $start_dt->format('d.m.y H:i');
+ }
+ $link = OCP\Util::linkTo('journal', 'index.php').'&id='.urlencode($object['id']);
+ $results[]=new OC_Search_Result($object['summary'],$info, $link,(string)$l->t('Journal'));//$name,$text,$link,$type
+ }
+ }
+ }
+ return $results;
+ }
+}
diff --git a/apps/journal/lib/vjournal.php b/apps/journal/lib/vjournal.php
new file mode 100644
index 00000000000..b9aedea041e
--- /dev/null
+++ b/apps/journal/lib/vjournal.php
@@ -0,0 +1,27 @@
+
+ *
+ * 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 .
+ *
+ */
+
+/**
+ * This class manages our journals
+ */
+class OC_Task_VJournal extends OC_Calendar_Object{
+}
diff --git a/apps/journal/settings.php b/apps/journal/settings.php
new file mode 100644
index 00000000000..35f0ee7d6c2
--- /dev/null
+++ b/apps/journal/settings.php
@@ -0,0 +1,45 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+
+OCP\JSON::checkLoggedIn();
+
+$errors = array();
+
+$required_apps = array(
+ array('id' => 'tal', 'name' => 'TAL Page Templates'),
+ array('id' => 'journal', 'name' => 'Journal'),
+ array('id' => 'contacts', 'name' => 'Contacts'),
+);
+foreach($required_apps as $app) {
+ if(!OCP\App::isEnabled($app['id'])) {
+ $error = (string)$l->t('The %%s app isn\'t enabled! Please enable it here: Enable %%s app');
+ $errors[] = sprintf($error, $app['name'],OCP\Util::linkTo('settings', 'apps'), $app['id'], $app['name']);
+ }
+}
+
+$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true);
+if( count($calendars) == 0 ) {
+ $error = (string)$l->t('You have no calendars. Please add one at the Calendar app');
+ $errors[] = sprintf($error, OCP\Util::linkTo('calendar', 'index.php'));
+}
+
+if(count($errors) > 0) {
+ $tmpl = new OCP\Template('journal', 'rtfm');
+ $tmpl->assign('errors',$errors, false);
+} else {
+ $cid = OCP\Config::getUserValue(OCP\User::getUser(), 'journal', 'default_calendar', null);
+ OCP\Util::addScript('journal', 'settings');
+ $tmpl = new OC_TALTemplate('journal', 'settings', 'user');
+ $tmpl->assign('calendars', $calendars);
+ $tmpl->assign('cid', $cid);
+}
+
+return $tmpl->fetchPage();
+
+?>
diff --git a/apps/journal/templates/index.pt b/apps/journal/templates/index.pt
new file mode 100644
index 00000000000..40ce351a543
--- /dev/null
+++ b/apps/journal/templates/index.pt
@@ -0,0 +1,101 @@
+
+
+
+ Journal
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/journal/templates/rtfm.php b/apps/journal/templates/rtfm.php
new file mode 100644
index 00000000000..8fd51fb8d02
--- /dev/null
+++ b/apps/journal/templates/rtfm.php
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/apps/journal/templates/settings.pt b/apps/journal/templates/settings.pt
new file mode 100644
index 00000000000..3e788f58818
--- /dev/null
+++ b/apps/journal/templates/settings.pt
@@ -0,0 +1,15 @@
+
diff --git a/apps/media/appinfo/version b/apps/media/appinfo/version
index e6adf3fc7bb..44bb5d1f743 100644
--- a/apps/media/appinfo/version
+++ b/apps/media/appinfo/version
@@ -1 +1 @@
-0.4
\ No newline at end of file
+0.4.1
\ No newline at end of file
diff --git a/apps/media/lib/share/album.php b/apps/media/lib/share/album.php
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/apps/media/lib/share/artist.php b/apps/media/lib/share/artist.php
new file mode 100644
index 00000000000..7218fa1a279
--- /dev/null
+++ b/apps/media/lib/share/artist.php
@@ -0,0 +1,65 @@
+.
+*/
+
+class OC_Share_Backend_Artist extends OCP\Share_Backend {
+
+ public function getSource($item, $uid) {
+ $query = OCP\DB::prepare('SELECT artist_id FROM *PREFIX*media_artists WHERE artist_id = ? AND song_user = ?');
+ $result = $query->execute(array($item, $uid))->fetchRow();
+ if (is_array($result)) {
+ return array('item' => $item, 'file' => $result['song_path']);
+ }
+ return false;
+ }
+
+ public function generateTarget($item, $uid, $exclude = null) {
+ // TODO Make sure target path doesn't exist already
+ return '/Shared'.$item;
+ }
+
+ public function formatItems($items, $format) {
+ $ids = array();
+ foreach ($items as $id => $info) {
+ $ids[] = $id;
+ }
+ $ids = "'".implode("','", $ids)."'";
+ switch ($format) {
+ case self::FORMAT_SOURCE_PATH:
+ $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')');
+ return $query->execute()->fetchAll();
+ case self::FORMAT_FILE_APP:
+ $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')');
+ $result = $query->execute();
+ $files = array();
+ while ($file = $result->fetchRow()) {
+ // Set target path
+ $file['path'] = $items[$file['id']]['item_target'];
+ $file['name'] = basename($file['path']);
+ // TODO Set permissions: $file['writable']
+ $files[] = $file;
+ }
+ return $files;
+ }
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/lib/share/song.php b/apps/media/lib/share/song.php
new file mode 100644
index 00000000000..fc69975f353
--- /dev/null
+++ b/apps/media/lib/share/song.php
@@ -0,0 +1,65 @@
+.
+*/
+
+class OC_Share_Backend_Song extends OCP\Share_Backend {
+
+ public function getSource($item, $uid) {
+ $query = OCP\DB::prepare('SELECT song_path FROM *PREFIX*media_songs WHERE song_id = ? AND song_user = ?');
+ $result = $query->execute(array($item, $uid))->fetchRow();
+ if (is_array($result)) {
+ return array('item' => $item, 'file' => $result['song_path']);
+ }
+ return false;
+ }
+
+ public function generateTarget($item, $uid, $exclude = null) {
+ // TODO Make sure target path doesn't exist already
+ return '/Shared'.$item;
+ }
+
+ public function formatItems($items, $format) {
+ $ids = array();
+ foreach ($items as $id => $info) {
+ $ids[] = $id;
+ }
+ $ids = "'".implode("','", $ids)."'";
+ switch ($format) {
+ case self::FORMAT_SOURCE_PATH:
+ $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')');
+ return $query->execute()->fetchAll();
+ case self::FORMAT_FILE_APP:
+ $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')');
+ $result = $query->execute();
+ $files = array();
+ while ($file = $result->fetchRow()) {
+ // Set target path
+ $file['path'] = $items[$file['id']]['item_target'];
+ $file['name'] = basename($file['path']);
+ // TODO Set permissions: $file['writable']
+ $files[] = $file;
+ }
+ return $files;
+ }
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/lib_ampache.php b/apps/media/lib_ampache.php
index d35cca150b2..d5a093338cc 100644
--- a/apps/media/lib_ampache.php
+++ b/apps/media/lib_ampache.php
@@ -271,7 +271,6 @@ class OC_MEDIA_AMPACHE{
");
return;
}
- global $SITEROOT;
$filter=$params['filter'];
$albums=OC_MEDIA_COLLECTION::getAlbums($filter);
$artist=OC_MEDIA_COLLECTION::getArtistName($filter);
diff --git a/apps/media/lib_collection.php b/apps/media/lib_collection.php
index e65930f551d..cacab8e959f 100644
--- a/apps/media/lib_collection.php
+++ b/apps/media/lib_collection.php
@@ -27,7 +27,6 @@ class OC_MEDIA_COLLECTION{
public static $uid;
private static $artistIdCache=array();
private static $albumIdCache=array();
- private static $songIdCache=array();
private static $queries=array();
/**
@@ -152,7 +151,7 @@ class OC_MEDIA_COLLECTION{
return $artistId;
}else{
$query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_artists` (`artist_name`) VALUES (?)");
- $result=$query->execute(array($name));
+ $query->execute(array($name));
return self::getArtistId($name);;
}
}
diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php
index 9e687a4af2c..54502f42575 100644
--- a/apps/media/lib_media.php
+++ b/apps/media/lib_media.php
@@ -27,12 +27,12 @@ class OC_MEDIA{
* @param array $params, parameters passed from OC_Hook
*/
public static function loginListener($params){
- if(isset($_POST['user']) and $_POST['password']){
- $name=$_POST['user'];
+ if(isset($params['uid']) and $params['password']){
+ $name=$params['uid'];
$query=OCP\DB::prepare("SELECT user_id from *PREFIX*media_users WHERE user_id LIKE ?");
$uid=$query->execute(array($name))->fetchAll();
if(count($uid)==0){
- $password=hash('sha256',$_POST['password']);
+ $password=hash('sha256',$params['password']);
$query=OCP\DB::prepare("INSERT INTO *PREFIX*media_users (user_id, user_password_sha256) VALUES (?, ?);");
$query->execute(array($name,$password));
}
diff --git a/apps/news/.gitignore b/apps/news/.gitignore
new file mode 100644
index 00000000000..16469399de3
--- /dev/null
+++ b/apps/news/.gitignore
@@ -0,0 +1,5 @@
+3rdparty/*
+news.kdev4
+*~
+.kdev4
+img/*
diff --git a/apps/news/ajax/createfeed.php b/apps/news/ajax/createfeed.php
new file mode 100644
index 00000000000..754d874957f
--- /dev/null
+++ b/apps/news/ajax/createfeed.php
@@ -0,0 +1,37 @@
+
+*
+* This file is licensed under the Affero General Public License version 3 or later.
+* See the COPYING-README file
+*
+*/
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('news');
+OCP\JSON::callCheck();
+
+$userid = OCP\USER::getUser();
+
+$feedurl = trim($_POST['feedurl']);
+$folderid = trim($_POST['folderid']);
+
+$feed = OC_News_Utils::fetch($feedurl);
+$feedmapper = new OC_News_FeedMapper();
+$feedid = $feedmapper->save($feed, $folderid);
+
+$l = OC_L10N::get('news');
+
+if(!$feedid) {
+ OCP\JSON::error(array('data' => array('message' => $l->t('Error adding folder.'))));
+ OCP\Util::writeLog('news','ajax/createfeed.php: Error adding feed: '.$_POST['feedurl'], OCP\Util::ERROR);
+ exit();
+}
+
+//TODO: replace the following with a real success case. see contact/ajax/createaddressbook.php for inspirations
+OCP\JSON::success(array('data' => array('message' => $l->t('Feed added!'))));
+
diff --git a/apps/news/ajax/createfolder.php b/apps/news/ajax/createfolder.php
new file mode 100644
index 00000000000..b5f624604dc
--- /dev/null
+++ b/apps/news/ajax/createfolder.php
@@ -0,0 +1,41 @@
+
+*
+* This file is licensed under the Affero General Public License version 3 or later.
+* See the COPYING-README file
+*
+*/
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('news');
+OCP\JSON::callCheck();
+
+$userid = OCP\USER::getUser();
+
+$name = trim($_POST['name']);
+$parentid = trim($_POST['parentid']);
+
+$foldermapper = new OC_News_FolderMapper($userid);
+
+if($parentid != 0)
+ $folder = new OC_News_Folder($name, NULL, $foldermapper->find($parentid));
+else
+ $folder = new OC_News_Folder($name);
+
+$folderid = $foldermapper->save($folder);
+
+$l = OC_L10N::get('news');
+
+if(!$folderid) {
+ OCP\JSON::error(array('data' => array('message' => $l->t('Error adding folder.'))));
+ OCP\Util::writeLog('news','ajax/createfolder.php: Error adding folder: '.$_POST['name'], OCP\Util::ERROR);
+}
+else {
+ //TODO: replace the following with a real success case. see contact/ajax/createaddressbook.php for inspirations
+ OCP\JSON::success(array('data' => array('message' => $l->t('Folder added!'))));
+}
\ No newline at end of file
diff --git a/apps/news/ajax/deletefeed.php b/apps/news/ajax/deletefeed.php
new file mode 100644
index 00000000000..d31a9417d14
--- /dev/null
+++ b/apps/news/ajax/deletefeed.php
@@ -0,0 +1,33 @@
+
+*
+* This file is licensed under the Affero General Public License version 3 or later.
+* See the COPYING-README file
+*
+*/
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('news');
+OCP\JSON::callCheck();
+
+$userid = OCP\USER::getUser();
+
+$feedid = $_POST['feedid'];
+
+$feedmapper = new OC_News_FeedMapper();
+$success = $feedmapper->deleteById($feedid);
+
+$l = OC_L10N::get('news');
+
+if(!$success) {
+ OCP\JSON::error(array('data' => array('message' => $l->t('Error removing feed.'))));
+ OCP\Util::writeLog('news','ajax/deletefeed.php: Error removing feed: '.$_POST['feedid'], OCP\Util::ERROR);
+ exit();
+}
+
+OCP\JSON::success(array('data' => array( 'feedid' => $feedid )));
diff --git a/apps/news/ajax/deletefolder.php b/apps/news/ajax/deletefolder.php
new file mode 100644
index 00000000000..bf975c6c329
--- /dev/null
+++ b/apps/news/ajax/deletefolder.php
@@ -0,0 +1,33 @@
+
+*
+* This file is licensed under the Affero General Public License version 3 or later.
+* See the COPYING-README file
+*
+*/
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('news');
+OCP\JSON::callCheck();
+
+$userid = OCP\USER::getUser();
+
+$folderid = trim($_POST['folderid']);
+
+$foldermapper = new OC_News_FolderMapper();
+$success = $foldermapper->deleteById($folderid);
+
+$l = OC_L10N::get('news');
+
+if(!$success) {
+ OCP\JSON::error(array('data' => array('message' => $l->t('Error removing folder.'))));
+ OCP\Util::writeLog('news','ajax/deletefolder.php: Error removing folder: '.$_POST['folderid'], OCP\Util::ERROR);
+ exit();
+}
+
+OCP\JSON::success(array('data' => array( 'folderid' => $folderid )));
diff --git a/apps/news/ajax/feeddialog.php b/apps/news/ajax/feeddialog.php
new file mode 100644
index 00000000000..c42771c9884
--- /dev/null
+++ b/apps/news/ajax/feeddialog.php
@@ -0,0 +1,7 @@
+ assign('allfeeds', $allfeeds);
+$output -> printpage();
\ No newline at end of file
diff --git a/apps/news/ajax/folderdialog.php b/apps/news/ajax/folderdialog.php
new file mode 100644
index 00000000000..97b679e662e
--- /dev/null
+++ b/apps/news/ajax/folderdialog.php
@@ -0,0 +1,7 @@
+ assign('allfeeds', $allfeeds);
+$output -> printpage();
\ No newline at end of file
diff --git a/apps/news/ajax/markitem.php b/apps/news/ajax/markitem.php
new file mode 100644
index 00000000000..c42dc9350cf
--- /dev/null
+++ b/apps/news/ajax/markitem.php
@@ -0,0 +1,35 @@
+
+*
+* This file is licensed under the Affero General Public License version 3 or later.
+* See the COPYING-README file
+*
+*/
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('news');
+OCP\JSON::callCheck();
+
+$itemid = $_POST['itemid'];
+
+$itemmapper = new OC_News_ItemMapper();
+$item = $itemmapper->find($itemid);
+$item->setRead();
+$success = $itemmapper->update($item);
+
+$l = OC_L10N::get('news');
+
+if(!$success) {
+ OCP\JSON::error(array('data' => array('message' => $l->t('Error marking item as read.'))));
+ OCP\Util::writeLog('news','ajax/markitem.php: Error marking item as read: '.$_POST['itemid'], OCP\Util::ERROR);
+ exit();
+}
+
+//TODO: replace the following with a real success case. see contact/ajax/createaddressbook.php for inspirations
+OCP\JSON::success(array('data' => array('itemid' => $itemid )));
+
diff --git a/apps/news/ajax/populateroot.php b/apps/news/ajax/populateroot.php
new file mode 100644
index 00000000000..a408c25ec5d
--- /dev/null
+++ b/apps/news/ajax/populateroot.php
@@ -0,0 +1,18 @@
+populate('Everything', 0);
+
+if ($allfeeds) {
+ $feedid = isset( $_GET['feedid'] ) ? $_GET['feedid'] : null;
+ if ($feedid == null) {
+
+ }
+}
+else {
+ $feedid = 0;
+}
diff --git a/apps/news/appinfo/app.php b/apps/news/appinfo/app.php
new file mode 100644
index 00000000000..eb1b54455dd
--- /dev/null
+++ b/apps/news/appinfo/app.php
@@ -0,0 +1,42 @@
+
+*
+* This file is licensed under the Affero General Public License version 3 or later.
+* See the COPYING-README file
+*
+*/
+
+OC::$CLASSPATH['OC_News_Item'] = 'apps/news/lib/item.php';
+OC::$CLASSPATH['OC_News_Collection'] = 'apps/news/lib/collection.php';
+OC::$CLASSPATH['OC_News_Feed'] = 'apps/news/lib/feed.php';
+OC::$CLASSPATH['OC_News_Folder'] = 'apps/news/lib/folder.php';
+
+OC::$CLASSPATH['OC_News_FeedMapper'] = 'apps/news/lib/feedmapper.php';
+OC::$CLASSPATH['OC_News_ItemMapper'] = 'apps/news/lib/itemmapper.php';
+OC::$CLASSPATH['OC_News_FolderMapper'] = 'apps/news/lib/foldermapper.php';
+
+OC::$CLASSPATH['OC_News_Utils'] = 'apps/news/lib/utils.php';
+
+
+$l = new OC_l10n('news');
+
+OCP\App::registerPersonal('news', 'settings');
+
+OCP\App::register( array(
+ 'order' => 70,
+ 'id' => 'news',
+ 'name' => 'News'
+));
+
+OCP\App::addNavigationEntry( array(
+ 'id' => 'news',
+ 'order' => 74,
+ 'href' => OC_Helper::linkTo( 'news', 'index.php' ),
+ 'icon' => OC_Helper::imagePath( 'news', 'icon.svg' ),
+ 'name' => $l->t('News')
+));
+
diff --git a/apps/news/appinfo/database.xml b/apps/news/appinfo/database.xml
new file mode 100644
index 00000000000..3f5f6c44afd
--- /dev/null
+++ b/apps/news/appinfo/database.xml
@@ -0,0 +1,186 @@
+
+
+ *dbname*
+ true
+ false
+ latin1
+
\ No newline at end of file
diff --git a/apps/news/templates/part.addfolder.php b/apps/news/templates/part.addfolder.php
new file mode 100644
index 00000000000..e1217160cee
--- /dev/null
+++ b/apps/news/templates/part.addfolder.php
@@ -0,0 +1,22 @@
+
+
">
+
+
+
Add new folder
+
+
+
+
+
+ inc("part.folderlist"); ?>
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/news/templates/part.feeds.php b/apps/news/templates/part.feeds.php
new file mode 100644
index 00000000000..587e73f366a
--- /dev/null
+++ b/apps/news/templates/part.feeds.php
@@ -0,0 +1,31 @@
+
';
diff --git a/apps/news/templates/part.nofeeds.php b/apps/news/templates/part.nofeeds.php
new file mode 100644
index 00000000000..bb78dedbf89
--- /dev/null
+++ b/apps/news/templates/part.nofeeds.php
@@ -0,0 +1,3 @@
+
+ t('You have no feeds in your reader.') ?>
+
\ No newline at end of file
diff --git a/apps/news/templates/settings.php b/apps/news/templates/settings.php
new file mode 100644
index 00000000000..ccbb004f001
--- /dev/null
+++ b/apps/news/templates/settings.php
@@ -0,0 +1,8 @@
+
diff --git a/apps/news/templates/test.php b/apps/news/templates/test.php
new file mode 100644
index 00000000000..d8e31e67590
--- /dev/null
+++ b/apps/news/templates/test.php
@@ -0,0 +1,85 @@
+save($folder);
+
+$feed = OC_News_Utils::fetch( 'http://www.dabacon.org/newpontiff/?feed=rss2' );
+
+$feedmapper->save($feed, $folder->getId());
+
+$feed = $feedmapper->findWithItems($feed->getId());
+echo ' ' . $feed->getTitle() . ' ';
+$items = $feed->getItems();
+
+foreach($items as $item) {
+
+ echo $item->getTitle() . ' - ';
+ if ($item->isRead()) {
+ echo $l->t('Read');
+ }
+ else {
+ echo $l->t('Unread');
+ }
+ echo ' - ';
+ if ($item->isImportant()) {
+ echo $l->t('Important');
+ }
+ else {
+ echo $l->t('Not important');
+ }
+ echo ' ';
+ $item->setImportant();
+}
+
+echo ' ...after changing status';
+echo ' ' . $feed->getTitle() . ' ';
+
+foreach($items as $item) {
+ echo $item->getTitle() . ' - ';
+ if ($item->isRead()) {
+ echo $l->t('Read');
+ }
+ else {
+ echo $l->t('Unread');
+ }
+ echo ' - ';
+ if ($item->isImportant()) {
+ echo $l->t('Important');
+ }
+ else {
+ echo $l->t('Not important');
+ }
+ echo ' ';
+ $item->setUnimportant();
+}
+
+$feedmapper->save($feed, $folder->getId());
+
+echo ' ...after saving and reloading';
+
+$feed = $feedmapper->findWithItems($feed->getId());
+echo ' ' . $feed->getTitle() . ' ';
+$items = $feed->getItems();
+
+foreach($items as &$item) {
+
+ echo $item->getTitle() . ' - ';
+ if ($item->isRead()) {
+ echo $l->t('Read');
+ }
+ else {
+ echo $l->t('Unread');
+ }
+ echo ' - ';
+ if ($item->isImportant()) {
+ echo $l->t('Important');
+ }
+ else {
+ echo $l->t('Not important');
+ }
+ echo ' ';
+}
\ No newline at end of file
diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml
index fa878762a05..1388ad9c316 100644
--- a/apps/remoteStorage/appinfo/info.xml
+++ b/apps/remoteStorage/appinfo/info.xml
@@ -7,4 +7,7 @@
Michiel de Jong4true
+
+ webdav.php
+
diff --git a/apps/remoteStorage/appinfo/version b/apps/remoteStorage/appinfo/version
index 490f510fc27..0e2c93950bb 100644
--- a/apps/remoteStorage/appinfo/version
+++ b/apps/remoteStorage/appinfo/version
@@ -1 +1 @@
-0.6
\ No newline at end of file
+0.7
\ No newline at end of file
diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php
index 5d481f315f8..e8b237628c4 100644
--- a/apps/remoteStorage/appinfo/webfinger.php
+++ b/apps/remoteStorage/appinfo/webfinger.php
@@ -1,8 +1,8 @@
-
+
{
"rel":"remoteStorage",
- "template":"/apps/remoteStorage/WebDAV.php//remoteStorage/{category}/",
+ "template":"/remote.php/remoteStorage//remoteStorage/{category}/",
"api":"WebDAV",
"auth":"/?app=remoteStorage&getfile=auth.php&userid="
}
-
+
diff --git a/apps/remoteStorage/lib_remoteStorage.php b/apps/remoteStorage/lib_remoteStorage.php
index 42cd9c90f64..c1765640c5d 100644
--- a/apps/remoteStorage/lib_remoteStorage.php
+++ b/apps/remoteStorage/lib_remoteStorage.php
@@ -17,12 +17,11 @@ class OC_remoteStorage {
$user=OCP\USER::getUser();
$query=OCP\DB::prepare("SELECT token FROM *PREFIX*authtoken WHERE user=? AND appUrl=? AND category=? LIMIT 1");
$result=$query->execute(array($user, $appUrl, $categories));
- $ret = array();
if($row=$result->fetchRow()) {
- return base64_encode('remoteStorage:'.$row['token']);
- } else {
- return false;
- }
+ return base64_encode('remoteStorage:'.$row['token']);
+ } else {
+ return false;
+ }
}
public static function getAllTokens() {
@@ -42,13 +41,13 @@ class OC_remoteStorage {
public static function deleteToken($token) {
$user=OCP\USER::getUser();
$query=OCP\DB::prepare("DELETE FROM *PREFIX*authtoken WHERE token=? AND user=?");
- $result=$query->execute(array($token,$user));
+ $query->execute(array($token,$user));
return 'unknown';//how can we see if any rows were affected?
}
private static function addToken($token, $appUrl, $categories){
$user=OCP\USER::getUser();
$query=OCP\DB::prepare("INSERT INTO *PREFIX*authtoken (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)");
- $result=$query->execute(array($token,$appUrl,$user,$categories));
+ $query->execute(array($token,$appUrl,$user,$categories));
}
public static function createCategories($appUrl, $categories) {
$token=uniqid();
diff --git a/apps/remoteStorage/oauth_ro_auth.php b/apps/remoteStorage/oauth_ro_auth.php
index 12d02d1cf5d..bed3093c3b3 100644
--- a/apps/remoteStorage/oauth_ro_auth.php
+++ b/apps/remoteStorage/oauth_ro_auth.php
@@ -9,10 +9,10 @@
class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBasic {
private $validTokens;
- private $category;
+ private $category;
public function __construct($validTokensArg, $categoryArg) {
$this->validTokens = $validTokensArg;
- $this->category = $categoryArg;
+ $this->category = $categoryArg;
}
/**
@@ -25,16 +25,16 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa
*/
protected function validateUserPass($username, $password){
//always give read-only:
- if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS')
+ if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS')
|| (isset($this->validTokens[$password]))
- || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
- ) {
+ || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
+ ) {
OC_Util::setUpFS();
return true;
} else {
- //var_export($_SERVER);
- //var_export($this->validTokens);
- //die('not getting in with "'.$username.'"/"'.$password.'"!');
+ //var_export($_SERVER);
+ //var_export($this->validTokens);
+ //die('not getting in with "'.$username.'"/"'.$password.'"!');
return false;
}
}
@@ -48,8 +48,8 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa
$userpass = $auth->getUserPass();
if (!$userpass) {
if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS')
- ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
- ) {
+ ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public'))
+ ) {
$userpass = array('', '');
} else {
$auth->requireLogin();
diff --git a/apps/remoteStorage/WebDAV.php b/apps/remoteStorage/webdav.php
similarity index 72%
rename from apps/remoteStorage/WebDAV.php
rename to apps/remoteStorage/webdav.php
index 7a81c18e0af..8d8ec6a45a1 100644
--- a/apps/remoteStorage/WebDAV.php
+++ b/apps/remoteStorage/webdav.php
@@ -25,22 +25,7 @@
*
*/
-
-// Do not load FS ...
-$RUNTIME_NOSETUPFS = true;
-
-
-require_once('../../lib/base.php');
-
-require_once('../../lib/user.php');
-require_once('../../lib/public/user.php');
-
-require_once('../../lib/app.php');
-require_once('../../lib/public/app.php');
-
-require_once('../../3rdparty/Sabre/DAV/Auth/IBackend.php');
-require_once('../../3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php');
-require_once('../../lib/connector/sabre/auth.php');
+OC_App::loadApps(array('filesystem','authentication'));
OCP\App::checkAppEnabled('remoteStorage');
require_once('lib_remoteStorage.php');
@@ -61,14 +46,15 @@ if(isset($_SERVER['HTTP_ORIGIN'])) {
header('Access-Control-Allow-Origin: *');
}
-$path = substr($_SERVER["REQUEST_URI"], strlen($_SERVER["SCRIPT_NAME"]));
+$path = substr($_SERVER["REQUEST_URI"], strlen($baseuri));
+
$pathParts = explode('/', $path);
// for webdav:
-// 0/ 1 / 2 / 3...
-// /$ownCloudUser/remoteStorage/$category/
+// 0 / 1 / 2...
+// $ownCloudUser/remoteStorage/$category/
-if(count($pathParts) >= 3 && $pathParts[0] == '') {
- list($dummy, $ownCloudUser, $dummy2, $category) = $pathParts;
+if(count($pathParts) >= 2) {
+ list($ownCloudUser, $dummy2, $category) = $pathParts;
OC_Util::setupFS($ownCloudUser);
@@ -77,13 +63,13 @@ if(count($pathParts) >= 3 && $pathParts[0] == '') {
$server = new Sabre_DAV_Server($publicDir);
// Path to our script
- $server->setBaseUri(OC::$WEBROOT."/apps/remoteStorage/WebDAV.php/$ownCloudUser");
+ $server->setBaseUri($baseuri.$ownCloudUser);
// Auth backend
$authBackend = new OC_Connector_Sabre_Auth_ro_oauth(
- OC_remoteStorage::getValidTokens($ownCloudUser, $category),
- $category
- );
+ OC_remoteStorage::getValidTokens($ownCloudUser, $category),
+ $category
+ );
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud');//should use $validTokens here
$server->addPlugin($authPlugin);
diff --git a/apps/shorty/.htaccess b/apps/shorty/.htaccess
new file mode 100644
index 00000000000..b7c4189ae49
--- /dev/null
+++ b/apps/shorty/.htaccess
@@ -0,0 +1,45 @@
+#
+# @package shorty an ownCloud url shortener plugin
+# @category internet
+# @author Christian Reiner
+# @copyright 2011-2012 Christian Reiner
+# @license GNU Affero General Public license (AGPL)
+# @link information
+# @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+#
+# 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 .
+#
+
+# @file .htaccess
+# These rules try to detect any requests that carry a "shorty id" and map them to forward.php?
+# That way you can use shorter (or more simple) urls to publish or relay links
+# @author Christian Reiner
+
+RewriteEngine On
+
+# example: http://.../apps/shorty/index.php/0123456789
+RewriteRule index\.php/([a-z0-9]{4,12})$ ../../public.php?service=shorty_relay&id=$1 [NC,L]
+
+# example: http://.../apps/shorty/index.php?0123456789
+RewriteCond %{QUERY_STRING} ^([a-z0-9]{4,12})$ [NC]
+RewriteRule index\.php ../../public.php?service=shorty_relay&id=%{QUERY_STRING} [L]
+
+# example: http://.../apps/shorty/?0123456789
+RewriteCond %{QUERY_STRING} ^([a-z0-9]{4,12})$ [NC]
+RewriteCond %{REQUEST_URI} meta/.+\.php
+RewriteRule - ../../public.php?service=shorty_relay&id=%{QUERY_STRING} [L]
+
+# example: http://.../apps/shorty/0123456789
+RewriteRule ^([a-z0-9]{4,12})$ ../../public.php?service=shorty_relay&id=$1 [NC,L]
diff --git a/apps/shorty/3rdparty/js/jquery.tinysort.min.js b/apps/shorty/3rdparty/js/jquery.tinysort.min.js
new file mode 100644
index 00000000000..225f799ca9d
--- /dev/null
+++ b/apps/shorty/3rdparty/js/jquery.tinysort.min.js
@@ -0,0 +1 @@
+(function(b){var o=!1,d=null,u=parseFloat,j=String.fromCharCode,q=Math.min,l=/(\d+\.?\d*)$/g,g,a=[],h,m,t=9472,f={},c;for(var p=32,k=j(p),r=255;p96&&ab<123;if(!I){if(x=="["){var D=S.length,M=D?S[D-1]:X,w=g.substr(ad+1).match(/[^\]]*/)[0],R=w.match(/{[^}]*}/g);if(R){for(ac=0,Z=R.length;acak?1:0));if(!au&&T.charOrder){if(m){for(var ax in f){var al=f[ax];am=am.replace(ax,al);ak=ak.replace(ax,al)}}if(am.match(c)!==d||ak.match(c)!==d){for(var ap=0,ao=q(am.length,ak.length);api?1:0))){break}}}}return ai}}Q.each(function(ak,al){var am=b(al),ai=W?(J?C.filter(al):am.find(V)):am,an=ah?ai.data(T.data):(H?ai.attr(T.attr):(T.useVal?ai.val():ai.text())),aj=am.parent();if(!ae[aj]){ae[aj]={s:[],n:[]}}if(ai.length>0){ae[aj].s.push({s:an,e:am,n:ak})}else{ae[aj].n.push({e:am,n:ak})}});for(v in ae){ae[v].s.sort(F)}for(v in ae){var ag=ae[v],K=[],Y=z,af=[0,0],ad;switch(T.place){case"first":b.each(ag.s,function(ai,aj){Y=q(Y,aj.n)});break;case"org":b.each(ag.s,function(ai,aj){K.push(aj.n)});break;case"end":Y=ag.n.length;break;default:Y=0}for(ad=0;ad=Y&&ad
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+
+/*
+ * Version: 1.1.4
+ * Build: 2010100721
+ */
+
+
+
+//---- qrconst.php -----------------------------
+
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Common constants
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ // Encoding modes
+
+ define('QR_MODE_NUL', -1);
+ define('QR_MODE_NUM', 0);
+ define('QR_MODE_AN', 1);
+ define('QR_MODE_8', 2);
+ define('QR_MODE_KANJI', 3);
+ define('QR_MODE_STRUCTURE', 4);
+
+ // Levels of error correction.
+
+ define('QR_ECLEVEL_L', 0);
+ define('QR_ECLEVEL_M', 1);
+ define('QR_ECLEVEL_Q', 2);
+ define('QR_ECLEVEL_H', 3);
+
+ // Supported output formats
+
+ define('QR_FORMAT_TEXT', 0);
+ define('QR_FORMAT_PNG', 1);
+
+ class qrstr {
+ public static function set(&$srctab, $x, $y, $repl, $replLen = false) {
+ $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl));
+ }
+ }
+
+
+
+//---- merged_config.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Config file, tuned-up for merged verion
+ */
+
+ define('QR_CACHEABLE', false); // use cache - more disk reads but less CPU power, masks and format templates are stored there
+ define('QR_CACHE_DIR', false); // used when QR_CACHEABLE === true
+ define('QR_LOG_DIR', false); // default error logs dir
+
+ define('QR_FIND_BEST_MASK', true); // if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code
+ define('QR_FIND_FROM_RANDOM', 2); // if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly
+ define('QR_DEFAULT_MASK', 2); // when QR_FIND_BEST_MASK === false
+
+ define('QR_PNG_MAXIMUM_SIZE', 1024); // maximum allowed png image width (in pixels), tune to make sure GD and PHP can handle such big images
+
+
+
+
+//---- qrtools.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Toolset, handy and debug utilites.
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRtools {
+
+ //----------------------------------------------------------------------
+ public static function binarize($frame)
+ {
+ $len = count($frame);
+ foreach ($frame as &$frameLine) {
+
+ for($i=0; $i<$len; $i++) {
+ $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0';
+ }
+ }
+
+ return $frame;
+ }
+
+ //----------------------------------------------------------------------
+ public static function tcpdfBarcodeArray($code, $mode = 'QR,L', $tcPdfVersion = '4.5.037')
+ {
+ $barcode_array = array();
+
+ if (!is_array($mode))
+ $mode = explode(',', $mode);
+
+ $eccLevel = 'L';
+
+ if (count($mode) > 1) {
+ $eccLevel = $mode[1];
+ }
+
+ $qrTab = QRcode::text($code, false, $eccLevel);
+ $size = count($qrTab);
+
+ $barcode_array['num_rows'] = $size;
+ $barcode_array['num_cols'] = $size;
+ $barcode_array['bcode'] = array();
+
+ foreach ($qrTab as $line) {
+ $arrAdd = array();
+ foreach(str_split($line) as $char)
+ $arrAdd[] = ($char=='1')?1:0;
+ $barcode_array['bcode'][] = $arrAdd;
+ }
+
+ return $barcode_array;
+ }
+
+ //----------------------------------------------------------------------
+ public static function clearCache()
+ {
+ self::$frames = array();
+ }
+
+ //----------------------------------------------------------------------
+ public static function buildCache()
+ {
+ QRtools::markTime('before_build_cache');
+
+ $mask = new QRmask();
+ for ($a=1; $a <= QRSPEC_VERSION_MAX; $a++) {
+ $frame = QRspec::newFrame($a);
+ if (QR_IMAGE) {
+ $fileName = QR_CACHE_DIR.'frame_'.$a.'.png';
+ QRimage::png(self::binarize($frame), $fileName, 1, 0);
+ }
+
+ $width = count($frame);
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+ for ($maskNo=0; $maskNo<8; $maskNo++)
+ $mask->makeMaskNo($maskNo, $width, $frame, $bitMask, true);
+ }
+
+ QRtools::markTime('after_build_cache');
+ }
+
+ //----------------------------------------------------------------------
+ public static function log($outfile, $err)
+ {
+ if (QR_LOG_DIR !== false) {
+ if ($err != '') {
+ if ($outfile !== false) {
+ file_put_contents(QR_LOG_DIR.basename($outfile).'-errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ } else {
+ file_put_contents(QR_LOG_DIR.'errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND);
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function dumpMask($frame)
+ {
+ $width = count($frame);
+ for($y=0;$y<$width;$y++) {
+ for($x=0;$x<$width;$x++) {
+ echo ord($frame[$y][$x]).',';
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function markTime($markerId)
+ {
+ list($usec, $sec) = explode(" ", microtime());
+ $time = ((float)$usec + (float)$sec);
+
+ if (!isset($GLOBALS['qr_time_bench']))
+ $GLOBALS['qr_time_bench'] = array();
+
+ $GLOBALS['qr_time_bench'][$markerId] = $time;
+ }
+
+ //----------------------------------------------------------------------
+ public static function timeBenchmark()
+ {
+ self::markTime('finish');
+
+ $lastTime = 0;
+ $startTime = 0;
+ $p = 0;
+
+ echo '
+
BENCHMARK
+ ';
+
+ foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) {
+ if ($p > 0) {
+ echo '
';
+
+ } else {
+
+ foreach ($frame as &$frameLine) {
+ $frameLine = join('', explode("\xc0", $frameLine));
+ $frameLine = join('▒', explode("\xc1", $frameLine));
+ $frameLine = join('', explode("\xa0", $frameLine));
+ $frameLine = join('▒', explode("\xa1", $frameLine));
+ $frameLine = join('◇', explode("\x84", $frameLine)); //format 0
+ $frameLine = join('◆', explode("\x85", $frameLine)); //format 1
+ $frameLine = join('☢', explode("\x81", $frameLine)); //special bit
+ $frameLine = join('', explode("\x90", $frameLine)); //clock 0
+ $frameLine = join('◷', explode("\x91", $frameLine)); //clock 1
+ $frameLine = join('', explode("\x88", $frameLine)); //version
+ $frameLine = join('▒', explode("\x89", $frameLine)); //version
+ $frameLine = join('♦', explode("\x01", $frameLine));
+ $frameLine = join('⋅', explode("\0", $frameLine));
+ }
+
+ ?>
+
+ ";
+ echo join(" ", $frame);
+ echo "";
+
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($frame)
+ {
+ return gzcompress(join("\n", $frame), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ return explode("\n", gzuncompress($code));
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFrame($version)
+ {
+ if($version < 1 || $version > QRSPEC_VERSION_MAX)
+ return null;
+
+ if(!isset(self::$frames[$version])) {
+
+ $fileName = QR_CACHE_DIR.'frame_'.$version.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ self::$frames[$version] = self::unserial(file_get_contents($fileName));
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ file_put_contents($fileName, self::serial(self::$frames[$version]));
+ }
+ } else {
+ self::$frames[$version] = self::createFrame($version);
+ }
+ }
+
+ if(is_null(self::$frames[$version]))
+ return null;
+
+ return self::$frames[$version];
+ }
+
+ //----------------------------------------------------------------------
+ public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; }
+ public static function rsBlockNum1($spec) { return $spec[0]; }
+ public static function rsDataCodes1($spec) { return $spec[1]; }
+ public static function rsEccCodes1($spec) { return $spec[2]; }
+ public static function rsBlockNum2($spec) { return $spec[3]; }
+ public static function rsDataCodes2($spec) { return $spec[4]; }
+ public static function rsEccCodes2($spec) { return $spec[2]; }
+ public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); }
+ public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; }
+
+ }
+
+
+
+//---- qrimage.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Image output of code using GD2
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('QR_IMAGE', true);
+
+ class QRimage {
+
+ //----------------------------------------------------------------------
+ public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/png");
+ ImagePng($image);
+ } else {
+ if($saveandprint===TRUE){
+ ImagePng($image, $filename);
+ header("Content-type: image/png");
+ ImagePng($image);
+ }else{
+ ImagePng($image, $filename);
+ }
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85)
+ {
+ $image = self::image($frame, $pixelPerPoint, $outerFrame);
+
+ if ($filename === false) {
+ Header("Content-type: image/jpeg");
+ ImageJpeg($image, null, $q);
+ } else {
+ ImageJpeg($image, $filename, $q);
+ }
+
+ ImageDestroy($image);
+ }
+
+ //----------------------------------------------------------------------
+ private static function image($frame, $pixelPerPoint = 4, $outerFrame = 4)
+ {
+ $h = count($frame);
+ $w = strlen($frame[0]);
+
+ $imgW = $w + 2*$outerFrame;
+ $imgH = $h + 2*$outerFrame;
+
+ $base_image =ImageCreate($imgW, $imgH);
+
+ $col[0] = ImageColorAllocate($base_image,255,255,255);
+ $col[1] = ImageColorAllocate($base_image,0,0,0);
+
+ imagefill($base_image, 0, 0, $col[0]);
+
+ for($y=0; $y<$h; $y++) {
+ for($x=0; $x<$w; $x++) {
+ if ($frame[$y][$x] == '1') {
+ ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]);
+ }
+ }
+ }
+
+ $target_image =ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint);
+ ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH);
+ ImageDestroy($base_image);
+
+ return $target_image;
+ }
+ }
+
+
+
+//---- qrinput.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Input encoding class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('STRUCTURE_HEADER_BITS', 20);
+ define('MAX_STRUCTURED_SYMBOLS', 16);
+
+ class QRinputItem {
+
+ public $mode;
+ public $size;
+ public $data;
+ public $bstream;
+
+ public function __construct($mode, $size, $data, $bstream = null)
+ {
+ $setData = array_slice($data, 0, $size);
+
+ if (count($setData) < $size) {
+ $setData = array_merge($setData, array_fill(0,$size-count($setData),0));
+ }
+
+ if(!QRinput::check($mode, $size, $setData)) {
+ throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData));
+ return null;
+ }
+
+ $this->mode = $mode;
+ $this->size = $size;
+ $this->data = $setData;
+ $this->bstream = $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeNum($version)
+ {
+ try {
+
+ $words = (int)($this->size / 3);
+ $bs = new QRbitstream();
+
+ $val = 0x1;
+ $bs->appendNum(4, $val);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (ord($this->data[$i*3 ]) - ord('0')) * 100;
+ $val += (ord($this->data[$i*3+1]) - ord('0')) * 10;
+ $val += (ord($this->data[$i*3+2]) - ord('0'));
+ $bs->appendNum(10, $val);
+ }
+
+ if($this->size - $words * 3 == 1) {
+ $val = ord($this->data[$words*3]) - ord('0');
+ $bs->appendNum(4, $val);
+ } else if($this->size - $words * 3 == 2) {
+ $val = (ord($this->data[$words*3 ]) - ord('0')) * 10;
+ $val += (ord($this->data[$words*3+1]) - ord('0'));
+ $bs->appendNum(7, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeAn($version)
+ {
+ try {
+ $words = (int)($this->size / 2);
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x02);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size);
+
+ for($i=0; $i<$words; $i++) {
+ $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45;
+ $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1]));
+
+ $bs->appendNum(11, $val);
+ }
+
+ if($this->size & 1) {
+ $val = QRinput::lookAnTable(ord($this->data[$words * 2]));
+ $bs->appendNum(6, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeMode8($version)
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x4);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size);
+
+ for($i=0; $i<$this->size; $i++) {
+ $bs->appendNum(8, ord($this->data[$i]));
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeKanji($version)
+ {
+ try {
+
+ $bs = new QRbitrtream();
+
+ $bs->appendNum(4, 0x8);
+ $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2));
+
+ for($i=0; $i<$this->size; $i+=2) {
+ $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);
+ if($val <= 0x9ffc) {
+ $val -= 0x8140;
+ } else {
+ $val -= 0xc140;
+ }
+
+ $h = ($val >> 8) * 0xc0;
+ $val = ($val & 0xff) + $h;
+
+ $bs->appendNum(13, $val);
+ }
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeModeStructure()
+ {
+ try {
+ $bs = new QRbitstream();
+
+ $bs->appendNum(4, 0x03);
+ $bs->appendNum(4, ord($this->data[1]) - 1);
+ $bs->appendNum(4, ord($this->data[0]) - 1);
+ $bs->appendNum(8, ord($this->data[2]));
+
+ $this->bstream = $bs;
+ return 0;
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSizeOfEntry($version)
+ {
+ $bits = 0;
+
+ if($version == 0)
+ $version = 1;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break;
+ case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break;
+ case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break;
+ case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break;
+ case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS;
+ default:
+ return 0;
+ }
+
+ $l = QRspec::lengthIndicator($this->mode, $version);
+ $m = 1 << $l;
+ $num = (int)(($this->size + $m - 1) / $m);
+
+ $bits += $num * (4 + $l);
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeBitStream($version)
+ {
+ try {
+
+ unset($this->bstream);
+ $words = QRspec::maximumWords($this->mode, $version);
+
+ if($this->size > $words) {
+
+ $st1 = new QRinputItem($this->mode, $words, $this->data);
+ $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words));
+
+ $st1->encodeBitStream($version);
+ $st2->encodeBitStream($version);
+
+ $this->bstream = new QRbitstream();
+ $this->bstream->append($st1->bstream);
+ $this->bstream->append($st2->bstream);
+
+ unset($st1);
+ unset($st2);
+
+ } else {
+
+ $ret = 0;
+
+ switch($this->mode) {
+ case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break;
+ case QR_MODE_AN: $ret = $this->encodeModeAn($version); break;
+ case QR_MODE_8: $ret = $this->encodeMode8($version); break;
+ case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break;
+ case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break;
+
+ default:
+ break;
+ }
+
+ if($ret < 0)
+ return -1;
+ }
+
+ return $this->bstream->size();
+
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+ };
+
+ //##########################################################################
+
+ class QRinput {
+
+ public $items;
+
+ private $version;
+ private $level;
+
+ //----------------------------------------------------------------------
+ public function __construct($version = 0, $level = QR_ECLEVEL_L)
+ {
+ if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid version no');
+ return NULL;
+ }
+
+ $this->version = $version;
+ $this->level = $level;
+ }
+
+ //----------------------------------------------------------------------
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ //----------------------------------------------------------------------
+ public function setVersion($version)
+ {
+ if($version < 0 || $version > QRSPEC_VERSION_MAX) {
+ throw new Exception('Invalid version no');
+ return -1;
+ }
+
+ $this->version = $version;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getErrorCorrectionLevel()
+ {
+ return $this->level;
+ }
+
+ //----------------------------------------------------------------------
+ public function setErrorCorrectionLevel($level)
+ {
+ if($level > QR_ECLEVEL_H) {
+ throw new Exception('Invalid ECLEVEL');
+ return -1;
+ }
+
+ $this->level = $level;
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendEntry(QRinputItem $entry)
+ {
+ $this->items[] = $entry;
+ }
+
+ //----------------------------------------------------------------------
+ public function append($mode, $size, $data)
+ {
+ try {
+ $entry = new QRinputItem($mode, $size, $data);
+ $this->items[] = $entry;
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+
+ public function insertStructuredAppendHeader($size, $index, $parity)
+ {
+ if( $size > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong size');
+ }
+
+ if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {
+ throw new Exception('insertStructuredAppendHeader wrong index');
+ }
+
+ $buf = array($size, $index, $parity);
+
+ try {
+ $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf);
+ array_unshift($this->items, $entry);
+ return 0;
+ } catch (Exception $e) {
+ return -1;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function calcParity()
+ {
+ $parity = 0;
+
+ foreach($this->items as $item) {
+ if($item->mode != QR_MODE_STRUCTURE) {
+ for($i=$item->size-1; $i>=0; $i--) {
+ $parity ^= $item->data[$i];
+ }
+ }
+ }
+
+ return $parity;
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeNum($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeNum($size)
+ {
+ $w = (int)$size / 3;
+ $bits = $w * 10;
+
+ switch($size - $w * 3) {
+ case 1:
+ $bits += 4;
+ break;
+ case 2:
+ $bits += 7;
+ break;
+ default:
+ break;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static $anTable = array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ );
+
+ //----------------------------------------------------------------------
+ public static function lookAnTable($c)
+ {
+ return (($c > 127)?-1:self::$anTable[$c]);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeAn($size, $data)
+ {
+ for($i=0; $i<$size; $i++) {
+ if (self::lookAnTable(ord($data[$i])) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsModeAn($size)
+ {
+ $w = (int)($size / 2);
+ $bits = $w * 11;
+
+ if($size & 1) {
+ $bits += 6;
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public static function estimateBitsMode8($size)
+ {
+ return $size * 8;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateBitsModeKanji($size)
+ {
+ return (int)(($size / 2) * 13);
+ }
+
+ //----------------------------------------------------------------------
+ public static function checkModeKanji($size, $data)
+ {
+ if($size & 1)
+ return false;
+
+ for($i=0; $i<$size; $i+=2) {
+ $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
+ if( $val < 0x8140
+ || ($val > 0x9ffc && $val < 0xe040)
+ || $val > 0xebbf) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /***********************************************************************
+ * Validation
+ **********************************************************************/
+
+ public static function check($mode, $size, $data)
+ {
+ if($size <= 0)
+ return false;
+
+ switch($mode) {
+ case QR_MODE_NUM: return self::checkModeNum($size, $data); break;
+ case QR_MODE_AN: return self::checkModeAn($size, $data); break;
+ case QR_MODE_KANJI: return self::checkModeKanji($size, $data); break;
+ case QR_MODE_8: return true; break;
+ case QR_MODE_STRUCTURE: return true; break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function estimateBitStreamSize($version)
+ {
+ $bits = 0;
+
+ foreach($this->items as $item) {
+ $bits += $item->estimateBitStreamSizeOfEntry($version);
+ }
+
+ return $bits;
+ }
+
+ //----------------------------------------------------------------------
+ public function estimateVersion()
+ {
+ $version = 0;
+ $prev = 0;
+ do {
+ $prev = $version;
+ $bits = $this->estimateBitStreamSize($prev);
+ $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if ($version < 0) {
+ return -1;
+ }
+ } while ($version > $prev);
+
+ return $version;
+ }
+
+ //----------------------------------------------------------------------
+ public static function lengthOfCode($mode, $version, $bits)
+ {
+ $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);
+ switch($mode) {
+ case QR_MODE_NUM:
+ $chunks = (int)($payload / 10);
+ $remain = $payload - $chunks * 10;
+ $size = $chunks * 3;
+ if($remain >= 7) {
+ $size += 2;
+ } else if($remain >= 4) {
+ $size += 1;
+ }
+ break;
+ case QR_MODE_AN:
+ $chunks = (int)($payload / 11);
+ $remain = $payload - $chunks * 11;
+ $size = $chunks * 2;
+ if($remain >= 6)
+ $size++;
+ break;
+ case QR_MODE_8:
+ $size = (int)($payload / 8);
+ break;
+ case QR_MODE_KANJI:
+ $size = (int)(($payload / 13) * 2);
+ break;
+ case QR_MODE_STRUCTURE:
+ $size = (int)($payload / 8);
+ break;
+ default:
+ $size = 0;
+ break;
+ }
+
+ $maxsize = QRspec::maximumWords($mode, $version);
+ if($size < 0) $size = 0;
+ if($size > $maxsize) $size = $maxsize;
+
+ return $size;
+ }
+
+ //----------------------------------------------------------------------
+ public function createBitStream()
+ {
+ $total = 0;
+
+ foreach($this->items as $item) {
+ $bits = $item->encodeBitStream($this->version);
+
+ if($bits < 0)
+ return -1;
+
+ $total += $bits;
+ }
+
+ return $total;
+ }
+
+ //----------------------------------------------------------------------
+ public function convertData()
+ {
+ $ver = $this->estimateVersion();
+ if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ }
+
+ for(;;) {
+ $bits = $this->createBitStream();
+
+ if($bits < 0)
+ return -1;
+
+ $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
+ if($ver < 0) {
+ throw new Exception('WRONG VERSION');
+ return -1;
+ } else if($ver > $this->getVersion()) {
+ $this->setVersion($ver);
+ } else {
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendPaddingBit(&$bstream)
+ {
+ $bits = $bstream->size();
+ $maxwords = QRspec::getDataLength($this->version, $this->level);
+ $maxbits = $maxwords * 8;
+
+ if ($maxbits == $bits) {
+ return 0;
+ }
+
+ if ($maxbits - $bits < 5) {
+ return $bstream->appendNum($maxbits - $bits, 0);
+ }
+
+ $bits += 4;
+ $words = (int)(($bits + 7) / 8);
+
+ $padding = new QRbitstream();
+ $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
+
+ if($ret < 0)
+ return $ret;
+
+ $padlen = $maxwords - $words;
+
+ if($padlen > 0) {
+
+ $padbuf = array();
+ for($i=0; $i<$padlen; $i++) {
+ $padbuf[$i] = ($i&1)?0x11:0xec;
+ }
+
+ $ret = $padding->appendBytes($padlen, $padbuf);
+
+ if($ret < 0)
+ return $ret;
+
+ }
+
+ $ret = $bstream->append($padding);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function mergeBitStream()
+ {
+ if($this->convertData() < 0) {
+ return null;
+ }
+
+ $bstream = new QRbitstream();
+
+ foreach($this->items as $item) {
+ $ret = $bstream->append($item->bstream);
+ if($ret < 0) {
+ return null;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getBitStream()
+ {
+
+ $bstream = $this->mergeBitStream();
+
+ if($bstream == null) {
+ return null;
+ }
+
+ $ret = $this->appendPaddingBit($bstream);
+ if($ret < 0) {
+ return null;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function getByteStream()
+ {
+ $bstream = $this->getBitStream();
+ if($bstream == null) {
+ return null;
+ }
+
+ return $bstream->toByte();
+ }
+ }
+
+
+
+
+
+
+//---- qrbitstream.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Bitstream class
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRbitstream {
+
+ public $data = array();
+
+ //----------------------------------------------------------------------
+ public function size()
+ {
+ return count($this->data);
+ }
+
+ //----------------------------------------------------------------------
+ public function allocate($setLength)
+ {
+ $this->data = array_fill(0, $setLength, 0);
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromNum($bits, $num)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($bits);
+
+ $mask = 1 << ($bits - 1);
+ for($i=0; $i<$bits; $i++) {
+ if($num & $mask) {
+ $bstream->data[$i] = 1;
+ } else {
+ $bstream->data[$i] = 0;
+ }
+ $mask = $mask >> 1;
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public static function newFromBytes($size, $data)
+ {
+ $bstream = new QRbitstream();
+ $bstream->allocate($size * 8);
+ $p=0;
+
+ for($i=0; $i<$size; $i++) {
+ $mask = 0x80;
+ for($j=0; $j<8; $j++) {
+ if($data[$i] & $mask) {
+ $bstream->data[$p] = 1;
+ } else {
+ $bstream->data[$p] = 0;
+ }
+ $p++;
+ $mask = $mask >> 1;
+ }
+ }
+
+ return $bstream;
+ }
+
+ //----------------------------------------------------------------------
+ public function append(QRbitstream $arg)
+ {
+ if (is_null($arg)) {
+ return -1;
+ }
+
+ if($arg->size() == 0) {
+ return 0;
+ }
+
+ if($this->size() == 0) {
+ $this->data = $arg->data;
+ return 0;
+ }
+
+ $this->data = array_values(array_merge($this->data, $arg->data));
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendNum($bits, $num)
+ {
+ if ($bits == 0)
+ return 0;
+
+ $b = QRbitstream::newFromNum($bits, $num);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function appendBytes($size, $data)
+ {
+ if ($size == 0)
+ return 0;
+
+ $b = QRbitstream::newFromBytes($size, $data);
+
+ if(is_null($b))
+ return -1;
+
+ $ret = $this->append($b);
+ unset($b);
+
+ return $ret;
+ }
+
+ //----------------------------------------------------------------------
+ public function toByte()
+ {
+
+ $size = $this->size();
+
+ if($size == 0) {
+ return array();
+ }
+
+ $data = array_fill(0, (int)(($size + 7) / 8), 0);
+ $bytes = (int)($size / 8);
+
+ $p = 0;
+
+ for($i=0; $i<$bytes; $i++) {
+ $v = 0;
+ for($j=0; $j<8; $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$i] = $v;
+ }
+
+ if($size & 7) {
+ $v = 0;
+ for($j=0; $j<($size & 7); $j++) {
+ $v = $v << 1;
+ $v |= $this->data[$p];
+ $p++;
+ }
+ $data[$bytes] = $v;
+ }
+
+ return $data;
+ }
+
+ }
+
+
+
+
+//---- qrsplit.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Input splitting classes
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * The following data / specifications are taken from
+ * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004)
+ * or
+ * "Automatic identification and data capture techniques --
+ * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+ class QRsplit {
+
+ public $dataStr = '';
+ public $input;
+ public $modeHint;
+
+ //----------------------------------------------------------------------
+ public function __construct($dataStr, $input, $modeHint)
+ {
+ $this->dataStr = $dataStr;
+ $this->input = $input;
+ $this->modeHint = $modeHint;
+ }
+
+ //----------------------------------------------------------------------
+ public static function isdigitat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9')));
+ }
+
+ //----------------------------------------------------------------------
+ public static function isalnumat($str, $pos)
+ {
+ if ($pos >= strlen($str))
+ return false;
+
+ return (QRinput::lookAnTable(ord($str[$pos])) >= 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function identifyMode($pos)
+ {
+ if ($pos >= strlen($this->dataStr))
+ return QR_MODE_NUL;
+
+ $c = $this->dataStr[$pos];
+
+ if(self::isdigitat($this->dataStr, $pos)) {
+ return QR_MODE_NUM;
+ } else if(self::isalnumat($this->dataStr, $pos)) {
+ return QR_MODE_AN;
+ } else if($this->modeHint == QR_MODE_KANJI) {
+
+ if ($pos+1 < strlen($this->dataStr))
+ {
+ $d = $this->dataStr[$pos+1];
+ $word = (ord($c) << 8) | ord($d);
+ if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) {
+ return QR_MODE_KANJI;
+ }
+ }
+ }
+
+ return QR_MODE_8;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatNum()
+ {
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+ while(self::isdigitat($this->dataStr, $p)) {
+ $p++;
+ }
+
+ $run = $p;
+ $mode = $this->identifyMode($p);
+
+ if($mode == QR_MODE_8) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+ if($mode == QR_MODE_AN) {
+ $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln
+ + QRinput::estimateBitsModeAn(1) // + 4 + la
+ - QRinput::estimateBitsModeAn($run + 1);// - 4 - la
+ if($dif > 0) {
+ return $this->eatAn();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_NUM, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatAn()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 0;
+
+ while(self::isalnumat($this->dataStr, $p)) {
+ if(self::isdigitat($this->dataStr, $p)) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+
+ $dif = QRinput::estimateBitsModeAn($p) // + 4 + la
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsModeAn($q); // - 4 - la
+
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+
+ if(!self::isalnumat($this->dataStr, $p)) {
+ $dif = QRinput::estimateBitsModeAn($run) + 4 + $la
+ + QRinput::estimateBitsMode8(1) // + 4 + l8
+ - QRinput::estimateBitsMode8($run + 1); // - 4 - l8
+ if($dif > 0) {
+ return $this->eat8();
+ }
+ }
+
+ $ret = $this->input->append(QR_MODE_AN, $run, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eatKanji()
+ {
+ $p = 0;
+
+ while($this->identifyMode($p) == QR_MODE_KANJI) {
+ $p += 2;
+ }
+
+ $ret = $this->input->append(QR_MODE_KANJI, $p, str_split($this->dataStr));
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function eat8()
+ {
+ $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion());
+ $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion());
+
+ $p = 1;
+ $dataStrLen = strlen($this->dataStr);
+
+ while($p < $dataStrLen) {
+
+ $mode = $this->identifyMode($p);
+ if($mode == QR_MODE_KANJI) {
+ break;
+ }
+ if($mode == QR_MODE_NUM) {
+ $q = $p;
+ while(self::isdigitat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else if($mode == QR_MODE_AN) {
+ $q = $p;
+ while(self::isalnumat($this->dataStr, $q)) {
+ $q++;
+ }
+ $dif = QRinput::estimateBitsMode8($p) // + 4 + l8
+ + QRinput::estimateBitsModeAn($q - $p) + 4 + $la
+ - QRinput::estimateBitsMode8($q); // - 4 - l8
+ if($dif < 0) {
+ break;
+ } else {
+ $p = $q;
+ }
+ } else {
+ $p++;
+ }
+ }
+
+ $run = $p;
+ $ret = $this->input->append(QR_MODE_8, $run, str_split($this->dataStr));
+
+ if($ret < 0)
+ return -1;
+
+ return $run;
+ }
+
+ //----------------------------------------------------------------------
+ public function splitString()
+ {
+ while (strlen($this->dataStr) > 0)
+ {
+ if($this->dataStr == '')
+ return 0;
+
+ $mode = $this->identifyMode(0);
+
+ switch ($mode) {
+ case QR_MODE_NUM: $length = $this->eatNum(); break;
+ case QR_MODE_AN: $length = $this->eatAn(); break;
+ case QR_MODE_KANJI:
+ if ($hint == QR_MODE_KANJI)
+ $length = $this->eatKanji();
+ else $length = $this->eat8();
+ break;
+ default: $length = $this->eat8(); break;
+
+ }
+
+ if($length == 0) return 0;
+ if($length < 0) return -1;
+
+ $this->dataStr = substr($this->dataStr, $length);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function toUpper()
+ {
+ $stringLen = strlen($this->dataStr);
+ $p = 0;
+
+ while ($p<$stringLen) {
+ $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint);
+ if($mode == QR_MODE_KANJI) {
+ $p += 2;
+ } else {
+ if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) {
+ $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32);
+ }
+ $p++;
+ }
+ }
+
+ return $this->dataStr;
+ }
+
+ //----------------------------------------------------------------------
+ public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true)
+ {
+ if(is_null($string) || $string == '\0' || $string == '') {
+ throw new Exception('empty string!!!');
+ }
+
+ $split = new QRsplit($string, $input, $modeHint);
+
+ if(!$casesensitive)
+ $split->toUpper();
+
+ return $split->splitString();
+ }
+ }
+
+
+
+//---- qrrscode.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Reed-Solomon error correction support
+ *
+ * Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q
+ * (libfec is released under the GNU Lesser General Public License.)
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsItem {
+
+ public $mm; // Bits per symbol
+ public $nn; // Symbols per block (= (1<= $this->nn) {
+ $x -= $this->nn;
+ $x = ($x >> $this->mm) + ($x & $this->nn);
+ }
+
+ return $x;
+ }
+
+ //----------------------------------------------------------------------
+ public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ // Common code for intializing a Reed-Solomon control block (char or int symbols)
+ // Copyright 2004 Phil Karn, KA9Q
+ // May be used under the terms of the GNU Lesser General Public License (LGPL)
+
+ $rs = null;
+
+ // Check parameter ranges
+ if($symsize < 0 || $symsize > 8) return $rs;
+ if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs;
+ if($prim <= 0 || $prim >= (1<<$symsize)) return $rs;
+ if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values!
+ if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding
+
+ $rs = new QRrsItem();
+ $rs->mm = $symsize;
+ $rs->nn = (1<<$symsize)-1;
+ $rs->pad = $pad;
+
+ $rs->alpha_to = array_fill(0, $rs->nn+1, 0);
+ $rs->index_of = array_fill(0, $rs->nn+1, 0);
+
+ // PHP style macro replacement ;)
+ $NN =& $rs->nn;
+ $A0 =& $NN;
+
+ // Generate Galois field lookup tables
+ $rs->index_of[0] = $A0; // log(zero) = -inf
+ $rs->alpha_to[$A0] = 0; // alpha**-inf = 0
+ $sr = 1;
+
+ for($i=0; $i<$rs->nn; $i++) {
+ $rs->index_of[$sr] = $i;
+ $rs->alpha_to[$i] = $sr;
+ $sr <<= 1;
+ if($sr & (1<<$symsize)) {
+ $sr ^= $gfpoly;
+ }
+ $sr &= $rs->nn;
+ }
+
+ if($sr != 1){
+ // field generator polynomial is not primitive!
+ $rs = NULL;
+ return $rs;
+ }
+
+ /* Form RS code generator polynomial from its roots */
+ $rs->genpoly = array_fill(0, $nroots+1, 0);
+
+ $rs->fcr = $fcr;
+ $rs->prim = $prim;
+ $rs->nroots = $nroots;
+ $rs->gfpoly = $gfpoly;
+
+ /* Find prim-th root of 1, used in decoding */
+ for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn)
+ ; // intentional empty-body loop!
+
+ $rs->iprim = (int)($iprim / $prim);
+ $rs->genpoly[0] = 1;
+
+ for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) {
+ $rs->genpoly[$i+1] = 1;
+
+ // Multiply rs->genpoly[] by @**(root + x)
+ for ($j = $i; $j > 0; $j--) {
+ if ($rs->genpoly[$j] != 0) {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)];
+ } else {
+ $rs->genpoly[$j] = $rs->genpoly[$j-1];
+ }
+ }
+ // rs->genpoly[0] can never be zero
+ $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)];
+ }
+
+ // convert rs->genpoly[] to index form for quicker encoding
+ for ($i = 0; $i <= $nroots; $i++)
+ $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]];
+
+ return $rs;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode_rs_char($data, &$parity)
+ {
+ $MM =& $this->mm;
+ $NN =& $this->nn;
+ $ALPHA_TO =& $this->alpha_to;
+ $INDEX_OF =& $this->index_of;
+ $GENPOLY =& $this->genpoly;
+ $NROOTS =& $this->nroots;
+ $FCR =& $this->fcr;
+ $PRIM =& $this->prim;
+ $IPRIM =& $this->iprim;
+ $PAD =& $this->pad;
+ $A0 =& $NN;
+
+ $parity = array_fill(0, $NROOTS, 0);
+
+ for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) {
+
+ $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
+ if($feedback != $A0) {
+ // feedback term is non-zero
+
+ // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
+ // always be for the polynomials constructed by init_rs()
+ $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback);
+
+ for($j=1;$j<$NROOTS;$j++) {
+ $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS-$j])];
+ }
+ }
+
+ // Shift
+ array_shift($parity);
+ if($feedback != $A0) {
+ array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]);
+ } else {
+ array_push($parity, 0);
+ }
+ }
+ }
+ }
+
+ //##########################################################################
+
+ class QRrs {
+
+ public static $items = array();
+
+ //----------------------------------------------------------------------
+ public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad)
+ {
+ foreach(self::$items as $rs) {
+ if($rs->pad != $pad) continue;
+ if($rs->nroots != $nroots) continue;
+ if($rs->mm != $symsize) continue;
+ if($rs->gfpoly != $gfpoly) continue;
+ if($rs->fcr != $fcr) continue;
+ if($rs->prim != $prim) continue;
+
+ return $rs;
+ }
+
+ $rs = QRrsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
+ array_unshift(self::$items, $rs);
+
+ return $rs;
+ }
+ }
+
+
+
+//---- qrmask.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Masking
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ define('N1', 3);
+ define('N2', 3);
+ define('N3', 40);
+ define('N4', 10);
+
+ class QRmask {
+
+ public $runLength = array();
+
+ //----------------------------------------------------------------------
+ public function __construct()
+ {
+ $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0);
+ }
+
+ //----------------------------------------------------------------------
+ public function writeFormatInformation($width, &$frame, $mask, $level)
+ {
+ $blacks = 0;
+ $format = QRspec::getFormatInfo($mask, $level);
+
+ for($i=0; $i<8; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[8][$width - 1 - $i] = chr($v);
+ if($i < 6) {
+ $frame[$i][8] = chr($v);
+ } else {
+ $frame[$i + 1][8] = chr($v);
+ }
+ $format = $format >> 1;
+ }
+
+ for($i=0; $i<7; $i++) {
+ if($format & 1) {
+ $blacks += 2;
+ $v = 0x85;
+ } else {
+ $v = 0x84;
+ }
+
+ $frame[$width - 7 + $i][8] = chr($v);
+ if($i == 0) {
+ $frame[8][7] = chr($v);
+ } else {
+ $frame[8][6 - $i] = chr($v);
+ }
+
+ $format = $format >> 1;
+ }
+
+ return $blacks;
+ }
+
+ //----------------------------------------------------------------------
+ public function mask0($x, $y) { return ($x+$y)&1; }
+ public function mask1($x, $y) { return ($y&1); }
+ public function mask2($x, $y) { return ($x%3); }
+ public function mask3($x, $y) { return ($x+$y)%3; }
+ public function mask4($x, $y) { return (((int)($y/2))+((int)($x/3)))&1; }
+ public function mask5($x, $y) { return (($x*$y)&1)+($x*$y)%3; }
+ public function mask6($x, $y) { return ((($x*$y)&1)+($x*$y)%3)&1; }
+ public function mask7($x, $y) { return ((($x*$y)%3)+(($x+$y)&1))&1; }
+
+ //----------------------------------------------------------------------
+ private function generateMaskNo($maskNo, $width, $frame)
+ {
+ $bitMask = array_fill(0, $width, array_fill(0, $width, 0));
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if(ord($frame[$y][$x]) & 0x80) {
+ $bitMask[$y][$x] = 0;
+ } else {
+ $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y);
+ $bitMask[$y][$x] = ($maskFunc == 0)?1:0;
+ }
+
+ }
+ }
+
+ return $bitMask;
+ }
+
+ //----------------------------------------------------------------------
+ public static function serial($bitFrame)
+ {
+ $codeArr = array();
+
+ foreach ($bitFrame as $line)
+ $codeArr[] = join('', $line);
+
+ return gzcompress(join("\n", $codeArr), 9);
+ }
+
+ //----------------------------------------------------------------------
+ public static function unserial($code)
+ {
+ $codeArr = array();
+
+ $codeLines = explode("\n", gzuncompress($code));
+ foreach ($codeLines as $line)
+ $codeArr[] = str_split($line);
+
+ return $codeArr;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false)
+ {
+ $b = 0;
+ $bitMask = array();
+
+ $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat';
+
+ if (QR_CACHEABLE) {
+ if (file_exists($fileName)) {
+ $bitMask = self::unserial(file_get_contents($fileName));
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo))
+ mkdir(QR_CACHE_DIR.'mask_'.$maskNo);
+ file_put_contents($fileName, self::serial($bitMask));
+ }
+ } else {
+ $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d);
+ }
+
+ if ($maskGenOnly)
+ return;
+
+ $d = $s;
+
+ for($y=0; $y<$width; $y++) {
+ for($x=0; $x<$width; $x++) {
+ if($bitMask[$y][$x] == 1) {
+ $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]);
+ }
+ $b += (int)(ord($d[$y][$x]) & 1);
+ }
+ }
+
+ return $b;
+ }
+
+ //----------------------------------------------------------------------
+ public function makeMask($width, $frame, $maskNo, $level)
+ {
+ $masked = array_fill(0, $width, str_repeat("\0", $width));
+ $this->makeMaskNo($maskNo, $width, $frame, $masked);
+ $this->writeFormatInformation($width, $masked, $maskNo, $level);
+
+ return $masked;
+ }
+
+ //----------------------------------------------------------------------
+ public function calcN1N3($length)
+ {
+ $demerit = 0;
+
+ for($i=0; $i<$length; $i++) {
+
+ if($this->runLength[$i] >= 5) {
+ $demerit += (N1 + ($this->runLength[$i] - 5));
+ }
+ if($i & 1) {
+ if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) {
+ $fact = (int)($this->runLength[$i] / 3);
+ if(($this->runLength[$i-2] == $fact) &&
+ ($this->runLength[$i-1] == $fact) &&
+ ($this->runLength[$i+1] == $fact) &&
+ ($this->runLength[$i+2] == $fact)) {
+ if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) {
+ $demerit += N3;
+ } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) {
+ $demerit += N3;
+ }
+ }
+ }
+ }
+ }
+ return $demerit;
+ }
+
+ //----------------------------------------------------------------------
+ public function evaluateSymbol($width, $frame)
+ {
+ $head = 0;
+ $demerit = 0;
+
+ for($y=0; $y<$width; $y++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ $frameY = $frame[$y];
+
+ if ($y>0)
+ $frameYM = $frame[$y-1];
+
+ for($x=0; $x<$width; $x++) {
+ if(($x > 0) && ($y > 0)) {
+ $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]);
+ $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]);
+
+ if(($b22 | ($w22 ^ 1))&1) {
+ $demerit += N2;
+ }
+ }
+ if(($x == 0) && (ord($frameY[$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($x > 0) {
+ if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ for($x=0; $x<$width; $x++) {
+ $head = 0;
+ $this->runLength[0] = 1;
+
+ for($y=0; $y<$width; $y++) {
+ if($y == 0 && (ord($frame[$y][$x]) & 1)) {
+ $this->runLength[0] = -1;
+ $head = 1;
+ $this->runLength[$head] = 1;
+ } else if($y > 0) {
+ if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) {
+ $head++;
+ $this->runLength[$head] = 1;
+ } else {
+ $this->runLength[$head]++;
+ }
+ }
+ }
+
+ $demerit += $this->calcN1N3($head+1);
+ }
+
+ return $demerit;
+ }
+
+
+ //----------------------------------------------------------------------
+ public function mask($width, $frame, $level)
+ {
+ $minDemerit = PHP_INT_MAX;
+ $bestMaskNum = 0;
+ $bestMask = array();
+
+ $checked_masks = array(0,1,2,3,4,5,6,7);
+
+ if (QR_FIND_FROM_RANDOM !== false) {
+
+ $howManuOut = 8-(QR_FIND_FROM_RANDOM % 9);
+ for ($i = 0; $i < $howManuOut; $i++) {
+ $remPos = rand (0, count($checked_masks)-1);
+ unset($checked_masks[$remPos]);
+ $checked_masks = array_values($checked_masks);
+ }
+
+ }
+
+ $bestMask = $frame;
+
+ foreach($checked_masks as $i) {
+ $mask = array_fill(0, $width, str_repeat("\0", $width));
+
+ $demerit = 0;
+ $blacks = 0;
+ $blacks = $this->makeMaskNo($i, $width, $frame, $mask);
+ $blacks += $this->writeFormatInformation($width, $mask, $i, $level);
+ $blacks = (int)(100 * $blacks / ($width * $width));
+ $demerit = (int)((int)(abs($blacks - 50) / 5) * N4);
+ $demerit += $this->evaluateSymbol($width, $mask);
+
+ if($demerit < $minDemerit) {
+ $minDemerit = $demerit;
+ $bestMask = $mask;
+ $bestMaskNum = $i;
+ }
+ }
+
+ return $bestMask;
+ }
+
+ //----------------------------------------------------------------------
+ }
+
+
+
+
+//---- qrencode.php -----------------------------
+
+
+
+
+/*
+ * PHP QR Code encoder
+ *
+ * Main encoder classes.
+ *
+ * Based on libqrencode C library distributed under LGPL 2.1
+ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi
+ *
+ * PHP QR Code is distributed under LGPL 3
+ * Copyright (C) 2010 Dominik Dzienia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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
+ * Lesser 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+ class QRrsblock {
+ public $dataLength;
+ public $data = array();
+ public $eccLength;
+ public $ecc = array();
+
+ public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs)
+ {
+ $rs->encode_rs_char($data, $ecc);
+
+ $this->dataLength = $dl;
+ $this->data = $data;
+ $this->eccLength = $el;
+ $this->ecc = $ecc;
+ }
+ };
+
+ //##########################################################################
+
+ class QRrawcode {
+ public $version;
+ public $datacode = array();
+ public $ecccode = array();
+ public $blocks;
+ public $rsblocks = array(); //of RSblock
+ public $count;
+ public $dataLength;
+ public $eccLength;
+ public $b1;
+
+ //----------------------------------------------------------------------
+ public function __construct(QRinput $input)
+ {
+ $spec = array(0,0,0,0,0);
+
+ $this->datacode = $input->getByteStream();
+ if(is_null($this->datacode)) {
+ throw new Exception('null imput string');
+ }
+
+ QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec);
+
+ $this->version = $input->getVersion();
+ $this->b1 = QRspec::rsBlockNum1($spec);
+ $this->dataLength = QRspec::rsDataLength($spec);
+ $this->eccLength = QRspec::rsEccLength($spec);
+ $this->ecccode = array_fill(0, $this->eccLength, 0);
+ $this->blocks = QRspec::rsBlockNum($spec);
+
+ $ret = $this->init($spec);
+ if($ret < 0) {
+ throw new Exception('block alloc error');
+ return null;
+ }
+
+ $this->count = 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function init(array $spec)
+ {
+ $dl = QRspec::rsDataCodes1($spec);
+ $el = QRspec::rsEccCodes1($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+
+ $blockNo = 0;
+ $dataPos = 0;
+ $eccPos = 0;
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ if(QRspec::rsBlockNum2($spec) == 0)
+ return 0;
+
+ $dl = QRspec::rsDataCodes2($spec);
+ $el = QRspec::rsEccCodes2($spec);
+ $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
+
+ if($rs == NULL) return -1;
+
+ for($i=0; $iecccode,$eccPos);
+ $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs);
+ $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc);
+
+ $dataPos += $dl;
+ $eccPos += $el;
+ $blockNo++;
+ }
+
+ return 0;
+ }
+
+ //----------------------------------------------------------------------
+ public function getCode()
+ {
+ $ret;
+
+ if($this->count < $this->dataLength) {
+ $row = $this->count % $this->blocks;
+ $col = $this->count / $this->blocks;
+ if($col >= $this->rsblocks[0]->dataLength) {
+ $row += $this->b1;
+ }
+ $ret = $this->rsblocks[$row]->data[$col];
+ } else if($this->count < $this->dataLength + $this->eccLength) {
+ $row = ($this->count - $this->dataLength) % $this->blocks;
+ $col = ($this->count - $this->dataLength) / $this->blocks;
+ $ret = $this->rsblocks[$row]->ecc[$col];
+ } else {
+ return 0;
+ }
+ $this->count++;
+
+ return $ret;
+ }
+ }
+
+ //##########################################################################
+
+ class QRcode {
+
+ public $version;
+ public $width;
+ public $data;
+
+ //----------------------------------------------------------------------
+ public function encodeMask(QRinput $input, $mask)
+ {
+ if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) {
+ throw new Exception('wrong version');
+ }
+ if($input->getErrorCorrectionLevel() > QR_ECLEVEL_H) {
+ throw new Exception('wrong level');
+ }
+
+ $raw = new QRrawcode($input);
+
+ QRtools::markTime('after_raw');
+
+ $version = $raw->version;
+ $width = QRspec::getWidth($version);
+ $frame = QRspec::newFrame($version);
+
+ $filler = new FrameFiller($width, $frame);
+ if(is_null($filler)) {
+ return NULL;
+ }
+
+ // inteleaved data and ecc codes
+ for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) {
+ $code = $raw->getCode();
+ $bit = 0x80;
+ for($j=0; $j<8; $j++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0));
+ $bit = $bit >> 1;
+ }
+ }
+
+ QRtools::markTime('after_filler');
+
+ unset($raw);
+
+ // remainder bits
+ $j = QRspec::getRemainder($version);
+ for($i=0; $i<$j; $i++) {
+ $addr = $filler->next();
+ $filler->setFrameAt($addr, 0x02);
+ }
+
+ $frame = $filler->frame;
+ unset($filler);
+
+
+ // masking
+ $maskObj = new QRmask();
+ if($mask < 0) {
+
+ if (QR_FIND_BEST_MASK) {
+ $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel());
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, (intval(QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel());
+ }
+ } else {
+ $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel());
+ }
+
+ if($masked == NULL) {
+ return NULL;
+ }
+
+ QRtools::markTime('after_mask');
+
+ $this->version = $version;
+ $this->width = $width;
+ $this->data = $masked;
+
+ return $this;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeInput(QRinput $input)
+ {
+ return $this->encodeMask($input, -1);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString8bit($string, $version, $level)
+ {
+ if(string == NULL) {
+ throw new Exception('empty string!');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = $input->append($input, QR_MODE_8, strlen($string), str_split($string));
+ if($ret < 0) {
+ unset($input);
+ return NULL;
+ }
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeString($string, $version, $level, $hint, $casesensitive)
+ {
+
+ if($hint != QR_MODE_8 && $hint != QR_MODE_KANJI) {
+ throw new Exception('bad hint');
+ return NULL;
+ }
+
+ $input = new QRinput($version, $level);
+ if($input == NULL) return NULL;
+
+ $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive);
+ if($ret < 0) {
+ return NULL;
+ }
+
+ return $this->encodeInput($input);
+ }
+
+ //----------------------------------------------------------------------
+ public static function png($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodePNG($text, $outfile, $saveandprint=false);
+ }
+
+ //----------------------------------------------------------------------
+ public static function text($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encode($text, $outfile);
+ }
+
+ //----------------------------------------------------------------------
+ public static function raw($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = QRencode::factory($level, $size, $margin);
+ return $enc->encodeRAW($text, $outfile);
+ }
+ }
+
+ //##########################################################################
+
+ class FrameFiller {
+
+ public $width;
+ public $frame;
+ public $x;
+ public $y;
+ public $dir;
+ public $bit;
+
+ //----------------------------------------------------------------------
+ public function __construct($width, &$frame)
+ {
+ $this->width = $width;
+ $this->frame = $frame;
+ $this->x = $width - 1;
+ $this->y = $width - 1;
+ $this->dir = -1;
+ $this->bit = -1;
+ }
+
+ //----------------------------------------------------------------------
+ public function setFrameAt($at, $val)
+ {
+ $this->frame[$at['y']][$at['x']] = chr($val);
+ }
+
+ //----------------------------------------------------------------------
+ public function getFrameAt($at)
+ {
+ return ord($this->frame[$at['y']][$at['x']]);
+ }
+
+ //----------------------------------------------------------------------
+ public function next()
+ {
+ do {
+
+ if($this->bit == -1) {
+ $this->bit = 0;
+ return array('x'=>$this->x, 'y'=>$this->y);
+ }
+
+ $x = $this->x;
+ $y = $this->y;
+ $w = $this->width;
+
+ if($this->bit == 0) {
+ $x--;
+ $this->bit++;
+ } else {
+ $x++;
+ $y += $this->dir;
+ $this->bit--;
+ }
+
+ if($this->dir < 0) {
+ if($y < 0) {
+ $y = 0;
+ $x -= 2;
+ $this->dir = 1;
+ if($x == 6) {
+ $x--;
+ $y = 9;
+ }
+ }
+ } else {
+ if($y == $w) {
+ $y = $w - 1;
+ $x -= 2;
+ $this->dir = -1;
+ if($x == 6) {
+ $x--;
+ $y -= 8;
+ }
+ }
+ }
+ if($x < 0 || $y < 0) return null;
+
+ $this->x = $x;
+ $this->y = $y;
+
+ } while(ord($this->frame[$y][$x]) & 0x80);
+
+ return array('x'=>$x, 'y'=>$y);
+ }
+
+ } ;
+
+ //##########################################################################
+
+ class QRencode {
+
+ public $casesensitive = true;
+ public $eightbit = false;
+
+ public $version = 0;
+ public $size = 3;
+ public $margin = 4;
+
+ public $structured = 0; // not supported yet
+
+ public $level = QR_ECLEVEL_L;
+ public $hint = QR_MODE_8;
+
+ //----------------------------------------------------------------------
+ public static function factory($level = QR_ECLEVEL_L, $size = 3, $margin = 4)
+ {
+ $enc = new QRencode();
+ $enc->size = $size;
+ $enc->margin = $margin;
+
+ switch ($level.'') {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ $enc->level = $level;
+ break;
+ case 'l':
+ case 'L':
+ $enc->level = QR_ECLEVEL_L;
+ break;
+ case 'm':
+ case 'M':
+ $enc->level = QR_ECLEVEL_M;
+ break;
+ case 'q':
+ case 'Q':
+ $enc->level = QR_ECLEVEL_Q;
+ break;
+ case 'h':
+ case 'H':
+ $enc->level = QR_ECLEVEL_H;
+ break;
+ }
+
+ return $enc;
+ }
+
+ //----------------------------------------------------------------------
+ public function encodeRAW($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ return $code->data;
+ }
+
+ //----------------------------------------------------------------------
+ public function encode($intext, $outfile = false)
+ {
+ $code = new QRcode();
+
+ if($this->eightbit) {
+ $code->encodeString8bit($intext, $this->version, $this->level);
+ } else {
+ $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive);
+ }
+
+ QRtools::markTime('after_encode');
+
+ if ($outfile!== false) {
+ file_put_contents($outfile, join("\n", QRtools::binarize($code->data)));
+ } else {
+ return QRtools::binarize($code->data);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ public function encodePNG($intext, $outfile = false,$saveandprint=false)
+ {
+ try {
+
+ ob_start();
+ $tab = $this->encode($intext);
+ $err = ob_get_contents();
+ ob_end_clean();
+
+ if ($err != '')
+ QRtools::log($outfile, $err);
+
+ $maxSize = (int)(QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin));
+
+ QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint);
+
+ } catch (Exception $e) {
+
+ QRtools::log($outfile, $e->getMessage());
+
+ }
+ }
+ }
+
+
diff --git a/apps/shorty/ajax/add.php b/apps/shorty/ajax/add.php
new file mode 100644
index 00000000000..b1130e47db6
--- /dev/null
+++ b/apps/shorty/ajax/add.php
@@ -0,0 +1,101 @@
+
+* @license GNU AFFERO GENERAL PUBLIC LICENSE (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/add.php
+ * @brief Ajax method to add a new shorty defined by request arguments
+ * @param title (string) Human readable title of the shorty
+ * @param target (url) Remote target url meant to be shortened
+ * @param until (date) Date until when the created shorty is valid and usable
+ * @param notes (string) Any additional information in text form
+ * @param favicon (url) Reference to the shortcut icon used in target url
+ * @returns (json) success/error state indicator
+ * @returns (json) Associative array of attributes of the generated shorty
+ * @returns (json) Human readable message
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+ //no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $p_id = OC_Shorty_Tools::shorty_id ( );
+ $p_status = OC_Shorty_Type::req_argument ( 'status', OC_Shorty_Type::STATUS, FALSE );
+ $p_title = OC_Shorty_Type::req_argument ( 'title', OC_Shorty_Type::STRING, FALSE );
+ $p_target = OC_Shorty_Type::req_argument ( 'target', OC_Shorty_Type::URL, TRUE );
+ $p_until = OC_Shorty_Type::req_argument ( 'until', OC_Shorty_Type::DATE, FALSE );
+ $p_notes = OC_Shorty_Type::req_argument ( 'notes', OC_Shorty_Type::STRING, FALSE );
+ $p_favicon = OC_Shorty_Type::req_argument ( 'favicon', OC_Shorty_Type::URL, FALSE );
+ // register shorty at backend
+ $p_source = OC_Shorty_Backend::registerUrl ( $p_id );
+ // fallback title: choose hostname if no title is specified
+ $p_title = $p_title ? trim($p_title) : parse_url($p_target,PHP_URL_HOST);
+ // insert new shorty into our database
+ $param = array
+ (
+ ':user' => OCP\User::getUser(),
+ ':id' => $p_id,
+ ':status' => $p_status ? $p_status : '',
+ ':title' => $p_title ? $p_title : '',
+ ':favicon' => $p_favicon ? $p_favicon : '',
+ ':source' => $p_source ? $p_source : '',
+ ':target' => $p_target ? $p_target : '',
+ ':notes' => $p_notes ? $p_notes : '',
+ ':until' => $p_until,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_INSERT );
+ $query->execute ( $param );
+
+ // read new entry for feedback
+ $param = array
+ (
+ ':user' => OCP\User::getUser(),
+ ':id' => $p_id,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_VERIFY );
+ $entries = $query->execute($param)->FetchAll();
+ if ( (1==count($entries))
+ &&(isset($entries[0]['id']))
+ &&($p_id==$entries[0]['id']) )
+ $entries[0]['relay']=OC_Shorty_Tools::relayUrl ( $entries[0]['id'] );
+ else
+ throw new OC_Shorty_Exception ( "failed to verify stored shorty with id '%1s'", array($p_id) );
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\Util::writeLog( 'shorty', sprintf("Url shortened to: %s",$p_source), OC_Log::INFO );
+ OCP\JSON::success ( array ( 'data' => $entries[0],
+ 'message' => OC_Shorty_L10n::t("Url shortened to: %s",$p_source) ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/click.php b/apps/shorty/ajax/click.php
new file mode 100644
index 00000000000..c88f6eec4f5
--- /dev/null
+++ b/apps/shorty/ajax/click.php
@@ -0,0 +1,67 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/click.php
+ * @brief Ajax method to register a 'click', a single hit on an existing and valid shorty
+ * @param id (string) Internal id of a referenced shorty
+ * @returns (json) success/error state indicator
+ * @returns (json) Associative array holding the id of the shorty whose click was registered
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ if (isset($_GET['id']))
+ {
+ $p_id = OC_Shorty_Type::req_argument ( $_GET['id'], OC_Shorty_Type::ID, TRUE );
+ $param = array
+ (
+ 'user' => OCP\User::getUser(),
+ 'id' => $p_id,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_CLICK );
+ $query->execute ( $param );
+ OCP\JSON::success ( array ( 'data' => array('id'=>$p_id),
+ 'message' => OC_Shorty_L10n::t('Click registered') ) );
+ }
+ else
+ throw new OC_Shorty_Exception ( "request failed: missing mandatory argument 'id'" );
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/count.php b/apps/shorty/ajax/count.php
new file mode 100644
index 00000000000..9a00083bb9f
--- /dev/null
+++ b/apps/shorty/ajax/count.php
@@ -0,0 +1,54 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/count.php
+ * @brief Ajax method to retrieve a list of important sums, counts of the existing set of shortys
+ * @returns (json) success/error state indicator
+ * @returns (json) Associative array of counts
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = TRUE;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $countResult = OC_Shorty_Tools::countShorties ( );
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\JSON::success ( array ( 'data' => $countResult,
+ 'message' => OC_Shorty_L10n::t('Counted entries and clicks') ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/del.php b/apps/shorty/ajax/del.php
new file mode 100644
index 00000000000..b24f83915ed
--- /dev/null
+++ b/apps/shorty/ajax/del.php
@@ -0,0 +1,63 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/del.php
+ * @brief Ajax method to delete an existing shorty
+ * @param id (string) Internal id of a referenced shorty
+ * @returns (json) success/error state indicator
+ * @returns (json) Key of shorty that was deleted
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $p_id = OC_Shorty_Type::req_argument ( 'id', OC_Shorty_Type::ID, TRUE );
+ $param = array
+ (
+ 'user' => OCP\User::getUser(),
+ 'id' => $p_id,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_DELETE );
+ $query->execute($param);
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\Util::writeLog( 'shorty', sprintf("Shorty with id '%s' deleted",$p_id), OC_Log::INFO );
+ OCP\JSON::success ( array ( 'data' => array('id'=>$p_id),
+ 'message' => OC_Shorty_L10n::t("Shorty with id '%s' deleted",$p_id) ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/edit.php b/apps/shorty/ajax/edit.php
new file mode 100644
index 00000000000..252e4fe5c29
--- /dev/null
+++ b/apps/shorty/ajax/edit.php
@@ -0,0 +1,88 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/edit.php
+ * @brief Ajax method to modify aspects of an existing shorty
+ * @param id (string) Internal id of the referenced shorty
+ * @param title (string) Human readable title
+ * @param notes (string) Any additional information in free text form
+ * @returns (json) success/error state indicator
+ * @returns (json) Associative array holding the id of the shorty whose click was registered
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $p_id = OC_Shorty_Type::req_argument ( 'id', OC_Shorty_Type::ID, TRUE );
+ $p_status = OC_Shorty_Type::req_argument ( 'status', OC_Shorty_Type::STATUS, FALSE );
+ $p_title = OC_Shorty_Type::req_argument ( 'title', OC_Shorty_Type::STRING, FALSE );
+ $p_until = OC_Shorty_Type::req_argument ( 'until', OC_Shorty_Type::DATE, FALSE );
+ $p_notes = OC_Shorty_Type::req_argument ( 'notes', OC_Shorty_Type::STRING, FALSE );
+ $param = array
+ (
+ ':user' => OCP\User::getUser ( ),
+ ':id' => $p_id,
+ ':status'=> $p_status ? $p_status : '',
+ ':title' => $p_title ? $p_title : '',
+ ':notes' => $p_notes ? $p_notes : '',
+ ':until' => $p_until,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_UPDATE );
+ $query->execute ( $param );
+
+ // read new entry for feedback
+ $param = array
+ (
+ 'user' => OCP\User::getUser(),
+ 'id' => $p_id,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_VERIFY );
+ $entries = $query->execute($param)->FetchAll();
+ if ( (1==count($entries))
+ &&(isset($entries[0]['id']))
+ &&($p_id==$entries[0]['id']) )
+ $entries[0]['relay']=OC_Shorty_Tools::relayUrl ( $entries[0]['id'] );
+ else
+ throw new OC_Shorty_Exception ( "failed to verify stored shorty with id '%1s'", array($p_id) );
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\Util::writeLog( 'shorty', sprintf("Modifications for shorty with id '%s' saved",$p_id), OC_Log::INFO );
+ OCP\JSON::success ( array ( 'data' => $entries[0],
+ 'message' => OC_Shorty_L10n::t("Modifications for shorty with id '%s' saved",$p_id) ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/list.php b/apps/shorty/ajax/list.php
new file mode 100644
index 00000000000..8b4e9b3e81e
--- /dev/null
+++ b/apps/shorty/ajax/list.php
@@ -0,0 +1,77 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/list.php
+ * @brief Ajax method to retrieve a list of existing shortys
+ * @returns (json) success/error state indicator
+ * @returns (number) Total number of shortys in the list
+ * @returns (json) Numeric array of all shortys, associative array of attributes as values for every single shorty contained
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = TRUE;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ // first remove any entries already marked as 'deleted'
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_REMOVE );
+ $result = $query->execute(array(':user'=>OCP\User::getUser()));
+ // now comes the real list selection
+// define ('PAGE_SIZE', 100);
+// $p_offset = OC_Shorty_Type::req_argument ( 'page', OC_Shorty_Type::INTEGER, FALSE) * PAGE_SIZE;
+ // pre-sort list according to user preferences
+ $p_sort = OC_Shorty_Type::$SORTING[OCP\Config::getUserValue(OCP\User::getUser(),'shorty','list-sort-code','cd')];
+ $param = array
+ (
+ ':user' => OCP\User::getUser ( ),
+ ':sort' => $p_sort,
+// ':offset' => $p_offset,
+// ':limit' => PAGE_SIZE,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_LIST );
+ $result = $query->execute($param);
+ $reply = $result->fetchAll();
+ // enhance all entries with the relay url
+ foreach (array_keys($reply) as $key)
+ if (isset($reply[$key]['id']))
+ $reply[$key]['relay']=OC_Shorty_Tools::relayUrl ( $reply[$key]['id'] );
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\JSON::success ( array ( 'data' => $reply,
+ 'count' => sizeof($reply),
+ 'message' => OC_Shorty_L10n::t('Number of entries: %s', count($reply)) ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/meta.php b/apps/shorty/ajax/meta.php
new file mode 100644
index 00000000000..5f9d5c3c315
--- /dev/null
+++ b/apps/shorty/ajax/meta.php
@@ -0,0 +1,56 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/meta.php
+ * @brief Ajax method to query meta information about a given remote url
+ * @param target (string) Url of a remote web resource
+ * @returns (json) success/error state indicator
+ * @returns (array) Associative array of meta data aspects
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $target = OC_Shorty_Type::req_argument ( 'target', OC_Shorty_Type::URL, TRUE );
+ $meta = OC_Shorty_Meta::fetchMetaData(htmlspecialchars_decode($target));
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\JSON::success ( array ( 'data' => $meta,
+ 'message' => OC_Shorty_L10n::t("Target url '%s' is valid", $meta['target']) ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/preferences.php b/apps/shorty/ajax/preferences.php
new file mode 100644
index 00000000000..e35a2f7aff3
--- /dev/null
+++ b/apps/shorty/ajax/preferences.php
@@ -0,0 +1,119 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/preferences.php
+ * @brief Ajax method to store one or more personal preferences
+ * @param backend-type (string) Identifier of chosen backend type
+ * @param backend-static-base (string) Url to use as a base when the static backend is active
+ * @param backend-google-key (string) Personal authentication key to use when the google backend is active
+ * @param backend-bitly-key (string) Personal authentication key to use when the bit.li backend is active
+ * @param backend-bitly-user (string) Personal authentication user to use when the bit.li backend is active
+ * @param sms-control (string) Controls wether a 'send as sms' action should be offered is the sharing dialog
+ * @param list-sort-code (string) Two character sorting key controlling the active sorting of shorty lists
+ * @returns (json) success/error state indicator
+ * @returns (json) Associative array holding the stored values by their key
+ * @returns (json) Human readable message describing the result
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $data = array();
+ switch ( $_SERVER['REQUEST_METHOD'] )
+ {
+ case 'POST':
+ // detect provided preferences
+ $data = array();
+ foreach (array_keys($_POST) as $key)
+ if ( isset(OC_Shorty_Type::$PREFERENCE[$key]) ) // ignore unknown preference keys
+ {
+ $type = OC_Shorty_Type::$PREFERENCE[$key];
+ $data[$key] = OC_Shorty_Type::req_argument ( $key, $type, FALSE );
+ }
+ // eliminate settings not explicitly set
+ $data = array_diff ( $data, array(FALSE) );
+ // store settings
+ foreach ( $data as $key=>$val )
+ OCP\Config::setUserValue( OCP\User::getUser(), 'shorty', $key, $val );
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\Util::writeLog( 'shorty', sprintf("Preference(s) '%s' saved.",implode(',',array_keys($data))), OC_Log::INFO );
+ OCP\JSON::success ( array ( 'data' => $data,
+ 'message' => OC_Shorty_L10n::t("Preference(s) '%s' saved.",implode(',',array_keys($data))) ) );
+ break;
+ case 'GET':
+ // detect requested preferences
+ foreach (array_keys($_GET) as $key)
+ {
+ if ( isset(OC_Shorty_Type::$PREFERENCE[$key]) ) // ignore unknown preference keys
+ {
+ $type = OC_Shorty_Type::$PREFERENCE[$key];
+ $data[$key] = OCP\Config::getUserValue( OCP\User::getUser(), 'shorty', $key);
+ // morph value into an explicit type
+ switch ($type)
+ {
+ case OC_Shorty_Type::ID:
+ case OC_Shorty_Type::STATUS:
+ case OC_Shorty_Type::SORTKEY:
+ case OC_Shorty_Type::SORTVAL:
+ case OC_Shorty_Type::STRING:
+ case OC_Shorty_Type::URL:
+ case OC_Shorty_Type::DATE:
+ settype ( $data[$key], 'string' );
+ break;
+ case OC_Shorty_Type::INTEGER:
+ case OC_Shorty_Type::TIMESTAMP:
+ settype ( $data[$key], 'integer' );
+ break;
+ case OC_Shorty_Type::FLOAT:
+ settype ( $data[$key], 'float' );
+ break;
+ default:
+ } // switch
+ }
+ } // foreach
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\Util::writeLog( 'shorty', sprintf("Preference(s) '%s' saved.",implode(',',array_keys($data))), OC_Log::INFO );
+ OCP\JSON::success ( array ( 'data' => $data,
+ 'message' => OC_Shorty_L10n::t('Preference(s) retrieved.') ) );
+ break;
+ default:
+ throw new OC_Shorty_Exception ( "unexpected request method '%s'", $_SERVER['REQUEST_METHOD'] );
+ } // switch
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/settings.php b/apps/shorty/ajax/settings.php
new file mode 100644
index 00000000000..af76b69e076
--- /dev/null
+++ b/apps/shorty/ajax/settings.php
@@ -0,0 +1,109 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/settings.php
+ * @brief Ajax method to store one or more system settings (plugin settings)
+ * @param backend-static-base (string) Url to use as a base when the static backend is active (plugins default, may be overridden by user preference)
+ * @returns (json) success/error state indicator
+ * @returns (json) Associative array holding the stored values by their key
+ * @returns (json) Human readable message describing the result
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkAdminUser ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $data = array();
+ switch ( $_SERVER['REQUEST_METHOD'] )
+ {
+ case 'POST':
+ // detect provided settings
+ $data = array();
+ foreach (array_keys($_POST) as $key)
+ if ( isset(OC_Shorty_Type::$SETTING[$key]) ) // ignore unknown preference keys
+ {
+ $type = OC_Shorty_Type::$SETTING[$key];
+ $data[$key] = OC_Shorty_Type::req_argument ( $key, $type, FALSE );
+ }
+ // eliminate settings not explicitly set
+ $data = array_diff ( $data, array(FALSE) );
+ // store settings one by one
+ foreach ( $data as $key=>$val )
+ OCP\Config::setAppValue( 'shorty', $key, $val );
+ break;
+ case 'GET':
+ // detect requested settings
+ foreach (array_keys($_GET) as $key)
+ {
+ if ( isset(OC_Shorty_Type::$SETTING[$key]) ) // ignore unknown preference keys
+ {
+ $type = OC_Shorty_Type::$SETTING[$key];
+ $data[$key] = OCP\Config::getUserValue( OCP\User::getUser(), 'shorty', $key);
+ // morph value into an explicit type
+ switch ($type)
+ {
+ case OC_Shorty_Type::ID:
+ case OC_Shorty_Type::STATUS:
+ case OC_Shorty_Type::SORTKEY:
+ case OC_Shorty_Type::SORTVAL:
+ case OC_Shorty_Type::STRING:
+ case OC_Shorty_Type::URL:
+ case OC_Shorty_Type::DATE:
+ settype ( $data[$key], 'string' );
+ break;
+ case OC_Shorty_Type::INTEGER:
+ case OC_Shorty_Type::TIMESTAMP:
+ settype ( $data[$key], 'integer' );
+ break;
+ case OC_Shorty_Type::FLOAT:
+ settype ( $data[$key], 'float' );
+ break;
+ default:
+ } // switch
+ }
+ } // foreach
+ break;
+ default:
+ throw new OC_Shorty_Exception ( "unexpected request method '%s'", $_SERVER['REQUEST_METHOD'] );
+ } // switch
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\Util::writeLog( 'shorty', sprintf("Setting(s) '%s' saved.",implode(',',array_keys($data))), OC_Log::INFO );
+ OCP\JSON::success ( array ( 'data' => $data,
+ 'message' => OC_Shorty_L10n::t('Setting saved.') ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/ajax/status.php b/apps/shorty/ajax/status.php
new file mode 100644
index 00000000000..6d00bf58854
--- /dev/null
+++ b/apps/shorty/ajax/status.php
@@ -0,0 +1,67 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file ajax/status.php
+ * @brief Ajax method to modify the status of an existing shorty
+ * @param id (string) Internal id of the referenced shorty
+ * @param title (string) Human readable title
+ * @param notes (string) Any additional information in free text form
+ * @returns (json) success/error state indicator
+ * @returns (json) Associative array holding the id of the shorty whose click was registered
+ * @author Christian Reiner
+ */
+
+// swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+OC_Shorty_Tools::ob_control ( TRUE );
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS = true;
+
+// Check if we are a user
+OCP\JSON::checkLoggedIn ( );
+OCP\JSON::checkAppEnabled ( 'shorty' );
+
+try
+{
+ $p_id = OC_Shorty_Type::req_argument ( 'id', OC_Shorty_Type::ID, TRUE );
+ $p_status = OC_Shorty_Type::req_argument ( 'status', OC_Shorty_Type::STATUS, FALSE );
+ $param = array
+ (
+ 'user' => OCP\User::getUser ( ),
+ 'id' => $p_id,
+ 'status' => $p_status,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_STATUS );
+ $query->execute ( $param );
+
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ OC_Shorty_Tools::ob_control ( FALSE );
+ OCP\Util::writeLog( 'shorty', sprintf("Status change for shorty with id '%s' saved",$p_id), OC_Log::INFO );
+ OCP\JSON::success ( array ( 'data' => array('id'=>$p_id),
+ 'message' => sprintf(OC_Shorty_L10n::t("Status change for shorty with id '%s' saved"),$p_id) ) );
+} catch ( Exception $e ) { OC_Shorty_Exception::JSONerror($e); }
+?>
diff --git a/apps/shorty/appinfo/app.php b/apps/shorty/appinfo/app.php
new file mode 100644
index 00000000000..635fda4ee27
--- /dev/null
+++ b/apps/shorty/appinfo/app.php
@@ -0,0 +1,53 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file appinfo/app.php
+ * @brief Basic registration of plugin at ownCloud
+ * @author Christian Reiner
+ */
+
+OC::$CLASSPATH['OC_Shorty_Backend'] = 'apps/shorty/lib/backend.php';
+OC::$CLASSPATH['OC_Shorty_Exception'] = 'apps/shorty/lib/exception.php';
+OC::$CLASSPATH['OC_Shorty_HttpException'] = 'apps/shorty/lib/exception.php';
+OC::$CLASSPATH['OC_Shorty_L10n'] = 'apps/shorty/lib/l10n.php';
+OC::$CLASSPATH['OC_Shorty_Meta'] = 'apps/shorty/lib/meta.php';
+OC::$CLASSPATH['OC_Shorty_Query'] = 'apps/shorty/lib/query.php';
+OC::$CLASSPATH['OC_Shorty_Tools'] = 'apps/shorty/lib/tools.php';
+OC::$CLASSPATH['OC_Shorty_Type'] = 'apps/shorty/lib/type.php';
+
+OCP\App::addNavigationEntry ( array ( 'id' => 'shorty_index',
+ 'order' => 71,
+ 'href' => OCP\Util::linkTo ( 'shorty', 'index.php' ),
+ 'icon' => OCP\Util::imagePath( 'shorty', 'shorty.svg' ),
+ 'name' => 'Shorty' ) );
+
+OCP\App::register ( array ( 'order' => 71, 'id' => 'shorty', 'name' => 'Shorty' ) );
+OCP\App::registerAdmin ( 'shorty', 'settings' );
+OCP\App::registerPersonal ( 'shorty', 'preferences' );
+OCP\Util::connectHook ( 'OC_User', 'post_deleteUser', 'OC_Shorty_Hooks', 'deleteUser');
+
+?>
diff --git a/apps/shorty/appinfo/database.xml b/apps/shorty/appinfo/database.xml
new file mode 100644
index 00000000000..0cac24dbf25
--- /dev/null
+++ b/apps/shorty/appinfo/database.xml
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+ *dbname*
+ true
+ false
+ latin1
+
+ *dbprefix*shorty
+
+
+ id
+ text
+ true
+ 12
+
+
+ status
+ text
+ 'shared'
+ true
+ 10
+
+
+ title
+ text
+ ''
+ true
+ 80
+
+
+ favicon
+ text
+ ''
+ false
+ 1024
+
+
+ source
+ text
+ true
+ 4096
+
+
+ target
+ text
+ true
+ 4096
+
+
+ user
+ text
+ true
+ 64
+
+
+ until
+ date
+ false
+
+
+
+ created
+ date
+ false
+
+
+
+ accessed
+ timestamp
+ false
+
+
+
+ clicks
+ integer
+ 0
+ true
+ true
+ 10
+
+
+ notes
+ text
+ ''
+ true
+ 4096
+
+
+
+ shorty_id
+ true
+
+ id
+ descending
+
+
+
+ shorty_user
+ false
+
+ user
+ ascending
+
+
+
+ shorty_source
+ false
+
+ source
+ ascending
+
+
+
+
+
diff --git a/apps/shorty/appinfo/info.xml b/apps/shorty/appinfo/info.xml
new file mode 100644
index 00000000000..bbee9dd673b
--- /dev/null
+++ b/apps/shorty/appinfo/info.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+ shorty
+ Shorty
+ Shorty offers a service to store, manage and use a collection of short links
+pointing to ressources in the web. The features are a combination of a
+centralized bookmarks collection, an url shortener and an access control.
+The collection is presented as a link of "shorties", a shorty can be created
+either manually in the list ("New") or by using the "Shortlet". The Shortlet
+is something like a Booklet, a JS based bookmark meant to be stored inside the
+bookmark toolbar or area of a web browser. When clicked, the page currently
+open in the browser will be offered to be added to the list of existing shorties.
+For more information see: http://apps.owncloud.com/content/show.php/Shorty?content=150401
+Current version: 0.2.2 (06.06.2012)
+ 0.2.2
+ AGPL
+ Christian Reiner
+ 4
+
+ relay.php
+ qrcode.php
+
+
diff --git a/apps/shorty/appinfo/migrate.php b/apps/shorty/appinfo/migrate.php
new file mode 100644
index 00000000000..5efce251a65
--- /dev/null
+++ b/apps/shorty/appinfo/migrate.php
@@ -0,0 +1,101 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file appinfo/migrate.php
+ * @brief OC migration support
+ * @author Christian Reiner
+ */
+
+class OC_Migration_Provider_Shorty extends OC_Migration_Provider
+{
+
+ function export ( )
+ {
+ OCP\Util::writeLog ( 'migration','starting export for Shorty', OCP\Util::INFO );
+ $options = array(
+ 'table'=>'shorty',
+ 'matchcol'=>'user',
+ 'matchval'=>$this->uid,
+ 'idcol'=>'id'
+ );
+ $ids = $this->content->copyRows( $options );
+ $count = OC_Shorty_Tools::countShorties();
+ // check for success
+ if( (is_array($ids) && is_array($count))
+ && (count($ids)==$count['sum_shortys']) )
+ return true;
+ else return false;
+ } // function export
+
+ function import ( )
+ {
+ switch( $this->appinfo->version )
+ {
+ default:
+ $query = $this->content->prepare( "SELECT * FROM shorty WHERE user_id LIKE ?" );
+ $result = $query->execute( array( $this->olduid ) );
+ if (is_array(is_array($result)))
+ {
+ while( $row = $result->fetchRow() )
+ {
+ $param = array (
+ 'id' => $row['id'],
+ 'status' => $row['status'],
+ 'title' => $row['title'],
+ 'favicon' => $row['favicon'],
+ 'source' => $row['source'],
+ 'target' => $row['target'],
+ 'user' => $row['user'],
+ 'until' => $row['until'],
+ 'created' => $row['created'],
+ 'accessed' => $row['accessed'],
+ 'clicks' => $row['clicks'],
+ 'notes' => $row['notes'],
+ );
+ // import each shorty one by one, no special treatment required, since no autoincrement id is used
+ $query = OCP\DB::prepare( sprintf ( "INSERT INTO *PREFIX*shorty(%s) VALUES (%s)",
+ implode(',',array_keys($param)),
+ implode(',',array_fill(0,count($param),'?')) ) );
+ $query->execute( $param );
+ } // while
+ } // if
+ break;
+ } // switch
+ // check for success by counting the generated entries
+ $count = OC_Shorty_Tools::countShorties();
+ if( (is_array($result) && is_array($count))
+ && (count($result)==$count['sum_shortys']) )
+ return true;
+ else return false;
+ } // function import
+
+} // class OC_Migration_Provider_Shorty
+
+// Load the provider
+new OC_Migration_Provider_Shorty ( 'shortys' );
+
+?>
diff --git a/apps/shorty/css/preferences.css b/apps/shorty/css/preferences.css
new file mode 100644
index 00000000000..37a86a534a6
--- /dev/null
+++ b/apps/shorty/css/preferences.css
@@ -0,0 +1,109 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file css/preferences.css
+ * @brief Style definitions for the user preference dialog
+ * @author Christian Reiner
+ */
+
+#shorty img {
+ vertical-align: middle;
+ margin:0;
+ padding:0;
+}
+#shorty #shortlet {
+ outline: medium none;
+ padding: 0.6em 0.8em;
+ background-image: -moz-linear-gradient(center bottom, Lavender 0%, white 50%);
+ border: 1px solid #DDDDDD;
+ border-radius: 0.5em 0.5em 0.5em 0.5em;
+ box-shadow: 0 1px 0 #BBBBBB inset;
+}
+#shorty #shortlet a {
+ font-style: italic;
+ color: DarkSlateGray;
+}
+#shorty #shortlet:after {
+ content: url("%appswebroot%/apps/shorty/img/drag_me.png");
+ position: relative;
+ top: -12px;
+ margin-left: -160px;
+}
+#shorty .chzn-container {
+ vertical-align: middle;
+}
+
+#shorty .title {
+ margin-bottom: 0.6em;
+}
+
+#shorty .aspect {
+ display: inline-block;
+ font-weight: bold;
+ width: 6em;
+ margin: 0 0.2em 0.4em 0;
+}
+
+#shorty .explain {
+ display: inline-block;
+ font-style: italic;
+ margin: 0.3em;
+}
+
+#shorty .example {
+ font-family: Monospace;
+ margin: 0.3em;
+}
+
+#shorty a.external {
+ text-decoration:underline;
+}
+#shorty a.external:after {
+ content:url("%appswebroot%/apps/shorty/img/actions/external.png");
+ margin-left:0.3em;
+}
+
+#verification #hourglass {
+ display: block;
+ text-align: center;
+ width: 100%; height: 100%;
+ margin-top: 20px;
+}
+
+#verification #failure {
+ display: none;
+ position: absolute;
+}
+
+#verification #success {
+ display: none;
+ position: absolute;
+}
+
+#verification legend {
+ width: 100%;
+ text-align: center;
+}
diff --git a/apps/shorty/css/settings.css b/apps/shorty/css/settings.css
new file mode 100644
index 00000000000..e92031d8913
--- /dev/null
+++ b/apps/shorty/css/settings.css
@@ -0,0 +1,59 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file css/settings.css
+ * @brief Style definitions for the system settings dialog
+ * @author Christian Reiner
+ */
+
+#shorty img {
+ vertical-align: middle;
+ margin:0;
+ padding:0;
+}
+#shorty .aspect {
+ display: inline-block;
+ font-weight: bold;
+ width: 6em;
+ margin: 0 0.2em 0.4em;
+}
+
+#shorty .explain {
+ display: inline-block;
+ font-style: italic;
+ margin: 0.3em;
+}
+
+#shorty .example {
+ font-family: Monospace;
+ margin: 0.3em;
+ text-decoration: underline;
+ color: blue;
+}
+
+#shorty .title {
+ margin-bottom: 0.6em;
+}
diff --git a/apps/shorty/css/shorty.css b/apps/shorty/css/shorty.css
new file mode 100644
index 00000000000..b071f06f77f
--- /dev/null
+++ b/apps/shorty/css/shorty.css
@@ -0,0 +1,488 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file css/shorty.css
+ * @brief Style definitions of the plugins own view
+ * @author Christian Reiner
+ */
+
+/* everything :-) */
+#content { overflow: auto; }
+
+/* a case sensitive help button */
+#content #controls #help {
+/* float:right; */
+}
+/* adapt optical appearance in case the help button is pushed into the header */
+#header #help {
+ float:right;
+ background-color:blue;
+}
+
+/* the shorty desktop, a canvas, contains all elements except of those pushed into the header */
+#desktop {
+ display: inline-block;
+ position: absolute;
+/* width: 100%; height: 100%; */
+ background: transparent;
+ text-align: left;
+ margin-top: 30px;
+ padding: 2.5em 0.5em;
+ padding-top: 20px;
+}
+/* top bar, holding main control elements */
+#controls {
+ display: none;
+}
+/* a dialog for verification of the static backend setup */
+#verification {
+ overflow: hidden;
+}
+#verification p {
+ padding-bottom:0.4em;
+}
+
+/* an activity visualizer, currently a 'rotating timer wheel' */
+#hourglass {
+ display: none;
+ position: absolute;
+ width: 100%; height: 100%;
+ z-index: 1;
+ background: transparent;
+}
+#hourglass img {
+ margin:4em 8em;
+}
+/* a replacement list visualized in case the 'real' list is empty */
+#vacuum {
+ display: none;
+ position: absolute;
+ left:8em; top:9em;
+ width: 100%; height: 100%;
+ z-index: 2;
+ white-space: nowrap;
+ font-style: italic;
+ background: transparent;
+}
+.shorty-standalone #busy {
+ display: none;
+ float: right;
+}
+
+/* the list of shorties */
+#list {
+ display: inline;
+ position: absolute;
+ z-index: 3;
+ margin: 0.4em;
+ background: transparent;
+ table-layout: fixed;
+ width: 100%;
+}
+/* the collapsable toolbar below the lists column title row */
+#list thead #toolbar img {
+ cursor: pointer;
+ vertical-align: middle;
+}
+#list thead #toolbar img.shorty-sorter {
+ opacity:0.6;
+}
+/* last column in the list, content usually invisible, except when hovered */
+#list thead #toolbar img.shorty-active {
+ border:1px solid #888888;
+ border-radius: 0.3em;
+}
+#list thead tr:nth-child(2) > th {
+ padding-bottom:0.5em;
+}
+/* somewhat more fixed layout for the table */
+#list thead #titlebar th span {
+ display:inline-block;
+}
+#list tbody th#title,
+#list tbody td#title,
+#list tbody th#target,
+#list tbody td#target {
+ width:10em;
+}
+#list thead #clicks span {
+ width:3em;
+}
+#list thead #until span {
+ width:5em;
+}
+#list thead #toolbar input,
+#list thead #toolbar .chzn-container {
+ width:6em;
+ font-size: 11px;
+ margin: 0;
+ padding: 0.4em 0.3em 0.2em;
+ vertical-align: baseline;
+ background-color:#FFFFFF;
+}
+
+/* column title apeparance */
+#list thead th {
+ font-weight: bold;
+ padding: 1px 6px;
+}
+#list thead th#clicks,
+#list thead th#until,
+#list thead th#actions {
+ text-align: center;
+}
+/* hide the dummy row */
+#list thead tr:last-child { display: none; }
+/* hide the content of the 'actions' column, except when being hovered */
+#list tbody tr td span.shorty-actions {
+ visibility:visible;
+ opacity:0;
+ transition: opacity 500ms;
+ -moz-transition: opacity 500ms;
+ -webkit-transition: opacity 500ms;
+ -ms-transition: opacity 500ms;
+ -o-transition: opacity 500ms;
+ width:100px;
+ overflow:auto;
+}
+#list tbody tr:hover td span.shorty-actions {
+ opacity:1;
+ transition: opacity 500ms;
+ -moz-transition: opacity 500ms;
+ -webkit-transition: opacity 500ms;
+ -ms-transition: opacity 500ms;
+ -o-transition: opacity 500ms;
+}
+/* highlight a specific, 'clicked' row in the list */
+#list tbody tr.clicked {
+ border-radius: 0.5em;
+ background-color:#EEEEEE;
+ box-shadow: 0 0 0.5px #777777;
+ transition: background-color 500ms;
+ -moz-transition: background-color 500ms;
+ -webkit-transition: background-color 500ms;
+ -ms-transition: background-color 500ms;
+ -o-transition: background-color 500ms;
+}
+/* strike through visible content of rows (entries) marked as deleted */
+#list tbody tr.deleted>#title,
+#list tbody tr.deleted>#target,
+#list tbody tr.deleted>#clicks,
+#list tbody tr.deleted>#until {
+ color: gray;
+ text-decoration: line-through;
+ background: transparent url('%appswebroot%/apps/shorty/img/status/strike.png') 0 50% repeat-x;
+}
+/* replace the 'delete' action icon by an 'undelete' icon */
+#list tbody tr.deleted>#actions #del:after {
+ content: url("%appswebroot%/apps/shorty/img/actions/recycle.png");
+ position: relative;
+ top: 3px;
+ margin-left: -19px;
+ padding: 0;
+ opacity: 0;
+}
+#list tbody tr.deleted:hover>#actions #del>img {
+ opacity: 0;
+}
+#list tbody tr.deleted:hover>#actions #del:after {
+ opacity: 1;
+}
+/* general table cell appearance */
+#list tbody td {
+ padding: 1px 6px;
+}
+#list tbody td#until {
+ text-align: center;
+}
+#list tbody td#clicks {
+ text-align: right;
+ margin-right: 0.4em;
+}
+/* prettyfied select boxes */
+#content span.shorty-select select {
+ width: 6.4em;
+ margin: 0;
+ padding: 0.2em;
+ opacity:0.8;
+ border:0 none;
+ font-size: 10px;
+}
+#content span.shorty-select {
+ border: 1px solid #DDDDDD;
+ border-radius: 0.5em 0.5em 0.5em 0.5em;
+ box-shadow: 0 1px 1px #FFFFFF, 0 1px 0 #BBBBBB inset;
+ outline: medium none;
+ padding: 2px;
+}
+/* general style of interactive dialogs */
+.shorty-dialog {
+ display: none;
+ color: black;
+ border-bottom-left-radius: 1em;
+ border-bottom-right-radius: 1em;
+ box-shadow: 0 2px 1px #777777;
+ vertical-align:middle;
+ z-index:100;
+}
+.shorty-dialog fieldset {
+ position:relative;
+ border-bottom-left-radius: 0.5em;
+ border-bottom-right-radius: 0.5em;
+ border: 1px solid gray;
+ padding: 2px 4px 8px;
+}
+.shorty-dialog br {
+ clear: left;
+}
+.shorty-dialog legend {
+ font-weight: bold;
+ margin: 0 0 0 5px;
+ padding: 0 0.5em 0 0.2em;
+}
+.shorty-dialog legend a {
+ padding: 0;
+}
+.shorty-dialog textarea {
+ width: 80%; height: 5em;
+ background: none repeat scroll 0 0 #F8F8F8;
+ color: #555555;
+ border: 1px solid #DDDDDD;
+ border-radius: 0.5em;
+ cursor: text;
+ vertical-align:baseline;
+ margin: 0.3em;
+}
+.shorty-dialog textarea:hover,
+.shorty-dialog textarea:active {
+ background-color: #FFFFFF;
+ color: #333333;
+ opacity: 1;
+}
+.shorty-dialog input {
+ width: 80%;
+}
+.shorty-dialog a,
+.shorty-dialog input,
+.shorty-dialog select,
+.shorty-dialog textarea,
+.shorty-dialog button {
+ padding: 0.4em 0.2em;
+}
+.shorty-dialog input[readonly],
+.shorty-dialog select[readonly],
+.shorty-dialog textarea[readonly],
+.shorty-dialog button[readonly] {
+ border: 0 none;
+/* box-shadow: 0 0 0; */
+ box-shadow: 0 0 0 #FFFFFF;
+ outline: none;
+ background: none;
+ background-color: #F7F7F7;
+ padding: 0.3em 0.3em;
+}
+.shorty-dialog button#confirm {
+ width: 12em; height: 2em;
+ border: 1px solid lightgray;
+ background-color: #EEEEEE;
+}
+.shorty-dialog button#confirm.sharp {
+ background-color: white;
+}
+.shorty-dialog button#confirm.sharp:hover {
+/* border-color: green; */
+ border: 1px solid gray;
+}
+
+/* dialogs floating above other content ('standing alone') */
+.shorty-standalone {
+ width: 46em;
+ margin: -1px 1em 0;
+ padding: 0.5em;
+ background: none repeat scroll 0 0 #F7F7F7;
+}
+.shorty-standalone fieldset {
+ width: 45em;
+}
+/* dialogs related to elements ('embedded in the content') */
+.shorty-embedded {
+ left: 6em;
+ width: 34em;
+ margin: -1px 4em 0;
+ padding: 0.5em;
+ background: none repeat scroll 0 0 #EEEEEE;
+ position: absolute;
+}
+.shorty-embedded fieldset {
+/* width: 32em; */
+}
+.shorty-embedded a#source,
+.shorty-embedded a#relay,
+.shorty-embedded a#target {
+ display: inline-block;
+ width: 26em;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ vertical-align: middle;
+}
+.shorty-embedded a.shorty-clickable:before {
+ content:url("%appswebroot%/apps/shorty/img/actions/external.png");
+ margin-right:0.3em;
+}
+.shorty-embedded select {
+ margin: 0 0 0.2em 1.0em;
+ padding: 0.2em 0.4em 0.2em 0.2em;
+ border: 0 none;
+ background-color: #F4F4F4;
+}
+.shorty-embedded img.shorty-usage {
+ opacity: 0.6;
+ margin: 1em 1.5em 0.2em;
+ padding: 0;
+}
+.shorty-embedded img.shorty-usage.disabled {
+ opacity: 0.2;
+}
+.shorty-embedded img.shorty-usage:not(.disabled):hover {
+ cursor: pointer;
+}
+.shorty-dialog label {
+ display: inline-block;
+ clear:none;
+ width: 5.5em;
+ text-align: left;
+ vertical-align: baseline;
+ padding: 0.6em 0.5em 0.4em 0.3em;
+ cursor: default;
+}
+.shorty-dialog span {
+ padding: 0.6em 0.5em 0.4em 0.3em;
+}
+.shorty-dialog span.label-line {
+ display: inline;
+ clear: none;
+ margin: 0;
+ padding: 0;
+}
+/* elements (rows) freshly added to the list, this calss might trigger visual effects like pulsation */
+.shorty-fresh {
+ display: none;
+}
+/* elements (rows) currently filtered out (toolbars column filter options) */
+.shorty-filtered {
+ display: none;
+}
+/* style of all icons (as opposed to all images) */
+.shorty-icon {
+ display: inline;
+ background: transparent;
+ vertical-align: middle;
+ padding: 1px;
+}
+/* dim entries with expired 'until' dates */
+.shorty-expired {
+ color: gray;
+}
+/* but keep embedded dialogs readable */
+.shorty-expired .shorty-dialog {
+ color: black;
+}
+.shorty-expired img {
+ opacity : 0.2;
+}
+.shorty-expired .shorty-dialog img {
+ opacity : 1;
+}
+.shorty-expired #until {
+ text-decoration: line-through;
+}
+/* some general style flavours */
+.shorty-prompt {
+ margin-left: 4px;
+}
+.shorty-value {
+ margin-right: 4px;
+}
+.shorty-single {
+ padding: 0.5em 1em;
+}
+.shorty-single:hover {
+ background-color: #EAEAEA;
+}
+.shorty-single:hover .shorty-actions {
+ display: block;
+}
+.shorty-id {
+ color: black;
+}
+.shorty-date {
+ width: 40%;
+}
+.shorty-notes {
+ color: blue;
+ text-decoration: none;
+}
+.shorty-source {
+ color: gray;
+}
+.shorty-target {
+ color: green;
+}
+.shorty-meta {
+ margin: 0.1em;
+}
+/* close buttons in dialogs, embedded or standalone */
+.shorty-close-button {
+ display:inline;
+ float:left;
+ margin:0 0.8em 0 0.3em;
+ padding:0;
+}
+.shorty-close-button img {
+ vertical-align: middle;
+ margin:0;
+ padding:0;
+}
+/* unlike elsewhere we underline clickable links in dialogs */
+.shorty-clickable {
+ white-space: nowrap;
+ text-decoration: underline;
+}
+.shorty-framed {
+ display:inline-block;
+ padding:1em;
+ background-color:white;
+ border:1px solid black;
+}
+/* keep long entries like urls from disrupting the compact appearance of the list */
+.ellipsis {
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ width: inherit;
+}
\ No newline at end of file
diff --git a/apps/shorty/doc/CHANGELOG b/apps/shorty/doc/CHANGELOG
new file mode 100644
index 00000000000..b1b09b50fb8
--- /dev/null
+++ b/apps/shorty/doc/CHANGELOG
@@ -0,0 +1,59 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+Changelog:
+
+* Wed Jun 06 2012 Christian Reiner: version 0.2.2
+Added swallowing of accidential outputs during ajax requests.
+This protects the replies JSON struction from being corrupted.
+Additional server side log entries on level INFO
+* Sat Jun 02 2012 Christian Reiner: version 0.2.1
+Fixed broken Shortlet
+* Fri Jun 01 2012 Christian Reiner: version 0.2.0
+Support of ownCloud version 4 (app interface incompatible to prior versions)
+Additional status 'private' for Shortys only accessible for the owner himself
+Rudimentary SMS support for smart phones (more an assistance, can be disabled)
+Additional QRCodes to publish Shorty source urls on the web (or elsewhere)
+Data migration support as introduced by OC4
+Cleanup of Shortys upon user account deletion
+* Sun May 28 2012 Christian Reiner: version 0.1.6
+Activation of Shorty sharing via SMS.
+Enhanced backend descriptions.
+* Sat Apr 28 2012 Christian Reiner: version 0.1.5
+Target url verification is less strict now, this way also more 'exotic' urls notations are accepted.
+A few changes to the initilization scripts to prevent race conditions at startup.
+Changed url specification in the Shortlet to prevent conflicts with a serverside suhosin protection.
+* Fri Apr 27 2012 Christian Reiner: version 0.1.4
+Visualization of busy state in dialogs whilst fetch meta data from targets.
+* Fri Apr 27 2012 Christian Reiner: version 0.1.3
+Prevented untimely submission of dialogs before having validated their contents.
+* Fri Apr 27 2012 Christian Reiner: version 0.1.2
+Fixed race conditions during initialization.
+* Thu Apr 26 2012 Christian Reiner: version 0.1.1
+Minor appearance corrections.
+* Wed Apr 25 2012 Christian Reiner: version 0.1.0
+First official release.
+* Tue Apr 24 2012 Christian Reiner: version 0.0.9
+Initial release.
+Buggy, but some parts actually working.
diff --git a/apps/shorty/doc/CONFIGURATION b/apps/shorty/doc/CONFIGURATION
new file mode 100644
index 00000000000..2a7dab60d19
--- /dev/null
+++ b/apps/shorty/doc/CONFIGURATION
@@ -0,0 +1,113 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+There are five aspects that can be configured:
+1.) System wide "static backend" (optional)
+2.) Selection of a backend per user (optional)
+3.) Import of the "shortlet" into a web browser (optional)
+4.) Access control inside each single "shorty" (implicit)
+5.) Sending of Shortys as SMS
+
+1.) System wide "static backend" (optional)
+(Requires adminisstrative rights)
+This optional setting enables a 'static backend'. That is a backend generting
+source urls based on a static base url, thus the name. That base url should be
+chosen as short as possible, since this defines the total length of all links
+to be posted and used. Most likely the definition of that url requires the
+configuration of rewrite rules on the server side. So it is only an option for
+experienced adminsitrators with access to the server configuration, be it
+centralized or based on decentralized per-directory rules (".htaccess files").
+A basic example is packed in the .htaccess file (might be hidden in a directory
+listing because of the leading dot (.) in the name). The rules must be
+configured such that all requests to urls of the scheme
+are mapped directly onto the web url of the shorty plugin
+(http:////apps/shorty/). The is a string, 6-12
+characters long, hard to predict. It is guaranteed to be unique throughout the
+system (though in a technically crude manner...). The perfect situation for the
+definition of a meaningful static backend would be a domain with a very short
+name and configuration access to something close to the web root.
+A random example is my own domain "christian-reiner.info". Since that is not
+exactly short I would love to use the additional domain "c-r.info" for the sole
+purpose of a static backend. Unfortunately that domain has been reserved by a
+domain grabber who asks thousands of Euros for it ! So that's no option. At
+least you should try to find a setup where the web path of the ownCloud
+application is not part of the base url. So that you get something like
+http:///.
+(Note that the shorty key is NOT part of the base url configuration).
+You can easily test your setup any time by clicking on the example dynamically
+visualized next to the configuration option. It runs a simple self-tests of the
+setup in background.
+There is no need that the base url must be served by the same http server that
+serves the owncoud system. A static rewrite setup may well forward short source
+urls to the shorty plugin inside a remote ownCloud installation. However, it is
+a known issue that the internal validation of the setup won't work with such
+scenario.
+
+2.) Selection of a backend per user (optional)
+To generate the a source url that is part of every shorty the plugin uses a
+backend. The configuration is done by using a preference option in the personal
+preference section of the configuration. You can simply chose one of the
+offered backends (combo box). Changing the backend does not affect any
+previously generated shorties. Meaning they stay valid and usable and keep
+their once defined source url.
+
+Different backends are implemented:
+i.) "-none-"
+As you have guessed this is something like a "dummy" backend without any
+implemented logic. That means the source urls generated are exactly based on
+the web url of the shorty plugin in your ownCloud system. This is not a very
+clever setup, but it certainly works and keeps your data private.
+ii.) "static backend"
+If configured in the system administration a "static backend" is offered. For a
+description see C-1. This backend typically offers shorter source urls, but its
+setup required administrative rights on some http server system.
+iii.) online services (url shorteners)
+A few online services are offered as backends to generate short source urls.
+Usage of some of those services requires you to open a free account at their
+site. Detailed configuration requirements are displayed for the chosen backend.
+If you don't care for details and just want short urls then have a try with the
+tin.ny backend. No registration required, reliable service. But keep in mind
+you depend on an external service as opposed to using a local static backend.
+
+3.) Import of the "shortlet" into a web browser (optional)
+Shorty comes with a neat little "Shortlet" offered in the personal preferneces
+section of ownCloud. It is a "button" you can import into your web browsers
+bookmark toolbar or area by simple drag & drop. The Shortlet should work with
+most modern browsers, though probably not all. Just have a try with it.
+
+4.) Access control inside each single "shorty" (implicit)
+There are a few attributres you can configure freely inside each stored shorty:
+- a title just shown inside the shorty plugin (serves recognition).
+- a notes area, maybe you want to write down whom you send the shorty ?
+- a status option that controls the usage of the shorty.
+
+5.) Sending of Shortys as SMS
+Besides sending of Shortys as email message and copying a Shortys source url
+to the clipboard a third action can be enabled inside the sharing dialog:
+To send a Shortys source url as SMS.
+However, the approach is extremely minimalistic, it relies on the client system
+to correctly handle a 'sms url'. This is typically only given on a mobile
+device, a smart phone. In addition, such url does not allow to specify a message
+body, so the Shortys source url has to be copied and pasted manually.
+The option is disabled by default.
diff --git a/apps/shorty/doc/COPYRIGHT b/apps/shorty/doc/COPYRIGHT
new file mode 100644
index 00000000000..5e501826f28
--- /dev/null
+++ b/apps/shorty/doc/COPYRIGHT
@@ -0,0 +1,23 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
diff --git a/apps/shorty/doc/CREDITS b/apps/shorty/doc/CREDITS
new file mode 100644
index 00000000000..8f7a3dd037c
--- /dev/null
+++ b/apps/shorty/doc/CREDITS
@@ -0,0 +1,32 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+The shorty plugin has been developed by:
+
+Christian Reiner, Hamburg, Germany
+E-Mail: foss@christian-reiner.information
+
+Contributions:
+I'd be more than happy to welcome all contributions to this little experiment.
+Drop me a note, an idea, a patch, a good bottle of wine, anything !
diff --git a/apps/shorty/doc/INSTALLATION b/apps/shorty/doc/INSTALLATION
new file mode 100644
index 00000000000..0a98605376a
--- /dev/null
+++ b/apps/shorty/doc/INSTALLATION
@@ -0,0 +1,53 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+This package is a plugin for the ownCloud web application ("ownCloud app").
+
+There are two ways of installation: automatic and manual
+
+* Automatic installation:
+You need login to your ownCloud using an account with administrative rights.
+Open the 'Apps' section of the adminstration and select 'shorty', enable it.
+Then go on below with the basic configuration steps.
+
+* Manual installation:
+Download the package from here:
+http://apps.owncloud.com/content/show.php/Shorty?content=150401
+Create a subfolder 'shorty' in the "apps" subfolder of your ownCloud web root.
+Unpack the contents of the package into the new folder 'shorty'.
+Now load ownCloud in your favorite web browser and login with an administrative
+account.
+Enable the plugin in the "Apps" section of the configuration ("*") inside
+ownClouds web gui (requires admin rights).
+
+* Basic configuration steps for BOTH types of installation:
+The "Admin" section of the configuration allows to configure a base url to
+enable usage of the static backend (see USAGE).
+The "Preferences" section of that configuration now offers two elements:
+- a "Shortlet" to be dragged to the browsers bookmark area (see USAGE).
+- a configuration of a backend to use to generate source urls (see USAGE).
+The main part of the plugin can be accessed in the navigation menu as "Shorty".
+
+***
+Have fun !
diff --git a/apps/shorty/doc/ISSUES b/apps/shorty/doc/ISSUES
new file mode 100644
index 00000000000..083c4648636
--- /dev/null
+++ b/apps/shorty/doc/ISSUES
@@ -0,0 +1,58 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+This is a short list of known issues:
+
+- The datepicker popup in the edit dialog fails to set the expiration date:
+I have no idea what the problem is. Exactly the same object works flawlessly
+in the dialog to create a new shorty. I can't see any difference, except in
+the edit dialog the date is taken from the existing row entry (working fine).
+Maybe there is some sort of magic spell to awake a datepicker after changing
+its date value ? No idea...
+
+- Clicking on a source or relay url from within the sharing dialog updates the
+clicks counter and access time correctly, but the shown list entry is not
+updated accordingly.
+
+- Shorty does not work in the konqueror web browser
+All appears to be working fine except for one annoying thing:
+the standalone dialogs won't open ! (show, create and edit entries)
+Therefore Shorty is currently more or less useless in this browser.
+
+- Shorty does not work in the rekonq web browser
+The basic navigation elements (menu left) work, but the desktop stays empty.
+Apparently no script is executed, an issue with the js document.ready event.
+
+- It appears there is an issue with setting opacity in the Safari browser:
+The status filter in the lists toolbar shows an ugly black brick instead of a
+dimmed icon overlay. Also some other style issues appear, but the plugin seems
+to be completely usable.
+
+- When internally validating the setup of the base url used in the static
+backend you get a false positive ("doesn't work") although things are fine, if
+the mapping of base url to shorty plugin is done via a redirect. This is for
+exaple the case if you implement the mapping on a remote server because you
+want to use a different domain name for statically shortened urls.
+The reason is that jqueries ajax calls won't handle redirects internally. A
+wrapper will have to be implemented.
diff --git a/apps/shorty/doc/README b/apps/shorty/doc/README
new file mode 100644
index 00000000000..4f590f30cd0
--- /dev/null
+++ b/apps/shorty/doc/README
@@ -0,0 +1,42 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+This "shorty" package implements a plugin for the ownCloud web application.
+Shorty offers a service to store, manage and use a collection of short links
+pointing to ressources in the web. The features are a combination of a
+centralized bookmarks collection, an url shortener and a basic access control.
+The collection is presented as a list of "shorties". A shorty can be created
+either manually in the list ("New shorty") or by using the "Shortlet". The
+Shortlet is something like a Bookmarklet, a script based bookmark meant to be
+stored inside the bookmark collection of a web browser. When clicked, the page
+currently open in the browser will be added to the list of existing shorties.
+
+Each Shorty contains a source and a target url. The source url can be used to
+be posted in forums, sent inside an email message or whatever. It is typically
+shorter than the target url, a full blown web url. But that actually depends
+on the target url and the backend used. The target url is the ressource
+identified by the shorty. Basic access control is implemented to control the
+access of shorties posted to forums or sent per email, but not on a per user or
+per group base. Existing shorties can be blocked for usage, set to expire on a
+certain date and obviously can be removed permanently again.
diff --git a/apps/shorty/doc/ROADMAP b/apps/shorty/doc/ROADMAP
new file mode 100644
index 00000000000..b18099c9af2
--- /dev/null
+++ b/apps/shorty/doc/ROADMAP
@@ -0,0 +1,96 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+Future versions might implement additional features and services. Currently
+planned are the following:
+
+* Ability to create Shortys directly from shared files inside ownCloud
+Creating a Shorty based on the public url generated for shared files works like
+charm. However it is not very convenient to do that manually. It would be great
+to offer that option directly where file sharing is done, in that very dialog.
+However I currently do not see any means to achieve that on ownCloud. There is
+no feature offered to hook additional functionality into exsiting apps.
+This means either the existing file sharing app has to be modified or such a
+modifying core feature has to be implemented into the ownCLoud core.
+Or maybe someone has got another idea ?
+
+* Integrated proxy function as an alternative to forwarding:
+Currently all browser requests are forwarded to the final target url, provided
+that the referenced shorty is still valid and accessible. This is a simple but
+effective strategy. Offering a transparent proxy service as an alternative
+would greatly enhance the privacy protection, since the final target url of a
+referenced web ressource would never be handed out.
+
+* Better, more fine grained access control:
+Currently the only means of access control, apart from defining or removing a
+shorty are its "status". The status can block an otherwise valid shorty from
+usage from outside ownCloud, it can restrict its access to authenticated users
+or it can make an existing shorty freely accessible to the public. Other
+ownCloud plugins allow to define individual access to content on per-user or
+per-group rules.
+
+* Support of further backends:
+Currently the usage of six url shortener services is implemented, besides the
+two private or local ones. Unfortunately three of these had to be disabled due
+to stability issues. Besides correcting the implementation for the disabled it
+might make sense to support further backends, especially other types besides
+online services.
+
+* Performance, stability and usability enhancements:
+"Shorty" is my first web application using up to date web technologies.
+Therefore the implementation certainly is far from the quality one could expect
+from a serious and experienced web developer. One could also put this into more
+constructive terms: there is definately much room for enhancements :-)
+
+* Translations of the shorty module itself (i18n/l10n):
+Main development language is english (us), a german translation exists. I do
+not feel fit enough in other languages to create a translation myself. So I
+certainly welcome any assistance in that aspect. The effort consists of editing
+one single file, take a look at the german example in file l10n/de.php.
+
+* Usage of svg icons instead of png graphics:
+I created all icons in svg format. ownCloud implements a mechanism to use those
+in browsers supporting svg directly. However I did not yet figure out how to
+use that feature without adding too much scripting overhead. Is it possible
+to reference icons the ownCloud way directly in a php based template, without
+the need to explicit handling via javascript ?
+I wish a better API documentation would exist for ownCloud !
+
+* Localization for the date picker fields
+Currently date inputs (expiration date) are not validated explicitly.
+A number of formats work, are accepted correct, but there is no localization
+of the input format to the users language.
+
+* Integrated help button
+One thing I miss throughout the ownCloud application is a context sensitive
+help, or a help button at all ! Originally I planned to integrate on into this
+plugin. However I think that is more something that should be implemented as a
+core feature of ownCloud. Maybe as a separate plugin (app). SOmehing like a
+help framework with a documented api where other plugins (and core apps) can
+attach help texts or documents to. Context sensivity would then simply mean to
+update the reference in the help button for each action taken.
+
+Contributions:
+I'd be more than happy to welcome all contributions to this little experiment.
+Drop me a note, an idea, a patch, a good bottle of wine, anything !
diff --git a/apps/shorty/doc/STATUS b/apps/shorty/doc/STATUS
new file mode 100644
index 00000000000..0b85d147028
--- /dev/null
+++ b/apps/shorty/doc/STATUS
@@ -0,0 +1,42 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+The current status of this package is as follows:
+
+The initial release is definately buggy and contains annoying shortcomings.
+It has been developed on a linux system using a firefox browser, so this is
+most likely the best working combination. Basic usage test have been made with
+a few other browsers. The package appears to be working in general though there
+might be some minor differences in apeparance.
+
+Known shortcomings are:
+- only very basic input validation is done.
+- access control is not yet fine grained, no user or group support exists.
+- markup, scripting and style definitions are the work of a web app newbie.
+
+So "sorry for any inconveniences" !
+
+Contributions:
+I'd be more than happy to welcome all contributions to this little experiment.
+Drop me a note, an idea, a patch, a good bottle of wine, anything !
diff --git a/apps/shorty/doc/USAGE b/apps/shorty/doc/USAGE
new file mode 100644
index 00000000000..a82f881992a
--- /dev/null
+++ b/apps/shorty/doc/USAGE
@@ -0,0 +1,64 @@
+*****
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*****
+
+=== Overview ===
+See file README.
+
+=== Installation ===
+See file INSTALLATION.
+
+=== Requirements ===
+ownCloud version 2 or newer on the server side.
+A css3 capable and reasonably standards conform web browser.
+A scripting capable bookmarks feature for the optional Shortlet thing.
+
+=== Usage ===
+This is only a very basic description. The plugin should be fairly intuitive to
+use, just have a try with a few web urls. To get going I suggest that you
+- chose a backend (see configuration below).
+- import the shortlet into your preferred web browser (see configuration below).
+- define a few shorties either manually ("New") or by using the shortlet.
+All actions on existing shorties are accessed using the action icons shown to
+the right of the shorty in the list. Daily uses is probably based on selection
+the 'share' action and grabbing the visualized "source url".
+By clicking on the header row of the list of shorties you can open (and close)
+a "toolbox" offering options to sort, filter and reload the list. The sorting
+you chose is stored in a persistant manner, not so any filters defined. You
+cannot accidentially close the toolbox when there are still filters active.
+You can remove (delete) existing shorties, but those shorties will only be
+marked as deleted. This way you can 'undelete' them if required. Permanent
+removal is done next time you access the shorty plugin. If you really want to
+make sure the removal is done NOW then simply reload the list. This is done in
+the "toolbox" of the list (click on the header row) by chosing the "reload"
+icon on the left (or by a simple browser reload).
+
+=== PRIVACY ===
+Shorty allows you to stay more in control over links you post or send. It is up
+to you to decide wether a once posted link should stay valid and usable or not.
+However it is also very important to keep in mind that such form of control can
+never replace any serious form of access control over the content published via
+a published link. Shorty works as a short url resolver, meaning it forwards any
+requesting web browser to the real address of the published content. This might
+change when if future an integrated proxy function gets implemented, as opposed
+to the current forwarding mechanism.
diff --git a/apps/shorty/img/actions/down.png b/apps/shorty/img/actions/down.png
new file mode 100644
index 00000000000..d2627e525ee
Binary files /dev/null and b/apps/shorty/img/actions/down.png differ
diff --git a/apps/shorty/img/actions/down.svg b/apps/shorty/img/actions/down.svg
new file mode 100644
index 00000000000..b01642681c1
--- /dev/null
+++ b/apps/shorty/img/actions/down.svg
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/external.png b/apps/shorty/img/actions/external.png
new file mode 100644
index 00000000000..5da1c41c45d
Binary files /dev/null and b/apps/shorty/img/actions/external.png differ
diff --git a/apps/shorty/img/actions/external.svg b/apps/shorty/img/actions/external.svg
new file mode 100644
index 00000000000..32453b1ca29
--- /dev/null
+++ b/apps/shorty/img/actions/external.svg
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/info.png b/apps/shorty/img/actions/info.png
new file mode 100644
index 00000000000..2febafa33e5
Binary files /dev/null and b/apps/shorty/img/actions/info.png differ
diff --git a/apps/shorty/img/actions/info.svg b/apps/shorty/img/actions/info.svg
new file mode 100644
index 00000000000..e92ab4fbf0f
--- /dev/null
+++ b/apps/shorty/img/actions/info.svg
@@ -0,0 +1,70 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/left.png b/apps/shorty/img/actions/left.png
new file mode 100644
index 00000000000..e76cf4df2ed
Binary files /dev/null and b/apps/shorty/img/actions/left.png differ
diff --git a/apps/shorty/img/actions/left.svg b/apps/shorty/img/actions/left.svg
new file mode 100644
index 00000000000..670f0c3069a
--- /dev/null
+++ b/apps/shorty/img/actions/left.svg
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/minus.png b/apps/shorty/img/actions/minus.png
new file mode 100644
index 00000000000..72ee0f69ff2
Binary files /dev/null and b/apps/shorty/img/actions/minus.png differ
diff --git a/apps/shorty/img/actions/open.png b/apps/shorty/img/actions/open.png
new file mode 100644
index 00000000000..54cdd3a2a4c
Binary files /dev/null and b/apps/shorty/img/actions/open.png differ
diff --git a/apps/shorty/img/actions/open.svg b/apps/shorty/img/actions/open.svg
new file mode 100644
index 00000000000..46e137d73f9
--- /dev/null
+++ b/apps/shorty/img/actions/open.svg
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/plus.png b/apps/shorty/img/actions/plus.png
new file mode 100644
index 00000000000..f18d9c6c31a
Binary files /dev/null and b/apps/shorty/img/actions/plus.png differ
diff --git a/apps/shorty/img/actions/recycle.png b/apps/shorty/img/actions/recycle.png
new file mode 100644
index 00000000000..29444fd7fd9
Binary files /dev/null and b/apps/shorty/img/actions/recycle.png differ
diff --git a/apps/shorty/img/actions/recycle.svg b/apps/shorty/img/actions/recycle.svg
new file mode 100644
index 00000000000..61ee3ac8409
--- /dev/null
+++ b/apps/shorty/img/actions/recycle.svg
@@ -0,0 +1,93 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/reload.png b/apps/shorty/img/actions/reload.png
new file mode 100644
index 00000000000..06df1f194e0
Binary files /dev/null and b/apps/shorty/img/actions/reload.png differ
diff --git a/apps/shorty/img/actions/right.png b/apps/shorty/img/actions/right.png
new file mode 100644
index 00000000000..a252a751554
Binary files /dev/null and b/apps/shorty/img/actions/right.png differ
diff --git a/apps/shorty/img/actions/right.svg b/apps/shorty/img/actions/right.svg
new file mode 100644
index 00000000000..997068063e3
--- /dev/null
+++ b/apps/shorty/img/actions/right.svg
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/shade.png b/apps/shorty/img/actions/shade.png
new file mode 100644
index 00000000000..a49b9ad34c4
Binary files /dev/null and b/apps/shorty/img/actions/shade.png differ
diff --git a/apps/shorty/img/actions/shade.svg b/apps/shorty/img/actions/shade.svg
new file mode 100644
index 00000000000..1823941c790
--- /dev/null
+++ b/apps/shorty/img/actions/shade.svg
@@ -0,0 +1,78 @@
+
+
+
+
diff --git a/apps/shorty/img/actions/up.png b/apps/shorty/img/actions/up.png
new file mode 100644
index 00000000000..c878f84396c
Binary files /dev/null and b/apps/shorty/img/actions/up.png differ
diff --git a/apps/shorty/img/actions/up.svg b/apps/shorty/img/actions/up.svg
new file mode 100644
index 00000000000..728243b3b5d
--- /dev/null
+++ b/apps/shorty/img/actions/up.svg
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/apps/shorty/img/blank.png b/apps/shorty/img/blank.png
new file mode 100644
index 00000000000..9386bf3e17b
Binary files /dev/null and b/apps/shorty/img/blank.png differ
diff --git a/apps/shorty/img/calendar.png b/apps/shorty/img/calendar.png
new file mode 100644
index 00000000000..0e5fdb75bb6
Binary files /dev/null and b/apps/shorty/img/calendar.png differ
diff --git a/apps/shorty/img/drag_me.png b/apps/shorty/img/drag_me.png
new file mode 100644
index 00000000000..3ebac17d2d3
Binary files /dev/null and b/apps/shorty/img/drag_me.png differ
diff --git a/apps/shorty/img/drag_me.svg b/apps/shorty/img/drag_me.svg
new file mode 100644
index 00000000000..f1d44cdfb9d
--- /dev/null
+++ b/apps/shorty/img/drag_me.svg
@@ -0,0 +1,135 @@
+
+
+
+
diff --git a/apps/shorty/img/loading-bar.gif b/apps/shorty/img/loading-bar.gif
new file mode 100644
index 00000000000..b02798a87eb
Binary files /dev/null and b/apps/shorty/img/loading-bar.gif differ
diff --git a/apps/shorty/img/loading-disk.gif b/apps/shorty/img/loading-disk.gif
new file mode 100644
index 00000000000..22c961011cd
Binary files /dev/null and b/apps/shorty/img/loading-disk.gif differ
diff --git a/apps/shorty/img/loading-led.gif b/apps/shorty/img/loading-led.gif
new file mode 100644
index 00000000000..78893c90606
Binary files /dev/null and b/apps/shorty/img/loading-led.gif differ
diff --git a/apps/shorty/img/scheme/A.png b/apps/shorty/img/scheme/A.png
new file mode 100644
index 00000000000..65163556bab
Binary files /dev/null and b/apps/shorty/img/scheme/A.png differ
diff --git a/apps/shorty/img/scheme/B.png b/apps/shorty/img/scheme/B.png
new file mode 100644
index 00000000000..a5562488824
Binary files /dev/null and b/apps/shorty/img/scheme/B.png differ
diff --git a/apps/shorty/img/scheme/C.png b/apps/shorty/img/scheme/C.png
new file mode 100644
index 00000000000..61b873de73f
Binary files /dev/null and b/apps/shorty/img/scheme/C.png differ
diff --git a/apps/shorty/img/scheme/D.png b/apps/shorty/img/scheme/D.png
new file mode 100644
index 00000000000..6ab7451d04a
Binary files /dev/null and b/apps/shorty/img/scheme/D.png differ
diff --git a/apps/shorty/img/scheme/E.png b/apps/shorty/img/scheme/E.png
new file mode 100644
index 00000000000..d9c4e07001b
Binary files /dev/null and b/apps/shorty/img/scheme/E.png differ
diff --git a/apps/shorty/img/scheme/F.png b/apps/shorty/img/scheme/F.png
new file mode 100644
index 00000000000..2663b5eb308
Binary files /dev/null and b/apps/shorty/img/scheme/F.png differ
diff --git a/apps/shorty/img/scheme/G.png b/apps/shorty/img/scheme/G.png
new file mode 100644
index 00000000000..d1d965d34cb
Binary files /dev/null and b/apps/shorty/img/scheme/G.png differ
diff --git a/apps/shorty/img/scheme/H.png b/apps/shorty/img/scheme/H.png
new file mode 100644
index 00000000000..c644a77bfc5
Binary files /dev/null and b/apps/shorty/img/scheme/H.png differ
diff --git a/apps/shorty/img/scheme/J.png b/apps/shorty/img/scheme/J.png
new file mode 100644
index 00000000000..0eaa189f4f6
Binary files /dev/null and b/apps/shorty/img/scheme/J.png differ
diff --git a/apps/shorty/img/scheme/K.png b/apps/shorty/img/scheme/K.png
new file mode 100644
index 00000000000..3d587ae4478
Binary files /dev/null and b/apps/shorty/img/scheme/K.png differ
diff --git a/apps/shorty/img/scheme/L.png b/apps/shorty/img/scheme/L.png
new file mode 100644
index 00000000000..4e37b9f3645
Binary files /dev/null and b/apps/shorty/img/scheme/L.png differ
diff --git a/apps/shorty/img/scheme/M.png b/apps/shorty/img/scheme/M.png
new file mode 100644
index 00000000000..a4a02e60b40
Binary files /dev/null and b/apps/shorty/img/scheme/M.png differ
diff --git a/apps/shorty/img/scheme/N.png b/apps/shorty/img/scheme/N.png
new file mode 100644
index 00000000000..6c860f77347
Binary files /dev/null and b/apps/shorty/img/scheme/N.png differ
diff --git a/apps/shorty/img/scheme/O.png b/apps/shorty/img/scheme/O.png
new file mode 100644
index 00000000000..14a05847b5c
Binary files /dev/null and b/apps/shorty/img/scheme/O.png differ
diff --git a/apps/shorty/img/scheme/P.png b/apps/shorty/img/scheme/P.png
new file mode 100644
index 00000000000..e53d3ee9801
Binary files /dev/null and b/apps/shorty/img/scheme/P.png differ
diff --git a/apps/shorty/img/scheme/Q.png b/apps/shorty/img/scheme/Q.png
new file mode 100644
index 00000000000..d07c42a76de
Binary files /dev/null and b/apps/shorty/img/scheme/Q.png differ
diff --git a/apps/shorty/img/scheme/R.png b/apps/shorty/img/scheme/R.png
new file mode 100644
index 00000000000..9b522d6c5a0
Binary files /dev/null and b/apps/shorty/img/scheme/R.png differ
diff --git a/apps/shorty/img/scheme/S.png b/apps/shorty/img/scheme/S.png
new file mode 100644
index 00000000000..1aff52f7525
Binary files /dev/null and b/apps/shorty/img/scheme/S.png differ
diff --git a/apps/shorty/img/scheme/T.png b/apps/shorty/img/scheme/T.png
new file mode 100644
index 00000000000..1736eedc47b
Binary files /dev/null and b/apps/shorty/img/scheme/T.png differ
diff --git a/apps/shorty/img/scheme/U.png b/apps/shorty/img/scheme/U.png
new file mode 100644
index 00000000000..320938cedf1
Binary files /dev/null and b/apps/shorty/img/scheme/U.png differ
diff --git a/apps/shorty/img/scheme/V.png b/apps/shorty/img/scheme/V.png
new file mode 100644
index 00000000000..2fe5e5a1621
Binary files /dev/null and b/apps/shorty/img/scheme/V.png differ
diff --git a/apps/shorty/img/scheme/W.png b/apps/shorty/img/scheme/W.png
new file mode 100644
index 00000000000..e7b37b011f2
Binary files /dev/null and b/apps/shorty/img/scheme/W.png differ
diff --git a/apps/shorty/img/scheme/X.png b/apps/shorty/img/scheme/X.png
new file mode 100644
index 00000000000..4a3d8bd3816
Binary files /dev/null and b/apps/shorty/img/scheme/X.png differ
diff --git a/apps/shorty/img/scheme/Y.png b/apps/shorty/img/scheme/Y.png
new file mode 100644
index 00000000000..af76045e897
Binary files /dev/null and b/apps/shorty/img/scheme/Y.png differ
diff --git a/apps/shorty/img/scheme/Z.png b/apps/shorty/img/scheme/Z.png
new file mode 100644
index 00000000000..0d00ced9c14
Binary files /dev/null and b/apps/shorty/img/scheme/Z.png differ
diff --git a/apps/shorty/img/shorty.png b/apps/shorty/img/shorty.png
new file mode 100644
index 00000000000..3fa8a55f9d8
Binary files /dev/null and b/apps/shorty/img/shorty.png differ
diff --git a/apps/shorty/img/shorty.svg b/apps/shorty/img/shorty.svg
new file mode 100644
index 00000000000..79b2ee69015
--- /dev/null
+++ b/apps/shorty/img/shorty.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/status/active.png b/apps/shorty/img/status/active.png
new file mode 100644
index 00000000000..51d013cd058
Binary files /dev/null and b/apps/shorty/img/status/active.png differ
diff --git a/apps/shorty/img/status/active.svg b/apps/shorty/img/status/active.svg
new file mode 100644
index 00000000000..bb4c0573af1
--- /dev/null
+++ b/apps/shorty/img/status/active.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/status/bad.png b/apps/shorty/img/status/bad.png
new file mode 100644
index 00000000000..11627a21fee
Binary files /dev/null and b/apps/shorty/img/status/bad.png differ
diff --git a/apps/shorty/img/status/good.png b/apps/shorty/img/status/good.png
new file mode 100644
index 00000000000..08ce0a4453a
Binary files /dev/null and b/apps/shorty/img/status/good.png differ
diff --git a/apps/shorty/img/status/neutral.png b/apps/shorty/img/status/neutral.png
new file mode 100644
index 00000000000..5b2ad589cb9
Binary files /dev/null and b/apps/shorty/img/status/neutral.png differ
diff --git a/apps/shorty/img/status/strike.png b/apps/shorty/img/status/strike.png
new file mode 100644
index 00000000000..d9a867fa004
Binary files /dev/null and b/apps/shorty/img/status/strike.png differ
diff --git a/apps/shorty/img/usage/64/arrow.png b/apps/shorty/img/usage/64/arrow.png
new file mode 100644
index 00000000000..ac35f78ad1c
Binary files /dev/null and b/apps/shorty/img/usage/64/arrow.png differ
diff --git a/apps/shorty/img/usage/64/clipboard.png b/apps/shorty/img/usage/64/clipboard.png
new file mode 100644
index 00000000000..701bb6a7796
Binary files /dev/null and b/apps/shorty/img/usage/64/clipboard.png differ
diff --git a/apps/shorty/img/usage/64/email.png b/apps/shorty/img/usage/64/email.png
new file mode 100644
index 00000000000..cfa1f767821
Binary files /dev/null and b/apps/shorty/img/usage/64/email.png differ
diff --git a/apps/shorty/img/usage/64/globe.png b/apps/shorty/img/usage/64/globe.png
new file mode 100644
index 00000000000..2d41da4819a
Binary files /dev/null and b/apps/shorty/img/usage/64/globe.png differ
diff --git a/apps/shorty/img/usage/64/home.png b/apps/shorty/img/usage/64/home.png
new file mode 100644
index 00000000000..8cff03b28c6
Binary files /dev/null and b/apps/shorty/img/usage/64/home.png differ
diff --git a/apps/shorty/img/usage/64/info.png b/apps/shorty/img/usage/64/info.png
new file mode 100644
index 00000000000..2069088f64a
Binary files /dev/null and b/apps/shorty/img/usage/64/info.png differ
diff --git a/apps/shorty/img/usage/64/locked.png b/apps/shorty/img/usage/64/locked.png
new file mode 100644
index 00000000000..841d2d4483f
Binary files /dev/null and b/apps/shorty/img/usage/64/locked.png differ
diff --git a/apps/shorty/img/usage/64/network.png b/apps/shorty/img/usage/64/network.png
new file mode 100644
index 00000000000..fac47ac7b75
Binary files /dev/null and b/apps/shorty/img/usage/64/network.png differ
diff --git a/apps/shorty/img/usage/64/qrcode.png b/apps/shorty/img/usage/64/qrcode.png
new file mode 100644
index 00000000000..207741888d3
Binary files /dev/null and b/apps/shorty/img/usage/64/qrcode.png differ
diff --git a/apps/shorty/img/usage/64/sms.png b/apps/shorty/img/usage/64/sms.png
new file mode 100644
index 00000000000..5dcd2c12231
Binary files /dev/null and b/apps/shorty/img/usage/64/sms.png differ
diff --git a/apps/shorty/img/usage/64/team.png b/apps/shorty/img/usage/64/team.png
new file mode 100644
index 00000000000..e6ea3375807
Binary files /dev/null and b/apps/shorty/img/usage/64/team.png differ
diff --git a/apps/shorty/img/usage/64/unlocked.png b/apps/shorty/img/usage/64/unlocked.png
new file mode 100644
index 00000000000..54501bbd11f
Binary files /dev/null and b/apps/shorty/img/usage/64/unlocked.png differ
diff --git a/apps/shorty/img/usage/64/workstation.png b/apps/shorty/img/usage/64/workstation.png
new file mode 100644
index 00000000000..909098c9d8d
Binary files /dev/null and b/apps/shorty/img/usage/64/workstation.png differ
diff --git a/apps/shorty/img/usage/_.svg b/apps/shorty/img/usage/_.svg
new file mode 100644
index 00000000000..e0ef15d83c3
--- /dev/null
+++ b/apps/shorty/img/usage/_.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/arrow.svg b/apps/shorty/img/usage/arrow.svg
new file mode 100644
index 00000000000..94b2bf96abe
--- /dev/null
+++ b/apps/shorty/img/usage/arrow.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/clipboard.svg b/apps/shorty/img/usage/clipboard.svg
new file mode 100644
index 00000000000..a470bdaae45
--- /dev/null
+++ b/apps/shorty/img/usage/clipboard.svg
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/email.svg b/apps/shorty/img/usage/email.svg
new file mode 100644
index 00000000000..7283939e37a
--- /dev/null
+++ b/apps/shorty/img/usage/email.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/globe.svg b/apps/shorty/img/usage/globe.svg
new file mode 100644
index 00000000000..018f161dbf4
--- /dev/null
+++ b/apps/shorty/img/usage/globe.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/home.svg b/apps/shorty/img/usage/home.svg
new file mode 100644
index 00000000000..8c29644503c
--- /dev/null
+++ b/apps/shorty/img/usage/home.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/info.svg b/apps/shorty/img/usage/info.svg
new file mode 100644
index 00000000000..31a3b6387eb
--- /dev/null
+++ b/apps/shorty/img/usage/info.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/locked.svg b/apps/shorty/img/usage/locked.svg
new file mode 100644
index 00000000000..1691a9dfa4d
--- /dev/null
+++ b/apps/shorty/img/usage/locked.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/network.svg b/apps/shorty/img/usage/network.svg
new file mode 100644
index 00000000000..1d3a0257121
--- /dev/null
+++ b/apps/shorty/img/usage/network.svg
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/qrcode.svg b/apps/shorty/img/usage/qrcode.svg
new file mode 100644
index 00000000000..83802042f7a
--- /dev/null
+++ b/apps/shorty/img/usage/qrcode.svg
@@ -0,0 +1,271 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/sms.svg b/apps/shorty/img/usage/sms.svg
new file mode 100644
index 00000000000..5d519df9570
--- /dev/null
+++ b/apps/shorty/img/usage/sms.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/team.svg b/apps/shorty/img/usage/team.svg
new file mode 100644
index 00000000000..c2d3c0075d2
--- /dev/null
+++ b/apps/shorty/img/usage/team.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/unlocked.svg b/apps/shorty/img/usage/unlocked.svg
new file mode 100644
index 00000000000..b993bd1458b
--- /dev/null
+++ b/apps/shorty/img/usage/unlocked.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/img/usage/workstation.svg b/apps/shorty/img/usage/workstation.svg
new file mode 100644
index 00000000000..104d98f8552
--- /dev/null
+++ b/apps/shorty/img/usage/workstation.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/index.php b/apps/shorty/index.php
new file mode 100644
index 00000000000..8c729bae149
--- /dev/null
+++ b/apps/shorty/index.php
@@ -0,0 +1,149 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file index.php
+ * This is the plugins central position
+ * All requests to the plugin are handled by this file.
+ * Exceptions: system settings, user preferences and relaying
+ * @access public
+ * @author Christian Reiner
+ */
+
+OCP\App::setActiveNavigationEntry ( 'shorty_index' );
+
+OCP\Util::addStyle ( 'shorty', 'shorty' );
+
+OCP\Util::addScript ( 'shorty/3rdparty','jquery.tinysort.min' );
+OCP\Util::addScript ( 'shorty', 'shorty' );
+OCP\Util::addScript ( 'shorty', 'init' );
+if ( OC_Log::DEBUG==OC_Config::getValue( "loglevel", OC_Log::WARN ) )
+ OCP\Util::addScript ( 'shorty', 'debug' );
+
+// strategy:
+// - first: decide which action is requested
+// - second: execute that action with an optional argument provided
+
+// defaults:
+$act = 'index';
+$arg = NULL;
+// we try to guess what the request indicates:
+// - a (shorty) id to be looked up in the database resulting in a forwarding to the stored target
+// - a (target) url to be added as a new shorty
+// - none of the two, so just a plain list of existing shortys
+foreach ($_GET as $key=>$val) // in case there are unexpected, additional arguments like a timestamp added by some stupid proxy
+{
+ switch ($key)
+ {
+ // this is the OC4 argument used to identify the app called, we ignore it:
+ case 'app':
+ break;
+ // any recognizable argument key indicating a url to be added as new shorty ?
+ case 'url':
+ case 'uri':
+ case 'target':
+ case 'link':
+ // example: http://.../shorty/index.php?url=http%...
+ $act = 'acquire';
+ $arg = OC_Shorty_Type::req_argument($key,OC_Shorty_Type::URL,FALSE);
+ break 2; // skip switch AND foreach
+ // no recognizable key but something else, hm...
+ // this _might_ be some unexcepted argument, or:
+ // it is an expected argument, but without recognizable key, so we try to guess by examining the content
+ // we restrict this 'guessing' to cases where only a single argument is specified
+ default:
+ if ( (1==sizeof($_GET)) // only one single request argument
+ &&( ! reset($_GET)) ) // no value, so maybe just an id
+ {
+ // use that source instead of $key, since $key contains replaced chars (php specific exceptions due to var name problems)
+ $raw = urldecode($_SERVER['QUERY_STRING']);
+ // now try to interpret its content
+ if (NULL!==($value=OC_Shorty_Type::normalize($raw,OC_Shorty_Type::URL,FALSE)))
+ {
+ // the query string is a url, acquire it as a new shorty
+ $act = 'acquire';
+ $arg = $raw;
+ break 2;
+ }
+ else
+ {
+ // no pattern recognized, so we assume an ordinary index action
+ $act = 'index';
+ break 2;
+ }
+ } // if
+ $act='index';
+ break 2;
+ } // switch key
+} // foreach key
+
+// next, execute the "act" whilst considering the 'arg'
+switch ($act)
+{
+ case 'acquire': // add url as new shorty
+ // keep the url specified as referer, that is the one we want to store
+ $_SESSION['shorty-referrer'] = $arg;
+ OCP\Util::writeLog( 'shorty', sprintf("Detected an incoming Shortlet request for url '%s...'",substr($arg,0,80)), OC_Log::INFO );
+ header ( sprintf('Location: %s', OCP\Util::linkTo('shorty','index.php')) );
+ exit();
+ // =====
+ case 'index': // action 'index': list of shortys
+ default:
+ try
+ {
+ // is this a redirect from a call with a target url to be added ?
+ if ( isset($_SESSION['shorty-referrer']) )
+ {
+ // this takes care of handling the url on the client side
+ OCP\Util::addScript ( 'shorty', 'add' );
+ // add url taked from the session vars to anything contained in the query string
+ $_SERVER['QUERY_STRING'] = implode('&',array_merge(array('url'=>$_SESSION['shorty-referrer']),explode('&',$_SERVER['QUERY_STRING'])));
+ }
+ else
+ {
+ // simple desktop initialization, no special actions contained
+ OCP\Util::addScript ( 'shorty', 'list' );
+ }
+ $tmpl = new OCP\Template( 'shorty', 'tmpl_index', 'user' );
+ // the (remote) base url of the qrcode generator
+ $tmpl->assign ( 'qrcode-url', sprintf('%s?service=%s&url=',OCP\Util::linkToAbsolute("", "public.php"),'shorty_qrcode') );
+ // available status (required for select filter in toolbox)
+ $shorty_status['']=sprintf('- %s -',OC_Shorty_L10n::t('all'));
+ foreach ( OC_Shorty_Type::$STATUS as $status )
+ $shorty_status[$status] = OC_Shorty_L10n::t($status);
+ $tmpl->assign ( 'shorty-status', $shorty_status );
+ // any referrer we want to hand over to the browser ?
+ if ( array_key_exists('shorty-referrer',$_SESSION) )
+ $tmpl->assign ( 'shorty-referrer', $_SESSION['shorty-referrer'] );
+ // is sending sms enabled in the personal preferences ?
+ $tmpl->assign ( 'sms-control', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','sms-control','disabled') );
+ // clean up session var so that a browser reload does not trigger the same action again
+ unset ( $_SESSION['shorty-referrer'] );
+ $tmpl->printPage();
+ } catch ( OC_Shorty_Exception $e ) { OCP\JSON::error ( array ( 'message'=>$e->getTranslation(), 'data'=>$result ) ); }
+} // switch
+
+?>
diff --git a/apps/shorty/js/add.js b/apps/shorty/js/add.js
new file mode 100644
index 00000000000..7b120d888ce
--- /dev/null
+++ b/apps/shorty/js/add.js
@@ -0,0 +1,51 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file js/add.js
+ * @brief Client side desktop initialization in case of a call with an url to add
+ * @author Christian Reiner
+ */
+
+$(document).ready(function(){
+ // initialize desktop
+ var dialog = $('#dialog-add');
+ $.when(
+ Shorty.WUI.Controls.init()
+ ).pipe(function(){
+ Shorty.WUI.List.build();
+ }).done(function(){
+ $.when(
+ Shorty.WUI.Dialog.toggle(dialog)
+ ).done(function(){
+ // any referrer handed over from php (explicitly in markup) ?
+ var target=$('#controls').attr('data-referrer');
+ $('#controls').removeAttr('data-referrer');
+ dialog.find('#target').val(target);
+ dialog.find('#title').focus();
+ Shorty.WUI.Meta.collect(dialog);
+ })
+ })
+}); // document.ready
diff --git a/apps/shorty/js/debug.js b/apps/shorty/js/debug.js
new file mode 100644
index 00000000000..a45819152ee
--- /dev/null
+++ b/apps/shorty/js/debug.js
@@ -0,0 +1,71 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 wpayloadanty 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 .
+*
+*/
+
+/**
+ * @file js/debug.js
+ * @brief Client side debugging methods
+ * The inclusion of these definitions trigger additional debug outputs
+ * @author Christian Reiner
+ */
+
+$(document).ready(function(){
+ $.extend(Shorty,{
+ Debug:{
+ log: function(issue){
+ switch (typeof(issue)){
+ case 'string':
+ console.log(issue);
+ break;
+ default:
+ console.log(this.dump(issue));
+ } // switch
+ }, // Shorty.Debuglog()
+
+ dump: function(payload,level){
+ var dumped_text = "";
+ if(!level) level = 0;
+ // some padding given at the beginning of the line
+ var level_padding = "";
+ for(var j=0;j \"" + value + "\"\n";
+ }
+ }
+ } else { //Stings/Chars/Numbers etc.
+ dumped_text = "==>"+payload+"<==("+typeof(payload)+")";
+ }
+ return dumped_text;
+ } // Shorty.Debug.dump()
+ } // Shorty.Debug
+ });
+});
diff --git a/apps/shorty/js/init.js b/apps/shorty/js/init.js
new file mode 100644
index 00000000000..3ab9fa7091a
--- /dev/null
+++ b/apps/shorty/js/init.js
@@ -0,0 +1,92 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file js/init.js
+ * @brief Client side initialization of desktop actionss
+ * @author Christian Reiner
+ */
+
+$(document).ready(function(){
+ // make notification closeable
+ $('#content #notification').bind('click',Shorty.WUI.Notification.hide);
+ // button to open the 'add' dialog
+ $('#controls #add').bind('click',function(){Shorty.WUI.Dialog.toggle($('#dialog-add'))});
+ // close button in dialogs
+ $('.shorty-dialog #close').bind('click',function(){Shorty.WUI.Dialog.hide($(this).parents('form').eq(0));});
+ // status selection in embedded share dialog
+ $('.shorty-embedded#dialog-share #status').bind('change',function(){
+ Shorty.Action.Url.status($(this).siblings('#id').val(),$(this).val());
+ });
+ // button to open the tools header row in the list
+ $('#list #titlebar').bind('click',Shorty.WUI.List.Toolbar.toggle);
+ // button to reload the list
+ $('#list #toolbar').find('#reload').bind('click',Shorty.WUI.List.build);
+ // sort buttons
+ $('#list #toolbar').find('shorty-sorter').bind('click',Shorty.WUI.List.sort);
+ // add date picker options
+ $.datepicker.setDefaults({
+ dateFormat :'yy-mm-dd',
+ appendText: "(yyyy-mm-dd)",
+ changeMonth: true,
+ changeYear: true,
+ showOtherMonths: true,
+ selectOtherMonths: true,
+ showOn: 'button',
+ buttonImage: $('#controls').find('#until').eq(0).attr('icon'),
+ buttonImageOnly: true
+ });
+ $('#controls #until:not([readonly])').datepicker();
+ // bind usage to the usage icons
+ $('#dialog-share img.shorty-usage:not(.disabled)').live('click',function(e){Shorty.WUI.Entry.send(e,$(this));});
+ // bind actions to the actions icons
+ $('#list tbody .shorty-actions a').live('click',function(e){Shorty.WUI.Entry.click(e,$(this));});
+ // bind highlighting to clicks on a row, except for the action icons
+ $('#list tbody tr td:not(#actions)').live('click',function(){
+ Shorty.WUI.List.highlight($(this).parents('tr'));
+ Shorty.WUI.Dialog.hide($('.shorty-embedded').eq(0));
+ });
+ // pretty select boxes where applicable (class controlled)
+ $('.chosen').chosen();
+ // title & target filter reaction
+ $('#list thead tr#toolbar').find('th#target,th#title').find('#filter').bind('keyup',function(){
+ Shorty.WUI.List.filter(
+ $($(this).context.parentElement.parentElement).attr('id'),
+ $(this).val()
+ );
+ });
+ // status filter reaction
+ $('#list thead tr#toolbar th#status select').change(function(){
+ Shorty.WUI.List.filter(
+ $(this).parents('th').attr('id'),
+ $(this).find(':selected').val()
+ );
+ });
+ // column sorting reaction
+ $('#list thead tr#toolbar div img.shorty-sorter').bind('click',function(){
+ Shorty.WUI.List.sort($(this).attr('data-sort-code'));
+ });
+}); // document.ready
+
diff --git a/apps/shorty/js/list.js b/apps/shorty/js/list.js
new file mode 100644
index 00000000000..89f319e2e33
--- /dev/null
+++ b/apps/shorty/js/list.js
@@ -0,0 +1,39 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file js/list.js
+ * @brief Client side desktop initialization for normal calls of the plugin
+ * @author Christian Reiner
+ */
+
+$(document).ready(function(){
+ // initialize desktop
+ $.when(
+ Shorty.WUI.Controls.init()
+ ).then(function(){
+ Shorty.WUI.List.build();
+ });
+}); // document.ready
diff --git a/apps/shorty/js/preferences.js b/apps/shorty/js/preferences.js
new file mode 100644
index 00000000000..4cda3ab37ac
--- /dev/null
+++ b/apps/shorty/js/preferences.js
@@ -0,0 +1,69 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file js/preferences.js
+ * @brief Client side activity initialization for the user preferences dialog
+ * @author Christian Reiner
+ */
+
+$(document).ready(function(){
+ // backend preferences, activate hints for currently selected backend
+ var type=$('#shorty #backend-type').val()||'';
+ if (type.length){
+ $('#shorty #backend-'+type).show();
+ }
+ // backend 'static': initialize example that depends on backend-base system setting
+ if ($('#shorty #backend-static #backend-static-base').val().length)
+ $('#shorty #backend-static #example').text($('#shorty #backend-static #backend-static-base').val()+'');
+ // backend 'static': offer a clickable example link to verify the correct setup
+ $('#shorty #backend-static #example').bind('click',function(event){
+ event.preventDefault();
+ Shorty.Action.Setting.verify();
+ });
+ // react with a matching explanation and example url when backend type is chosen
+ $('.chosen').chosen();
+ $('#shorty #backend-type').change(
+ function(){
+ var type=$('#shorty #backend-type').val();
+ $('#shorty .backend-supplement').hide();
+ if (type.length){
+ $('#shorty .backend-supplement').filter('#backend-'+type).fadeIn('slow');
+ // save preference
+ Shorty.Action.Preference.set($('#shorty #backend-type').serialize());
+ return false;
+ }
+ }
+ );
+ // safe preferences
+ $('#shorty .backend-supplement').focusout(function(){
+ // save preference
+ Shorty.Action.Preference.set($(this).find('input').serialize());
+ });
+ // safe sms-control
+ $('#shorty #sms-control').change(function(){
+ Shorty.Action.Preference.set($('#shorty #sms-control').serialize());
+ });
+});
diff --git a/apps/shorty/js/settings.js b/apps/shorty/js/settings.js
new file mode 100644
index 00000000000..254ab4f0c13
--- /dev/null
+++ b/apps/shorty/js/settings.js
@@ -0,0 +1,53 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file js/settings.js
+ * @brief Client side activity initialization for the system settings dialog
+ * @author Christian Reiner
+ */
+
+$(document).ready(function(){
+ // initialize example that depends on backend-base
+ if ($('#shorty #backend-static-base').val().length)
+ $('#shorty #backend-static #example').text($('#shorty #backend-static-base').val()+'');
+ // modify example upon input of a base
+ $('#shorty #backend-static-base').bind('input',function(){
+ $('#shorty #backend-static #example').text($('#shorty #backend-static-base').val()+'');
+ });
+ // backend 'static': offer a clickable example link to verify the correct setup
+ $('#shorty #backend-static #example').bind('click',function(event){
+ event.preventDefault();
+ Shorty.Action.Setting.verify();
+ });
+ // store setting
+ $('#shorty #backend-static-base').focusout(function(){
+ // modify example
+ $('#shorty #backend-static #example').text($('#shorty #backend-static-base').val()+'');
+ // save setting
+ Shorty.Action.Setting.set($('#shorty #backend-static-base').serialize());
+ return false;
+ });
+});
diff --git a/apps/shorty/js/shorty.js b/apps/shorty/js/shorty.js
new file mode 100644
index 00000000000..688c3e6e158
--- /dev/null
+++ b/apps/shorty/js/shorty.js
@@ -0,0 +1,1489 @@
+/**
+* @package shorty an ownCloud url shortener plugin
+* @category internet
+* @author Christian Reiner
+* @copyright 2011-2012 Christian Reiner
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file js/shorty.js
+ * @brief Client side activity library
+ * @author Christian Reiner
+ */
+
+// max()-selector
+// usage: var maxWidth = $("a").max(function() {return $(this).width(); });
+$.fn.max = function(selector) {
+ return Math.max.apply(null, this.map(function(index, el) { return selector.apply(el); }).get() );
+}
+// min()-selector
+// usage: var minWidth = $("a").min(function() {return $(this).width(); });
+$.fn.min = function(selector) {
+ return Math.min.apply(null, this.map(function(index, el) { return selector.apply(el); }).get() );
+}
+
+/**
+ * @class Shorty
+ * @brief Central activity library for the client side
+ * @author Christian Reiner
+ */
+Shorty =
+{
+ // ===== Shorty.WUI =====
+ WUI:
+ {
+ // ===== Shorty.WUI.Controls =====
+ Controls:
+ {
+ // ===== Shorty.WUI.Controls.init =====
+ init: function(){
+ if (Shorty.Debug) Shorty.Debug.log("init controls");
+ var dfd = new $.Deferred();
+ $.when(
+ Shorty.WUI.Controls.toggle(),
+ Shorty.WUI.Sums.fill()
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Controls.init
+ // ===== Shorty.WUI.Controls.toggle =====
+ toggle: function(){
+ if (Shorty.Debug) Shorty.Debug.log("toggle controls");
+ var dfd = new $.Deferred();
+ Shorty.WUI.Notification.hide();
+ // show or hide dialog
+ var controls = $('#controls');
+ if ( ! controls.is(':visible')){
+ $.when(
+ $.when(
+ controls.slideDown('slow')
+ ).done(Shorty.WUI.Sums.fill)
+ ).done(dfd.resolve)
+ }else{
+ $.when(
+ controls.slideUp('fast')
+ ).done(dfd.resolve)
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.Controls.toggle
+ }, // Shorty.WUI.Controls
+ // ===== Shorty.WUI.Desktop =====
+ Desktop:
+ {
+ // ===== Shorty.WUI.Desktop.show =====
+ show: function(duration){
+ if (Shorty.Debug) Shorty.Debug.log("show desktop");
+ duration = duration || 'slow';
+ var dfd = new $.Deferred();
+ $.when($('#desktop').fadeTo(duration,1.0)).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Desktop.show
+ // ===== Shorty.WUI.Desktop.hide =====
+ hide: function(duration){
+ if (Shorty.Debug) Shorty.Debug.log("hide desktop");
+ duration = duration || 'slow';
+ var dfd = new $.Deferred();
+ $.when($('#desktop').fadeTo(duration,0.3)).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Desktop.hide
+ }, // Shorty.WUI.Desktop
+ // ===== Shorty.WUI.Dialog =====
+ Dialog:
+ {
+ // ===== Shorty.WUI.Dialog.execute =====
+ execute: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("execute dialog "+dialog.attr('id'));
+ var dfd = new $.Deferred();
+ switch ( dialog.attr('id') ){
+ case 'dialog-add':
+ $.when(
+ Shorty.WUI.Notification.hide(),
+ Shorty.Action.Url.add()
+ ).done(dfd.resolve)
+ break;
+ case 'dialog-edit':
+ $.when(
+ Shorty.WUI.Notification.hide(),
+ Shorty.Action.Url.edit()
+ ).done(dfd.resolve)
+ break;
+ case 'dialog-del':
+ $.when(
+ Shorty.WUI.Notification.hide(),
+ Shorty.Action.Url.del()
+ ).done(dfd.resolve)
+ break;
+ default:
+ dfd.resolve();
+ } // switch
+ return dfd.promise();
+ }, // Shorty.WUI.Dialog.execute
+ // ===== Shorty.WUI.Dialog.hide =====
+ hide: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("hide dialog "+dialog.attr('id'));
+ var duration = 'slow';
+ var dfd = new $.Deferred();
+ if (!dialog.is(':visible'))
+ dfd.resolve();
+ else{
+ $.when(
+ dialog.slideUp(duration)
+ ).pipe(function(){
+ switch ( dialog.attr('id') ){
+ case 'dialog-add':
+ dialog.find('#confirm').unbind('click');
+ dialog.find('#target').unbind('focusout');
+ break;
+ default:
+ } // switch
+ }).pipe(function(){
+ if (dialog.hasClass('shorty-standalone'))
+ Shorty.WUI.Desktop.show();
+ }).done(dfd.resolve)
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.Dialog.hide
+ // ===== Shorty.WUI.Dialog.reset =====
+ reset: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("reset dialog "+dialog.attr('id'));
+ var dfd = new $.Deferred();
+ if (dialog){
+ // reset dialog fields
+ $.when(
+ $.each(dialog.find('#status'),function(){
+ if($(this).is('[data]'))
+ $(this).val($(this).attr('data'));
+ else $(this).val('');
+ }),
+ $.each(dialog.find('input,textarea'),function(){
+ if($(this).is('[data]'))
+ $(this).val($(this).attr('data')).attr('placeholder',$(this).attr('data'));
+ else $(this).val('').attr('placeholder','');
+ }),
+ $.each(dialog.find('.shorty-value'),function(){
+ if($(this).is('[data]'))
+ $(this).text($(this).attr('data'));
+ else $(this).text('');
+ }),
+ $.each(dialog.find('.shorty-icon'), function(){
+ if($(this).is('[data]'))
+ $(this).attr('src',$(this).attr('data'));
+ else $(this).attr('src','');
+ }),
+ Shorty.WUI.Dialog.sharpen(dialog,false)
+ ).done(dfd.resolve)
+ }
+ else
+ dfd.resolve();
+ return dfd.promise();
+ }, // Shorty.WUI.Dialog.reset
+ // ===== Shorty.WUI.Dialog.sharpen =====
+ sharpen: function(dialog,sharpness){
+ if (Shorty.Debug) Shorty.Debug.log("toggle sharpness of dialog '"+dialog.attr('id')+"' to "+sharpness);
+ var confirm=dialog.find('#confirm');
+ if (sharpness){
+ $('#dialog-add #busy').fadeOut('slow');
+ confirm.unbind('click');
+ confirm.bind('click',{dialog: dialog}, function(event){event.preventDefault();Shorty.WUI.Dialog.execute(event.data.dialog);});
+ confirm.addClass('sharp');
+ }else{
+ confirm.unbind('click');
+ confirm.bind('click',function(event){event.preventDefault();dialog.find('#target').effect('highlight',{'color':'#CCCCCC'},500);});
+ confirm.removeClass('sharp');
+ }
+ }, // Shorty.WUI.Dialog.sharpen
+ // ===== Shorty.WUI.Dialog.show =====
+ show: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("show dialog "+dialog.attr('id'));
+ var duration = 'slow';
+ var dfd = new $.Deferred();
+ if (dialog.is(':visible'))
+ // dialog already open, nothing to do...
+ dfd.resolve();
+ else{
+ $('#content form.shorty-dialog').each(function(){
+ Shorty.WUI.Dialog.hide($(this));
+ });
+ // hide 'old' notifications
+ Shorty.WUI.Notification.hide(),
+ // some preparations
+ $.when(
+ function(){
+ var dfd = new $.Deferred();
+ if (dialog.hasClass('shorty-standalone'))
+ $.when(Shorty.WUI.Desktop.hide()).done(dfd.resolve)
+ else dfd.resolve();
+ return dfd.promise();
+ }()
+ ).pipe(function(){
+ // prevent submission before entering anything
+ Shorty.WUI.Dialog.sharpen(dialog,false);
+ // show dialog
+ dialog.slideDown(duration);
+ }).pipe(function(){
+ // initialize dialog actions
+ switch(dialog.attr('id')){
+ case 'dialog-add':
+ dialog.find('#target').focus();
+ dialog.find('#target').bind('focusout', {dialog: dialog}, function(event){Shorty.WUI.Meta.collect(event.data.dialog);});
+ break;
+ case 'dialog-edit':
+ dialog.find('#title').focus();
+ Shorty.WUI.Dialog.sharpen(dialog,true);
+ break;
+ } // switch
+ }).done(dfd.resolve)
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.Dialog.show
+ // ===== Shorty.WUI.Dialog.toggle =====
+ toggle: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("toggle dialog "+dialog.attr('id'));
+ var dfd = new $.Deferred();
+ Shorty.WUI.Notification.hide();
+ // show or hide dialog
+ if ( ! dialog.is(':visible'))
+ $.when(Shorty.WUI.Dialog.show(dialog)).done(dfd.resolve)
+ else
+ $.when(Shorty.WUI.Dialog.hide(dialog)).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Dialog.toggle
+ // ===== Shorty.WUI.Dialog.wipe =====
+ wipe: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("wipe dialog "+dialog.attr('id'));
+ var dfd = new $.Deferred();
+ if (dialog){
+ // wipe dialog fields
+ $.when(
+ $.each(dialog.find('#status'), function(){$(this).attr('data','blocked');$(this).val('blocked');}),
+ $.each(dialog.find('input'), function(){$(this).attr('data','');$(this).val('');}),
+ $.each(dialog.find('textarea'), function(){$(this).attr('data','');$(this).val('');}),
+ $.each(dialog.find('.shorty-value'),function(){$(this).attr('data','');$(this).text('');}),
+ $.each(dialog.find('.shorty-icon'), function(){$(this).attr('data','');$(this).attr('src','');}),
+ Shorty.WUI.Dialog.sharpen(dialog,false)
+ ).done(dfd.resolve)
+ }
+ else
+ dfd.resolve();
+ return dfd.promise();
+ }, // Shorty.WUI.Dialog.wipe
+ }, // Shorty.WUI.Dialog
+ // ===== Shorty.WUI.Entry =====
+ Entry:
+ {
+ // ===== Shorty.WUI.Entry.click =====
+ click: function(event,element){
+ var dfd = new $.Deferred();
+ var entry=element.parents('tr').eq(0);
+ if (Shorty.Debug) Shorty.Debug.log(event.type+" on action "+element.attr('id')+" for entry "+entry.attr('id'));
+ //
+ if ($('.shorty-dialog').is(':visible'))
+ $('.shorty-dialog').each(function(){Shorty.WUI.Dialog.hide($(this));});
+ else{
+ // highlight clicked row as active entry
+ $.when(
+ Shorty.WUI.List.highlight(entry)
+ ).pipe(function(){
+ if ('click'==event.type){
+ switch(element.attr('id')){
+ case 'del': Shorty.WUI.Entry.del(entry); break;
+ case 'edit': Shorty.WUI.Entry.edit(entry); break;
+ case 'open': Shorty.Action.Url.forward(entry); break;
+ case 'share': Shorty.WUI.Entry.share(entry); break;
+ case 'show': Shorty.WUI.Entry.show(entry); break;
+ } // switch
+ } // if click
+ }).done(dfd.resolve)
+ } // else
+ return dfd.promise();
+ }, // Shorty.WUI.Entry.click
+ // ===== Shorty.WUI.Entry.del =====
+ del: function(entry){
+ if (Shorty.Debug) Shorty.Debug.log("delete entry "+entry.attr('id'));
+ if (entry.hasClass('deleted')){
+ // change status to deleted
+ Shorty.Action.Url.status(entry.attr('data-id'),'blocked');
+ // mark row as undeleted
+ entry.removeClass('deleted');
+ }else{
+ // change status to deleted
+ Shorty.Action.Url.status(entry.attr('data-id'),'deleted');
+ // mark row as deleted
+ entry.addClass('deleted');
+ }
+ }, // Shorty.WUI.Entry.del
+ // ===== Shorty.WUI.Entry.edit =====
+ edit: function(entry){
+ if (Shorty.Debug) Shorty.Debug.log("modify entry "+entry.attr('id'));
+ var dfd = new $.Deferred();
+ // use the existing edit dialog for this
+ var dialog=$('#controls #dialog-edit');
+ // load entry into dialog
+ dialog.find('#id').val(entry.attr('data-id'));
+ dialog.find('#status').val(entry.attr('data-status')||'');
+ dialog.find('#source').val(entry.attr('data-source'||''));
+ dialog.find('#target').val(entry.attr('data-target'||''));
+ dialog.find('#title').val(entry.attr('data-title')||'');
+ dialog.find('#clicks').val(entry.attr('data-clicks')||'');
+ dialog.find('#created').val(entry.attr('data-created')||'');
+ dialog.find('#accessed').val(entry.attr('data-accessed')||'');
+ dialog.find('#notes').val(entry.attr('data-notes')||'');
+// dialog.find('#until').datepicker('setDate',new Date(entry.attr('data-until'))||'');
+ dialog.find('#until').datepicker('setDate',new Date(entry.attr('data-until'))||'')
+ .datepicker('refresh');
+ // open edit dialog
+ Shorty.WUI.Dialog.show(dialog)
+ $.when(
+ Shorty.WUI.Meta.get(entry.attr('data-target'))
+ ).pipe(function(response){
+ var meta=response.data;
+ if (meta.final)
+ dialog.find('#target').val(meta.final);
+ dialog.find('#title').attr('placeholder',meta.title);
+ dialog.find('#meta').fadeTo('fast',0,function(){
+ Shorty.WUI.Meta.reset(dialog);
+ // specify the icons and information to be shown as meta data
+ dialog.find('#staticon').attr('src',meta.staticon);
+ dialog.find('#schemicon').attr('src',meta.schemicon);
+ dialog.find('#favicon').attr('src',meta.favicon);
+ dialog.find('#mimicon').attr('src',meta.mimicon);
+ dialog.find('#explanation').html(meta.title?meta.title:'[ '+meta.explanation+' ]');
+ dialog.find('#meta').fadeTo('fast',1);
+ })
+ }).done(function(){
+ Shorty.WUI.Dialog.sharpen(dialog,true);
+ dfd.resolve();
+ }).fail(function(){
+ Shorty.WUI.Dialog.sharpen(dialog,false);
+ dfd.reject();
+ })
+ return dfd.promise();
+ }, // Shorty.WUI.Entry.edit
+ // ===== Shorty.WUI.Entry.send =====
+ send: function(event,element){
+ var dfd = new $.Deferred();
+ var action=element.attr('id');
+ var entry=element.parents('tr');
+ if (Shorty.Debug) Shorty.Debug.log("send action "+action+" on entry "+entry.attr('data-id'));
+ // take action
+ $.when(
+ Shorty.Action.Url.send(action,entry)
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Entry.send
+ // ===== Shorty.WUI.Entry.share =====
+ share: function(entry){
+ if (Shorty.Debug) Shorty.Debug.log("share entry "+entry.attr('id'));
+ var dfd = new $.Deferred();
+ // use the existing 'share' dialog for this
+ var dialog=$('#dialog-share');
+ // fill and show dialog
+ dialog.find('#id').val(entry.attr('data-id'))
+ .attr('data',entry.attr('data-id'));
+ dialog.find('#source').attr('href',entry.attr('data-source'))
+ .text(entry.attr('data-source'));
+ dialog.find('#relay').attr('href',entry.attr('data-relay'))
+ .text(entry.attr('data-relay'));
+ dialog.find('#target').attr('href',entry.attr('data-target'))
+ .text(entry.attr('data-target'));
+ dialog.find('#status').attr('value',entry.attr('data-status'))
+ .attr('data',entry.attr('data-status'));
+ // move 'share' dialog towards entry
+ dialog.appendTo(entry.find('td#actions')),
+ // open dialog
+ $.when(
+ Shorty.WUI.Dialog.show(dialog)
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Entry.share
+ // ===== Shorty.WUI.Entry.show =====
+ show: function(entry){
+ if (Shorty.Debug) Shorty.Debug.log("show entry "+entry.attr('id'));
+ var dfd = new $.Deferred();
+ // use the existing edit dialog for this
+ var dialog=$('#controls #dialog-show');
+ // load entry into dialog
+ dialog.find('#id').attr('data-id',entry.attr('data-id')).val(entry.attr('data-id'));
+ dialog.find('#status').attr('data-status',entry.attr('data-status')||'').val(t('shorty',entry.attr('data-status'))||'');
+ dialog.find('#source').attr('data-source',entry.attr('data-source')).val(entry.attr('data-source'));
+ dialog.find('#target').attr('data-target',entry.attr('data-target')).val(entry.attr('data-target'));
+ dialog.find('#title').attr('data-title',entry.attr('data-title')).val(entry.attr('data-title'));
+ dialog.find('#until').attr('data-until',entry.attr('data-until')||'').val(entry.attr('data-until')||'');
+ dialog.find('#clicks').attr('data-clicks',entry.attr('data-clicks')||'').val(entry.attr('data-clicks')||'');
+ dialog.find('#created').attr('data-created',entry.attr('data-created')||'').val(entry.attr('data-created')||'');
+ dialog.find('#accessed').attr('data-accessed',entry.attr('data-accessed')||'').val(entry.attr('data-accessed')||'');
+ dialog.find('#notes').attr('data-notes',entry.attr('data-notes')).val(entry.attr('data-notes'));
+ // open edit dialog
+ Shorty.WUI.Dialog.show(dialog)
+ $.when(
+ Shorty.WUI.Meta.get(entry.attr('data-target'))
+ ).pipe(function(response){
+ var meta=response.data;
+ if (meta.final)
+ dialog.find('#target').val(meta.final);
+ dialog.find('#title').attr('placeholder',meta.title);
+ dialog.find('#meta').fadeTo('fast',0,function(){
+ Shorty.WUI.Meta.reset(dialog);
+ // specify the icons and information to be shown as meta data
+ dialog.find('#staticon').attr('src',meta.staticon);
+ dialog.find('#schemicon').attr('src',meta.schemicon);
+ dialog.find('#favicon').attr('src',meta.favicon);
+ dialog.find('#mimicon').attr('src',meta.mimicon);
+ dialog.find('#explanation').html(meta.title?meta.title:'[ '+meta.explanation+' ]');
+ dialog.find('#meta').fadeTo('fast',1);
+ })
+ }).done(function(){
+ dfd.resolve();
+ }).fail(function(){
+ dfd.reject();
+ })
+ return dfd.promise();
+ } // Shorty.WUI.Entry.show
+ }, // Shorty.WUI.Entry
+ // ===== Shorty.WUI.Hourglass =====
+ // Shorty.WUI.Hourglass
+ Hourglass:
+ {
+ // ===== Shorty.WUI.Hourglass.toggle =====
+ toggle: function(show){
+ if (Shorty.Debug) Shorty.Debug.log("toggle hourglass to "+show?"true":"false");
+ var dfd = new $.Deferred();
+ var hourglass = $('#desktop .shorty-hourglass');
+ if (show){
+ if (hourglass.is(':visible'))
+ dfd.resolve();
+ else
+ $.when(
+ hourglass.fadeIn('fast')
+ ).done(dfd.resolve)
+ }else{
+ if (!hourglass.is(':visible'))
+ dfd.resolve();
+ else
+ $.when(
+ hourglass.fadeOut('slow')
+ ).done(dfd.resolve)
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.Hourglass.toggle
+ }, // Shorty.WUI.Hourglass
+ // ===== Shorty.WUI.List =====
+ List:
+ {
+ // ===== Shorty.WUI.List.add =====
+ add: function(list,hidden){
+ if (Shorty.Debug) Shorty.Debug.log("add entry to list holding "+list.length+" entries");
+ var dfd = new $.Deferred();
+ // insert list elements (sets) one by one
+ var row,set;
+ $.each(list,function(i,set){
+ // clone dummy row from list header: dummy is the last row
+ row = $('#desktop #list thead tr:last-child').eq(0).clone();
+ // set row id to entry id
+ row.attr('id',set.id);
+ // add attributes to row, as data and value
+ $.each(['id','status','title','source','relay','target','clicks','created','accessed','until','notes','favicon'],
+ function(j,aspect){
+ if (hidden)
+ row.addClass('shorty-fresh'); // might lead to a pulsate effect later
+ // we wrap the cells content into a span tag
+ var span=$('');
+ // enhance row with real set values
+ if ('undefined'==set[aspect])
+ row.attr('data-'+this,'');
+ else row.attr('data-'+this,set[aspect]);
+ // fill data into corresponsing column
+ var title, content, classes=[];
+ switch(aspect)
+ {
+ case 'favicon':
+ span.html('');
+ break;
+ case 'until':
+ if (null==set[aspect])
+ span.text('-never-');
+ else{
+ span.text(set[aspect]);
+ if (Shorty.Date.expired(set[aspect]))
+ row.addClass('shorty-expired');
+ }
+ break;
+ case 'title':
+ span.text(set[aspect]);
+ span.addClass('ellipsis');
+ break;
+ case 'target':
+ span.text(set[aspect]);
+ span.attr('title',set[aspect]);
+ span.addClass('ellipsis');
+ break;
+ case 'status':
+ if ('deleted'==set[aspect])
+ row.addClass('deleted');
+// span.text(set[aspect]);
+ span.text(t('shorty',set[aspect]));
+ break;
+ default:
+ span.text(set[aspect]);
+ } // switch
+ row.find('td#'+aspect).empty().append(span);
+ }) // each aspect
+ // insert new row in table
+ $('#desktop #list tbody').prepend(row);
+ }) // each
+ return dfd.promise();
+ }, // Shorty.WUI.List.add
+ // ===== Shorty.WUI.List.build =====
+ build: function()
+ {
+ if (Shorty.Debug) Shorty.Debug.log("build list");
+ var dfd = new $.Deferred();
+ // prepare loading
+ $.when(
+ Shorty.WUI.Hourglass.toggle(true),
+ Shorty.WUI.List.dim(false)
+ ).done(function(){
+ // retrieve new entries
+ $.when(
+ Shorty.WUI.List.get()
+ ).pipe(function(response){
+ Shorty.WUI.List.fill(response.data);
+ }).done(function(){
+ $.when(
+ Shorty.WUI.List.show(),
+ Shorty.WUI.List.dim(true)
+ ).always(function(){
+ Shorty.WUI.Hourglass.toggle(false)
+ dfd.resolve();
+ })
+ }).fail(function(){
+ dfd.reject();
+ })
+ })
+ return dfd.promise();
+ }, // Shorty.WUI.List.build
+ // ===== Shorty.WUI.List.dim =====
+ dim: function(show){
+ if (Shorty.Debug) Shorty.Debug.log("dim list to "+(show?"true":"false"));
+ var duration='slow';
+ var dfd =new $.Deferred();
+ var list=$('#desktop #list');
+ var body=list.find('tbody');
+ if (show)
+ {
+ var rows=body.find('tr.shorty-fresh');
+ Shorty.WUI.List.highlight(rows.eq(0));
+ rows.each(function(){
+ $(this).removeClass('shorty-fresh');
+ $(this).find('td').effect('pulsate');
+ });
+ $.when(
+ Shorty.WUI.List.vacuum(),
+ body.fadeIn(duration)
+ ).done(dfd.resolve)
+ }else{
+ if (!body.is(':visible'))
+ dfd.resolve();
+ else
+ {
+ $.when(
+ body.fadeOut(duration)
+ ).done(dfd.resolve)
+ }
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.List.dim
+ // ===== Shorty.WUI.List.empty =====
+ empty: function(){
+ if (Shorty.Debug) Shorty.Debug.log("empty list");
+ var dfd = new $.Deferred();
+ // move embedded dialogs back to their safe place in the controls
+ $('.shorty-embedded').appendTo($('#controls #dialog-show'));
+ // remove all rows, one by one
+ $.when(
+ $('#desktop').find('#list tbody tr').each(function(){
+ if(''!=$(this).attr('id'))
+ $(this).remove();
+ })
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.List.empty
+ // ===== Shorty.WUI.List.fill =====
+ fill: function(list){
+ if (Shorty.Debug) Shorty.Debug.log("fill list");
+ var dfd = new $.Deferred();
+ $.when(
+ Shorty.WUI.Sums.fill(),
+ Shorty.WUI.List.empty(),
+ Shorty.WUI.List.add(list,false)
+ ).pipe(
+ // filter list
+ Shorty.WUI.List.filter('target',$('#list thead tr#toolbar th#target #filter').val()),
+ Shorty.WUI.List.filter('title', $('#list thead tr#toolbar th#title #filter').val()),
+ Shorty.WUI.List.filter('status',$('#list thead tr#toolbar th#status select :selected').val())
+ ).pipe(
+ // sort list
+ $.when(
+ Shorty.Action.Preference.get('list-sort-code')
+ ).done(function(pref){
+ Shorty.WUI.List.sort(pref['list-sort-code']);
+ })
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.List.fill
+ // ===== Shorty.WUI.List.filter =====
+ filter: function(column,pattern){
+ if (Shorty.Debug) Shorty.Debug.log("filter list by column "+column);
+ var dfd = new $.Deferred();
+ $.when(
+ $('#list tbody tr').filter(function(){
+ return (-1==$(this).find('td#'+column+' span').text().toLowerCase().indexOf(pattern.toLowerCase()));
+ }).addClass('shorty-filtered'),
+ $('#list tbody tr').not(function(){
+ return (-1==$(this).find('td#'+column+' span').text().toLowerCase().indexOf(pattern.toLowerCase()));
+ }).removeClass('shorty-filtered')
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.List.filter
+ // ===== Shorty.WUI.List.get =====
+ get: function(){
+ if (Shorty.Debug) Shorty.Debug.log("get list");
+ var dfd = new $.Deferred();
+ $.when(
+ $.ajax({
+ type: 'GET',
+ url: OC.filePath('shorty','ajax','list.php'),
+ cache: false
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ )
+ ).done(function(response){
+ dfd.resolve(response);
+ }).fail(function(response){
+ dfd.reject(response);
+ })
+ return dfd.promise();
+ }, // Shorty.WUI.List.get
+ // ===== Shorty.WUI.List.hide =====
+ hide: function(duration){
+ if (Shorty.Debug) Shorty.Debug.log("hide list");
+ duration = 'slow';
+ var dfd = new $.Deferred();
+ var list = $('#desktop #list');
+ if ( ! list.is(':visible'))
+ dfd.resolve();
+ else
+ {
+ $.when(
+ list.fadeOut(duration)
+ ).done(dfd.resolve)
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.List.hide
+ // ===== Shorty.WUI.List.highlight =====
+ highlight: function(entry){
+ if (Shorty.Debug) Shorty.Debug.log("highlighting list entry "+entry.attr('id'));
+ var dfd = new $.Deferred();
+ // close any open embedded dialog
+ $.when(
+ Shorty.WUI.Dialog.hide($('.shorty-dialog'))
+ ).pipe(function(){
+ // neutralize all rows that might have been highlighted
+ $('#desktop #list tr').removeClass('clicked');
+ entry.addClass('clicked');
+ }).always(dfd.resolve);
+ return dfd.promise();
+ }, // Shorty.WUI.List.highlight
+ // ===== Shorty.WUI.List.modify =====
+ modify: function(list,hidden){
+ if (Shorty.Debug) Shorty.Debug.log("modify entry in list holding "+list.length+" entries");
+ var dfd = new $.Deferred();
+ // modify list elements (sets) one by one
+ var row,set;
+ $.each(list,function(i,set){
+ // select row from list by id
+ row=$('#desktop #list tbody tr#'+set.id);
+ // modify attributes in row, as data and value
+ $.each(['status','title','until','notes'],
+ function(j,aspect){
+ if (set[aspect]){
+ // enhance row with actual set values
+ row.attr('data-'+this,set[aspect]);
+ if (hidden) row.addClass('shorty-fresh');
+ // fill data into corresponsing column
+ var content, classes=[];
+ switch(aspect)
+ {
+ case 'until':
+ if (null==set[aspect])
+ content='-never-';
+ else{
+ content=set[aspect];
+ if (Shorty.Date.expired(set[aspect]))
+ row.addClass('shorty-expired');
+ }
+ break;
+ case 'title':
+ classes.push('ellipsis');
+ content=set[aspect];
+ break;
+ case 'status':
+ if ('deleted'==set[aspect])
+ row.addClass('deleted');
+ content=set[aspect];
+ break;
+ default:
+ content=set[aspect];
+ } // switch
+ // show modified column immediately or keep it for a later pulsation effect ?
+ row.find('td').filter('#'+aspect).html(''+content+'');
+ } // if aspect
+ }) // each aspect
+ }) // each entry
+ return dfd.resolve().promise();
+ }, // Shorty.WUI.List.modify
+ // ===== Shorty.WUI.List.show =====
+ show: function(duration){
+ if (Shorty.Debug) Shorty.Debug.log("show list");
+ duration = 'slow';
+ var dfd = new $.Deferred();
+ var list = $('#desktop #list');
+ if (list.is(':visible'))
+ dfd.resolve();
+ else
+ {
+ // list currently not visible, show it
+ $.when(
+ list.find('tbody').show(),
+ list.fadeIn(duration)
+ ).done(function(){
+ dfd.resolve();
+ Shorty.WUI.List.vacuum();
+ })
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.List.show
+ // ===== Shorty.WUI.List.sort =====
+ sort: function(sortCode){
+ sortCore = sortCode || 'cd';
+ var icon=$('#list thead tr#toolbar th div img[data-sort-code="'+sortCode+'"]');
+ var sortCol=icon.parents('th').attr('id');
+ var sortDir=icon.attr('data-sort-direction');
+ if (Shorty.Debug) Shorty.Debug.log("sorting list column "+sortCol+" "+(sortDir=='asc'?'ascending':'descending'));
+ // use the 'tinysort' jquery plugin for sorting
+ switch (sortCol){
+ case 'until':
+ $('#list tbody>tr').tsort('td#until',{order:sortDir});
+ break;
+ default:
+ $('#list tbody>tr').tsort({attr:'data-'+sortCol,order:sortDir});
+ } // switch
+ // mark currently active sort icon
+ var icons=$('#list thead tr#toolbar img.shorty-sorter');
+ icons.removeClass('shorty-active');
+ icons.filter('[data-sort-code="'+sortCode+'"]').addClass('shorty-active');
+ // store the sorting code as preference, for returning list retrievals
+ Shorty.Action.Preference.set({'list-sort-code':sortCode});
+ }, // Shorty.WUI.List.sort
+ // ===== Shorty.WUI.List.toggle =====
+ toggle: function(duration){
+ if (Shorty.Debug) Shorty.Debug.log("toggle list");
+ duration = 'slow';
+ var dfd = new $.Deferred();
+ if (list.is(':visible'))
+ return Shorty.WUI.List.hide();
+ else
+ return Shorty.WUI.List.show();
+ }, // Shorty.WUI.List.toggle
+ // ===== Shorty.WUI.List.vacuum =====
+ vacuum: function(){
+ if (Shorty.Debug) Shorty.Debug.log("vacuum list");
+ // list is empty if no row exists
+ if (0!=$('#list tbody').find('tr').length)
+ $('#vacuum').fadeOut('fast');
+ else
+ $('#vacuum').fadeIn('slow');
+ }, // Shorty.WUI.List.vacuum
+ // ===== Shorty.WUI.List.Toolbar =====
+ Toolbar:
+ {
+ // ===== Shorty.WUI.List.Toolbar.toggle =====
+ toggle: function(duration){
+ if (Shorty.Debug) Shorty.Debug.log("toggle list toolbar");
+ duration = duration || 'slow';
+ var button=$('#list #tools');
+ var toolbar=$('#list #toolbar');
+ var dfd = new $.Deferred();
+ if (!toolbar.find('div').is(':visible')){
+ // tool NOT visible: open toolbar
+ $.when(
+ toolbar.find('div').slideDown(duration)
+ ).pipe(
+ button.attr('src',button.attr('data-minus'))
+ ).done(dfd.resolve)
+ }else{ // toolbar IS visible
+ // any filters active? prevent closing of toolbar !
+ if ( ( (toolbar.find('th#title,#target').find('div input#filter:[value!=""]').length)
+ &&(toolbar.find('th#title,#target').find('div input#filter:[value!=""]').effect('pulsate')) )
+ ||( (toolbar.find('th#status select :selected').val())
+// &&(toolbar.find('#status div.chzn-container').effect('pulsate')) )
+ &&(toolbar.find('#status').effect('pulsate')) )
+ ) {
+ if (Shorty.Debug) Shorty.Debug.log('active filter prevents closing of toolbar');
+ }else{
+ // close toolbar
+ $.when(
+ toolbar.find('div').slideUp(duration)
+ ).pipe(
+ button.attr('src',button.attr('data-plus'))
+ ).done(dfd.resolve)
+ }
+ }
+ return dfd.promise();
+ }, // Shorty.WUI.List.Toolbar.toggle
+ }, // Shorty.WUI.List.Toolbar
+ }, // Shorty.WUI.List
+ // ===== Shorty.WUI.Notification =====
+ Notification:
+ {
+ // ===== Shorty.WUI.Notification.hide =====
+ hide: function(){
+ if (Shorty.Debug) Shorty.Debug.log("hide notification");
+ var dfd = new $.Deferred();
+ $.when(
+ $('#notification').slideUp('fast')
+ ).pipe(function(){
+ $('#notification').text('');
+ }).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Notification.hide
+ // ===== Shorty.WUI.Notification.show =====
+ show: function(message,level){
+ if (Shorty.Debug) Shorty.Debug.log("show notification with level "+level);
+ level = level || 'info';
+ var dfd = new $.Deferred();
+ var duration = 'slow';
+ var notification = $('#notification');
+ if (message && message.length){
+ $.when(
+ notification.slideUp('fast')
+ ).done(function(){
+ switch(level){
+ case 'debug':
+ // detect debug mode by checking, of function 'debug()' exists
+ if ( Shorty.Debug ){
+ Shorty.Debug.log('Debug: '+message);
+ $.when(
+ notification.attr('title', 'debug message'),
+ notification.text('Debug: '+message),
+ notification.slideDown(duration)
+ ).done(dfd.resolve)
+ }
+ else
+ dfd.resolve();
+ break;
+ case 'error':
+ if (Shorty.Debug)
+ Shorty.Debug.log('Error: '+message);
+ $.when(
+ notification.attr('title', 'error message'),
+ notification.text('Error: ' + message),
+ notification.slideDown(duration)
+ ).done(dfd.resolve)
+ break;
+ default: // 'info'
+ if ( message.length ){
+ if (Shorty.Debug)
+ Shorty.Debug.log('Info: '+message);
+ $.when(
+ notification.text(message),
+ notification.slideDown(duration)
+ ).done(dfd.resolve)
+ }else{
+ $.when(
+ notification.text('')
+ ).done(dfd.resolve)
+ }
+ } // switch
+ })
+ } // if message
+ return dfd.promise();
+ }, // Shorty.WUI.Notification.show
+ }, // Shorty.WUI.Notification
+ // ===== Shorty.WUI.Meta: =====
+ Meta:
+ {
+ // ===== Shorty.WUI.Meta.collect =====
+ collect: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("collect meta data");
+ var dfd = new $.Deferred();
+ var target = $('#dialog-add #target').val().trim();
+ // don't bother getting active on empty input
+ if ( ! target.length ){
+ dialog.find('#target').focus();
+ dfd.resolve();
+ return dfd.promise();
+ }
+ // start expressing activity
+ $('#dialog-add #busy').fadeIn('fast');
+ // fill in fallback protocol scheme 'http' if none is specified
+ var regexp = /^[a-zA-Z0-9]+\:\//;
+ if ( ! regexp.test(target) ){
+ target = 'http://' + target;
+ dialog.find('#target').val(target);
+ }
+ // query meta data from target
+ $.when(
+ Shorty.WUI.Meta.get(target)
+ ).done(function(response){
+ var meta=response.data;
+ if (meta.final)
+ dialog.find('#target').val(meta.final);
+ dialog.find('#title').attr('placeholder',meta.title);
+ dialog.find('#meta').fadeTo('fast',0,function(){
+ Shorty.WUI.Meta.reset(dialog);
+ // specify the icons and information to be shown as meta data
+ dialog.find('#staticon').attr('src',meta.staticon);
+ dialog.find('#schemicon').attr('src',meta.schemicon);
+ dialog.find('#favicon').attr('src',meta.favicon);
+ dialog.find('#mimicon').attr('src',meta.mimicon);
+ dialog.find('#explanation').html(meta.title?meta.title:'[ '+meta.explanation+' ]');
+ dialog.find('#meta').fadeTo('fast',1);
+ Shorty.WUI.Dialog.sharpen(dialog,true);
+ // stop expressing activity
+ $('#dialog-add #busy').fadeOut('slow');
+ });
+ dfd.resolve(response);
+ }).fail(function(reponse){
+ Shorty.WUI.Dialog.sharpen(dialog,false);
+ dfd.reject(response);
+ })
+ return dfd.promise();
+ }, // Shorty.WUI.Meta.collect
+ // ===== Shorty.WUI.Meta.get =====
+ get: function(target){
+ if (Shorty.Debug) Shorty.Debug.log("get meta data for target "+target);
+ var dfd = new $.Deferred();
+ $.ajax({
+ type: 'GET',
+ url: OC.filePath('shorty','ajax','meta.php'),
+ cache: false,
+ data: { target: encodeURIComponent(target) }
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response);},
+ function(response){return Shorty.Ajax.fail(response);}
+ ).done(function(response){
+ dfd.resolve(response);
+ }).fail(function(response){
+ dfd.reject(response);
+ })
+ return dfd.promise();
+ }, // Shorty.WUI.Meta.get
+ // ===== Shorty.WUI.Meta.reset =====
+ reset: function(dialog){
+ if (Shorty.Debug) Shorty.Debug.log("reset meta data");
+ dialog.find('#staticon').attr('src',dialog.find('#staticon').attr('data'));
+ dialog.find('#schemicon').attr('src',dialog.find('#schemicon').attr('data'));
+ dialog.find('#favicon').attr('src',dialog.find('#favicon').attr('data'));
+ dialog.find('#mimicon').attr('src',dialog.find('#mimicon').attr('data'));
+ dialog.find('#explanation').html(dialog.find('#explanation').attr('data'));
+ dialog.find('#meta').fadeTo('fast',1);
+ }, // Shorty.WUI.Meta.reset
+ }, // Shorty.WUI.Meta
+ // ===== Shorty.WUI.Sums =====
+ Sums:
+ {
+ // ===== Shorty.WUI.Sums.fill =====
+ fill: function(){
+ if (Shorty.Debug) Shorty.Debug.log("fill sums");
+ var dfd = new $.Deferred();
+ $.when(
+ // update (set) sum values in the control bar
+ Shorty.WUI.Sums.get(function(data){
+ $('#controls #sum_shortys').text(data.sum_shortys);
+ $('#controls #sum_clicks').text(data.sum_clicks);
+ })
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.WUI.Sums.fill
+ // ===== Shorty.WUI.Sums.get =====
+ get: function(callback){
+ if (Shorty.Debug) Shorty.Debug.log("get sums");
+ var dfd = new $.Deferred();
+ $.when(
+ $.ajax({
+ type: 'GET',
+ url: OC.filePath('shorty','ajax','count.php'),
+ cache: false,
+ data: { }
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ )
+ ).done(function(response){
+ if (callback) callback(response.data);
+ dfd.resolve(response);
+ }).fail(function(response){
+ dfd.reject(response);
+ })
+ return dfd.promise();
+ }, // Shorty.WUI.Sums.get
+ }, // Shorty.WUI.Sums
+ }, // Shorty.WUI
+
+ //==========
+
+ Action:
+ {
+ // ===== Shorty.Action.Preference =====
+ Preference:
+ {
+ // ===== Shorty.Action.Preference.get =====
+ get:function(data){
+ if (Shorty.Debug){Shorty.Debug.log("get preference(s):");Shorty.Debug.log(data);}
+ var dfd = new $.Deferred();
+ $.ajax({
+ type: 'GET',
+ url: OC.filePath('shorty','ajax','preferences.php'),
+ cache: false,
+ data: data
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).always(function(response){
+ if (Shorty.Debug){Shorty.Debug.log("got preference(s):");Shorty.Debug.log(response.data);}
+ }).done(function(response){
+ dfd.resolve(response.data);
+ }).fail(function(response){
+ dfd.reject({});
+ })
+ return dfd.promise();
+ }, // Shorty.Action.Preference.get
+ // ===== Shorty.Action.Preference.set =====
+ set:function(data){
+ if (Shorty.Debug){Shorty.Debug.log("set preference(s):");Shorty.Debug.log(data);}
+ var dfd = new $.Deferred();
+ $.ajax({
+ type: 'POST',
+ url: OC.filePath('shorty','ajax','preferences.php'),
+ cache: false,
+ data: data
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).always(function(response){
+ if (Shorty.Debug){Shorty.Debug.log("got preference(s):");Shorty.Debug.log(response.data);}
+ }).done(function(response){
+ dfd.resolve(response.data);
+ }).fail(function(response){
+ dfd.reject({});
+ })
+ return dfd.promise();
+ }, // Shorty.Action.Preference.set
+ }, // Shorty.Action.Preference
+ // ===== Shorty.Action.Setting =====
+ Setting:
+ {
+ // ===== Shorty.Action.Setting.get =====
+ get:function(data){
+ if (Shorty.Debug){Shorty.Debug.log("get setting(s):");Shorty.Debug.log(data);}
+ var dfd = new $.Deferred();
+ $.ajax({
+ type: 'GET',
+ url: OC.filePath('shorty','ajax','settings.php'),
+ cache: false,
+ data: data
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).always(function(response){
+ if (Shorty.Debug){Shorty.Debug.log("got preference(s):");Shorty.Debug.log(response.data);}
+ }).done(function(response){
+ dfd.resolve(response.data);
+ }).fail(function(response){
+ dfd.reject({});
+ })
+ return dfd.promise();
+ }, // Shorty.Action.Setting.get
+ // ===== Shorty.Action.Setting.set =====
+ set:function(data){
+ if (Shorty.Debug){Shorty.Debug.log("set setting(s):");Shorty.Debug.log(data);}
+ var dfd = new $.Deferred();
+ $.ajax({
+ type: 'POST',
+ url: OC.filePath('shorty','ajax','settings.php'),
+ cache: false,
+ data: data
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).always(function(response){
+ if (Shorty.Debug){Shorty.Debug.log("got preference(s):");Shorty.Debug.log(response.data);}
+ }).done(function(response){
+ dfd.resolve(response.data);
+ }).fail(function(response){
+ dfd.reject({});
+ })
+ return dfd.promise();
+ }, // Shorty.Action.Setting.set
+ // ===== Shorty.Action.Setting.popup =====
+ popup:{},
+ // ===== Shorty.Action.Setting.verify =====
+ verify:function(){
+ if (!Shorty.Action.Setting.popup.dialog){
+ Shorty.Action.Setting.popup=$('#shorty #dialog-verification');
+ Shorty.Action.Setting.popup.dialog({show:'fade',autoOpen:false,modal:true});
+ Shorty.Action.Setting.popup.dialog('option','minHeight',240 );
+ }
+ var dfd = new $.Deferred();
+ $.when(
+ this.check(Shorty.Action.Setting.popup,
+ $('#shorty #backend-static #backend-static-base').val())
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // Shorty.Action.Setting.verify
+ // ===== Shorty.Action.Setting.check =====
+ check:function(popup,target){
+ popup.find('#verification-target').text(target);
+ popup.dialog('open');
+ popup.find('#success').hide();
+ popup.find('#failure').hide();
+ popup.find('#hourglass').fadeIn('fast');
+ var dfd = new $.Deferred();
+ $.ajax({
+ // the '0000000000' below is a special id recognized for testing purposes
+ url: target+'0000000000',
+ cache: false,
+ data: { },
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).done(function(response){
+ $.when(
+ popup.find('#hourglass').fadeOut('fast')
+ ).then(function(){
+ popup.find('#success').fadeIn('fast');
+ dfd.resolve(response);
+ })
+ }).fail(function(response){
+ $.when(
+ popup.find('#hourglass').fadeOut('fast')
+ ).then(function(){
+ popup.find('#failure').fadeIn('fast');
+ dfd.reject(response);
+ })
+ })
+ return dfd.promise();
+ } // Shorty.Action.Setting.check
+ }, // Shorty.Action.Setting
+ // ===== Shorty.Action.Url =====
+ Url:
+ {
+ // ===== Shorty.Action.Url.add =====
+ add:function(){
+ if (Shorty.Debug) Shorty.Debug.log("action add url");
+ var dfd=new $.Deferred();
+ var dialog=$('#dialog-add');
+ var status=dialog.find('#status').val()||'public';
+ var target=dialog.find('#target').val()||'';
+ var title =dialog.find('#title').val()||'';
+ var notes =dialog.find('#notes').val()||'';
+ var until =dialog.find('#until').val()||'';
+ // store favicon from meta data, except it is the internal default blank
+ var favicon = dialog.find('#meta #favicon').attr('src');
+ favicon=(favicon==dialog.find('#meta #favicon').attr('data'))?'':favicon;
+ // perform upload of new shorty
+ $.when(
+ Shorty.WUI.Notification.hide(),
+ // close and neutralize dialog
+ Shorty.WUI.Dialog.hide(dialog),
+ Shorty.WUI.List.dim(false),
+ Shorty.WUI.List.show()
+ ).done(function(){
+ var data={status: status,
+ target: target,
+ title: title,
+ notes: notes,
+ until: until,
+ favicon: favicon};
+ if (Shorty.Debug) Shorty.Debug.log(data);
+ $.ajax({
+ type: 'POST',
+ url: OC.filePath('shorty','ajax','add.php'),
+ cache: false,
+ data: data
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).done(function(response){
+ // wipe entries in dialog
+ Shorty.WUI.Dialog.reset(dialog)
+ }).done(function(response){
+ // add shorty to existing list
+ Shorty.WUI.List.add([response.data],true);
+ Shorty.WUI.List.dim(true)
+ dfd.resolve(response);
+ }).fail(function(response){
+ Shorty.WUI.List.dim(true)
+ dfd.reject(response);
+ })
+ })
+ return dfd.promise();
+ }, // ===== Shorty.Action.Url.add =====
+ // ===== Shorty.Action.Url.edit =====
+ edit: function(){
+ if (Shorty.Debug) Shorty.Debug.log("action modify url");
+ var dfd=new $.Deferred();
+ var dialog=$('#dialog-edit');
+ var id =dialog.find('#id').val();
+ var status=dialog.find('#status').val()||'blocked';
+ var title =dialog.find('#title').val()||'';
+ var until =dialog.find('#until').val()||'';
+ var notes =dialog.find('#notes').val()||'';
+ // perform modification of existing shorty
+ $.when(
+ Shorty.WUI.Notification.hide(),
+ // close and neutralize dialog
+ Shorty.WUI.Dialog.hide(dialog),
+ Shorty.WUI.List.dim(false),
+ Shorty.WUI.List.show()
+ ).done(function(){
+ var data={id: id,
+ status: status,
+ title: title,
+ notes: notes,
+ until: until};
+ if (Shorty.Debug) Shorty.Debug.log(data);
+ $.ajax({
+ type: 'POST',
+ url: OC.filePath('shorty','ajax','edit.php'),
+ cache: false,
+ data: data,
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).done(function(response){
+ // wipe entries in dialog
+ Shorty.WUI.Dialog.reset(dialog);
+ // modify existing entry in list
+ Shorty.WUI.List.modify([response.data],true);
+ Shorty.WUI.List.dim(true)
+ dfd.resolve(response);
+ }).fail(function(response){
+ dfd.reject(response);
+ })
+ })
+ return dfd.promise();
+ }, // ===== Shorty.Action.Url.edit =====
+ // ===== Shorty.Action.Url.del =====
+ del: function(){
+ if (Shorty.Debug) Shorty.Debug.log("action delete url");
+ var dfd = new $.Deferred();
+ var dialog = $('#dialog-edit');
+ var id = dialog.find('#id').val();
+ $.when(
+// Shorty.WUI.Notification.hide(),
+ $.ajax({
+ type: 'GET',
+ url: OC.filePath('shorty','ajax','del.php'),
+ cache: false,
+ data: { id: id }
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ )
+ ).done(function(response){
+ // close and neutralize dialog
+ Shorty.WUI.Dialog.hide(dialog);
+ // hide and remove deleted entry
+ // ...
+ dfd.resolve(response.data);
+ }).fail(function(response){
+ dfd.reject(response.data);
+ })
+ return dfd.promise();
+ }, // ===== Shorty.Action.Url.del =====
+ // ===== Shorty.Action.Url.forward =====
+ forward: function(entry){
+ if (Shorty.Debug) Shorty.Debug.log("action forward to entry "+entry.attr('id'));
+ var url=entry.attr('data-target');
+ if (Shorty.Debug) Shorty.Debug.log("opening target url '"+url+"' in new window");
+ window.open(url);
+ }, // Shorty.Action.Url.forward
+ // ===== Shorty.Action.Url.send =====
+ send: function(action,entry){
+ if (Shorty.Debug) Shorty.Debug.log("action send via "+action+" with entry "+entry.attr('id'));
+ switch (action){
+ case 'usage-email':
+ var mailSubject=entry.attr('data-title')||'';
+ var mailBody=entry.attr('data-notes')+"\n\n"+entry.attr('data-source');
+ window.location='mailto:""?'
+ +'subject='+encodeURIComponent(mailSubject)
+ +'&body='+encodeURIComponent(mailBody);
+ break;
+ case 'usage-sms':
+ var smsBody=entry.attr('data-title')+" - "+entry.attr('data-notes')+" - "+entry.attr('data-source');
+ // unfortunately there is no way to get the body over into the sms application on "sms urls"...
+ window.prompt(t('shorty',"Copy to clipboard: Ctrl+C, then paste into SMS: Ctrl-V"), smsBody );
+ window.location='sms:';
+ break;
+ case 'usage-qrcode':
+ var title =entry.attr('data-title');
+ var source=entry.attr('data-source');
+ var target=entry.attr('data-target');
+ Shorty.Action.Usage.Dialog.qrcode(title,source,target);
+ break;
+ case 'usage-clipboard':
+ window.prompt(t('shorty',"Copy to clipboard: Ctrl+C"), entry.attr('data-source'));
+ break;
+ default:
+ if (Shorty.Debug) Shorty.Debug.log("usage action '"+action+"' is disabled, refusing to comply");
+ }
+ }, // Shorty.Action.Url.send
+ // ===== Shorty.Action.Url.show =====
+ show: function(){
+ var dfd = new $.Deferred();
+ var dialog = $('#dialog-show');
+ var id = dialog.find('#id').val();
+ var record = $(this).parent().parent();
+ $('#shorty-add-id').val(record.attr('data-id'));
+ $('#shorty-add-id').val(record.attr('data-status'));
+ $('#shorty-add-source').val(record.children('.shorty-source:first').text());
+ $('#shorty-add-target').val(record.children('.shorty-target:first').text());
+ $('#shorty-add-notes').val(record.children('.shorty-notes:first').text());
+ $('#shorty-add-until').val(record.children('.shorty-until:first').text());
+ $.when(
+ function(){
+ if ($('.shorty-add').css('display') == 'none'){
+ $('.shorty-add').slideToggle();
+ }
+ },
+ $('html, body').animate({ scrollTop: $('.shorty-menu').offset().top }, 500)
+ ).done(dfd.resolve)
+ return dfd.promise();
+ }, // ===== Shorty.Action.Url.show =====
+ // ===== Shorty.Action.Url.status =====
+ status: function(id,status){
+ if (Shorty.Debug) Shorty.Debug.log("changing status of id "+id+" to "+status);
+ var dfd = new $.Deferred();
+ $.ajax({
+ type: 'GET',
+ url: OC.filePath('shorty','ajax','status.php'),
+ cache: false,
+ data: { id : id,
+ status: status }
+ }).pipe(
+ function(response){return Shorty.Ajax.eval(response)},
+ function(response){return Shorty.Ajax.fail(response)}
+ ).done(function(){
+ // update the rows content
+ var row=$('#list tbody tr#'+id);
+ row.attr('data-status',status);
+ row.find('td#status span').text(t('shorty',status));
+ dfd.resolve();
+ }).fail(dfd.reject)
+ return dfd.promise();
+ } // Shorty.Action.Url.status
+ }, // ===== Shorty.Action.Url =====
+ // ===== Shorty.Action.Usage =====
+ Usage:
+ {
+ // ===== Shorty.Action.Usage.Popup =====
+ Popup:
+ {
+ // ===== Shorty.Action.Usage.Popup.qrcode =====
+ qrcode:{},
+ }, // Shorty.Action.Usage.Popup
+ // ===== Shorty.Action.Usage.Dialog =====
+ Dialog:
+ {
+ // ===== Shorty.Action.Usage.Dialog.qrcode =====
+ qrcode:function(title,source,target){
+ var qrcode=Shorty.Action.Usage.Popup.qrcode;
+ if (!qrcode.dialog){
+ qrcode=$('#dialog-qrcode');
+ qrcode.dialog({show:'fade',autoOpen:false,modal:true});
+ qrcode.dialog('option','width',240 );
+ //qrcode.dialog('option','height',80 );
+ }
+ // a hidden input field ('qrcode-url') holds the base url to the qrcode generator
+ // we just add the url parameter for this specific entrys source url
+ var url=qrcode.find('#qrcode-url').val()+encodeURIComponent(source);
+ qrcode.dialog('option','title',title);
+ qrcode.find('#qrcode-img img').attr('src',url).attr('title',source);
+ qrcode.find('#qrcode-val a').text(url);
+ qrcode.bind('click',function(){
+ qrcode.find('#qrcode-img').toggle();
+ qrcode.find('#qrcode-val').toggle();
+ });
+ qrcode.dialog('open');
+ } // Shorty.Action.Usage.Dialog.qrcode
+ } // Shorty.Action.Usage.Dialog
+ } // Shorty.Action.Usage
+ }, // Shorty.Action
+
+ // ===========
+
+ // ===== Shorty.Ajax =====
+ Ajax:
+ {
+ // ===== Shorty.Ajax.eval =====
+ eval:function(response){
+ if (Shorty.Debug) Shorty.Debug.log("eval ajax response of status "+response.status);
+ // Check to see if the response is truely successful.
+ if (response.status){
+ // this is a valid response
+ if ('success'==response.status){
+ Shorty.WUI.Notification.show(response.message,'debug');
+ return new $.Deferred().resolve(response);
+ } else {
+ Shorty.WUI.Notification.show(response.message,'error');
+ return new $.Deferred().reject(response);
+ }
+// }else{
+ // TEST (regex) if this is a DB error:
+ // DB Error: "SQLSTATE[HY000]: General error: 1 near "WHERE": syntax error".....
+// // not a valid response, maybe a DB error ?
+// if ('DB error'==response)
+ }
+ }, // Shorty.Ajax.eval
+
+ // ===== Shorty.Ajax.fail =====
+ fail:function(response){
+ if (Shorty.Debug) Shorty.Debug.log("handle ajax failure");
+ return new $.Deferred().reject({
+ status: 'error',
+ data: null,
+ message: [ "Unexpected error: " + response.status + " " + response.statusText ]
+ });
+ } // Shorty.Ajax.fail
+ }, // Shorty.Ajax
+
+ // ==== Shorty.Date =====
+ Date:
+ {
+ // ===== Shorty.Date.expired =====
+ expired:function(date){
+ return (Date.parse(date)<=Date.parse(Date()));
+ } // Shorty.Date.expired
+ } // Shorty.Date
+
+} // Shorty
diff --git a/apps/shorty/l10n/de.php b/apps/shorty/l10n/de.php
new file mode 100644
index 00000000000..cc4eb624a02
--- /dev/null
+++ b/apps/shorty/l10n/de.php
@@ -0,0 +1,157 @@
+ "alle",
+"blocked" => "geblockt",
+"deleted" => "gelöscht",
+"disabled" => "deaktiviert",
+"enabled" => "aktiviert",
+"never" => "niemals",
+"private" => "privat",
+"public" => "öffentlich",
+"shared" => "geteilt",
+
+"bitly.com service" => "bitly.com-Service",
+"cli.gs service" => "cli.gs-Service",
+"goo.gl service" => "goo.gl-Service",
+"is.gd service" => "is.gd-Service",
+"ti.ny service" => "ti.ny-Service",
+"tiny.cc service" => "tiny.cc-Service",
+"static backend" => "statisches Backend",
+
+"Access" => "Zugriff",
+"Add a new shorty" => "Neuen Shorty hinzufügen",
+"Add as new" => "Neu hinzufügen",
+"Add page as 'Shorty' to ownCloud" => "Seite als 'Shorty' zu ownCloud hinzufügen",
+"Anything that appears helpful …" => "Alles, was hilfreich erscheint …",
+"Backend" => "Backend",
+"Base url" => "Basis-Url",
+"Choose a service…" => "Wähle einen Dienst…",
+"Click for qrcode image" => "Klicken für QRCode-Bild",
+"Click for qrcode url" => "Klicken für QRCode-Url",
+"Click it, for whatever site you want to create a Shorty." => "Klicke es, für welche Seite auch immer Du ein 'Shorty' generieren willst.",
+"Click registered" => "Click registriert",
+"Clicks" => "Klicks",
+"Close" => "Schließen",
+"Copy to clipboard" => "In die Zwischenablage kopieren",
+"Copy to clipboard: Ctrl+C" => "In Zwischenablage kopieren: Strg-C",
+"Copy to clipboard: Ctrl+C, then paste into SMS: Ctrl-V" => "In Zwischenablage kopieren: Strg-C, dann in SMS einfügen: Strg-V",
+"Counted entries and clicks" => "Einträge und Klicks gezählt",
+"Creation" => "Generierung",
+"Delete shorty" => "Shorty löschen",
+"Drag this to your browser bookmarks." => "Zieh' dies in die Lesezeichen Deines Browsers.",
+"Example" => "Beispiel",
+"Exception" => "Ausnahme",
+"Exception (%s)" => "Ausnahme (%s)",
+"Expiration" => "Verfall",
+"List currently empty." => "Liste derzeit leer.",
+"Loading" => "Lade",
+"Modifications for shorty with id '%s' saved" => "Änderungen zu Shorty mit Kennung '%s' gespeichert",
+"Modify attributes" => "Attribute ändern",
+"Modify shorty" => "Shorty ändern",
+"New Shorty" => "Neuer Shorty",
+"Notes" => "Notizen",
+"Number of entries" => "Anzahl der Einträge",
+"Number of entries: %s" => "Anzahl der Einträge: %s",
+"QRCode" => "QRCode",
+"Open source url" => "Quell-Url öffnen",
+"Open relay url" => "Relais-Url öffnen",
+"Open target" => "Ziel öffnen",
+"Open target url" => "Ziel-Url öffnen",
+"Preference saved." => "Präferenz gespeichert.",
+"Preference(s) retrieved." => "Präferenz(en) abgefragt.",
+"Relay url" => "Relais-Url",
+"Reload list" => "Liste neu laden",
+"Save" => "Speichern",
+"Send by email" => "Als E-Mail verschicken",
+"Send by SMS" => "Als SMS verschicken",
+"Setting saved." => "Einstellung gespeichert.",
+"Shortlet" => "Shortlet",
+"Shorty with id '%s' deleted" => "Shorty mit Kennung '%s' gelöscht",
+"Shorty source url" => "Shorty Quell-Url",
+"Shorty title" => "Shorty-Titel",
+"Show details" => "Details zeigen",
+"Show as QRCode" => "Als QRCode anzeigen",
+"Sort ascending" => "aufsteigend sortieren",
+"Sort descending" => "absteigend sortieren",
+"Source url" => "Quell-Url",
+"Specify a backend base url…" => "Lege eine Basis-Url für das Backend fest…",
+"Status" => "Status",
+"Status change for shorty with id '%s' saved" => "Statusänderung für Shorty mit Kennung '%s' gespeichert",
+"Target" => "Ziel",
+"Target url" => "Ziel-Url",
+"Target url '%s' is valid" => "Ziel-Url '%s' ist valide",
+"Test and use" => "Testen und nutzen",
+"Title" => "Titel",
+"Total of clicks" => "Summe der Klicks",
+"Unexpected type of exception caught: %s" => "Unerwarteter Type einer Ausnahme aufgetreten: %s",
+"Unknown object of type caught: %s" => "Unbekanntes Objekttyp aufgetreten: %s",
+"Url shortened to: %s" => "Url gekürzt zu: %s",
+"Verification by click" => "Prüfung durch Klicken",
+
+"%s\nMessage(code): %s (%s)\nFile(line): %s (%s)\nInfo: %%s" => "%s\Nachricht(Code): %s (%s)\nDatei(Zeile): %s (%s)\nInfo: %%s",
+
+"API access key" => "API-Zugriffsschlüssel",
+"Account" => "Nutzerkonto",
+"API key" => "API-Schlüssel",
+"bit.ly user" => "bit.ly-Nutzer",
+"bit.ly user name" => "bit.ly-Nutzerkennung",
+"bit.ly key" => "bit.ly-Schlüssel",
+"bit.ly users key" => "bit.ly-Nutzerschlüssel",
+"Google API key" => "Google API-Schlüssel",
+"Google API account" => "Google API-Nutzerkonto",
+"tiny.cc user" => "tiny.cc-Nutzer",
+"tiny.cc user name" => "tiny.cc-Nutzerkennung",
+"tiny.cc key" => "tiny.cc-Schlüssel",
+"tiny.cc user key" => "tiny.cc-Nutzerschlüssel",
+
+"The external 'bitly.com' service is used to register a short url for each generated shorty."
+=> "Der externe 'bitly.com'-Dienst wird genutzt, um eine Kurz-Url für jeden generierten Shorty zu registrieren.",
+"This means you have to register an '%s' at their site first."
+=> "Das bedeutet, zunächst muss ein '%s' auf deren Seite registriert werden.",
+"The external 'turl' service is used to register a short url for each generated shorty."
+=> "Der externe 'turl'-Dienst wird genutzt, um eine Kurz-Url für jeden generierten Shorty zu registrieren.",
+"The external 'ti.ny' service is used to register a short url for each generated shorty."
+=> "Der externe 'ti.ny'-Dienst wird genutzt, um eine Kurz-Url für jeden generierten Shorty zu registrieren.",
+"The external 'cli.gs' service is used to register a short url for each generated shorty."
+=> "Der externe 'cli.gs'-Dienst wird genutzt, um eine Kurz-Url für jeden generierten Shorty zu registrieren.",
+"The external 'is.gd' service is used to register a short url for each generated shorty."
+=> "Der externe 'is.gd'-Dienst wird genutzt, um eine Kurz-Url für jeden generierten Shorty zu registrieren.",
+"The external 'tiny.cc' service is used to register a short url for each generated shorty."
+=>"Der externe 'tiny.cc'-Dienst wird genutzt, um eine Kurz-Url für jeden generierten Shorty zu registrieren.",
+"No backend is used, direct links pointing to your ownCloud are generated."
+=> "Kein Backend wird verwendet, einfache direkte Links zur ownCloud werden erzeugt.",
+"The service requires you to authenticate yourself by providing a valid bit.ly user name and an '%s'."
+=> "Dieser Dienst erfordert die Authentifizierung durch eine valide bit.ly-Nutzerkennung und einen '%s'",
+"Such links are most likely longer than those generated when using a backend."
+=> "Solche Links werden sicherlich länger sein, als wenn ein Backend eingesetzt wird.",
+"However this option does not rely on any third party service and keeps your shortys under your control."
+=> "Dafür stützt sich diese Option aber nicht auf fremde Angebote und belässt die Shorties unter eigener Kontrolle.",
+"A static, rule-based backend is used, shorty links are generated relative to a given base url."
+=> "Ein statisches, regelbasiertes Backend wird genutzt, Shorty-Links werden relativ zu einer gegebenen Basis-Url generiert.",
+"Since this setup depends on server based configuration rules the base url can only be specified in the 'Admin' section of the configuration."
+=> "Da diese Konfiguration von serverseitigen Konfigurationsregeln abhängt, kann die Basis-Url nur unter 'Verwaltung' in der Konfiguration festgelegt werden.",
+"You have to take care that any request to the url configured here is internally mapped to the 'shorty' module."
+=> "Es muss sicher gestellt werden, dass jede Anfrage an die hier konfigurierte URL intern umgesetzt wird auf das 'Shorty'-Modul",
+"Have a try with the example link provided, click it, it should result in a confirmation that your setup is working."
+=> "Einfach den verfügbaren Beispiel-Link durch Anklicken testen. Das sollte zu einer Bestätigung führen, dass die Konfiguration funktioniert.",
+"Leave empty if you can't provide a short base url that is mapped the described way."
+=> "Einfach leer lassen, wenn keine Basis-URL angegeben werden kann, die wir beschrieben umgesetzt wird.",
+"Only use this backend, if you can provide a short base url that is mapped the described way. Your shorties won't work otherwise."
+=> "Dieses Backend kann nur verwendet werden, wenn die Basis-Url in der beschriebenen Art umgesetzt wird. Die Shorties werden andernfalls nicht nutzbar sein.",
+"Googles external 'goo.gl service' is used to register a short url for each generated shorty."
+=> "Googles externer 'goo.gl'-Dienst wird genutzt, um eine Kurz-Url für jeden generierten Shorty zu registrieren.",
+"You must provide a valid '%s' to use this service."
+=> "Es muss eine valide '%s' angegeben werden um diesen Dienst nutzen zu können. ",
+"This means you require a 'Google API console account'."
+=> "Das bedeutet, dass ein 'Google API console account' benötigt wird",
+"Register a new '%s' at their pages."
+=> "Einen neuen '%s' auf deren Seite registrieren.",
+"Enabling the SMS option will offer sending a Shorty via SMS."
+=> "Aktivieren der SMS-Option bietet das Versenden von Shortys via SMS an.",
+"Unfortunately support for 'SMS url handling' is usually only found on mobile devices like smart phones."
+=> "Unglücklicherweise werden 'SMS-Urls' typischerweise nur auf mobilen Geräten wie etwa Smartphones unterstützt.",
+"In addition, the implementations found in Android or iOS are minimalistic, buggy and differ from system to system."
+=> "Darüber hinaus sind die Implementierungen, etwa die in Adroid oder auch iOS minimalistisch, fehlerhaft und unterschiedlich von System zu System.",
+"In short: this might not work for you, therefore you can disable it…"
+=> "Kurz gesagt: die mag nicht funktionieren, daher can die Option deaktiviert werden…",
+);
\ No newline at end of file
diff --git a/apps/shorty/lib/backend.php b/apps/shorty/lib/backend.php
new file mode 100644
index 00000000000..aa6c58b4490
--- /dev/null
+++ b/apps/shorty/lib/backend.php
@@ -0,0 +1,286 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/backend.php
+ * Routines to use remote (online) shortening services as backends in a local workflow
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_Backend
+ * @brief Library to register urls using backends, typically remote (online) url shortening services
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_Backend
+{
+ /**
+ * @method OC_Shorty_Backend::registerUrl
+ * @brief Wrapper function around the specific backend routines
+ * @param id (string) Internal shorty id used to reference a shorty upon usage.
+ * @returns (string) The shortened url as generated by a specific backend.
+ * @throws OC_Shorty_Exception taking over the explaining of the failure from the specific backend
+ * @access public
+ * @author Christian Reiner
+ */
+ static function registerUrl ( $id )
+ {
+ try
+ {
+ // construct the $relay, the url to be called to reach THIS service (ownclouds shorty plugin)
+ $relay = OC_Shorty_Tools::relayUrl ( $id );
+ // call backend specific work horse
+ switch ( $type=OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-type','none') )
+ {
+ default: return OC_Shorty_Backend::registerUrl_default ( $id, $relay );
+ case 'static': return OC_Shorty_Backend::registerUrl_static ( $id, $relay );
+ case 'bitly': return OC_Shorty_Backend::registerUrl_bitly ( $id, $relay );
+ case 'cligs': return OC_Shorty_Backend::registerUrl_cligs ( $id, $relay );
+ case 'google': return OC_Shorty_Backend::registerUrl_google ( $id, $relay );
+ case 'isgd': return OC_Shorty_Backend::registerUrl_isgd ( $id, $relay );
+ case 'tinyurl': return OC_Shorty_Backend::registerUrl_tinyurl ( $id, $relay );
+ case 'tinycc': return OC_Shorty_Backend::registerUrl_tinycc ( $id, $relay );
+ } // switch
+ } // try
+ catch (OC_Shorty_Exception $e)
+ {
+ throw $e;
+ } // catch
+ catch (Exception $e)
+ {
+ throw new OC_Shorty_Exception ( "Failed to register url '%s' at '%s' backend", array($relay,$type) );
+ } // catch
+ } // OC_Shorty_Backend::registerUrl
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_default
+ * @brief Pseudo-registers a given local relay url
+ * @param id (string)
+ * @param relay (url)
+ * @returns validated and pseudo-registered relay
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_default ( $id, $relay )
+ {
+ return OC_Shorty_Type::validate ( $relay, OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_default
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_static
+ * @brief Registers a given local relay url as local static shorty
+ * @param id (string)
+ * @param relay (url)
+ * @returns registered and validated relay url
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_static ( $id, $relay )
+ {
+ if ( (FALSE===($base=trim ( OCP\Config::getAppValue('shorty','backend-static-base',FALSE))))
+ ||(empty($base)) )
+ throw new OC_Shorty_Exception ( 'No base url defined for the static backend' );
+ return OC_Shorty_Type::validate ( $base.$id, OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_static
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_bitly
+ * @brief Registers a given local relay url at the bit.ly shortening service
+ * @param id (string)
+ * @param relay (url)
+ * @returns registered and validated relay url
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_bitly ( $id, $relay )
+ {
+ $bitly_api_user = OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-bitly-user','');
+ $bitly_api_key = OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-bitly-key', '');
+ if ( ! $bitly_api_key || ! $bitly_api_user )
+ throw new OC_Shorty_Exception ( 'No API user or key configured' );
+ $curl = curl_init ( );
+ curl_setopt ( $curl, CURLOPT_URL, 'https://api-ssl.bit.ly/shorten' );
+ curl_setopt ( $curl, CURLOPT_SSL_VERIFYHOST, TRUE );
+ curl_setopt ( $curl, CURLOPT_POST, TRUE );
+ curl_setopt ( $curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json') );
+ curl_setopt ( $curl, CURLOPT_POSTFIELDS, json_encode(array('version'=>'2.0.1',
+ 'longUrl'=>$relay,
+ 'format'=>'json',
+ 'login'=>$bitly_api_user,
+ 'apiKey'=>$bitly_api_key) ) );
+ curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, TRUE );
+ if ( (FALSE===($reply=curl_exec($curl)))
+ ||(NULL===($payload=json_decode($reply)))
+ ||(!is_object($payload))
+ ||(!property_exists($payload,'id')) )
+ {
+ throw new OC_Shorty_Exception ( "Failed to register url at backend 'static'" );
+ }
+ curl_close ( $curl );
+ return OC_Shorty_Type::validate ( $payload->id, OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_bitly
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_cligs
+ * @brief Registers a given local relay url at the cli.gs shortening service
+ * @param id (string)
+ * @param relay (url)
+ * @returns registered and validated relay url
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_cligs ( $id, $relay )
+ {
+ $curl = curl_init ( );
+ curl_setopt ( $curl, CURLOPT_URL, sprintf('http://cli.gs/api/v2/cligs/create?url=%s&appid=owncloud_shorty&test=1', urlencode(trim($relay))) );
+ curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, TRUE );
+ if ( (FALSE===($reply=curl_exec($curl)))
+ ||( ! preg_match( '/^(.+)$/', $reply, $match )) )
+ {
+ throw new OC_Shorty_Exception ( "Failed to register url at backend 'cli.gs'" );
+ }
+ curl_close ( $curl );
+ return OC_Shorty_Type::validate ( $match[1], OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_cligs
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_isgd
+ * @brief Registers a given local relay url at the is.gd shortening service
+ * @param id (string)
+ * @param relay (url)
+ * @returns registered and validated relay url
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_isgd ( $id, $relay )
+ {
+ $curl = curl_init ( );
+ curl_setopt ( $curl, CURLOPT_URL, sprintf('http://is.gd/create.php?format=simple&url=%s', urlencode(trim($relay))) );
+ curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, TRUE );
+ if ( (FALSE===($reply=curl_exec($curl)))
+ ||( ! preg_match( '/^(.+)$/', $reply, $match )) )
+ {
+ throw new OC_Shorty_Exception ( "Failed to register url at backend 'is.gd'" );
+ }
+ curl_close ( $curl );
+ return OC_Shorty_Type::validate ( $match[1], OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_isgd
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_google
+ * @brief Registers a given local relay url at the google shortening service
+ * @param id (string)
+ * @param relay (url)
+ * @returns registered and validated relay url
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_google ( $id, $relay )
+ {
+ $api_key = OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-google-key','');
+ if ( ! $api_key )
+ throw new OC_Shorty_Exception ( 'No goo.gl API key configured' );
+ $curl = curl_init ( );
+ curl_setopt ( $curl, CURLOPT_URL, 'https://www.googleapis.com/urlshortener/v1/url' );
+ curl_setopt ( $curl, CURLOPT_SSL_VERIFYHOST, TRUE );
+ curl_setopt ( $curl, CURLOPT_POST, TRUE );
+ curl_setopt ( $curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json') );
+ curl_setopt ( $curl, CURLOPT_POSTFIELDS, json_encode(array('longUrl'=>$relay,
+ 'key'=>$api_key) ) );
+ curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, TRUE );
+ if ( (FALSE===($reply=curl_exec($curl)))
+ ||(NULL===($payload=json_decode($reply)))
+ ||(!is_object($payload))
+ ||(!property_exists($payload,'id')) )
+ {
+ throw new OC_Shorty_Exception ( "Failed to register url at backend 'goo.gl'" );
+ }
+ curl_close ( $curl );
+ return OC_Shorty_Type::validate ( $payload->id, OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_google
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_tinycc
+ * @brief Registers a given local relay url at the tiny.cc shortening service
+ * @param id (string)
+ * @param relay (url)
+ * @returns registered and validated relay url
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_tinycc ( $id, $relay )
+ {
+ $api_user = OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-tinycc-user','');
+ $api_key = OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-tinycc-key','');
+ if ( ! $api_key || ! $api_user )
+ throw new OC_Shorty_Exception ( 'No goo.gl API key configured' );
+ $curl = curl_init ( );
+ curl_setopt ( $curl, CURLOPT_URL, 'http://tiny.cc/?c=shorten' );
+ curl_setopt ( $curl, CURLOPT_SSL_VERIFYHOST, TRUE );
+ curl_setopt ( $curl, CURLOPT_POST, TRUE );
+ curl_setopt ( $curl, CURLOPT_HEADER, TRUE );
+ curl_setopt ( $curl, CURLOPT_POSTFIELDS, array('longUrl'=>$relay,
+ 'version'=>'2.0.3',
+ 'format'=>'json',
+ 'login'=>$api_user,
+ 'apiKey'=>$api_key) );
+ curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, TRUE );
+ if ( (FALSE===($reply=curl_exec($curl)))
+ ||(NULL===($payload=json_decode($reply)))
+ ||(!is_object($payload))
+ ||(!property_exists($payload,'id')) )
+ {
+ throw new OC_Shorty_Exception ( "Failed to register url at backend 'tiny.cc'" );
+ }
+ curl_close ( $curl );
+ return OC_Shorty_Type::validate ( $payload->id, OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_google
+
+ /**
+ * @method OC_Shorty_Backend::registerUrl_tinyurl
+ * @brief Registers a given local relay url at the tinyURL shortening service
+ * @param id (string)
+ * @param relay (url)
+ * @returns registered and validated relay url
+ * @access public
+ * @author Chrisian Reiner
+ */
+ static function registerUrl_tinyurl ( $id, $relay )
+ {
+ $curl = curl_init ( );
+ curl_setopt ( $curl, CURLOPT_URL, sprintf('http://tinyurl.com/api-create.php?url=%s', urlencode(trim($relay))) );
+ curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, TRUE );
+ if ( (FALSE===($reply=curl_exec($curl)))
+ ||( ! preg_match( '/^(.+)$/', $reply, $match )) )
+ {
+ throw new OC_Shorty_Exception ( "Failed to register url at backend 'tinyUrl'" );
+ }
+ curl_close ( $curl );
+ return OC_Shorty_Type::validate ( $match[1], OC_Shorty_Type::URL );
+ } // OC_Shorty_Backend::registerUrl_tinyurl
+
+} // class OC_Shorty_Backend
diff --git a/apps/shorty/lib/exception.php b/apps/shorty/lib/exception.php
new file mode 100644
index 00000000000..daeb9d10a9c
--- /dev/null
+++ b/apps/shorty/lib/exception.php
@@ -0,0 +1,162 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/exception.php
+ * Application specific exception class
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_Exception
+ * @brief Application specific exception class
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_Exception extends Exception
+{
+ protected $phrase = '';
+ protected $param = array ( );
+
+ /**
+ * @method OC_Shorty_Exception::__construct
+ * @brief: Constructs an exception based on a phrase and a set of parameters
+ * @param phrase (string) Human readable message that should be translatable
+ * @param param (array) Set of parameters to be used as sprintf arguments to fill the phrase
+ * @access public
+ * @author Christian Reiner
+ */
+ public function __construct ( $phrase, $param=array() )
+ {
+ if ( is_array($param) )
+ $this->param = $param;
+ else $this->param = array($param);
+ $this->phrase = $phrase;
+// $this->message = vsprintf ( $phrase, $this->params );
+ Exception::__construct ( vsprintf($phrase,$this->param), 1 );
+ }
+
+ /**
+ * @method OC_Shorty_Exception::getTranslation
+ * @brief: Returns the translated message of the exception
+ * @returns (string) Translated message including the filled in set of arguments
+ * @access public
+ * @author Christian Reiner
+ */
+ public function getTranslation ( )
+ {
+ return OC_Shorty_L10n::t ( $this->phrase, $this->param );
+ }
+
+ /**
+ * @method OC_Shorty_Exception::JSONerror
+ * @brief Calls OCP\JSON::error with a pretty formated version of an exception
+ * @param e (exception) an exception object holding information
+ * @returns (json) OCP\JSON::error
+ * @access public
+ * @author Christian Reiner
+ */
+ static function JSONerror ( $e )
+ {
+ $title = OC_Shorty_L10n::t("Exception");
+ switch ( get_class($e) )
+ {
+ case 'OC_Shorty_Exception':
+ $message = $e->getTranslation();
+ break;
+ case 'PDOException':
+ $message = sprintf ( OC_Shorty_L10n::t( "%s\nMessage(code): %s (%s)\nFile(line): %s (%s)\nInfo: %%s",
+ OC_Shorty_L10n::t("Exception (%s)", get_class($e)),
+ htmlspecialchars($e->getMessage()),
+ htmlspecialchars($e->getCode()),
+ htmlspecialchars($e->getFile()),
+ htmlspecialchars($e->getLine()) ),
+ (method_exists($e,'errorInfo') ? trim($e->errorInfo()) : '-/-') );
+ break;
+ default:
+ if ( is_a($e,'Exception') )
+ $message = OC_Shorty_L10n::t("Unexpected type of exception caught: %s", get_class($e));
+ else $message = OC_Shorty_L10n::t("Unknown object of type caught: %s", get_class($e));
+ } // switch
+ // swallow any accidential output generated by php notices and stuff to preserve a clean JSON reply structure
+ $output = trim ( OC_Shorty_Tools::ob_control(FALSE) );
+ if ( $output )
+ {
+ $message = "! Swallowing accidential output from ajax routines ! \n"
+ ."Please fix this ! Here is the first line: \n"
+ .substr ( $output, 0, strpos($output,"\n") );
+ OCP\Util::writeLog( 'shorty', $message, OC_Log::WARN );
+ } // output
+ // return a clean JSON error
+ return OCP\JSON::error ( array ( 'title' => $title,
+ 'message' => sprintf("%s: %s", $title, $message) ) );
+ } // function error
+} // class OC_Shorty_Exception
+
+/**
+ * @class OC_Shorty_HttpException
+ * @brief Application specific exception class: protocol layer
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_HttpException extends OC_Shorty_Exception
+{
+
+ /**
+ * @method OC_Shorty_HttpException::__construct
+ * @brief: Constructs an exception based on a phrase and a set of parameters
+ * @param status (integer) Http status code
+ * @access public
+ * @author Christian Reiner
+ */
+ public function __construct ( $status )
+ {
+ if ( is_numeric($status)
+ && array_key_exists($status,OC_Shorty_Type::$HTTPCODE) )
+ {
+ $status = intval($status);
+ $phrase = OC_Shorty_Type::$HTTPCODE[$status];
+ }
+ else
+ {
+ $status = 400;
+ $phrase = OC_Shorty_Type::$HTTPCODE[400]; // "Bad Request"
+ } // switch
+
+ // return http status code to client (browser)
+ if ( ! headers_sent() )
+ {
+ header ( sprintf("HTTP/1.0 %s %s",$status,$phrase) );
+ }
+ $tmpl = new OCP\Template("shorty", "tmpl_http_status", "guest");
+ $tmpl->assign("explanation", OC_Shorty_L10n::t($phrase));
+ $tmpl->printPage();
+ exit;
+ } // function __construct
+
+} // class OC_Shorty_HttpException
+
+?>
diff --git a/apps/shorty/lib/hooks.php b/apps/shorty/lib/hooks.php
new file mode 100644
index 00000000000..6ba59eccbda
--- /dev/null
+++ b/apps/shorty/lib/hooks.php
@@ -0,0 +1,64 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/hooks.php
+ * Static class providing routines to populate hooks called by other parts of ownCloud
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_Hooks
+ * @brief Static 'namespace' class for api hook population
+ * ownCloud propagates to use static classes as namespaces instead of OOP.
+ * This 'namespace' defines routines to populate hooks called by other parts of ownCloud
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_Hooks
+{
+ /**
+ * @brief Deletes all Shortys and preferences of a certain user
+ * @param paramters (array) parameters from postDeleteUser-Hook
+ * @return bool
+ */
+ public static function deleteUser ( $parameters )
+ {
+ OCP\Util::writeLog ( 'user post delete','wiping all users Shortys', OCP\Util::INFO );
+ $result = TRUE;
+ $param = array ( 'user' => OCP\User::getUser() );
+ // wipe shortys
+ $query = OCP\DB::prepare ( OC_Shorty_Query::WIPE_SHORTYS );
+ if ( FALSE===$query->execute($param) )
+ $result = FALSE;
+ // wipe preferences
+ $query = OCP\DB::prepare ( OC_Shorty_Query::WIPE_PREFERENCES );
+ if ( FALSE===$query->execute($param) )
+ $result = FALSE;
+ // report completion success
+ return $result;
+ }
+}
diff --git a/apps/shorty/lib/l10n.php b/apps/shorty/lib/l10n.php
new file mode 100644
index 00000000000..648130645a0
--- /dev/null
+++ b/apps/shorty/lib/l10n.php
@@ -0,0 +1,93 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/l10n.php
+ * Translation singleton
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_L10n
+ * @brief Convenient translation singleton
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_L10n
+{
+ /**
+ * @var OC_Shorty_L10n::dictionary
+ * @brief An internal dictionary file filled from the translation files provided.
+ * @access private
+ * @author Christian Reiner
+ */
+ private $dictionary;
+
+ /**
+ * @var OC_Shorty_L10n::instance
+ * @brief Internal singleton object
+ * @access private
+ * @author Christian Reiner
+ */
+ static private $instance=NULL;
+
+ /**
+ * @method OC_Shorty_L10n::__construct
+ * @brief
+ * @access private
+ * @author Christian Reiner
+ */
+ private function __construct ( ) { $this->dictionary = new OC_L10n('shorty'); }
+
+ /**
+ * @method OC_Shorty_L10n::t
+ * @brief Translates a given string into the users session language and fills any placeolders
+ * @param phrase to be translated
+ * @param … further arguments used as filling tokens in the tradition of printf strategies
+ * @returns translated phrase or the original phrase incase no translation could be found
+ * @access public
+ * @author Christian Reiner
+ */
+ static public function t ( $phrase )
+ {
+ // create singleton instance, if required
+ if ( ! self::$instance )
+ self::$instance = new OC_Shorty_L10n ( );
+ // handle different styles of how arguments can be handed over to this method
+ switch ( func_num_args() )
+ {
+ case 1: return self::$instance->dictionary->t ( $phrase, array() );
+ case 2: $arg = func_get_arg(1);
+ if ( is_array($arg) )
+ return self::$instance->dictionary->t ( $phrase, $arg );
+ else return self::$instance->dictionary->t ( $phrase, array($arg) );
+ default: $args = func_get_args();
+ array_shift ( $args );
+ return self::$instance->dictionary->t ( $phrase, $args );
+ }
+ }
+} // class OC_Shorty_L10n
+?>
diff --git a/apps/shorty/lib/meta.php b/apps/shorty/lib/meta.php
new file mode 100644
index 00000000000..91937bd3c8a
--- /dev/null
+++ b/apps/shorty/lib/meta.php
@@ -0,0 +1,213 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/meta.php
+ * Routines to retrieve meta information about a remote url
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_Meta
+ * @brief Static 'namespace' class for url meta information retrieval
+ * ownCloud propagates to use static classes as namespaces instead of OOP.
+ * This 'namespace' defines routines for the retrieval of meta information about remote urls.
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_Meta
+{
+
+ /**
+ * @method OC_Shorty_Meta::fetchMetaData
+ * @brief Retrieves the meta information to a given remote url
+ * @param url decoded target url for which meta information if requested
+ * @returns associative array holding the requested meta data
+ * @access public
+ * @author Christian Reiner
+ */
+ static function fetchMetaData ( $url )
+ {
+ $url_token = parse_url ( $url );
+ // some sane fallback values, in case we cannot get the meta data
+ $meta = array();
+ $meta['target'] = $url;
+ $meta['title'] = strtolower ( $url_token['host'] );
+ $meta['scheme'] = strtolower ( $url_token['scheme'] );
+ $meta['mimetype'] = 'application/octet-stream';
+ $meta['schemicon'] = self::selectIcon ( 'scheme', strtolower($url_token['scheme']) );
+ // we wont bother retrieving data about other protocols than http or ftp
+ if ( ! in_array(strtolower($url_token['scheme']),array('http','https','ftp','ftps')) )
+ return $meta;
+ // to fetch meta data we rely on curl being installed
+ if ( ! function_exists('curl_init') )
+ return $meta;
+ // try to retrieve the meta data
+ $handle = curl_init ( );
+ curl_setopt ( $handle, CURLOPT_URL, $url );
+ curl_setopt ( $handle, CURLOPT_RETURNTRANSFER, 1 );
+ curl_setopt ( $handle, CURLOPT_FOLLOWLOCATION, TRUE );
+ curl_setopt ( $handle, CURLOPT_MAXREDIRS, 10 );
+ if ( FALSE!==($page=curl_exec($handle)) )
+ {
+ // try to extract title from page
+ preg_match ( "/.*(.*)<\/title>.*<\/head>/si", $page, $match );
+ $meta['title'] = htmlspecialchars_decode ( $match[1] );
+ $meta['staticon'] = self::selectIcon ( 'state', TRUE );
+ // final url after a possible redirection
+ $meta['final'] = curl_getinfo ( $handle, CURLINFO_EFFECTIVE_URL );
+ // try to extract favicon from page
+ preg_match ( '/<[^>]*link[^>]*(rel=["\']icon["\']|rel=["\']shortcut icon["\']) .*href=["\']([^>]*)["\'].*>/iU', $page, $match );
+ if (1 OC_Shorty_Type::$HTTPCODE,
+ 'explanation' => array
+ (
+ 200 => 'Target url is valid and resolved.',
+ 201 => 'The request has been fulfilled and created a new ressource.',
+ 202 => 'The request has been accepted.',
+ 203 => 'The request yielded in non-authorative information.',
+ 204 => 'The request has been fulfilled but not produced any content.',
+ 205 => 'The request has been fulfilled and the view should be reset.',
+ 206 => 'The request has been fulfilled partially.',
+ )
+ );
+ // resolve specified code against map or provide some fallback content
+ if ( key_exists($aspect,$_code_map) && key_exists($identifier,$_code_map[$aspect]) )
+ return $_code_map[$aspect][$identifier];
+ else
+ {
+ switch ( $aspect )
+ {
+ case 'status': return sprintf("Status %s [unknown]",$identifier);
+ case 'explanation': return sprintf("[Undefined status code '%s']",$identifier);
+ default: throw new OC_Shorty_Exception ( "unknown aspect '%s' requested to resolve code '%s'",
+ array($aspect,$identifier) );
+ } // switch
+ }
+ } // function selectCode
+
+ /**
+ * @method OC_Shorty_Meta::selectIcon
+ * @brief Some helper utility for the easy integrate of icon references into templates and alike
+ * @param aspect a string indicating a section/pool an icon is to be chosen from
+ * @param identifier a string indicating a specific icon to be referenced
+ * @returns a hyper reference to an icon in form of a string
+ * @access public
+ * @author Christian Reiner
+ */
+ static function selectIcon ( $aspect, $identifier )
+ {
+ switch ( $aspect )
+ {
+ case 'state':
+ switch ($identifier)
+ {
+ case TRUE: return OCP\Util::imagePath('shorty', 'status/good.png');
+ case FALSE: return OCP\Util::imagePath('shorty', 'status/bad.png');
+ default: return OCP\Util::imagePath('shorty', 'status/neutral.png');
+ } // switch identifier
+ case 'scheme':
+ switch ($identifier)
+ {
+ case 'http':
+ case 'https': return OCP\Util::imagePath('shorty', 'scheme/H.png');
+ case 'ftp':
+ case 'ftps': return OCP\Util::imagePath('shorty', 'scheme/F.png');
+ case 'sftp': return OCP\Util::imagePath('shorty', 'scheme/S.png');
+ case 'mailto': return OCP\Util::imagePath('shorty', 'scheme/M.png');
+ case 'gopher': return OCP\Util::imagePath('shorty', 'scheme/G.png');
+ case 'webdav':
+ case 'webdavs': return OCP\Util::imagePath('shorty', 'scheme/W.png');
+ default: return OCP\Util::imagePath('shorty', 'blank.png');
+ } // switch identifier
+ case 'mimetype':
+ $identifier = explode('/',$identifier);
+ switch ($identifier[0])
+ {
+ case 'audio': return OCP\Util::imagePath('core', 'filetypes/audio.png');
+ case 'text': return OCP\Util::imagePath('core', 'filetypes/text.png');
+ case 'video': return OCP\Util::imagePath('core', 'filetypes/video.png');
+ case 'application':
+ switch ($identifier[1])
+ {
+ case 'pdf': return OCP\Util::imagePath('core', 'filetypes/application-pdf.png');
+ default: return OCP\Util::imagePath('shorty', 'blank.png');
+ } // switch identifier[1]
+ default: return OCP\Util::imagePath('shorty', 'blank.png');
+ } // switch identifier[0]
+ } // switch aspect
+ } // function selectIcon
+
+} // class OC_Shorty_Meta
+?>
diff --git a/apps/shorty/lib/query.php b/apps/shorty/lib/query.php
new file mode 100644
index 00000000000..6501a54d927
--- /dev/null
+++ b/apps/shorty/lib/query.php
@@ -0,0 +1,57 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/query.php
+ * Static catalog of sql queries
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_Query
+ * @brief Static catalog of sql queries
+ * These query templates are referenced by a OC_Shorty_Query::URL_...
+ * They have to be prapared by adding an array of parameters
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_Query
+{
+ const URL_INSERT = "INSERT INTO *PREFIX*shorty (id,status,favicon,title,source,target,user,until,created,notes) VALUES (:id,:status,:favicon,:title,:source,:target,:user,:until,CURRENT_DATE,:notes)";
+ const URL_DELETE = "DELETE FROM *PREFIX*shorty WHERE user=:user AND id=:id";
+ const URL_REMOVE = "DELETE FROM *PREFIX*shorty WHERE user=:user AND 'deleted'=status";
+ const URL_UPDATE = "UPDATE *PREFIX*shorty SET status=:status,title=:title,until=:until,notes=:notes WHERE user=:user AND id=:id";
+ const URL_STATUS = "UPDATE *PREFIX*shorty SET status=:status WHERE user=:user AND id=:id";
+ const URL_CLICK = "UPDATE *PREFIX*shorty SET accessed=CURRENT_TIMESTAMP, clicks=(clicks+1) WHERE id=:id";
+ const URL_FORWARD = "SELECT user,source,target,status,(until IS NOT NULL AND until!='' AND until
diff --git a/apps/shorty/lib/tools.php b/apps/shorty/lib/tools.php
new file mode 100644
index 00000000000..2b3bac70672
--- /dev/null
+++ b/apps/shorty/lib/tools.php
@@ -0,0 +1,235 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/tools.php
+ * A collection of general utility routines
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_Tools
+ * @brief Collection of a few practical routines, a tool box
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_Tools
+{
+ // internal flag indicating if output buffering should be used to prevent accidentially output during ajax requests
+ static $ob_usage = TRUE;
+ // internal flag indicating if there is currently an output buffer active
+ static $ob_active = FALSE;
+
+ /**
+ * @method OC_Shorty_Tools::ob_control
+ * @param on (boolean) wether to activate or deactivate the buffer
+ * @access public
+ * @author Christian Reiner
+ */
+ static function ob_control ( $on=TRUE )
+ {
+ $output = NULL;
+ if ( self::$ob_usage )
+ {
+ // attempt to use outpout buffering
+ if ( $on )
+ {
+ // start buffering if possible and not yet started before
+ if ( function_exists('ob_start') // output buffers installed at all ?
+ && ! self::$ob_active ) // don't stack buffers (create buffer only, if not yet started)
+ {
+ ob_implicit_flush ( FALSE );
+ ob_start ( );
+ self::$ob_active = TRUE;
+ }
+ } // if $on==TRUE
+ else
+ {
+ // end buffering _if_ it has been started before
+ if ( self::$ob_active )
+ {
+ $output = ob_get_contents ( );
+ ob_end_clean ( );
+ self::$ob_active = FALSE;
+ }
+ } // if $on==FALSE
+ } // if ob_usage
+ return $output;
+ } // function ob_control
+
+ /**
+ * @method OC_Shorty_Tools::db_escape
+ * @brief escape a value for incusion in db statements
+ * @param value (string) value to be escaped
+ * @returns (string) escaped string value
+ * @throws OC_Shorty_Exception in case of an unknown database engine
+ * @access public
+ * @author Christian Reiner
+ * @todo use mdb2::quote() / mdb2:.escape() instead ?
+ */
+ static function db_escape ( $value )
+ {
+ $type = OCP\Config::getSystemValue ( 'dbtype', 'sqlite' );
+ switch ( $type )
+ {
+ case 'sqlite':
+ case 'sqlite3':
+ return sqlite_escape_string ( $value );
+ case 'pgsql':
+ return pg_escape_string ( $value );
+ case 'mysql':
+ if (get_magic_quotes_gpc())
+ return mysql_real_escape_string ( stripslashes($value) );
+ else return mysql_real_escape_string ( $value );
+ }
+ throw new OC_Shorty_Exception ( "unknown database backend type '%1'", array($type) );
+ } // function db_escape
+
+ /**
+ * @method OC_Shorty_Tools::db_timestamp
+ * @brief current timestamp as required by db engine
+ * @returns (string) current timestamp as required by db engine
+ * @throws OC_Shorty_Exception in case of an unknown database engine
+ * @access public
+ * @author Christian Reiner
+ * @todo not really required any more, we rely on CURRENT_TIMESTAMP instead
+ */
+ static function db_timestamp ( )
+ {
+ $type = OCP\Config::getSystemValue( "dbtype", "sqlite" );
+ switch ( $type )
+ {
+ case 'sqlite':
+ case 'sqlite3': return "strftime('%s','now')";
+ case 'mysql': return 'UNIX_TIMESTAMP()';
+ case 'pgsql': return "date_part('epoch',now())::integer";
+ }
+ throw new OC_Shorty_Exception ( "unknown database backend type '%1'", array($type) );
+ } // function db_timestamp
+
+ /**
+ * @method OC_Shorty_Tools::shorty_id
+ * @brief Creates a unique id to be used for a new shorty entry
+ * @returns (string) valid and unique id
+ * @access public
+ * @author Christian Reiner
+ */
+ static function shorty_id ( )
+ {
+ // each shorty installation uses a (once self generated) 62 char alphabet
+ $alphabet=OCP\Config::getAppValue('shorty','id-alphabet');
+ if ( empty($alphabet) )
+ {
+ $alphabet = self::randomAlphabet(62);
+ OCP\Config::setAppValue ( 'shorty', 'id-alphabet', $alphabet );
+ }
+ // use alphabet to generate a id being unique over time
+ return self::convertToAlphabet ( str_replace(array(' ','.'),'',microtime()), $alphabet );
+ } // function shorty_id
+
+ /**
+ *
+ */
+ static function randomAlphabet ($length)
+ {
+ if ( ! is_integer($length) )
+ return FALSE;
+ $c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxwz0123456789";
+ for($l=0;$l<$length;$l++) $s .= $c{rand(0,strlen($c))};
+ return str_shuffle($s);
+ } // function randomAlphabet
+
+ /**
+ * @method OC_Shorty_Tools::convertToAlphabet
+ * @brief Converts a given decimal number into an arbitrary base (alphabet)
+ * @param number decimal value to be converted
+ * @returns (string) converted value in string notation
+ * @access public
+ * @author Christian Reiner
+ */
+ static function convertToAlphabet ( $number, $alphabet )
+ {
+ $alphabetLen = strlen($alphabet);
+ $decVal = (int) $number;
+ $number = FALSE;
+ $nslen = 0;
+ $pos = 1;
+ while ($decVal > 0)
+ {
+ $valPerChar = pow($alphabetLen, $pos);
+ $curChar = floor($decVal / $valPerChar);
+ if ($curChar >= $alphabetLen)
+ {
+ $pos++;
+ } else {
+ $decVal -= ($curChar * $valPerChar);
+ if ($number === FALSE)
+ {
+ $number = str_repeat($alphabet{1}, $pos);
+ $nslen = $pos;
+ }
+ $number = substr($number, 0, ($nslen - $pos)) . $alphabet{$curChar} . substr($number, (($nslen - $pos) + 1));
+ $pos--;
+ }
+ }
+ if ($number === FALSE) $number = $alphabet{1};
+ return $number;
+ }
+
+ /**
+ * @method OC_Shorty_Tools::relayUrl
+ * @brief Generates a relay url for a given id acting as a href target for all backends
+ * @param id (string) shorty id as shorty identification
+ * @returns (string) generated absolute relay url
+ * @access public
+ * @author Christian Reiner
+ */
+ static function relayUrl ($id)
+ {
+ return sprintf ( '%s?service=%s&id=%s', OCP\Util::linkToAbsolute("", "public.php"), 'shorty_relay', $id );
+ } // function relayUrl
+
+ /**
+ * @method OC_Shorty_Tools::countShortys
+ * @brief Returns the total number of entries and clicks from the database
+ * @returns (array) two elements sum_shortys & sum_clicks holding an integer each
+ * @access public
+ * @author Christian Reiner
+ */
+ static function countShorties ()
+ {
+ $param = array
+ (
+ ':user' => OCP\User::getUser ( ),
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_COUNT );
+ $result = $query->execute($param);
+ $reply = $result->fetchAll();
+ return $reply[0];
+ } // function countShorties
+
+} // class OC_Shorty_Tools
+?>
diff --git a/apps/shorty/lib/type.php b/apps/shorty/lib/type.php
new file mode 100644
index 00000000000..97cbf04ddb4
--- /dev/null
+++ b/apps/shorty/lib/type.php
@@ -0,0 +1,291 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file lib/type.php
+ * Type handling, recognition and verification routines
+ * @author Christian Reiner
+ */
+
+/**
+ * @class OC_Shorty_Type
+ * @brief Static 'namespace' class offering routines and constants used to handle type recognition and value verification
+ * @access public
+ * @author Christian Reiner
+ */
+class OC_Shorty_Type
+{
+ // the 'types' of values we deal with, actually more something like flavours
+ const ID = 'id';
+ const STATUS = 'status';
+ const SORTKEY = 'sortkey';
+ const SORTVAL = 'sortval';
+ const STRING = 'string';
+ const URL = 'url';
+ const INTEGER = 'integer';
+ const FLOAT = 'float';
+ const DATE = 'date';
+ const TIMESTAMP = 'timestamp';
+ // a list of all valid list sorting codes
+ static $SORTING = array (
+ '' =>'created DESC', // default
+ 'aa'=>'accessed', 'ad'=>'accessed DESC',
+ 'ca'=>'created', 'cd'=>'created DESC',
+ 'da'=>'until', 'dd'=>'until DESC',
+ 'ha'=>'clicks', 'hd'=>'clicks DESC',
+ 'ka'=>'id', 'kd'=>'id DESC',
+ 'sa'=>'status', 'sd'=>'status DESC',
+ 'ta'=>'title', 'td'=>'title DESC',
+ 'ua'=>'target', 'ud'=>'target DESC' );
+ // a list of all valid user preferences
+ static $PREFERENCE = array (
+ 'backend-type' => OC_Shorty_Type::STRING,
+ 'backend-static-base' => OC_Shorty_Type::URL,
+ 'backend-bitly-user' => OC_Shorty_Type::STRING,
+ 'backend-bitly-key' => OC_Shorty_Type::STRING,
+ 'backend-google-key' => OC_Shorty_Type::STRING,
+ 'backend-tinycc-user' => OC_Shorty_Type::STRING,
+ 'backend-tinycc-key' => OC_Shorty_Type::STRING,
+ 'sms-control' => OC_Shorty_Type::STRING,
+ 'list-sort-code' => OC_Shorty_Type::SORTKEY,
+ );
+ // valid status for entries
+ static $STATUS = array (
+ 'blocked',
+ 'private',
+ 'shared',
+ 'public',
+ 'deleted',
+ );
+ // a list of implemented backends
+ static $BACKENDS = array (
+ 'none' => ' [ none ] ',
+ 'static' => 'static backend',
+// 'bitly' => 'bitly.com service',
+// 'cligs' => 'cli.gs service',
+ 'isgd' => 'is.gd service',
+ 'google' => 'goo.gl service',
+// 'tinycc' => 'tiny.cc service',
+ 'tinyurl' => 'ti.ny service',
+ );
+ // a list of all valid system settings
+ static $SETTING = array (
+ 'backend-static-base' => OC_Shorty_Type::URL,
+ );
+ static $HTTPCODE = array (
+ 200 => 'Ok',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 306 => '(unused)',
+ 307 => 'Temporary Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ );
+
+ /**
+ * @method OC_Shorty_Type::validate
+ * @brief Validates a given value against a type specific regular expression
+ * Validates a given value according to the claimed type of the value.
+ * Validation is done by matching the value against a type specific regular expression.
+ * @param value the value to be verified according to the specified type
+ * @param type the type the value is said to belong to, important for verification
+ * @param strict flag indicating if the verification should be done strict, that is if an exception should be thrown in case of a failure
+ * @returns the value itself in case of a positive validation, NULL or an exception in case of a failure, depending on the flag indication strict mode
+ * @throws error indicating a failed validation in case of strict mode
+ * @access public
+ * @author Christian Reiner
+ */
+ static function validate ( $value, $type, $strict=FALSE )
+ {
+ switch ( $type )
+ {
+ case self::ID:
+ if ( preg_match ( '/^[a-z0-9]{2,20}$/i', $value ) )
+ return $value;
+ elseif ( ! $strict)
+ return NULL;
+ throw new OC_Shorty_Exception ( "invalid value '%s' for type '%s'", array( ((24
diff --git a/apps/shorty/preferences.php b/apps/shorty/preferences.php
new file mode 100644
index 00000000000..6e122fe410b
--- /dev/null
+++ b/apps/shorty/preferences.php
@@ -0,0 +1,68 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file settings.php
+ * This plugins user preferences dialog
+ * The dialog will be included in the general framework of the user preferences page
+ * @access public
+ * @author Christian Reiner
+ */
+
+OCP\Util::addStyle ( '3rdparty', 'chosen/chosen' );
+OCP\Util::addStyle ( 'shorty', 'shorty' );
+OCP\Util::addStyle ( 'shorty', 'preferences' );
+
+OCP\Util::addScript ( '3rdparty', 'chosen/chosen.jquery.min' );
+OCP\Util::addScript ( 'shorty', 'shorty' );
+OCP\Util::addScript ( 'shorty', 'preferences' );
+if ( OC_Log::DEBUG==OC_Config::getValue( "loglevel", OC_Log::WARN ) )
+ OCP\Util::addScript ( 'shorty', 'debug' );
+
+
+// fetch template
+$tmpl = new OCP\Template ( 'shorty', 'tmpl_preferences' );
+// inflate template
+$backend_types = OC_Shorty_Type::$BACKENDS;
+// kick out static option again if no global backend base has been specified in the system settings
+$backend_static_base = OCP\Config::getAppValue('shorty','backend-static-base','');
+if ( empty($backend_static_base)
+ || !parse_url($backend_static_base,PHP_URL_SCHEME)
+ || !parse_url($backend_static_base,PHP_URL_HOST) )
+ unset($backend_types['static']);
+// feed template engine
+$tmpl->assign ( 'backend-types', $backend_types );
+$tmpl->assign ( 'backend-static-base', $backend_static_base );
+$tmpl->assign ( 'backend-bitly-user', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-bitly-user','') );
+$tmpl->assign ( 'backend-bitly-key', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-bitly-key','') );
+$tmpl->assign ( 'backend-google-key', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-google-key','') );
+$tmpl->assign ( 'backend-tinycc-user', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-tinycc-user','') );
+$tmpl->assign ( 'backend-tinycc-key', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-tinycc-key','') );
+$tmpl->assign ( 'backend-type', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','backend-type','') );
+$tmpl->assign ( 'sms-control', OCP\Config::getUserValue(OCP\User::getUser(),'shorty','sms-control','disabled') );
+// render template
+return $tmpl->fetchPage ( );
+?>
diff --git a/apps/shorty/qrcode.php b/apps/shorty/qrcode.php
new file mode 100644
index 00000000000..43b91b22afb
--- /dev/null
+++ b/apps/shorty/qrcode.php
@@ -0,0 +1,100 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file qrcode.php
+ * Generates qr code barcodes cading a specified url
+ * @access public
+ * @author Christian Reiner
+ */
+
+require_once ( '3rdparty/php/phpqrcode.php' );
+
+$source = NULL;
+// we try to guess what the request indicates:
+// - a (source) url to be looked up in the database
+foreach ($_GET as $key=>$val) // in case there are unexpected, additional arguments like a timestamp added by some stupid proxy
+{
+ switch ($key)
+ {
+ default:
+ // unrecognized key, we ignore it
+ break;
+ case 'url':
+ case 'uri':
+ case 'ref':
+ case 'source':
+ case 'target':
+ // a recognized argument key indicating an id to be looked up
+ $source = OC_Shorty_Type::req_argument($key,OC_Shorty_Type::URL,FALSE);
+ break 2; // skip switch AND foreach
+ } // switch
+} // foreach
+
+// generate qr code for the specified url, IF it exists and is usable in the database
+try
+{
+ if ( $source )
+ {
+ $param = array ( 'source' => OC_Shorty_Type::normalize($source,OC_Shorty_Type::URL) );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_SOURCE );
+ $result = $query->execute($param)->FetchAll();
+
+ if ( FALSE===$result )
+ throw new OC_Shorty_HttpException ( 500 );
+ elseif ( ! is_array($result) )
+ throw new OC_Shorty_HttpException ( 500 );
+ elseif ( 0==sizeof($result) )
+ {
+ // no entry found => 404: Not Found
+ throw new OC_Shorty_HttpException ( 404 );
+ }
+ elseif ( 1 409: Conflict
+ throw new OC_Shorty_HttpException ( 409 );
+ }
+ elseif ( (!array_key_exists(0,$result)) || (!is_array($result[0])) || (!array_key_exists('source',$result[0])) )
+ {
+ // invalid entry => 500: Internal Server Error
+ throw new OC_Shorty_HttpException ( 500 );
+ }
+ elseif ( (!array_key_exists('source',$result[0])) || ('1'==$result[0]['expired']) )
+ {
+ // entry expired => 410: Gone
+ throw new OC_Shorty_HttpException ( 410 );
+ }
+ // generate qrcode, regardless of who sends the request
+ QRcode::png ( $source );
+ } // if $source
+ else
+ {
+ // refuse forwarding => 403: Forbidden
+ throw new OC_Shorty_HttpException ( 403 );
+ }
+} catch ( OC_Shorty_Exception $e ) { header($e->getMessage()); }
+
+?>
diff --git a/apps/shorty/relay.php b/apps/shorty/relay.php
new file mode 100644
index 00000000000..077a3625afb
--- /dev/null
+++ b/apps/shorty/relay.php
@@ -0,0 +1,140 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file relay.php
+ * This is the plugins central relaying feature
+ * All relay requests are handled by this file.
+ * @access public
+ * @author Christian Reiner
+ */
+
+OCP\App::setActiveNavigationEntry ( 'shorty_index' );
+
+$arg = NULL;
+// we try to guess what the request indicates:
+// - a (shorty) id to be looked up in the database resulting in a forwarding to the stored target
+// - a (target) url to be added as a new shorty
+// - none of the two, so just a plain list of existing shortys
+foreach ($_GET as $key=>$val) // in case there are unexpected, additional arguments like a timestamp added by some stupid proxy
+{
+ switch ($key)
+ {
+ default:
+ // unrecognized key, we ignore it
+ break;
+ case 'id':
+ case 'shorty':
+ case 'ref':
+ case 'entry':
+ // a recognized argument key indicating an id to be looked up
+ $arg = OC_Shorty_Type::req_argument($key,OC_Shorty_Type::ID,FALSE);
+ break 2; // skip switch AND foreach
+ } // switch
+} // foreach
+
+// an id was specified, ordinary or special meaning ?
+if ( '0000000000'==$arg )
+{
+ // this is a pseudo id, used to test the setup, so just return a positive message.
+ // this is used to test the setup of the static backend, shorty calls itself from there
+ OCP\Util::writeLog( 'shorty', "Positiv validation of static backend base url", OC_Log::INFO );
+ OCP\JSON::success ( array ( ) );
+ exit();
+}
+
+// now construct the target url and relay to it (if applicable)
+try
+{
+ // detect requested shorty id from request
+ $p_id = trim ( OC_Shorty_Type::normalize($arg,OC_Shorty_Type::ID) ) ;
+ if ( $p_id )
+ {
+ $param = array
+ (
+ 'id' => $p_id,
+ );
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_FORWARD );
+ $result = $query->execute($param)->FetchAll();
+ if ( FALSE===$result )
+ throw new OC_Shorty_HttpException ( 500 );
+ elseif ( ! is_array($result) )
+ throw new OC_Shorty_HttpException ( 500 );
+ elseif ( 0==sizeof($result) )
+ {
+ // no entry found => 404: Not Found
+ throw new OC_Shorty_HttpException ( 404 );
+ }
+ elseif ( 1 409: Conflict
+ throw new OC_Shorty_HttpException ( 409 );
+ }
+ elseif ( (!array_key_exists(0,$result)) || (!is_array($result[0])) || (!array_key_exists('target',$result[0])) )
+ {
+ // invalid entry => 500: Internal Server Error
+ throw new OC_Shorty_HttpException ( 500 );
+ }
+ elseif ( (!array_key_exists('target',$result[0])) || ('1'==$result[0]['expired']) )
+ {
+ // entry expired => 410: Gone
+ throw new OC_Shorty_HttpException ( 410 );
+ }
+ // an usable target !
+ $target = trim($result[0]['target']);
+ // check status of matched entry
+ switch (trim($result[0]['status']))
+ {
+ default:
+ case 'blocked':
+ // refuse forwarding => 403: Forbidden
+ throw new OC_Shorty_HttpException ( 403 );
+ case 'private':
+ // check if user owns the Shorty, deny access if not
+ if ( $result[0]['user']!=OCP\User::getUser() )
+ // refuse forwarding => 403: Forbidden
+ throw new OC_Shorty_HttpException ( 403 );
+ // NO break; but fall through to the action in 'case public:'
+ case 'shared':
+ // check if we are a user, deny access if not
+ if ( ! OCP\User::isLoggedIn() )
+ // refuse forwarding => 403: Forbidden
+ throw new OC_Shorty_HttpException ( 403 );
+ // NO break; but fall through to the action in 'case public:'
+ case 'public':
+ // forward to target, regardless of who sends the request
+ header("HTTP/1.0 301 Moved Permanently");
+ // http forwarding header
+ header ( sprintf('Location: %s', $target) );
+ } // switch status
+ // register click in shorty
+ $query = OCP\DB::prepare ( OC_Shorty_Query::URL_CLICK );
+ $query->execute ( $param );
+ exit();
+ } // if id
+} catch ( OC_Shorty_Exception $e ) { header($e->getMessage()); }
+
+?>
diff --git a/apps/shorty/settings.php b/apps/shorty/settings.php
new file mode 100644
index 00000000000..f10e354a7be
--- /dev/null
+++ b/apps/shorty/settings.php
@@ -0,0 +1,52 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+
+/**
+ * @file settings.php
+ * This plugins system settings dialog
+ * The dialog will be included in the general framework of the system settings page
+ * @access public
+ * @author Christian Reiner
+ */
+
+OCP\Util::addStyle ( '3rdparty', 'chosen/chosen' );
+OCP\Util::addStyle ( 'shorty', 'shorty' );
+OCP\Util::addStyle ( 'shorty', 'settings' );
+
+OCP\Util::addScript ( '3rdparty', 'chosen/chosen.jquery.min' );
+OCP\Util::addScript ( 'shorty', 'shorty' );
+OCP\Util::addScript ( 'shorty', 'settings' );
+if ( OC_Log::DEBUG==OC_Config::getValue( "loglevel", OC_Log::WARN ) )
+ OCP\Util::addScript ( 'shorty', 'debug' );
+
+
+// fetch template
+$tmpl = new OCP\Template ( 'shorty', 'tmpl_settings' );
+// inflate template
+$tmpl->assign ( 'backend-static-base', OCP\Config::getAppValue('shorty','backend-static-base','') );
+// render template
+return $tmpl->fetchPage ( );
+?>
diff --git a/apps/shorty/templates/tmpl_dlg_qrcode.php b/apps/shorty/templates/tmpl_dlg_qrcode.php
new file mode 100644
index 00000000000..15b0eb2c366
--- /dev/null
+++ b/apps/shorty/templates/tmpl_dlg_qrcode.php
@@ -0,0 +1,52 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_dlg_verify.php b/apps/shorty/templates/tmpl_dlg_verify.php
new file mode 100644
index 00000000000..14b10d84174
--- /dev/null
+++ b/apps/shorty/templates/tmpl_dlg_verify.php
@@ -0,0 +1,70 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_http_status.php b/apps/shorty/templates/tmpl_http_status.php
new file mode 100644
index 00000000000..a43b4e3c328
--- /dev/null
+++ b/apps/shorty/templates/tmpl_http_status.php
@@ -0,0 +1,41 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_index.php b/apps/shorty/templates/tmpl_index.php
new file mode 100644
index 00000000000..fc4c4f91616
--- /dev/null
+++ b/apps/shorty/templates/tmpl_index.php
@@ -0,0 +1,66 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_preferences.php b/apps/shorty/templates/tmpl_preferences.php
new file mode 100644
index 00000000000..61294c1914c
--- /dev/null
+++ b/apps/shorty/templates/tmpl_preferences.php
@@ -0,0 +1,257 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_settings.php b/apps/shorty/templates/tmpl_settings.php
new file mode 100644
index 00000000000..bcbab5ae014
--- /dev/null
+++ b/apps/shorty/templates/tmpl_settings.php
@@ -0,0 +1,70 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_url_add.php b/apps/shorty/templates/tmpl_url_add.php
new file mode 100644
index 00000000000..abf51bdab73
--- /dev/null
+++ b/apps/shorty/templates/tmpl_url_add.php
@@ -0,0 +1,86 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_url_edit.php b/apps/shorty/templates/tmpl_url_edit.php
new file mode 100644
index 00000000000..395b75ecab5
--- /dev/null
+++ b/apps/shorty/templates/tmpl_url_edit.php
@@ -0,0 +1,100 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_url_list.php b/apps/shorty/templates/tmpl_url_list.php
new file mode 100644
index 00000000000..7a93eab9d8d
--- /dev/null
+++ b/apps/shorty/templates/tmpl_url_list.php
@@ -0,0 +1,167 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_url_share.php b/apps/shorty/templates/tmpl_url_share.php
new file mode 100644
index 00000000000..b42396d6a05
--- /dev/null
+++ b/apps/shorty/templates/tmpl_url_share.php
@@ -0,0 +1,89 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
+
+
diff --git a/apps/shorty/templates/tmpl_url_show.php b/apps/shorty/templates/tmpl_url_show.php
new file mode 100644
index 00000000000..462d8e8b9cb
--- /dev/null
+++ b/apps/shorty/templates/tmpl_url_show.php
@@ -0,0 +1,85 @@
+
+* @license GNU Affero General Public license (AGPL)
+* @link information
+* @link repository https://svn.christian-reiner.info/svn/app/oc/shorty
+*
+* 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 .
+*
+*/
+?>
+
+
+
+
+
diff --git a/apps/storage_charts/ajax/config.php b/apps/storage_charts/ajax/config.php
new file mode 100644
index 00000000000..22fa0384290
--- /dev/null
+++ b/apps/storage_charts/ajax/config.php
@@ -0,0 +1,44 @@
+.
+*
+*/
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('storage_charts');
+
+if(in_array($_POST['k'], Array('hu_size','sc_sort','hu_size_hus'))){
+ switch($_POST['o']){
+ case 'set':
+ $i = NULL;
+ if(is_array($_POST['i'])){
+ $i = serialize($_POST['i']);
+
+ }elseif(is_numeric($_POST['i'])){
+ $i = $_POST['i'];
+ }
+ OC_DLStCharts::setUConfValue($_POST['k'], $i);
+ break;
+ case 'get':
+ $v = OC_DLStCharts::getUConfValue($_POST['k']);
+ OCP\JSON::encodedPrint(Array('r' => $v['uc_val']));
+ break;
+ }
+}
diff --git a/apps/storage_charts/ajax/data.php b/apps/storage_charts/ajax/data.php
new file mode 100644
index 00000000000..a9a36d3c642
--- /dev/null
+++ b/apps/storage_charts/ajax/data.php
@@ -0,0 +1,37 @@
+.
+*
+*/
+
+OCP\JSON::checkLoggedIn();
+OCP\JSON::checkAppEnabled('storage_charts');
+
+$l = new OC_L10N('storage_charts');
+
+// Update and save the new configuration
+if(is_numeric($_POST['s']) && in_array($_POST['k'], Array('hu_size','hu_size_hus'))){
+ OC_DLStCharts::setUConfValue($_POST['k'], $_POST['s']);
+ if(strcmp($_POST['k'],'hu_size') == 0){
+ OCP\JSON::encodedPrint(Array('r' => OC_DLStChartsLoader::loadChart('clines_usse', $l)));
+ }else{
+ OCP\JSON::encodedPrint(Array('r' => OC_DLStChartsLoader::loadChart('chisto_us', $l)));
+ }
+}
diff --git a/apps/storage_charts/appinfo/app.php b/apps/storage_charts/appinfo/app.php
new file mode 100644
index 00000000000..3efec67ea8f
--- /dev/null
+++ b/apps/storage_charts/appinfo/app.php
@@ -0,0 +1,51 @@
+.
+*
+*/
+
+OCP\App::checkAppEnabled('storage_charts');
+
+OC::$CLASSPATH['OC_DLStCharts'] = "apps/storage_charts/lib/db.class.php";
+OC::$CLASSPATH['OC_DLStChartsLoader'] = "apps/storage_charts/lib/loader.class.php";
+
+OCP\App::register(Array(
+ 'order' => 60,
+ 'id' => 'storage_charts',
+ 'name' => 'Storage Charts'
+));
+
+OCP\App::addNavigationEntry(Array(
+ 'id' => 'storage_charts',
+ 'order' => 60,
+ 'href' => OCP\Util::linkTo('storage_charts', 'charts.php'),
+ 'icon' => OCP\Util::imagePath('storage_charts', 'chart.png'),
+ 'name' => 'DL Charts'
+));
+
+OCP\App::registerPersonal('storage_charts','settings');
+
+$data_dir = OCP\Config::getSystemValue('datadirectory', '');
+if(OCP\User::getUser() && strlen($data_dir) != 0){
+ $fs = OCP\Files::getStorage('files');
+ $used = OC_DLStCharts::getTotalDataSize(OC::$CONFIG_DATADIRECTORY);
+ $total = OC_DLStCharts::getTotalDataSize($data_dir) + $fs->free_space();
+ OC_DLStCharts::update($used, $total);
+}
diff --git a/apps/storage_charts/appinfo/app_admin.php b/apps/storage_charts/appinfo/app_admin.php
new file mode 100644
index 00000000000..9181d2f54bb
--- /dev/null
+++ b/apps/storage_charts/appinfo/app_admin.php
@@ -0,0 +1,58 @@
+.
+*
+*/
+
+OCP\App::checkAppEnabled('storage_charts');
+
+$l = OC_L10N::get('storage_charts');
+
+OC::$CLASSPATH['OC_DLStCharts'] = "apps/storage_charts/lib/db.class.php";
+OC::$CLASSPATH['OC_DLStChartsLoader'] = "apps/storage_charts/lib/loader.class.php";
+
+if(OC_Group::inGroup(OCP\User::getUser(), 'admin')){
+ OCP\App::register(Array(
+ 'order' => 60,
+ 'id' => 'storage_charts',
+ 'name' => 'Storage Charts'
+ ));
+
+ OCP\App::addNavigationEntry(Array(
+ 'id' => 'storage_charts',
+ 'order' => 60,
+ 'href' => OCP\Util::linkTo('storage_charts', 'charts.php'),
+ 'icon' => OCP\Util::imagePath('storage_charts', 'chart.png'),
+ 'name' => 'DL Charts'
+ ));
+
+ OCP\App::registerPersonal('storage_charts','settings');
+}elseif(OCP\User::isLoggedIn() && $_GET['app'] == 'storage_charts'){
+ die($l->t('Permission denied.'));
+}
+
+// Get storage value for logged in user
+$data_dir = OCP\Config::getSystemValue('datadirectory', '');
+if(OCP\User::getUser() && strlen($data_dir) != 0){
+ $fs = OCP\Files::getStorage('files');
+ $used = OC_DLStCharts::getTotalDataSize(OC::$CONFIG_DATADIRECTORY);
+ $total = OC_DLStCharts::getTotalDataSize($data_dir) + $fs->free_space();
+ OC_DLStCharts::update($used, $total);
+}
\ No newline at end of file
diff --git a/apps/storage_charts/appinfo/database.xml b/apps/storage_charts/appinfo/database.xml
new file mode 100644
index 00000000000..cac8136d2e9
--- /dev/null
+++ b/apps/storage_charts/appinfo/database.xml
@@ -0,0 +1,99 @@
+
+
+ *dbname*
+ true
+ false
+ utf8
+
There is no standardized way to do this currently. I have seen both translation keys using printf formatting
+ and custom interpolation using e.g. curly brackets.
+ After:
+
+<tal:block i18n:name="username" tal:content="user" />
+<p i18n:translate="">Your user name is ${username}.</p>
+
+
Your user name is ${username}.
+
Or you can wrap it in some markup:
+
+<p i18n:translate="">
+Welcome back <span i18n:name="username" tal:replace="user"/>.
+</p>