]> git.pld-linux.org Git - packages/eventum.git/blame - eventum-order.patch
- order patch now works somewhat
[packages/eventum.git] / eventum-order.patch
CommitLineData
29e18d8b 1--- eventum-new/ajax/order.php 2008-10-15 02:02:25.000000000 +0300
be762003
ER
2+++ eventum-new/ajax/order.php 2008-10-15 02:02:25.000000000 +0300
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";
68+ $res = $GLOBALS["db_api"]->dbh->query($stmt);
69+ if (PEAR::isError($res)) {
70+ Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
71+ die('update failed');
72+ }
73+ }
74+ }
75+}
be762003
ER
76--- eventum/ajax/update.php 2008-10-15 01:46:20.000000000 +0300
77+++ eventum-new/ajax/update.php 2008-10-15 02:02:25.000000000 +0300
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) {
99+ echo Date_API::getSimpleDate(sprintf('%04d-%02d-%02d', $year, $month, $day), false);
100+ } else {
101+ echo 'update failed';
102+ }
103+ exit;
104+ break;
105+ default:
106+ die('object type not supported');
107+ break;
108+}
be762003
ER
109--- eventum/include/class.display_column.php 2008-10-15 01:46:20.000000000 +0300
110+++ eventum-new/include/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];
be762003
ER
123--- eventum/include/class.issue.php 2008-10-15 01:46:20.000000000 +0300
124+++ eventum-new/include/class.issue.php 2008-10-15 02:02:25.000000000 +0300
125@@ -1356,6 +1356,7 @@
9fbd0c65
ER
126 Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
127 return -1;
128 } else {
129+ Issue::moveOrderForAllUsers($issue_id, 1000);
130 $prj_id = Issue::getProjectID($issue_id);
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();
151+ $prj_id = Issue::getProjectID($issue_id);
152+
153+ // get all of the 'current' information of this issue
154+ $current = Issue::getDetails($issue_id);
155+
156+ $stmt = "UPDATE
157+ " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
158+ SET
159+ iss_updated_date='" . Date_API::getCurrentDateGMT() . "',
160+ iss_last_public_action_date='" . Date_API::getCurrentDateGMT() . "',
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+
216+ $res = $GLOBALS["db_api"]->dbh->query($stmt);
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) {
263+ Issue::clearClosed($issue_id);
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";
323+ $res = $GLOBALS["db_api"]->dbh->query($stmt);
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,
340- '" . Date_API::getCurrentDateGMT() . "'
341+ '" . Date_API::getCurrentDateGMT() . "',
342+ $order
343 )";
344 $res = $GLOBALS["db_api"]->dbh->query($stmt);
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";
369+ $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
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+ }
412+ $res = $GLOBALS["db_api"]->dbh->query($stmt);
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 }
429+ $deleted_order_list = Issue::getDeleteUserAssociationOrderList($issue_id);
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 }
437+ Issue::rearrangeDeleteUserAsssociationOrderList($deleted_order_list);
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);
445+ $delete_order_list = Issue::getDeleteUserAssociationOrderList($issue_id, $usr_id);
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 }
453+ Issue::rearrangeDeleteUserAssociationOrderList($delete_order_list);
454 return 1;
455 }
456 }
be762003 457@@ -2379,6 +2643,11 @@
9fbd0c65
ER
458 {
459 $sort_by = Issue::getParam('sort_by');
460 $sort_order = Issue::getParam('sort_order');
461+ $users = Issue::getParam('users');
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+ }
466 $rows = Issue::getParam('rows');
467 $hide_closed = Issue::getParam('hide_closed');
468 if ($hide_closed === '') {
be762003 469@@ -2483,6 +2752,7 @@
aa90cfdb
ER
470 "last_action_date" => "desc",
471 "usr_full_name" => "asc",
472 "iss_expected_resolution_date" => "desc",
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 }
517+ Issue::moveOrderForAllUsers($issue_id, 1);
518 }
519
520
be762003 521@@ -4322,8 +4605,127 @@
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
554+ $orderlist = Issue::getDeleteUserAssociationOrderList($issue_id_str, $usr_id);
555+ Issue::rearrangeDeleteUserAssociationOrderList($orderlist);
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";
564+ $res = $GLOBALS["db_api"]->dbh->query($stmt);
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";
579+ $res = $GLOBALS["db_api"]->dbh->query($stmt);
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+
611+ $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
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);
631+ $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
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) {
637+ Issue::reorderUserIssues($row["isu_usr_id"], $issue_id, $neworder);
638+ }
639+ }
640+
641 }
642
643+
644+
645+
646 // benchmarking the included file (aka setup time)
647 if (APP_BENCHMARK) {
648 $GLOBALS['bench']->setMarker('Included Issue Class');
bf790e39
ER
649--- eventum-r3749/js/global.js~ 2008-10-15 02:03:48.000000000 +0300
650+++ eventum-r3749/js/global.js 2008-10-15 02:06:00.000000000 +0300
651@@ -799,4 +799,39 @@
652 });
be762003 653 });
bf790e39 654 });
be762003 655+
bf790e39 656+$(document).ready(function() {
be762003
ER
657+ // dialog type calender isn't working in Konqueror beacuse it's not a supported browser for either jQuery or jQuery UI
658+ // http://groups.google.com/group/jquery-ui/browse_thread/thread/ea61238c34cb5f33/046837b02fb90b5c
659+ if (navigator.appName != 'Konqueror') {
660+ $(".inline_date_pick").click(function() {
661+ var masterObj = this;
662+ var masterObjPos = $(masterObj).offset();
663+ // offset gives uses top and left but datepicker needs pageX and pageY
bf790e39 664+ var masterObjPos = {pageX: masterObjPos.left, pageY: masterObjPos.top};
be762003
ER
665+ $(this).datepicker(
666+ // we use dialog type calender so we won't haveto have a hidden element on the page
667+ 'dialog',
668+ // selected date
669+ masterObj.innerHTML,
670+ // onclick handler
671+ function (date, dteObj) {
672+ fieldName = masterObj.id.substr(0,masterObj.id.indexOf('|'));
673+ issueID = masterObj.id.substr(masterObj.id.indexOf('|')+1);
674+ $.post("/ajax/update.php", {fieldName: fieldName, issueID: issueID, day: dteObj.selectedDay, month: (dteObj.selectedMonth+1), year: dteObj.selectedYear}, function(data) {
675+ if (data.length > 0) {
676+ masterObj.innerHTML = data;
677+ }
678+ }, "text");
679+ },
680+ // config
681+ {dateFormat: 'dd M yy', duration: ""},
682+ // position of the datepicker calender - taken from div's offset
683+ masterObjPos
684+ );
685+ return false;
686+ });
687+ }
688+});
bf790e39
ER
689+
690 //-->
be762003
ER
691--- eventum/list.php 2008-10-15 01:46:20.000000000 +0300
692+++ eventum-new/list.php 2008-10-15 02:02:25.000000000 +0300
9fbd0c65
ER
693@@ -67,6 +67,11 @@
694 $profile['sort_by'] . "&sort_order=" . $profile['sort_order']);
695 }
696
697+@$reorder_usr_id = $_REQUEST["reorder_user"];
698+@$reorder_issue_id = $_REQUEST["reorder_source"];
699+@$reorder_neworder = $_REQUEST["reorder_neworder"];
700+Issue::reorderUserIssues($reorder_usr_id, $reorder_issue_id, $reorder_neworder);
701+
702 $options = Issue::saveSearchParams();
703 $tpl->assign("options", $options);
704 $tpl->assign("sorting", Issue::getSortingInfo($options));
be762003 705@@ -90,6 +95,21 @@
9fbd0c65 706 }
be762003 707 $assign_options += $users;
9fbd0c65
ER
708
709+// get the isu_order (assignated users) ordering user
710+if (!empty($options["users"])) {
711+ if ($options["users"] == -2) {
712+ $isu_order_user = $usr_id;
713+ } else
714+ if ($options["users"] > 0) {
715+ $isu_order_user = $options["users"];
716+ } else {
717+ unset($isu_order_user);
718+ }
719+} else {
720+ unset($isu_order_user);
721+}
722+$tpl->assign("isu_order_user", $isu_order_user);
723+
724 $list = Issue::getListing($prj_id, $options, $pagerRow, $rows);
725 $tpl->assign("list", $list["list"]);
726 $tpl->assign("list_info", $list["info"]);
be762003
ER
727--- eventum/templates/list.tpl.html 2008-10-15 01:46:20.000000000 +0300
728+++ eventum-new/templates/list.tpl.html 2008-10-15 02:02:25.000000000 +0300
9fbd0c65
ER
729@@ -89,6 +89,28 @@
730 f.target = '_popup';
731 f.submit();
732 }
733+function reorderBulk(order_user, neworder)
734+{
735+ url = page_url + "?";
736+ url += "reorder_user=" + order_user;
d95f9b26 737+
9fbd0c65
ER
738+ items = document.getElementsByName("item[]");
739+ checkedcount = 0;
740+ for (var i = 0; i < items.length; i++) {
d95f9b26
ER
741+ if (items[i].checked) {
742+ url += "&reorder_source[" + checkedcount + "]=" + items[i].value;
743+ checkedcount++;
744+ }
9fbd0c65
ER
745+ }
746+ if (checkedcount == 0) {
d95f9b26 747+ alert('{/literal}{t escape=js}Please choose which issues to move to the new place.{/t}{literal}');
9fbd0c65
ER
748+ return false;
749+ }
750+
751+ url += "&reorder_neworder=" + neworder;
752+
753+ window.location.href = url;
754+}
755 function hideClosed(f)
756 {
757 if (f.hide_closed.checked) {
be762003
ER
758@@ -150,6 +172,13 @@
759 f.go.disabled = true;
760 }
761 }
762+function updateCustomFields(issue_id)
763+{
764+ var features = 'width=560,height=460,top=30,left=30,resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no';
765+ var customWin = window.open('custom_fields.php?issue_id=' + issue_id, '_custom_fields', features);
766+ customWin.focus();
767+ return false;
768+}
769 //-->
770 </script>
771 {/literal}
772@@ -166,11 +195,11 @@
773 <input type="hidden" name="cat" value="bulk_update">
774 <tr>
775 <td>
776- <table bgcolor="#FFFFFF" width="100%" cellspacing="1" cellpadding="2" border="0">
777- <tr>
778+ <table bgcolor="#FFFFFF" width="100%" cellspacing="1" cellpadding="2" border="0" id="issue_list_table">
779+ <tr class="nodrag">
780 <td colspan="{$col_count}" class="default">
781 <table width="100%" cellspacing="0" cellpadding="0" border="0">
782- <tr>
783+ <tr class="nodrag">
784 <td class="default">
785 <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>
786 {include file="help_link.tpl.html" topic="list"}
787@@ -190,7 +219,7 @@
788 </table>
789 </td>
790 </tr>
791- <tr bgcolor="{$cell_color}">
792+ <tr bgcolor="{$cell_color}" class="nodrag">
793 {if $current_role > $roles.developer}
794 <td width="1%">
795 <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');toggleBulkUpdate();">
796@@ -205,7 +234,7 @@
797 {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 798 </td>
be762003 799 {/foreach}
9fbd0c65 800- {else}
9fbd0c65
ER
801+ {elseif $field_name != 'isu_order' || $isu_order_user}
802 <td align="{$column.align|default:'center'}" class="default_white" nowrap {if $column.width != ''}width="{$column.width}"{/if}>
803 {if $field_name == 'iss_summary'}
804 <table cellspacing="0" cellpadding="1" width="100%">
be762003 805@@ -221,6 +250,9 @@
9fbd0c65
ER
806 </table>
807 {elseif $sorting.links[$field_name] != ''}
be762003 808 <a title="{t}sort by{/t} {$column.title}" href="{$sorting.links[$field_name]}" class="white_link">{$column.title}</a>
d95f9b26
ER
809+ {if $field_name == 'isu_order'}
810+ <br>{$users[$isu_order_user]}
811+ {/if}
be762003 812 {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
813 {else}
814 {$column.title}
be762003
ER
815@@ -229,19 +261,20 @@
816 {/if}
817 {/foreach}
818 </tr>
819+ <tbody>
820 {section name="i" loop=$list}
821- <tr {if $current_role >= $roles.developer AND $list[i].iqu_status > 0}style="text-decoration: line-through;"{/if}>
822+ <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}>
823 {if $current_role > $roles.developer}
824 <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>
825 {/if}
826 {foreach from=$columns item=column key=field_name}
827 {if $field_name == 'custom_fields'}
828 {foreach from=$list[i].custom_field key=fld_id item=fld_value}
829- <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
830- {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
831+ <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default custom_field" onclick="return updateCustomFields({$list[i].iss_id});">
832+ {$fld_value|formatCustomValue:$fld_id:$list[i].iss_id}
9fbd0c65
ER
833 </td>
834 {/foreach}
835- {else}
836+ {elseif $field_name != 'isu_order' || $isu_order_user}
837 <td bgcolor="{$list[i].status_color}" align="{$column.align|default:'center'}" class="default">
838 {if $field_name == 'iss_id'}
be762003
ER
839 <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_id}</a>
840@@ -276,7 +309,7 @@
841 {elseif $field_name == 'iss_percent_complete'}
842 {$list[i].iss_percent_complete|escape:"html"}%
843 {elseif $field_name == 'iss_expected_resolution_date'}
844- {$list[i].iss_expected_resolution_date|escape:"html"}
845+ <div class="inline_date_pick" id="expected_resolution_date|{$list[i].iss_id}">{$list[i].iss_expected_resolution_date|escape:"html"}&nbsp;</div>
846 {elseif $field_name == 'iss_summary'}
847 <a href="view.php?id={$list[i].iss_id}" class="link" title="{t}view issue details{/t}">{$list[i].iss_summary|escape:"html"}</a>
848 {if $list[i].redeemed}
849@@ -285,6 +318,10 @@
9fbd0c65
ER
850 {if $list[i].iss_private == 1}
851 <b>[Private]</b>
be762003
ER
852 {/if}
853+ {elseif $field_name == 'isu_order'}
854+ {if $list[i].assigned_users_order[$current_user_id]}
855+ <img src="{$rel_url}images/updown.gif" alt="move">
d95f9b26 856+ {/if}
9fbd0c65
ER
857 {/if}
858 </td>
859 {/if}
be762003
ER
860@@ -297,10 +334,11 @@
861 </td>
862 </tr>
863 {/section}
864- <tr bgcolor="{$cell_color}">
865+ </tbody>
866+ <tr bgcolor="{$cell_color}" class="nodrag">
867 <td colspan="{$col_count}">
868 <table width="100%" cellspacing="0" cellpadding="0">
869- <tr>
870+ <tr class="nodrag">
871 <td width="30%" nowrap>
872 {if $current_role > $roles.developer}
873 <input type="button" value="{t}All{/t}" class="shortcut" onClick="javascript:toggleSelectAll(this.form, 'item[]');">
874@@ -352,6 +390,29 @@
875 </form>
876 </table>
877 <br />
878-
879+<script type="text/javascript">
880+{*
881+ * Order issues by drag and drop:
882+ * only if sorted by order and viewing your own issues
883+ *}
884+{if $options.sort_by == "isu_order" and $current_user_id == $isu_order_user}
885+{literal}
886+var before = ''; // make it global variable
887+$('#issue_list_table').tableDnD({
888+ onDragClass: "tDnD_whileDrag",
889+ onDragStart: function(table, row) {
890+ before = $.tableDnD.serialize('id');
891+ },
892+ onDrop: function(table, row) {
893+ $.post("/ajax/order.php", {before: before, after: $.tableDnD.serialize('id')}, function(data) {
894+ if (data.length > 0) {
895+ alert(data);
896+ }
897+ }, "text");
898+ }
899+});
900+{/literal}
901+{/if}
902+</script>
903 {include file="app_info.tpl.html"}
904-{include file="footer.tpl.html"}
905+{include file="footer.tpl.html"}
906\ No newline at end of file
621b0b5e
ER
907--- ./js/jquery/jquery.tablednd.js (revision 0)
908+++ ./js/jquery/jquery.tablednd.js (revision 0)
909@@ -0,0 +1,382 @@
910+/**
911+ * TableDnD plug-in for JQuery, allows you to drag and drop table rows
912+ * You can set up various options to control how the system will work
913+ * Copyright (c) Denis Howlett <denish@isocra.com>
914+ * Licensed like jQuery, see http://docs.jquery.com/License.
915+ *
916+ * Configuration options:
917+ *
918+ * onDragStyle
919+ * This is the style that is assigned to the row during drag. There are limitations to the styles that can be
920+ * associated with a row (such as you can't assign a border--well you can, but it won't be
921+ * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
922+ * a map (as used in the jQuery css(...) function).
923+ * onDropStyle
924+ * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
925+ * to what you can do. Also this replaces the original style, so again consider using onDragClass which
926+ * is simply added and then removed on drop.
927+ * onDragClass
928+ * This class is added for the duration of the drag and then removed when the row is dropped. It is more
929+ * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
930+ * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
931+ * stylesheet.
932+ * onDrop
933+ * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
934+ * and the row that was dropped. You can work out the new order of the rows by using
935+ * table.rows.
936+ * onDragStart
937+ * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
938+ * table and the row which the user has started to drag.
939+ * onAllowDrop
940+ * Pass a function that will be called as a row is over another row. If the function returns true, allow
941+ * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
942+ * the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
943+ * scrollAmount
944+ * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
945+ * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
946+ * FF3 beta
947+ * dragHandle
948+ * This is the name of a class that you assign to one or more cells in each row that is draggable. If you
949+ * specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
950+ * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
951+ * the whole row is draggable.
952+ *
953+ * Other ways to control behaviour:
954+ *
955+ * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
956+ * that you don't want to be draggable.
957+ *
958+ * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
959+ * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
960+ * an ID as must all the rows.
961+ *
962+ * Other methods:
963+ *
964+ * $("...").tableDnDUpdate()
965+ * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
966+ * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
967+ * The table maintains the original configuration (so you don't have to specify it again).
968+ *
969+ * $("...").tableDnDSerialize()
970+ * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
971+ * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
972+ *
973+ * Known problems:
974+ * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
975+ *
976+ * Version 0.2: 2008-02-20 First public version
977+ * Version 0.3: 2008-02-07 Added onDragStart option
978+ * Made the scroll amount configurable (default is 5 as before)
979+ * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
980+ * Added onAllowDrop to control dropping
981+ * Fixed a bug which meant that you couldn't set the scroll amount in both directions
982+ * Added serialize method
983+ * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
984+ * draggable
985+ * Improved the serialize method to use a default (and settable) regular expression.
986+ * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
987+ */
988+jQuery.tableDnD = {
989+ /** Keep hold of the current table being dragged */
990+ currentTable : null,
991+ /** Keep hold of the current drag object if any */
992+ dragObject: null,
993+ /** The current mouse offset */
994+ mouseOffset: null,
995+ /** Remember the old value of Y so that we don't do too much processing */
996+ oldY: 0,
997+
998+ /** Actually build the structure */
999+ build: function(options) {
1000+ // Set up the defaults if any
1001+
1002+ this.each(function() {
1003+ // This is bound to each matching table, set up the defaults and override with user options
1004+ this.tableDnDConfig = jQuery.extend({
1005+ onDragStyle: null,
1006+ onDropStyle: null,
1007+ // Add in the default class for whileDragging
1008+ onDragClass: "tDnD_whileDrag",
1009+ onDrop: null,
1010+ onDragStart: null,
1011+ scrollAmount: 5,
1012+ serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
1013+ serializeParamName: null, // If you want to specify another parameter name instead of the table ID
1014+ dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
1015+ }, options || {});
1016+ // Now make the rows draggable
1017+ jQuery.tableDnD.makeDraggable(this);
1018+ });
1019+
1020+ // Now we need to capture the mouse up and mouse move event
1021+ // We can use bind so that we don't interfere with other event handlers
1022+ jQuery(document)
1023+ .bind('mousemove', jQuery.tableDnD.mousemove)
1024+ .bind('mouseup', jQuery.tableDnD.mouseup);
1025+
1026+ // Don't break the chain
1027+ return this;
1028+ },
1029+
1030+ /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
1031+ makeDraggable: function(table) {
1032+ var config = table.tableDnDConfig;
1033+ if (table.tableDnDConfig.dragHandle) {
1034+ // We only need to add the event to the specified cells
1035+ var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table);
1036+ cells.each(function() {
1037+ // The cell is bound to "this"
1038+ jQuery(this).mousedown(function(ev) {
1039+ jQuery.tableDnD.dragObject = this.parentNode;
1040+ jQuery.tableDnD.currentTable = table;
1041+ jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
1042+ if (config.onDragStart) {
1043+ // Call the onDrop method if there is one
1044+ config.onDragStart(table, this);
1045+ }
1046+ return false;
1047+ });
1048+ })
1049+ } else {
1050+ // For backwards compatibility, we add the event to the whole row
1051+ var rows = jQuery("tr", table); // get all the rows as a wrapped set
1052+ rows.each(function() {
1053+ // Iterate through each row, the row is bound to "this"
1054+ var row = jQuery(this);
1055+ if (! row.hasClass("nodrag")) {
1056+ row.mousedown(function(ev) {
1057+ if (ev.target.tagName == "TD") {
1058+ jQuery.tableDnD.dragObject = this;
1059+ jQuery.tableDnD.currentTable = table;
1060+ jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
1061+ if (config.onDragStart) {
1062+ // Call the onDrop method if there is one
1063+ config.onDragStart(table, this);
1064+ }
1065+ return false;
1066+ }
1067+ }).css("cursor", "move"); // Store the tableDnD object
1068+ }
1069+ });
1070+ }
1071+ },
1072+
1073+ updateTables: function() {
1074+ this.each(function() {
1075+ // this is now bound to each matching table
1076+ if (this.tableDnDConfig) {
1077+ jQuery.tableDnD.makeDraggable(this);
1078+ }
1079+ })
1080+ },
1081+
1082+ /** Get the mouse coordinates from the event (allowing for browser differences) */
1083+ mouseCoords: function(ev){
1084+ if(ev.pageX || ev.pageY){
1085+ return {x:ev.pageX, y:ev.pageY};
1086+ }
1087+ return {
1088+ x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
1089+ y:ev.clientY + document.body.scrollTop - document.body.clientTop
1090+ };
1091+ },
1092+
1093+ /** Given a target element and a mouse event, get the mouse offset from that element.
1094+ To do this we need the element's position and the mouse position */
1095+ getMouseOffset: function(target, ev) {
1096+ ev = ev || window.event;
1097+
1098+ var docPos = this.getPosition(target);
1099+ var mousePos = this.mouseCoords(ev);
1100+ return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
1101+ },
1102+
1103+ /** Get the position of an element by going up the DOM tree and adding up all the offsets */
1104+ getPosition: function(e){
1105+ var left = 0;
1106+ var top = 0;
1107+ /** Safari fix -- thanks to Luis Chato for this! */
1108+ if (e.offsetHeight == 0) {
1109+ /** Safari 2 doesn't correctly grab the offsetTop of a table row
1110+ this is detailed here:
1111+ http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
1112+ the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
1113+ note that firefox will return a text node as a first child, so designing a more thorough
1114+ solution may need to take that into account, for now this seems to work in firefox, safari, ie */
1115+ e = e.firstChild; // a table cell
1116+ }
1117+
1118+ while (e.offsetParent){
1119+ left += e.offsetLeft;
1120+ top += e.offsetTop;
1121+ e = e.offsetParent;
1122+ }
1123+
1124+ left += e.offsetLeft;
1125+ top += e.offsetTop;
1126+
1127+ return {x:left, y:top};
1128+ },
1129+
1130+ mousemove: function(ev) {
1131+ if (jQuery.tableDnD.dragObject == null) {
1132+ return;
1133+ }
1134+
1135+ var dragObj = jQuery(jQuery.tableDnD.dragObject);
1136+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
1137+ var mousePos = jQuery.tableDnD.mouseCoords(ev);
1138+ var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
1139+ //auto scroll the window
1140+ var yOffset = window.pageYOffset;
1141+ if (document.all) {
1142+ // Windows version
1143+ //yOffset=document.body.scrollTop;
1144+ if (typeof document.compatMode != 'undefined' &&
1145+ document.compatMode != 'BackCompat') {
1146+ yOffset = document.documentElement.scrollTop;
1147+ }
1148+ else if (typeof document.body != 'undefined') {
1149+ yOffset=document.body.scrollTop;
1150+ }
1151+
1152+ }
1153+
1154+ if (mousePos.y-yOffset < config.scrollAmount) {
1155+ window.scrollBy(0, -config.scrollAmount);
1156+ } else {
1157+ var windowHeight = window.innerHeight ? window.innerHeight
1158+ : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
1159+ if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
1160+ window.scrollBy(0, config.scrollAmount);
1161+ }
1162+ }
1163+
1164+
1165+ if (y != jQuery.tableDnD.oldY) {
1166+ // work out if we're going up or down...
1167+ var movingDown = y > jQuery.tableDnD.oldY;
1168+ // update the old value
1169+ jQuery.tableDnD.oldY = y;
1170+ // update the style to show we're dragging
1171+ if (config.onDragClass) {
1172+ dragObj.addClass(config.onDragClass);
1173+ } else {
1174+ dragObj.css(config.onDragStyle);
1175+ }
1176+ // If we're over a row then move the dragged row to there so that the user sees the
1177+ // effect dynamically
1178+ var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
1179+ if (currentRow) {
1180+ // TODO worry about what happens when there are multiple TBODIES
1181+ if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
1182+ jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
1183+ } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
1184+ jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
1185+ }
1186+ }
1187+ }
1188+
1189+ return false;
1190+ },
1191+
1192+ /** We're only worried about the y position really, because we can only move rows up and down */
1193+ findDropTargetRow: function(draggedRow, y) {
1194+ var rows = jQuery.tableDnD.currentTable.rows;
1195+ for (var i=0; i<rows.length; i++) {
1196+ var row = rows[i];
1197+ var rowY = this.getPosition(row).y;
1198+ var rowHeight = parseInt(row.offsetHeight)/2;
1199+ if (row.offsetHeight == 0) {
1200+ rowY = this.getPosition(row.firstChild).y;
1201+ rowHeight = parseInt(row.firstChild.offsetHeight)/2;
1202+ }
1203+ // Because we always have to insert before, we need to offset the height a bit
1204+ if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
1205+ // that's the row we're over
1206+ // If it's the same as the current row, ignore it
1207+ if (row == draggedRow) {return null;}
1208+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
1209+ if (config.onAllowDrop) {
1210+ if (config.onAllowDrop(draggedRow, row)) {
1211+ return row;
1212+ } else {
1213+ return null;
1214+ }
1215+ } else {
1216+ // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
1217+ var nodrop = jQuery(row).hasClass("nodrop");
1218+ if (! nodrop) {
1219+ return row;
1220+ } else {
1221+ return null;
1222+ }
1223+ }
1224+ return row;
1225+ }
1226+ }
1227+ return null;
1228+ },
1229+
1230+ mouseup: function(e) {
1231+ if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
1232+ var droppedRow = jQuery.tableDnD.dragObject;
1233+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
1234+ // If we have a dragObject, then we need to release it,
1235+ // The row will already have been moved to the right place so we just reset stuff
1236+ if (config.onDragClass) {
1237+ jQuery(droppedRow).removeClass(config.onDragClass);
1238+ } else {
1239+ jQuery(droppedRow).css(config.onDropStyle);
1240+ }
1241+ jQuery.tableDnD.dragObject = null;
1242+ if (config.onDrop) {
1243+ // Call the onDrop method if there is one
1244+ config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
1245+ }
1246+ jQuery.tableDnD.currentTable = null; // let go of the table too
1247+ }
1248+ },
1249+
1250+ serialize: function() {
1251+ if (jQuery.tableDnD.currentTable) {
1252+ return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
1253+ } else {
1254+ return "Error: No Table id set, you need to set an id on your table and every row";
1255+ }
1256+ },
1257+
1258+ serializeTable: function(table) {
1259+ var result = "";
1260+ var tableId = table.id;
1261+ var rows = table.rows;
1262+ for (var i=0; i<rows.length; i++) {
1263+ if (result.length > 0) result += "&";
1264+ var rowId = rows[i].id;
1265+ if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
1266+ rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
1267+ }
1268+
1269+ result += tableId + '[]=' + rowId;
1270+ }
1271+ return result;
1272+ },
1273+
1274+ serializeTables: function() {
1275+ var result = "";
1276+ this.each(function() {
1277+ // this is now bound to each matching table
1278+ result += jQuery.tableDnD.serializeTable(this);
1279+ });
1280+ return result;
1281+ }
1282+
1283+}
1284+
1285+jQuery.fn.extend(
1286+ {
1287+ tableDnD : jQuery.tableDnD.build,
1288+ tableDnDUpdate : jQuery.tableDnD.updateTables,
1289+ tableDnDSerialize: jQuery.tableDnD.serializeTables
1290+ }
1291+);
1292\ No newline at end of file
1293--- eventum-r3765/templates/header.tpl.html~ 2008-10-28 20:23:04.000000000 +0200
1294+++ eventum-r3765/templates/header.tpl.html 2008-10-28 22:39:10.604728264 +0200
1295@@ -8,6 +8,7 @@
1296 <script language="JavaScript" src="{$rel_url}js/jquery/jquery.js"></script>
1297 <script language="JavaScript" src="{$rel_url}js/jquery/form.js"></script>
1298 <script language="JavaScript" src="{$rel_url}js/jquery/ui.datepicker.js"></script>
1299+<script language="JavaScript" src="{$rel_url}js/jquery/jquery.tablednd.js"></script>
1300 <link rel="stylesheet" href="{$rel_url}js/jquery/ui.datepicker.css">
1301 <script language="JavaScript">
1302 <!--
8bba1b86
ER
1303--- /dev/null 2006-03-28 14:00:37.000000000 +0300
1304+++ ./order_patch-patch.sql 2008-08-27 17:16:21.444016830 +0300
1305@@ -0,0 +1,5 @@
1306+ALTER TABLE eventum_issue_user
1307+ ADD isu_order int(11) NOT NULL DEFAULT '0' AFTER isu_assigned_date,
1308+ ADD INDEX isu_order (isu_order);
1309+// set at least some order for now
1310+update eventum_issue_user set isu_order=isu_iss_id;
c2cb0c93
ER
1311--- eventum-r3765/css/style.css~ 2008-06-19 08:30:31.000000000 +0300
1312+++ eventum-r3765/css/style.css 2008-10-29 17:28:49.393768970 +0200
1313@@ -172,4 +172,9 @@
1314 font-size: 70%;
1315 font-family: Verdana, Arial, Helvetica, sans-serif;
1316 padding: 10px;
1317-}
1318\ No newline at end of file
1319+}
1320+
1321+.tDnD_whileDrag td {
1322+ background-color: #ffffdd;
1323+ border: 1px solid red;
1324+}
This page took 0.18578 seconds and 4 git commands to generate.