diff --git a/src/IDF/Middleware.php b/src/IDF/Middleware.php
index a1603da..e6fdfe0 100644
--- a/src/IDF/Middleware.php
+++ b/src/IDF/Middleware.php
@@ -34,9 +34,9 @@ class IDF_Middleware
* When processing the request, check if matching a project. If
* so, directly set $request->project to the project.
*
- * The url to match a project is in the format
- * /p/(\w+)/whatever. This means that it will not try to match on
- * /login/ or /logout/.
+ * The url to match a project is in the format /p/(\w+)/whatever
+ * or /api/p/(\w+)/whatever. This means that it will not try to
+ * match on /login/ or /logout/.
*
* @param Pluf_HTTP_Request The request
* @return bool false or redirect.
@@ -44,7 +44,7 @@ class IDF_Middleware
function process_request(&$request)
{
$match = array();
- if (preg_match('#^/p/([\-\w]+)/#', $request->query, $match)) {
+ if (preg_match('#^/(?:api/p|p)/([\-\w]+)/#', $request->query, $match)) {
try {
$request->project = IDF_Project::getOr404($match[1]);
} catch (Pluf_HTTP_Error404 $e) {
diff --git a/src/IDF/Precondition.php b/src/IDF/Precondition.php
index 686f7ad..c26c8f5 100644
--- a/src/IDF/Precondition.php
+++ b/src/IDF/Precondition.php
@@ -142,4 +142,36 @@ class IDF_Precondition
}
return self::accessTabGeneric($request, 'downloads_access_rights');
}
+
+ /**
+ * Based on the request, it is automatically setting the user.
+ *
+ * API calls are not translated.
+ */
+ static public function apiSetUser($request)
+ {
+ // REQUEST is used to be used both for POST and GET requests.
+ if (!isset($request->REQUEST['_hash'])
+ or !isset($request->REQUEST['_login'])
+ or !isset($request->REQUEST['_salt'])) {
+ // equivalent to anonymous access.
+ return true;
+ }
+ $db =& Pluf::db();
+ $true = Pluf_DB_BooleanToDb(true, $db);
+ $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) {
+ // Should return a special authentication error like user
+ // not found.
+ return true;
+ }
+ $hash = sha1($request->REQUEST['_salt'].sha1($users[0]->password));
+ if ($hash != $request->REQUEST['_hash']) {
+ return true; // Again need authentication error
+ }
+ $request->user = $users[0];
+ return true;
+ }
}
\ No newline at end of file
diff --git a/src/IDF/Views.php b/src/IDF/Views.php
index b1b8ab4..9de0386 100644
--- a/src/IDF/Views.php
+++ b/src/IDF/Views.php
@@ -184,6 +184,22 @@ class IDF_Views
}
+ /**
+ * API FAQ.
+ */
+ public function faqApi($request, $match)
+ {
+ $title = __('InDefero API (Application Programming Interface)');
+ $projects = self::getProjects($request->user);
+ return Pluf_Shortcuts_RenderToResponse('idf/faq-api.html',
+ array(
+ 'page_title' => $title,
+ 'projects' => $projects,
+ ),
+ $request);
+
+ }
+
/**
* Returns a list of projects accessible for the user.
*
diff --git a/src/IDF/Views/Api.php b/src/IDF/Views/Api.php
new file mode 100644
index 0000000..c29ef0e
--- /dev/null
+++ b/src/IDF/Views/Api.php
@@ -0,0 +1,110 @@
+user from the
+ * _login, _hash and _salt parameters.
+ */
+class IDF_Views_Api
+{
+ /**
+ * View list of issues for a given project.
+ */
+ public $issuesIndex_precond = array('IDF_Precondition::apiSetUser',
+ 'IDF_Precondition::accessIssues');
+ public function issuesIndex($request, $match)
+ {
+ $views = new IDF_Views_Issue();
+ $p = $views->index($request, $match, true);
+ $out = array(
+ 'project' => $request->project->shortname,
+ 'open' => $p['open'],
+ 'closed' => $p['closed'],
+ 'issues' => $p['issues']->render_array(),
+ );
+ return new Pluf_HTTP_Response_Json($out);
+ }
+
+ /**
+ * Create a new issue.
+ */
+ public $issueCreate_precond = array('IDF_Precondition::apiSetUser',
+ 'IDF_Precondition::accessIssues');
+ public function issueCreate($request, $match)
+ {
+ $views = new IDF_Views_Issue();
+ $p = $views->create($request, $match, true);
+ $out = array();
+ if ($request->method == 'GET') {
+ // We give the details of the form
+ $out['doc'] = 'A POST request against this url will allow you to create a new issue.';
+ if ($request->user->hasPerm('IDF.project-owner', $request->project)
+ or $request->user->hasPerm('IDF.project-member', $request->project)) {
+ $out['status'] = array();
+ foreach (self::getTags($request->project) as $tag) {
+ $out['status'][] = $tag->name;
+ }
+ }
+
+ } else {
+ // We need to give back the results of the creation
+ if (is_object($p) and 'IDF_Issue' == get_class($p)) {
+ $out['mess'] = 'success';
+ $out['issue'] = $p->id;
+ } else {
+ $out['mess'] = 'error';
+ $out['errors'] = $p['form']->errors;
+ }
+ }
+ return new Pluf_HTTP_Response_Json($out);
+ }
+
+ /**
+ * Get the list of tags to give them to the end users when doing a
+ * GET request against a form. That way it is possible for them to
+ * know which tags/labels are available.
+ *
+ * @param IDF_Project Current project
+ * @param string Which tags to get ('issue-open')
+ * @return ArrayObject Tags
+ */
+
+ public static function getTags($project, $what='issue-open')
+ {
+ switch ($what) {
+ case 'issue-open':
+ $key = 'labels_issue_open';
+ $default = IDF_Form_IssueTrackingConf::init_open;
+ return $project->getTagsFromConfig($key, $default);
+ case 'issue-closed':
+ return $project->getTagIdsByStatus('closed');
+ }
+ return array();
+ }
+}
\ No newline at end of file
diff --git a/src/IDF/Views/Issue.php b/src/IDF/Views/Issue.php
index fe3c11a..8a7b783 100644
--- a/src/IDF/Views/Issue.php
+++ b/src/IDF/Views/Issue.php
@@ -35,7 +35,7 @@ class IDF_Views_Issue
* View list of issues for a given project.
*/
public $index_precond = array('IDF_Precondition::accessIssues');
- public function index($request, $match)
+ public function index($request, $match, $api=false)
{
$prj = $request->project;
$title = sprintf(__('%s Open Issues'), (string) $prj);
@@ -65,15 +65,15 @@ class IDF_Views_Issue
$pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request);
+ $params = array('project' => $prj,
+ 'page_title' => $title,
+ 'open' => $open,
+ 'closed' => $closed,
+ 'issues' => $pag,
+ 'cloud' => 'issues');
+ if ($api) return $params;
return Pluf_Shortcuts_RenderToResponse('idf/issues/index.html',
- array('project' => $prj,
- 'page_title' => $title,
- 'open' => $open,
- 'closed' => $closed,
- 'issues' => $pag,
- 'cloud' => 'issues',
- ),
- $request);
+ $params, $request);
}
/**
@@ -133,7 +133,7 @@ class IDF_Views_Issue
public $create_precond = array('IDF_Precondition::accessIssues',
'Pluf_Precondition::loginRequired');
- public function create($request, $match)
+ public function create($request, $match, $api=false)
{
$prj = $request->project;
$title = __('Submit a new issue');
@@ -169,20 +169,22 @@ class IDF_Views_Issue
$email->addTextMessage($tmpl->render($context));
$email->sendMail();
}
+ if ($api) return $issue;
return new Pluf_HTTP_Response_Redirect($url);
}
} else {
$form = new IDF_Form_IssueCreate(null, $params);
}
- $arrays = self::autoCompleteArrays($prj);
+ $params = array_merge(
+ array('project' => $prj,
+ 'form' => $form,
+ 'page_title' => $title,
+ ),
+ self::autoCompleteArrays($prj)
+ );
+ if ($api == true) return $params;
return Pluf_Shortcuts_RenderToResponse('idf/issues/create.html',
- array_merge(
- array('project' => $prj,
- 'form' => $form,
- 'page_title' => $title,
- ),
- $arrays),
- $request);
+ $params, $request);
}
public $search_precond = array('IDF_Precondition::accessIssues');
diff --git a/src/IDF/Views/User.php b/src/IDF/Views/User.php
index ca553ab..036e1a1 100644
--- a/src/IDF/Views/User.php
+++ b/src/IDF/Views/User.php
@@ -38,6 +38,9 @@ class IDF_Views_User
public $myAccount_precond = array('Pluf_Precondition::loginRequired');
public function myAccount($request, $match)
{
+ // As the password is salted, we can directly take the sha1 of
+ // the salted password.
+ $api_key = sha1($request->user->password);
$params = array('user' => $request->user);
if ($request->method == 'POST') {
$form = new IDF_Form_UserAccount($request->POST, $params);
@@ -52,6 +55,7 @@ class IDF_Views_User
}
return Pluf_Shortcuts_RenderToResponse('idf/user/myaccount.html',
array('page_title' => __('Your Account'),
+ 'api_key' => $api_key,
'form' => $form),
$request);
}
diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php
index 52fd269..3eedef9 100644
--- a/src/IDF/conf/urls.php
+++ b/src/IDF/conf/urls.php
@@ -272,4 +272,24 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/tabs/$#',
'model' => 'IDF_Views_Project',
'method' => 'adminTabs');
+// ---------- API ----------------------------------------
+
+$ctl[] = array('regex' => '#^/help/api/$#',
+ 'base' => $base,
+ 'priority' => 4,
+ 'model' => 'IDF_Views',
+ 'method' => 'faqApi');
+
+$ctl[] = array('regex' => '#^/api/p/([\-\w]+)/issues/$#',
+ 'base' => $base,
+ 'priority' => 4,
+ 'model' => 'IDF_Views_Api',
+ 'method' => 'issuesIndex');
+
+$ctl[] = array('regex' => '#^/api/p/([\-\w]+)/issues/create/$#',
+ 'base' => $base,
+ 'priority' => 4,
+ 'model' => 'IDF_Views_Api',
+ 'method' => 'issueCreate');
+
return $ctl;
diff --git a/src/IDF/templates/idf/base-simple.html b/src/IDF/templates/idf/base-simple.html
index c87aba6..4358844 100644
--- a/src/IDF/templates/idf/base-simple.html
+++ b/src/IDF/templates/idf/base-simple.html
@@ -35,7 +35,7 @@