From: Kristian Kræmmer Nielsen Date: Sun, 15 Aug 2010 01:51:47 +0000 (+0200) Subject: Seth is a "idea" organiser. X-Git-Url: https://git.jkkn.net/?a=commitdiff_plain;h=9095e2fcd0dd7d204c9e409630339361c99a90f3;p=seth.tv2.dk.git Seth is a "idea" organiser. It builds of a princip of keeping everything is simple objects and then organising these using their properties. This is done by allowing to create filters on the fly. Dragging between groups and filters are allowed and will automatically assign and attach properties to the elements. Hope you will have fun using this interface :-) /Kristian Kræmmer --- 9095e2fcd0dd7d204c9e409630339361c99a90f3 diff --git a/inc/Seth/Controller/Filter.php b/inc/Seth/Controller/Filter.php new file mode 100644 index 0000000..03efe4a --- /dev/null +++ b/inc/Seth/Controller/Filter.php @@ -0,0 +1,112 @@ + + */ +class Seth_Controller_Filter { + + private $_groupId = null; + + public function setGroupId($id) { + $this->_groupId = intval($id); + } + + private function _printSelect($id, $values, $selected=null) { + printf(''; + } + + /** Return all groups + */ + public static function getGroups() { + $db = DB_PDO::get('seth'); + $groups = $db->getHash('SELECT id, title FROM `group` ORDER BY title', + array(), 'id', 'title'); + return $groups; + } + + /** Return all filters for a given group + */ + public static function getFilters($groupId, Seth_Model_Filter $exclude) { + $db = DB_PDO::get('seth'); + $filters = $db->getHash('SELECT id, title FROM filter WHERE group_id=? ORDER BY title', + array($groupId), 'id', 'title'); + $filters = array(''=>'') + $filters; + if ($exclude && !$exclude->isNew()) { + unset($filters[$exclude->getId()]); + } + return $filters; + } + + public function printGroupSelect() { + $groups = self::getGroups(); + echo '
'; + $this->_printSelect('group_id', $groups, $this->_groupId); + echo '
' . "\n"; + } + + /** + * Method inserts "edit" links into I2_GUI_List for filters + */ + static public function insertEditLinks(array &$row, Seth_Model_Filter $filter) { + $row['title'] = sprintf('%s', + $filter->getId(), rawurlencode($filter->getTitle()), + htmlentities($filter->getTitle())); + $row['action'] = + '
'. + sprintf('Redigér | ', + $filter->getId()). + sprintf('Slet', + $filter->getId(), htmlentities(rawurlencode($filter->getTitle()))) + .'
'; + } + + static private function _param($key) { + return isset($_POST[$key]) ? $_POST[$key] : null; + } + + /** + * Delete filter + */ + private static function _remove($id) { + $delete = new Seth_Model_Filter(intval($id)); + if (!$delete->isNew()) { + $delete->delete(); + } + return array('ok'=>true); + } + + /** + * Process callbacks + */ + static public function process() { + $action = self::_param('action'); + + $rtn = null; + switch ($action) { + case 'remove': + $rtn = self::_remove(self::_param('id')); + default: + break; + } + + if (!is_null($rtn)) { + header('Content-Type: application/json'); + echo json_encode($rtn); + die(); + } + } + +} + + +?> diff --git a/inc/Seth/Controller/Group.php b/inc/Seth/Controller/Group.php new file mode 100644 index 0000000..f145db1 --- /dev/null +++ b/inc/Seth/Controller/Group.php @@ -0,0 +1,77 @@ + + */ +class Seth_Controller_Group { + + // Insert actions into I2_GUI_List + static public function insertActions(array &$row, Seth_Model_Group $group) { + $row['actions'] = + '
'. + sprintf('Omdøb | ', + $group->getId(), htmlentities(rawurlencode($group->getTitle()))) . + sprintf('Slet', + $group->getId(), htmlentities(rawurlencode($group->getTitle()))) . + '
'; + } + + /* Create group */ + private static function _new($title) { + $new = new Seth_Model_Group(); + $new->setTitle($title); + $new->save(); + return array('ok'=>true); + } + + /* Rename group */ + private static function _rename($id, $title) { + $rename = new Seth_Model_Group(intval($id)); + if ($rename->isNew()) { + return array('err'=>'Group no longer exists'); + } + $rename->setTitle($title); + $rename->save(); + return array('ok'=>true); + } + + /* Delete group */ + private static function _remove($id) { + $delete = new Seth_Model_Group(intval($id)); + if (!$delete->isNew()) { + $delete->delete(); + } + return array('ok'=>true); + } + + + static private function _param($key) { + return isset($_POST[$key]) ? $_POST[$key] : null; + } + + static public function process() { + $action = self::_param('action'); + + $rtn = null; + switch ($action) { + case 'new': + $rtn = self::_new(self::_param('title')); + break; + case 'rename': + $rtn = self::_rename(self::_param('id'), self::_param('title')); + break; + case 'remove': + $rtn = self::_remove(self::_param('id')); + default: + break; + } + + if (!is_null($rtn)) { + header('Content-Type: application/json'); + echo json_encode($rtn); + die(); + } + } + +} diff --git a/inc/Seth/Controller/List.php b/inc/Seth/Controller/List.php new file mode 100644 index 0000000..972cefe --- /dev/null +++ b/inc/Seth/Controller/List.php @@ -0,0 +1,397 @@ + + */ +class Seth_Controller_List { + + private $_viewId; + + /** + * Initializes new List controller + * + * @param integer $view View we are displaying + */ + public function __construct($viewId) { + $this->_viewId = $viewId; + } + + /** + * Create new element + * @param $obj array('title'=>, 'filter_id'=>, 'group_defined_as'=>) + */ + private function _newItem(array $obj) { + // create the new object + $elm = new Seth_Model_Element(); + // fill in the entered fields from the interface + $elm->setTitle(utf8_decode($obj['title'])); + + /* We must also match the group parameters */ + $gd = $obj['group_defined_as']; + if ($gd) { + foreach (unserialize(utf8_decode($gd)) as $key => $value) { + $elm->set($key, $value); + } + } + + $filter = new Seth_Model_Filter(intval($obj['filter_id'])); + if ($filter->isNew()) { + return array('err'=>'Filter no longer exists'); + } + + // Always set group id + $elm->setGroupId($filter->getGroupId()); + $elm->save(); + + // Set order index to be at bottom at the beginning + $order = new Seth_Model_Element_FilterOrder($filter->getId(), $elm->getId()); + $order->setOrderIndex(9223372036854775807); // max bigint + $order->save(); + + return array('new'=>true, 'list' => $this->_getList($filter)); + } + + /** Format a groupset title a bit nice */ + static private function _formatGroupTitle($group) { + $title = ''; + foreach ($group as $key => $value) { + if ($title != '') { + $title .= '; '; + } + // For now change field name by splitting _ and uppercasing first letters + $name = array(); + foreach (split('_', $key) as $part) { + $name[] = ucfirst($part); + } + $name = join(' ', $name); + if (is_null($value)) { + $value = '(ikke sat)'; + } + $title .= htmlentities(ucfirst($name)) . ': ' . htmlentities($value); + } + return utf8_encode($title); + } + + /** + * Place here warning (NOT IMPLEMENTED) + */ + static private function _formatPlaceHere($definition) { + return null; + } + + /** + * Adds some information for mouseover + */ + static private function _formatDescription(Seth_Model_Element $item, + $desc) { + // TODO: Implement a overlay for mouseover instead of : + + $text = ''; + if ($desc && $us=$desc->getUserStory()) { + $text .= $desc->getUserStory() . " \n\n"; + } + $text .= 'Prioritet: ' . $item->getPriority() . " \n"; + if ($cp=$item->getContactPerson()) { + $text .= 'Kontakt: ' . $cp . " \n"; + } + if ($ar=$item->getArea()) { + $text .= 'Område: ' . $ar . " \n"; + } + if ($as=$item->getAssignee()) { + $text .= 'Tildelt: ' . $as . " \n"; + } + + return $text; + } + + /** + * Returns a group definition (including filter requirements) + * + * All = requirements from filter are copied, if other type + * exists we will only allow creating/drag if group overrides them + * + * This is used for new elements created in the group/dragged there + * @param array $group Group elements optional + * @return string Serializd array that defines group or null if not able to do so + */ + static private function _getGroupDefinition(Seth_Model_filter $filter, array $group = array()) { + $define = array(); + $conds = $filter->getFilterByIncludeInherited(); + if ($conds) { + $conds = Seth_Model_Filter::cleanFilter($conds); + foreach ($conds as $cond) { + // all + if ($cond['cond'] == '=') { + $define[$cond['field']] = $cond['value']; + } elseif ($cond['cond'] == 'IS NULL') { + $define[$cond['field']] = null; + } else { + // must be in group + if (!isset($group[$cond['field']])) { + return null; // we cannot guess this value - no drag/create here + } + } + } + } + $define += $group; // add group by definition + + $define['group_id'] = $filter->getGroupId(); + + return utf8_encode(serialize($define)); + } + + /** Returns a filtered list by id + * @param mixed $listid Id or Seth_Model_Filter object + */ + static private function _getList($listid) { + // Start by finding the filter object + if (!$listid instanceof Seth_Model_Filter) { + $filter = new Seth_Model_Filter(intval($listid)); + if ($filter->isNew()) { + // failed + return array('err'=>'Filter/view no longer exists'); + } + } else { + $filter = $listid; + } + + $items = Seth_Model_Element::find($filter->getQueryConditions()); + $desc = Seth_Model_Element_Descriptions::multiple( + Object_Util::extract($items, 'id'), ORM::INDEX_BY_KEY); + + $groupby = $filter->getGroupByIncludeInherited(); + + $groups = array(); + $curGroup = null; + + // If we have no elements or are not grouping, items will appear in a + // standard group: + $define = self::_getGroupDefinition($filter); + $current = array( + 'title' => utf8_encode(empty($conds) ? 'Alle elementer' : 'Filtrede elementer'), + 'when_placing_here' => self::_formatPlaceHere($define), + 'defined_as' => $define, + 'elements' => array()); + + // Construct our response by grouping elements of the same group + foreach ($items as $no => $item) { + if ($groupby) { + $thisGroup = array(); + foreach ($groupby as $gby) { + $thisGroup[$gby] = $item->get($gby); + } + + if ($thisGroup !== $curGroup) { + if (!is_null($curGroup)) $groups[] = $current; + $define = self::_getGroupDefinition($filter, $thisGroup); + $current = array( + 'title' => self::_formatGroupTitle($thisGroup), + 'when_placing_here' => self::_formatPlaceHere($define), + 'defined_as' => $define, + 'elements' => array()); + $curGroup = $thisGroup; + } + } + + $current['elements'][] = array( + 'title' => utf8_encode($item->getTitle()), + 'id' => $item->getId(), + 'description' => utf8_encode(self::_formatDescription($item, + isset($desc[$no]) ? $desc[$no] : null))); + } + + $groups[] = $current; // always at least one empty group + + return array('groups'=>$groups); + } + + /** + * Stores new tab order (order of filters) + * + * @param integer $ViewId view_id + * @param array $order Ordered array of filter_ids + */ + private function _setFilterOrder($viewId, $order) { + if (!is_array($order)) { + $order = array(); // empty view + } + $view = new Seth_Model_View($viewId); + if ($view->isNew()) { + // failed + return array('err'=>'Filter/view no longer exists'); + } + $view->setFilterOrder($order); + $view->save(); + return array('save'=>true); + } + + /** + * Load filter order + * + * @param $viewId integer view id + * @return array of oredered filter ids as JSON + */ + public function getFilterOrder(Seth_Model_View $view) { + // Get titles + $filters = ORM::multiple(Seth_Model_Filter::TYPE, $view->getFilterOrder()); + $titles = array(); + foreach ($filters as $filter) { + $titles[] = array('id' => $filter->getId(), 'title' => utf8_encode($filter->getTitle())); // JSON must be UTF-8 + } + return json_encode($titles); + } + + /** + * Move an element to another filtered list + * + * @param integer $elementId Element to move + * @param integer $filterId Id of filter to obey + */ + static private function _moveToList($elementId, $filterId) { + $element = new Seth_Model_Element($elementId); + if ($element->isNew()) { + return array('err'=>'Element no longer exists'); + } + $filter = new Seth_Model_Filter($filterId); + if ($filter->isNew()) { + return array('err'=>'Filter no longer exists'); + } + + // just copy all = rows from the filter - fail if there is non = conditions + $conds = $filter->getFilterByIncludeInherited(); + if ($conds) { + $conds = Seth_Model_Filter::cleanFilter($conds); + foreach ($conds as $cond) { + if ($cond['cond'] != '=' && $cond['cond'] != 'IS NULL') { + return array('err'=>'Kan ikke flyttes her, filtret benytter andet end "=" krav!'); + } + if ($cond['cond'] == 'IS NULL') { + $element->set($cond['field'], null); + } else { + $element->set($cond['field'], $cond['value']); + } + } + } + + // always copy group id + $element->setGroupId($filter->getGroupId()); + + // save + $element->save(); + + return array('save'=>true); + } + + /** + * Moves an element to another location and/or group + * + * @param array $obj Array of ('element_id'=>, 'above_id'=>, 'below_id'=>, 'group_defined_as'=>, 'filter_id'=>) + */ + private function _moveElement(array $obj) { + + // 1) First check if all elements are alive + $element = new Seth_Model_Element(intval($obj['element_id'])); + if ($element->isNew()) { + return array('err'=>'Element no longer exists'); + } + + $filter = new Seth_Model_Filter(intval($obj['filter_id'])); + if ($filter->isNew()) { + return array('err'=>'Filter no longer exists'); + } + + // 2) Check and find above/below objects + $above = $below = null; + if (!empty($obj['above_id'])) { + $above = new Seth_Model_Element(intval($obj['above_id'])); + if ($above->isNew()) $above = null; + } + + if (!empty($obj['below_id'])) { + $below = new Seth_Model_Element(intval($obj['below_id'])); + if ($below->isNew()) $below = null; + } + + /* 3) Move element into the right group */ + $gd = $obj['group_defined_as']; + if ($gd) { + foreach (unserialize(utf8_decode($gd)) as $key => $value) { + $element->set($key, $value); + } + } + $element->save(); + + /** 3) The differcult part - try to place it between the two elements + * We only change the order_index in the filter many-to-many relation + */ + Seth_Model_Element_FilterOrder::placeBetween( + $filter, $element, $above, $below); + + return array('moved'=>true, 'list' => $this->_getList($filter)); + } + + /** + * Delete an element + * + * @param integer $id Element id + */ + static private function _deleteElement($id) { + $element = new Seth_Model_Element($id); + if ($element->isNew()) { + return array('err'=>'Element var allerede slettet.'); + } + $element->delete(); + return array('delete'=>true); + } + + private function _param($key) { + return isset($_POST[$key]) ? $_POST[$key] : null; + } + + public function process() { + $action = $this->_param('action'); + + $rtn = null; + switch ($action) { + case 'new': + // create new + $rtn = $this->_newItem($this->_param('obj')); + break; + case 'get': + // get a filtered list + $rtn = array('list' => $this->_getList($this->_param('listid'))); + break; + case 'setfilterorder': + // store new filter order in view + $rtn = $this->_setFilterOrder($this->_param('view_id'), $this->_param('order')); + break; + case 'move_to_list'; + // moves item to another list + $rtn = $this->_moveToList($this->_param('element_id'), $this->_param('filter_id')); + break; + case 'move_element'; + // moves item to another location and/or group + $rtn = $this->_moveElement($this->_param('obj')); + break; + case 'delete_element'; + // moves item to another list + $rtn = $this->_deleteElement($this->_param('element_id')); + default: + break; + } + + if (!is_null($rtn)) { + header('Content-Type: application/json'); + echo json_encode($rtn); + die(); + } + } + +} diff --git a/inc/Seth/Controller/Page.php b/inc/Seth/Controller/Page.php new file mode 100644 index 0000000..32d2817 --- /dev/null +++ b/inc/Seth/Controller/Page.php @@ -0,0 +1,36 @@ +<?php + +/** + * Makes up the general header for a Seth page + * @author Kristian Kræmmer Nielsen <jkkn@tv2.dk> + */ +class Seth_Controller_Page { + + static public function printTop($head='') { + $vendor = getSite('vendor'); + echo <<<EOH +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Seth - projekt organizer + + + + + + + $head + + +EOH; + } + + static public function printBottom() { + echo << + +EOF; + } + +} diff --git a/inc/Seth/Controller/View.php b/inc/Seth/Controller/View.php new file mode 100644 index 0000000..589fb10 --- /dev/null +++ b/inc/Seth/Controller/View.php @@ -0,0 +1,78 @@ + + */ +class Seth_Controller_View { + + // Insert actions into I2_GUI_List + static public function insertActions(array &$row, Seth_Model_View $view) { + $row['actions'] = + '
'. + sprintf('Omdøb | ', + $view->getId(), htmlentities(rawurlencode($view->getTitle()))) . + sprintf('Slet', + $view->getId(), htmlentities(rawurlencode($view->getTitle()))) . + '
'; + } + + /* Create view */ + private static function _new($title, $groupId) { + $new = new Seth_Model_View(); + $new->setTitle($title); + $new->setGroupId(intval($groupId)); + $new->save(); + return array('ok'=>true); + } + + /* Rename view */ + private static function _rename($id, $title) { + $rename = new Seth_Model_View(intval($id)); + if ($rename->isNew()) { + return array('err'=>'View no longer exists'); + } + $rename->setTitle($title); + $rename->save(); + return array('ok'=>true); + } + + /* Delete view */ + private static function _remove($id) { + $delete = new Seth_Model_View(intval($id)); + if (!$delete->isNew()) { + $delete->delete(); + } + return array('ok'=>true); + } + + + static private function _param($key) { + return isset($_POST[$key]) ? $_POST[$key] : null; + } + + static public function process() { + $action = self::_param('action'); + + $rtn = null; + switch ($action) { + case 'new': + $rtn = self::_new(self::_param('title'), self::_param('group_id')); + break; + case 'rename': + $rtn = self::_rename(self::_param('id'), self::_param('title')); + break; + case 'remove': + $rtn = self::_remove(self::_param('id')); + default: + break; + } + + if (!is_null($rtn)) { + header('Content-Type: application/json'); + echo json_encode($rtn); + die(); + } + } + +} diff --git a/inc/Seth/Element/Filter.php b/inc/Seth/Element/Filter.php new file mode 100755 index 0000000..83bd5ae --- /dev/null +++ b/inc/Seth/Element/Filter.php @@ -0,0 +1,85 @@ + + */ +class Seth_Element_Filter extends StraightForm_Element_Container { + + private $_title = ''; + private $_elmField; + private $_elmCondition; + private $_elmValue; + + /** + * New filter element + * @param string $title Title displayed above + * @param array $fields Array of fields to select + * @param array $conds Array of possible conditions + */ + public function __construct($title, array $fields, array $conds) { + $this->_title = $title; + $this->_elmField = StraightForm_BasicElements::select(null, $fields, key($fields)); + $this->_elmCondition = StraightForm_BasicElements::select(null, $conds, key($conds)); + $this->_elmValue = StraightForm_BasicElements::text(null, 30); + $this->_idField = $this->addElement($this->_elmField); + $this->_idCond = $this->addElement($this->_elmCondition); + $this->_idValue = $this->addElement($this->_elmValue); + } + + public function getHTML($id, $label = true) { + + if ($label && !empty($this->_title)) { + $html = sprintf('', + htmlentities($id), + htmlentities($this->_title), + !$this->isValid() ? ' *':'') . "\n
\n"; + } else { + $html = ''; + } + $html .= self::getSubHTML($this->_idField, $id, false); + $html .= self::getSubHTML($this->_idCond, $id, false); + $html .= self::getSubHTML($this->_idValue, $id, false); + $html .= '
'; + + return $html; + } + + public function isValid() { + if (!parent::isValid()) { + return false; + } + /* Value must not be set if field has not been selected */ + if (!$this->_elmField->getValue() && $this->_elmValue->getValue() != '') { + return false; + } + return true; + } + + public function getValue() { + if ($this->_elmField->getValue()) { + return + array('field' => $this->_elmField->getValue(), + 'cond' => $this->_elmCondition->getValue(), + 'value' => $this->_elmValue->getValue()); + } else { + return null; + } + } + + public function setValue($value) { + if (!is_null($value)) { + $this->_elmField->setValue($value['field']); + $this->_elmCondition->setValue($value['cond']); + $this->_elmValue->setValue($value['value']); + } else { + $this->_elmField->setValue(0); + $this->_elmConditon->setValue(0); + $this->_elmValue->setValue(''); + } + } + +} diff --git a/inc/Seth/Model/Element.php b/inc/Seth/Model/Element.php new file mode 100755 index 0000000..dc1ce23 --- /dev/null +++ b/inc/Seth/Model/Element.php @@ -0,0 +1,120 @@ + + * @version $Revision$ + * @access public + * @package Seth + */ +class Seth_Model_Element extends ORM_AbstractBase { + + const TYPE = __CLASS__; + + /** + * Create new and set defaults + */ + public function __construct($element_id = null) { + parent::__construct($element_id); + if (is_null($element_id)) { + $this->_fields['priority'] = 100; // default priority + $this->_fields['created_at'] = date('c'); + } + } + + /** + * Configuration of the ORM object + * + * @param ORM_Configuration $config + */ + public static function _ORMConfiguration(ORM_Configuration $config) { + $config->db = 'seth'; + $config->table = 'element'; + $config->autoIncrement = 'id'; +/* $config->type['deadline'] = ORM::TYPE_DATE; + $config->type['created_at'] = ORM::TYPE_DATE; + $config->type['completed_date'] = ORM::TYPE_DATE;*/ + $config->createGet(array('id')); + $config->createGetSet(array('title', 'group_id', 'priority', 'area', + 'contact_person', 'estimate_design', 'estimate_development', + 'actual_design', 'actual_development', 'approved', 'design', + 'contact_person_email', 'completed', 'deleted', 'bug', 'deadline', + 'created_at', 'sprint', 'completed_date', 'po_time', 'site', + 'ressource', 'product_owner', 'assignee')); + } + + + /** + * Return array of fields that may be filtered + */ + public static function getFilterFields() { + return + array('id', 'title', 'priority', 'area', + 'contact_person', 'estimate_design', 'estimate_development', + 'actual_design', 'actual_development', 'approved', 'design', + 'contact_person_email', 'completed', 'deleted', 'bug', 'deadline', + 'created_at', 'sprint', 'completed_date', 'po_time', 'site', + 'ressource', 'product_owner', 'assignee'); + } + + /** + * Allows to return a specific field + * @param string $field Name of field + * @return mixed value of field + */ + public function get($field) { + return $this->_fields[$field]; + } + + /** + * Allows to set a specific field + * @param string $field Name of field + * @param mixed value of field + */ + public function set($field, $value) { + $this->_fields[$field] = $value; + } + + /** + * Shortcut to find Seth_Element objects from database + * @param mixed $cond Optional SQL condition as string or ORM_Condition object + * @param array $params If condition is provided as string, optional array of parameters + * @return array Array of Seth_Element objects + */ + static function find($cond = null, $params = array()) { + return ORM::find(self::TYPE, $cond, $params); + } + + /** + * Shortcut to count number of Seth_Element objects in database + * @param mixed $cond Optional SQL condition as string or ORM_Condition object + * @param array $params If condition is provided as string, optional array of parameters + * @return integer Number of objects in database + */ + static function count($cond = null, $params = array()) { + return ORM::count(self::TYPE, $cond, $params); + } + + /** + * Shortcut to see if a Seth_Element object exists in database + * @param mixed $cond Optional SQL condition as string or ORM_Condition object + * @param array $params If condition is provided as string, optional array of parameters + * @return boolean Returns true if at least one object exists, otherwise false + */ + static function exists($cond = null, $params = array()) { + return ORM::exists(self::TYPE, $cond, $params); + } + + /** + * Shortcut to instantiate multiple Seth_Element objects by primary key + * @param array $pks Array of primary keys + * @param integer $type One of the ORM ordering constants + * @return array Array of Seth_Element objects + */ + static function multiple(array $pks, $type = ORM::ORDERED) { + return ORM::multiple(self::TYPE, $pks, $type); + } + +} + +?> diff --git a/inc/Seth/Model/Element/Descriptions.php b/inc/Seth/Model/Element/Descriptions.php new file mode 100755 index 0000000..0ec0d5c --- /dev/null +++ b/inc/Seth/Model/Element/Descriptions.php @@ -0,0 +1,84 @@ + + * @version $Revision$ + * @access public + * @package Seth_Element + */ +class Seth_Model_Element_Descriptions extends ORM_AbstractBase { + + const TYPE = __CLASS__; + + /** + * Constructor + * + * Creates a new Seth_Element_Descriptions object or loads a Seth_Element_Descriptions object by primary key + * WARNING: No auto_increment column found, you need to always set primary keys explicit! + * + * @param integer $element_id primary key + * @return object returns Seth_Element_Descriptions object + */ + public function __construct($element_id = null) { + parent::__construct($element_id); + } + + /** + * Configuration of the ORM object + * + * @param ORM_Configuration $config + */ + public static function _ORMConfiguration(ORM_Configuration $config) { + $config->db = 'seth'; + $config->table = 'element_descriptions'; + $config->pk = array('element_id'); + $config->createGet(array('element_id')); + $config->createGetSet(array('user_story', 'specifications', + 'description', 'url', 'material_in_shared_folder')); + } + + /** + * Shortcut to find Seth_Element_Descriptions objects from database + * @param mixed $cond Optional SQL condition as string or ORM_Condition object + * @param array $params If condition is provided as string, optional array of parameters + * @return array Array of Seth_Element_Descriptions objects + */ + static function find($cond = null, $params = array()) { + return ORM::find(self::TYPE, $cond, $params); + } + + /** + * Shortcut to count number of Seth_Element_Descriptions objects in database + * @param mixed $cond Optional SQL condition as string or ORM_Condition object + * @param array $params If condition is provided as string, optional array of parameters + * @return integer Number of objects in database + */ + static function count($cond = null, $params = array()) { + return ORM::count(self::TYPE, $cond, $params); + } + + /** + * Shortcut to see if a Seth_Element_Descriptions object exists in database + * @param mixed $cond Optional SQL condition as string or ORM_Condition object + * @param array $params If condition is provided as string, optional array of parameters + * @return boolean Returns true if at least one object exists, otherwise false + */ + static function exists($cond = null, $params = array()) { + return ORM::exists(self::TYPE, $cond, $params); + } + + /** + * Shortcut to instantiate multiple Seth_Element_Descriptions objects by primary key + * @param array $pks Array of primary keys + * @param integer $type One of the ORM ordering constants + * @return array Array of Seth_Element_Descriptions objects + */ + static function multiple(array $pks, $type = ORM::ORDERED) { + return ORM::multiple(self::TYPE, $pks, $type); + } + +} + +?> diff --git a/inc/Seth/Model/Element/FilterOrder.php b/inc/Seth/Model/Element/FilterOrder.php new file mode 100644 index 0000000..25bc7a1 --- /dev/null +++ b/inc/Seth/Model/Element/FilterOrder.php @@ -0,0 +1,126 @@ + + * @version $Revision$ + * @access public + * @package Seth_Model_Element + */ +class Seth_Model_Element_FilterOrder extends ORM_AbstractBase { + + const TYPE = __CLASS__; + + /** + * Constructor + * + * Creates a new Seth_Element_Filter_Order object or loads a Seth_Element_Filter_Order object by primary keys + * WARNING: No auto_increment column found, you need to always set primary keys explicit! + * + * @param integer $filter_id primary key + * @param integer $element_id primary key + * @return object returns Seth_Element_Filter_Order object + */ + public function __construct($filter_id = null, $element_id = null) { + if (!is_null($filter_id) || !is_null($element_id)) { + parent::__construct(array($filter_id, $element_id)); + } else { + parent::__construct(); + } + } + + /** + * Configuration of the ORM object + * + * @param ORM_Configuration $config + */ + public static function _ORMConfiguration(ORM_Configuration $config) { + $config->db = 'seth'; + $config->table = 'element_filter_order'; + $config->pk = array('filter_id', 'element_id'); + $config->createGet(array('filter_id', 'element_id')); + $config->createGetSet(array('order_index')); + } + + /** + * Very naive way of placing an element between two elements + * + * We receive the entire ordering, then reorder all elements each time + * @param object $filter Filter displayed + * @param object $place Element to place + * @param object $above Element that should be above + * @param object $below Element that should be below + * TODO: Reimplement using a any other algorithm :-) + */ + public static function placeBetween(Seth_Model_Filter $filter, + Seth_Model_Element $place, $above, $below) { + // Get all elements from list + $conds = $filter->getQueryConditions(); + $elms = ORM::find(Seth_Model_Element::TYPE, $conds); + // Get current ordering + $ordering = ORM::find(self::TYPE, 'filter_id=?', $filter->getId()); + // order by element + $orderByElm = array(); + foreach ($ordering as $order) { + $orderByElm[$order->getElementId()] = $order; + } + $placeId = $place->getId(); + $aboveId = !is_null($above) ? $above->getId() : null; + $belowId = !is_null($below) ? $below->getId() : null; + + // reindex + $index = 0; + $newOrder = array(); + $wasPlaced = false; + foreach ($elms as $elm) { + $id = $elm->getId(); + + if ($id == $placeId) { + continue; // we skip the element we are going to place + } + + $index++; + if (isset($orderByElm[$id])) { + $newOrder[] = $order = $orderByElm[$id]; + unset($orderByElm[$id]); + } else { + $newOrder[] = $order = new self($filter->getId(), $id); + } + + // check to see if we should place our element above or below this one + if ($id == $belowId && !is_null($below) && !$wasPlaced) { + $placeIndex = ($index++); + $wasPlaced = true; + } + + $order->setOrderIndex($index); + + if ($id == $aboveId && !is_null($above) && !$wasPlaced) { + $placeIndex = (++$index); + $wasPlaced = true; + } + } + + // Place item + if ($wasPlaced) { + if (isset($orderByElm[$placeId])) { + $newOrder[] = $order = $orderByElm[$placeId]; + unset($orderByElm[$placeId]); + } else { + $newOrder[] = $order = new self($filter->getId(), $placeId); + } + $order->setOrderIndex($placeIndex); + } + + // save all + ORM_Multiple::save($newOrder); + // delete unused + ORM_Multiple::delete($orderByElm); + } + +} + +?> diff --git a/inc/Seth/Model/Filter.php b/inc/Seth/Model/Filter.php new file mode 100755 index 0000000..ab42654 --- /dev/null +++ b/inc/Seth/Model/Filter.php @@ -0,0 +1,230 @@ + + * @version $Revision$ + * @access public + * @package Seth + */ +class Seth_Model_Filter extends ORM_AbstractBase { + + const TYPE = __CLASS__; + + /** + * Configuration of the ORM object + * + * @param ORM_Configuration $config + */ + public static function _ORMConfiguration(ORM_Configuration $config) { + $config->db = 'seth'; + $config->table = 'filter'; + $config->autoIncrement = 'id'; + $config->createGet(array('id')); + $config->createGetSet(array('title', 'group_id', 'description', 'inherit_id')); + } + + /** + * Returns possible conditions + */ + public static function getPossibleConditions() { + return array('=', '!=', '<', '<=', '>' ,'>=', 'LIKE', 'ILIKE', 'IS NULL', 'IS NOT NULL'); + } + + /** + * Sets what to filter by + * @param array $rules Array of rules, of field, cond, value + */ + public function setFilterBy(array $rules) { + if (!empty($rules)) { + $this->_fields['filter_by'] = serialize($rules); + } else { + $this->_fields['filter_by'] = null; + } + } + + /** + * Gets what to filter by + * @return array Array of rules, of field, cond and value + */ + public function getFilterBy() { + if (isset($this->_fields['filter_by']) && !is_null($this->_fields['filter_by'])) { + return unserialize($this->_fields['filter_by']); + } + return null; + } + + /** + * Sets list of fields to group by + * + * @param array $fields List or fields to group by or null + */ + public function setGroupBy(array $fields) { + $this->_fields['group_by'] = + empty($fields) ? null : implode(',', $fields); + } + + /** + * Returns list of fields to group by + * + * @return array Array of fields to group by or null + */ + public function getGroupBy() { + if (!isset($this->_fields['group_by']) || is_null($this->_fields['group_by'])) { + return null; + } + return explode(',', $this->_fields['group_by']); + } + + /** + * Sets list of fields to order by + * + * @param array $fields List or fields to order by or null + */ + public function setOrderBy(array $fields) { + $this->_fields['order_by'] = + empty($fields) ? null : implode(',', $fields); + } + + /** + * Returns list of fields to order by + * + * @return array Array of fields to order by or null + */ + public function getOrderBy() { + if (!isset($this->_fields['order_by']) || is_null($this->_fields['order_by'])) { + return null; + } + return explode(',', $this->_fields['order_by']); + } + + /** + * Returns all filters, including inherited + */ + public function getFilterByIncludeInherited() { + $parentFilters = null; + // We will start by including all the conditions we inherit + if ($this->getInheritId()) { + $inherit = new Seth_Model_Filter($this->getInheritId()); + if (!$inherit->isNew()) { + $parentFilters = $inherit->getFilterByIncludeInherited(); + } + } + $myFilters = $this->getFilterBy(); + if (!is_null($myFilters) && !is_null($parentFilters)) { + $filters = array_merge($parentFilters, $myFilters); + } else { + $filters = (!is_null($myFilters)) ? $myFilters : $parentFilters; + } + return $filters; + } + + /** + * Filter out filters for more specific rules + * + * If a filter has a = or IS NULL condition these override any other condition + * Useful when determining what to set to match a filter + */ + public static function cleanFilter(array $all) { + $ignore = array(); + $filters = array(); + foreach ($all as $filter) { + if ($filter['cond'] == '=' || $filter['cond'] == 'IS NULL') { + $ignore[] = $filter['field']; + $filters[] = $filter; + } + } + // now add rest to list (that are not overridden) + foreach ($all as $filter) { + if (!in_array($filter['field'], $ignore)) { + $filters[] = $filter; + } + } + return $filters; + } + + /** + * Returns all fields to group by + */ + public function getGroupByIncludeInherited() { + $parentGB = null; + if ($this->getInheritId()) { + $inherit = new Seth_Model_Filter($this->getInheritId()); + if (!$inherit->isNew()) { + $parentGB = $inherit->getGroupByIncludeInherited(); + } + } + $myGB = $this->getGroupBy(); + if (!is_null($myGB) && !is_null($parentGB)) { + $GB = array_merge($parentGB, $myGB); + } else { + $GB = (!is_null($myGB)) ? $myGB : $parentGB; + } + return $GB; + } + + + + /** + * Returns a ORM_Condition object to query a list using this filter + * @param boolean $inherting include conditions for subfilters (that inherits this filter) + */ + public function getQueryConditions($inheriting = false) { + + $query = new ORM_Condition(); + + // We will start by all including all the conditions we inherit + if ($this->getInheritId()) { + $inherit = new Seth_Model_Filter($this->getInheritId()); + if (!$inherit->isNew()) { + $query->merge($inherit->getQueryConditions(true)); + } + } + + // load filter options + $conds = $this->getFilterBy(); + $orderby = $this->getOrderBy(); + $groupby = $this->getGroupBy(); + + if ($conds) { + foreach ($conds as $cond) { + // Ignore value if IS NULL or IS NOT NULL + if ($cond['cond'] == 'IS NULL' || $cond['cond'] == 'IS NOT NULL') { + $query->add(sprintf('e.%s %s', $cond['field'], $cond['cond'])); + } else { + $query->add(sprintf('e.%s %s ?', $cond['field'], $cond['cond']), $cond['value']); + } + } + } + + // Construct our ordering of the elements + if ($groupby) { + foreach ($groupby as $order) { + $query->addOrderBy('e.'.$order); + } + } + if ($orderby) { + foreach ($orderby as $order) { + $query->addOrderBy('e.'.$order); + } + } + + // Default conditions + if (!$inheriting) { + $query->add('e.group_id = ?', $this->getGroupId()); + // Always left join and order by the filter's own ordering + $query->add('FROM element e '. + 'LEFT JOIN element_filter_order o ON e.id=o.element_id AND o.filter_id=? '. + 'WHERE e.id=element.id '. + 'ORDER BY o.order_index', + $this->getId()); + } + + return $query; + } + +} + +?> diff --git a/inc/Seth/Model/Group.php b/inc/Seth/Model/Group.php new file mode 100755 index 0000000..2d40119 --- /dev/null +++ b/inc/Seth/Model/Group.php @@ -0,0 +1,30 @@ + + * @version $Revision$ + * @access public + * @package Seth + */ +class Seth_Model_Group extends ORM_AbstractBase { + + const TYPE = __CLASS__; + + /** + * Configuration of the ORM object + * + * @param ORM_Configuration $config + */ + public static function _ORMConfiguration(ORM_Configuration $config) { + $config->db = 'seth'; + $config->table = '`group`'; + $config->autoIncrement = 'id'; + $config->createGet(array('id')); + $config->createGetSet(array('title')); + } + +} + +?> diff --git a/inc/Seth/Model/View.php b/inc/Seth/Model/View.php new file mode 100755 index 0000000..6a8f99e --- /dev/null +++ b/inc/Seth/Model/View.php @@ -0,0 +1,51 @@ + + * @version $Revision$ + * @access public + * @package Seth + */ +class Seth_Model_View extends ORM_AbstractBase { + + const TYPE = __CLASS__; + + /** + * Configuration of the ORM object + * + * @param ORM_Configuration $config + */ + public static function _ORMConfiguration(ORM_Configuration $config) { + $config->db = 'seth'; + $config->table = 'view'; + $config->autoIncrement = 'id'; + $config->createGet(array('id')); + $config->createGetSet(array('title', 'group_id')); + } + + /** + * Sets list of filters to view + * + * @param array $fields List or fields to order by or null + */ + public function setFilterOrder(array $filters) { + $this->_fields['filter_order'] = + implode(',', $filters); + } + + /** + * Returns list of filters this view has + * + * @return array Array of filters in order + */ + public function getFilterOrder() { + if (!isset($this->_fields['filter_order'])) { + return array(); + } + return explode(',', $this->_fields['filter_order']); + } + +} +?> diff --git a/opdatering/addfilter.php b/opdatering/addfilter.php new file mode 100644 index 0000000..f23b7e1 --- /dev/null +++ b/opdatering/addfilter.php @@ -0,0 +1,47 @@ +setGroupId($groupId = intval($_GET['group_id'])); +} else { + echo 'Missing group_id';die(); +} + +echo << +$(function() { + SethManager.initAddFilter(); +}); + +EOF; + +// List of available groups to choose from +echo '

Gruppe:

'; +$controller->printGroupSelect(); + +echo '

Vælg liste:

'; +$list = new I2_GUI_List(); +$list->enableFeatures(I2_GUI_LIST_TOTALCOUNT); +$list->setHeaders(array('id' => 'ID', 'title'=>'Navn på liste', 'action' => 'Muligheder')); +$list->setLimit(25); +$list->setDataORM(Seth_Model_Filter::TYPE, 'group_id=?', array($groupId)); +$list->addOrderBy('title'); +$list->setRowCallback('insertEditLinks', 'Seth_Controller_Filter'); +echo $list->getHTML(); + +echo '
'; +printf('', $groupId); + + +Seth_Controller_Page::printBottom(); + +?> diff --git a/opdatering/css/seth.css b/opdatering/css/seth.css new file mode 100644 index 0000000..e9d398e --- /dev/null +++ b/opdatering/css/seth.css @@ -0,0 +1,90 @@ +html,body,#page, #tabs { + height: 100%; +} +#page, #tabs { + position: relative; + overflow: none; +} + +h1 { + font: 22pt Arial; + font-weight: bold; + margin: 0 0 10px 0; + text-align: center; +} + +#page { + width: 90%; + margin: 2pt auto 2pt; + padding: 4pt; + + border: 1px solid gray; + -moz-box-shadow: 10px 10px 5px #888; + -moz-border-radius: 10px; +} + +.table li { + list-style-type:none; +} + +ul.table { + margin: 0; + padding-left: 0; + min-height: 15px; /* allow drag'n'drop back */ +} + +.new input[type=text] { + width: 80%; + margin: 0 5px; +} + +div.group-seperator { + margin-top: 16px; + font-size: 14px; + font-weight: bold; +} + +#tabs-list { + font-size: 12px; +} + +#actions { + font: 11pt verdana bold; + text-align: right; + padding: 0 10px; +} + +#actions a { + color: darkblue; + text-decoration: none; +} + +#actions a:hover { + background-color: #FFFFB0; + text-decoration: underline overline; +} + +.filtered_list { + position:relative; + height: 80%; + overflow: scroll; +} + +.filtered_list a:hover { + background-color: #FFFF88; +} + +/* Dialogs */ + +#addlist, #editelement { + padding: 0; + margin: 0; + overflow: hidden; +} + +#addlistiframe, #editiframe { + width: 100%; + height: 100%; + border: 0; +} + diff --git a/opdatering/edit.php b/opdatering/edit.php new file mode 100644 index 0000000..ef527e6 --- /dev/null +++ b/opdatering/edit.php @@ -0,0 +1,93 @@ +isNew()) { + echo 'Element no longer exists'; die(); +} + +$elemDescription = new Seth_Model_Element_Descriptions($elementId); + +// Fields directly mapped to table (ORM object): +$ormControlElm = new StraightForm_ORMController($element, + array( + 'title' => 'Overskrift', + 'priority' => 'Prioritet (lavere tal er højest prioritet)', + 'area' => 'Område', + 'sprint' => 'Sprint', + 'contact_person_email' => 'Kontakt person (email)', + 'contact_person' => 'Kontakt person', + 'assignee' => 'Opgaven tilhører', + 'estimate_development' => 'Udviklings estimat', + 'estimate_design' => 'Design estimat', + 'actual_design' => 'Faktisk designtid', + 'actual_development' => 'Faktisk udviklingstid', + 'approved' => StraightForm_BasicElements::checkbox('Godkendt'), + 'design' => StraightForm_BasicElements::checkbox('Design klar'), + 'completed' => StraightForm_BasicElements::checkbox('Afsluttet'), + 'deleted' => StraightForm_BasicElements::checkbox('Slettet'), + 'bug' => StraightForm_BasicElements::checkbox('Bug/fejlrettelse'), + 'deadline' => 'Deadline', + 'created_at' => 'Oprettelsestidspunkt', + 'completed_date' => 'Færdiggørelsesdag', + 'po_time' => 'PO tid', + 'site' => 'Site', + 'ressource' => 'Ressource', + 'product_owner' => 'Product Owner', + 'group_id' => StraightForm_BasicElements::select('Gruppe:', Seth_Controller_Filter::getGroups()), + )); +$ormControlElm->setParameters('title', array('text_regex' => '/^.+/')); +$ormControlElm->setParameters('priority', array('text_regex' => '/^[0-9]*$/')); +$ormControlElm->setParameters('estimate_design', array('text_regex' => '/^[0-9]*$/')); +$ormControlElm->setParameters('estimate_development', array('text_regex' => '/^[0-9]*$/')); +$ormControlElm->setParameters('actual_development', array('text_regex' => '/^[0-9]*$/')); +$ormControlElm->setParameters('actual_design', array('text_regex' => '/^[0-9]*$/')); +$ormControlElm->setParameters('sprint', array('text_regex' => '/^[0-9]*$/')); +$ormControlElm->setParameters('po_time', array('text_regex' => '/^[0-9]*$/')); +$ormControlElm->setNullify(array('assignee', 'area', 'contact_person', 'estimate_design', + 'estimate_development', 'actual_design', 'actual_development', 'contact_person_email', + 'deadline', 'sprint', 'completed_date', 'po_time', 'site', 'ressource', 'product_owner')); +$ormControlElm->setAutoSave(false); + +$ormControlElmDesc = new StraightForm_ORMController($elemDescription, + array( + 'user_story' => StraightForm_BasicElements::textarea('User Story'), + 'specifications' => StraightForm_BasicElements::textarea('Specifikationer'), + 'description' => StraightForm_BasicElements::textarea('Beskrivelse'), + 'url' => 'URL', + 'material_in_shared_folder' => StraightForm_BasicElements::checkbox('Findes yderligere materiale på share') + )); +$ormControlElmDesc->setNullify(array('user_story', 'specifications', + 'description', 'url', 'material_in_shared_folder')); +$ormControlElmDesc->setAutoSave(false); + +// Form +$form = new StraightForm(new StraightForm_ChainController($ormControlElmDesc, $ormControlElm)); + +Seth_Controller_Page::printTop($form->getHead()); + +// Saving +if ($form->handleForm()) { + // post submit we will convert some fields back to what they really should be + $element->save(); + $elemDescription->save(); + // completed - return to list page + $newTitle = json_encode(utf8_encode($element->getTitle())); + echo ''; + +} else { + echo $form->getHTML(); +} + +Seth_Controller_Page::printBottom(); + +?> diff --git a/opdatering/editfilter.php b/opdatering/editfilter.php new file mode 100644 index 0000000..2ee2f90 --- /dev/null +++ b/opdatering/editfilter.php @@ -0,0 +1,140 @@ +getGroupId(); +} else { + // create new with group id as default + if (!$groupId) { + echo 'No group'; die(); + } + $filter = new Seth_Model_Filter(); + $filter->setGroupId($groupId); +} + +// Fields directly mapped to table (ORM object): +$ormController = new StraightForm_ORMController($filter, + array('title' => 'Navn til filtret:', + 'group_id' => StraightForm_BasicElements::select('Gruppe:', Seth_Controller_Filter::getGroups()), + 'inherit_id' => StraightForm_BasicElements::select('Byg ovenpå:', Seth_Controller_Filter::getFilters($groupId, $filter)), + 'description' => StraightForm_BasicElements::textarea('Beskrivelse:'))); + +$ormController->setParameters('title', array('text_regex' => '/^\w+/')); +$ormController->setNullify(array('inherit_id')); +$ormController->setAutoSave(false); + +// Form +$form = new StraightForm($ormController); + +// Fields added for prettifying gui making it easier to add conditions: +$fields = Seth_Model_Element::getFilterFields(); +sort($fields); +$fields = array_combine($fields, $fields); // same keys/values +$fields = array_merge(array(0=>''), $fields); // Empty default +$default = 0; + +$conds = Seth_Model_Filter::getPossibleConditions(); +$conds = array_combine($conds, $conds); // same keys/values + +$formFilters = array(); +$rules = $filter->getFilterBy(); +if (!is_null($rules)) reset($rules); + +for ($i=0; $i<6; $i++) { + $formFilters[] = $f = $form->addElement('filter'. $i, new Seth_Element_Filter($i?null:'Filtre:', $fields, $conds)); + if (!is_null($rules) && current($rules)) { + $f->setValue(current($rules)); + next($rules); + } +} + +// Add group by fields +$formGroupBy = array(); + +$groupBy = $filter->getGroupBy(); +if (!is_null($groupBy)) reset($groupBy); + +for ($i=0; $i<5; $i++) { + $formGroupBy[] = $f = $form->addElement('groupby'.$i, + StraightForm_BasicElements::select($i?null:'Gruppér efter:', $fields, $default)); + if (!is_null($groupBy) && current($groupBy)) { + $f->setValue(current($groupBy)); + next($groupBy); + } +} + +// Add order by fields +$formOrderBy = array(); + +$orderBy = $filter->getOrderBy(); +if (!is_null($orderBy)) reset($orderBy); + +for ($i=0; $i<5; $i++) { + $formOrderBy[] = $f = $form->addElement('orderby'.$i, + StraightForm_BasicElements::select($i?null:'Sortér efter:', $fields, $default)); + if (!is_null($orderBy) && current($orderBy)) { + $f->setValue(current($orderBy)); + next($orderBy); + } +} + + +if ($form->handleForm()) { + // post submit we will convert filters to a serialized field + $rules = array(); + foreach ($formFilters as $selection) { + $rule = $selection->getValue(); + if (!is_null($rule)) { + // if used add to list + $rules[] = $rule; + } + } + // group by + $groupby = array(); + foreach ($formGroupBy as $by) { + $value = $by->getValue(); + var_dump($value); + if ((string)$value != (string)$default) { + $groupby[] = $value; + } + } + + // order by + $orderby = array(); + foreach ($formOrderBy as $by) { + $value = $by->getValue(); + if ((string)$value != (string)$default) { + $orderby[] = $value; + } + } + + $filter->setFilterBy($rules); + $filter->setGroupBy($groupby); + $filter->setOrderBy($orderby); + $filter->save(); + // completed - return to list page + header('Location: addfilter.php?group_id=' . $filter->getGroupId()); + exit; +} + +Seth_Controller_Page::printTop($form->getHead()); + +echo $form->getHTML(); + +Seth_Controller_Page::printBottom(); + +?> diff --git a/opdatering/index.php b/opdatering/index.php new file mode 100644 index 0000000..61c0e87 --- /dev/null +++ b/opdatering/index.php @@ -0,0 +1,42 @@ + + +EOH; + +$page = new I2_GUI_Informational('Seth Projekt Organizer', 'TV 2 Net'."\n".'Seth is design and implemented by Kristian Kræmmer Nielsen , Copyright (c) 2010'); +$page->printTop($head); +$page->printSubtitle('Beskrivelse:'); + +echo 'Seth, opkaldt efter den egytiske gud for kaos.
I den filosofi at er du venner med Seth har du bedre chance for at få styr på dit projekt og ikke ende ud i kaos.

'; + +$page->printSubtitle('Grupper:'); + +$list = new I2_GUI_List(); +$list->enableFeatures(I2_GUI_LIST_TOTALCOUNT); +$list->setHeaders(array( + 'id' => 'Id', + 'title' => 'Titel', + 'actions' => 'Muligheder')); +$list->setLimit(25); +$list->setDataORM(Seth_Model_Group::TYPE); +$list->setOrderBy('title'); +$list->setRowCallback('insertActions', 'Seth_Controller_Group'); +$list->setRowLink('views.php?group_id=%d', 'id'); +echo $list->getHTML(); + +?> +
+ + +printBottom(); + +?> diff --git a/opdatering/js/group.js b/opdatering/js/group.js new file mode 100644 index 0000000..7a9a4b3 --- /dev/null +++ b/opdatering/js/group.js @@ -0,0 +1,84 @@ +/* + * Group list + */ + +SethGroup = { + + createNew: function() { + var name = prompt('Indtast navn:'); + if (name) { + name = name.replace(/\s*/, "") + if (name != '') { + // do callback + $.ajax({ + type: 'POST', + data: { + action: 'new', + title: name + }, + dataType: 'json', + error: function() { + alert('Server fejl'); + }, + success: function(data) { + if (data && data.ok) { + window.location.reload(); + } + } + }); + } + } + }, + + rename: function(id, title) { + var name = prompt('Indtast navn:', unescape(title)); + if (name) { + name = name.replace(/\s*/, "") + if (name != '') { + // do callback + $.ajax({ + type: 'POST', + data: { + action: 'rename', + id: id, + title: name + }, + dataType: 'json', + error: function() { + alert('Server fejl'); + }, + success: function(data) { + if (data && data.ok) { + window.location.reload(); + } + } + }); + } else { + alert('Ugyldigt navn'); + } + } + }, + + remove: function(id, title) { + if (confirm('Er du sikker på at slettet "'+unescape(title)+'" ?')) { + // do callback + $.ajax({ + type: 'POST', + data: { + action: 'remove', + id: id, + }, + dataType: 'json', + error: function() { + alert('Server fejl'); + }, + success: function(data) { + if (data && data.ok) { + window.location.reload(); + } + } + }); + } + } + +} diff --git a/opdatering/js/seth.js b/opdatering/js/seth.js new file mode 100644 index 0000000..0656df1 --- /dev/null +++ b/opdatering/js/seth.js @@ -0,0 +1,607 @@ + +var SethManager = { + + view_id: null, // ID of current view + group_id: null, // ID of group shown + + next_tab_no: 0, + + disableEditLinks: false, // manual disable all links when dragging + cancelMove: true, // cancels moves (used when moving into tabs) + currentTabNo: -1, // tab_no of current tab + + init: function(view_id, group_id, list_of_tabs) { + this.view_id = view_id; + this.group_id = group_id; + + // create addList dialog + $("#addlist").dialog({ + modal: true, + autoOpen: false, + height: 600, + width: 800, + buttons: { + "Fortryd": function() { + SethManager.closeAddListDialog(); + } + }, + close: function() { + SethManager.emptyListsAndUpdateCurrent(); + $('#addlistiframe').attr('src', 'about:blank'); // make F5 work + } + }); + + // create edit element dialog + $("#editelement").dialog({ + modal: true, + autoOpen: false, + height: 600, + width: 800, + buttons: { + "Fortryd": function() { + SethManager.closeEditDialog(); + }, + "Gem": function() { + SethManager.saveAndCloseEditDialog(); + }, + "Slet": function() { + SethManager.deleteAndCloseEditDialog(); + } + }, + close: function() { + $("#editiframe").attr('src', 'about:blank'); // make F5 work + } + }); + + // turn add/edit list links into open dialog + $("#addlist-link").click(function() { + SethManager.openAddListDialog(); + return false; + }); + $("#editlist-link").click(function() { + SethManager.openEditListDialog(); + return false; + }); + + // remove list link + $("#removelist-link").click(function() { + SethManager.removeCurrentList(); + return false; + }); + + // create filter tabs + $("#tabs").tabs({ + show: function(event, ui) { + SethManager.showTab(event, ui); + SethManager.currentTabNo = $(ui.panel).data('tab_no'); + } + }); + var tabsul = $("#tabs-list"); + + // Allow to sort tabs + tabsul.sortable(); + + tabsul.bind('sortupdate', function(event, ui) { + SethManager.storeFilterOrder(); + }); + + // create tabs + $.each(list_of_tabs, function(index, value) { + SethManager.addList(value.id, value.title); + }); + + }, + + // Callback and get a fresh list + updateList: function(tabno) { + var listDom = $("#list-" + tabno); + var list_id = listDom.data('list_id'); + listDom.data('list_status', 'loading'); + + // do callback + $.ajax({ + type: 'POST', + data: { + action: 'get', + listid: list_id + }, + dataType: 'json', + error: function() { + SethManager.commError(this); + }, + success: function(data) { + if (data && data.list) { + SethManager.fillInList(tabno, data.list); + } else { + SethManager.commError(this, data); + } + } + }); + }, + + // Fill in a list using return data + fillInList: function(tabno, listdata) { + // check for error (list returned as nested result) + if (listdata.err) { + this.commError(null, listdata); + return; + } + + var listDom = $("#list-" + tabno); + var first = true; + + listDom.empty(); + + listDom.data('list_status', 'loaded'); + + // We need to create a ul for each group + $.each(listdata.groups, function(index, value) { + var group = $('
'+value.title+'
'); + group.data('group_defined_as', value.defined_as); + group.data('group_when_placing_here', value.when_placing_here); + listDom.append(group); + + var table = $('