565 lines
18 KiB
PHP
565 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2001-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file LICENSE for license information (ASL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/apache.
|
|
*
|
|
* @package Mnemo
|
|
*/
|
|
/**
|
|
* Mnemo Base Class.
|
|
*
|
|
* @author Jon Parise <jon@horde.org>
|
|
* @package Mnemo
|
|
*/
|
|
class Mnemo
|
|
{
|
|
/**
|
|
* Sort by memo description.
|
|
*/
|
|
const SORT_DESC = 0;
|
|
|
|
/**
|
|
* Sort by notepad.
|
|
*/
|
|
const SORT_NOTEPAD = 2;
|
|
|
|
/**
|
|
* Sort by moddate
|
|
*/
|
|
const SORT_MOD_DATE = 3;
|
|
|
|
/**
|
|
* Sort in ascending order.
|
|
*/
|
|
const SORT_ASCEND = 0;
|
|
|
|
/**
|
|
* Sort in descending order.
|
|
*/
|
|
const SORT_DESCEND = 1;
|
|
|
|
/**
|
|
* No passphrase provided.
|
|
*/
|
|
const ERR_NO_PASSPHRASE = 100;
|
|
|
|
/**
|
|
* Decrypting failed
|
|
*/
|
|
const ERR_DECRYPT = 101;
|
|
|
|
/**
|
|
* Retrieves the current user's note list from storage. This function will
|
|
* also sort the resulting list, if requested.
|
|
*
|
|
* @param constant $sortby The field by which to sort. (self::SORT_DESC,
|
|
* self::SORT_NOTEPAD, self::SORT_MOD_DATE)
|
|
* @param constant $sortdir The direction by which to sort.
|
|
* (self::SORT_ASC, self::SORT_DESC)
|
|
*
|
|
* @return array A list of the requested notes.
|
|
*
|
|
* @see Mnemo_Driver::listMemos()
|
|
*/
|
|
public static function listMemos($sortby = self::SORT_DESC,
|
|
$sortdir = self::SORT_ASCEND)
|
|
{
|
|
global $conf, $display_notepads;
|
|
$memos = array();
|
|
|
|
/* Sort the memo list. */
|
|
$sort_functions = array(
|
|
self::SORT_DESC => 'ByDesc',
|
|
self::SORT_NOTEPAD => 'ByNotepad',
|
|
self::SORT_MOD_DATE => 'ByModDate'
|
|
);
|
|
|
|
foreach ($display_notepads as $notepad) {
|
|
$storage = $GLOBALS['injector']->getInstance('Mnemo_Factory_Driver')->create($notepad);
|
|
try {
|
|
$storage->retrieve();
|
|
} catch (Mnemo_Exception $e) {
|
|
$GLOBALS['notification']->push($e, 'horde.error');
|
|
}
|
|
$newmemos = $storage->listMemos();
|
|
$memos = array_merge($memos, $newmemos);
|
|
}
|
|
|
|
// Sort the array if we have a sort function defined
|
|
if (isset($sort_functions[$sortby])) {
|
|
$prefix = ($sortdir == self::SORT_DESCEND) ? '_rsort' : '_sort';
|
|
uasort($memos, array('Mnemo', $prefix . $sort_functions[$sortby]));
|
|
}
|
|
|
|
return $memos;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of notes in notepads that the current user owns.
|
|
*
|
|
* @return integer The number of notes that the user owns.
|
|
*/
|
|
public static function countMemos()
|
|
{
|
|
static $count;
|
|
if (isset($count)) {
|
|
return $count;
|
|
}
|
|
|
|
$notepads = self::listNotepads(true, Horde_Perms::ALL);
|
|
$count = 0;
|
|
foreach (array_keys($notepads) as $notepad) {
|
|
$storage = $GLOBALS['injector']->getInstance('Mnemo_Factory_Driver')->create($notepad);
|
|
$storage->retrieve();
|
|
$count += count($storage->listMemos());
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
|
|
/**
|
|
* Retrieves a specific note from storage.
|
|
*
|
|
* @param string $notepad The notepad to retrieve the note from.
|
|
* @param string $noteId The Id of the note to retrieve.
|
|
* @param string $passphrase A passphrase with which this note was
|
|
* supposed to be encrypted.
|
|
*
|
|
* @return array The note.
|
|
*/
|
|
public static function getMemo($notepad, $noteId, $passphrase = null)
|
|
{
|
|
$storage = $GLOBALS['injector']->getInstance('Mnemo_Factory_Driver')->create($notepad);
|
|
return $storage->get($noteId, $passphrase);
|
|
}
|
|
|
|
/**
|
|
* Get preview text for a note (the first 20 lines or so).
|
|
*
|
|
* @param array $note The note array
|
|
*
|
|
* @return string A few lines of the note for previews or tooltips.
|
|
*/
|
|
public static function getNotePreview($note)
|
|
{
|
|
$body = $note['body'];
|
|
if ($body instanceof Mnemo_Exception) {
|
|
$body = $body->getMessage();
|
|
}
|
|
$lines = explode("\n", wordwrap($body));
|
|
return implode("\n", array_splice($lines, 0, 20));
|
|
}
|
|
|
|
/**
|
|
* Lists all notepads a user has access to.
|
|
*
|
|
* This method takes the $conf['share']['hidden'] setting into account. If
|
|
* this setting is enabled, even if requesting permissions different than
|
|
* SHOW, it will only return calendars that the user owns or has SHOW
|
|
* permissions for. For checking individual calendar's permissions, use
|
|
* hasPermission() instead.
|
|
*
|
|
* @param boolean $owneronly Only return memo lists that this user owns?
|
|
* Defaults to false.
|
|
* @param integer $permission The permission to filter notepads by.
|
|
*
|
|
* @return array The memo lists.
|
|
*/
|
|
public static function listNotepads($owneronly = false,
|
|
$permission = Horde_Perms::SHOW)
|
|
{
|
|
if ($owneronly && !$GLOBALS['registry']->getAuth()) {
|
|
return array();
|
|
}
|
|
if ($owneronly || empty($GLOBALS['conf']['share']['hidden'])) {
|
|
try {
|
|
$notepads = $GLOBALS['mnemo_shares']->listShares(
|
|
$GLOBALS['registry']->getAuth(),
|
|
array('perm' => $permission,
|
|
'attributes' => $owneronly ? $GLOBALS['registry']->getAuth() : null,
|
|
'sort_by' => 'name'));
|
|
} catch (Horde_Share_Exception $e) {
|
|
Horde::log($e->getMessage(), 'ERR');
|
|
return array();
|
|
}
|
|
} else {
|
|
try {
|
|
$notepads = $GLOBALS['mnemo_shares']->listShares(
|
|
$GLOBALS['registry']->getAuth(),
|
|
array('perm' => $permission,
|
|
'attributes' => $GLOBALS['registry']->getAuth(),
|
|
'sort_by' => 'name'));
|
|
} catch (Horde_Share_Exception $e) {
|
|
Horde::log($e);
|
|
return array();
|
|
}
|
|
$display_notepads = @unserialize($GLOBALS['prefs']->getValue('display_notepads'));
|
|
if (is_array($display_notepads)) {
|
|
foreach ($display_notepads as $id) {
|
|
try {
|
|
$notepad = $GLOBALS['mnemo_shares']->getShare($id);
|
|
if ($notepad->hasPermission($GLOBALS['registry']->getAuth(), $permission)) {
|
|
$notepads[$id] = $notepad;
|
|
}
|
|
} catch (Horde_Exception_NotFound $e) {
|
|
} catch (Horde_Share_Exception $e) {
|
|
Horde::log($e);
|
|
return array();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $notepads;
|
|
}
|
|
|
|
/**
|
|
* Returns the default notepad for the current user at the specified
|
|
* permissions level.
|
|
*
|
|
* @param integer $permission Horde_Perms constant for permission level
|
|
* required.
|
|
*
|
|
* @return string The notepad identifier, or null if none.
|
|
*/
|
|
public static function getDefaultNotepad($permission = Horde_Perms::SHOW)
|
|
{
|
|
$notepads = self::listNotepads(false, $permission);
|
|
|
|
$default_notepad = $GLOBALS['prefs']->getValue('default_notepad');
|
|
if (isset($notepads[$default_notepad])) {
|
|
return $default_notepad;
|
|
}
|
|
|
|
$default_notepad = $GLOBALS['injector']
|
|
->getInstance('Mnemo_Factory_Notepads')
|
|
->create()
|
|
->getDefaultShare();
|
|
|
|
if (!isset($notepads[$default_notepad])) {
|
|
reset($notepads);
|
|
$default_notepad = key($notepads);
|
|
}
|
|
|
|
$GLOBALS['prefs']->setValue('default_notepad', $default_notepad);
|
|
return $default_notepad;
|
|
}
|
|
|
|
/**
|
|
* Returns the label to be used for a notepad.
|
|
*
|
|
* Attaches the owner name of shared notepads if necessary.
|
|
*
|
|
* @param Horde_Share_Object A notepad.
|
|
*
|
|
* @return string The notepad's label.
|
|
*/
|
|
public static function getLabel($notepad)
|
|
{
|
|
$label = $notepad->get('name');
|
|
if ($notepad->get('owner') &&
|
|
$notepad->get('owner') != $GLOBALS['registry']->getAuth()) {
|
|
$label .= ' [' . $GLOBALS['registry']->convertUsername($notepad->get('owner'), false) . ']';
|
|
}
|
|
return $label;
|
|
}
|
|
|
|
/**
|
|
* Returns the real name, if available, of a user.
|
|
*
|
|
* @return string The real name
|
|
*/
|
|
public static function getUserName($uid)
|
|
{
|
|
static $names = array();
|
|
|
|
if (!isset($names[$uid])) {
|
|
$ident = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($uid);
|
|
$ident->setDefault($ident->getDefault());
|
|
$names[$uid] = $ident->getValue('fullname');
|
|
if (empty($names[$uid])) {
|
|
$names[$uid] = $uid;
|
|
}
|
|
}
|
|
|
|
return $names[$uid];
|
|
}
|
|
|
|
/**
|
|
* Comparison function for sorting notes by description.
|
|
*
|
|
* @param array $a Note one.
|
|
* @param array $b Note two.
|
|
*
|
|
* @return integer 1 if memo one is greater, -1 if memo two is greater; 0
|
|
* if they are equal.
|
|
*/
|
|
protected static function _sortByDesc($a, $b)
|
|
{
|
|
return strcoll($a['desc'], $b['desc']);
|
|
}
|
|
|
|
/**
|
|
* Comparison function for reverse sorting notes by description.
|
|
*
|
|
* @param array $a Note one.
|
|
* @param array $b Note two.
|
|
*
|
|
* @return integer -1 if note one is greater, 1 if note two is greater; 0
|
|
* if they are equal.
|
|
*/
|
|
protected static function _rsortByDesc($a, $b)
|
|
{
|
|
return self::_sortByDesc($b, $a);
|
|
}
|
|
|
|
/**
|
|
* Comparison function for sorting notes by notepad name.
|
|
*
|
|
* @param array $a Note one.
|
|
* @param array $b Note two.
|
|
*
|
|
* @return integer 1 if note one is greater, -1 if note two is greater;
|
|
* 0 if they are equal.
|
|
*/
|
|
protected static function _sortByNotepad($a, $b)
|
|
{
|
|
$aowner = $a['memolist_id'];
|
|
$bowner = $b['memolist_id'];
|
|
|
|
$ashare = $GLOBALS['mnemo_shares']->getShare($aowner);
|
|
$bshare = $GLOBALS['mnemo_shares']->getShare($bowner);
|
|
|
|
if ($aowner != $ashare->get('owner')) {
|
|
$aowner = $ashare->get('name');
|
|
}
|
|
if ($bowner != $bshare->get('owner')) {
|
|
$bowner = $bshare->get('name');
|
|
}
|
|
|
|
return strcoll($aowner, $bowner);
|
|
}
|
|
|
|
/**
|
|
* Comparison function for reverse sorting notes by notepad name.
|
|
*
|
|
* @param array $a Note one.
|
|
* @param array $b Note two.
|
|
*
|
|
* @return integer -1 if note one is greater, 1 if note two is greater;
|
|
* 0 if they are equal.
|
|
*/
|
|
protected static function _rsortByNotepad($a, $b)
|
|
{
|
|
return self::_sortByNotepad($b, $a);
|
|
}
|
|
|
|
/**
|
|
* Comparison function for sorting notes by modification date.
|
|
*
|
|
* @param array $a Note one.
|
|
* @param array $b Note two.
|
|
*
|
|
* @return integer 1 if note one is greater, -1 if note two is greater;
|
|
* 0 if they are equal.
|
|
*/
|
|
protected static function _sortByModDate($a, $b)
|
|
{
|
|
// Get notes` history
|
|
$history = $GLOBALS['injector']->getInstance('Horde_History');
|
|
|
|
$guidA = 'mnemo:' . $a['memolist_id'] . ':' . $a['uid'];
|
|
$guidB = 'mnemo:' . $b['memolist_id'] . ':' . $b['uid'];
|
|
|
|
// Gets the timestamp of the most recent modification to the note
|
|
$modDateA = $history->getActionTimestamp($guidA, 'modify');
|
|
$modDateB = $history->getActionTimestamp($guidB, 'modify');
|
|
|
|
// If the note hasn't been modified, get the creation timestamp
|
|
if ($modDateA == 0) {
|
|
$modDateA = $history->getActionTimestamp($guidA, 'add');
|
|
}
|
|
if ($modDateB == 0) {
|
|
$modDateB = $history->getActionTimestamp($guidB, 'add');
|
|
}
|
|
if ($modDateA == $modDateB) {
|
|
return 0;
|
|
}
|
|
|
|
return ($modDateA > $modDateB) ? 1 : -1;
|
|
}
|
|
|
|
/**
|
|
* Comparison function for reverse sorting notes by modification date.
|
|
*
|
|
* @param array $a Note one.
|
|
* @param array $b Note two.
|
|
*
|
|
* @return integer -1 if note one is greater, 1 if note two is greater,
|
|
* 0 if they are equal.
|
|
*/
|
|
protected static function _rsortByModDate($a, $b)
|
|
{
|
|
return self::_sortByModDate($b, $a);
|
|
}
|
|
|
|
/**
|
|
* Returns the specified permission for the current user.
|
|
*
|
|
* @param string $permission A permission, currently only 'max_notes'.
|
|
*
|
|
* @return mixed The value of the specified permission.
|
|
*/
|
|
public static function hasPermission($permission)
|
|
{
|
|
$perms = $GLOBALS['injector']->getInstance('Horde_Perms');
|
|
|
|
if (!$perms->exists('mnemo:' . $permission)) {
|
|
return true;
|
|
}
|
|
$allowed = $perms->getPermissions('mnemo:' . $permission, $GLOBALS['registry']->getAuth());
|
|
if (is_array($allowed)) {
|
|
switch ($permission) {
|
|
case 'max_notes':
|
|
$allowed = max($allowed);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $allowed;
|
|
}
|
|
|
|
/**
|
|
* Returns a note's passphrase for symmetric encryption from the session
|
|
* cache.
|
|
*
|
|
* @param string $id A note id.
|
|
*
|
|
* @return string The passphrase, if set.
|
|
*/
|
|
public static function getPassphrase($id)
|
|
{
|
|
return (strlen($id))
|
|
? $GLOBALS['session']->get('mnemo', 'passphrase/' . $id)
|
|
: null;
|
|
}
|
|
|
|
/**
|
|
* Stores a note's passphrase for symmetric encryption in the session
|
|
* cache.
|
|
*
|
|
* @param string $id A note id.
|
|
* @param string $passphrase The note's passphrase.
|
|
*
|
|
* @return boolean True
|
|
*/
|
|
public static function storePassphrase($id, $passphrase)
|
|
{
|
|
global $session;
|
|
|
|
$session->set('mnemo', 'passphrase/' . $id, $passphrase, $session::ENCRYPT);
|
|
}
|
|
|
|
/**
|
|
* Initial app setup code.
|
|
*
|
|
* Defines the following $GLOBALS (@TODO these should use the injector)
|
|
* mnemo_shares
|
|
* display_notepads
|
|
*/
|
|
public static function initialize()
|
|
{
|
|
$GLOBALS['mnemo_shares'] = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Share')->create();
|
|
|
|
// Update the preference for which notepads to display. If the
|
|
// user doesn't have any selected notepads for view then fall
|
|
// back to some available notepad.
|
|
$GLOBALS['display_notepads'] = unserialize($GLOBALS['prefs']->getValue('display_notepads'));
|
|
if (($actionID = Horde_Util::getFormData('actionID')) !== null) {
|
|
$notepadId = Horde_Util::getFormData('display_notepad');
|
|
switch ($actionID) {
|
|
case 'add_displaylist':
|
|
if (!in_array($notepadId, $GLOBALS['display_notepads'])) {
|
|
$GLOBALS['display_notepads'][] = $notepadId;
|
|
}
|
|
break;
|
|
case 'remove_displaylist':
|
|
if (in_array($notepadId, $GLOBALS['display_notepads'])) {
|
|
$key = array_search($notepadId, $GLOBALS['display_notepads']);
|
|
unset($GLOBALS['display_notepads'][$key]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure all notepads exist now, to save on checking later.
|
|
$_temp = ($GLOBALS['display_notepads']) ? $GLOBALS['display_notepads'] : array();
|
|
|
|
$_all = self::listNotepads();
|
|
$GLOBALS['display_notepads'] = array();
|
|
foreach ($_temp as $id) {
|
|
if (isset($_all[$id])) {
|
|
$GLOBALS['display_notepads'][] = $id;
|
|
}
|
|
}
|
|
|
|
// All notepads for guests.
|
|
if (!count($GLOBALS['display_notepads']) &&
|
|
!$GLOBALS['registry']->getAuth()) {
|
|
$GLOBALS['display_notepads'] = array_keys($_all);
|
|
}
|
|
|
|
/* If the user doesn't own a notepad, create one. */
|
|
$notepads = $GLOBALS['injector']->getInstance('Mnemo_Factory_Notepads')
|
|
->create();
|
|
if (($new_default = $notepads->ensureDefaultShare()) !== null) {
|
|
$GLOBALS['display_notepads'][] = $new_default;
|
|
$GLOBALS['prefs']->setValue('default_notepad', $new_default);
|
|
}
|
|
|
|
$GLOBALS['prefs']->setValue('display_notepads', serialize($GLOBALS['display_notepads']));
|
|
}
|
|
|
|
/**
|
|
* Returns the notepads that should be used for syncing.
|
|
*
|
|
* @param boolean $prune Remove notepads ids from the sync list that no
|
|
* longer exist. The values are pruned *after* the
|
|
* results are passed back to the client to give
|
|
* sync clients a chance to remove their entries.
|
|
*
|
|
* @return array An array of notepad ids.
|
|
*/
|
|
static public function getSyncNotepads($prune = false)
|
|
{
|
|
$haveRemoved = false;
|
|
$cs = unserialize($GLOBALS['prefs']->getValue('sync_notepads'));
|
|
// Ensure we have an actual non-empty value for any entry.
|
|
$cs = array_filter($cs);
|
|
if (!empty($cs)) {
|
|
if ($prune) {
|
|
$notepads = self::listNotepads(true, Horde_Perms::DELETE);
|
|
$cscopy = array_flip($cs);
|
|
foreach ($cs as $c) {
|
|
if (empty($notepads[$c])) {
|
|
unset($cscopy[$c]);
|
|
$haveRemoved = true;
|
|
}
|
|
}
|
|
if ($haveRemoved) {
|
|
$GLOBALS['prefs']->setValue('sync_notepads', serialize(array_flip($cscopy)));
|
|
}
|
|
}
|
|
return $cs;
|
|
}
|
|
|
|
if ($cs = self::getDefaultNotepad(Horde_Perms::DELETE)) {
|
|
return array($cs);
|
|
}
|
|
|
|
return array();
|
|
}
|
|
|
|
}
|