]> git.pld-linux.org Git - packages/eventum.git/blame - eventum-order.patch
- update to 2.2-dev-r3890
[packages/eventum.git] / eventum-order.patch
CommitLineData
ae3e48e6
ER
1--- eventum-new/htdocs/ajax/order.php 2008-10-15 02:02:25.000000000 +0300
2+++ eventum-new/htdocs/ajax/order.php 2008-10-15 02:02:25.000000000 +0300
be762003
ER
3@@ -0,0 +1,72 @@
4+<?
5+require_once(dirname(__FILE__) . '/../init.php');
6+require_once(APP_INC_PATH . "class.auth.php");
7+require_once(APP_INC_PATH . "class.issue.php");
8+
9+// check login
10+if (!Auth::hasValidCookie(APP_COOKIE)) {
11+ exit;
12+}
13+
14+
15+
16+if (!isset($_POST['before']) || !isset($_POST['after'])) {
17+ exit;
18+}
19+
29e18d8b
ER
20+parse_str($_POST['before'], $before);
21+parse_str($_POST['after'], $after);
be762003
ER
22+
23+$before = $before['issue_list_table'];
24+$after = $after['issue_list_table'];
25+
26+
27+$before = array_slice($before, 2, count($before)-3);
28+$after = array_slice($after, 2, count($after)-3);
29+
30+if (count($before) != count($after) or count($before) < 1) {
31+ exit;
32+}
33+
34+$usr_id = Auth::getUserID();
35+
36+$order = Issue::getIssueOrderByUser($usr_id);
37+
38+if (!count($order)) {
39+ // no prev order list
40+ exit;
41+}
42+
43+$after_filterd = array();
44+$before_filterd = array();
45+
46+// remove issues that are not assigned to me
47+foreach ($after as $id) {
48+ if (isset($order[$id])) {
49+ $after_filterd[] = $id;
50+ }
51+}
52+foreach ($before as $id) {
53+ if (isset($order[$id])) {
54+ $before_filterd[] = $id;
55+ }
56+}
57+
58+foreach ($after_filterd as $key => $nID) {
59+ if ($nID != $before_filterd[$key]) {
60+ if ($nID) {
61+ $stmt = "UPDATE
62+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
63+ SET
64+ isu_order = " . $order[$before_filterd[$key]] . "
65+ WHERE
66+ isu_iss_id = $nID AND
67+ isu_usr_id = $usr_id";
c2e7ae63 68+ $res = DB_Helper::getInstance()->query($stmt);
be762003
ER
69+ if (PEAR::isError($res)) {
70+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
71+ die('update failed');
72+ }
73+ }
74+ }
75+}
ae3e48e6
ER
76--- eventum/htdocs/ajax/update.php 2008-10-15 01:46:20.000000000 +0300
77+++ eventum-new/htdocs/ajax/update.php 2008-10-15 02:02:25.000000000 +0300
be762003
ER
78@@ -0,0 +1,30 @@
79+<?
80+require_once(dirname(__FILE__) . '/../init.php');
81+require_once(APP_INC_PATH . "class.auth.php");
82+require_once(APP_INC_PATH . "class.issue.php");
83+
84+// check login
85+if (!Auth::hasValidCookie(APP_COOKIE)) {
86+ exit;
87+}
88+
89+if (!is_numeric($_POST['issueID'])) {
90+ exit;
91+}
92+
93+switch ($_POST['fieldName']) {
94+ case 'expected_resolution_date':
95+ $day = (int)$_POST['day'];
96+ $month = (int)$_POST['month'];
97+ $year = (int)$_POST['year'];
98+ if (Issue::updateField($_POST['issueID'], $_POST['fieldName'], sprintf('%04d-%02d-%02d', $year, $month, $day)) !== -1) {
c2e7ae63 99+ echo Date_Helper::getSimpleDate(sprintf('%04d-%02d-%02d', $year, $month, $day), false);
be762003
ER
100+ } else {
101+ echo 'update failed';
102+ }
103+ exit;
104+ break;
105+ default:
106+ die('object type not supported');
107+ break;
108+}
ae3e48e6
ER
109--- eventum/lib/eventum/class.display_column.php 2008-10-15 01:46:20.000000000 +0300
110+++ eventum-new/lib/eventum/class.display_column.php 2008-10-15 02:02:25.000000000 +0300
d95f9b26 111@@ -229,7 +229,10 @@
9fbd0c65
ER
112 ),
113 "iss_expected_resolution_date" => array(
d95f9b26
ER
114 "title" => ev_gettext("Expected Resolution Date")
115- )
9fbd0c65
ER
116+ ),
117+ "isu_order" => array(
be762003 118+ "title" => ev_gettext("Order")
d95f9b26 119+ ),
9fbd0c65
ER
120 )
121 );
d95f9b26 122 return $columns[$page];
b33d41f0
ER
123--- eventum-2.2/lib/eventum/class.issue.php~ 2009-07-23 14:02:20.000000000 +0300
124+++ eventum-2.2/lib/eventum/class.issue.php 2009-07-23 14:02:38.085536073 +0300
be762003 125@@ -1356,6 +1356,7 @@
9fbd0c65
ER
126 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
127 return -1;
128 } else {
3197cdb4
ER
129+ self::moveOrderForAllUsers($issue_id, 1000);
130 $prj_id = self::getProjectID($issue_id);
9fbd0c65 131
be762003
ER
132 // record the change
133@@ -1659,6 +1660,176 @@
134 }
135 }
136
137+ /**
138+ * Method used to update the a single detail field of a specific issue.
139+ *
140+ * @param integer $issue_id
141+ * @param string $field_name
142+ * @param string $field_value
143+ * @param string $field_type string or integer (for escape)
144+ * @return integer 1 on success, -1 otherwise
145+ */
146+ function updateField($issue_id, $field_name, $filed_value) {
147+
148+ $issue_id = Misc::escapeInteger($issue_id);
149+
150+ $usr_id = Auth::getUserID();
3197cdb4 151+ $prj_id = self::getProjectID($issue_id);
be762003
ER
152+
153+ // get all of the 'current' information of this issue
3197cdb4 154+ $current = self::getDetails($issue_id);
be762003
ER
155+
156+ $stmt = "UPDATE
157+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
158+ SET
c2e7ae63
ER
159+ iss_updated_date='" . Date_Helper::getCurrentDateGMT() . "',
160+ iss_last_public_action_date='" . Date_Helper::getCurrentDateGMT() . "',
be762003
ER
161+ iss_last_public_action_type='updated'";
162+
163+ switch ($field_name) {
164+ case 'category':
165+ $stmt .= ", iss_prc_id = " . Misc::escapeInteger($filed_value);
166+ break;
167+ case 'release':
168+ $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
169+ break;
170+ case 'expected_resolution_date':
171+ $stmt .= ", iss_expected_resolution_date = '" . Misc::escapeString($filed_value) . "'";
172+ break;
173+ case 'release':
174+ $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
175+ break;
176+ case 'priority':
177+ $stmt .= ", iss_pri_id = " . Misc::escapeInteger($filed_value);
178+ break;
179+ case 'status':
180+ $stmt .= ", iss_sta_id = " . Misc::escapeInteger($filed_value);
181+ break;
182+ case 'resolution':
183+ $stmt .= ", iss_res_id = " . Misc::escapeInteger($filed_value);
184+ break;
185+ case 'summary':
186+ $stmt .= ", iss_summary = '" . Misc::escapeString($filed_value) . "'";
187+ break;
188+ case 'description':
189+ $stmt .= ", iss_description = '" . Misc::escapeString($filed_value) . "'";
190+ break;
191+ case 'estimated_dev_time':
192+ $stmt .= ", iss_dev_time = '" . Misc::escapeString($filed_value) . "'";
193+ break;
194+ case 'percent_complete':
195+ $stmt .= ", iss_percent_complete = '" . Misc::escapeString($filed_value) . "'";
196+ break;
197+ case 'trigger_reminders':
198+ $stmt .= ", iss_trigger_reminders = " . Misc::escapeInteger($filed_value);
199+ break;
200+ case 'group':
201+ $stmt .= ", iss_grp_id = " . Misc::escapeInteger($filed_value);
202+ break;
203+ case 'private':
204+ $stmt .= ", iss_private = " . Misc::escapeInteger($filed_value);
205+ break;
206+ default:
207+ Error_Handler::logError("Unknown field name $field_name", __FILE__, __LINE__);
208+ return -1;
209+ break;
210+ }
211+
212+ $stmt .= "
213+ WHERE
214+ iss_id=$issue_id";
215+
c2e7ae63 216+ $res = DB_Helper::getInstance()->query($stmt);
be762003
ER
217+ if (PEAR::isError($res)) {
218+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
219+ return -1;
220+ } else {
221+ $new = array(
222+ 'category' => $current['iss_prc_id'],
223+ 'release' => $current['iss_pre_id'],
224+ 'expected_resolution_date' => $current['iss_expected_resolution_date'],
225+ 'release' => $current['iss_pre_id'],
226+ 'priority' => $current['iss_pri_id'],
227+ 'status' => $current['iss_sta_id'],
228+ 'resolution' => $current['iss_res_id'],
229+ 'summary' => $current['iss_summary'],
230+ 'description' => $current['iss_description'],
231+ 'estimated_dev_time' => $current['iss_dev_time'],
232+ 'percent_complete' => $current['iss_percent_complete'],
233+ 'trigger_reminders' => $current['iss_trigger_reminders'],
234+ 'group' => $current['iss_grp_id'],
235+ 'iss_private' => $current['private']
236+ );
237+ $new[$field_name] = $filed_value;
238+
239+ // add change to the history (only for changes on specific fields?)
240+ $updated_fields = array();
241+ if ($field_name == 'expected_resolution_date' && $current["iss_expected_resolution_date"] != $filed_value) {
242+ $updated_fields["Expected Resolution Date"] = History::formatChanges($current["iss_expected_resolution_date"], $filed_value);
243+ }
244+ if ($field_name == 'category' && $current["iss_prc_id"] != $filed_value) {
245+ $updated_fields["Category"] = History::formatChanges(Category::getTitle($current["iss_prc_id"]), Category::getTitle($filed_value));
246+ }
247+ if ($field_name == 'release' && $current["iss_pre_id"] != $filed_value) {
248+ $updated_fields["Release"] = History::formatChanges(Release::getTitle($current["iss_pre_id"]), Release::getTitle($filed_value));
249+ }
250+ if ($field_name == 'priority' && $current["iss_pri_id"] != $filed_value) {
251+ $updated_fields["Priority"] = History::formatChanges(Priority::getTitle($current["iss_pri_id"]), Priority::getTitle($filed_value));
252+ Workflow::handlePriorityChange($prj_id, $issue_id, $usr_id, $current, $new);
253+ }
254+ if ($field_name == 'status' && $current["iss_sta_id"] != $filed_value) {
255+ // clear out the last-triggered-reminder flag when changing the status of an issue
256+ Reminder_Action::clearLastTriggered($issue_id);
257+
258+ // if old status was closed and new status is not, clear closed data from issue.
259+ $old_status_details = Status::getDetails($current['iss_sta_id']);
260+ if ($old_status_details['sta_is_closed'] == 1) {
261+ $new_status_details = Status::getDetails($filed_value);
262+ if ($new_status_details['sta_is_closed'] != 1) {
3197cdb4 263+ self::clearClosed($issue_id);
be762003
ER
264+ }
265+ }
266+ $updated_fields["Status"] = History::formatChanges(Status::getStatusTitle($current["iss_sta_id"]), Status::getStatusTitle($filed_value));
267+ }
268+ if ($field_name == 'resolution' && $current["iss_res_id"] != $filed_value) {
269+ $updated_fields["Resolution"] = History::formatChanges(Resolution::getTitle($current["iss_res_id"]), Resolution::getTitle($filed_value));
270+ }
271+ if ($field_name == 'estimated_dev_time' && $current["iss_dev_time"] != $filed_value) {
272+ $updated_fields["Estimated Dev. Time"] = History::formatChanges(Misc::getFormattedTime(($current["iss_dev_time"]*60)), Misc::getFormattedTime(($filed_value*60)));
273+ }
274+ if ($field_name == 'summary' && $current["iss_summary"] != $filed_value) {
275+ $updated_fields["Summary"] = '';
276+ }
277+ if ($field_name == 'description' && $current["iss_description"] != $filed_value) {
278+ $updated_fields["Description"] = '';
279+ }
280+ if ($field_name == 'private' && ($filed_value != $current['iss_private'])) {
281+ $updated_fields["Private"] = History::formatChanges(Misc::getBooleanDisplayValue($current['iss_private']), Misc::getBooleanDisplayValue($filed_value));
282+ }
283+ if (count($updated_fields) > 0) {
284+ // log the changes
285+ $changes = '';
286+ $i = 0;
287+ foreach ($updated_fields as $key => $value) {
288+ if ($i > 0) {
289+ $changes .= "; ";
290+ }
291+ if (($key != "Summary") && ($key != "Description")) {
292+ $changes .= "$key: $value";
293+ } else {
294+ $changes .= "$key";
295+ }
296+ $i++;
297+ }
298+
299+ History::add($issue_id, $usr_id, History::getTypeID('issue_updated'), "Issue updated ($changes) by " . User::getFullName($usr_id));
300+ // send notifications for the issue being updated
301+ Notification::notifyIssueUpdated($issue_id, $current, $new);
302+ }
303+ }
304+ return 1;
305+ }
306+
307
308 /**
309 * Move the issue to a new project
310@@ -1820,16 +1991,33 @@
9fbd0c65
ER
311 {
312 $issue_id = Misc::escapeInteger($issue_id);
313 $assignee_usr_id = Misc::escapeInteger($assignee_usr_id);
314+ $order = 1;
315+ // move all orders down to free "order space" for this new association
316+ $stmt = "UPDATE
317+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
318+ SET
319+ isu_order = isu_order + 1
320+ WHERE
321+ isu_usr_id = $assignee_usr_id AND
322+ isu_order >= $order";
c2e7ae63 323+ $res = DB_Helper::getInstance()->query($stmt);
9fbd0c65
ER
324+ if (PEAR::isError($res)) {
325+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
326+ return -1;
327+ }
328+ // insert the new association
329 $stmt = "INSERT INTO
330 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
331 (
332 isu_iss_id,
333 isu_usr_id,
334- isu_assigned_date
335+ isu_assigned_date,
336+ isu_order
337 ) VALUES (
338 $issue_id,
339 $assignee_usr_id,
c2e7ae63
ER
340- '" . Date_Helper::getCurrentDateGMT() . "'
341+ '" . Date_Helper::getCurrentDateGMT() . "',
9fbd0c65
ER
342+ $order
343 )";
c2e7ae63 344 $res = DB_Helper::getInstance()->query($stmt);
9fbd0c65 345 if (PEAR::isError($res)) {
be762003 346@@ -1844,6 +2032,78 @@
9fbd0c65
ER
347 }
348 }
349
350+ /**
351+ * Method used to get the order list to be rearranged
352+ *
353+ * @access private
354+ * @param string $issue_id The issue ID or a comma seperated list of IDs already prepared for giving to mysql
355+ * @param string $usr_id The user to remove. When not specified, all users are taken as to be removed for that issue
356+ * @return mixed delete order list to be rearranged. Used as a parameter to the method of rearranging the order.
357+ */
358+ function getDeleteUserAssociationOrderList($issue_id, $usr_id = "")
359+ {
360+ // find all affected associantion orders
361+ $stmt = "SELECT isu_usr_id, isu_order FROM
362+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
363+ WHERE
364+ isu_iss_id IN ($issue_id)";
365+ if ($usr_id !== FALSE) {
366+ $stmt.= " AND isu_usr_id IN ($usr_id)";
367+ }
368+ $stmt.= "ORDER BY isu_order";
c2e7ae63 369+ $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
9fbd0c65
ER
370+ if (PEAR::isError($res)) {
371+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
372+ return -1;
373+ } else {
374+ $deleted_orders = array();
375+ foreach ($res as $row) {
376+ if (empty($deleted_orders[$row['isu_usr_id']])) {
377+ $deleted_orders[$row['isu_usr_id']] = array();
378+ }
379+ $deleted_orders[$row['isu_usr_id']] [] = $row['isu_order'];
380+ }
381+ return $deleted_orders;
382+ }
383+ }
384+
385+ /**
386+ *
387+ * Method used to rearrange order list in the db according to known deleted records
388+ *
389+ * @access private
390+ * @param mixed deleteorder list
391+ * @return void
392+ */
393+ function rearrangeDeleteUserAssociationOrderList($delete_order_list)
394+ {
395+ if (empty($delete_order_list) || (!is_array($delete_order_list))) {
396+ return -1;
397+ }
398+ foreach ($delete_order_list as $isu_usr_id => $orders) {
399+ for ($i = 0; $i < count($orders); $i++) { // traverse all deleted orders
400+ // move the orders after them up to take the "order space" of the deleted records
401+ $stmt = "UPDATE
402+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
403+ SET
404+ isu_order = isu_order - " . ($i+1) . "
405+ WHERE
406+ isu_usr_id = $isu_usr_id AND
407+ isu_order > " . $orders[$i];
408+ if ($i < count($orders) - 1) {
409+ $stmt.= " AND
410+ isu_order < " . $orders[$i+1];
411+ }
c2e7ae63 412+ $res = DB_Helper::getInstance()->query($stmt);
9fbd0c65
ER
413+ if (PEAR::isError($res)) {
414+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
415+ return -1;
416+ }
417+ }
418+ }
419+ return 1;
420+ }
421+
422
423 /**
424 * Method used to delete all user assignments for a specific issue.
be762003 425@@ -1859,6 +2119,7 @@
9fbd0c65
ER
426 if (is_array($issue_id)) {
427 $issue_id = implode(", ", $issue_id);
428 }
3197cdb4 429+ $deleted_order_list = self::getDeleteUserAssociationOrderList($issue_id);
9fbd0c65
ER
430 $stmt = "DELETE FROM
431 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
432 WHERE
be762003 433@@ -1871,6 +2132,7 @@
9fbd0c65
ER
434 if ($usr_id) {
435 History::add($issue_id, $usr_id, History::getTypeID('user_all_unassociated'), 'Issue assignments removed by ' . User::getFullName($usr_id));
436 }
3197cdb4 437+ self::rearrangeDeleteUserAsssociationOrderList($deleted_order_list);
9fbd0c65
ER
438 return 1;
439 }
440 }
be762003 441@@ -1889,6 +2151,7 @@
9fbd0c65
ER
442 {
443 $issue_id = Misc::escapeInteger($issue_id);
444 $usr_id = Misc::escapeInteger($usr_id);
3197cdb4 445+ $delete_order_list = self::getDeleteUserAssociationOrderList($issue_id, $usr_id);
9fbd0c65
ER
446 $stmt = "DELETE FROM
447 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
448 WHERE
be762003 449@@ -1903,6 +2166,7 @@
9fbd0c65
ER
450 History::add($issue_id, Auth::getUserID(), History::getTypeID('user_unassociated'),
451 User::getFullName($usr_id) . ' removed from issue by ' . User::getFullName(Auth::getUserID()));
452 }
3197cdb4 453+ self::rearrangeDeleteUserAssociationOrderList($delete_order_list);
9fbd0c65
ER
454 return 1;
455 }
456 }
be762003 457@@ -2379,6 +2643,11 @@
9fbd0c65 458 {
3197cdb4
ER
459 $sort_by = self::getParam('sort_by');
460 $sort_order = self::getParam('sort_order');
461+ $users = self::getParam('users');
9fbd0c65
ER
462+ if (empty($users) && ($sort_by == 'isu_order')) { // Sorting by isu_order is impossible when no user specified
463+ unset($sort_by);
464+ unset($sort_order);
465+ }
3197cdb4
ER
466 $rows = self::getParam('rows');
467 $hide_closed = self::getParam('hide_closed');
9fbd0c65 468 if ($hide_closed === '') {
b33d41f0 469@@ -2720,6 +2720,7 @@
aa90cfdb 470 "iss_expected_resolution_date" => "desc",
b33d41f0
ER
471 "pre_title" => "asc",
472 "assigned" => "asc",
aa90cfdb 473+ "isu_order" => "desc",
9fbd0c65 474 );
aa90cfdb
ER
475
476 foreach ($custom_fields as $fld_id => $fld_name) {
be762003 477@@ -3275,6 +3545,8 @@
9fbd0c65
ER
478 $ids = implode(", ", $ids);
479 $stmt = "SELECT
480 isu_iss_id,
481+ isu_order,
482+ isu_usr_id,
483 usr_full_name
484 FROM
485 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user,
be762003 486@@ -3286,6 +3558,7 @@
9fbd0c65
ER
487 if (PEAR::isError($res)) {
488 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
489 } else {
490+ // gather names of the users assigned to each issue
491 $t = array();
492 for ($i = 0; $i < count($res); $i++) {
493 if (!empty($t[$res[$i]['isu_iss_id']])) {
be762003 494@@ -3294,9 +3567,18 @@
9fbd0c65
ER
495 $t[$res[$i]['isu_iss_id']] = $res[$i]['usr_full_name'];
496 }
497 }
498+ // gather orders
499+ $o = array();
500+ for ($i = 0; $i < count($res); $i++) {
501+ if (empty($o[$res[$i]['isu_iss_id']])) {
502+ $o[$res[$i]['isu_iss_id']] = array();
503+ }
504+ $o[$res[$i]['isu_iss_id']][$res[$i]['isu_usr_id']] = $res[$i]['isu_order'];
505+ }
506 // now populate the $result variable again
507 for ($i = 0; $i < count($result); $i++) {
508 @$result[$i]['assigned_users'] = $t[$result[$i]['iss_id']];
509+ @$result[$i]['assigned_users_order'] = $o[$result[$i]['iss_id']];
510 }
511 }
512 }
be762003 513@@ -4264,6 +4546,7 @@
9fbd0c65
ER
514 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
515 return -1;
516 }
3197cdb4 517+ self::moveOrderForAllUsers($issue_id, 1);
9fbd0c65
ER
518 }
519
520
c2e7ae63 521@@ -4322,5 +4605,121 @@
9fbd0c65
ER
522 }
523 return $returns[$msg_id];
524 }
525+
526+ /**
527+ * Reorders user's issues as requested by user
528+ * @access public
529+ * @param $usr_id User to be reordered
530+ * @param $issue_id Issue or array of issues to be moved
531+ * @param $neworder The new order of the issues
532+ * @return void
533+ */
534+ function reorderUserIssues($usr_id, $issue_id, $neworder)
535+ {
536+ if (!isset($usr_id) || !isset($issue_id) || !isset($neworder)) {
537+ return false;
538+ }
539+ if (!is_numeric($usr_id) || !is_numeric($neworder)) {
540+ return false;
541+ }
542+ $usr_id = Misc::escapeInteger($usr_id);
543+ $issue_id = Misc::escapeInteger($issue_id);
544+ $neworder = Misc::escapeInteger($neworder);
545+ if (is_array($issue_id)) {
546+ $issue_count = count($issue_id);
547+ $issue_id_str = implode(", ", $issue_id);
548+ } else {
549+ $issue_count = 1;
550+ $issue_id_str = $issue_id;
551+ $issue_id = array($issue_id);
552+ }
553+ // do a nasty pretending to be deleting stuff so that reordering happens as if these elements were deleted
3197cdb4
ER
554+ $orderlist = self::getDeleteUserAssociationOrderList($issue_id_str, $usr_id);
555+ self::rearrangeDeleteUserAssociationOrderList($orderlist);
9fbd0c65
ER
556+ // move down the orders to free the "order space" needed
557+ $stmt = "UPDATE
558+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
559+ SET
560+ isu_order = isu_order + $issue_count
561+ WHERE
562+ isu_usr_id = $usr_id AND
563+ isu_order >= $neworder";
c2e7ae63 564+ $res = DB_Helper::getInstance()->query($stmt);
9fbd0c65
ER
565+ if (PEAR::isError($res)) {
566+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
567+ return -1;
568+ }
569+ //update the order for the issues being moved
570+ $i = 0;
571+ foreach ($issue_id as $iss_id) {
572+ $stmt = "UPDATE
573+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
574+ SET
575+ isu_order = " . ($neworder + $i) . "
576+ WHERE
577+ isu_usr_id = $usr_id AND
578+ isu_iss_id = $iss_id";
c2e7ae63 579+ $res = DB_Helper::getInstance()->query($stmt);
9fbd0c65
ER
580+ if (PEAR::isError($res)) {
581+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
582+ return -1;
583+ }
584+ $i++;
585+ }
586+ }
587+
be762003
ER
588+
589+ /**
590+ * Get users issue order list
591+ * @access public
592+ * @param $user_id User
593+ * @param $order_list Order of the issues
594+ * @return void
595+ */
596+ function getIssueOrderByUser($usr_id) {
597+
598+ if (!is_numeric($usr_id)) {
599+ return false;
600+ }
601+
602+ $stmt = "SELECT
603+ isu_iss_id, isu_order
604+ FROM
605+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
606+ WHERE
607+ isu_usr_id = " . $usr_id ;
608+
609+ $order_list = array();
610+
c2e7ae63 611+ $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
be762003
ER
612+
613+ if (PEAR::isError($res)) {
614+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
615+ return array();
616+ } else {
617+ foreach ($res as $row) {
618+ $order_list[$row["isu_iss_id"]] = $row["isu_order"];
619+ }
620+ }
621+ return $order_list;
622+ }
623+
9fbd0c65
ER
624+ function moveOrderForAllUsers($issue_id, $neworder)
625+ {
626+ // Move the issue to the top priority for the ppl it's assigned to
627+ $stmt = "SELECT isu_usr_id FROM
628+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
629+ WHERE
630+ isu_iss_id = " . Misc::escapeInteger($issue_id);
c2e7ae63 631+ $res = DB_Helper::getInstance()->getAll($stmt, DB_FETCHMODE_ASSOC);
9fbd0c65
ER
632+ if (PEAR::isError($res)) {
633+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
634+ return -1;
635+ }
636+ foreach ($res as $row) {
3197cdb4 637+ self::reorderUserIssues($row["isu_usr_id"], $issue_id, $neworder);
9fbd0c65
ER
638+ }
639+ }
640+
641 }
642
ae3e48e6
ER
643--- eventum-r3749/htdocs/js/global.js~ 2008-10-15 02:03:48.000000000 +0300
644+++ eventum-r3749/htdocs/js/global.js 2008-10-15 02:06:00.000000000 +0300
bf790e39
ER
645@@ -799,4 +799,39 @@
646 });
be762003 647 });
bf790e39 648 });
be762003 649+
bf790e39 650+$(document).ready(function() {
be762003
ER
651+ // dialog type calender isn't working in Konqueror beacuse it's not a supported browser for either jQuery or jQuery UI
652+ // http://groups.google.com/group/jquery-ui/browse_thread/thread/ea61238c34cb5f33/046837b02fb90b5c
653+ if (navigator.appName != 'Konqueror') {
654+ $(".inline_date_pick").click(function() {
655+ var masterObj = this;
656+ var masterObjPos = $(masterObj).offset();
657+ // offset gives uses top and left but datepicker needs pageX and pageY
bf790e39 658+ var masterObjPos = {pageX: masterObjPos.left, pageY: masterObjPos.top};
be762003
ER
659+ $(this).datepicker(
660+ // we use dialog type calender so we won't haveto have a hidden element on the page
661+ 'dialog',
662+ // selected date
663+ masterObj.innerHTML,
664+ // onclick handler
665+ function (date, dteObj) {
666+ fieldName = masterObj.id.substr(0,masterObj.id.indexOf('|'));
667+ issueID = masterObj.id.substr(masterObj.id.indexOf('|')+1);
668+ $.post("/ajax/update.php", {fieldName: fieldName, issueID: issueID, day: dteObj.selectedDay, month: (dteObj.selectedMonth+1), year: dteObj.selectedYear}, function(data) {
669+ if (data.length > 0) {
670+ masterObj.innerHTML = data;
671+ }
672+ }, "text");
673+ },
674+ // config
675+ {dateFormat: 'dd M yy', duration: ""},
676+ // position of the datepicker calender - taken from div's offset
677+ masterObjPos
678+ );
679+ return false;
680+ });
681+ }
682+});
bf790e39
ER
683+
684 //-->
ae3e48e6
ER
685--- eventum/htdocs/list.php 2008-10-15 01:46:20.000000000 +0300
686+++ eventum-new/htdocs/list.php 2008-10-15 02:02:25.000000000 +0300
9fbd0c65
ER
687@@ -67,6 +67,11 @@
688 $profile['sort_by'] . "&sort_order=" . $profile['sort_order']);
689 }
690
691+@$reorder_usr_id = $_REQUEST["reorder_user"];
692+@$reorder_issue_id = $_REQUEST["reorder_source"];
693+@$reorder_neworder = $_REQUEST["reorder_neworder"];
694+Issue::reorderUserIssues($reorder_usr_id, $reorder_issue_id, $reorder_neworder);
695+
696 $options = Issue::saveSearchParams();
697 $tpl->assign("options", $options);
698 $tpl->assign("sorting", Issue::getSortingInfo($options));
be762003 699@@ -90,6 +95,21 @@
9fbd0c65 700 }
be762003 701 $assign_options += $users;
9fbd0c65
ER
702
703+// get the isu_order (assignated users) ordering user
704+if (!empty($options["users"])) {
705+ if ($options["users"] == -2) {
706+ $isu_order_user = $usr_id;
707+ } else
708+ if ($options["users"] > 0) {
709+ $isu_order_user = $options["users"];
710+ } else {
711+ unset($isu_order_user);
712+ }
713+} else {
714+ unset($isu_order_user);
715+}
716+$tpl->assign("isu_order_user", $isu_order_user);
717+
718 $list = Issue::getListing($prj_id, $options, $pagerRow, $rows);
719 $tpl->assign("list", $list["list"]);
720 $tpl->assign("list_info", $list["info"]);
be762003
ER
721--- eventum/templates/list.tpl.html 2008-10-15 01:46:20.000000000 +0300
722+++ eventum-new/templates/list.tpl.html 2008-10-15 02:02:25.000000000 +0300
9fbd0c65
ER
723@@ -89,6 +89,28 @@
724 f.target = '_popup';
725 f.submit();
726 }
727+function reorderBulk(order_user, neworder)
728+{
729+ url = page_url + "?";
730+ url += "reorder_user=" + order_user;
d95f9b26 731+
9fbd0c65
ER
732+ items = document.getElementsByName("item[]");
733+ checkedcount = 0;
734+ for (var i = 0; i < items.length; i++) {
d95f9b26
ER
735+ if (items[i].checked) {
736+ url += "&reorder_source[" + checkedcount + "]=" + items[i].value;
737+ checkedcount++;
738+ }
9fbd0c65
ER
739+ }
740+ if (checkedcount == 0) {
d95f9b26 741+ alert('{/literal}{t escape=js}Please choose which issues to move to the new place.{/t}{literal}');
9fbd0c65
ER
742+ return false;
743+ }
744+
745+ url += "&reorder_neworder=" + neworder;
746+
747+ window.location.href = url;
748+}
749 function hideClosed(f)
750 {
751 if (f.hide_closed.checked) {
be762003
ER
752@@ -150,6 +172,13 @@
753 f.go.disabled = true;
754 }
755 }
756+function updateCustomFields(issue_id)
757+{
758+ var features = 'width=560,height=460,top=30,left=30,resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no';
759+ var customWin = window.open('custom_fields.php?issue_id=' + issue_id, '_custom_fields', features);
760+ customWin.focus();
761+ return false;
762+}
763 //-->
764 </script>
765 {/literal}
766@@ -166,11 +195,11 @@
767 <input type="hidden" name="cat" value="bulk_update">
768 <tr>
769 <td>
770- <table bgcolor="#FFFFFF" width="100%" cellspacing="1" cellpadding="2" border="0">
771- <tr>
772+ <table bgcolor="#FFFFFF" width="100%" cellspacing="1" cellpadding="2" border="0" id="issue_list_table">
773+ <tr class="nodrag">
774 <td colspan="{$col_count}" class="default">
775 <table width="100%" cellspacing="0" cellpadding="0" border="0">
776- <tr>
777+ <tr class="nodrag">
778 <td class="default">
779 <b>{t}Search Results{/t} ({$list_info.total_rows} {t}issues found{/t}{if $list_info.end_offset > 0}, {math equation="x + 1" x=$list_info.start_offset} - {$list_info.end_offset} {t}shown{/t}{/if})</b>
780 {include file="help_link.tpl.html" topic="list"}
781@@ -190,7 +219,7 @@
782 </table>
783 </td>
784 </tr>
785- <tr bgcolor="{$cell_color}">
786+ <tr bgcolor="{$cell_color}" class="nodrag">
787 {if $current_role > $roles.developer}
788 <td width="1%">
789 <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');toggleBulkUpdate();">
790@@ -205,7 +234,7 @@
791 {if $sorting.images[$fld_name_id] != ""}<a title="{t}sort by{/t} {$fld_title|escape:"html"}" href="{$sorting.links[$fld_name_id]}" class="white_link"><img border="0" src="{$sorting.images[$fld_name_id]}"></a>{/if}
9fbd0c65 792 </td>
be762003 793 {/foreach}
9fbd0c65 794- {else}
9fbd0c65
ER
795+ {elseif $field_name != 'isu_order' || $isu_order_user}
796 <td align="{$column.align|default:'center'}" class="default_white" nowrap {if $column.width != ''}width="{$column.width}"{/if}>
797 {if $field_name == 'iss_summary'}
798 <table cellspacing="0" cellpadding="1" width="100%">
be762003 799@@ -221,6 +250,9 @@
9fbd0c65
ER
800 </table>
801 {elseif $sorting.links[$field_name] != ''}
be762003 802 <a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link">{$column.title}</a>
d95f9b26
ER
803+ {if $field_name == 'isu_order'}
804+ <br>{$users[$isu_order_user]}
805+ {/if}
be762003 806 {if $sorting.images[$field_name] != ""}<a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link"><img border="0" src="{$sorting.images[$field_name]}"></a>{/if}
9fbd0c65
ER
807 {else}
808 {$column.title}
be762003
ER
809@@ -229,19 +261,20 @@
810 {/if}
811 {/foreach}
812 </tr>
813+ <tbody>
814 {section name="i" loop=$list}
815- <tr {if $current_role >= $roles.developer AND $list[i].iqu_status > 0}style="text-decoration: line-through;"{/if}>
816+ <tr {if $current_role >= $roles.developer AND $list[i].iqu_status > 0}style="text-decoration: line-through;"{/if} id="{$list[i].iss_id}" {if !$list[i].assigned_users_order[$current_user_id]}class="nodrag"{/if}>
817 {if $current_role > $roles.developer}
818 <td bgcolor="{$list[i].status_color}" width="1%" class="default" align="center"><input type="checkbox" name="item[]" value="{$list[i].iss_id}" onchange="toggleBulkUpdate();"></td>
819 {/if}
820 {foreach from=$columns item=column key=field_name}
821 {if $field_name == 'custom_fields'}
822 {foreach from=$list[i].custom_field key=fld_id item=fld_value}
823- <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
824- {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
825+ <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default custom_field" onclick="return updateCustomFields({$list[i].iss_id});">
826+ {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
9fbd0c65
ER
827 </td>
828 {/foreach}
829- {else}
830+ {elseif $field_name != 'isu_order' || $isu_order_user}
831 <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
832 {if $field_name == 'iss_id'}
be762003
ER
833 <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_id}</a>
834@@ -276,7 +309,7 @@
835 {elseif $field_name == 'iss_percent_complete'}
836 {$list[i].iss_percent_complete|escape:"html"}%
837 {elseif $field_name == 'iss_expected_resolution_date'}
838- {$list[i].iss_expected_resolution_date|escape:"html"}
839+ <div class="inline_date_pick" id="expected_resolution_date|{$list[i].iss_id}">{$list[i].iss_expected_resolution_date|escape:"html"}&nbsp;</div>
840 {elseif $field_name == 'iss_summary'}
841 <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_summary|escape:"html"}</a>
842 {if $list[i].redeemed}
843@@ -285,6 +318,10 @@
9fbd0c65
ER
844 {if $list[i].iss_private == 1}
845 <b>[Private]</b>
be762003
ER
846 {/if}
847+ {elseif $field_name == 'isu_order'}
848+ {if $list[i].assigned_users_order[$current_user_id]}
849+ <img src="{$rel_url}images/updown.gif" alt="move">
d95f9b26 850+ {/if}
9fbd0c65
ER
851 {/if}
852 </td>
853 {/if}
be762003
ER
854@@ -297,10 +334,11 @@
855 </td>
856 </tr>
857 {/section}
858- <tr bgcolor="{$cell_color}">
859+ </tbody>
860+ <tr bgcolor="{$cell_color}" class="nodrag">
861 <td colspan="{$col_count}">
862 <table width="100%" cellspacing="0" cellpadding="0">
863- <tr>
864+ <tr class="nodrag">
865 <td width="30%" nowrap>
866 {if $current_role > $roles.developer}
867 <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');">
868@@ -352,6 +390,29 @@
869 </form>
870 </table>
871 <br />
872-
873+<script type="text/javascript">
874+{*
875+ * Order issues by drag and drop:
876+ * only if sorted by order and viewing your own issues
877+ *}
878+{if $options.sort_by == "isu_order" and $current_user_id == $isu_order_user}
879+{literal}
880+var before = ''; // make it global variable
881+$('#issue_list_table').tableDnD({
882+ onDragClass: "tDnD_whileDrag",
883+ onDragStart: function(table, row) {
884+ before = $.tableDnD.serialize('id');
885+ },
886+ onDrop: function(table, row) {
887+ $.post("/ajax/order.php", {before: before, after: $.tableDnD.serialize('id')}, function(data) {
888+ if (data.length > 0) {
889+ alert(data);
890+ }
891+ }, "text");
892+ }
893+});
894+{/literal}
895+{/if}
896+</script>
897 {include file="app_info.tpl.html"}
898-{include file="footer.tpl.html"}
899+{include file="footer.tpl.html"}
900\ No newline at end of file
ae3e48e6
ER
901--- ./htdocs/js/jquery/jquery.tablednd.js (revision 0)
902+++ ./htdocs/js/jquery/jquery.tablednd.js (revision 0)
621b0b5e
ER
903@@ -0,0 +1,382 @@
904+/**
905+ * TableDnD plug-in for JQuery, allows you to drag and drop table rows
906+ * You can set up various options to control how the system will work
907+ * Copyright (c) Denis Howlett <denish@isocra.com>
908+ * Licensed like jQuery, see http://docs.jquery.com/License.
909+ *
910+ * Configuration options:
911+ *
912+ * onDragStyle
913+ * This is the style that is assigned to the row during drag. There are limitations to the styles that can be
914+ * associated with a row (such as you can't assign a border--well you can, but it won't be
915+ * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
916+ * a map (as used in the jQuery css(...) function).
917+ * onDropStyle
918+ * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
919+ * to what you can do. Also this replaces the original style, so again consider using onDragClass which
920+ * is simply added and then removed on drop.
921+ * onDragClass
922+ * This class is added for the duration of the drag and then removed when the row is dropped. It is more
923+ * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
924+ * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
925+ * stylesheet.
926+ * onDrop
927+ * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
928+ * and the row that was dropped. You can work out the new order of the rows by using
929+ * table.rows.
930+ * onDragStart
931+ * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
932+ * table and the row which the user has started to drag.
933+ * onAllowDrop
934+ * Pass a function that will be called as a row is over another row. If the function returns true, allow
935+ * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
936+ * the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
937+ * scrollAmount
938+ * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
939+ * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
940+ * FF3 beta
941+ * dragHandle
942+ * This is the name of a class that you assign to one or more cells in each row that is draggable. If you
943+ * specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
944+ * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
945+ * the whole row is draggable.
946+ *
947+ * Other ways to control behaviour:
948+ *
949+ * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
950+ * that you don't want to be draggable.
951+ *
952+ * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
953+ * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
954+ * an ID as must all the rows.
955+ *
956+ * Other methods:
957+ *
958+ * $("...").tableDnDUpdate()
959+ * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
960+ * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
961+ * The table maintains the original configuration (so you don't have to specify it again).
962+ *
963+ * $("...").tableDnDSerialize()
964+ * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
965+ * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
966+ *
967+ * Known problems:
968+ * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
969+ *
970+ * Version 0.2: 2008-02-20 First public version
971+ * Version 0.3: 2008-02-07 Added onDragStart option
972+ * Made the scroll amount configurable (default is 5 as before)
973+ * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
974+ * Added onAllowDrop to control dropping
975+ * Fixed a bug which meant that you couldn't set the scroll amount in both directions
976+ * Added serialize method
977+ * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
978+ * draggable
979+ * Improved the serialize method to use a default (and settable) regular expression.
980+ * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
981+ */
982+jQuery.tableDnD = {
983+ /** Keep hold of the current table being dragged */
984+ currentTable : null,
985+ /** Keep hold of the current drag object if any */
986+ dragObject: null,
987+ /** The current mouse offset */
988+ mouseOffset: null,
989+ /** Remember the old value of Y so that we don't do too much processing */
990+ oldY: 0,
991+
992+ /** Actually build the structure */
993+ build: function(options) {
994+ // Set up the defaults if any
995+
996+ this.each(function() {
997+ // This is bound to each matching table, set up the defaults and override with user options
998+ this.tableDnDConfig = jQuery.extend({
999+ onDragStyle: null,
1000+ onDropStyle: null,
1001+ // Add in the default class for whileDragging
1002+ onDragClass: "tDnD_whileDrag",
1003+ onDrop: null,
1004+ onDragStart: null,
1005+ scrollAmount: 5,
1006+ serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
1007+ serializeParamName: null, // If you want to specify another parameter name instead of the table ID
1008+ dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
1009+ }, options || {});
1010+ // Now make the rows draggable
1011+ jQuery.tableDnD.makeDraggable(this);
1012+ });
1013+
1014+ // Now we need to capture the mouse up and mouse move event
1015+ // We can use bind so that we don't interfere with other event handlers
1016+ jQuery(document)
1017+ .bind('mousemove', jQuery.tableDnD.mousemove)
1018+ .bind('mouseup', jQuery.tableDnD.mouseup);
1019+
1020+ // Don't break the chain
1021+ return this;
1022+ },
1023+
1024+ /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
1025+ makeDraggable: function(table) {
1026+ var config = table.tableDnDConfig;
1027+ if (table.tableDnDConfig.dragHandle) {
1028+ // We only need to add the event to the specified cells
1029+ var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table);
1030+ cells.each(function() {
1031+ // The cell is bound to "this"
1032+ jQuery(this).mousedown(function(ev) {
1033+ jQuery.tableDnD.dragObject = this.parentNode;
1034+ jQuery.tableDnD.currentTable = table;
1035+ jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
1036+ if (config.onDragStart) {
1037+ // Call the onDrop method if there is one
1038+ config.onDragStart(table, this);
1039+ }
1040+ return false;
1041+ });
1042+ })
1043+ } else {
1044+ // For backwards compatibility, we add the event to the whole row
1045+ var rows = jQuery("tr", table); // get all the rows as a wrapped set
1046+ rows.each(function() {
1047+ // Iterate through each row, the row is bound to "this"
1048+ var row = jQuery(this);
1049+ if (! row.hasClass("nodrag")) {
1050+ row.mousedown(function(ev) {
1051+ if (ev.target.tagName == "TD") {
1052+ jQuery.tableDnD.dragObject = this;
1053+ jQuery.tableDnD.currentTable = table;
1054+ jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
1055+ if (config.onDragStart) {
1056+ // Call the onDrop method if there is one
1057+ config.onDragStart(table, this);
1058+ }
1059+ return false;
1060+ }
1061+ }).css("cursor", "move"); // Store the tableDnD object
1062+ }
1063+ });
1064+ }
1065+ },
1066+
1067+ updateTables: function() {
1068+ this.each(function() {
1069+ // this is now bound to each matching table
1070+ if (this.tableDnDConfig) {
1071+ jQuery.tableDnD.makeDraggable(this);
1072+ }
1073+ })
1074+ },
1075+
1076+ /** Get the mouse coordinates from the event (allowing for browser differences) */
1077+ mouseCoords: function(ev){
1078+ if(ev.pageX || ev.pageY){
1079+ return {x:ev.pageX, y:ev.pageY};
1080+ }
1081+ return {
1082+ x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
1083+ y:ev.clientY + document.body.scrollTop - document.body.clientTop
1084+ };
1085+ },
1086+
1087+ /** Given a target element and a mouse event, get the mouse offset from that element.
1088+ To do this we need the element's position and the mouse position */
1089+ getMouseOffset: function(target, ev) {
1090+ ev = ev || window.event;
1091+
1092+ var docPos = this.getPosition(target);
1093+ var mousePos = this.mouseCoords(ev);
1094+ return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
1095+ },
1096+
1097+ /** Get the position of an element by going up the DOM tree and adding up all the offsets */
1098+ getPosition: function(e){
1099+ var left = 0;
1100+ var top = 0;
1101+ /** Safari fix -- thanks to Luis Chato for this! */
1102+ if (e.offsetHeight == 0) {
1103+ /** Safari 2 doesn't correctly grab the offsetTop of a table row
1104+ this is detailed here:
1105+ http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
1106+ the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
1107+ note that firefox will return a text node as a first child, so designing a more thorough
1108+ solution may need to take that into account, for now this seems to work in firefox, safari, ie */
1109+ e = e.firstChild; // a table cell
1110+ }
1111+
1112+ while (e.offsetParent){
1113+ left += e.offsetLeft;
1114+ top += e.offsetTop;
1115+ e = e.offsetParent;
1116+ }
1117+
1118+ left += e.offsetLeft;
1119+ top += e.offsetTop;
1120+
1121+ return {x:left, y:top};
1122+ },
1123+
1124+ mousemove: function(ev) {
1125+ if (jQuery.tableDnD.dragObject == null) {
1126+ return;
1127+ }
1128+
1129+ var dragObj = jQuery(jQuery.tableDnD.dragObject);
1130+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
1131+ var mousePos = jQuery.tableDnD.mouseCoords(ev);
1132+ var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
1133+ //auto scroll the window
1134+ var yOffset = window.pageYOffset;
1135+ if (document.all) {
1136+ // Windows version
1137+ //yOffset=document.body.scrollTop;
1138+ if (typeof document.compatMode != 'undefined' &&
1139+ document.compatMode != 'BackCompat') {
1140+ yOffset = document.documentElement.scrollTop;
1141+ }
1142+ else if (typeof document.body != 'undefined') {
1143+ yOffset=document.body.scrollTop;
1144+ }
1145+
1146+ }
1147+
1148+ if (mousePos.y-yOffset < config.scrollAmount) {
1149+ window.scrollBy(0, -config.scrollAmount);
1150+ } else {
1151+ var windowHeight = window.innerHeight ? window.innerHeight
1152+ : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
1153+ if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
1154+ window.scrollBy(0, config.scrollAmount);
1155+ }
1156+ }
1157+
1158+
1159+ if (y != jQuery.tableDnD.oldY) {
1160+ // work out if we're going up or down...
1161+ var movingDown = y > jQuery.tableDnD.oldY;
1162+ // update the old value
1163+ jQuery.tableDnD.oldY = y;
1164+ // update the style to show we're dragging
1165+ if (config.onDragClass) {
1166+ dragObj.addClass(config.onDragClass);
1167+ } else {
1168+ dragObj.css(config.onDragStyle);
1169+ }
1170+ // If we're over a row then move the dragged row to there so that the user sees the
1171+ // effect dynamically
1172+ var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
1173+ if (currentRow) {
1174+ // TODO worry about what happens when there are multiple TBODIES
1175+ if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
1176+ jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
1177+ } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
1178+ jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
1179+ }
1180+ }
1181+ }
1182+
1183+ return false;
1184+ },
1185+
1186+ /** We're only worried about the y position really, because we can only move rows up and down */
1187+ findDropTargetRow: function(draggedRow, y) {
1188+ var rows = jQuery.tableDnD.currentTable.rows;
1189+ for (var i=0; i<rows.length; i++) {
1190+ var row = rows[i];
1191+ var rowY = this.getPosition(row).y;
1192+ var rowHeight = parseInt(row.offsetHeight)/2;
1193+ if (row.offsetHeight == 0) {
1194+ rowY = this.getPosition(row.firstChild).y;
1195+ rowHeight = parseInt(row.firstChild.offsetHeight)/2;
1196+ }
1197+ // Because we always have to insert before, we need to offset the height a bit
1198+ if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
1199+ // that's the row we're over
1200+ // If it's the same as the current row, ignore it
1201+ if (row == draggedRow) {return null;}
1202+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
1203+ if (config.onAllowDrop) {
1204+ if (config.onAllowDrop(draggedRow, row)) {
1205+ return row;
1206+ } else {
1207+ return null;
1208+ }
1209+ } else {
1210+ // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
1211+ var nodrop = jQuery(row).hasClass("nodrop");
1212+ if (! nodrop) {
1213+ return row;
1214+ } else {
1215+ return null;
1216+ }
1217+ }
1218+ return row;
1219+ }
1220+ }
1221+ return null;
1222+ },
1223+
1224+ mouseup: function(e) {
1225+ if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
1226+ var droppedRow = jQuery.tableDnD.dragObject;
1227+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
1228+ // If we have a dragObject, then we need to release it,
1229+ // The row will already have been moved to the right place so we just reset stuff
1230+ if (config.onDragClass) {
1231+ jQuery(droppedRow).removeClass(config.onDragClass);
1232+ } else {
1233+ jQuery(droppedRow).css(config.onDropStyle);
1234+ }
1235+ jQuery.tableDnD.dragObject = null;
1236+ if (config.onDrop) {
1237+ // Call the onDrop method if there is one
1238+ config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
1239+ }
1240+ jQuery.tableDnD.currentTable = null; // let go of the table too
1241+ }
1242+ },
1243+
1244+ serialize: function() {
1245+ if (jQuery.tableDnD.currentTable) {
1246+ return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
1247+ } else {
1248+ return "Error: No Table id set, you need to set an id on your table and every row";
1249+ }
1250+ },
1251+
1252+ serializeTable: function(table) {
1253+ var result = "";
1254+ var tableId = table.id;
1255+ var rows = table.rows;
1256+ for (var i=0; i<rows.length; i++) {
1257+ if (result.length > 0) result += "&";
1258+ var rowId = rows[i].id;
1259+ if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
1260+ rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
1261+ }
1262+
1263+ result += tableId + '[]=' + rowId;
1264+ }
1265+ return result;
1266+ },
1267+
1268+ serializeTables: function() {
1269+ var result = "";
1270+ this.each(function() {
1271+ // this is now bound to each matching table
1272+ result += jQuery.tableDnD.serializeTable(this);
1273+ });
1274+ return result;
1275+ }
1276+
1277+}
1278+
1279+jQuery.fn.extend(
1280+ {
1281+ tableDnD : jQuery.tableDnD.build,
1282+ tableDnDUpdate : jQuery.tableDnD.updateTables,
1283+ tableDnDSerialize: jQuery.tableDnD.serializeTables
1284+ }
1285+);
1286\ No newline at end of file
e26b8884
ER
1287--- eventum-2.2/templates/header.tpl.html~ 2009-06-30 02:07:28.000000000 +0300
1288+++ eventum-2.2/templates/header.tpl.html 2009-06-30 02:07:49.981934267 +0300
621b0b5e 1289@@ -8,6 +8,7 @@
3197cdb4
ER
1290 <script type="text/javascript" src="{$rel_url}js/jquery/form.js?c=9984"></script>
1291 <script type="text/javascript" src="{$rel_url}js/jquery/blockui.js?c=eb13"></script>
1292 <script type="text/javascript" src="{$rel_url}js/jquery/ui.datepicker.js?c=a911"></script>
ae3e48e6 1293+<script type="text/javascript" src="{$rel_url}js/jquery/jquery.tablednd.js"></script>
e26b8884 1294 <link rel="stylesheet" href="{$rel_url}js/jquery/ui.datepicker.css?c=5096">
ae3e48e6 1295 <script type="text/javascript">
621b0b5e 1296 <!--
ae3e48e6
ER
1297--- eventum-r3765/htdocs/css/style.css~ 2008-06-19 08:30:31.000000000 +0300
1298+++ eventum-r3765/htdocs/css/style.css 2008-10-29 17:28:49.393768970 +0200
c2cb0c93
ER
1299@@ -172,4 +172,9 @@
1300 font-size: 70%;
1301 font-family: Verdana, Arial, Helvetica, sans-serif;
1302 padding: 10px;
1303-}
1304\ No newline at end of file
1305+}
1306+
1307+.tDnD_whileDrag td {
1308+ background-color: #ffffdd;
1309+ border: 1px solid red;
1310+}
ae3e48e6
ER
1311--- eventum~/htdocs/css/style.css 2008-11-06 16:54:30.000000000 +0200
1312+++ eventum/htdocs/css/style.css 2008-11-06 16:20:50.038350957 +0200
d0dabd47
ER
1313@@ -173,8 +173,21 @@
1314 font-family: Verdana, Arial, Helvetica, sans-serif;
1315 padding: 10px;
1316 }
1317-
1318 .tDnD_whileDrag td {
1319- background-color: #ffffdd;
1320- border: 1px solid red;
1321+ background-color: #ffffdd;
1322+}
1323+.tDnD_whileDrag td {
1324+ border: 1px solid red;
1325+}
1326+.inline_date_pick {
1327+ cursor: pointer;
1328+}
1329+.custom_field {
1330+ cursor: pointer;
1331+}
1332+.showDragHandle {
1333+ cursor: move;
1334+ background-image: url(../images/updown2.gif);
1335+ background-repeat: no-repeat;
1336+ background-position: center center;
1337 }
1338--- eventum~/templates/list.tpl.html 2008-11-06 16:54:30.000000000 +0200
1339+++ eventum/templates/list.tpl.html 2008-11-11 11:22:36.206950437 +0200
1340@@ -250,9 +250,6 @@
1341 </table>
1342 {elseif $sorting.links[$field_name] != ''}
1343 <a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link">{$column.title}</a>
1344- {if $field_name == 'isu_order'}
1345- <br>{$users[$isu_order_user]}
1346- {/if}
1347 {if $sorting.images[$field_name] != ""}<a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link"><img border="0" src="{$sorting.images[$field_name]}"></a>{/if}
1348 {else}
1349 {$column.title}
1350@@ -275,7 +272,7 @@
1351 </td>
1352 {/foreach}
1353 {elseif $field_name != 'isu_order' || $isu_order_user}
1354- <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
1355+ <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default{if $field_name == 'isu_order'} dragHandle{/if}">
1356 {if $field_name == 'iss_id'}
1357 <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_id}</a>
1358 {elseif $field_name == 'pri_rank'}
1359@@ -319,9 +316,7 @@
1360 <b>[Private]</b>
1361 {/if}
1362 {elseif $field_name == 'isu_order'}
1363- {if $list[i].assigned_users_order[$current_user_id]}
1364- <img src="{$rel_url}images/updown.gif" alt="move">
1365- {/if}
1366+ {if $options.sort_by == "isu_order" and $current_user_id == $isu_order_user}&nbsp;{/if}
1367 {/if}
1368 </td>
1369 {/if}
1370@@ -334,7 +329,7 @@
1371 </td>
1372 </tr>
1373 {/section}
1374- </tbody>
1375+ </tbody>
1376 <tr bgcolor="{$cell_color}" class="nodrag">
1377 <td colspan="{$col_count}">
1378 <table width="100%" cellspacing="0" cellpadding="0">
1379@@ -409,10 +404,16 @@
1380 alert(data);
1381 }
1382 }, "text");
1383- }
1384+ },
1385+ dragHandle: "dragHandle"
1386+});
1387+$("#issue_list_table tr").hover(function() {
1388+ $('#' + this.id + ' .dragHandle').addClass('showDragHandle');
1389+}, function() {
1390+ $('#' + this.id + ' .dragHandle').removeClass('showDragHandle');
1391 });
1392 {/literal}
1393 {/if}
1394 </script>
1395 {include file="app_info.tpl.html"}
1396-{include file="footer.tpl.html"}
1397\ No newline at end of file
1398+{include file="footer.tpl.html"}
ae3e48e6
ER
1399--- eventum-r3776/upgrade/update-database.php 2008-11-18 01:10:18.806283202 +0200
1400+++ eventum/upgrade/update-database.php 2008-11-18 01:15:20.703082655 +0200
1f67a110
ER
1401@@ -83,6 +83,7 @@
1402 1 => '01_notes.php',
1403 2 => '02_usr_alias.php',
1404 3 => '03_prj_mail_aliases.php',
1405+ 4 => '04_isu_order.php',
1406 );
1407
1408 // sanity check. check that the version table exists.
1409--- /dev/null 2008-11-04 20:33:38.146691408 +0200
ae3e48e6 1410+++ eventum/upgrade/patches/04_isu_order.php 2008-11-18 01:14:02.972104347 +0200
1f67a110
ER
1411@@ -0,0 +1,15 @@
1412+<?php
1413+
1414+function db_patch_4() {
1415+ $stmts = array();
1416+
1417+ $columns = db_getCol('DESC %TABLE_PREFIX%issue_user');
1418+ if (in_array('isu_order', $columns)) {
1419+ return $stmts;
1420+ }
1421+
1422+ $stmts[] = "ALTER TABLE %TABLE_PREFIX%issue_user ADD isu_order int(11) NOT NULL DEFAULT '0' AFTER isu_assigned_date, ADD INDEX isu_order (isu_order)";
1423+ $stmts[] = "UPDATE %TABLE_PREFIX%issue_user set isu_order=isu_iss_id";
1424+
1425+ return $stmts;
1426+}
ae3e48e6
ER
1427--- ../eventum-old2/htdocs/ajax/update.php 2008-10-13 11:14:23.673428821 +0300
1428+++ ./htdocs/ajax/update.php 2008-11-19 15:05:54.490054961 +0200
8022c588
ER
1429@@ -16,9 +16,17 @@
1430 case 'expected_resolution_date':
1431 $day = (int)$_POST['day'];
1432 $month = (int)$_POST['month'];
1433- $year = (int)$_POST['year'];
1434- if (Issue::updateField($_POST['issueID'], $_POST['fieldName'], sprintf('%04d-%02d-%02d', $year, $month, $day)) !== -1) {
c2e7ae63 1435- echo Date_Helper::getSimpleDate(sprintf('%04d-%02d-%02d', $year, $month, $day), false);
8022c588
ER
1436+ $year = (int)$_POST['year'];
1437+ if ($day == 0 && $month == 1 && $year == 0) {
1438+ // clear button
1439+ $date = null;
1440+ } else {
1441+ $date = sprintf('%04d-%02d-%02d', $year, $month, $day);
1442+ }
1443+ if (Issue::updateField($_POST['issueID'], $_POST['fieldName'], $date) !== -1) {
1444+ if (!is_null($date)) {
c2e7ae63 1445+ echo Date_Helper::getSimpleDate(sprintf('%04d-%02d-%02d', $year, $month, $day), false);
8022c588
ER
1446+ }
1447 } else {
1448 echo 'update failed';
1449 }
ae3e48e6
ER
1450--- eventum_tar/htdocs/js/global.js 2008-11-06 16:54:30.000000000 +0200
1451+++ ./htdocs/js/global.js 2008-11-19 15:06:21.820071605 +0200
8022c588
ER
1452@@ -817,10 +817,15 @@
1453 function (date, dteObj) {
1454 fieldName = masterObj.id.substr(0,masterObj.id.indexOf('|'));
1455 issueID = masterObj.id.substr(masterObj.id.indexOf('|')+1);
1456+ if (date == '') {
1457+ // clear button
1458+ dteObj.selectedDay = 0;
1459+ dteObj.selectedMonth = 0;
1460+ dteObj.selectedYear = 0;
1461+ }
1462+ //alertProperties(date);
1463 $.post("/ajax/update.php", {fieldName: fieldName, issueID: issueID, day: dteObj.selectedDay, month: (dteObj.selectedMonth+1), year: dteObj.selectedYear}, function(data) {
1464- if (data.length > 0) {
1465- masterObj.innerHTML = data;
1466- }
1467+ masterObj.innerHTML = data;
1468 }, "text");
1469 },
1470 // config
ae3e48e6
ER
1471--- eventum-old2/lib/eventum/class.issue.php 2008-10-15 18:30:31.590172372 +0300
1472+++ ./lib/eventum/class.issue.php 2008-11-19 15:04:43.710659328 +0200
8022c588
ER
1473@@ -1694,7 +1694,11 @@
1474 $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
1475 break;
1476 case 'expected_resolution_date':
1477- $stmt .= ", iss_expected_resolution_date = '" . Misc::escapeString($filed_value) . "'";
1478+ if (is_null($filed_value)) {
1479+ $stmt .= ", iss_expected_resolution_date = null";
1480+ } else {
1481+ $stmt .= ", iss_expected_resolution_date = '" . Misc::escapeString($filed_value) . "'";
1482+ }
1483 break;
1484 case 'release':
1485 $stmt .= ", iss_pre_id = " . Misc::escapeInteger($filed_value);
ae3e48e6
ER
1486--- eventum/lib/eventum/class.issue.php 2008-10-15 01:46:20.000000000 +0300
1487+++ eventum-new/lib/eventum/class.issue.php 2008-10-15 02:02:25.000000000 +0300
342dba98
ER
1488@@ -2050,7 +2050,7 @@
1489 " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_user
1490 WHERE
1491 isu_iss_id IN ($issue_id)";
1492- if ($usr_id !== FALSE) {
1493+ if (!empty($usr_id)) {
1494 $stmt.= " AND isu_usr_id IN ($usr_id)";
1495 }
1496 $stmt.= "ORDER BY isu_order";
1497@@ -2135,7 +2135,6 @@
1498 if ($usr_id) {
1499 History::add($issue_id, $usr_id, History::getTypeID('user_all_unassociated'), 'Issue assignments removed by ' . User::getFullName($usr_id));
1500 }
3197cdb4 1501- self::rearrangeDeleteUserAsssociationOrderList($deleted_order_list);
342dba98
ER
1502 return 1;
1503 }
1504 }
This page took 1.257289 seconds and 4 git commands to generate.