Laurent Opprecht 12 rokov pred
rodič
commit
bffe3f6015
43 zmenil súbory, kde vykonal 3944 pridanie a 564 odobranie
  1. 113 86
      main/auth/key/key_auth.class.php
  2. 0 1
      main/auth/key/login.php
  3. 3 3
      main/document/document.inc.php
  4. 336 342
      main/document/document.php
  5. 154 0
      main/document/file.php
  6. BIN
      main/img/icons/22/document_send.png
  7. 0 2
      main/inc/conf/course_info.conf.dist.php
  8. 185 0
      main/inc/lib/access_token.class.php
  9. 14 10
      main/inc/lib/autoload.class.php
  10. 36 3
      main/inc/lib/chamilo.class.php
  11. 145 0
      main/inc/lib/course.class.php
  12. 302 0
      main/inc/lib/document.class.php
  13. 1 1
      main/inc/lib/export.lib.inc.php
  14. 0 32
      main/inc/lib/header.class.php
  15. 251 0
      main/inc/lib/item_property.class.php
  16. 2 2
      main/inc/lib/javascript/jqgrid/css/ui.jqgrid.css
  17. 34 32
      main/inc/lib/login.lib.php
  18. 2 1
      main/inc/lib/main_api.lib.php
  19. 642 0
      main/inc/lib/portfolio.class.php
  20. 23 0
      main/inc/lib/response.class.php
  21. 29 4
      main/inc/lib/result_set.class.php
  22. 439 0
      main/inc/lib/student_publication.class.php
  23. 145 0
      main/inc/lib/system/io/temp.class.php
  24. 131 0
      main/inc/lib/system/net/curl.class.php
  25. 72 0
      main/inc/lib/system/net/http_channel.class.php
  26. 149 0
      main/inc/lib/system/portfolio/artefact.class.php
  27. 35 0
      main/inc/lib/system/portfolio/download.class.php
  28. 52 0
      main/inc/lib/system/portfolio/mahara.class.php
  29. 106 0
      main/inc/lib/system/portfolio/portfolio.class.php
  30. 19 0
      main/inc/lib/system/portfolio/user.class.php
  31. 81 0
      main/inc/lib/system/web/header.class.php
  32. 5 1
      main/inc/lib/system/web/request.class.php
  33. 0 0
      main/inc/lib/system/web/request_server.class.php
  34. 103 0
      main/inc/lib/user_api_key_manager.class.php
  35. 1 1
      main/inc/lib/usermanager.lib.php
  36. 129 0
      main/inc/lib/zip.class.php
  37. 0 1
      main/install/configuration.dist.php
  38. 1 0
      main/install/install_files.inc.php
  39. 46 40
      main/install/update-files-1.8.8-1.9.0.inc.php
  40. 6 0
      main/portfolio/index.php
  41. 15 0
      main/portfolio/share.php
  42. 133 0
      main/work/file.php
  43. 4 2
      main/work/work.lib.php

+ 113 - 86
main/auth/key/key_auth.class.php

@@ -2,18 +2,11 @@
 
 use \ChamiloSession as Session;
 
-
 /**
  * Used to authenticate user with an access token. By default this method is disabled.
- * Method used primarily to make API calls:Rss, etc.
- * 
- * Access is granted only for the services that are enabled. 
- * 
- * You need to call 
- * 
- *      KeyAuth::enable_services('my_service');
+ * Method used primarily to make API calls: Rss, file upload.
  * 
- * to enable this access method for a specific service before a call to global.inc.php.
+ * Access is granted only for the services that are enabled.  
  * 
  * To be secured this method must 
  * 
@@ -23,15 +16,40 @@ use \ChamiloSession as Session;
  * This authentication method is session less. This is to ensure that the navigator 
  * do not receive an access cookie that will grant it access to other parts of the
  * application.
- *
+ * 
+ * 
+ * Usage:
+ * 
+ * Enable KeyAuth for a specific service. Add the following lines so that 
+ * the key authentication method is enabled for a specific service before 
+ * calling global.inc.php.
+ * 
+ *      include_once '.../main/inc/autoload.inc.php';
+ *      KeyAuth::enable_services('my_service');
+ *      include_once '.../main/inc/global.inc.php';
+ * 
+ * 
+ * Enable url access for a short period of time:
+ * 
+ *      token = KeyAuth::create_temp_token();
+ *      url = '...?access_token=' . $token ;
+ * 
+ * @see AccessToken
  * @license see /license.txt
  * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
  */
 class KeyAuth
 {
 
+    const PARAM_ACCESS_TOKEN = 'access_token';
+
     protected static $services = array();
 
+    public static function create_temp_token($service = null, $duration = 60, $user_id = null)
+    {
+        return UserApiKeyManager::create_temp_token($service, $duration, $user_id);
+    }
+
     /**
      * Returns enabled services
      * 
@@ -52,15 +70,11 @@ class KeyAuth
     {
         $args = func_get_args();
         $names = array();
-        foreach ($args as $arg)
-        {
-            if (is_object($arg))
-            {
+        foreach ($args as $arg) {
+            if (is_object($arg)) {
                 $f = array($arg, 'get_service_name');
                 $name = call_user_func($f);
-            }
-            else
-            {
+            } else {
                 $name = $arg;
             }
             $name = substr($name, 0, 10);
@@ -72,20 +86,46 @@ class KeyAuth
     {
         $args = func_get_args();
         $names = array();
-        foreach ($args as $name)
-        {
+        foreach ($args as $name) {
             $name = substr($name, 0, 10);
             unset(self::$services[$name]);
         }
     }
 
+    public static function is_service_enabled($service)
+    {
+        $services = self::get_services();
+        foreach ($services as $s) {
+            if ($s == $service) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public static function clear_services()
     {
         self::$services[$name] = array();
     }
-    
+
+    /**
+     * Enable key authentication for the default service - i.e. chamilo 
+     */
+    public static function enable()
+    {
+        self::enable_services(UserApiKeyManager::default_service());
+    }
+
+    public static function disable()
+    {
+        self::$services[$name] = array();
+    }
+
     /**
-     * @return Returns true if authentication method is enabled. False otherwise.
+     * Returns true if the key authentication method is enabled. False otherwise.
+     * Default to false.
+     * 
+     * @return bool
      */
     public static function is_enabled()
     {
@@ -98,8 +138,7 @@ class KeyAuth
     public static function instance()
     {
         static $result = null;
-        if(empty($result))
-        {
+        if (empty($result)) {
             $result = new self();
         }
         return $result;
@@ -111,60 +150,52 @@ class KeyAuth
     }
 
     /**
-     * Returns true if security accepts to run otherwise returns false.
+     * Returns true if authentication accepts to run otherwise returns false.
      * 
      * @return boolean 
      */
     public function accept()
     {
-        $user_id = $this->get_user_id();
-        if (empty($user_id))
-        {
+        /**
+         * Authentication method must be enabled 
+         */
+        if (!self::is_enabled()) {
             return false;
         }
 
-        $services = $this->get_services();
-        if (empty($services))
-        {
+        $token = $this->get_access_token();
+        if ($token->is_empty()) {
             return false;
         }
 
-        $token = $this->get_token();
-        if (empty($token))
-        {
+        $key = UserApiKeyManager::get_by_id($token->get_id());
+        if (empty($key)) {
             return false;
         }
 
-//        $control = $this->get_control();
-//        if (empty($control))
-//        {
-//            return false;
-//        }
-
-        $user = UserManager::get_user_info_by_id($user_id);
-        if (empty($user))
-        {
+        /**
+         * The service corresponding to the key must be enabled. 
+         */
+        $service = $key['api_service'];
+        if (!self::is_service_enabled($service)) {
             return false;
         }
 
-        if ($user['active'] != 1)
-        {
+        /**
+         * User associated with the key must be active 
+         */
+        $user = UserManager::get_user_info_by_id($token->get_user_id());
+        if (empty($user)) {
             return false;
         }
-
-        foreach ($services as $service)
-        {
-            $keys = UserManager::get_api_keys($user_id, $service);
-            $keys = $keys ? $keys : array();
-            foreach ($keys as $key)
-            {
-                if ($key == $token)
-                {
-                    return true;
-                }
-            }
+        if (!$user['active']) {
+            return false;
         }
-        return false;
+
+        /**
+         * Token must be valid. 
+         */
+        return $token->is_valid();
     }
 
     /**
@@ -175,8 +206,7 @@ class KeyAuth
      */
     public function login()
     {
-        if (!$this->accept())
-        {
+        if (!$this->accept()) {
             return false;
         }
         /**
@@ -184,52 +214,49 @@ class KeyAuth
          */
         Session::destroy();
 
-        global $_user, $_uid;
-        $_uid = $this->get_user_id();
-        $_user = UserManager::get_user_info_by_id($_uid);
-
-        Session::write('_user',$_user);
-        Session::write('_uid',$_uid);
-        
         /**
          * We don't allow redirection since access is granted only for this call 
          */
         global $no_redirection, $noredirection;
         $no_redirection = true;
         $noredirection = true;
-        Session::write('noredirection',$noredirection);
+        Session::write('noredirection', $noredirection);
+        
+        $user_id = $this->get_user_id();
+        $course_code = $this->get_course_code();
+        $group_id = $this->get_group_id();
         
+        Login::init_user($user_id, true);
+        Login::init_course($course_code, true);
+        Login::init_group($group_id, true);
+
         return true;
     }
 
     /**
-     * Returns the request user id parameter
+     * Returns the request access token
      * 
-     * @return int 
+     * @return AccessToken
      */
+    public function get_access_token()
+    {
+        $string = Request::get(self::PARAM_ACCESS_TOKEN);
+        return AccessToken::parse($string);
+    }
+    
     public function get_user_id()
     {
-        return (int) Request::get('user_id');
+        return $this->get_access_token()->get_user_id();
     }
-
-    /**
-     * Returns the request security token parameter
-     * 
-     * @return string 
-     */
-    public function get_token()
+    
+    public function get_course_code()
     {
-        return Request::get('token');
+        return Request::get('cidReq', 0);
     }
-
-    /**
-     * Returns the control token parameter
-     * 
-     * @return string 
-     */
-    public function get_control()
+    
+    public function get_group_id()
     {
-        return Request::get('control');
+        return Request::get('gidReq', 0);
     }
 
 }

+ 0 - 1
main/auth/key/login.php

@@ -1 +0,0 @@
-<?php

+ 3 - 3
main/document/document.inc.php

@@ -223,9 +223,9 @@ function create_document_link($document_data, $show_as_icon = false, $counter =
                 $copy_to_myfiles = '<a href="'.$copy_myfiles_link.'" style="float:right"'.$prevent_multiple_click.'>'.Display::return_icon('briefcase.png', get_lang('CopyToMyFiles'), array(),ICON_SIZE_SMALL).'&nbsp;&nbsp;</a>';
             }
             $send_to = '';
-//            if ($filetype == 'file') {
-//                $send_to = Portfolio::share($document_data['id'], array('style' => 'float:right;'));
-//            }
+            if ($filetype == 'file') {
+                $send_to = Portfolio::share('document', $document_data['id'], array('style' => 'float:right;'));
+            }
         }
         
         $pdf_icon = '';

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 336 - 342
main/document/document.php


+ 154 - 0
main/document/file.php

@@ -0,0 +1,154 @@
+<?php
+
+Use Model\Document;
+Use Model\Course;
+
+/**
+ * 	Return either 
+ * 
+ *      - one document
+ *      - several documents (file and/or folders) zipped together
+ * 
+ * Used to transfer files to another application through http.
+ * 
+ * Script parameters:
+ * 
+ *      - id        id(s) of the document id=1 or id=1,2,4  
+ *      - cidReq    course code
+ * 
+ * Note this script enables key authentication so access with a key token is possible.
+ * 
+ * @package chamilo.document
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+require_once __DIR__ . '/../inc/autoload.inc.php';
+KeyAuth::enable();
+
+require_once __DIR__ . '/../inc/global.inc.php';
+
+$has_access = api_protect_course_script();
+if (!$has_access) {
+    exit;
+}
+
+session_cache_limiter('none');
+
+$ids = Request::get('id', '');
+$ids = $ids ? explode(',', $ids) : array();
+
+$course = Course::current();
+
+/**
+ * No files requested. We make sure we return 404 error to tell the client
+ * that the call failed. 
+ */
+if (count($ids) == 0 || empty($course)) {
+    Response::not_found();
+}
+
+/**
+ * One file requested. In this case we return the file itself. 
+ */
+if (count($ids) == 1) {
+    $id = reset($ids);
+    $doc = Document::get_by_id($course, $id);
+    if (empty($doc)) {
+        Response::not_found();
+    }
+
+    if ($doc->is_file()) {
+        $has_access = $doc->is_accessible();
+        if (!$has_access) {
+            Response::not_found();
+        }
+
+        event_download(Uri::here());
+        DocumentManager::file_send_for_download($doc);
+        exit;
+    }
+}
+
+/**
+ * Several files requested. In this case we zip them together. 
+ */
+$files = array();
+$folders = array();
+foreach ($ids as $id) {
+    $doc = Document::get_by_id($course, $id);
+    if (!$doc->is_accessible()) {
+        break;
+    }
+    if ($doc->is_file()) {
+        $files[] = $doc;
+    }
+    if ($doc->is_folder()) {
+        $folders[] = $doc;
+    }
+}
+
+$requested_folders = $folders;
+
+/**
+ * Note that if a parent folder is hidden children should not be accesible 
+ * even if they are visible. It is therefore not sufficient to check document 
+ * visibility. 
+ */
+while ($folders) {
+    $items = $folders;
+    $folders = array();
+    foreach ($items as $item) {
+        $children = $item->get_children();
+        foreach ($children as $child) {
+            if (!$child->is_accessible()) {
+                break;
+            }
+            if ($child->is_file()) {
+                $files[] = $child;
+            }
+            if ($child->is_folder()) {
+                $folders[] = $child;
+            }
+        }
+    }
+}
+
+$folders = $requested_folders;
+
+/**
+ * Requested files may not be accessible. 
+ */
+if (count($files) == 0) {
+    Response::not_found();
+}
+
+$root_dir = '';
+$items = array_merge($folders, $files);
+foreach ($items as $item) {
+    $path = $item->get_absolute_path();
+    $path = realpath($path);
+    $dir = dirname($path);
+
+    if (empty($root_dir) || strlen($root_dir) > strlen($dir)) {
+        $root_dir = $dir;
+    }
+}
+
+/**
+ * Zip files together. 
+ */
+$temp_zip_path = Chamilo::temp_file('zip');
+$zip_folder = new PclZip($temp_zip_path);
+foreach ($files as $file) {
+    if (empty($root_dir)) {
+        $root_dir = dirname($file);
+    }
+    $file = (string) $file;
+    $zip_folder->add($file, PCLZIP_OPT_REMOVE_PATH, $root_dir);
+}
+
+/**
+ * Send file for download 
+ */
+event_download(Uri::here());
+DocumentManager::file_send_for_download($temp_zip_path, false, get_lang('Documents') . '.zip');

BIN
main/img/icons/22/document_send.png


+ 0 - 2
main/inc/conf/course_info.conf.dist.php

@@ -41,5 +41,3 @@ $canReportExpirationDate			= true; // Needs to be true
 											// is not automaticly called
 //$linkToChangeDiskQuota			= 'changeQuota.php';
 $urlScriptToReportExpirationDate	= 'postpone.php'; // external script to postpone the expiration of course.
-
-?>

+ 185 - 0
main/inc/lib/access_token.class.php

@@ -0,0 +1,185 @@
+<?php
+
+/**
+ * An access token. Can be passed between applications to grant access.
+ * 
+ * The token aggregate several values together (key id, api key, user id). This
+ * is useful to pass a single value between application and avoid passing
+ * each value as a separate parameter.
+ * 
+ * Note that values are aggregated but not crypted. An external application could
+ * have access to individual components.
+ * 
+ * @see /main/auth/key_auth.class.php
+ * @see table user_api_key
+ * 
+ * Usage:
+ * 
+ * Validate token:
+ * 
+ *      $data = Request::get('access_token');
+ *      $token = AccessToken::parse($data); 
+ *      $token->is_valid();
+ * 
+ * Pass token
+ * 
+ *      $token = new AccessToken(1, 1, '+*ç*%ç*ç');
+ *      $url = '.....?access_token=' . $token;
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class AccessToken
+{
+
+    static function empty_token()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = new self(0, 0, '');
+        }
+        return $result;
+    }
+
+    /**
+     *
+     * @param type $string
+     * @return AccessToken 
+     */
+    static function parse($string)
+    {
+        if (empty($string)) {
+            return self::empty_token();
+        }
+
+        $data = base64_decode($string);
+        $data = explode('/', $data);
+
+        if (count($data) != 3) {
+            return self::empty_token();
+        }
+
+        $id = $data[0];
+        $user_id = $data[1];
+        $key = $data[2];
+        return new self($id, $user_id, $key);
+    }
+    
+    /**
+     *
+     * @param int       $id
+     * @param int       $user_id
+     * @param string    $key 
+     * @return AccessToken
+     */
+    static function create($id, $user_id, $key)
+    {
+        $is_valid = !empty($id) && !empty($user_id) && !empty($key);
+        return $is_valid ? new self($id, $user_id, $key) : self::empty_token();
+    }
+
+    protected $id = 0;
+    protected $user_id = 0;
+    protected $key = '';
+
+    /**
+     *
+     * @param int       $id
+     * @param int       $user_id
+     * @param string    $key 
+     */
+    function __construct($id, $user_id, $key)
+    {
+        $this->id = $id;
+        $this->user_id = $user_id;
+        $this->key = $key;
+    }
+
+    /**
+     * The user_api_key id.
+     * 
+     * @return int
+     */
+    function get_id()
+    {
+        return $this->id;
+    }
+
+    /**
+     * User id.
+     * 
+     * @return string
+     */
+    function get_user_id()
+    {
+        return $this->user_id;
+    }
+
+    /**
+     * User api key.
+     * 
+     * @return string
+     */
+    function get_key()
+    {
+        return $this->key;
+    }
+
+    /**
+     * True if the token is an empty token. I.e. a no access token.
+     * 
+     * @return bool
+     */
+    function is_empty()
+    {
+        return empty($this->id) || empty($this->user_id) || empty($this->key);
+    }
+
+    /**
+     * Validate token against the database. Returns true if token is valid, 
+     * false otherwise.
+     * 
+     * @return boolean 
+     */
+    function is_valid()
+    {
+        if ($this->is_empty()) {
+            return false;
+        }
+        $key = UserApiKeyManager::get_by_id($this->id);
+        if (empty($key)) {
+            return false;
+        }
+
+        if ($key['api_key'] != $this->key) {
+            return false;
+        }
+
+        if ($key['user_id'] != $this->user_id) {
+            return false;
+        }
+
+        $time = time();
+        $validity_start_date = $key['validity_start_date'] ? strtotime($key['validity_start_date']) : $time;
+        $validity_end_date = $key['validity_end_date'] ? strtotime($key['validity_end_date']) : $time + 100000;
+        return $validity_start_date <= $time && $time <= $validity_end_date;
+    }
+
+    /**
+     * Returns a string representation of the token that can be passed in a url or a form.
+     * The string representation can be parsed by calling AccessToken::parse();
+     * 
+     * @return string
+     */
+    function __toString()
+    {
+        $data[] = $this->id;
+        $data[] = $this->user_id;
+        $data[] = $this->key;
+
+        $result = implode('/', $data);
+        $result = base64_encode($result);
+        return $result;
+    }
+
+}

+ 14 - 10
main/inc/lib/autoload.class.php

@@ -56,6 +56,7 @@ class Autoload
          */
         $result = array();
         $result['AbstractLink'] = '/main/gradebook/lib/be/abstractlink.class.php';
+        $result['AccessToken'] = '/main/inc/lib/access_token.class.php';
         $result['Accessurleditcoursestourl'] = '/main/inc/lib/access_url_edit_courses_to_url_functions.lib.php';
         $result['Accessurleditsessionstourl'] = '/main/inc/lib/access_url_edit_sessions_to_url_functions.lib.php';
         $result['Accessurledituserstourl'] = '/main/inc/lib/access_url_edit_users_to_url_functions.lib.php';
@@ -152,9 +153,6 @@ class Autoload
         $result['FillBlanks'] = '/main/exercice/fill_blanks.class.php';
         $result['FlatViewDataGenerator'] = '/main/gradebook/lib/flatview_data_generator.class.php';
         $result['FlatViewTable'] = '/main/gradebook/lib/fe/flatviewtable.class.php';
-        $result['FormElement'] = '/main/media/lib/form_element.class.php';
-        $result['FormElementTextarea'] = '/main/media/lib/form_element_textarea.class.php';
-        $result['FormRule'] = '/main/media/lib/form_rule.class.php';
         $result['FormValidator'] = '/main/inc/lib/formvalidator/FormValidator.class.php';
         $result['Forum'] = '/main/coursecopy/classes/Forum.class.php';
         $result['ForumCategory'] = '/main/coursecopy/classes/ForumCategory.class.php';
@@ -235,7 +233,7 @@ class Autoload
         $result['HTML_QuickForm_xbutton'] = '/main/inc/lib/pear/HTML/QuickForm/xbutton.php';
         $result['HTML_Table'] = '/main/inc/lib/pear/HTML/Table.php';
         $result['HTML_Table_Storage'] = '/main/inc/lib/pear/HTML/Table/Storage.php';
-        $result['Header'] = '/main/inc/lib/header.class.php';
+        $result['Header'] = '/main/inc/lib/system/web/header.class.php';
         $result['HotSpot'] = '/main/exercice/hotspot.class.php';
         $result['HotSpotDelineation'] = '/main/exercice/hotspot.class.php';
         $result['Html'] = '/main/inc/lib/html.class.php';
@@ -261,9 +259,11 @@ class Autoload
         $result['Login'] = '/main/inc/lib/login.lib.php';
         $result['LoginRedirection'] = '/main/inc/lib/login_redirection.class.php';
         $result['Matching'] = '/main/exercice/matching.class.php';
-        $result['Media'] = '/main/media/model/media.class.php';
-        $result['MediaForm'] = '/main/media/lib/media_form.class.php';
         $result['MessageManager'] = '/main/inc/lib/message.lib.php';
+        $result['Model\Course'] = '/main/inc/lib/course.class.php';
+        $result['Model\Document'] = '/main/inc/lib/document.class.php';
+        $result['Model\ItemProperty'] = '/main/inc/lib/item_property.class.php';
+        $result['Model\StudentPublication'] = '/main/inc/lib/student_publication.class.php';
         $result['MultipleAnswer'] = '/main/exercice/multiple_answer.class.php';
         $result['MultipleAnswerCombination'] = '/main/exercice/multiple_answer_combination.class.php';
         $result['MultipleAnswerCombinationTrueFalse'] = '/main/exercice/multiple_answer_combination_true_false.class.php';
@@ -271,8 +271,6 @@ class Autoload
         $result['MyHorBar'] = '/main/inc/lib/pchart/MyHorBar.class.php';
         $result['MySpace'] = '/main/mySpace/myspace.lib.php';
         $result['Nanogong'] = '/main/inc/lib/nanogong.lib.php';
-        $result['NetworkCredential'] = '/main/inc/lib/system/net/network_credential.class.php';
-        $result['NewMediaForm'] = '/main/media/lib/new_media_form.class.php';
         $result['NotebookManager'] = '/main/inc/lib/notebook.lib.php';
         $result['Notification'] = '/main/inc/lib/notification.lib.php';
         $result['OLE'] = '/main/inc/lib/pear/OLE/OLE.php';
@@ -298,9 +296,11 @@ class Autoload
         $result['PclZip'] = '/main/inc/lib/pclzip/pclzip.lib.php';
         $result['Plugin'] = '/main/inc/lib/plugin.class.php';
         $result['Portfolio'] = '/main/inc/lib/portfolio.class.php';
+        $result['PortfolioBulkAction'] = '/main/inc/lib/portfolio.class.php';
         $result['PortfolioController'] = '/main/inc/lib/portfolio.class.php';
         $result['PortfolioShare'] = '/main/inc/lib/portfolio.class.php';
         $result['Portfolio\Artefact'] = '/main/inc/lib/system/portfolio/artefact.class.php';
+        $result['Portfolio\Download'] = '/main/inc/lib/system/portfolio/download.class.php';
         $result['Portfolio\Mahara'] = '/main/inc/lib/system/portfolio/mahara.class.php';
         $result['Portfolio\Portfolio'] = '/main/inc/lib/system/portfolio/portfolio.class.php';
         $result['Portfolio\User'] = '/main/inc/lib/system/portfolio/user.class.php';
@@ -313,8 +313,8 @@ class Autoload
         $result['Quiz'] = '/main/coursecopy/classes/Quiz.class.php';
         $result['QuizQuestion'] = '/main/coursecopy/classes/QuizQuestion.class.php';
         $result['Redirect'] = '/main/inc/lib/redirect.class.php';
-        $result['Request'] = '/main/inc/lib/request.class.php';
-        $result['RequestServer'] = '/main/inc/lib/request_server.class.php';
+        $result['Request'] = '/main/inc/lib/system/web/request.class.php';
+        $result['RequestServer'] = '/main/inc/lib/system/web/request_server.class.php';
         $result['Resource'] = '/main/coursecopy/classes/Resource.class.php';
         $result['Result'] = '/main/gradebook/lib/be/result.class.php';
         $result['ResultSet'] = '/main/inc/lib/result_set.class.php';
@@ -370,6 +370,7 @@ class Autoload
         $result['SystemAnnouncementManager'] = '/main/inc/lib/system_announcements.lib.php';
         $result['System\Session'] = '/main/inc/lib/system/session.class.php';
         $result['TableSort'] = '/main/inc/lib/table_sort.class.php';
+        $result['Temp'] = '/main/inc/lib/system/io/temp.class.php';
         $result['Template'] = '/main/inc/lib/template.lib.php';
         $result['Text_Diff'] = '/main/inc/lib/pear/Text/Diff.php';
         $result['Text_Diff3'] = '/main/inc/lib/pear/Text/Diff3.php';
@@ -406,6 +407,7 @@ class Autoload
         $result['Uri'] = '/main/inc/lib/uri.class.php';
         $result['UrlManager'] = '/main/inc/lib/urlmanager.lib.php';
         $result['User'] = '/main/auth/shibboleth/app/model/user.class.php';
+        $result['UserApiKeyManager'] = '/main/inc/lib/user_api_key_manager.class.php';
         $result['UserDataGenerator'] = '/main/gradebook/lib/user_data_generator.class.php';
         $result['UserForm'] = '/main/gradebook/lib/fe/userform.class.php';
         $result['UserGroup'] = '/main/inc/lib/usergroup.lib.php';
@@ -417,6 +419,7 @@ class Autoload
         $result['Utf8Encoder'] = '/main/inc/lib/system/text/utf8_encoder.class.php';
         $result['Wiki'] = '/main/coursecopy/classes/wiki.class.php';
         $result['XapianIndexer'] = '/main/inc/lib/search/xapian/XapianIndexer.class.php';
+        $result['Zip'] = '/main/inc/lib/zip.class.php';
         $result['ZombieManager'] = '/main/inc/lib/zombie/zombie_manager.class.php';
         $result['ZombieReport'] = '/main/inc/lib/zombie/zombie_report.class.php';
         $result['_Admin'] = '/main/auth/shibboleth/app/model/scaffold/admin.class.php';
@@ -445,6 +448,7 @@ class Autoload
         $result['db'] = '/main/inc/lib/db.lib.php';
         $result['document_processor'] = '/main/inc/lib/search/tool_processors/document_processor.class.php';
         $result['iDatabase'] = '/main/install/i_database.class.php';
+        $result['langstats'] = '/main/cron/lang/langstats.class.php';
         $result['learnpath'] = '/main/newscorm/learnpath.class.php';
         $result['learnpathItem'] = '/main/newscorm/learnpathItem.class.php';
         $result['learnpathList'] = '/main/newscorm/learnpathList.class.php';

+ 36 - 3
main/inc/lib/chamilo.class.php

@@ -42,7 +42,7 @@ class Chamilo
     {
         return ChamiloSession::instance();
     }
-    
+
     /**
      *
      * @return CurrentUser
@@ -93,10 +93,43 @@ class Chamilo
         return api_get_path(SYS_COURSE_PATH);
     }
 
-    public static function temp($ext = '')
+    /**
+     * Returns a temporary file - one that is automatically deleted at the end
+     * of the script.
+     * 
+     * @param string $ext
+     * @return Temp 
+     */
+    public static function temp_file($ext = '')
     {
         $ext = $ext ? '.' . $ext : '';
-        return api_get_path(SYS_ARCHIVE_PATH) . uniqid() . $ext;
+        Temp::set_temp_root(api_get_path(SYS_ARCHIVE_PATH) . 'temp');
+        $path = Temp::get_temporary_name() . $ext;
+        return Temp::create($path);
+    }
+
+    /**
+     * Returns a temporary directory - one that is automatically deleted at the end
+     * of the script.
+     * 
+     * @param string $ext
+     * @return Temp 
+     */
+    public static function temp_dir()
+    {
+        $ext = $ext ? '.' . $ext : '';
+        Temp::set_temp_root(api_get_path(SYS_ARCHIVE_PATH) . 'temp');
+        $path = Temp::get_temporary_name() . $ext;
+        return Temp::dir($path);
+    }
+
+    /**
+     *
+     * @return Zip
+     */
+    public static function temp_zip()
+    {
+        return Zip::create(self::temp_file('zip'));
     }
 
     public static function path($path = '')

+ 145 - 0
main/inc/lib/course.class.php

@@ -0,0 +1,145 @@
+<?php
+
+namespace Model;
+
+use Database;
+use ResultSet;
+
+/**
+ * Description of course
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class Course
+{
+
+    /**
+     *
+     * @param string $where
+     * @return \ResultSet 
+     */
+    public static function query($where)
+    {
+        $table = Database::get_main_table(TABLE_MAIN_COURSE);
+        $sql = "SELECT * FROM $table ";
+        $sql .= $where ? "WHERE $where" : '';
+        $result = new ResultSet($sql);
+        return $result->return_type(__CLASS__);
+    }
+
+    /**
+     *
+     * @param string $code
+     * @return \Model\Course|null
+     */
+    public static function get_by_code($code)
+    {
+        $current = self::current();
+        if ($current && $current->get_code() == $code) {
+            return $current;
+        }
+        return self::query("code = '$code'")->first();
+    }
+
+    /**
+     *
+     * @param int $id
+     * @return \Model\Course|null
+     */
+    public static function get_by_id($id)
+    {
+        $id = (int) $id;
+        $current = self::current();
+        if ($current && $current->get_id() == $id) {
+            return $current;
+        }
+        return self::query("id = $id")->first();
+    }
+
+    /**
+     * 
+     * @return \Model\Course|null
+     */
+    public static function current()
+    {
+        global $_course;
+        /**
+         * Note that $_course = -1 when not set.
+         */
+        if (empty($_course) || !is_array($_course)) {
+            return null;
+        }
+
+        static $result = null;
+        if (empty($result)) {
+            $id = $_course['real_id'];
+            $result = self::query("id = $id")->first();
+        }
+        return $result;
+    }
+
+    protected $id = 0;
+    protected $code = 0;
+    protected $directory = '';
+    protected $show_score = '';
+
+    public function __construct($data)
+    {
+        $data = (object) $data;
+        $this->id = $data->id;
+        $this->code = $data->code;
+        $this->directory = $data->directory;
+        $this->show_score = $data->show_score;
+    }
+
+    public function get_id()
+    {
+        return $this->id;
+    }
+
+    public function set_id($value)
+    {
+        $this->id = (int) $value;
+    }
+
+    public function get_code()
+    {
+        return $this->code;
+    }
+
+    public function set_code($value)
+    {
+        $this->code = (int) $value;
+    }
+
+    public function get_directory()
+    {
+        return $this->directory;
+    }
+
+    public function set_directory($value)
+    {
+        $this->directory = (int) $value;
+    }
+
+    public function get_show_score()
+    {
+        return $this->show_score;
+    }
+
+    public function set_show_score($value)
+    {
+        $this->show_score = (int) $value;
+    }
+
+    public function get_path()
+    {
+        $dir = $this->directory;
+        if (empty($dir)) {
+            return '';
+        }
+        return api_get_path(SYS_COURSE_PATH) . $dir . '/';
+    }
+
+}

+ 302 - 0
main/inc/lib/document.class.php

@@ -0,0 +1,302 @@
+<?php
+
+namespace Model;
+
+use Database;
+use ResultSet;
+
+/**
+ * Represent a database "document" object - i.e. a file or a folder. 
+ * 
+ * Note: 
+ * 
+ * Each database column is mapped to a property.
+ * 
+ * The item_property table is available through its own property but is loaded
+ * alongside document data.
+ * 
+ * Some db query functions exists in this class and would need to be adapted
+ * to Symphony once it is moved to production. Yet the object structure should
+ * stay. 
+ * 
+ * @see \Model\ItemProperty
+ * @see table c_document
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class Document
+{
+
+    /**
+     *
+     * @param string $where
+     * @return \ResultSet 
+     */
+    public static function query($where)
+    {
+        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
+        $table_document = Database::get_course_table(TABLE_DOCUMENT);
+        $tool = TOOL_DOCUMENT;
+
+        $sql = "SELECT doc.*,         
+                       prop.id AS property_id, 
+                       prop.tool, 
+                       prop.insert_user_id,
+                       prop.insert_date,
+                       prop.lastedit_date,
+                       prop.ref,
+                       prop.lastedit_type,
+                       prop.lastedit_user_id,
+                       prop.to_group_id, 
+                       prop.to_user_id, 
+                       prop.visibility, 
+                       prop.start_visible, 
+                       prop.end_visible, 
+                       prop.id_session
+                FROM 
+                    $table_document AS doc, 
+                    $table_item_property AS prop
+                WHERE 
+                    (doc.id = prop.ref AND
+                     doc.c_id = prop.c_id AND
+                     prop.tool = '$tool')";
+
+        $sql .= $where ? "AND ($where)" : '';
+        $result = new ResultSet($sql);
+        return $result->return_type(__CLASS__);
+    }
+
+    /**
+     *
+     * @param int|Course $c_id
+     * @param int $id
+     * @return \Model\Document 
+     */
+    public static function get_by_id($c_id, $id)
+    {
+        $c_id = is_object($c_id) ? $c_id->get_id() : (int) $c_id;
+        return self::query("doc.c_id = $c_id AND doc.id = $id")->first();
+    }
+
+    /**
+     *
+     * @param int|Course $c_id
+     * @param int $id
+     * @return \Model\Document 
+     */
+    public static function get_by_path($c_id, $path)
+    {
+        $c_id = is_object($c_id) ? $c_id->get_id() : (int) $c_id;
+        return self::query("doc.c_id = $c_id AND doc.path = '$path'")->first();
+    }
+
+    protected $c_id = 0;
+    protected $id = 0;
+    protected $path = '';
+    protected $comment = '';
+    protected $title = '';
+    protected $filetype = '';
+    protected $size = 0;
+    protected $readonly = false;
+    protected $session_id = 0;
+    protected $course = null;
+    protected $item_property = null;
+
+    public function __construct($data)
+    {
+        $data = (object) $data;
+        $this->c_id = (int) $data->c_id;
+        $this->id = (int) $data->id;
+        $this->path = $data->path;
+        $this->comment = $data->comment;
+        $this->title = $data->title;
+        $this->filetype = $data->filetype;
+        $this->size = (int) $data->size;
+        $this->readonly = (bool) $data->readonly;
+        $this->session_id = (int) $data->session_id;
+
+        $this->course = null;
+
+        if (isset($data->property_id)) {
+            $property = (array) $data;
+            $property = (object) $property;
+            $property->id = $property->property_id;
+            $this->item_property = ItemProperty::create($property);
+        } else {
+            $this->item_property = null;
+        }
+    }
+
+    public function get_c_id()
+    {
+        return $this->c_id;
+    }
+
+    public function set_c_id($value)
+    {
+        $this->c_id = (int) $value;
+        $this->course = null;
+        $this->item_property = null;
+    }
+
+    public function get_id()
+    {
+        return $this->id;
+    }
+
+    public function set_id($value)
+    {
+        $this->id = (int) $value;
+        $this->item_property = null;
+    }
+
+    public function get_path()
+    {
+        return $this->path;
+    }
+
+    public function set_path($value)
+    {
+        $this->path = $value;
+    }
+
+    public function get_comment()
+    {
+        return $this->comment;
+    }
+
+    public function set_comment($value)
+    {
+        $this->comment = $value;
+    }
+
+    public function get_title()
+    {
+        return $this->title;
+    }
+
+    public function set_title($value)
+    {
+        $this->title = $value;
+    }
+
+    public function get_filetype()
+    {
+        return $this->filetype;
+    }
+
+    public function set_filetype($value)
+    {
+        $this->filetype = $value;
+    }
+
+    public function get_size()
+    {
+        return $this->size;
+    }
+
+    public function set_size($value)
+    {
+        $this->size = (int) $value;
+    }
+
+    public function get_readonly()
+    {
+        return $this->readonly;
+    }
+
+    public function set_readonly($value)
+    {
+        $this->readonly = (bool) $value;
+    }
+
+    public function get_session_id()
+    {
+        return $this->session_id;
+    }
+
+    public function set_session_id($value)
+    {
+        $this->session_id = (int) $value;
+    }
+
+    public function is_folder()
+    {
+        return $this->filetype == 'folder';
+    }
+
+    public function is_file()
+    {
+        return $this->filetype == 'file';
+    }
+
+    public function is_visible()
+    {
+        $this->get_item_property()->get_visibility() == 1;
+    }
+
+    public function is_accessible()
+    {
+        return api_is_allowed_to_edit() || $this->is_visible();
+    }
+
+    /**
+     *
+     * @return \Model\Course
+     */
+    public function get_course()
+    {
+        if ($this->course && $this->course->get_id() == $this->c_id) {
+            return $this->course;
+        }
+
+        $this->course = Course::get_by_id($this->c_id);
+        return $this->course;
+    }
+
+    /**
+     *
+     * @return \Model\ItemProperty
+     */
+    public function get_item_property()
+    {
+        if ($this->item_property && $this->item_property->get_c_id() == $this->c_id && $this->item_property->get_ref() == $this->id) {
+            return $this->item_property;
+        }
+
+        $this->item_property = ItemProperty::get_by_ref($this->id, TOOL_DOCUMENT);
+        return $this->item_property;
+    }
+
+    public function get_absolute_path()
+    {
+        $course = $this->get_course();
+        return $course->get_path() . 'document' . $this->path;
+    }
+
+    public function __toString()
+    {
+        return $this->get_absolute_path();
+    }
+
+    /**
+     *
+     * @param bool $all
+     * @return ResultSet|array
+     */
+    public function get_children($all = false)
+    {
+        if (!$this->is_folder()) {
+            return array();
+        }
+        $path = $this->path;
+        $c_id = $this->c_id;
+        if ($this->all) {
+            $where = "doc.c_id = $c_id AND doc.path LIKE '$path/%'";
+        } else {
+            $where = "doc.c_id = $c_id AND doc.path LIKE '$path/%' AND doc.path NOT LIKE '$path/%/%'";
+        }
+        return self::query($where);
+    }
+
+}

+ 1 - 1
main/inc/lib/export.lib.inc.php

@@ -57,7 +57,7 @@ class Export {
         if(empty($data)){
             return false;
         }
-        $path = Chamilo::temp();
+        $path = Chamilo::temp_file();
         $converter = new Utf8Encoder(null, true);
         $file = FileWriter::create($path, $converter);
         $file = CsvWriter::create($file);

+ 0 - 32
main/inc/lib/header.class.php

@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Header utility functions.
- *
- * @license see /license.txt
- * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
- */
-class Header
-{
-
-    public static function content_type($mime_type, $charset = '')
-    {
-        if (empty($mime_type))
-        {
-            return;
-        }
-        $type = $charset ?  "$mime_type;charset=$charset" : $mime_type;
-        header('Content-type: ' . $type);
-    }
-
-    public static function content_type_xml()
-    {
-        header('Content-type: text/xml');
-    }
-
-    public static function content_type_javascript()
-    {
-        header('Content-type: application/javascript');
-    }
-
-}

+ 251 - 0
main/inc/lib/item_property.class.php

@@ -0,0 +1,251 @@
+<?php
+
+namespace Model;
+use ResultSet;
+
+use Database;
+
+/**
+ * Represent a database "item_property" object - i.e. common properties for tool
+ * objects: created date, modified date, etc. 
+ * 
+ * Note: 
+ * 
+ * Each database column is mapped to a property.
+ * 
+ * 
+ * Some db query functions exists in this class and would need to be adapted
+ * to Sympony once it is moved to production. Yet the object structure should
+ * stay. 
+ * 
+ * @see \Model\ItemProperty
+ * @see table c_item_property
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class ItemProperty
+{
+
+    /**
+     *
+     * @param string $where
+     * @return \ResultSet 
+     */
+    public static function query($where)
+    {
+        $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
+        $sql = "SELECT * FROM $table ";
+        $sql .= $where ? "WHERE $where" : '';
+        $result = new ResultSet($sql);
+        return $result->return_type(__CLASS__);
+    }
+
+    /**
+     *
+     * @param id $ref
+     * @param string $tool
+     * @return \Model\ItemProperty 
+     */
+    public static function get_by_ref($ref, $tool)
+    {
+        return self::query("ref=$ref AND tool = '$tool'")->first();
+    }
+
+    /**
+     *
+     * @param array|object $data
+     * @return \Model\ItemProperty
+     */
+    static function create($data)
+    {
+        return new self($data);
+    }
+
+    protected $c_id = 0;
+    protected $id = 0;
+    protected $tool = '';
+    protected $insert_user_id = 0;
+    protected $insert_date = 0;
+    protected $lastedit_date = 0;
+    protected $ref = '';
+    protected $lastedit_type = '';
+    protected $lastedit_user_id = 0;
+    protected $to_group_id = null;
+    protected $to_user_id = null;
+    protected $visibility = 1;
+    protected $start_visible = 0;
+    protected $end_visible = 0;
+    protected $id_session = 0;
+
+    public function __construct($data)
+    {
+        $data = (object) $data;
+        $this->c_id = $data->c_id;
+        $this->id = $data->id;
+        $this->tool = $data->tool;
+        $this->insert_user_id = $data->insert_user_id;
+        $this->insert_date = $data->insert_date;
+        $this->lastedit_date = $data->lastedit_date;
+        $this->lastedit_date = $data->lastedit_date;
+        $this->ref = $data->ref;
+        $this->lastedit_type = $data->lastedit_type;
+        $this->lastedit_user_id = $data->lastedit_user_id;
+        $this->to_group_id = $data->to_group_id;
+        $this->to_user_id = $data->to_user_id;
+        $this->visibility = $data->visibility;
+        $this->start_visible = $data->start_visible;
+        $this->end_visible = $data->end_visible;
+        $this->id_session = $data->id_session;
+    }
+
+    public function get_c_id()
+    {
+        return $this->c_id;
+    }
+
+    public function set_c_id($value)
+    {
+        $this->c_id = (int) $value;
+    }
+
+    public function get_id()
+    {
+        return $this->id;
+    }
+
+    public function set_id($value)
+    {
+        $this->id = (int) $value;
+    }
+
+    public function get_tool()
+    {
+        return $this->tool;
+    }
+
+    public function set_tool($value)
+    {
+        $this->tool = $value;
+    }
+
+    public function get_insert_user_id()
+    {
+        return $this->insert_user_id;
+    }
+
+    public function set_insert_user_id($value)
+    {
+        $this->insert_user_id = $value;
+    }
+
+    public function get_insert_date()
+    {
+        return $this->insert_date;
+    }
+
+    public function set_insert_date($value)
+    {
+        $this->insert_date = $value;
+    }
+
+    public function get_lastedit_date()
+    {
+        return $this->lastedit_date;
+    }
+
+    public function set_lastedit_date($value)
+    {
+        $this->lastedit_date = $value;
+    }
+
+    public function get_ref()
+    {
+        return $this->ref;
+    }
+
+    public function set_ref($value)
+    {
+        $this->ref = $value;
+    }
+
+    public function get_lastedit_type()
+    {
+        return $this->lastedit_type;
+    }
+
+    public function set_lastedit_type($value)
+    {
+        $this->lastedit_type = $value;
+    }
+
+    public function get_lastedit_user_id()
+    {
+        return $this->lastedit_user_id;
+    }
+
+    public function set_lastedit_user_id($value)
+    {
+        $this->lastedit_user_id = $value;
+    }
+
+    public function get_to_group_id()
+    {
+        return $this->to_group_id;
+    }
+
+    public function set_to_group_id($value)
+    {
+        $this->to_group_id = $value;
+    }
+
+    public function get_to_user_id()
+    {
+        return $this->to_user_id;
+    }
+
+    public function set_to_user_id($value)
+    {
+        $this->to_user_id = $value;
+    }
+
+    public function get_visibility()
+    {
+        return $this->visibility;
+    }
+
+    public function set_visibility($value)
+    {
+        $this->visibility = $value;
+    }
+
+    public function get_start_visible()
+    {
+        return $this->start_visible;
+    }
+
+    public function set_start_visible($value)
+    {
+        $this->start_visible = $value;
+    }
+
+    public function get_end_visible()
+    {
+        return $this->end_visible;
+    }
+
+    public function set_end_visible($value)
+    {
+        $this->end_visible = $value;
+    }
+
+    public function get_id_session()
+    {
+        return $this->id_session;
+    }
+
+    public function set_id_session($value)
+    {
+        $this->id_session = $value;
+    }
+
+}

+ 2 - 2
main/inc/lib/javascript/jqgrid/css/ui.jqgrid.css

@@ -29,11 +29,11 @@
 tr.ui-search-toolbar input {margin: 1px 0px 0px 0px}
 tr.ui-search-toolbar select {margin: 1px 0px 0px 0px}
 /* body */ 
-.ui-jqgrid .ui-jqgrid-bdiv {position: relative; margin: 0em; padding:0; overflow: auto; text-align:left;}
+.ui-jqgrid .ui-jqgrid-bdiv {position: relative; margin: 0em; padding:0; overflow: visible; text-align:left;}
 .ui-jqgrid .ui-jqgrid-btable {table-layout:fixed; margin:0em; outline-style: none; }
 .ui-jqgrid tr.jqgrow { outline-style: none; }
 .ui-jqgrid tr.jqgroup { outline-style: none; }
-.ui-jqgrid tr.jqgrow td {font-weight: normal; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
+.ui-jqgrid tr.jqgrow td {font-weight: normal; overflow: visible; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
 .ui-jqgrid tr.jqgfirstrow td {padding: 0 2px 0 2px;border-right-width: 1px; border-right-style: solid;}
 .ui-jqgrid tr.jqgroup td {font-weight: normal; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
 .ui-jqgrid tr.jqfoot td {font-weight: bold; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}

+ 34 - 32
main/inc/lib/login.lib.php

@@ -2,7 +2,6 @@
 
 use \ChamiloSession as Session;
 
-
 /* For licensing terms, see /license.txt */
 /**
  * 	Code library for login process
@@ -262,20 +261,22 @@ class Login
                     $is_allowedCreateCourse = (bool) (($uData ['status'] == 1) or (api_get_setting('drhCourseManagerRights') and $uData['status'] == 4));
                     ConditionalLogin::check_conditions($uData);
 
-                    Session::write('_user',$_user);
+                    Session::write('_user', $_user);
                     UserManager::update_extra_field_value($_user['user_id'], 'already_logged_in', 'true');
-                    Session::write('is_platformAdmin',$is_platformAdmin);
-                    Session::write('is_allowedCreateCourse',$is_allowedCreateCourse);
-
-                    // If request_uri is setted we have to go further to have course permissions
-                    if (empty($_SESSION['request_uri']) || !isset($_SESSION['request_uri'])) {
-                        if (isset($_SESSION['noredirection'])) {
-                            //If we just want to reset info without redirecting user
-                            unset($_SESSION['noredirection']);
-                        } else {
-                            LoginRedirection::redirect();
-                        }
-                    }
+                    Session::write('is_platformAdmin', $is_platformAdmin);
+                    Session::write('is_allowedCreateCourse', $is_allowedCreateCourse);
+
+//                  
+//                    
+//                    // If request_uri is setted we have to go further to have course permissions
+//                    if (empty($_SESSION['request_uri']) || !isset($_SESSION['request_uri'])) {
+//                        if (isset($_SESSION['noredirection'])) {
+//                            //If we just want to reset info without redirecting user
+//                            unset($_SESSION['noredirection']);
+//                        } else {
+//                            LoginRedirection::redirect();
+//                        }
+//                    }
                 } else {
                     header('location:' . api_get_path(WEB_PATH));
                     //exit("WARNING UNDEFINED UID !! ");
@@ -285,8 +286,8 @@ class Login
                 Session::erase('_uid');
             }
 
-            Session::write('is_platformAdmin',$is_platformAdmin);
-            Session::write('is_allowedCreateCourse',$is_allowedCreateCourse);
+            Session::write('is_platformAdmin', $is_platformAdmin);
+            Session::write('is_allowedCreateCourse', $is_allowedCreateCourse);
         } else { // continue with the previous values
             $_user = $_SESSION['_user'];
             $is_platformAdmin = $_SESSION['is_platformAdmin'];
@@ -315,6 +316,7 @@ class Login
      */
     static function init_course($course_id, $reset)
     {
+        global $_configuration;
         global $is_platformAdmin;
         global $is_allowedCreateCourse;
         global $_user;
@@ -331,7 +333,7 @@ class Login
         global $is_sessionAdmin;
         global $is_allowed_in_course;
 
-        if (isset($reset) && $reset) {
+        if ($reset) {
             // Course session data refresh requested or empty data        
             if ($course_id) {
                 $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
@@ -373,11 +375,11 @@ class Login
                     $_course['activate_legal'] = $course_data['activate_legal'];
                     $_course['show_score'] = $course_data['show_score']; //used in the work tool
 
-                    Session::write('_cid',$_cid);
-                    Session::write('_course',$_course);
+                    Session::write('_cid', $_cid);
+                    Session::write('_course', $_course);
 
                     //@TODO real_cid should be cid, for working with numeric course id
-                    Session::write('_real_cid',$_real_cid);
+                    Session::write('_real_cid', $_real_cid);
 
                     // if a session id has been given in url, we store the session
                     if (api_get_setting('use_session_mode') == 'true') {
@@ -520,7 +522,7 @@ class Login
         //Course - User permissions
         $is_sessionAdmin = false;
 
-        if ((isset($uidReset) && $uidReset) || (isset($cidReset) && $cidReset)) {
+        if ($reset) {
 
             if (isset($user_id) && $user_id && isset($_cid) && $_cid) {
 
@@ -528,7 +530,7 @@ class Login
                 $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
                 $sql = "SELECT * FROM $course_user_table
                    WHERE user_id  = '" . $user_id . "' AND relation_type <> " . COURSE_RELATION_TYPE_RRHH . "
-                   AND course_code = '$cidReq'";
+                   AND course_code = '$course_id'";
                 $result = Database::query($sql);
 
                 $cuData = null;
@@ -548,7 +550,7 @@ class Login
                         }
                     }
                     $_courseUser['role'] = $cuData['role'];
-                    Session::write('_courseUser',$_courseUser);
+                    Session::write('_courseUser', $_courseUser);
                 }
 
                 //We are in a session course? Check session permissions
@@ -610,14 +612,14 @@ class Login
                                         } else {
                                             $is_courseAdmin = false;
                                         }
-                                        Session::write('_courseUser',$_courseUser);
+                                        Session::write('_courseUser', $_courseUser);
                                         break;
                                     case '0': //student
                                         $is_courseMember = true;
                                         $is_courseTutor = false;
                                         $is_courseAdmin = false;
                                         $is_sessionAdmin = false;
-                                        Session::write('_courseUser',$_courseUser);
+                                        Session::write('_courseUser', $_courseUser);
                                         break;
                                     default:
                                         //unregister user
@@ -701,13 +703,13 @@ class Login
             }
 
             // save the states
-            Session::write('is_courseAdmin',$is_courseAdmin);
-            Session::write('is_courseMember',$is_courseMember);
-            Session::write('is_courseTutor',$is_courseTutor);
-            Session::write('is_courseCoach',$is_courseCoach);
-            Session::write('is_allowed_in_course',$is_allowed_in_course);
+            Session::write('is_courseAdmin', $is_courseAdmin);
+            Session::write('is_courseMember', $is_courseMember);
+            Session::write('is_courseTutor', $is_courseTutor);
+            Session::write('is_courseCoach', $is_courseCoach);
+            Session::write('is_allowed_in_course', $is_allowed_in_course);
 
-            Session::write('is_sessionAdmin',$is_sessionAdmin);
+            Session::write('is_sessionAdmin', $is_sessionAdmin);
         } else { // continue with the previous values
             if (isset($_SESSION ['_courseUser'])) {
                 $_courseUser = $_SESSION ['_courseUser'];
@@ -744,7 +746,7 @@ class Login
                 if (Database::num_rows($result) > 0) { // This group has recorded status related to this course
                     $gpData = Database::fetch_array($result);
                     $_gid = $gpData ['id'];
-                    Session::write('_gid',$_gid);
+                    Session::write('_gid', $_gid);
                 } else {
                     Session::erase('_gid');
                 }

+ 2 - 1
main/inc/lib/main_api.lib.php

@@ -2680,7 +2680,8 @@ function api_not_found($print_headers = false) {
  * @version dokeos 1.8, August 2006
  */
 function api_not_allowed($print_headers = false, $message = null) {
-	
+    Header::response_code(403);
+    
     $home_url   = api_get_path(WEB_PATH);
     $user       = api_get_user_id(); //0 if not defined
     $course     = api_get_course_id();

+ 642 - 0
main/inc/lib/portfolio.class.php

@@ -0,0 +1,642 @@
+<?php
+
+/*
+ * This file contains several classes related to portfolios management to avoid
+ * having too much files under the lib/. 
+ * 
+ * Once external libraries are moved to their own directory it would be worth
+ * moving them to their own files under a common portfolio directory.
+ */
+
+use Model\Document;
+use Model\Course;
+
+/**
+ * A portfolio is used to present content to other people. In most cases it is
+ * an external application. 
+ * 
+ * From the application point of view it is an end point to which the user can send
+ * content.
+ * 
+ * Available portfolios are configured in /main/inc/config/portfolio.conf.php
+ * 
+ * The Portfolio class serves as an entry point to other portfolio components:
+ * 
+ *      - portfolio controller
+ *      - portfolio share button
+ *      - portfolio action
+ * 
+ * Note:
+ * 
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class Portfolio extends Portfolio\Portfolio
+{
+
+    /**
+     * Returns all portfolios available
+     * 
+     * @return array
+     */
+    public static function all()
+    {
+        $conf = Chamilo::path('/main/inc/conf/portfolio.conf.php');
+        if (!is_readable($conf)) {
+            return array();
+        }
+        include $conf;
+        return isset($portfolios) ? $portfolios : array();
+    }
+
+    /**
+     * Returns a portfolio from its name.
+     * 
+     * @param string $name
+     * @return Portfolio\Portfolio
+     */
+    public static function get($name)
+    {
+        $items = self::all();
+        foreach ($items as $item) {
+            if ($item->get_name() == $name) {
+                return $item;
+            }
+        }
+        return Portfolio\Portfolio::none();
+    }
+
+    /**
+     * True if portfolios are enabled. False otherwise.
+     * 
+     * @return boolean 
+     */
+    public static function is_enabled()
+    {
+        if (api_is_anonymous()) {
+            return false;
+        }
+        $user_id = api_get_user_id();
+        if (empty($user_id)) {
+            return false;
+        }
+        $portfolios = self::all();
+        if (count($portfolios) == 0) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * The controller for portfolio.
+     * 
+     * @return \PortfolioController 
+     */
+    public static function controller()
+    {
+        return PortfolioController::instance();
+    }
+
+    /**
+     * Returns a share component/button.
+     * 
+     * @param string $tool
+     * @param int $id
+     * @param array $attributes
+     * @return \PortfolioShare 
+     */
+    public static function share($tool, $id, $attributes = array())
+    {
+        return PortfolioShare::factory($tool, $id, $attributes);
+    }
+
+    /**
+     * Returns the list of actions.
+     * 
+     * @return array 
+     */
+    public static function actions()
+    {
+        return PortfolioController::actions();
+    }
+
+    /**
+     * Returns a temporary url to download files and/or folders.
+     * 
+     * @param string|array $ids
+     * @return string 
+     */
+    public static function download_url($ids, $tool)
+    {
+        $ids = is_array($ids) ? implode(',', $ids) : $ids;
+
+        $params = Uri::course_params();
+        $params['id'] = $ids;
+        $params[KeyAuth::PARAM_ACCESS_TOKEN] = KeyAuth::create_temp_token();
+        $result = Uri::url("/main/$tool/file.php", $params, false);
+        return $result;
+    }
+
+}
+
+/**
+ * The portfolio controller. Responsible to dispatch/process portfolio actions.
+ * 
+ * Usage:
+ * 
+ *      if(Porfolio::contoller()->accept()){
+ *          Portfolio::controller()->run();
+ *      }
+ * 
+ * 
+ */
+class PortfolioController
+{
+
+    const PARAM_ACTION = 'action';
+    const PARAM_ID = 'id';
+    const PARAM_TOOL = 'tool';
+    const PARAM_PORTFOLIO = 'portfolio';
+    const PARAM_CONTROLLER = 'controller';
+    const PARAM_SECURITY_TOKEN = 'sec_token';
+    const ACTION_SHARE = 'share';
+    const NAME = 'portfolio';
+
+    /**
+     *
+     * @return \PortfolioController 
+     */
+    static function instance()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = new self();
+        }
+        return $result;
+    }
+
+    protected $message = '';
+
+    protected function __construct()
+    {
+        
+    }
+
+    public static function portfolios()
+    {
+        return Portfolio::all();
+    }
+
+    /**
+     * List of actions for the SortableTable. 
+     * 
+     * @return array 
+     */
+    public static function actions()
+    {
+        static $result = null;
+        if (!is_null($result)) {
+            return $result;
+        }
+
+        $items = self::portfolios();
+        if (empty($items)) {
+            $result = array();
+            return $result;
+        }
+
+        $result = array();
+        foreach ($items as $item) {
+            $action = PortfolioBulkAction::create($item);
+            $result[] = $action;
+        }
+        return $result;
+    }
+
+    /**
+     * Returns true if the controller accept to process the current request. 
+     * Returns false otherwise.
+     * 
+     * @return boolean 
+     */
+    function accept()
+    {
+        if (!Portfolio::is_enabled()) {
+            return false;
+        }
+        $actions = self::actions();
+        foreach ($actions as $action) {
+            if ($action->accept()) {
+                return true;
+            }
+        }
+
+        if ($this->get_controller() != self::NAME) {
+            return false;
+        }
+        if (!Security::check_token('get')) {
+            return false;
+        }
+        $id = $this->get_id();
+        if (empty($id)) {
+            return false;
+        }
+
+        return $this->get_action() == self::ACTION_SHARE;
+    }
+
+    /**
+     * Returns the value of the current controller request parameters. That is
+     * the name of the controller which shall handle the current request. 
+     * 
+     * @return string
+     */
+    function get_controller()
+    {
+        return Request::get(self::PARAM_CONTROLLER);
+    }
+
+    /**
+     * Returns the value of the action parameter. That is which action shall be
+     * performed. That is share to send an object to a portfolio.
+     * 
+     * @return string 
+     */
+    function get_action()
+    {
+        $result = Request::get(self::PARAM_ACTION);
+        return ($result == self::ACTION_SHARE) ? self::ACTION_SHARE : '';
+    }
+
+    /**
+     * Returns the value of the id parameter: id of object to send.
+     * 
+     * @return int
+     */
+    function get_id()
+    {
+        return (int) Request::get(self::PARAM_ID);
+    }
+
+    /**
+     * The course code (id) to which the object belongs.
+     * 
+     * @return string
+     */
+    function course_code()
+    {
+        return Chamilo::session()->course()->code();
+    }
+
+    /**
+     * The name of the porfolio where to send.
+     * 
+     * @return type 
+     */
+    function get_portfolio()
+    {
+        return Request::get(self::PARAM_PORTFOLIO);
+    }
+
+    /**
+     * Name of the tool: document, work, etc. Defaults to current_course_tool.
+     * 
+     * @global string $current_course_tool
+     * @return string 
+     */
+    function get_tool()
+    {
+        global $current_course_tool;
+        return Request::get(self::PARAM_TOOL, $current_course_tool);
+    }
+
+    /**
+     * Returns the end user message after running the controller..
+     * @return string
+     */
+    function message()
+    {
+        return $this->message;
+    }
+
+    /**
+     * Execute the controller action as required. If a registered action accept 
+     * the current request the controller calls it.
+     * 
+     * If not action is accept the current request and current action is "share"
+     * the controller execute the "send to portfolio" action
+     * 
+     * @return PortfolioController
+     */
+    function run()
+    {
+        if (!$this->accept()) {
+            return $this;
+        }
+
+        $actions = self::actions();
+        foreach ($actions as $action) {
+            if ($action->accept()) {
+                return $action->run();
+            }
+        }
+
+        $action = $this->get_action();
+        if ($action == self::ACTION_SHARE) {
+
+            $user = new \Portfolio\User();
+            $user->email = Chamilo::user()->email();
+
+            $tool = $this->get_tool();
+            $id = $this->get_id();
+            $url = Portfolio::download_url($id, $tool);
+
+            $artefact = new Portfolio\Artefact($url);
+
+            $name = $this->get_portfolio();
+            $result = Portfolio::get($name)->send($user, $artefact);
+            if ($result) {
+                $this->message = Display::return_message(get_lang('SentSuccessful'), 'normal');
+            } else {
+                $this->message = Display::return_message(get_lang('SentFailed'), 'error');
+            }
+            return $this;
+        } else {
+            $this->message = '';
+        }
+        return $this;
+    }
+
+}
+
+/**
+ * This component is used to display a "send to portfolio" button for a specific
+ * object. 
+ * 
+ * Note that the component implement the __toString() magic method and can be 
+ * therefore used in situation where a string is expected: for ex echo $button.
+ * 
+ * Usage
+ * 
+ *      $button = Portfolio::share(...);
+ *      echo $button;
+ * 
+ */
+class PortfolioShare
+{
+
+    /**
+     * Create a "send to portfolio" button
+     * 
+     * @param string $tool          The name of the tool: document, work.
+     * @param int $c_id             The id of the course
+     * @param int $id               The id of the object 
+     * @param array $attributes     Html attributes
+     * @return \PortfolioShare  
+     */
+    static function factory($tool, $id, $attributes = array())
+    {
+        $result = new self($tool, $id, $attributes);
+        return $result;
+    }
+
+    /**
+     * Returns the current secuirty token. Used to avoid see surfing attacks.
+     * 
+     * @return type 
+     */
+    static function security_token()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = Security::get_token();
+        }
+        return $result;
+    }
+
+    protected $id = 0;
+    protected $attributes = array();
+    protected $tool = '';
+
+    function __construct($tool, $id, $attributes = array())
+    {
+        $this->tool = $tool;
+        $this->id = (int) $id;
+        $this->attributes = $attributes;
+    }
+
+    /**
+     * Object id to send
+     * @return int
+     */
+    function get_id()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Object id to send
+     * @return int
+     */
+    function get_c_id()
+    {
+        return $this->c_id;
+    }
+
+    /**
+     * Html attributes. 
+     * 
+     * @return array
+     */
+    function get_attributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Name of the tool. I.e. the type of the id parameter. Can be document, work.
+     * 
+     * @return string
+     */
+    function get_tool()
+    {
+        return $this->tool;
+    }
+
+    /**
+     * Display the component.
+     * 
+     * @return string 
+     */
+    function display()
+    {
+        if (!Portfolio::is_enabled()) {
+            return '';
+        }
+        $id = $this->id;
+        $tool = $this->tool;
+
+        $attributes = $this->attributes;
+        $attributes['z-index'] = 100000;
+        $s = ' ';
+        foreach ($attributes as $key => $value) {
+            $s .= $key . '="' . $value . '" ';
+        }
+
+        $result = array();
+        $result[] = '<span ' . $s . ' >';
+        $result[] = '<span class="dropdown" >';
+        $result[] = '<a href="#" data-toggle="dropdown" class="dropdown-toggle">';
+        $result[] = Display::return_icon('document_send.png', get_lang('Send'), array(), ICON_SIZE_SMALL) . '<b class="caret"></b>';
+        $result[] = '</a>';
+        $result[] = '<ul class="dropdown-menu">';
+
+        $portfolios = Portfolio::all();
+        foreach ($portfolios as $portfolio) {
+            $parameters = Uri::course_params();
+            $parameters[PortfolioController::PARAM_ACTION] = PortfolioController::ACTION_SHARE;
+            $parameters[PortfolioController::PARAM_CONTROLLER] = PortfolioController::NAME;
+            $parameters[PortfolioController::PARAM_PORTFOLIO] = $portfolio->get_name();
+            $parameters[PortfolioController::PARAM_SECURITY_TOKEN] = self::security_token();
+            $parameters[PortfolioController::PARAM_TOOL] = $this->get_tool();
+            $parameters[PortfolioController::PARAM_ID] = $id;
+            $parameters[PortfolioController::PARAM_TOOL] = $tool;
+            $url = Chamilo::url('/main/portfolio/share.php', $parameters);
+            $result[] = '<li>';
+            $result[] = '<a href="' . $url . '">' . $portfolio->get_title() . '</a>';
+            $result[] = '</li>';
+        }
+        $result[] = '</ul>';
+        $result[] = '</span>';
+        $result[] = '</span>';
+        return implode("\n", $result);
+    }
+
+    function __toString()
+    {
+        return $this->display();
+    }
+
+}
+
+/**
+ * A "send to this portfolio" action. Actions are used by the SortableTable to 
+ * perform actions on a set of objects. An action is composed of
+ * 
+ *      - a name
+ *      - a title (displayed to the user)
+ *      - code to execute
+ * 
+ * Usage:
+ * 
+ *       $form_actions = array();
+ *       $form_action['...'] = get_lang('...');
+ *       $portfolio_actions = Portfolio::actions();
+ *       foreach($portfolio_actions as $action){
+ *           $form_action[$action->get_name()] = $action->get_title();
+ *       }
+ *       $table->set_form_actions($form_action, 'path');
+ * 
+ * @see SortableTable
+ */
+class PortfolioBulkAction
+{
+
+    /**
+     *
+     * @param \Portfolio\Portfolio $portfolio
+     * @return PortfolioBulkAction
+     */
+    public static function create($portfolio)
+    {
+        return new self($portfolio);
+    }
+
+    protected $name = '';
+    protected $title = '';
+    protected $portfolio = null;
+
+    /**
+     *
+     * @param \Portfolio\Portfolio $portfolio 
+     */
+    public function __construct($portfolio)
+    {
+        $this->name = md5(__CLASS__) . '_' . $portfolio->get_name();
+        $this->title = $portfolio->get_title() ? $portfolio->get_title() : get_lang('SendTo') . ' ' . $portfolio->get_name();
+        $this->portfolio = $portfolio;
+    }
+
+    public function get_name()
+    {
+        return $this->name;
+    }
+
+    public function get_title()
+    {
+        return $this->title;
+    }
+
+    /**
+     *
+     * @return \Portfolio\Portfolio
+     */
+    public function get_portfolio()
+    {
+        return $this->portfolio;
+    }
+
+    public function accept()
+    {
+        $name = $this->get_name();
+        $action = Request::get(PortfolioController::PARAM_ACTION);
+        if ($name != $action) {
+            return false;
+        }
+        $pathes = Request::get('path');
+        if (empty($pathes)) {
+            return false;
+        }
+
+        $course = Course::current();
+        if (empty($course)) {
+            return false;
+        }
+        return true;
+    }
+
+    public function run()
+    {
+        if (!$this->accept()) {
+            return false;
+        }
+
+        $course = Course::current();
+
+        $pathes = Request::get('path');
+        $pathes = is_array($pathes) ? $pathes : array($pathes);
+
+        $ids = array();
+        foreach ($pathes as $path) {
+            $doc = Document::get_by_path($course, $path);
+            if ($doc) {
+                $ids[] = $doc->get_id();
+            }
+        }
+        if (empty($ids)) {
+            return false;
+        }
+
+        $user = new \Portfolio\User();
+        $user->email = Chamilo::user()->email();
+
+        $artefact = new Portfolio\Artefact();
+        $artefact->url = Portfolio::download_url($ids);
+
+        $portfolio = $this->get_portfolio();
+        $result = $portfolio->send($user, $artefact);
+        return $result;
+    }
+
+}

+ 23 - 0
main/inc/lib/response.class.php

@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of response
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class Response
+{
+
+    public static function not_found()
+    {
+        Header::response_code(404);
+        exit;
+    }
+
+}

+ 29 - 4
main/inc/lib/result_set.class.php

@@ -28,14 +28,16 @@ class ResultSet implements Countable, Iterator
     protected $limit_offset = null;
     protected $orderby_column = null;
     protected $orderby_direction = null;
+    protected $return_type = null;
 
-    function __construct($sql, $limit_count = null, $limit_offset = null, $orderby_column = null, $orderby_direction = null)
+    function __construct($sql, $limit_count = null, $limit_offset = null, $orderby_column = null, $orderby_direction = null, $return_type = null)
     {
         $this->sql = $sql;
         $this->limit_count = $limit_count;
         $this->limit_offset = $limit_offset;
         $this->orderby_column = $orderby_column;
         $this->orderby_direction = $direction;
+        $this->return_type = $return_type;
     }
 
     public function sql()
@@ -53,8 +55,7 @@ class ResultSet implements Countable, Iterator
 
         if (strpos($sql, ' ORDER ') || strpos($sql, ' LIMIT ') || strpos($sql, ' OFFSET ')) {
             $sql = "SELECT * FROM ($sql) AS dat ";
-        }else
-        {
+        } else {
             $sql .= ' ';
         }
 
@@ -94,6 +95,14 @@ class ResultSet implements Countable, Iterator
         return $this->count;
     }
 
+    public function first()
+    {
+        foreach ($this as $item) {
+            return $item;
+        }
+        return null;
+    }
+
     /**
      *
      * @param int $count
@@ -122,6 +131,13 @@ class ResultSet implements Countable, Iterator
         return $result;
     }
 
+    public function return_type($value)
+    {
+        $result = clone($this);
+        $result->return_type = $value;
+        return $result;
+    }
+
     public function current()
     {
         return $this->current;
@@ -134,7 +150,16 @@ class ResultSet implements Countable, Iterator
 
     public function next()
     {
-        $this->current = Database::fetch_assoc($this->handle());
+        $data = Database::fetch_assoc($this->handle());
+        if (!$data) {
+            $this->current = $this->return_type ? null : array();
+        } else if (empty($this->return_type)) {
+            $this->current = $data;
+        } else if ($this->return_type == 'object') {
+            $this->current = (object) $data;
+        } else {
+            $this->current = new $this->return_type($data);
+        }
         $this->index++;
         return $this->current;
     }

+ 439 - 0
main/inc/lib/student_publication.class.php

@@ -0,0 +1,439 @@
+<?php
+
+namespace Model;
+
+use Database;
+use ResultSet;
+
+/**
+ * Represent a database "student_publication" object. 
+ * 
+ * Note: 
+ * 
+ * Each database column is mapped to a property.
+ * 
+ * The item_property table is available through its own property but is loaded
+ * alongside document data.
+ * 
+ * Some db query functions exists in this class and would need to be adapted
+ * to Symphony once it is moved to production. Yet the object structure should
+ * stay. 
+ * 
+ * @see \Model\ItemProperty
+ * @see table c_student_publication
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class StudentPublication
+{
+    
+    public static function void()
+    {
+        static $result = null;
+        if($result)
+        {
+            return $result;
+        }
+        
+        $result = new self();
+        return $result;
+    }
+    
+    
+
+    /**
+     *
+     * @param string $where
+     * @return \ResultSet 
+     */
+    public static function query($where)
+    {
+        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
+        $table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
+        $tool = 'work';
+
+        $sql = "SELECT pub.*,         
+                       prop.id AS property_id, 
+                       prop.tool, 
+                       prop.insert_user_id,
+                       prop.insert_date,
+                       prop.lastedit_date,
+                       prop.ref,
+                       prop.lastedit_type,
+                       prop.lastedit_user_id,
+                       prop.to_group_id, 
+                       prop.to_user_id, 
+                       prop.visibility, 
+                       prop.start_visible, 
+                       prop.end_visible, 
+                       prop.id_session
+                FROM 
+                    $table AS pub, 
+                    $table_item_property AS prop
+                WHERE 
+                    (pub.id = prop.ref AND
+                     pub.c_id = prop.c_id AND
+                     prop.tool = '$tool')";
+
+        $sql .= $where ? "AND ($where)" : '';
+        $result = new ResultSet($sql);
+        return $result->return_type(__CLASS__);
+    }
+
+    /**
+     *
+     * @param int|Course $c_id
+     * @param int $id
+     * @return \Model\StudentPublication 
+     */
+    public static function get_by_id($c_id, $id)
+    {
+        $c_id = is_object($c_id) ? $c_id->get_id() : (int) $c_id;
+        return self::query("pub.c_id = $c_id AND pub.id = $id")->first();
+    }
+
+    protected $c_id = 0;
+    protected $id = 0;
+    protected $url = '';
+    protected $title = '';
+    protected $description = '';
+    protected $author = '';
+    protected $active = null;
+    protected $accepted = false;
+    protected $post_group_id = 0;
+    protected $sent_date = 0;
+    protected $filetype = '';
+    protected $has_properties = 0;
+    protected $view_properties = null;
+    protected $qualification = 0;
+    protected $date_of_qualification = 0;
+    protected $parent_id = 0;
+    protected $qualificator_id = 0;
+    protected $weight = 0;
+    protected $session_id = 0;
+    protected $user_id = null;
+    protected $allow_text_assignment = 0;
+    protected $contains_file = 0;
+    protected $course = null;
+    protected $item_property = null;
+
+    public function __construct($data)
+    {
+        $data = (object) $data;
+        $this->c_id = (int) $data->c_id;
+        $this->id = (int) $data->id;
+        $this->url = $data->url;
+        $this->title = $data->title;
+        $this->description = $data->description;
+        $this->author = $data->author;
+        $this->active = $data->active;
+        $this->accepted = $data->accepted;
+        $this->post_group_id = $data->post_group_id;
+        $this->sent_date = $data->sent_date;
+        $this->filetype = $data->filetype;
+        $this->has_properties = $data->has_properties;
+        $this->view_properties = $data->view_properties;
+        $this->qualification = $data->qualification;
+        $this->date_of_qualification = $data->date_of_qualification;
+        $this->parent_id = $data->parent_id;
+        $this->qualificator_id = $data->qualificator_id;
+        $this->weight = $data->weight;
+        $this->session_id = $data->session_id;
+        $this->user_id = $data->user_id;
+        $this->allow_text_assignment = $data->allow_text_assignment;
+        $this->contains_file = $data->contains_file;
+        $this->course = $data->course;
+        $this->item_property = $data->item_property;
+
+        $this->course = null;
+
+        if (isset($data->property_id)) {
+            $property = (array) $data;
+            $property = (object) $property;
+            $property->id = $property->property_id;
+            $this->item_property = ItemProperty::create($property);
+        } else {
+            $this->item_property = null;
+        }
+    }
+    
+
+
+    public function get_c_id()
+    {
+        return $this->c_id;
+    }
+
+    public function set_c_id($value)
+    {
+        $this->c_id = $value;
+    }
+
+    public function get_id()
+    {
+        return $this->id;
+    }
+
+    public function set_id($value)
+    {
+        $this->id = $value;
+    }
+
+    public function get_url()
+    {
+        return $this->url;
+    }
+
+    public function set_url($value)
+    {
+        $this->url = $value;
+    }
+
+    public function get_title()
+    {
+        return $this->title;
+    }
+
+    public function set_title($value)
+    {
+        $this->title = $value;
+    }
+
+    public function get_description()
+    {
+        return $this->description;
+    }
+
+    public function set_description($value)
+    {
+        $this->description = $value;
+    }
+
+    public function get_author()
+    {
+        return $this->author;
+    }
+
+    public function set_author($value)
+    {
+        $this->author = $value;
+    }
+
+    public function get_active()
+    {
+        return $this->active;
+    }
+
+    public function set_active($value)
+    {
+        $this->active = $value;
+    }
+
+    public function get_accepted()
+    {
+        return $this->accepted;
+    }
+
+    public function set_accepted($value)
+    {
+        $this->accepted = $value;
+    }
+
+    public function get_post_group_id()
+    {
+        return $this->post_group_id;
+    }
+
+    public function set_post_group_id($value)
+    {
+        $this->post_group_id = $value;
+    }
+
+    public function get_sent_date()
+    {
+        return $this->sent_date;
+    }
+
+    public function set_sent_date($value)
+    {
+        $this->sent_date = $value;
+    }
+
+    public function get_filetype()
+    {
+        return $this->filetype;
+    }
+
+    public function set_filetype($value)
+    {
+        $this->filetype = $value;
+    }
+
+    public function get_has_properties()
+    {
+        return $this->has_properties;
+    }
+
+    public function set_has_properties($value)
+    {
+        $this->has_properties = $value;
+    }
+
+    public function get_view_properties()
+    {
+        return $this->view_properties;
+    }
+
+    public function set_view_properties($value)
+    {
+        $this->view_properties = $value;
+    }
+
+    public function get_qualification()
+    {
+        return $this->qualification;
+    }
+
+    public function set_qualification($value)
+    {
+        $this->qualification = $value;
+    }
+
+    public function get_date_of_qualification()
+    {
+        return $this->date_of_qualification;
+    }
+
+    public function set_date_of_qualification($value)
+    {
+        $this->date_of_qualification = $value;
+    }
+
+    public function get_parent_id()
+    {
+        return $this->parent_id;
+    }
+
+    public function set_parent_id($value)
+    {
+        $this->parent_id = $value;
+    }
+
+    public function get_qualificator_id()
+    {
+        return $this->qualificator_id;
+    }
+
+    public function set_qualificator_id($value)
+    {
+        $this->qualificator_id = $value;
+    }
+
+    public function get_weight()
+    {
+        return $this->weight;
+    }
+
+    public function set_weight($value)
+    {
+        $this->weight = $value;
+    }
+
+    public function get_session_id()
+    {
+        return $this->session_id;
+    }
+
+    public function set_session_id($value)
+    {
+        $this->session_id = $value;
+    }
+
+    public function get_user_id()
+    {
+        return $this->user_id;
+    }
+
+    public function set_user_id($value)
+    {
+        $this->user_id = $value;
+    }
+
+    public function get_allow_text_assignment()
+    {
+        return $this->allow_text_assignment;
+    }
+
+    public function set_allow_text_assignment($value)
+    {
+        $this->allow_text_assignment = $value;
+    }
+
+    public function get_contains_file()
+    {
+        return $this->contains_file;
+    }
+
+    public function set_contains_file($value)
+    {
+        $this->contains_file = $value;
+    }
+
+    public function is_folder()
+    {
+        return $this->filetype == 'folder';
+    }
+
+    public function is_file()
+    {
+        return $this->filetype == 'file';
+    }
+
+    public function is_visible()
+    {
+        $this->get_item_property()->get_visibility() == 1;
+    }
+
+    public function is_accessible($user = null)
+    {
+        $user_id = $user ? $user : api_get_user_id();
+        $result = $this->is_visible() || $this->get_user_id() == $user_id || api_is_allowed_to_edit();
+        return $result;
+    }
+
+    public function get_absolute_path()
+    {
+        return api_get_path(SYS_COURSE_PATH) . api_get_course_path() . '/' . $this->get_url();
+    }
+
+    /**
+     *
+     * @return \Model\ItemProperty
+     */
+    public function get_item_property()
+    {
+        if ($this->item_property && $this->item_property->get_c_id() == $this->c_id && $this->item_property->get_ref() == $this->id) {
+            return $this->item_property;
+        }
+
+        $this->item_property = ItemProperty::get_by_ref($this->id, TOOL_DOCUMENT);
+        return $this->item_property;
+    }
+
+    /**
+     *
+     * @param bool $all
+     * @return ResultSet|array
+     */
+    public function get_children()
+    {
+        if (!$this->is_folder()) {
+            return array();
+        }
+        $id = $this->id;
+        $c_id = $this->c_id;
+        $where = "pub.c_id = $c_id AND pub.parent_id = $id";
+        return self::query($where);
+    }
+    
+}

+ 145 - 0
main/inc/lib/system/io/temp.class.php

@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * Temporary file/folder. The file/folder is automatically deleted at
+ * the end of the script/during garbage collection.
+ * 
+ * The object implements __toString so it can be used as string variable.
+ * 
+ * Usage
+ * 
+ *      $path = Temp::file();
+ *      file_puts_content($path, $content);
+ * 
+ * or
+ * 
+ *      $path = Temp::dir();
+ *      ...
+ *
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class Temp
+{
+
+    /**
+     * Recursively delete files and/or folders.
+     * 
+     * @param string $path
+     * @return boolean 
+     */
+    public static function delete($path)
+    {
+        if (!file_exists($path)) {
+            return false;
+        }
+
+        if (is_file($path)) {
+            unlink($path);
+            return true;
+        }
+        $files = scandir($path);
+        $files = array_diff($files, array('.', '..'));
+        foreach ($files as $file) {
+            self::delete($file);
+        }
+        rmdir($path);
+    }
+
+    private static $temp_root = '';
+
+    /**
+     * Set the temp root directory. Temporary files are by default created in this directory.
+     * Defaults to sys_get_temp_dir().
+     * 
+     * @param string $value 
+     */
+    public static function set_temp_root($value)
+    {
+        self::$temp_root = $value;
+    }
+
+    public static function get_temp_root()
+    {
+        if (empty(self::$temp_root)) {
+            self::$temp_root = sys_get_temp_dir();
+        }
+
+        return self::$temp_root;
+    }
+
+    /**
+     * Returns a path to a non-existing temporary file located under temp_dir.
+     * 
+     * @return string 
+     */
+    public static function get_temporary_name()
+    {
+        $result = self::get_temp_root() . '/' . md5(uniqid('tmp', true));
+        while (file_exists($result)) {
+            $result = self::get_temp_root() . '/' . md5(uniqid('tmp', true));
+        }
+        return $result;
+    }
+
+    /**
+     *
+     * @param string $path
+     * @return Temp 
+     */
+    public static function file($path = '')
+    {
+        $path = $path ? $path : self::get_temporary_name();
+        return new self($path);
+    }
+
+    /**
+     *
+     * @param string $path
+     * @return Temp 
+     */
+    public static function dir($path = '')
+    {
+        $path = $path ? $path : self::get_temporary_name();
+        if (!file_exists($path)) {
+            mkdir($path, 0777, $true);
+        }
+        return new self($path);
+    }
+
+    /**
+     *
+     * @param string $path
+     * @return Temp 
+     */
+    public static function create($path = '')
+    {
+        $path = $path ? $path : self::get_temporary_name();
+        return new self($path);
+    }
+
+    protected $path = '';
+
+    function __construct($path = '')
+    {
+        $this->path = $path;
+    }
+
+    function get_path()
+    {
+        return $this->path;
+    }
+
+    function __toString()
+    {
+        return $this->path;
+    }
+
+    function __destruct()
+    {
+        $path = $this->path;
+        self::delete($path);
+    }
+
+}

+ 131 - 0
main/inc/lib/system/net/curl.class.php

@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * 
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class Curl
+{
+
+    protected static $default_options = array();
+
+    static function get_default_options($options = array())
+    {
+        if (empty(self::$default_options)) {
+            self::$default_options[CURLOPT_HEADER] = false;
+            self::$default_options[CURLOPT_RETURNTRANSFER] = true;
+            self::$default_options[CURLOPT_SSL_VERIFYPEER] = false;
+        }
+
+        $result = self::$default_options;
+        foreach ($options as $key => $value) {
+            $result[$key] = $value;
+        }
+        return $result;
+    }
+
+    static function set_default_option($key, $value)
+    {
+        $options = $this->get_options(array($key => $value));
+        self::$default_options = $options;
+    }
+
+    /**
+     *
+     * @param string $url
+     * @param array $options
+     * @return Curl
+     */
+    static function get($url, $options = array())
+    {
+        $options[CURLOPT_HTTPGET] = true;
+        $result = new self($url, $options);
+        return $result;
+    }
+
+    /**
+     *
+     * @param string $url
+     * @param array $fields
+     * @param array $options
+     * @return Curl 
+     */
+    static function post($url, $fields, $options = array())
+    {
+        $options[CURLOPT_POST] = true;
+        $options[CURLOPT_POSTFIELDS] = $fields;
+        $result = new self($url, $options);
+        return $result;
+    }
+
+    protected $url = '';
+    protected $options = array();
+    protected $content = '';
+    protected $info = array();
+    protected $error = '';
+    protected $error_no = 0;
+
+    function __construct($url, $options = array())
+    {
+        $this->url = $url;
+        $this->options = self::get_default_options($options);
+    }
+
+    function url()
+    {
+        return $this->url;
+    }
+
+    function options()
+    {
+        return $this->options;
+    }
+
+    function execute()
+    {
+        $ch = curl_init();
+
+        $options = $this->options;
+        $options[CURLOPT_URL] = $this->url;
+        curl_setopt_array($ch, $options);
+
+        $this->content = curl_exec($ch);
+        $this->error = curl_error($ch);
+        $this->info = curl_getinfo($ch);
+        $this->error_no = curl_errno($ch);
+
+        curl_close($ch);
+
+        return $this->content;
+    }
+
+    function content()
+    {
+        return $this->content;
+    }
+
+    /**
+     * @return array|string
+     */
+    function info($key = false)
+    {
+        if ($key) {
+            return isset($this->info[$key]) ? $this->info[$key] : false;
+        } else {
+            return $this->info;
+        }
+    }
+
+    function error()
+    {
+        return $this->error;
+    }
+
+    function error_no()
+    {
+        return $this->error_no;
+    }
+
+}

+ 72 - 0
main/inc/lib/system/net/http_channel.class.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace net;
+
+use Curl;
+
+/**
+ * Description of channel
+ *
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class HttpChannel
+{
+
+    /**
+     *
+     * @param string $url
+     * @param type $modules
+     * @return HttpChannel 
+     */
+    static function create($url, $modules = array())
+    {
+        return new self($url, $modules);
+    }
+
+    protected $base_url = '';
+    protected $modules = array();
+
+    public function __construct($base_url = '', $modules = array())
+    {
+        $this->base_url = $base_url;
+        $this->modules = $modules;
+    }
+    
+    function modules()
+    {
+        return $this->modules;
+    }
+
+    function get($url, $parameters)
+    {
+        $options = $this->get_options();
+        $url = $this->base_url . $url;
+        return Curl::get($url, $options)->execute();
+    }
+
+    function post($url, $fields)
+    {
+        $options = $this->get_options();
+        $url = $this->base_url . $url;
+        return Curl::post($url, $fields, $options)->execute();
+    }
+
+    protected function get_options()
+    {
+        $result = array();
+        $modules = $this->modules();
+        foreach ($modules as $module) {
+            if (is_array($module)) {
+                $options = $module;
+            } else {
+
+                $options = $module->get_options();
+            }
+            $result = array_merge($result, $options);
+        }
+        return $result;
+    }
+
+}

+ 149 - 0
main/inc/lib/system/portfolio/artefact.class.php

@@ -0,0 +1,149 @@
+<?php
+
+namespace Portfolio;
+
+/**
+ * An artefact is any object the user can display in its portfolio. 
+ * 
+ * The artefact point either to a local file or to a url from which the object's content
+ * can be fetched.
+ * 
+ * Usage
+ * 
+ *      $artefact = new artefact();
+ *      $artefact->set_path('...');
+ * 
+ * or 
+ * 
+ * 
+ *      $artefact = new artefact();
+ *      $artefact->set_url('...');
+ *
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class Artefact
+{
+
+    protected $id = '';
+    protected $mime_type = '';
+    protected $name = '';
+    protected $description = '';
+    protected $path = '';
+    protected $url = '';
+    protected $creation_date = '';
+    protected $modification_date = '';
+    protected $metadata = null;
+
+    /**
+     *
+     * @param string $file Either url or file path
+     */
+    public function __construct($file = '')
+    {
+        if ($file) {
+            if (strpos($file, 'http') !== false) {
+                $this->url = $file;
+            } else {
+                $this->path = $file;
+            }
+        }
+        $this->id = uniqid('', true);
+        $this->mime_type = '';
+        $time = time();
+        $this->creation_date = $time;
+        $this->modification_date = $time;
+    }
+
+    public function get_id()
+    {
+        return $this->id;
+    }
+
+    public function set_id($value)
+    {
+        $this->id = $value;
+    }
+
+    public function get_name()
+    {
+        return $this->name;
+    }
+
+    public function set_name($value)
+    {
+        $this->name = $value;
+    }
+
+    public function get_mime_type()
+    {
+        return $this->mime_type;
+    }
+
+    public function set_mime_type($value)
+    {
+        $this->mime_type = $value;
+    }
+
+    public function get_description()
+    {
+        return $this->description;
+    }
+
+    public function set_description($value)
+    {
+        $this->description = $value;
+    }
+
+    public function get_path()
+    {
+        return $this->path;
+    }
+
+    public function set_path($value)
+    {
+        $this->path = $value;
+    }
+
+    public function get_url()
+    {
+        return $this->url;
+    }
+
+    public function set_url($value)
+    {
+        $this->url = $value;
+    }
+
+    public function get_creation_date()
+    {
+        return $this->creation_date;
+    }
+
+    public function set_creation_date($value)
+    {
+        $this->creation_date = $value;
+    }
+
+    public function get_modification_date()
+    {
+        return $this->modification_date;
+    }
+
+    public function set_modification_date($value)
+    {
+        $this->modification_date = $value;
+    }
+
+    public function get_metadata()
+    {
+        return $this->metadata;
+    }
+
+    public function set_metadata($value)
+    {
+        $this->metadata = $value;
+    }
+
+}

+ 35 - 0
main/inc/lib/system/portfolio/download.class.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Portfolio;
+
+use Header;
+
+/**
+ * Download file to your desktop
+ *
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class Download extends Portfolio
+{
+
+    function __construct()
+    {
+        parent::__construct('download', null);
+    }
+
+    /**
+     *
+     * @param User $user
+     * @param Artefact $artefact
+     * @return bool
+     */
+    function send($user, $artefact)
+    {
+        if ($artefact->get_url()) {
+            Header::location($artefact->get_url());
+        }
+    }
+
+}

+ 52 - 0
main/inc/lib/system/portfolio/mahara.class.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Portfolio;
+
+use Header;
+
+/**
+ * Interface with a Mahara portfolio.
+ * 
+ * This class requires that the connect mahara plugin is installed and enabled.
+ *
+ * @see https://mahara.org/
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class Mahara extends Portfolio
+{
+
+    protected $url = '';
+
+    /**
+     *
+     * @param string $url       The root url 
+     */
+    function __construct($url)
+    {
+        parent::__construct('Mahara', null);
+        $this->url = $url;
+    }
+
+    function get_url()
+    {
+        return $this->url;
+    }
+
+    /**
+     *
+     * @param User $user
+     * @param Artefact $artefact
+     * @return bool
+     */
+    function send($user, $artefact)
+    {
+        $root = $this->get_url();
+        rtrim($root, '/');
+        $url = $artefact->get_url();
+        $url = $root . '/artefact/connect/upload.php?url=' . urlencode($url) . '&extract=true';
+        Header::location($url);
+    }
+
+}

+ 106 - 0
main/inc/lib/system/portfolio/portfolio.class.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace Portfolio;
+
+/**
+ * Portfolio are used to display and share content. The porfolio class represents
+ * one (external) portfolio application and allows to share content with an it. 
+ *
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class Portfolio
+{
+
+    public static function none()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = new self('empty', null);
+        }
+        return $result;
+    }
+
+    public static function all()
+    {
+        
+    }
+
+    protected $name;
+    protected $title = '';
+    protected $description = '';
+    protected $channel;
+
+    function __construct($name, $channel = null)
+    {
+        $this->name = $name;
+        $this->title = $name;
+        $this->channel = $channel;
+    }
+
+    /**
+     * The name of the portfolio - i.e. the unique id.
+     * @return type 
+     */
+    function get_name()
+    {
+        return $this->name;
+    }
+    
+    /**
+     * Title for the end user.
+     * 
+     * @return type 
+     */
+    function get_title()
+    {
+        return $this->title;
+    }
+    
+    function set_title($value)
+    {
+        $this->title = $value;
+    }
+    
+    /**
+     * Description for the end user.
+     * 
+     * @return type 
+     */
+    function get_description()
+    {
+        return $this->description;
+    }
+    
+    function set_description($value)
+    {
+        $this->description = $value;
+    }
+
+    /**
+     *
+     * @return HttpChannel
+     */
+    function channel()
+    {
+        return $this->channel;
+    }
+
+    function __toString()
+    {
+        return $this->name;
+    }
+
+    /**
+     *
+     * @param User $user
+     * @param Artefact $artefact
+     * @return bool
+     */
+    function send($user, $artefact)
+    {
+        return false;
+    }
+
+}

+ 19 - 0
main/inc/lib/system/portfolio/user.class.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Portfolio;
+
+/**
+ * A portfolio user is used to share identity between two applications.
+ * Not used at this point.
+ *
+ * @copyright (c) 2012 University of Geneva
+ * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class User
+{
+    public $id;
+    public $email;
+    public $token;
+        
+}

+ 81 - 0
main/inc/lib/system/web/header.class.php

@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * Header utility functions.
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class Header
+{
+
+    public static function response_code($response_code)
+    {
+        if (function_exists('http_response_code')) {
+            http_response_code($response_code);
+            return;
+        }
+
+        switch ($response_code) {
+            case 400:
+                header("HTTP/1.0 $response_code Bad Request");
+            case 401:
+                header("HTTP/1.0 $response_code Unauthorized");
+            case 402:
+                header("HTTP/1.0 $response_code Payment Required");
+            case 403:
+                header("HTTP/1.0 $response_code Forbidden");
+            case 404:
+                header("HTTP/1.0 $response_code Not Found");
+            default:
+                header("HTTP/1.0 $response_code");
+        }
+    }
+
+    public static function content_type($mime_type, $charset = '')
+    {
+        if (empty($mime_type)) {
+            return;
+        }
+        $type = $charset ? "$mime_type;charset=$charset" : $mime_type;
+        header('Content-type: ' . $type);
+    }
+
+    public static function content_type_xml()
+    {
+        header('Content-type: text/xml');
+    }
+
+    public static function content_type_javascript()
+    {
+        header('Content-type: application/javascript');
+    }
+
+    /**
+     * Redirect the navigator to the specified url.
+     * 
+     * @param string $url 
+     */
+    public static function location($url)
+    {
+        header("Location: $url");
+        exit;
+    }
+
+    public static function expires($timestamp)
+    {
+        $value = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
+        header('Expires: ' . $value);
+    }
+
+    public static function cache_control($value)
+    {
+        header('Cache-Control: ' . $value);
+    }
+
+    public static function pragma($value)
+    {
+        header('Pragma: ' . $value);
+    }
+
+}

+ 5 - 1
main/inc/lib/request.class.php → main/inc/lib/system/web/request.class.php

@@ -11,7 +11,11 @@ class Request
 
     public static function get($key, $default = null)
     {
-        return isset($_GET[$key]) ? $_GET[$key] : $default;
+        return isset($_REQUEST[$key]) ? $_REQUEST[$key] : $default;
+    }
+    
+    public static function has($key){
+        return isset($_REQUEST[$key]);
     }
     
     /**

+ 0 - 0
main/inc/lib/request_server.class.php → main/inc/lib/system/web/request_server.class.php


+ 103 - 0
main/inc/lib/user_api_key_manager.class.php

@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * Manage user api keys
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class UserApiKeyManager
+{
+
+    /**
+     * The name of the default service.
+     * 
+     * @return string 
+     */
+    public static function default_service()
+    {
+        return 'chamilo';
+    }
+
+    /**
+     *  
+     */
+    public static function end_of_time()
+    {
+        $time = 2147483647; //mysql int max value
+    }
+
+    public static function get_by_id($id)
+    {
+        $table = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
+        $sql = "SELECT * FROM $table WHERE id=$id";
+        $res = Database::query($sql);
+        if (Database::num_rows($res) < 1) {
+            return false;
+        }
+        $result = Database::fetch_array($res, 'ASSOC');
+        return $result;
+    }
+
+    /**
+     *
+     * @param int $duration in seconds
+     * @param int $user_id
+     * @param string $api_service
+     * @param string $api_end_point
+     * @return AccessToken 
+     */
+    public static function create_temp_token($api_service = null, $duration = 60, $user_id = null, $api_end_point = null)
+    {
+        $time = time();
+        $validity_start_date = $time;
+        $validity_end_date = $time + $duration;
+        return self::create_token($user_id, $api_key = null, $api_service, $api_end_point, $validity_start_date, $validity_end_date);
+    }
+
+    /**
+     *
+     * @param int $user_id
+     * @param string $api_key
+     * @param string $api_service
+     * @param string $api_end_point
+     * @param int $validity_start_date
+     * @param int $validity_end_date
+     * @param string $description
+     * @return AccessToken 
+     */
+    public static function create_token($user_id = null, $api_key = null, $api_service = null, $api_end_point = null, $validity_start_date = null, $validity_end_date = null, $description)
+    {
+        $time = time();
+        $user_id = $user_id ? $user_id : Chamilo::user()->user_id();
+        $api_key = $api_key ? $api_key : uniqid('', true);
+        $api_service = $api_service ? $api_service : self::default_service();
+        $api_end_point = $api_end_point ? $api_end_point : '';
+        $validity_start_date = $validity_start_date ? $validity_start_date : $time;
+        $validity_end_date = $validity_end_date ? $validity_end_date : self::end_of_time();
+        $created_date = $time;
+
+        $user_id = (int) $user_id;
+        $api_key = Database::escape_string($api_key);
+        $api_service = Database::escape_string($api_service);
+        $api_end_point = Database::escape_string($api_end_point);
+        $validity_start_date = date('Y-m-d H:i:s', $validity_start_date);
+        $validity_end_date = date('Y-m-d H:i:s', $validity_end_date);
+        $created_date = date('Y-m-d H:i:s', $created_date);
+
+        $values = array();
+        $values['user_id'] = $user_id;
+        $values['api_key'] = $api_key;
+        $values['api_service'] = $api_service;
+        $values['api_end_point'] = $api_end_point;
+        $values['validity_start_date'] = $validity_start_date;
+        $values['validity_end_date'] = $validity_end_date;
+        $values['created_date'] = $created_date;
+
+        $table = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
+
+        $id = Database::insert($table, $values);
+        return AccessToken::create($id, $user_id, $api_key);
+    }
+
+}

+ 1 - 1
main/inc/lib/usermanager.lib.php

@@ -2253,7 +2253,7 @@ class UserManager {
         $service_name = Database::escape_string($api_service);
         if (is_string($service_name) === false) { return false;}
         $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
-        $sql = "SELECT id, api_key FROM $t_api WHERE user_id = ".$user_id." AND api_service='".$api_service."';";
+        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
         $res = Database::query($sql);
         if ($res === false) return false; //error during query
         $num = Database::num_rows($res);

+ 129 - 0
main/inc/lib/zip.class.php

@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * Wrapper around pclzip. Makes a bit easier to use compression.
+ * 
+ * Usage:
+ * 
+ *      $zip = Zip::create('...');
+ *      $zip->add($file_path, $local_path);
+ * 
+ * Note
+ * 
+ * Pclzip do not accept method callbacks. It only accepts pure function callbacks.
+ * As a result the implementation is a bit more complicated than it should be.
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+class Zip
+{
+
+    protected static $pool = array();
+
+    public static function pool($hash = '')
+    {
+        if (empty($hash)) {
+            return self::$pool;
+        } else {
+            return self::$pool[$hash];
+        }
+    }
+    
+    
+    /**
+     *
+     * @param string $path
+     * @return Zip 
+     */
+    public static function create($path)
+    {
+        return new self($path);
+    }
+
+    protected $path = '';
+    protected $archive = null;
+    protected $entries = array();
+
+    public function __construct($path = '')
+    {
+        $this->path = $path;
+        self::$pool[$this->get_hash()] = $this;
+    }
+
+    public function get_path()
+    {
+        return $this->path;
+    }
+
+    public function get_hash()
+    {
+        return md5($this->path);
+    }
+
+    public function add($file_path, $archive_path = '', $comment = '')
+    {
+        /**
+         * Remove c: when working on windows. 
+         */
+        if (substr($file_path, 1, 1) == ':') {
+            $file_path = substr($file_path, 2);
+        }
+        
+        $entry = array(
+            'file_path' => $file_path,
+            'archive_path' => $archive_path,
+            'comment' => $comment
+        );
+        $this->entries[$file_path] = $entry;
+
+        $callback_name = 'zipcallback_' . $this->get_hash();
+        if (!function_exists($callback_name)) {
+            $callback = '';
+            $callback .= 'function ' . $callback_name . '($event, &$header){';
+            $callback .= '$parts = explode(\'_\', __FUNCTION__);';
+            $callback .= '$hash = end($parts);';
+            $callback .= 'return Zip::pool($hash)->callback($event, $header);';
+            $callback .= '};';
+            eval($callback);
+        }
+
+        $archive = $this->archive();
+        $archive->add($file_path, PCLZIP_CB_PRE_ADD, $callback_name);
+    }
+    
+    /**
+     *
+     * @return PclZip
+     */
+    protected function archive()
+    {
+        if ($this->archive) {
+            return $this->archive;
+        }
+        if (empty($this->path)) {
+            return null;
+        }
+        return $this->archive = new PclZip($this->path);
+    }
+
+    public function callback($event, &$header)
+    {
+        if ($event != PCLZIP_CB_PRE_ADD) {
+            return 0;
+        }
+
+        $path = $header['filename'];
+        if (!isset($this->entries[$path])) {
+            return 1;
+        }
+
+        $entry = $this->entries[$path];
+        $archive_path = $entry['archive_path'];
+        if (!empty($archive_path)) {
+            $header['stored_filename'] = $archive_path;
+        }
+        return 1;
+    }
+
+}

+ 0 - 1
main/install/configuration.dist.php

@@ -165,4 +165,3 @@ $_configuration['deny_delete_users'] = false;
 // Version settings
 $_configuration['system_version']    = '{NEW_VERSION}';
 $_configuration['system_stable']     = NEW_VERSION_STABLE;
-?>

+ 1 - 0
main/install/install_files.inc.php

@@ -30,6 +30,7 @@ if (defined('SYSTEM_INSTALLATION')) {
 	copy(api_get_path(CONFIGURATION_PATH).'course_info.conf.dist.php', api_get_path(CONFIGURATION_PATH).'course_info.conf.php');
 	copy(api_get_path(CONFIGURATION_PATH).'mail.conf.dist.php', api_get_path(CONFIGURATION_PATH).'mail.conf.php');
 	copy(api_get_path(CONFIGURATION_PATH).'profile.conf.dist.php', api_get_path(CONFIGURATION_PATH).'profile.conf.php');
+	copy(api_get_path(CONFIGURATION_PATH).'portfolio.conf.dist.php', api_get_path(CONFIGURATION_PATH).'portfolio.conf.php');
 } else {
 	echo 'You are not allowed here !';
 }

+ 46 - 40
main/install/update-files-1.8.8-1.9.0.inc.php

@@ -1,4 +1,5 @@
 <?php
+
 /* For licensing terms, see /license.txt */
 
 /**
@@ -10,44 +11,51 @@
  * current configuration file.
  * @package chamilo.install
  */
-
 Log::notice('Entering file');
 
 if (defined('SYSTEM_INSTALLATION')) {
 
-	// Edit the configuration file
-	$file = file(api_get_path(CONFIGURATION_PATH).'configuration.php');
-	$fh = fopen(api_get_path(CONFIGURATION_PATH).'configuration.php', 'w');
-	$found_version_old = false;
-	$found_stable_old = false;
-	$found_version = false;
-	$found_stable = false;
-	$found_software_name = false;
-	$found_software_url = false;
-	foreach ($file as $line) {
-		$ignore = false;
-		if (stripos($line,'$userPasswordCrypted') !== false) {
-			$line = '$_configuration[\'password_encryption\'] = \''.($userPasswordCrypted).'\';'."\r\n";
-		} elseif (stripos($line, '?>') !== false) {
-			$ignore = true;
-		}
-		if (!$ignore) {
-			fwrite($fh, $line);
-		}
-	}
-	if (!$found_version) {
-		fwrite($fh, '$_configuration[\'system_version\'] = \''.$new_version.'\';'."\r\n");
-	}
-	if (!$found_stable) {
-		fwrite($fh, '$_configuration[\'system_stable\'] = '.($new_version_stable?'true':'false').';'."\r\n");
-	}
-	if (!$found_software_name) {
-		fwrite($fh, '$_configuration[\'software_name\'] = \''.$software_name.'\';'."\r\n");
-	}
-	if (!$found_software_url) {
-		fwrite($fh, '$_configuration[\'software_url\'] = \''.$software_url.'\';'."\r\n");
-	}
-	$string = <<<EOP
+    $conf_dir = api_get_path(CONFIGURATION_PATH);
+    $portfolio_conf_dist = $conf_dir . 'portfolio.conf.dist.php';
+    $portfolio_conf = $conf_dir . 'portfolio.conf.dist.php';
+
+    if (!file_exists($portfolio_conf)) {
+        copy($portfolio_conf_dist, $portfolio_conf);
+    }
+
+    // Edit the configuration file
+    $file = file(api_get_path(CONFIGURATION_PATH) . 'configuration.php');
+    $fh = fopen(api_get_path(CONFIGURATION_PATH) . 'configuration.php', 'w');
+    $found_version_old = false;
+    $found_stable_old = false;
+    $found_version = false;
+    $found_stable = false;
+    $found_software_name = false;
+    $found_software_url = false;
+    foreach ($file as $line) {
+        $ignore = false;
+        if (stripos($line, '$userPasswordCrypted') !== false) {
+            $line = '$_configuration[\'password_encryption\'] = \'' . ($userPasswordCrypted) . '\';' . "\r\n";
+        } elseif (stripos($line, '?>') !== false) {
+            $ignore = true;
+        }
+        if (!$ignore) {
+            fwrite($fh, $line);
+        }
+    }
+    if (!$found_version) {
+        fwrite($fh, '$_configuration[\'system_version\'] = \'' . $new_version . '\';' . "\r\n");
+    }
+    if (!$found_stable) {
+        fwrite($fh, '$_configuration[\'system_stable\'] = ' . ($new_version_stable ? 'true' : 'false') . ';' . "\r\n");
+    }
+    if (!$found_software_name) {
+        fwrite($fh, '$_configuration[\'software_name\'] = \'' . $software_name . '\';' . "\r\n");
+    }
+    if (!$found_software_url) {
+        fwrite($fh, '$_configuration[\'software_url\'] = \'' . $software_url . '\';' . "\r\n");
+    }
+    $string = <<<EOP
 //============================================================================
 // Hosting settings - Allows you to set limits to the Chamilo portal when
 // hosting it for a third party. These settings can be overwritten by an
@@ -65,12 +73,10 @@ $_configuration['hosting_limit_sessions'] = 0;
 // Default (0) = no limit
 $_configuration['hosting_limit_disk_space'] = 0;
 EOP;
-        fwrite($fh,$string);
-	fwrite($fh, '?>');
-	fclose($fh);
-
+    fwrite($fh, $string);
+    fwrite($fh, '?>');
+    fclose($fh);
 } else {
 
-	echo 'You are not allowed here !';
-
+    echo 'You are not allowed here !';
 }

+ 6 - 0
main/portfolio/index.php

@@ -0,0 +1,6 @@
+<?php
+/**
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+

+ 15 - 0
main/portfolio/share.php

@@ -0,0 +1,15 @@
+<?php
+
+/**
+ *
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+require_once __DIR__ . '/../inc/global.inc.php';
+
+$has_access = api_protect_course_script();
+if (!$has_access) {
+    exit;
+}
+
+Portfolio::controller()->run();

+ 133 - 0
main/work/file.php

@@ -0,0 +1,133 @@
+<?php
+
+Use Model\StudentPublication;
+Use Model\Course;
+
+/**
+ * 	Return either 
+ * 
+ *      - one work item (file)
+ *      - several work items (files) zipped together
+ * 
+ * Used to transfer files to another application through http.
+ * 
+ * Script parameters:
+ * 
+ *      - id        id(s) of the work item id=1 or id=1,2,4  
+ *      - cidReq    course code
+ * 
+ * Note this script enables key authentication so access with a key token is possible.
+ * 
+ * @package chamilo.document
+ * @license see /license.txt
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
+ */
+require_once __DIR__ . '/../inc/autoload.inc.php';
+KeyAuth::enable();
+
+require_once __DIR__ . '/../inc/global.inc.php';
+
+$has_access = api_protect_course_script();
+if (!$has_access) {
+    exit;
+}
+
+session_cache_limiter('none');
+
+$ids = Request::get('id', '');
+$ids = $ids ? explode(',', $ids) : array();
+
+$course = Course::current();
+
+/**
+ * No files requested. We make sure we return 404 error to tell the client
+ * that the call failed. 
+ */
+if (count($ids) == 0 || empty($course)) {
+    Response::not_found();
+}
+
+/**
+ * One file/folder requested.
+ */
+if (count($ids) == 1) {
+    $id = reset($ids);
+    $pub = StudentPublication::get_by_id($course, $id);
+    if (empty($pub)) {
+        Response::not_found();
+    }
+
+    $has_access = $pub->is_accessible();
+    if (!$has_access) {
+        Response::not_found();
+    }
+
+    if ($pub->is_file()) {
+        event_download(Uri::here());
+        DocumentManager::file_send_for_download($pub->get_absolute_path(), false, $pub->get_title());
+        exit;
+    }
+
+    /**
+     * one folder requested 
+     */
+    $items = array();
+    $children = $pub->get_children();
+    foreach ($children as $child) {
+        if ($child->is_accessible()) {
+            $items[] = $child;
+        }
+    }
+    if (count($items) == 0) {
+        Response::not_found();
+    }
+
+    $zip = Chamilo::temp_zip();
+    foreach ($items as $item) {
+        $path = $item->get_absolute_path();
+        $title = $item->get_title();
+        $zip->add($path, $title);
+    }
+    event_download(Uri::here());
+    DocumentManager::file_send_for_download($zip->get_path(), false, $pub->get_title() . '.zip');
+}
+
+/**
+ * Several files requested. In this case we zip them together. 
+ */
+$items = array();
+foreach ($ids as $id) {
+    $pub = StudentPublication::get_by_id($course, $id);
+    if (!$pub->is_accessible()) {
+        break;
+    }
+    if ($pub->is_file()) {
+        $items[] = $pub;
+    }
+    /**
+     * We ignore folders 
+     */
+}
+
+/**
+ * Requested files may not be accessible. 
+ */
+if (count($items) == 0) {
+    Response::not_found();
+}
+
+/**
+ * Zip files together. 
+ */
+$zip = Chamilo::temp_zip();
+foreach ($items as $item) {
+    $path = $item->get_absolute_path();
+    $title = $item->get_title();
+    $zip->add($path, $title);
+}
+
+/**
+ * Send file for download 
+ */
+event_download(Uri::here());
+DocumentManager::file_send_for_download($zip->get_path(), false, get_lang('StudentPublications') . '.zip');

+ 4 - 2
main/work/work.lib.php

@@ -1660,7 +1660,9 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
                     $link_to_download = '<a href="download.php?id='.$item_id.'">'.Display::return_icon('save.png', get_lang('Save'),array(), ICON_SIZE_SMALL).'</a> ';
                 } else {
                     $link_to_download = '<a href="view.php?id='.$item_id.'">'.Display::return_icon('default.png', get_lang('View'),array(), ICON_SIZE_SMALL).'</a> ';
-                } 
+                }
+                
+                $send_to = Portfolio::share('work', $work['id'],  array('style' => 'white-space:nowrap;'));
                 
                 $work['qualification'] = $qualification_string;
 
@@ -1724,7 +1726,7 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
                     $qualificator_id = Display::label(get_lang('Revised'), 'success');
                 }                                       
                 $work['qualificator_id'] = $qualificator_id;
-                $work['actions'] = $link_to_download.$action;
+                $work['actions'] = $send_to.$link_to_download.$action;
                 $works[] = $work;
             }            
         }

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov