Refactored code and added additional features
authorArtem Daniliants <artem@daniliants.com>
Thu, 13 May 2010 19:13:47 +0000 (22:13 +0300)
committerArtem Daniliants <artem@daniliants.com>
Thu, 13 May 2010 19:13:47 +0000 (22:13 +0300)
- Added avatar support
- Added support for fetching list of all registered users
- Added support for viewing user's profile
- Modified database: added last_activity and description columns
- Added support for last_activity (when user last contacted server)

Server/application/config/api.php
Server/application/config/upload.php [new file with mode: 0644]
Server/application/controllers/api.php [deleted file]
Server/application/controllers/results.php [new file with mode: 0644]
Server/application/controllers/users.php [new file with mode: 0644]
Server/application/helpers/apiler.php [new file with mode: 0644]
Server/application/models/database_structure.sql
Server/application/models/user.php
Server/application/views/api/user_info.php [new file with mode: 0644]
Server/application/views/api/user_list.php [new file with mode: 0644]

index a38951c..99b56a8 100644 (file)
 /*
  * Salt for hashing (should always be changed on deployment!)
  */
-$config['salf'] = 'klzdjkhI/&/567Û%#ÛgbnkBJHVTVjdhiuhdbmzcss-__FDHSYUWYUTUDGBZ';
\ No newline at end of file
+$config['salf'] = 'klzdjkhI/&/567Û%#ÛgbnkBJHVTVjdhiuhdbmzcss-__FDHSYUWYUTUDGBZ';
+
+/*
+ * Maximum filesize for avatar images (in bytes)
+ */
+$config['avatar_max_filesize'] = '102400';
+
+/*
+ * Allowed image types for avatars
+ */
+$config['avatar_allowed_filetypes'] = array('image/jpeg');
\ No newline at end of file
diff --git a/Server/application/config/upload.php b/Server/application/config/upload.php
new file mode 100644 (file)
index 0000000..6861499
--- /dev/null
@@ -0,0 +1,17 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * @package  Core
+ *
+ * This path is relative to your index file. Absolute paths are also supported.
+ */
+$config['directory'] = DOCROOT.'static/uploads/avatars';
+
+/**
+ * Enable or disable directory creation.
+ */
+$config['create_directories'] = TRUE;
+
+/**
+ * Remove spaces from uploaded filenames.
+ */
+$config['remove_spaces'] = TRUE;
\ No newline at end of file
diff --git a/Server/application/controllers/api.php b/Server/application/controllers/api.php
deleted file mode 100644 (file)
index fe8a5f7..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php defined('SYSPATH') OR die('No direct access allowed.');
-/*
- * API controller for communicating with mobile clients
- * 
- * @author      Artem Daniliants <artem@daniliants.com>
- * @copyright   (c) 2010 Speed Freak team
- * @license     http://opensource.org/licenses/gpl-license.php GNU Public License
- */
-
-class Api_Controller extends Controller{
-    
-       /*
-        * Default action when no parameters are given to controller
-        */
-       public function index(){
-        url::redirect(Kohana::config('api.default_redirect'),301);
-    }
-    
-    /*
-     * New user registration
-     */
-    public function register(){
-       $xml = $this->get_xml();
-       try {
-          $user = new User_Model($xml->login, $xml->password, $xml->email);
-          echo "OK";
-       }
-        catch (Exception $e) {
-            echo $e->getMessage() . "\n";
-            die;
-        } 
-    }
-    
-    /*
-     * Returns XML file supplied by client
-     */
-    private function get_xml(){
-        if (isset($_POST['xml'])){
-            $xml = simplexml_load_string($_POST['xml']);
-        }
-        elseif (isset($_FILES['xml'])){
-            $xml = simplexml_load_file($_FILES['xml']['tmp_name']);
-        }
-        else{
-            header("HTTP/1.1 400 Bad Request");
-            echo "Please supply required parameters";
-            die;
-        }
-        return $xml;
-    }
-    
-    /*
-     * Check that supplied credentials are valid using basic authentication
-     *
-     */
-    public function login(){
-       if ($this->is_authorized()){
-                 print "OK";
-                 die;
-       }
-               else
-                 $this->not_authorized();
-    }
-
-    /*
-     * Validate supplied credentials
-     */
-    public function is_authorized(){
-       if (isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])){
-            $user = new User_Model();
-            if ($user->login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']))
-                return true;
-            else
-                return false;
-       }
-        else
-            return false;
-
-    }
-
-    /*
-     * Display "You're not authorized error to client
-     *
-     * @todo Need to create function for generally displaying errors
-     */
-    public function not_authorized(){
-       header('HTTP/1.0 401 Unauthorized');
-        print "Invalid credentials or not registered";
-        die;
-    }
-
-    /*
-     * Get categories list and output it as XML
-     *
-     */
-    public function categories(){
-       if ($this->is_authorized()){
-               $view = new View('api/categories');
-               $cat = new Category_Model();
-               $view->categories=$cat->get_all();
-               $view->render(true);
-       }
-       else
-          $this->not_authorized();
-    }
-
-    /*
-     * Get results
-     *
-     */
-    public function results($category, $limit, $show_unit=false){
-       $results = New Result_Model();
-       $cat = New Category_Model();
-        if ($cat->category_exists($category) AND $this->is_authorized() AND isset($limit)){
-               $view = new View('api/results');
-               $view->results = $results->get_results($category, $limit);
-               $view->show_unit=$show_unit;
-               $view->render(true);
-           }
-        else
-            $this->not_authorized();
-    }
-
-    /*
-     * Submit results to selected category
-     *
-     * @param string $category Category to which results are submitted
-     */
-    public function update($category){
-       $cat = New Category_Model();
-       if ($cat->category_exists($category) AND $this->is_authorized()){
-               $xml = $this->get_xml();
-               $result = New Result_Model();
-               if ($result->insert($category,$_SERVER['PHP_AUTH_USER'], $xml['value'])){
-                       print "OK";
-                       die;
-               }
-               else {
-                       header("HTTP/1.1 400 Bad Request");
-                   echo "Invalid request";
-                   die;
-               }
-       }
-       else {
-            header("HTTP/1.0 404 Not Found");
-            die('Category not found or not authorized');
-       }
-
-    }
-}
\ No newline at end of file
diff --git a/Server/application/controllers/results.php b/Server/application/controllers/results.php
new file mode 100644 (file)
index 0000000..71a63d3
--- /dev/null
@@ -0,0 +1,71 @@
+ <?php defined('SYSPATH') OR die('No direct access allowed.');
+/*
+ * API controller for managing and viewing results and categories
+ * 
+ * @author      Artem Daniliants <artem@daniliants.com>
+ * @copyright   (c) 2010 Speed Freak team
+ * @license     http://opensource.org/licenses/gpl-license.php GNU Public License
+ */
+
+class Results_Controller extends Controller{
+       
+       /*
+     * Get categories list and output it as XML
+     *
+     */
+    public function categories(){
+       if (apiler::is_authorized()){
+               $view = new View('api/categories');
+               $cat = new Category_Model();
+               $view->categories=$cat->get_all();
+               $view->render(true);
+       }
+       else
+          apiler::not_authorized();
+    }
+
+    /*
+     * Get results
+     *
+     */
+    public function list_results($category, $limit, $show_unit=false){
+       $results = New Result_Model();
+       $cat = New Category_Model();
+        if ($cat->category_exists($category) AND apiler::is_authorized() AND isset($limit)){
+               $view = new View('api/results');
+               $view->results = $results->get_results($category, $limit);
+               $view->show_unit=$show_unit;
+               $view->render(true);
+           }
+        else
+            apiler::not_authorized();
+    }
+
+    /*
+     * Submit results to selected category
+     *
+     * @param string $category Category to which results are submitted
+     */
+    public function update($category){
+       $cat = New Category_Model();
+       if ($cat->category_exists($category) AND apiler::is_authorized()){
+               $xml = apiler::get_xml();
+               $result = New Result_Model();
+               if ($result->insert($category,$_SERVER['PHP_AUTH_USER'], $xml['value'])){
+                       print "OK";
+                       die;
+               }
+               else {
+                       header("HTTP/1.1 400 Bad Request");
+                   echo "Invalid request";
+                   die;
+               }
+       }
+       else {
+            header("HTTP/1.0 404 Not Found");
+            die('Category not found or not authorized');
+       }
+
+    }
+    
+}
\ No newline at end of file
diff --git a/Server/application/controllers/users.php b/Server/application/controllers/users.php
new file mode 100644 (file)
index 0000000..ee20517
--- /dev/null
@@ -0,0 +1,106 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/*
+ * API for registering users and updating profile information
+ * 
+ * @author      Artem Daniliants <artem@daniliants.com>
+ * @copyright   (c) 2010 Speed Freak team
+ * @license     http://opensource.org/licenses/gpl-license.php GNU Public License
+ */
+
+class Users_Controller extends Controller{
+    
+    
+       /**
+        * When no parameters are supplied visitor is redirected to project's website
+        * 
+        * @access public
+        * @return void
+        */
+       public function index(){
+        url::redirect(Kohana::config('api.default_redirect'),301);
+    }
+    
+    
+    /**
+     * Register new user
+     * 
+     * @access public
+     * @return string Returns "OK" string upon succession and error message otherwise
+     */
+    public function register(){
+       $xml = apiler::get_xml();
+       try {
+          $user = new User_Model($xml->login, $xml->password, $xml->email, $xml->description);
+          $this->store_avatar($user->get_id($xml->login));
+          echo "OK";
+       }
+        catch (Exception $e) {
+            echo $e->getMessage() . "\n";
+            die;
+        } 
+    }
+    
+    
+    /**
+     * Display user's information
+     * 
+     * @access public
+     * @param string Username that we wish to get information for
+     * @return string Returns information as XML or error message
+     */
+    public function info($username){
+       if (apiler::is_authorized()){
+                       $view = new View('api/user_info');
+                       $user = new User_Model();
+                       $view->user=$user->get_info($username);
+                       if ($view->user==false)
+                               die('User not found');
+                       if (file_exists(Kohana::config('upload.directory').'/'.$view->user->id.'.jpg'))
+                               $view->avatar=url::site('static/uploads/avatars/'.$view->user->id.'.jpg', 'http');
+                       $view->render(true);
+       }
+               else
+                       apiler::not_authorized();
+    }
+    
+    
+    /**
+     * View all registered users
+     * 
+     * @access public
+     * @return string Returns XML containing list of all users or error message
+     */
+    public function list_all(){
+       $users = new User_Model();
+       $list = $users->list_all_users();
+       $view = new View('api/user_list');
+       $view->list = $list;
+       $view->render(true);
+    }
+    
+    
+    /**
+     * Check that supplied avatar is valid and store it
+     * 
+     * @access private
+     * @param array $image Uploaded item found in $_FILES array
+     * @param integer $id User id that will be used as filename
+     * @return boolean Returns TRUE upon succession and FALSE otherwise
+     */
+    private function store_avatar($id){
+       if (isset($_FILES['avatar'])){
+               $info = getimagesize($_FILES['avatar']['tmp_name']);
+        
+                       if ($_FILES['avatar']['size']<=Kohana::config('api.avatar_max_filesize') AND in_array($info['mime'], Kohana::config('api.avatar_allowed_filetypes')))
+                       {
+                               if (upload::save('avatar', $id.'.jpg'))
+                                       return True;
+                               else
+                                       return False;
+                       }
+                       else
+                               return False;
+               }
+    }
+   
+}
\ No newline at end of file
diff --git a/Server/application/helpers/apiler.php b/Server/application/helpers/apiler.php
new file mode 100644 (file)
index 0000000..d142785
--- /dev/null
@@ -0,0 +1,83 @@
+<?php defined('SYSPATH') or die('No direct script access.');
+/*
+ * Helper class mainly for API controllers
+ * 
+ * @author      Artem Daniliants <artem@daniliants.com>
+ * @copyright   (c) 2010 Speed Freak team
+ * @license     http://opensource.org/licenses/gpl-license.php GNU Public License
+ */
+
+class apiler_Core {
+
+    /**
+     * Get XML either from POST variable or FILES variable
+     * 
+     * @access private
+     * @static
+     * @return mixed Returns either SimpleXml object or outputs error along with HTTP 400 error
+     */
+    public static function get_xml(){
+        if (isset($_POST['xml'])){
+            $xml = simplexml_load_string($_POST['xml']);
+        }
+        elseif (isset($_FILES['xml'])){
+            $xml = simplexml_load_file($_FILES['xml']['tmp_name']);
+        }
+        else{
+            header("HTTP/1.1 400 Bad Request");
+            echo "Please supply required parameters";
+            die;
+        }
+        return $xml;
+    }
+    
+    
+    /**
+     * Check if user is authorized
+     * 
+     * @access public
+     * @static
+     * @return boolean Returns TRUE if authorized and FALSE otherwise
+     */
+    public static function is_authorized(){
+               if (isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])){
+                   $user = new User_Model();
+                   if ($user->login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']))
+                       return true;
+                   else
+                       return false;
+               }
+               else
+                   return false;
+
+    }
+    
+    
+    /**
+     * Display "Unauthorized error"
+     * 
+     * @access public
+     * @static
+     * @return string Prints error text
+     */
+    public static function not_authorized(){
+               header('HTTP/1.0 401 Unauthorized');
+        print "Invalid credentials or not registered";
+        die;
+    }
+    
+    /**
+     * Verify user's credentials
+     *  
+     * @access public
+     * @return string Outputs "OK" or error
+     */
+    public function login(){
+               if ($this->is_authorized()){
+               print "OK";
+               die;
+           }else
+                       $this->not_authorized();
+     }
+    
+}
\ No newline at end of file
index 7b2ed70..111c0e9 100644 (file)
@@ -1,10 +1,10 @@
-# Sequel Pro dump
-# Version 1630
+# Sequel Pro dump
+# Version 2210
 # http://code.google.com/p/sequel-pro
 #
 # Host: localhost (MySQL 5.1.37)
 # Database: speedfreak
-# Generation Time: 2010-04-21 11:03:11 +0300
+# Generation Time: 2010-05-13 21:56:50 +0300
 # ************************************************************
 
 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
@@ -61,10 +61,12 @@ CREATE TABLE `users` (
   `username` char(255) DEFAULT NULL,
   `password` char(255) DEFAULT NULL,
   `email` char(255) DEFAULT NULL,
+  `description` text,
+  `last_activity` datetime DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `login_unique` (`username`),
   UNIQUE KEY `email_unique` (`email`)
-) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
index 78ef8ca..0535789 100644 (file)
@@ -17,7 +17,7 @@ class User_Model extends Model {
         * @param string $email Valid email address
         * @return bool Returns True if operation was successfull and exception otherwise
         */
-    public function __construct($username='', $password='', $email=''){
+    public function __construct($username='', $password='', $email='', $description=''){
         
        // load database library into $this->db
         parent::__construct();
@@ -36,7 +36,7 @@ class User_Model extends Model {
             elseif ($this->user_exists($username, $email))
                 throw new Exception('User already exists (login or email matched)');
                 
-            if ($this->register($username, $password, $email)->valid())
+            if ($this->register($username, $password, $email, $description)->valid())
                 return true;
             else
                 return false;
@@ -52,14 +52,14 @@ class User_Model extends Model {
      * @param string $email Valid email address
      * @return bool Returns True if operation was successfull and exception otherwise
      */
-    private function register($username, $password, $email){
+    private function register($username, $password, $email, $description){
        // hash password
         $password = $this->hash($password);
         
         // @todo I can't seem to get query working when password binding has '' around it like others
         if ($this->user_exists($username, $email)==false)
-          return $this->db->query("INSERT into users SET username = '?', password = ?, email = '?'",
-                  $username, $password, $email);
+          return $this->db->query("INSERT into users SET username = '?', password = ?, description='?', last_activity=NOW(), email = '?'",
+                  $username, $password, $description, $email);
         else
             return false;
     }
@@ -82,13 +82,23 @@ class User_Model extends Model {
      * @return bool Returns True if user exists and false otherwise
      */
     private function user_exists($username, $email){
-       if ($this->db->query("SELECT id FROM users WHERE username = '?' OR email = '?'",
+       if ($this->db->query("SELECT id FROM users WHERE username='?' OR email='?'",
                           $username, $email)->count()>0)
               return true;
            else
               return false;          
     }
     
+    
+    public function get_info($username){
+       $result = $this->db->query("SELECT * FROM users WHERE username = ?", $username);
+               if ($result->count()>0)
+           return $result[0];
+        else
+           return false;
+    }
+    
+    
     /*
      * Get user id
      *
@@ -96,12 +106,26 @@ class User_Model extends Model {
      * @return integer|bool User id if successful or false
      */
     public function get_id($username){
-        $result = $this->db->query("SELECT id FROM users WHERE username = ?", $username);
-       if ($result->count()>0)
+        $result = $this->db->query("SELECT id FROM users WHERE username='?'", $username);
+               if ($result->count()>0)
            return $result[0]->id;
         else
            return false;
     }
+    
+    /**
+     * List all users found in database
+     * 
+     * @access public
+     * @return boolean|object Returns object containing all users or false
+     */
+    public function list_all_users(){
+        $result = $this->db->query("SELECT * FROM users");
+               if ($result->count()>0)
+           return $result;
+        else
+           return false;
+    }
 
     /*
      * Check if supplied credentials are valid
diff --git a/Server/application/views/api/user_info.php b/Server/application/views/api/user_info.php
new file mode 100644 (file)
index 0000000..598eed3
--- /dev/null
@@ -0,0 +1,3 @@
+<?php echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
+
+<user login="<?php echo $user->username ; ?>" description="<![CDATA <?php echo $user->description; ?> ]]>" last_activity="<?php echo $user->last_activity ; ?>" <?php if (isset($avatar)) echo 'avatar="'.$avatar.'" '; ?> />
\ No newline at end of file
diff --git a/Server/application/views/api/user_list.php b/Server/application/views/api/user_list.php
new file mode 100644 (file)
index 0000000..261fb04
--- /dev/null
@@ -0,0 +1,8 @@
+<?php echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
+
+<users><?php if (count($list)>0) foreach($list as $user){ ?>
+       
+       <user login="<?php echo $user->username ; ?>" description="<![CDATA <?php echo $user->description; ?> ]]>" last_activity="<?php echo $user->last_activity ; ?>" <?php if (isset($avatar)) echo 'avatar="'.$avatar.'" '; ?> />
+       <?php } ?>
+
+</users>
\ No newline at end of file