1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 include '../inc/common.php';
6 MTrackACL::requireAllRights('Timeline', 'read');
7 mtrack_head("Timeline");
10 function mtrack_timeline_order_events_newest_first($a, $b)
12 return strcmp($b['changedate'], $a['changedate']);
15 function mtrack_get_timeline($start_time = '-2 weeks',
16 $only_users = null, $limit = 50)
18 if (is_string($start_time)) {
19 $date_limit = strtotime($start_time);
21 $date_limit = $start_time; // assume that it's a timestamp
23 /* round back to earlier minute (aids caching) */
24 $date_limit -= $date_limit % 60;
25 $db_date_limit = MTrackDB::unixtime($date_limit);
29 if (is_string($only_users)) {
30 $filter_users = array(mtrack_canon_username($only_users));
31 } else if (is_array($only_users)) {
32 $filter_users = array();
33 foreach ($only_users as $user) {
34 $filter_users[] = mtrack_canon_username($user);
38 $proj_by_id = array();
39 foreach (MTrackDB::q('select projid from projects')->fetchAll() as $r) {
40 $proj_by_id[$r[0]] = MTrackProject::loadById($r[0]);
44 foreach (MTrackDB::q('select repoid from repos')->fetchAll() as $row) {
46 $repo = MTrackRepo::loadById($repoid);
47 $reponame = $repo->getBrowseRootName();
48 if ($reponame == 'default/wiki') continue;
49 $checker = new MTrackCommitChecker($repo);
51 $hist = $repo->history(null, $db_date_limit);
52 if (is_array($hist)) foreach ($hist as $e) {
53 if (is_array($filter_users)) {
55 foreach ($filter_users as $fuser) {
56 if (mtrack_canon_username($e->changeby) === $fuser) {
65 /* we want to include changesets that do not reference tickets */
66 $pid = $repo->projectFromPath($e->files);
68 $proj = $proj_by_id[$pid];
69 $e->changelog = $proj->adjust_links($e->changelog, true);
71 $actions = $checker->parseCommitMessage($e->changelog);
73 foreach ($actions as $act) {
75 $tickets[$tkt] = $tkt;
76 $repo_changes_by_ticket[$tkt][$reponame][$e->rev] = $e->rev;
78 if (count($tickets) == 0) {
80 'changedate' => $e->ctime,
81 'who' => $e->changeby,
82 'object' => "changeset:$reponame:$e->rev",
83 'reason' => $e->changelog,
88 foreach (MTrackDB::q("select
89 changedate, who, object, reason from changes
91 order by changedate desc
92 ", $db_date_limit)->fetchAll(PDO::FETCH_ASSOC) as $row) {
93 if (is_array($filter_users)) {
95 foreach ($filter_users as $fuser) {
96 if (mtrack_canon_username($row['who']) === $fuser) {
108 usort($events, 'mtrack_timeline_order_events_newest_first');
112 function _mtrack_timeline_is_repo_visible($reponame)
114 static $cache = array();
115 $me = MTrackAuth::whoami();
116 if (isset($cache[$me][$reponame])) {
117 return $cache[$me][$reponame];
120 if (ctype_digit($reponame)) {
121 $oid = "repo:$reponame";
123 $repo = MTrackRepo::loadByName($reponame);
125 $oid = "repo:$repo->repoid";
131 $ok = MTrackACL::hasAnyRights($oid, array(
132 'read', 'checkout'));
136 $cache[$me][$reponame] = $ok;
140 function mtrack_render_timeline($user = null)
145 // fixme = this should be alot more efficient...
147 $events = mtrack_get_timeline('-2 weeks', $user, $limit);
148 //$events = mtrack_cache('mtrack_get_timeline',
149 // array('-2 weeks', $user, $limit), 300, array('Timeline', $user));
151 echo "<div class='timeline'>";
153 foreach ($events as $row) {
158 $d = date_create($row['changedate'], new DateTimeZone('UTC'));
159 date_timezone_set($d, new DateTimeZone(date_default_timezone_get()));
160 $time = $d->format('H:i');
161 $day = $d->format('D, M d Y');
163 if ($last_date != $day) {
164 echo "<h1 class='timelineday'>$day</h1>\n";
168 // figure out an event type based on the object and the reason
169 if (strpos($row['object'], ':') !== false) {
170 list($object, $id) = explode(':', $row['object'], 3);
173 $object = $row['object'];
176 $item = $row['object'];
179 if (!strncmp($row['reason'], 'created ', 8)) {
180 $eventclass = ' newticket';
181 } elseif (!strncmp($row['reason'], 'closed ', 7)) {
182 $eventclass = ' closedticket';
184 $eventclass = ' editticket';
186 $item = "Ticket " . mtrack_ticket($id);
189 $eventclass = ' editwiki';
190 $item = "Wiki " . mtrack_wiki_link($id);
193 $eventclass = ' editmilestone';
194 $item = "Milestone <span class='milestone'><a href='{$ABSWEB}milestone.php/$id'>$id</a></span>";
197 $eventclass = ' newchangeset';
198 preg_match("/^changeset:(.*):([^:]+)$/", $row['object'], $M);
200 if (!_mtrack_timeline_is_repo_visible($repo)) {
204 $item = "<a href='{$ABSWEB}Browse.php/$repo'>$repo</a> change " . mtrack_changeset($id, $repo);
207 $item = "<a href='{$ABSWEB}snippet.php/$id'>View Snippet</a>";
210 static $repos = null;
211 if ($repos === null) {
213 foreach (MTrackDB::q(
214 'select repoid, shortname, parent from repos')->fetchAll()
219 if (!_mtrack_timeline_is_repo_visible($id)) {
222 if (isset($repos[$id])) {
223 $name = MTrackRepo::makeDisplayName($repos[$id]);
224 $item = "<a href='{$ABSWEB}Browse.php/$name'>$name</a>";
226 $item = "<item has been deleted>";
231 $reason = MTrack_Wiki::format_to_oneliner($row['reason']);
233 echo "<div class='timelineevent'>",
234 mtrack_username($row['who'], array(
237 'class' => 'timelineface'
239 "<div class='timelinetext'>",
240 "<div class='timelinereason'>",
242 "<span class='time'>$time</span> $item by ",
243 mtrack_username($row['who'], array('no_image' => true)),
249 mtrack_render_timeline();