1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 function mtrack_timeline_order_events_newest_first($a, $b)
6 return strcmp($b['changedate'], $a['changedate']);
9 function mtrack_get_timeline($start_time = '-2 weeks',
10 $only_users = null, $limit = 50)
12 if (is_string($start_time)) {
13 $date_limit = strtotime($start_time);
15 $date_limit = $start_time; // assume that it's a timestamp
17 /* round back to earlier minute (aids caching) */
18 $date_limit -= $date_limit % 60;
19 $db_date_limit = MTrackDB::unixtime($date_limit);
23 if (is_string($only_users)) {
24 $filter_users = array(mtrack_canon_username($only_users));
25 } else if (is_array($only_users)) {
26 $filter_users = array();
27 foreach ($only_users as $user) {
28 $filter_users[] = mtrack_canon_username($user);
32 $proj_by_id = array();
33 foreach (MTrackDB::q('select projid from projects')->fetchAll() as $r) {
34 $proj_by_id[$r[0]] = MTrackProject::loadById($r[0]);
38 foreach (MTrackDB::q('select repoid from repos')->fetchAll() as $row) {
40 $repo = MTrackRepo::loadById($repoid);
41 $reponame = $repo->getBrowseRootName();
42 if ($reponame == 'default/wiki') continue;
43 $checker = new MTrackCommitChecker($repo);
45 $hist = $repo->history(null, $db_date_limit);
46 if (is_array($hist)) foreach ($hist as $e) {
47 if (is_array($filter_users)) {
49 foreach ($filter_users as $fuser) {
50 if (mtrack_canon_username($e->changeby) === $fuser) {
59 /* we want to include changesets that do not reference tickets */
60 $pid = $repo->projectFromPath($e->files);
62 $proj = $proj_by_id[$pid];
63 $e->changelog = $proj->adjust_links($e->changelog, true);
65 $actions = $checker->parseCommitMessage($e->changelog);
67 foreach ($actions as $act) {
69 $tickets[$tkt] = $tkt;
70 $repo_changes_by_ticket[$tkt][$reponame][$e->rev] = $e->rev;
72 if (count($tickets) == 0) {
74 'changedate' => $e->ctime,
75 'who' => $e->changeby,
76 'object' => "changeset:$reponame:$e->rev",
77 'reason' => $e->changelog,
82 foreach (MTrackDB::q("select
83 changedate, who, object, reason from changes
85 order by changedate desc
86 ", $db_date_limit)->fetchAll(PDO::FETCH_ASSOC) as $row) {
87 if (is_array($filter_users)) {
89 foreach ($filter_users as $fuser) {
90 if (mtrack_canon_username($row['who']) === $fuser) {
102 usort($events, 'mtrack_timeline_order_events_newest_first');
106 function _mtrack_timeline_is_repo_visible($reponame)
108 static $cache = array();
109 $me = MTrackAuth::whoami();
110 if (isset($cache[$me][$reponame])) {
111 return $cache[$me][$reponame];
114 if (ctype_digit($reponame)) {
115 $oid = "repo:$reponame";
117 $repo = MTrackRepo::loadByName($reponame);
119 $oid = "repo:$repo->repoid";
125 $ok = MTrackACL::hasAnyRights($oid, array(
126 'read', 'checkout'));
130 $cache[$me][$reponame] = $ok;
134 function mtrack_render_timeline($user = null)
139 $events = mtrack_cache('mtrack_get_timeline',
140 array('-2 weeks', $user, $limit), 300, array('Timeline', $user));
142 echo "<div class='timeline'>";
144 foreach ($events as $row) {
149 $d = date_create($row['changedate'], new DateTimeZone('UTC'));
150 date_timezone_set($d, new DateTimeZone(date_default_timezone_get()));
151 $time = $d->format('H:i');
152 $day = $d->format('D, M d Y');
154 if ($last_date != $day) {
155 echo "<h1 class='timelineday'>$day</h1>\n";
159 // figure out an event type based on the object and the reason
160 if (strpos($row['object'], ':') !== false) {
161 list($object, $id) = explode(':', $row['object'], 3);
164 $object = $row['object'];
167 $item = $row['object'];
170 if (!strncmp($row['reason'], 'created ', 8)) {
171 $eventclass = ' newticket';
172 } elseif (!strncmp($row['reason'], 'closed ', 7)) {
173 $eventclass = ' closedticket';
175 $eventclass = ' editticket';
177 $item = "Ticket " . mtrack_ticket($id);
180 $eventclass = ' editwiki';
181 $item = "Wiki " . mtrack_wiki($id);
184 $eventclass = ' editmilestone';
185 $item = "Milestone <span class='milestone'><a href='{$ABSWEB}milestone.php/$id'>$id</a></span>";
188 $eventclass = ' newchangeset';
189 preg_match("/^changeset:(.*):([^:]+)$/", $row['object'], $M);
191 if (!_mtrack_timeline_is_repo_visible($repo)) {
195 $item = "<a href='{$ABSWEB}browse.php/$repo'>$repo</a> change " . mtrack_changeset($id, $repo);
198 $item = "<a href='{$ABSWEB}snippet.php/$id'>View Snippet</a>";
201 static $repos = null;
202 if ($repos === null) {
204 foreach (MTrackDB::q(
205 'select repoid, shortname, parent from repos')->fetchAll()
210 if (!_mtrack_timeline_is_repo_visible($id)) {
213 if (isset($repos[$id])) {
214 $name = MTrackRepo::makeDisplayName($repos[$id]);
215 $item = "<a href='{$ABSWEB}browse.php/$name'>$name</a>";
217 $item = "<item has been deleted>";
222 $reason = MTrackWiki::format_to_oneliner($row['reason']);
224 echo "<div class='timelineevent'>",
225 mtrack_username($row['who'], array(
228 'class' => 'timelineface'
230 "<div class='timelinetext'>",
231 "<div class='timelinereason'>",
233 "<span class='time'>$time</span> $item by ",
234 mtrack_username($row['who'], array('no_image' => true)),