Fixed issue 71, add atom feeds.
The basic building block is here with the feed of the timeline, it is rather easy to add the feed other elements based on this work. This will require iterations and polishing.
This commit is contained in:
parent
a79d13ee3f
commit
9c44bc5fe5
@ -183,4 +183,36 @@ class IDF_Commit extends Pluf_Model
|
||||
<div class="helptext right">'.__('Commit').' <a href="'.$url.'" class="mono">'.$this->scm_id.'</a>, '.__('by').' '.strip_tags($this->origauthor).'</div></td></tr>';
|
||||
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 = '<content type="xhtml">'."\n"
|
||||
.'<div xmlns="http://www.w3.org/1999/xhtml">'
|
||||
.$tag->start($this->summary, $request, false, false, false);
|
||||
if ($this->fullmessage) {
|
||||
$summary .= '<br /><br />'
|
||||
.$tag->start($this->fullmessage, $request, false, false, false);
|
||||
}
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
$summary .= '</div></content>';
|
||||
$out = '<entry>
|
||||
<title>'.Pluf_esc($request->project->name).': '.__('Commit').' '.$this->scm_id.'</title>
|
||||
<link href="'.$url.'"/>
|
||||
<id>'.$url.'</id>
|
||||
<updated>'.$date.'</updated>'.$summary.'
|
||||
</entry>
|
||||
';
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
@ -195,4 +195,34 @@ class IDF_Issue extends Pluf_Model
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%s" class="%s">issue %d</a>'), $url, $ic, $this->id).', '.__('by').' '.Pluf_esc($submitter).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$base = '<entry>
|
||||
<title>%%title%%</title>
|
||||
<link href="%%url%%"/>
|
||||
<id>%%url%%</id>
|
||||
<updated>%%date%%</updated>
|
||||
<content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<pre>%%content%%</pre>
|
||||
</div></content>
|
||||
</entry>';
|
||||
$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));
|
||||
}
|
||||
}
|
@ -172,4 +172,58 @@ class IDF_IssueComment extends Pluf_Model
|
||||
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$base = '<entry>
|
||||
<title>%%title%%</title>
|
||||
<link href="%%url%%"/>
|
||||
<id>%%url%%</id>
|
||||
<updated>%%date%%</updated>
|
||||
<content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
|
||||
%%content%%
|
||||
</div></content>
|
||||
</entry>';
|
||||
$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 = '<p><pre>'.$tag->start($this->content, $request, false).'</pre></p>';
|
||||
if ($this->changedIssue()) {
|
||||
$content .= '<p>';
|
||||
foreach ($this->changes as $w => $v) {
|
||||
$content .= '<strong>';
|
||||
switch ($w) {
|
||||
case 'su':
|
||||
$content .= __('Summary:'); break;
|
||||
case 'st':
|
||||
$content .= __('Status:'); break;
|
||||
case 'ow':
|
||||
$content .= __('Owner:'); break;
|
||||
case 'lb':
|
||||
$content .= __('Labels:'); break;
|
||||
}
|
||||
$content .= '</strong> ';
|
||||
if ($w == 'lb') {
|
||||
$content .= Pluf_esc(implode(', ', $v));
|
||||
} else {
|
||||
$content .= Pluf_esc($v);
|
||||
}
|
||||
$content .= ' ';
|
||||
}
|
||||
$content .= '</p>';
|
||||
}
|
||||
$date = Pluf_Date::gmDateToGmString($this->creation_dtime);
|
||||
return Pluf_Translation::sprintf($base,
|
||||
array('url' => $url,
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'date' => $date));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -165,4 +165,9 @@ class IDF_Review extends Pluf_Model
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
@ -110,4 +110,9 @@ class IDF_Review_FileComment extends Pluf_Model
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -115,4 +115,9 @@ class IDF_Review_Patch extends Pluf_Model
|
||||
public function timelineFragment($request)
|
||||
{
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -184,4 +184,30 @@ class IDF_Upload extends Pluf_Model
|
||||
<div class="helptext right">'.sprintf(__('Addition of <a href="%s">download %d</a>'), $url, $this->id).', '.__('by').' '.Pluf_esc($submitter).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$base = '<entry>
|
||||
<title>%%title%%</title>
|
||||
<link href="%%url%%"/>
|
||||
<id>%%url%%</id>
|
||||
<updated>%%date%%</updated>
|
||||
<content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
|
||||
%%content%%
|
||||
</div></content>
|
||||
</entry>';
|
||||
$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));
|
||||
}
|
||||
}
|
@ -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('<?xml version="1.0" encoding="utf-8"?>'
|
||||
."\n".$tmpl->render($context),
|
||||
'application/atom+xml; charset=utf-8');
|
||||
}
|
||||
|
||||
|
||||
|
@ -198,4 +198,31 @@ class IDF_WikiPage extends Pluf_Model
|
||||
<div class="helptext right">'.sprintf(__('Creation of <a href="%s">page %s</a>'), $url, Pluf_esc($this->title)).', '.__('by').' '.Pluf_esc($submitter).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$base = '<entry>
|
||||
<title>%%title%%</title>
|
||||
<link href="%%url%%"/>
|
||||
<id>%%url%%</id>
|
||||
<updated>%%date%%</updated>
|
||||
<content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
|
||||
%%content%%
|
||||
</div></content>
|
||||
</entry>';
|
||||
$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));
|
||||
}
|
||||
|
||||
}
|
@ -189,4 +189,32 @@ class IDF_WikiRevision extends Pluf_Model
|
||||
<div class="helptext right">'.sprintf(__('Change of <a href="%s">%s</a>'), $url, Pluf_esc($page->title)).', '.__('by').' '.Pluf_esc($submitter).'</div></td></tr>';
|
||||
return Pluf_Template::markSafe($out);
|
||||
}
|
||||
|
||||
public function feedFragment($request)
|
||||
{
|
||||
$base = '<entry>
|
||||
<title>%%title%%</title>
|
||||
<link href="%%url%%"/>
|
||||
<id>%%url%%</id>
|
||||
<updated>%%date%%</updated>
|
||||
<content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
|
||||
%%content%%
|
||||
</div></content>
|
||||
</entry>';
|
||||
$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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
|
13
src/IDF/templates/idf/index.atom
Normal file
13
src/IDF/templates/idf/index.atom
Normal file
@ -0,0 +1,13 @@
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>{$title}, {$project} - {$project.shortdesc}</title>
|
||||
{if !$user.isAnonymous()} <subtitle>{blocktrans}Personal project feed for {$user}.{/blocktrans}</subtitle>{/if}
|
||||
<link href="{$feedurl}" rel="self"/>
|
||||
<link href="{$viewurl}"/>
|
||||
<updated>{$updated}</updated>
|
||||
<author>
|
||||
<name>Not given</name>
|
||||
<email>Not given</email>
|
||||
</author>
|
||||
<id>{$feedurl}</id>
|
||||
{$body}
|
||||
</feed>
|
@ -1,4 +1,5 @@
|
||||
{extends "idf/base.html"}
|
||||
{block extraheader}<link rel="alternate" type="application/atom+xml" title="{trans 'Latest updates'}" href="{$feedurl}"/>{/block}
|
||||
{block docclass}yui-t2{/block}
|
||||
{block tabhome} class="active"{/block}
|
||||
{block subtabs}
|
||||
|
Loading…
Reference in New Issue
Block a user