diff --git a/src/IDF/Commit.php b/src/IDF/Commit.php
index d4671eb..8ee82c8 100644
--- a/src/IDF/Commit.php
+++ b/src/IDF/Commit.php
@@ -183,4 +183,36 @@ class IDF_Commit extends Pluf_Model
'.__('Commit').'
'.$this->scm_id.', '.__('by').' '.strip_tags($this->origauthor).'
';
return Pluf_Template::markSafe($out);
}
+
+ /**
+ * Returns the feed fragment for the commit.
+ *
+ * @param Pluf_HTTP_Request
+ * @return Pluf_Template_SafeString
+ */
+ public function feedFragment($request)
+ {
+ $url = Pluf::f('url_base')
+ .Pluf_HTTP_URL_urlForView('IDF_Views_Source::commit',
+ array($request->project->shortname,
+ $this->scm_id));
+ $tag = new IDF_Template_IssueComment();
+ $summary = ''."\n"
+ .''
+ .$tag->start($this->summary, $request, false, false, false);
+ if ($this->fullmessage) {
+ $summary .= '
'
+ .$tag->start($this->fullmessage, $request, false, false, false);
+ }
+ $date = Pluf_Date::gmDateToGmString($this->creation_dtime);
+ $summary .= '
';
+ $out = '
+ '.Pluf_esc($request->project->name).': '.__('Commit').' '.$this->scm_id.'
+
+ '.$url.'
+ '.$date.''.$summary.'
+
+';
+ return $out;
+ }
}
diff --git a/src/IDF/Issue.php b/src/IDF/Issue.php
index 1cda1a2..d901852 100644
--- a/src/IDF/Issue.php
+++ b/src/IDF/Issue.php
@@ -195,4 +195,34 @@ class IDF_Issue extends Pluf_Model
'.sprintf(__('Creation of
issue %d'), $url, $ic, $this->id).', '.__('by').' '.Pluf_esc($submitter).'
';
return Pluf_Template::markSafe($out);
}
+
+ public function feedFragment($request)
+ {
+ $base = '
+ %%title%%
+
+ %%url%%
+ %%date%%
+
+';
+ $url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
+ array($request->project->shortname,
+ $this->id));
+ $title = sprintf(__('%s: Issue %d created - %s'),
+ Pluf_esc($request->project->name),
+ $this->id, Pluf_esc($this->summary));
+ // Get the first comment of this issue.
+ $cts = $this->get_comments_list(array('order' => 'id ASC',
+ 'nb' => 1));
+ $tag = new IDF_Template_IssueComment();
+ $content = $tag->start($cts[0]->content, $request, false);
+ $date = Pluf_Date::gmDateToGmString($this->creation_dtime);
+ return Pluf_Translation::sprintf($base,
+ array('url' => $url,
+ 'title' => $title,
+ 'content' => $content,
+ 'date' => $date));
+ }
}
\ No newline at end of file
diff --git a/src/IDF/IssueComment.php b/src/IDF/IssueComment.php
index 741a3d5..fe1ab2d 100644
--- a/src/IDF/IssueComment.php
+++ b/src/IDF/IssueComment.php
@@ -172,4 +172,58 @@ class IDF_IssueComment extends Pluf_Model
return Pluf_Template::markSafe($out);
}
+
+ public function feedFragment($request)
+ {
+ $base = '
+ %%title%%
+
+ %%url%%
+ %%date%%
+
+ %%content%%
+
+';
+ $issue = $this->get_issue();
+ $url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
+ array($request->project->shortname,
+ $issue->id));
+ $url .= '#ic'.$this->id;
+ $title = sprintf(__('%s: Comment on issue %d - %s'),
+ Pluf_esc($request->project->name),
+ $issue->id, Pluf_esc($issue->summary));
+ $submitter = $this->get_submitter();
+ $tag = new IDF_Template_IssueComment();
+ $content = ''.$tag->start($this->content, $request, false).'
';
+ if ($this->changedIssue()) {
+ $content .= '';
+ foreach ($this->changes as $w => $v) {
+ $content .= '';
+ switch ($w) {
+ case 'su':
+ $content .= __('Summary:'); break;
+ case 'st':
+ $content .= __('Status:'); break;
+ case 'ow':
+ $content .= __('Owner:'); break;
+ case 'lb':
+ $content .= __('Labels:'); break;
+ }
+ $content .= ' ';
+ if ($w == 'lb') {
+ $content .= Pluf_esc(implode(', ', $v));
+ } else {
+ $content .= Pluf_esc($v);
+ }
+ $content .= ' ';
+ }
+ $content .= '
';
+ }
+ $date = Pluf_Date::gmDateToGmString($this->creation_dtime);
+ return Pluf_Translation::sprintf($base,
+ array('url' => $url,
+ 'title' => $title,
+ 'content' => $content,
+ 'date' => $date));
+ }
}
diff --git a/src/IDF/Middleware.php b/src/IDF/Middleware.php
index 94d37ae..0d4f6ad 100644
--- a/src/IDF/Middleware.php
+++ b/src/IDF/Middleware.php
@@ -52,18 +52,23 @@ class IDF_Middleware
}
$request->conf = new IDF_Conf();
$request->conf->setProject($request->project);
- $ak = array('downloads_access_rights' => 'hasDownloadsAccess',
- 'wiki_access_rights' => 'hasWikiAccess',
- 'review_access_rights' => 'hasReviewAccess',
- 'source_access_rights' => 'hasSourceAccess',
- 'issues_access_rights' => 'hasIssuesAccess');
- $request->rights = array();
- foreach ($ak as $key=>$val) {
- $request->rights[$val] = (true === IDF_Precondition::accessTabGeneric($request, $key));
- }
+ self::setRights($request);
}
return false;
}
+
+ public static function setRights(&$request)
+ {
+ $ak = array('downloads_access_rights' => 'hasDownloadsAccess',
+ 'wiki_access_rights' => 'hasWikiAccess',
+ 'review_access_rights' => 'hasReviewAccess',
+ 'source_access_rights' => 'hasSourceAccess',
+ 'issues_access_rights' => 'hasIssuesAccess');
+ $request->rights = array();
+ foreach ($ak as $key=>$val) {
+ $request->rights[$val] = (true === IDF_Precondition::accessTabGeneric($request, $key));
+ }
+ }
}
diff --git a/src/IDF/Precondition.php b/src/IDF/Precondition.php
index 018a1c2..a52b670 100644
--- a/src/IDF/Precondition.php
+++ b/src/IDF/Precondition.php
@@ -161,7 +161,7 @@ class IDF_Precondition
return self::accessTabGeneric($request, 'review_access_rights');
}
- /**
+ /**
* Based on the request, it is automatically setting the user.
*
* API calls are not translated.
@@ -180,7 +180,7 @@ class IDF_Precondition
$sql = new Pluf_SQL('login=%s AND active='.$true,
$request->REQUEST['_login']);
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
- if ($users->count() != 1) {
+ if ($users->count() != 1 or !$users[0]->active) {
// Should return a special authentication error like user
// not found.
return true;
@@ -190,6 +190,70 @@ class IDF_Precondition
return true; // Again need authentication error
}
$request->user = $users[0];
+ IDF_Middleware::setRights($request);
+ return true;
+ }
+
+ /**
+ * Based on the request, it is automatically setting the user.
+ *
+ * Authenticated feeds have a token set at the end of the url in
+ * the for of 'authenticated/url/token/234092384023woeiur/'. If
+ * you remove 'token/234092384023woeiur/' the url is not
+ * authenticated.
+ *
+ * If the user is already logged in and not anonymous and no token
+ * is given, then the user is unset and a non authenticated user
+ * is loaded. This is to avoid people to not understand why a
+ * normally not authenticated feed is providing authenticated
+ * data.
+ */
+ static public function feedSetUser($request)
+ {
+ if (!isset($request->project)) {
+ return true; // we do not act on non project pages at the
+ // moment.
+ }
+ if (!$request->user->isAnonymous()) {
+ // by default anonymous
+ $request->user = new Pluf_User();
+ IDF_Middleware::setRights($request);
+ }
+ $match = array();
+ if (!preg_match('#/token/([^/]+)/$#', $request->query, $match)) {
+ return true; // anonymous
+ }
+ $token = $match[1];
+ $hash = substr($token, 0, 2);
+ $encrypted = substr($token, 2);
+ if ($hash != substr(md5(Pluf::f('secret_key').$encrypted), 0, 2)) {
+ return true; // no match in the hash, anonymous
+ }
+ $cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
+ list($userid, $projectid) = split(':', $cr->decrypt($encrypted), 2);
+ if ($projectid != $request->project->id) {
+ return true; // anonymous
+ }
+ $user = new Pluf_User($userid);
+ if (!$user->active) {
+ return true; // anonymous
+ }
+ $request->user = $user;
+ IDF_Middleware::setRights($request);
return true;
}
+
+ /**
+ * Generate the token for the feed.
+ *
+ * @param IDF_Project
+ * @param Pluf_User
+ * @return string Token
+ */
+ static public function genFeedToken($project, $user)
+ {
+ $cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
+ $encrypted = trim($cr->encrypt($user->id.':'.$project->id), '~');
+ return substr(md5(Pluf::f('secret_key').$encrypted), 0, 2).$encrypted;
+ }
}
\ No newline at end of file
diff --git a/src/IDF/Review.php b/src/IDF/Review.php
index 5e04be8..224ce85 100644
--- a/src/IDF/Review.php
+++ b/src/IDF/Review.php
@@ -165,4 +165,9 @@ class IDF_Review extends Pluf_Model
{
return '';
}
+
+ public function feedFragment($request)
+ {
+ return '';
+ }
}
\ No newline at end of file
diff --git a/src/IDF/Review/FileComment.php b/src/IDF/Review/FileComment.php
index 9f0f223..3975ffc 100644
--- a/src/IDF/Review/FileComment.php
+++ b/src/IDF/Review/FileComment.php
@@ -110,4 +110,9 @@ class IDF_Review_FileComment extends Pluf_Model
{
return '';
}
+
+ public function feedFragment($request)
+ {
+ return '';
+ }
}
diff --git a/src/IDF/Review/Patch.php b/src/IDF/Review/Patch.php
index f25765c..f00845b 100644
--- a/src/IDF/Review/Patch.php
+++ b/src/IDF/Review/Patch.php
@@ -115,4 +115,9 @@ class IDF_Review_Patch extends Pluf_Model
public function timelineFragment($request)
{
}
+
+ public function feedFragment($request)
+ {
+ return '';
+ }
}
diff --git a/src/IDF/Upload.php b/src/IDF/Upload.php
index 4d02a3a..da18d03 100644
--- a/src/IDF/Upload.php
+++ b/src/IDF/Upload.php
@@ -184,4 +184,30 @@ class IDF_Upload extends Pluf_Model
'.sprintf(__('Addition of
download %d'), $url, $this->id).', '.__('by').' '.Pluf_esc($submitter).'
';
return Pluf_Template::markSafe($out);
}
+
+ public function feedFragment($request)
+ {
+ $base = '
+ %%title%%
+
+ %%url%%
+ %%date%%
+
+ %%content%%
+
+';
+ $url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view',
+ array($request->project->shortname,
+ $this->id));
+ $title = sprintf(__('%s: Download %d added - %s'),
+ Pluf_esc($request->project->name),
+ $this->id, Pluf_esc($this->summary));
+ $content = Pluf_esc($this->summary);
+ $date = Pluf_Date::gmDateToGmString($this->creation_dtime);
+ return Pluf_Translation::sprintf($base,
+ array('url' => $url,
+ 'title' => $title,
+ 'content' => $content,
+ 'date' => $date));
+ }
}
\ No newline at end of file
diff --git a/src/IDF/Views/Project.php b/src/IDF/Views/Project.php
index 3d113cf..0a40400 100644
--- a/src/IDF/Views/Project.php
+++ b/src/IDF/Views/Project.php
@@ -115,14 +115,91 @@ class IDF_Views_Project
// the first tag is the featured, the last is the deprecated.
$downloads = $tags[0]->get_idf_upload_list();
}
+ $pages = array();
+ if ($request->rights['hasWikiAccess']) {
+ $tags = IDF_Views_Wiki::getWikiTags($prj);
+ $pages = $tags[0]->get_idf_wikipage_list();
+ }
+ if (!$request->user->isAnonymous()) {
+ $feedurl = Pluf_HTTP_URL_urlForView('idf_project_timeline_feed_auth',
+ array($prj->shortname,
+ IDF_Precondition::genFeedToken($prj, $request->user)));
+ } else {
+ $feedurl = Pluf_HTTP_URL_urlForView('idf_project_timeline_feed',
+ array($prj->shortname));
+ }
return Pluf_Shortcuts_RenderToResponse('idf/project/timeline.html',
array(
'page_title' => $title,
+ 'feedurl' => $feedurl,
'timeline' => $pag,
'team' => $team,
'downloads' => $downloads,
),
$request);
+
+ }
+
+ /**
+ * Timeline feed.
+ *
+ * A custom view to have a bit more control on the way to handle
+ * it and optimize the output.
+ *
+ */
+ public $timelineFeed_precond = array('IDF_Precondition::feedSetUser',
+ 'IDF_Precondition::baseAccess');
+ public function timelineFeed($request, $match)
+ {
+ $prj = $request->project;
+ // Need to check the rights
+ $rights = array();
+ if (true === IDF_Precondition::accessSource($request)) {
+ $rights[] = '\'IDF_Commit\'';
+ IDF_Scm::syncTimeline($request);
+ }
+ if (true === IDF_Precondition::accessIssues($request)) {
+ $rights[] = '\'IDF_Issue\'';
+ $rights[] = '\'IDF_IssueComment\'';
+ }
+ if (true === IDF_Precondition::accessDownloads($request)) {
+ $rights[] = '\'IDF_Upload\'';
+ }
+ if (true === IDF_Precondition::accessWiki($request)) {
+ $rights[] = '\'IDF_WikiPage\'';
+ $rights[] = '\'IDF_WikiRevision\'';
+ }
+ if (count($rights) == 0) {
+ $rights[] = '\'IDF_Dummy\'';
+ }
+ $sqls = sprintf('model_class IN (%s)', implode(', ', $rights));
+ $sql = new Pluf_SQL('project=%s AND '.$sqls, array($prj->id));
+ $params = array(
+ 'filter' => $sql->gen(),
+ 'order' => 'creation_dtime DESC',
+ 'nb' => 50,
+ );
+ $items = Pluf::factory('IDF_Timeline')->getList($params);
+ $set = new Pluf_Model_Set($items,
+ array('public_dtime' => 'public_dtime'));
+ $out = array();
+ foreach ($set as $item) {
+ $out[] = $item->feedFragment($request);
+ }
+ $out = Pluf_Template::markSafe(implode("\n", $out));
+ $tmpl = new Pluf_Template('idf/index.atom');
+ $title = __('Updates');
+ $feedurl = Pluf::f('url_base').Pluf::f('idf_base').$request->query;
+ $viewurl = Pluf_HTTP_URL_urlForView('IDF_Views_Project::timeline',
+ array($prj->shortname));
+ $context = new Pluf_Template_Context_Request($request,
+ array('body' => $out,
+ 'title' => $title,
+ 'feedurl' => $feedurl,
+ 'viewurl' => $viewurl));
+ return new Pluf_HTTP_Response(''
+ ."\n".$tmpl->render($context),
+ 'application/atom+xml; charset=utf-8');
}
diff --git a/src/IDF/WikiPage.php b/src/IDF/WikiPage.php
index 6d17a11..92ee800 100644
--- a/src/IDF/WikiPage.php
+++ b/src/IDF/WikiPage.php
@@ -198,4 +198,31 @@ class IDF_WikiPage extends Pluf_Model
'.sprintf(__('Creation of
page %s'), $url, Pluf_esc($this->title)).', '.__('by').' '.Pluf_esc($submitter).'
';
return Pluf_Template::markSafe($out);
}
+
+ public function feedFragment($request)
+ {
+ $base = '
+ %%title%%
+
+ %%url%%
+ %%date%%
+
+ %%content%%
+
+';
+ $url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
+ array($request->project->shortname,
+ $this->title));
+ $title = sprintf(__('%s: Documentation page %s added - %s'),
+ Pluf_esc($request->project->name),
+ Pluf_esc($this->title), Pluf_esc($this->summary));
+ $content = Pluf_esc($this->summary);
+ $date = Pluf_Date::gmDateToGmString($this->creation_dtime);
+ return Pluf_Translation::sprintf($base,
+ array('url' => $url,
+ 'title' => $title,
+ 'content' => $content,
+ 'date' => $date));
+ }
+
}
\ No newline at end of file
diff --git a/src/IDF/WikiRevision.php b/src/IDF/WikiRevision.php
index e920d2e..783f2d8 100644
--- a/src/IDF/WikiRevision.php
+++ b/src/IDF/WikiRevision.php
@@ -189,4 +189,32 @@ class IDF_WikiRevision extends Pluf_Model
'.sprintf(__('Change of
%s'), $url, Pluf_esc($page->title)).', '.__('by').' '.Pluf_esc($submitter).'
';
return Pluf_Template::markSafe($out);
}
+
+ public function feedFragment($request)
+ {
+ $base = '
+ %%title%%
+
+ %%url%%
+ %%date%%
+
+ %%content%%
+
+';
+ $page = $this->get_wikipage();
+ $url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::view',
+ array($request->project->shortname,
+ $page->title));
+ $title = sprintf(__('%s: Documentation page %s updated - %s'),
+ Pluf_esc($request->project->name),
+ Pluf_esc($page->title), Pluf_esc($page->summary));
+ $content = Pluf_esc($this->summary);
+ $date = Pluf_Date::gmDateToGmString($this->creation_dtime);
+ return Pluf_Translation::sprintf($base,
+ array('url' => $url,
+ 'title' => $title,
+ 'content' => $content,
+ 'date' => $date));
+ }
+
}
diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php
index bce6226..43693ca 100644
--- a/src/IDF/conf/urls.php
+++ b/src/IDF/conf/urls.php
@@ -91,6 +91,20 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/timeline/$#',
'model' => 'IDF_Views_Project',
'method' => 'timeline');
+$ctl[] = array('regex' => '#^/p/([\-\w]+)/feed/timeline/$#',
+ 'base' => $base,
+ 'priority' => 4,
+ 'model' => 'IDF_Views_Project',
+ 'method' => 'timelineFeed',
+ 'name' => 'idf_project_timeline_feed');
+
+$ctl[] = array('regex' => '#^/p/([\-\w]+)/feed/timeline/token/(.*)/$#',
+ 'base' => $base,
+ 'priority' => 4,
+ 'model' => 'IDF_Views_Project',
+ 'method' => 'timelineFeed',
+ 'name' => 'idf_project_timeline_feed_auth');
+
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/$#',
'base' => $base,
'priority' => 4,
diff --git a/src/IDF/templates/idf/index.atom b/src/IDF/templates/idf/index.atom
new file mode 100644
index 0000000..f4ff7e0
--- /dev/null
+++ b/src/IDF/templates/idf/index.atom
@@ -0,0 +1,13 @@
+
+ {$title}, {$project} - {$project.shortdesc}
+{if !$user.isAnonymous()} {blocktrans}Personal project feed for {$user}.{/blocktrans}{/if}
+
+
+ {$updated}
+
+ Not given
+ Not given
+
+ {$feedurl}
+{$body}
+
diff --git a/src/IDF/templates/idf/project/timeline.html b/src/IDF/templates/idf/project/timeline.html
index 395acbe..f0fd70d 100644
--- a/src/IDF/templates/idf/project/timeline.html
+++ b/src/IDF/templates/idf/project/timeline.html
@@ -1,4 +1,5 @@
{extends "idf/base.html"}
+{block extraheader}{/block}
{block docclass}yui-t2{/block}
{block tabhome} class="active"{/block}
{block subtabs}