Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/site
diff options
context:
space:
mode:
authorBenoit Tremblay <bentremblay@benoit-tremblays-macbook-pro.local>2009-09-25 18:52:07 (GMT)
committer Benoit Tremblay <bentremblay@benoit-tremblays-macbook-pro.local>2009-09-25 18:52:07 (GMT)
commit40b90e363e538112e2f64863229b214fbb2b698c (patch)
tree57659f376c985dba73d86969d4967042f331034b /site
parent1927c85b30c48c09a069443c0e69dff752d3c858 (diff)
Tutorius API code
Diffstat (limited to 'site')
-rw-r--r--site/app/controllers/components/tutorius.php131
-rw-r--r--site/app/controllers/tutorius_api_controller.php896
-rw-r--r--site/app/views/tutorius_api/error.thtml43
-rw-r--r--site/app/views/tutorius_api/index.thtml62
-rw-r--r--site/app/views/tutorius_api/register_new_user.thtml44
-rw-r--r--site/app/views/tutorius_api/review.thtml52
6 files changed, 1228 insertions, 0 deletions
diff --git a/site/app/controllers/components/tutorius.php b/site/app/controllers/components/tutorius.php
new file mode 100644
index 0000000..25d9496
--- /dev/null
+++ b/site/app/controllers/components/tutorius.php
@@ -0,0 +1,131 @@
+<?php
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is addons.mozilla.org site.
+ *
+ * The Initial Developer of the Original Code is
+ * Justin Scott <fligtar@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Frederic Wenzel <fwenzel@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+class TutoriusComponent extends Object {
+ var $controller;
+ var $platforms;
+ var $applications;
+ var $versionIds;
+ var $navCategories;
+
+ /**
+ * Save a reference to the controller on startup
+ * @param object &$controller the controller using this component
+ */
+ function startup(&$controller) {
+ $this->controller =& $controller;
+
+ }
+
+ /**
+ * Given a base URL and a relative URL, produce an absolute URL.
+ * see: http://us.php.net/manual/en/function.parse-url.php#76979
+ */
+ function resolveUrl($base, $url) {
+ if (!strlen($base)) return $url;
+ if (!strlen($url)) return $base;
+ if (preg_match('!^[a-z]+:!i', $url)) return $url;
+
+ $base = parse_url($base);
+ if ($url{0} == "#") {
+ $base['fragment'] = substr($url, 1);
+ return $this->unparseUrl($base);
+ }
+ unset($base['fragment']);
+ unset($base['query']);
+
+ if (substr($url, 0, 2) == "//") {
+ return $this->unparseUrl(array(
+ 'scheme'=>$base['scheme'],
+ 'path'=>substr($url,2),
+ ));
+ } else if ($url{0} == "/") {
+ $base['path'] = $url;
+ } else {
+ $path = explode('/', $base['path']);
+ $url_path = explode('/', $url);
+ array_pop($path);
+ $end = array_pop($url_path);
+ foreach ($url_path as $segment) {
+ if ($segment == '.') {
+ // skip
+ } else if ($segment == '..' && $path && $path[sizeof($path)-1] != '..') {
+ array_pop($path);
+ } else {
+ $path[] = $segment;
+ }
+ }
+ if ($end == '.') {
+ $path[] = '';
+ } else if ($end == '..' && $path && $path[sizeof($path)-1] != '..') {
+ $path[sizeof($path)-1] = '';
+ } else {
+ $path[] = $end;
+ }
+ $base['path'] = join('/', $path);
+
+ }
+ return $this->unparseUrl($base);
+ }
+
+ /**
+ * Given the results of parse_url, produce a URL.
+ * see: http://us.php.net/manual/en/function.parse-url.php#85963
+ */
+ function unparseUrl($parsed)
+ {
+ if (!is_array($parsed)) return false;
+
+ $uri = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '' : '//') : '';
+ $uri .= isset($parsed['user']) ? $parsed['user'].(isset($parsed['pass']) ? ':'.$parsed['pass'] : '').'@' : '';
+ $uri .= isset($parsed['host']) ? $parsed['host'] : '';
+ $uri .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
+
+ if (isset($parsed['path'])) {
+ $uri .= (substr($parsed['path'], 0, 1) == '/') ?
+ $parsed['path'] : ((!empty($uri) ? '/' : '' ) . $parsed['path']);
+ }
+
+ $uri .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
+ $uri .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
+
+ return $uri;
+ }
+
+
+}
+?> \ No newline at end of file
diff --git a/site/app/controllers/tutorius_api_controller.php b/site/app/controllers/tutorius_api_controller.php
new file mode 100644
index 0000000..24446f8
--- /dev/null
+++ b/site/app/controllers/tutorius_api_controller.php
@@ -0,0 +1,896 @@
+<?php
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is addons.mozilla.org site.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * l.m.orchard <lorchard@mozilla.com> (Original Author)
+ * Frederic Wenzel <fwenzel@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+uses('sanitize');
+
+/**
+ * Controller implementing addon sharing API
+ * see: https://wiki.mozilla.org/User:LesOrchard/BandwagonAPI
+ */
+class TutoriusApiController extends AppController
+{
+ var $name = 'TutoriusApi';
+
+ // bump for new releases
+ // 0 or unspecified is for Fx3b3
+ // 0.9 is for Fx3b4
+ var $newest_api_version = 0.1;
+
+ var $beforeFilter = array(
+ '_checkSandbox'
+ );
+ var $uses = array(
+ 'Addon', 'Review', 'Addonlog', 'AddonCollection', 'Addontype', 'ApiAuthToken',
+ 'Application', 'Collection', 'File', 'Platform', 'Category', 'Translation',
+ 'UpdateCount', 'Version', 'User'
+ );
+ var $components = array(
+ 'Amo', 'Developers', 'Email', 'Image', 'Pagination', 'Search', 'Session',
+ 'Versioncompare'
+ );
+ var $helpers = array(
+ 'Html', 'Link', 'Time', 'Localization', 'Ajax', 'Number',
+ 'Pagination'
+ );
+
+ var $securityLevel = 'low';
+
+ const STATUS_OK = '200 OK';
+ const STATUS_CREATED = '201 Created';
+ const STATUS_ACCEPTED = '202 Accepted';
+ const STATUS_FOUND = '302 Found';
+ const STATUS_SEE_OTHER = '303 See Other';
+ const STATUS_NOT_MODIFIED = '304 Not Modified';
+ const STATUS_BAD_REQUEST = '400 Bad Request';
+ const STATUS_UNAUTHORIZED = '401 Unauthorized';
+ const STATUS_FORBIDDEN = '403 Forbidden';
+ const STATUS_NOT_FOUND = '404 Not Found';
+ const STATUS_METHOD_NOT_ALLOWED = '405 Method Not Allowed';
+ const STATUS_CONFLICT = '409 Conflict';
+ const STATUS_GONE = '410 Gone';
+ const STATUS_UNSUPPORTED_MEDIA = '415 Unsupported Media Type';
+ const STATUS_ERROR = '500 Internal Server Error';
+
+ var $cache_lifetime = 0; // 0 seconds
+
+ function forceCache() {
+ header('Cache-Control: public, max-age=' . $this->cache_lifetime);
+ header('Vary: X-API-Auth');
+ header('Last-Modified: ' . gmdate("D, j M Y H:i:s", $this->last_modified) . " GMT");
+ header('Expires: ' . gmdate("D, j M Y H:i:s", $this->last_modified + $this->cache_lifetime) . " GMT");
+ }
+
+ /**
+ * This function is executed for every request to the API
+ *
+ */
+ function beforeFilter() {
+ Configure::write('Session.checkAgent', false);
+
+ $this->last_modified = time();
+
+ $this->layout = 'rest';
+
+ if (!$this->isWriteHttpMethod()) {
+ // Only force shadow DB on reads.
+ $this->forceShadowDb();
+ }
+
+ $this->Collection->caching = false;
+ $this->AddonCollection->caching = false;
+ $this->User->caching = false;
+ $this->ApiAuthToken->caching = false;
+
+ $this->SimpleAuth->enabled = false;
+ $this->SimpleAcl->enabled = false;
+
+ // extract API version
+ $url = $_SERVER['REQUEST_URI'];
+
+ $matches = array();
+ if (preg_match('/api\/([\d\.]*)\//', $url, $matches)) {
+ $this->api_version = $matches[1];
+ if (!is_numeric($this->api_version)) {
+ $this->api_version = $this->newest_api_version;
+ }
+ } else {
+ // nothing supplied: assume Fx3b3
+ $this->api_version = 0;
+ }
+
+ // Establish a base URL for this request.
+ $this->base_url = ( empty($_SERVER['HTTPS']) ? 'http' : 'https' ) .
+ '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ if ( ($qpos = strpos($this->base_url, '?')) !== FALSE) {
+ // Toss out any query parameters.
+ $this->base_url = substr($this->base_url, 0, $qpos);
+ }
+ $this->_sendToView('base_url', $this->base_url);
+
+ $pos = strpos($this->base_url, 'api/');
+ $this->site_base_url = substr($this->base_url, 0, $pos);
+ $this->_sendToView('site_base_url', $this->site_base_url);
+
+
+ // Trying to get the current authenticated user
+ $this->auth_user = $this->_getAuthUser();
+
+ $this->_sendToView('auth_user', $this->auth_user);
+ }
+
+ /**
+ * This is the main function for this controller
+ * its role is to list all available functions via the API
+ */
+ function index() {
+
+
+ }
+
+ /**********************************************
+ /*************** AUTHENTIFICATION *************
+ /*********************************************/
+
+ /*
+ * Authenticate a user and then
+ * return an authentication key so the user doesn't have to authenticate with its
+ * username/password every request
+ *
+ */
+ function auth() {
+ $args = func_get_args();
+ return $this->dispatchHttpMethod(array(
+ 'POST' => 'auth_POST'
+ ), $args);
+ }
+
+ /**
+ * Generate a new auth token for the authenticated user.
+ */
+ function auth_POST($context) {
+ extract($context);
+
+ // Check HTTP basic auth
+ if (null == $this->auth_user &&
+ !empty($_SERVER['PHP_AUTH_USER']) &&
+ !empty($_SERVER['PHP_AUTH_PW'])) {
+ // Try validating the user by HTTP Basic auth username and password.
+ $someone = $this->User->findByEmail($_SERVER['PHP_AUTH_USER']);
+ if (!empty($someone['User']['id']) && $someone['User']['confirmationcode'] != '') {
+ // User not yet verified.
+ $auth_user = null;
+ } else if ($this->User->checkPassword($someone['User'], $_SERVER['PHP_AUTH_PW'])) {
+ $this->auth_user = $someone['User'];
+ $this->auth_user['Group'] = $someone['Group'];
+ }
+ }
+
+ // If authentification was successful, this will allow
+ // the function to continue. Otherwise, an access denied
+ // message will be displayed.
+ $this->_checkLoggedIn();
+
+ $token_value = $this->ApiAuthToken->generateTokenValue();
+
+ $data = array(
+ 'ApiAuthToken' => array(
+ 'token' => $token_value,
+ 'user_id' => $this->auth_user['id']
+ )
+ );
+ $this->Amo->clean($data);
+
+ if (!$this->ApiAuthToken->save($data)) {
+ return $this->renderStatus(
+ self::STATUS_ERROR, 'error',
+ array('reason' => 'auth_token_generation_failed')
+ );
+ }
+
+ $token_url = $this->base_url . '/' . $token_value;
+
+ return $this->renderStatus(
+ self::STATUS_CREATED, 'auth_token', array(
+ 'value' => $token_value,
+ 'url' => $token_url
+ ), $token_url
+ );
+ }
+
+ /**
+ * Dispatcher for auth token detail resource.
+ */
+ function auth_detail($token) {
+ $args = func_get_args();
+ return $this->dispatchHttpMethod(array(
+ 'DELETE' => 'auth_detail_DELETE'
+ ), $args);
+ }
+
+ /**
+ * Delete an existing token, rendering it unusable in the future. (eg. for
+ * logout)
+ */
+ function auth_detail_DELETE($context, $token) {
+ extract($context);
+
+ $rv = $this->ApiAuthToken->deleteByUserIdAndToken(
+ $this->auth_user['id'], $token
+ );
+
+ if ($rv) {
+ return $this->renderStatus(self::STATUS_GONE, 'empty');
+ } else {
+ return $this->renderStatus(
+ self::STATUS_NOT_FOUND, 'error',
+ array('reason' => 'token_unknown')
+ );
+ }
+ }
+
+ /**********************************************
+ /************** USER REGISTRATION *************
+ /*********************************************/
+
+ function registerNewUser() {
+
+ $args = func_get_args();
+ return $this->dispatchHttpMethod(array(
+ 'POST' => 'registerNewUser_POST'
+ ), $args);
+
+ }
+
+ /**
+ * This creates an account via a POST request
+ *
+ */
+ function registerNewUser_POST($context) {
+ extract($context);
+
+ // Create the User array and map it with the POST variables
+ $data['User'] = $this->getParams(array(
+ 'nickname' => '',
+ 'password' => '',
+ 'email' => '',
+ ));
+
+ // filter nickname characters
+ //$this->data['User']['nickname'] = $this->_filterNick($this->data['User']['nickname']);
+
+
+ // No confirmation code. We want to simplify the process for now.
+ // User is auto-approved
+ $data['User']['confirmationcode'] = '';
+
+ $this->User->data = $data;
+ $this->Amo->clean($this->User->data);
+
+ // hash password
+ $this->User->data['User']['password'] = $this->User->createPassword($this->User->data['User']['password']);
+
+ // no empty pw
+ if ($data['User']['password'] == '')
+ $this->User->invalidate('password');
+
+ // email has to be unique
+ $allemail = $this->User->findAll("email='{$this->User->data['User']['email']}'");
+ if (!empty($allemail)) {
+ $this->User->invalidate('email');
+ return $this->_renderError(
+ array(
+ 'reason' => 'Email already in use',
+ 'details' => 'You cannot create another user with the same email'
+ )
+ );
+ }
+
+ // if nickname is defined it has to be unique
+ if (!$data['User']['nickname'] == '') {
+ $allnicks = $this->User->findAll("nickname='{$this->User->data['User']['nickname']}'");
+ if (!empty($allnicks)) {
+ $this->User->invalidate('nickname');
+ return $this->_renderError(
+ array(
+ 'reason' => 'nickname already in use',
+ 'details' => 'cannot create a new user because the selected nickname is already in use.'
+ )
+ );
+ }
+ }
+
+ // any errors? Get out of here.
+ if (!$this->User->save()) {
+ return $this->_renderError(
+ array(
+ 'reason' => 'There was an error while creating the account'
+ )
+ );
+ $this->render();
+ return;
+ }
+ }
+
+ /**********************************************
+ /*************** PUBLISH PROCESS **************
+ /*********************************************/
+
+ /**
+ * Dispatcher for the publish method
+ *
+ */
+ function publish() {
+
+ $args = func_get_args();
+ return $this->dispatchHttpMethod(array(
+ 'POST' => 'publish_POST'
+ ), $args);
+
+ }
+
+ /**
+ * Publish a new tutorial
+ *
+ */
+ function publish_POST($context) {
+ extract($context);
+
+ $this->_checkLoggedIn();
+
+
+
+ }
+
+ /**********************************************
+ /**************** REVIEW PROCESS **************
+ /*********************************************/
+
+ function review($id) {
+
+ $args = func_get_args();
+ return $this->dispatchHttpMethod(array(
+ 'POST' => 'review_POST'
+ ), $args);
+
+ }
+
+ /**
+ * This function allow rating on activities for the authenticated user.
+ *
+ */
+ function review_POST($context, $id) {
+ extract($context);
+
+ // Restricted part of the api
+ $this->_checkLoggedIn();
+
+ // First, search for the addon we want to rate
+ $this->_getAddons(array($id));
+
+ // Check if the addon we are looking for exist
+ // the viewVars is set via _getAddons function
+ if (isset($this->viewVars['addonsdata'][$id])) {
+
+ // The addon we want to rate is found
+ $addon = $this->viewVars['addonsdata'][$id];
+ $this->_sendToView('addon', $addon);
+
+ // check if the current user owns this Addon
+ // A user can't review his own add-on
+ $isauthor = $this->_checkOwnership($id);
+
+ if($isauthor) {
+ return $this->_renderError(
+ array(
+ 'reason' => 'You cannot review your own tutorial'
+ )
+ );
+ }
+
+ // Start the review process
+ $this->_saveOrUpdateReview($addon);
+
+ } else {
+
+ // Output error to view if tutorial not found
+ return $this->_renderError(
+ array('reason' => 'Tutorial not found')
+ );
+ }
+
+ }
+
+
+ /**********************************************
+ /*************** HELPER FUNCTIONS *************
+ /*********************************************/
+
+ /**
+ * This function execute the review process for a particular addon
+ *
+ */
+ function _saveOrUpdateReview($addon) {
+ global $valid_status;
+
+ // Create the Review array and map it with the POST variables
+ $data['Review'] = $this->getParams(array(
+ 'rating' => '',
+ 'title' => '',
+ 'body' => '',
+ 'id' => 0,
+ ));
+
+ // Add the new review or edit an existing review
+ if (isset($data['Review'])) {
+ $old_title = $data['Review']['title'];
+ $old_body = $data['Review']['body'];
+ $this->Amo->clean($data['Review']);
+
+ // validate rating
+ if ($data['Review']['rating'] < 0 || $data['Review']['rating'] > 5) {
+ $this->Review->invalidate('rating');
+ return;
+ }
+
+ // Fill in basic details for the review
+ $data['Review']['version_id'] = $this->Version->getVersionByAddonId($addon['Addon']['id'], $valid_status); // add version id to data array
+ $data['Review']['user_id'] = $this->auth_user['id'];
+ $data['Review']['editorreview'] = 0; // auto-approve review
+
+ // if id is set for the review, check if it's valid
+ if ($data['Review']['id'] !== 0) {
+ $oldreview = $this->Review->find("Version.addon_id = {$addon['Addon']['id']} AND Review.user_id = {$this->auth_user['id']}");
+ if (!isset($oldreview['Review']['id']) || $oldreview['Review']['id'] === $data['Review']['id'])
+ $this->Review->invalidate('id');
+ }
+
+ // Save information in DB
+ if ($this->Review->save($data)) {
+ //Log addon action for new reviews
+ if (empty($data['Review']['id'])) {
+ //$this->Addonlog->logAddReview($this, $addon['Addon']['id'], $this->Review->getLastInsertID());
+ }
+
+ $this->Review->updateBayesianRating(array($addon['Addon']['id']));
+ $this->_sendToView('success', true);
+
+ // We return the saved review to the View
+ $reviews = $this->Review->getReviews(array($this->Review->getLastInsertID()));
+ $thisReview = $reviews[0];
+
+ $this->_sendToView('review', $thisReview);
+
+ return;
+
+ } else {
+ $data['Review']['title'] = $old_title;
+ $data['Review']['body'] = $old_body;
+
+ return $this->_renderError(
+ array('reason' => 'There was an error while saving the review')
+ );
+ }
+ }
+
+ }
+
+
+ /**
+ * API specific publish
+ * Uses XML encoding and is UTF-8 safe
+ * @param mixed the data array (or string) to be html-encoded (by reference)
+ * @param bool clean the array keys as well?
+ * @return void
+ */
+ function _sendToView($viewvar, $value, $sanitizeme = true) {
+ if ($sanitizeme) {
+ if (is_array($value)) {
+ $this->_sanitizeArrayForXML($value);
+ } else {
+ $tmp = array($value);
+ $this->_sanitizeArrayForXML($tmp);
+ $value = $tmp[0];
+ }
+ }
+ $this->set($viewvar, $value);
+ }
+
+ /**
+ * API specific sanitize
+ * xml-encode an array, recursively
+ * UTF-8 safe
+ *
+ * @param mixed the data array to be encoded
+ * @param bool clean the array keys as well?
+ * @return void
+ */
+ var $sanitize_patterns = array(
+ "/\&/u", "/</u", "/>/u",
+ '/"/u', "/'/u",
+ '/[\cA-\cL]/u',
+ '/[\cN-\cZ]/u',
+ );
+ var $sanitize_replacements = array(
+ "&amp;", "&lt;", "&gt;",
+ "&quot;", "&#39;",
+ "",
+ ""
+ );
+ var $sanitize_field_exceptions = array(
+ 'id'=>1, 'guid'=>1, 'addontype_id'=>1, 'status'=>1, 'higheststatus'=>1,
+ 'icontype'=>1, 'version_id'=>1, 'platform_id'=>1, 'size'=>1, 'hash'=>1,
+ 'codereview'=>1, 'password'=>1, 'emailhidden'=>1, 'sandboxshown'=>1,
+ 'averagerating'=>1, 'textdir'=>1, 'locale'=>1, 'locale_html'=>1,
+ 'created'=>1, 'modified'=>1, 'datestatuschanged'=>1
+ );
+
+ function _sanitizeArrayForXML(&$data, $cleankeys = false) {
+
+ if (empty($data)) return;
+
+ foreach ($data as $key => $value) {
+ if (isset($this->sanitize_field_exceptions[$key])) {
+ // @todo This if() statement is a temporary solution until we come up with
+ // a better way of excluding fields from being sanitized.
+ continue;
+ } else if (empty($value)) {
+ continue;
+ } else if (is_array($value)) {
+ $this->_sanitizeArrayForXML($data[$key], $cleankeys);
+ } else {
+ $data[$key] = preg_replace(
+ $this->sanitize_patterns,
+ $this->sanitize_replacements,
+ $data[$key]
+ );
+ }
+ }
+
+ // change the keys if necessary
+ if ($cleankeys) {
+ $keys = array_keys($data);
+ $this->_sanitizeArrayForXML($keys, false);
+ $data = array_combine($keys, array_values($data));
+ }
+
+ }
+
+ /**
+ * Render an HTTP status along with optional template and location.
+ *
+ * @param string HTTP status
+ * @param string (optional) Name of a view to render
+ * @param array (optional) Vars to be published to the template
+ * @param string (optional) URL for Location: header
+ */
+ function renderStatus($status, $view=null, $ns=null, $location=null) {
+ $this->layout = ($view == 'empty') ? '' : 'rest';
+ header('HTTP/1.1 ' . $status);
+ if (!empty($ns)) foreach ($ns as $k=>$v)
+ $this->_sendToView($k, $v);
+ if (null !== $location)
+ header('Location: '.$location);
+ if (null !== $view)
+ return $this->render($view);
+ }
+
+ /**
+ * Dispatch to the appropriate handler based on HTTP method and a map of
+ * handlers.
+ */
+ function dispatchHttpMethod($map, $args=NULL, $context=null) {
+
+ if (null == $args) $args = array();
+ if (null == $context) $context = array();
+
+ $method = $this->getHttpMethod();
+
+ if ('OPTIONS' == $method) {
+ header('Allow: ' . join(', ', array_keys($map)));
+ $this->_sendToView('methods', array_keys($map));
+ return $this->render('options');
+ }
+
+ if (!isset($map[$method])) {
+ return $this->renderStatus(
+ self::STATUS_METHOD_NOT_ALLOWED, 'error',
+ array('reason' => $method . '_not_allowed')
+ );
+ }
+
+ return call_user_func_array(
+ array($this, $map[$method]),
+ array_merge(array($context), $args)
+ );
+ }
+
+ /**
+ * Grab named keys from POST parameters. Missing parameters will be
+ * set as null.
+ *
+ * @param array list of named parameters.
+ */
+ function getParams($list) {
+ $params = array();
+ $raw = array();
+ if ($this->getHttpMethod() != 'PUT') {
+ $raw = array_merge($_GET, $_POST);
+ } else {
+ $raw = array();
+ if (!empty($_SERVER['CONTENT_LENGTH'])) {
+ // HACK: $_POST isn't populated by PUT
+ $data = file_get_contents('php://input');
+ mb_parse_str($data, $raw);
+ }
+ $raw = array_merge($_GET, $raw);
+ }
+ foreach ($list as $name=>$default) {
+ $params[$name] = isset($raw[$name]) ?
+ $raw[$name] : $default;
+ }
+ return $params;
+ }
+
+ /**
+ * Figure out the current HTTP method, with overrides accepted in a _method
+ * parameter (GET/POST) or in an X_HTTP_METHOD_OVERRIDE header ala Google
+ */
+ function getHttpMethod() {
+ if (!empty($_POST['_method']))
+ return strtoupper($_POST['method']);
+ if (!empty($_GET['_method']))
+ return strtoupper($_GET['method']);
+ if (!empty($_SERVER['X_HTTP_METHOD_OVERRIDE']))
+ return strtoupper($_SERVER['X_HTTP_METHOD_OVERRIDE']);
+ if (!empty($_SERVER['REQUEST_METHOD']))
+ return strtoupper($_SERVER['REQUEST_METHOD']);
+ }
+
+ /**
+ * Return whether the current HTTP method is a request to write in some
+ * way.
+ */
+ function isWriteHttpMethod() {
+ return in_array($this->getHttpMethod(), array('POST', 'DELETE', 'PUT'));
+ }
+
+ /**
+ * If an if-modified-since header was provided, return a 304 if the
+ * collection indeed has not been modified since the given time.
+ */
+ function isNotModifiedSince() {
+ $since = @$_SERVER['HTTP_IF_MODIFIED_SINCE'];
+ if ('GET' == $this->getHttpMethod() && $since) {
+ if ($this->last_modified <= strtotime($since)) {
+ return $this->renderStatus(
+ self::STATUS_NOT_MODIFIED, 'empty'
+ );
+ }
+ }
+ }
+
+ /**
+ * Standalone string sanitize for XML
+ *
+ * @param string
+ * @return string
+ */
+ function sanitizeForXML($value) {
+ return preg_replace(
+ $this->sanitize_patterns,
+ $this->sanitize_replacements,
+ $value
+ );
+ }
+
+
+ /**
+ * Check if the current authenticated user has ownership
+ * of the specified addon id
+ */
+ function _checkOwnership($id) {
+
+ $user_id = $this->auth_user['id'];
+
+ //Check if user is an admin
+ if ($this->SimpleAcl->actionAllowed('Admin', 'EditAnyAddon', $this->auth_user)) {
+ return false;
+ }
+
+ // Query
+ $results = $this->Addon->query("SELECT * FROM addons_users WHERE addon_id={$id} AND user_id={$user_id}");
+
+ //check if user is an author of the add-on
+ if ($results)
+ return true;
+ else
+ return false;
+ }
+
+
+ /**
+ * Get Addon details
+ *
+ * @param ids: Array containing ids of the addons we want
+ */
+ function _getAddons($ids) {
+
+ $addonsdata = array();
+ foreach ($ids as $id) {
+ $_conditions = array(
+ 'Addon.id' => $id,
+ 'Addon.inactive' => 0,
+ 'Addon.addontype_id' => array(ADDON_EXTENSION,ADDON_PLUGIN)
+ );
+
+ // get basic addon data
+ // same criteria as used by the amo display action
+ $this->Addon->bindOnly('User', 'Version', 'Tag', 'AddonCategory');
+ $addon_data = $this->Addon->find($_conditions, null , null , 1);
+
+ if (empty($addon_data)) {
+ // this covers the case where we turned up something in the requested set that
+ // was invalid for whatever reason.
+ continue;
+ }
+
+ // get addon type
+ $this_addon_type = $this->Addontype->findById($addon_data['Addon']['addontype_id']);
+ $addon_data['Addon_type'] = $this_addon_type;
+
+ $install_version
+ = $this->Version->getVersionByAddonId($addon_data['Addon']['id'],
+ STATUS_PUBLIC);
+
+ // find the addon version to report to user
+ foreach ($addon_data['Version'] as $v) {
+ if ($v['id'] == $install_version) {
+ $addon_data['install_version'] = $v['version'];
+ break;
+ }
+ }
+
+ // get filename for install
+ $fileinfo = $this->File->findAllByVersion_id(
+ $install_version, null, null, null, null, 0);
+
+ if (!is_array($fileinfo) || count($fileinfo)==0) {
+ // don't return addons that don't have a valid
+ // file associated with them
+ continue;
+ }
+
+ // get compatible apps
+ $compatible_apps = $this->Version->getCompatibleApps($install_version);
+ $addon_data['Compatible_apps'] = $compatible_apps;
+
+
+ // get compatible platforms
+
+ foreach($fileinfo as &$file) {
+ $this->Platform->unbindFully();
+ $this_plat = $this->Platform->findById($file['Platform']['id']);
+ $file['Platform']['apiname'] = $this_plat['Translation']['name']['string'];
+ $platforms[] = $this_plat;
+ }
+
+ if ($this->api_version > 0 ) {
+ // return an array of compatible os names
+ // right now logic is still wrong, but this enables
+ // xml changes and logic will be fixed later
+ if (empty($platforms)) {
+ $addon_data['all_compatible_os'] = array();
+ } else {
+ $addon_data['all_compatible_os'] = $platforms;
+ }
+
+
+ }
+
+ // pull highlighted preview thumbnail url
+ $addon_data['Thumbnail'] = $this->Image->getHighlightedPreviewURL($id);
+
+ // the icon
+ $addon_data['Icon'] = $this->Image->getAddonIconURL($id);
+
+ $addon_data['fileinfo'] = $fileinfo;
+
+ // add data to array
+ $addonsdata[$id] = $addon_data;
+ }
+
+ $this->set('addonsdata' , $addonsdata);
+
+ }
+
+ /**
+ * Return the current authenticated user, or return null and set 401
+ * Unauthorized headers.
+ *
+ * @return mixed Authenticated user details.
+ */
+ function _getAuthUser() {
+ $auth_user = null;
+
+ // Check an auth header token
+ if (null == $auth_user && !empty($_SERVER['HTTP_X_API_AUTH'])) {
+ // Try accepting an API auth token in a header.
+ $token = $_SERVER['HTTP_X_API_AUTH'];
+ $auth_user = $this->ApiAuthToken->getUserForAuthToken($token);
+ }
+
+ return $auth_user;
+ }
+
+ /**
+ * Returns a access denied "401 Unauthorized"
+ * error when the user is not authenticated
+ */
+ function _access_denied() {
+
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Basic realm="Tutorius API"');
+ $this->_sendToView('reason', 'unauthorized');
+ $this->_sendToView('href', $this->base_url . 'auth');
+ $this->render('error');
+ exit();
+ }
+
+ /**
+ * This function check if the user is authenticated.
+ *
+ */
+ function _checkLoggedIn( $admin = false ) {
+
+ if (!$this->auth_user || ($admin && $this->SimpleAcl->actionAllowed('Admin', '%', $this->auth_user)))
+ $this->_access_denied();
+ }
+
+ /**
+ * Render the error view with an error message and details
+ *
+ */
+ function _renderError($ns) {
+ $this->layout = ('error' == 'empty') ? '' : 'rest';
+ if (!empty($ns)) foreach ($ns as $k=>$v)
+ $this->_sendToView($k, $v);
+
+ return $this->render('error');
+ }
+}
diff --git a/site/app/views/tutorius_api/error.thtml b/site/app/views/tutorius_api/error.thtml
new file mode 100644
index 0000000..f83d00d
--- /dev/null
+++ b/site/app/views/tutorius_api/error.thtml
@@ -0,0 +1,43 @@
+<?php
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is addons.mozilla.org site.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * l.m.orchard <lorchard@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+?>
+
+<error xmlns="http://addons.mozilla.org/"
+ reason="<?php echo $reason ?>"
+ <?php if (isset($details)): ?>details="<?php echo $details ?>"<?php endif ?>
+/> \ No newline at end of file
diff --git a/site/app/views/tutorius_api/index.thtml b/site/app/views/tutorius_api/index.thtml
new file mode 100644
index 0000000..2a790e2
--- /dev/null
+++ b/site/app/views/tutorius_api/index.thtml
@@ -0,0 +1,62 @@
+<?php
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is addons.mozilla.org site.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Laura Thomson <lthomson@mozilla.com> (Original Author)
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+<tutorius xmlns="http://tutorius.org" xml:base="<?php echo $base_url ?>">
+ <review href="review" >
+ <description>Used to post or update a review</description>
+ <method>POST</method>
+ <params>
+ <param>
+ <name>title</name>
+ <type>string</type>
+ <description>Title of the review</description>
+ </param>
+ <param>
+ <name>body</name>
+ <type>string</type>
+ <description>Review content</description>
+ </param>
+ <param>
+ <name>rating</name>
+ <type>int</type>
+ <description>rating between 0 and 5</description>
+ </param>
+ </params>
+ </review>
+ <login href="login" />
+ <creatAccount href="createAccount" />
+ </tutorius> \ No newline at end of file
diff --git a/site/app/views/tutorius_api/register_new_user.thtml b/site/app/views/tutorius_api/register_new_user.thtml
new file mode 100644
index 0000000..212c493
--- /dev/null
+++ b/site/app/views/tutorius_api/register_new_user.thtml
@@ -0,0 +1,44 @@
+<?php
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is addons.mozilla.org site.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Laura Thomson <lthomson@mozilla.com> (Original Author)
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+ if (isset($error)) { ?>
+ <error><?php echo ($error); ?></error>
+<?php
+ } else { ?>
+
+ <createAccount>success</createAccount>
+
+ <?php } ?> \ No newline at end of file
diff --git a/site/app/views/tutorius_api/review.thtml b/site/app/views/tutorius_api/review.thtml
new file mode 100644
index 0000000..3590839
--- /dev/null
+++ b/site/app/views/tutorius_api/review.thtml
@@ -0,0 +1,52 @@
+<?php
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is addons.mozilla.org site.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Laura Thomson <lthomson@mozilla.com> (Original Author)
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+ if (isset($error)) { ?>
+ <error><?php echo ($error); ?></error>
+<?php
+ } else { ?>
+
+ <review xml:base="<?php echo $base_url ?>">
+ <meta>
+ <added><?php echo $review['Review']['created'] ?></added>
+ <addedby><?php echo $review['User']['nickname']?></addedby>
+ </meta>
+ <title><?php echo $review['Translation']['en-US']['title']['string'] ?></title>
+ <body><?php echo $review['Translation']['en-US']['body']['string']?><body>
+ <rating><?php echo $review['Review']['rating']?></rating>
+ </review>
+
+ <?php } ?> \ No newline at end of file