Allow to change task due dates from the ajax interface.
}
var midnight = this.parseDate(date),
+ resizable = event.value.pe && (Object.isUndefined(event.value.vl) || event.value.vl),
innerDiv = new Element('div', { className: 'kronolithEventInfo' }),
minHeight = 0, height,
draggerTop, draggerBottom;
if (event.value.fi) {
- draggerTop = new Element('div', { id: event.value.nodeId + 'top', className: 'kronolithDragger kronolithDraggerTop' }).setStyle(style);
+ if (resizable) {
+ draggerTop = new Element('div', { id: event.value.nodeId + 'top', className: 'kronolithDragger kronolithDraggerTop' }).setStyle(style);
+ div.addClassName('kronolithFirst');
+ }
} else {
innerDiv.setStyle({ top: 0 });
}
if (event.value.la) {
- draggerBottom = new Element('div', { id: event.value.nodeId + 'bottom', className: 'kronolithDragger kronolithDraggerBottom' }).setStyle(style);
+ if (resizable) {
+ draggerBottom = new Element('div', { id: event.value.nodeId + 'bottom', className: 'kronolithDragger kronolithDraggerBottom' }).setStyle(style);
+ div.addClassName('kronolithLast');
+ }
} else {
innerDiv.setStyle({ bottom: 0 });
}
if (draggerBottom) {
minHeight += draggerBottom.getHeight();
}
+ if (!minHeight) {
+ minHeight = parseInt(innerDiv.getStyle('lineHeight'))
+ + (parseInt(innerDiv.getStyle('paddingTop')) || 0)
+ + (parseInt(innerDiv.getStyle('paddingBottom')) || 0);
+ }
div.setStyle({ height: Math.max(Math.round(event.value.start.getElapsed(event.value.end) / 60000) * this[storage].height / 60 - this[storage].spacing | 0, minHeight) + 'px' });
if (event.value.pe) {
*/
protected function _saveEvent($event)
{
- $result = $this->_signedResponse($event->calendarType . '|' . $event->calendar);
+ $result = $this->_signedResponse($this->_vars->cal ? $this->_vars->cal : ($event->calendarType . '|' . $event->calendar));
try {
$event->save();
$end = new Horde_Date($this->_vars->view_end);
}
/**
+ * Saves an (existing) event in the backend.
+ *
+ * @param Kronolith_Event_Horde $event The event to save.
+ *
+ * @return integer The event id.
+ * @throws Horde_Exception
+ */
+ public function saveEvent($event)
+ {
+ if (!isset($this->api)) {
+ list($this->api, $category) = explode('/', $this->calendar, 2);
+ }
+ $this->_params['registry']->call($this->api . '/saveTimeObject', array($event->timeobject));
+ }
+
+ /**
* @todo: implement getTimeObject in timeobjects API.
* @throws Kronolith_Exception
* @throws Horde_Exception_NotFound
*/
public function getDriver()
{
- return Kronolith::getDriver(null, $this->calendar);
+ return Kronolith::getDriver(str_replace('Kronolith_Event_', '', get_class($this)), $this->calendar);
}
/**
}
/**
+ * Imports a backend specific event object.
+ *
+ * @param mixed $eventObject Backend specific event object that this
+ * object will represent.
+ */
+ public function fromDriver($event)
+ {
+ }
+
+ /**
+ * Prepares this event to be saved to the backend.
+ */
+ public function toDriver()
+ {
+ }
+
+ /**
* Exports this event in iCalendar format.
*
* @param Horde_iCalendar &$calendar A Horde_iCalendar object that acts as
* - fg: foreground color
* - pe: edit permissions?
* - pd: delete permissions?
+ * - vl: variable, i.e. editable length?
* - a: alarm text or minutes
* - r: recurrence type (Horde_Date_Recurrence::RECUR_* constant) or json
* representation of Horde_Date_Recurrence object.
if (!$this->private ||
$this->creator == $GLOBALS['registry']->getAuth()) {
- $link .= $this->getEditUrl(
+ $url = $this->getEditUrl(
array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'),
- 'url' => $from_url))
- ->link(array('title' => sprintf(_("Edit %s"), $event_title),
- 'class' => 'iconEdit'))
- . Horde::fullSrcImg('edit-' . $icon_color . '.png',
- array('attr' => array('alt' => _("Edit"))))
- . '</a>';
+ 'url' => $from_url));
+ if ($url) {
+ $link .= $url->link(array('title' => sprintf(_("Edit %s"), $event_title),
+ 'class' => 'iconEdit'))
+ . Horde::fullSrcImg('edit-' . $icon_color . '.png',
+ array('attr' => array('alt' => _("Edit"))))
+ . '</a>';
+ }
}
if ($this->hasPermission(Horde_Perms::DELETE)) {
- $link .= $this->getDeleteUrl(
+ $url = $this->getDeleteUrl(
array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'),
- 'url' => $from_url))
- ->link(array('title' => sprintf(_("Delete %s"), $event_title),
- 'class' => 'iconDelete'))
- . Horde::fullSrcImg('delete-' . $icon_color . '.png',
- array('attr' => array('alt' => _("Delete"))))
- . '</a>';
+ 'url' => $from_url));
+ if ($url) {
+ $link .= $url->link(array('title' => sprintf(_("Delete %s"), $event_title),
+ 'class' => 'iconDelete'))
+ . Horde::fullSrcImg('delete-' . $icon_color . '.png',
+ array('attr' => array('alt' => _("Delete"))))
+ . '</a>';
+ }
}
}
protected $_link;
/**
+ * The link to edit this event.
+ *
+ * @var string
+ */
+ protected $_editLink;
+
+ /**
+ * The link to delete this event.
+ *
+ * @var string
+ */
+ protected $_deleteLink;
+
+ /**
* The link to this event in the ajax interface.
*
* @var string
*/
- protected $_ajax_link;
+ protected $_ajaxLink;
/**
* Any parameters to identify the object in the other Horde application.
protected $_params;
/**
+ * A bitmask of permissions the current user has on this object.
+ *
+ * @var integer
+ */
+ protected $_permissions;
+
+ /**
+ * Whether this event has a variable length.
+ *
+ * @boolean
+ */
+ protected $_variableLength;
+
+ /**
+ * Time object hash.
+ *
+ * @array
+ */
+ public $timeobject;
+
+ /**
* Constructor.
*
* @param Kronolith_Driver $driver The backend driver that this event is
parent::__construct($driver, $eventObject);
}
+ /**
+ * Imports a backend specific event object.
+ *
+ * @param array $event Backend specific event object that this object
+ * will represent.
+ */
public function fromDriver($event)
{
$eventStart = new Horde_Date($event['start']);
$this->status = Kronolith::STATUS_FREE;
$this->_params = $event['params'];
$this->_link = !empty($event['link']) ? $event['link'] : null;
- $this->_ajax_link = !empty($event['ajax_link']) ? $event['ajax_link'] : null;
+ $this->_editLink = !empty($event['edit_link']) ? $event['edit_link'] : null;
+ $this->_deleteLink = !empty($event['delete_link']) ? $event['delete_link'] : null;
+ $this->_ajaxLink = !empty($event['ajax_link']) ? $event['ajax_link'] : null;
$this->_backgroundColor = Kronolith::backgroundColor($event);
$this->_foregroundColor = Kronolith::foregroundColor($event);
$recurrence->setRecurType($event['recurrence']['type']);
if (isset($event['recurrence']['end'])) {
- $recurrence->setRecurEnd($event['recurrence']['end']);
+ $recurrence->setRecurEnd(new Horde_Date($event['recurrence']['end']));
}
if (isset($event['recurrence']['interval'])) {
$recurrence->setRecurInterval($event['recurrence']['interval']);
}
if (isset($event['recurrence']['exceptions'])) {
foreach ($event['recurrence']['exceptions'] as $exception) {
- $recurrence->addException(new Horde_Date($exception));
+ $recurrence->addException($exception);
}
}
$this->recurrence = $recurrence;
}
+
+ if (isset($event['permissions'])) {
+ $this->_permissions = $event['permissions'];
+ }
+ if (isset($event['variable_length'])) {
+ $this->_variableLength = $event['variable_length'];
+ }
+
$this->initialized = true;
$this->stored = true;
}
/**
+ * Prepares this event to be saved to the backend.
+ */
+ public function toDriver()
+ {
+ $this->timeobject = array(
+ 'id' => substr($this->id, strlen($this->_api) + 1),
+ 'icon' => $this->icon,
+ 'title' => $this->title,
+ 'description' => $this->description,
+ 'start' => $this->start->format('Y-m-d\TH:i:s'),
+ 'end' => $this->end->format('Y-m-d\TH:i:s'),
+ 'params' => $this->_params,
+ 'link' => $this->_link,
+ 'ajax_link' => $this->_ajaxLink,
+ 'permissions' => $this->_permissions,
+ 'variable_length' => $this->_variableLength);
+ if ($this->recurs()) {
+ $this->timeobject['recurrence'] = array('type' => $this->recurrence->getRecurType());
+ if ($end = $this->recurrence->getRecurEnd()) {
+ $this->timeobject['recurrence']['end'] = $end->format('Y-m-d\TH:i:s');
+ }
+ if ($interval = $this->recurrence->getRecurInterval()) {
+ $this->timeobject['recurrence']['interval'] = $interval;
+ }
+ if ($count = $this->recurrence->getRecurCount()) {
+ $this->timeobject['recurrence']['count'] = $count;
+ }
+ if ($days = $this->recurrence->getRecurOnDays()) {
+ $this->timeobject['recurrence']['days'] = $days;
+ }
+ if ($count = $this->recurrence->getRecurCount()) {
+ $this->timeobject['recurrence']['count'] = $count;
+ }
+ if ($exceptions = $this->recurrence->getExceptions()) {
+ $this->timeobject['recurrence']['exceptions'] = $exceptions;
+ }
+ }
+ }
+
+ /**
* Encapsulates permissions checking.
*
* @param integer $permission The permission to check for.
*/
public function hasPermission($permission, $user = null)
{
+ if (isset($this->_permissions)) {
+ return (bool)($this->_permissions & $permission);
+ }
+
switch ($permission) {
case Horde_Perms::SHOW:
case Horde_Perms::READ:
return null;
}
$url = clone $this->_link;
- return $this->_link->setRaw($full);
+ return $url->setRaw($full);
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return Horde_Url
+ */
+ public function getEditUrl($params = array(), $full = false)
+ {
+ if (empty($this->_editLink)) {
+ return null;
+ }
+ $url = clone $this->_editLink;
+ if (isset($params['url'])) {
+ $url->add('url', $params['url']);
+ }
+ return $url->setRaw($full);
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return Horde_Url
+ */
+ public function getDeleteUrl($params = array(), $full = false)
+ {
+ if (empty($this->_deleteLink)) {
+ return null;
+ }
+ $url = clone $this->_deleteLink;
+ if (isset($params['url'])) {
+ $url->add('url', $params['url']);
+ }
+ return $url->setRaw($full);
}
/**
public function toJson($allDay = null, $full = false, $time_format = 'H:i')
{
$json = parent::toJson($allDay, $full, $time_format);
- if ($this->_ajax_link) {
- $json->aj = $this->_ajax_link;
+ if ($this->_ajaxLink) {
+ $json->aj = $this->_ajaxLink;
} else {
$json->ln = (string)$this->getViewUrl(array(), true);
}
+ if (isset($this->_variableLength)) {
+ $json->vl = $this->_variableLength;
+ }
return $json;
}
*/
public $calendarType = 'remote';
+ /**
+ * Imports a backend specific event object.
+ *
+ * @param Horde_iCalendar_vEvent Backend specific event object that this
+ * object will represent.
+ */
public function fromDriver($vEvent)
{
$this->fromiCalendar($vEvent);
$this->stored = true;
}
+ /**
+ * Prepares this event to be saved to the backend.
+ */
public function toDriver()
{
return $this->toiCalendar();
$this->_tags = Kronolith::getTagger()->getTags($this->uid, 'event');
}
+ /**
+ * Imports a backend specific event object.
+ *
+ * @param array $event Backend specific event object that this object
+ * will represent.
+ */
public function fromDriver($event)
{
$this->id = $event['uid'];
$this->stored = true;
}
+ /**
+ * Prepares this event to be saved to the backend.
+ */
public function toDriver()
{
$event = array();
*/
private $_properties = array();
+ /**
+ * Imports a backend specific event object.
+ *
+ * @param array $event Backend specific event object that this object
+ * will represent.
+ */
public function fromDriver($SQLEvent)
{
$driver = $this->getDriver();
$this->stored = true;
}
+ /**
+ * Prepares this event to be saved to the backend.
+ */
public function toDriver()
{
$driver = $this->getDriver();
}
}
+ /**
+ * Imports a backend specific event object.
+ *
+ * @param array $event Backend specific event object that this object
+ * will represent.
+ */
public function fromDriver($SQLEvent)
{
$driver = $this->getDriver();
$this->stored = true;
}
+ /**
+ * Prepares this event to be saved to the backend.
+ */
public function toDriver()
{
$driver = $this->getDriver();
overflow: hidden;
line-height: 16px;
}
-div.kronolithEditable:hover .kronolithEventInfo,
-div.kronolithEvent.kronolithSelected .kronolithEventInfo {
+div.kronolithEditable.kronolithFirst:hover .kronolithEventInfo,
+div.kronolithEvent.kronolithFirst.kronolithSelected .kronolithEventInfo {
top: 7px;
+}
+div.kronolithEditable.kronolithLast:hover .kronolithEventInfo,
+div.kronolithEvent.kronolithLast.kronolithSelected .kronolithEventInfo {
bottom: 7px;
}
div.kronolithEditable:hover .kronolithDragger,
'end' => $due_date,
'category' => $task->category,
'color' => $allowed_tasklists[$task->tasklist]->get('color'),
+ 'permissions' => $GLOBALS['nag_shares']->getPermissions($task->tasklist, $GLOBALS['registry']->getAuth()),
+ 'variable_length' => false,
'params' => array('task' => $task->id,
'tasklist' => $task->tasklist),
- 'link' => Horde_Util::addParameter(Horde::applicationUrl('view.php', true), array('tasklist' => $task->tasklist, 'task' => $task->id)),
+ 'link' => Horde::applicationUrl('view.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id)),
+ 'edit_link' => Horde::applicationUrl('task.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id, 'actionID' => 'modify_task')),
+ 'delete_link' => Horde::applicationUrl('task.php', true)->add(array('tasklist' => $task->tasklist, 'task' => $task->id, 'actionID' => 'delete_task')),
'ajax_link' => 'task:' . $task->tasklist . ':' . $task->id);
}
}
/**
+ * Saves properties of a time object back to the task that it represents.
+ *
+ * At the moment only the title, description and due date are saved.
+ *
+ * @param array $timeobject A time object hash.
+ */
+ public function saveTimeObject($timeobject)
+ {
+ Horde::logMessage(print_r($timeobject, true));
+ $storage = Nag_Driver::singleton();
+ $existing = $storage->get($timeobject['id']);
+ if (is_a($existing, 'PEAR_Error')) {
+ return $existing;
+ }
+ if (!array_key_exists($existing->tasklist,
+ Nag::listTasklists(false, Horde_Perms::EDIT))) {
+ return PEAR::raiseError(_("Permission Denied"));
+ }
+ $storage = Nag_Driver::singleton($existing->tasklist);
+
+ if (isset($timeobject['start'])) {
+ $due = new Horde_Date($timeobject['start']);
+ $due = $due->timestamp();
+ } else {
+ $due = $existing->due;
+ }
+
+ return $storage->modify(
+ $timeobject['id'],
+ isset($timeobject['title']) ? $timeobject['title'] : $existing->name,
+ isset($timeobject['description']) ? $timeobject['description'] : $existing->desc,
+ $existing->start,
+ $due,
+ $existing->priority,
+ $existing->estimate,
+ $existing->completed,
+ $existing->category,
+ $existing->alarm,
+ $existing->methods,
+ $existing->parent_id,
+ $existing->private,
+ $existing->owner,
+ $existing->assignee,
+ $existing->completed_date,
+ $existing->tasklist);
+ }
+
+ /**
* Lists alarms for a given moment.
*
* @param integer $time The time to retrieve alarms for.
function get($taskId)
{
/* Build the SQL query. */
- $query = sprintf('SELECT * FROM %s WHERE task_owner = ? and task_id = ?',
+ $query = sprintf('SELECT * FROM %s WHERE task_id = ?',
$this->_params['table']);
- $values = array($this->_tasklist, $taskId);
+ $values = array($taskId);
/* Log the query at a DEBUG log level. */
Horde::logMessage(sprintf('Nag_Driver_Sql::get(): %s VALUES: %s', $query, print_r($values, true)), 'DEBUG');
$this->addHidden('', 'actionID', 'text', true);
$this->addHidden('', 'task_id', 'text', false);
$this->addHidden('', 'old_tasklist', 'text', false);
+ $this->addHidden('', 'url', 'text', false);
$this->addVariable(_("Name"), 'name', 'text', true);
if (!$GLOBALS['prefs']->isLocked('default_tasklist') &&
$task_url_task = Horde_Util::addParameter($task_url_list[$this->tasklist], 'task', $this->id);
$this->complete_link = Horde_Util::addParameter($task_url_task, 'actionID', 'complete_task');
$this->edit_link = Horde_Util::addParameter($task_url_task, 'actionID', 'modify_task');
- $this->delete_link = Horde_Util::addParameter($task_url_task, 'actionID', 'delete_tasks');
+ $this->delete_link = Horde_Util::addParameter($task_url_task, 'actionID', 'delete_task');
}
/**
}
}
- /* Return to the task list. */
- header('Location: ' . Horde::applicationUrl('list.php', true));
+ /* Return to the last page or to the task list. */
+ if ($url = Horde_Util::getFormData('url')) {
+ header('Location: ' . $url);
+ } else {
+ header('Location: ' . Horde::applicationUrl('list.php', true));
+ }
exit;
}
$vars = new Horde_Variables($task->toHash());
$vars->set('actionID', 'save_task');
$vars->set('old_tasklist', $task->tasklist);
+ $vars->set('url', Horde_Util::getFormData('url'));
$form = new Nag_TaskForm($vars, sprintf(_("Edit: %s"), $task->name), $share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::DELETE));
break;
}
$notification->push(sprintf(_("There was a problem saving the task: %s."), $result->getMessage()), 'horde.error');
} else {
$notification->push(sprintf(_("Saved %s."), $info['name']), 'horde.success');
- /* Return to the task list. */
- header('Location: ' . Horde::applicationUrl('list.php', true));
+ /* Return to the last page or to the task list. */
+ if ($url = Horde_Util::getFormData('url')) {
+ header('Location: ' . $url);
+ } else {
+ header('Location: ' . Horde::applicationUrl('list.php', true));
+ }
exit;
}
break;
-case 'delete_tasks':
+case 'delete_task':
/* Delete the task if we're provided with a valid task ID. */
_delete(Horde_Util::getFormData('task'), Horde_Util::getFormData('tasklist'));
}
}
if ($share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::DELETE)) {
- $links[] = Horde::widget(Horde::applicationUrl(Horde_Util::addParameter($taskurl, 'actionID', 'delete_tasks')), _("Delete"), 'smallheader', '', $prefs->getValue('delete_opt') ? 'return window.confirm(\'' . addslashes(_("Really delete this task?")) . '\');' : '', _("_Delete"));
+ $links[] = Horde::widget(Horde::applicationUrl(Horde_Util::addParameter($taskurl, 'actionID', 'delete_task')), _("Delete"), 'smallheader', '', $prefs->getValue('delete_opt') ? 'return window.confirm(\'' . addslashes(_("Really delete this task?")) . '\');' : '', _("_Delete"));
}
require NAG_TEMPLATES . '/common-header.inc';