Browse Source

see #5193: adding but not enabling course description changes

Laurent Opprecht 12 years ago
parent
commit
f0bb2f1e4a

+ 172 - 0
main/course_description/ajax_controller.class.php

@@ -0,0 +1,172 @@
+<?php
+
+namespace CourseDescription;
+
+use \Display;
+use \Template;
+use \FormValidator;
+use \Security;
+use \Uri;
+use Header;
+
+/**
+ * Ajax controller. Dispatch request and perform required action.
+ * 
+ *      - delete category/link 
+ * 
+ * Usage:
+ * 
+ *      $controller = AjaxController::instance();
+ *      $controller->run();
+ * 
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Genevas
+ * @license /license.txt
+ */
+class AjaxController extends \Controller
+{
+
+    const ACTION_DELETE = 'delete';
+    const ACTION_DELETE_BY_COURSE = 'delete_by_course';
+
+    /**
+     * Return the instance of the controller.
+     * 
+     * @return  \CourseDescription\AjaxController
+     */
+    public static function instance()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = new self();
+        }
+        return $result;
+    }
+
+    protected function __construct()
+    {
+        
+    }
+
+    /**
+     * Prepare the environment. Set up breadcrumps and raise tracking event. 
+     */
+    protected function prolog()
+    {
+        event_access_tool(TOOL_COURSE_DESCRIPTION);
+    }
+
+    public function is_allowed_to_edit()
+    {
+        if (Request::is_student_view()) {
+            return false;
+        }
+        $session_id = Request::get_session_id();
+
+        if ($session_id != 0 && api_is_allowed_to_session_edit(false, true) == false) {
+            return false;
+        }
+
+        if (!api_is_allowed_to_edit(false, true, true)) {
+            return false;
+        }
+        return true;
+    }
+    
+    public function authorize()
+    {
+        $authorize = api_protect_course_script();
+        if (!$authorize) {
+            return false;
+        }
+
+        $c_id = Request::get_c_id();
+        if (empty($c_id)) {
+            return false;
+        }
+        if (Request::is_student_view()) {
+            return false;
+        }
+        if (!$this->is_allowed_to_edit()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 
+     */
+    public function delete()
+    {
+        if (!$this->is_allowed_to_edit()) {
+            $this->forbidden();
+            return;
+        }
+
+        $description = (object) array();
+        $description->c_id = Request::get_c_id();
+        $description->id = Request::get_id();
+
+        $success = CourseDescription::repository()->remove($description);
+
+        $this->response($success);
+    }
+    /**
+     * 
+     */
+    public function delete_by_course()
+    {
+        if (!$this->is_allowed_to_edit()) {
+            $this->forbidden();
+            return;
+        }
+        
+        $course = (object) array();
+        $course->c_id = Request::get_c_id();
+        $course->session_id = Request::get_session_id();
+        
+        $success = CourseDescription::repository()->remove_by_course($course);
+
+        $this->response($success);
+    }
+
+    function forbidden()
+    {
+        $this->response(false, get_lang('YourAreNotAuthorized'));
+    }
+
+    public function unknown()
+    {
+        $this->response(false, get_lang('UnknownAction'));
+    }
+
+    /**
+     * Action exists but implementation is missing. 
+     */
+    public function missing()
+    {
+        $this->response(false, get_lang('NoImplementation'));
+    }
+
+    /**
+     * Display a standard json responce.
+     * 
+     * @param bool $success
+     * @param string $message 
+     * @param object $data
+     */
+    public function response($success = false, $message = '', $data = null)
+    {
+        $message = trim($message);
+        $response = (object) array();
+        $response->success = $success;
+        if ($message) {
+            $response->message = Display::return_message($message, $success ? 'normal' : 'error');
+        } else {
+            $response->message = '';
+        }
+        $response->data = $data;
+        $this->render_json($response);
+    }
+
+}

+ 394 - 0
main/course_description/controller.class.php

@@ -0,0 +1,394 @@
+<?php
+
+namespace CourseDescription;
+
+use \Display;
+use \Template;
+use \FormValidator;
+use \Security;
+use Uri;
+use Redirect;
+use Chamilo;
+use Javascript;
+
+/**
+ * Controller for course description. Dispatch request and peform required action.
+ * 
+ *      - list course description for course
+ *      - add a new course description to a course/session
+ *      - edit a course session
+ *      - delete a course session
+ * 
+ * Usage:
+ * 
+ *      $controller = CourseDescriptionController::instance();
+ *      $controller->run();
+ * 
+ * @package chamilo.course_description 
+ * @author Christian Fasanando <christian1827@gmail.com>
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Genevas
+ * @license see /license.txt
+ */
+class Controller extends \Controller
+{
+
+    const ACTION_ADD = 'add';
+    const ACTION_EDIT = 'edit';
+    const ACTION_DELETE = 'delete';
+    const ACTION_LISTING = 'listing';
+    const ACTION_DEFAULT = 'listing';
+    const ACTION_EXPORT_CSV = 'export_csv';
+    const ACTION_IMPORT_CSV = 'import_csv';
+
+    /**
+     * Return the instance of the controller.
+     * 
+     * @return CourseDescriptionController 
+     */
+    public static function instance()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = new self();
+        }
+        return $result;
+    }
+
+    protected function __construct()
+    {
+        
+    }
+
+    /**
+     * Action to perform. 
+     * Returns the request parameter.
+     * 
+     * @return string
+     */
+    public function get_action()
+    {
+        if (Request::is_student_view()) {
+            return self::ACTION_LISTING;
+        }
+
+        $result = parent::get_action();
+        $result = $result ? $result : self::ACTION_DEFAULT;
+        return $result;
+    }
+
+    public function is_allowed_to_edit()
+    {
+        if (Request::is_student_view()) {
+            return false;
+        }
+        $session_id = Request::get_session_id();
+
+        if ($session_id != 0 && api_is_allowed_to_session_edit(false, true) == false) {
+            return false;
+        }
+
+        if (!api_is_allowed_to_edit(false, true, true)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Whether the call is authorized or not.
+     * 
+     * @return boolean 
+     */
+    public function authorize()
+    {
+        $authorize = api_protect_course_script(true);
+        if (!$authorize) {
+            return false;
+        }
+
+        $c_id = Request::get_c_id();
+        if (empty($c_id)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Prepare the environment. Set up breadcrumps and raise tracking event. 
+     */
+    protected function prolog()
+    {
+        global $interbreadcrumb;
+        $interbreadcrumb = array();
+        $interbreadcrumb[] = array('url' => 'index.php', 'name' => get_lang('CourseProgram'));
+
+        $type_id = Request::get_description_type();
+        $type = CourseDescriptionType::repository()->find_one_by_id($type_id);
+        if ($type) {
+            $interbreadcrumb[] = array('url' => '#', 'name' => $type->get_title());
+        }
+
+        global $this_section;
+        $this_section = SECTION_COURSES;
+
+        global $current_course_tool;
+        $current_course_tool = TOOL_COURSE_DESCRIPTION;
+
+        // Tracking
+        event_access_tool(TOOL_COURSE_DESCRIPTION);
+    }
+
+    /**
+     * Javascript used by the controller
+     * 
+     * @return string
+     */
+    public function javascript()
+    {
+        $src = Chamilo::url('/main/course_description/resources/js/main.js');
+        $result = Javascript::tag($src);
+
+        $www = Chamilo::url();
+        $code = "var www = '$www';\n";
+        //$code .= Javascript::get_lang('');
+        $result .= Javascript::tag_code($code);
+        return $result;
+    }
+
+    /**
+     * Returns a url for an action that the controller can process
+     * 
+     * @param string $action
+     * @param array $params
+     * @return string 
+     */
+    public function url($action = '', $params = array())
+    {
+        $url_params = Uri::course_params();
+        if ($c_id = Request::get_c_id()) {
+            $url_params[Request::PARAM_C_ID] = $c_id;
+        }
+        if ($id = Request::get_id()) {
+            $url_params[Request::PARAM_ID] = $id;
+        }
+        if ($session_id = Request::get_session_id()) {
+            $url_params[Request::PARAM_SESSION_ID] = $session_id;
+        }
+        $url_params[Request::PARAM_ACTION] = $action;
+
+        foreach ($params as $key => $value) {
+            $url_params[$key] = $value;
+        }
+
+        $result = Uri::url('/main/course_description/index.php', $url_params, false);
+        return $result;
+    }
+
+    /**
+     * List course descriptions.
+     * 
+     * @param array messages 
+     */
+    public function listing()
+    {
+        $course = (object) array();
+        $course->c_id = Request::get_c_id();
+        $course->session_id = Request::get_session_id();
+
+        $repo = CourseDescription::repository();
+        $descriptions = $repo->find_by_course($course);
+
+        $data = (object) array();
+        $data->descriptions = $descriptions;
+        $this->render('index', $data);
+    }
+
+    /**
+     * Performs the edit action. 
+     */
+    public function edit()
+    {
+        if (!$this->is_allowed_to_edit()) {
+            $this->forbidden();
+            return;
+        }
+        
+        $id = Request::get_id();
+        $c_id = Request::get_c_id();
+
+        $repo = CourseDescription::repository();
+        $description = $repo->find_one_by_id($c_id, $id);
+
+        $action = $this->url(self::ACTION_EDIT);
+        $form = CourseDescriptionForm::create($action, $description);
+
+        if ($form->validate()) {
+            $success = $repo->save($description);
+            
+            $message = $success ? get_lang('DescriptionUpdated') : get_lang('Error');
+
+            $home = $this->url(self::ACTION_DEFAULT);
+            Redirect::go($home);
+        }
+        
+        $data = (object) array();
+        $data->form = $form;
+        $this->render('edit', $data);
+    }
+
+    /**
+     * Perform the add action
+     */
+    public function add()
+    {
+        if (!$this->is_allowed_to_edit()) {
+            $this->forbidden();
+            return;
+        }
+
+        $type_id = Request::get_description_type();
+        $type = CourseDescriptionType::repository()->find_one_by_id($type_id);
+
+        $c_id = Request::get_c_id();
+        $session_id = Request::get_session_id();
+
+        if (empty($type)) {
+            $home = $this->url(self::ACTION_DEFAULT);
+            Redirect::go($home);
+        }
+
+        $description = $type->create();
+        $description->c_id = $c_id;
+        $description->session_id = $session_id;
+
+        $params = array();
+        $params[Request::PARAM_DESCRIPTION_TYPE] = $type_id;
+        $action = $this->url(self::ACTION_ADD, $params);
+        $form = CourseDescriptionForm::create($action, $description);
+
+        if ($form->validate()) {
+            $repo = CourseDescription::repository();
+            $success = $repo->save($description);
+
+            $message = $success ? get_lang('CourseDescriptionAdded') : get_lang('Error');
+
+            $home = $this->url();
+            Redirect::go($home);
+        }
+
+        //$is_valid = !empty($type) && !empty($c_id) && !empty($title) && !empty($content) && Security::check_token();
+
+        $data = (object) array();
+        $data->type = $type;
+        $data->form = $form;
+        $this->render('edit', $data);
+    }
+
+    /**
+     * Performs the delete action.
+     * 
+     * @todo: could be worth to require a security token in the url and check it. Currently confirmation is done through javascript confirmation only.
+     */
+    public function delete()
+    {
+        if (!$this->is_allowed_to_edit()) {
+            $this->forbidden();
+            return;
+        }
+//        $is_valid = Security::check_token();
+//        if (!$is_valid) {
+//            $this->listing();
+//            return false;
+//        }
+
+        $description->c_id = Request::get_c_id();
+        $description->id = Request::get_id();
+
+        $repo = CourseDescription::repository();
+        $success = $repo->remove($description);
+
+        $message = $success ? get_lang('CourseDescriptionDeleted') : get_lang('Error');
+
+        $home = $this->url();
+        Redirect::go($home);
+    }
+
+    public function export_csv()
+    {
+        $c_id = Request::get_c_id();
+        $session_id = Request::get_session_id();
+
+        $course = (object) array();
+        $course->c_id = $c_id;
+        $course->session_id = $session_id;
+        $descriptions = CourseDescription::repository()->find_by_course($course);
+
+        $writer = new CsvWriter();
+        $writer->add($descriptions);
+        $path = $writer->get_path();
+
+        \DocumentManager :: file_send_for_download($path, true, get_lang('CourseDescriptions') . '.csv');
+    }
+
+    public function import_csv()
+    {
+        if (!$this->is_allowed_to_edit()) {
+            $this->forbidden();
+            return;
+        }
+
+        $action = $this->url(self::ACTION_IMPORT_CSV);
+        $form = new UploadFileForm('import_csv', 'post', $action);
+        $form->init();
+        if ($form->validate()) {
+            $file = $form->get_file();
+            $path = $file->tmp_name;
+            $reader = new CsvReader($path);
+            $descriptions = $reader->get_items();
+            
+            $c_id = Request::get_c_id();
+            $session_id = Request::get_session_id();
+            $course = (object) array();
+            $course->c_id = $c_id;
+            $course->session_id = $session_id;
+
+            $import = new CourseImport($course);
+            $import->add($descriptions);
+            $home = $this->url(self::ACTION_DEFAULT);
+            Redirect::go($home);
+        }
+
+        $data = (object) array();
+        $data->form = $form;
+        $this->render('upload', $data);
+    }
+
+    /**
+     * Render a template using data. Adds a few common parameters to the data array.
+     * 
+     * @see /main/template/default/course_description/
+     * @param string $template
+     * @param array $data 
+     */
+    protected function render($template, $data)
+    {
+        $data = $data ? $data : (object) array();
+
+        $_user = api_get_user_info();
+        $session_id = Request::get_session_id();
+        $data->session_image = api_get_session_image($session_id, $_user);
+
+        $sec_token = Security::get_token();
+        $data->sec_token = $sec_token;
+
+        $data->root = $this->url('');
+
+        $data->types = CourseDescriptionType::repository()->all();
+
+        $data->session_id = $session_id;
+        $data->c_id = Request::get_c_id();
+        $data->is_allowed_to_edit = $this->is_allowed_to_edit();
+        parent::render("course_description/$template.tpl", $data);
+    }
+
+}

+ 626 - 0
main/course_description/course_description.class.php

@@ -0,0 +1,626 @@
+<?php
+
+namespace CourseDescription;
+
+/**
+ * Object Model for the "Course Description" database table. Allows to 
+ * 
+ *      - query database
+ *      - create/insert new course descriptions
+ *      - update/edit course descriptions
+ *      - delete course descriptions
+ * 
+ * Course descriptions used to provide descriptions for course/sessions. 
+ * A course/session can have several descriptions associated to it from various types.
+ * Course descriptions are primarily made primarily of
+ * 
+ *      - a title
+ *      - some content
+ *      - a type (for ex: info, objectives, etc)
+ * 
+ * Usage:
+ *      
+ *      Create
+ * 
+ *      $des = new CourseDescription();
+ *      $des->set_title('...');
+ *      $des->set_content('...');
+ *      $des->set_description_type(...);
+ *      $des->insert();
+ * 
+ *      Update
+ * 
+ *      $des = CourseDescription::get_by_id(..., ...);
+ *      $des->set_title('...');
+ *      $des->update();
+ * 
+ *      Delete
+ * 
+ *      $des = CourseDescription::get_by_id(..., ...);
+ *      $des->delete();
+ * 
+ * @package chamilo.course_description
+ * @author Christian Fasanando <christian1827@gmail.com>
+ * @author Laurent Opprecht <laurent@opprecht.info> for the University of Geneva
+ * @licence /license.txt
+ */
+class CourseDescription
+{
+
+    /**
+     * Return the repository.
+     * 
+     * @return \CourseDescription\CourseDescriptionRepository 
+     */
+    public static function repository()
+    {
+        return CourseDescriptionRepository::instance();
+    }
+
+    /**
+     * Returns the list of all available types
+     * 
+     * @return array
+     */
+    public static function get_types()
+    {
+        return CourseDescriptionType::all();
+    }
+
+//    /**
+//     * Deprecated (still used by web services)
+//     * 
+//     * @param int Course id
+//     * @deprecated use get_descriptions_by_course
+//     * @return array Array of CourseDescriptions
+//     */
+//    public static function get_descriptions($course_id)
+//    {
+//        // Get course code
+//        $course_info = api_get_course_info_by_id($course_id);
+//        if (!empty($course_info)) {
+//            $course_id = $course_info['real_id'];
+//        } else {
+//            return array();
+//        }
+//        $t_course_desc = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+//        $sql = "SELECT * FROM $t_course_desc WHERE c_id = $course_id AND session_id = '0';";
+//        $sql_result = Database::query($sql);
+//        $results = array();
+//        while ($row = Database::fetch_array($sql_result)) {
+//            $desc_tmp = new CourseDescription();
+//            $desc_tmp->set_id($row['id']);
+//            $desc_tmp->set_title($row['title']);
+//            $desc_tmp->set_content($row['content']);
+//            $desc_tmp->set_session_id($row['session_id']);
+//            $desc_tmp->set_description_type($row['description_type']);
+//            $desc_tmp->set_progress($row['progress']);
+//            $results[] = $desc_tmp;
+//        }
+//        return $results;
+//    }
+
+    /**
+     *
+     * @param object $data
+     * @return \CourseDescription\CourseDescription
+     */
+    public static function create($data = null)
+    {
+        return new self($data);
+    }
+
+    protected $c_id;
+    protected $id;
+    protected $title;
+    protected $content;
+    protected $session_id;
+    protected $description_type;
+    protected $progress;
+    protected $type = null;
+
+    function __construct($data = null)
+    {
+        if ($data) {
+            foreach ($this as $key => $value) {
+                if (isset($data->{$key})) {
+                    $this->{$key} = $data->{$key};
+                }
+            }
+        }
+    }
+
+    function __get($name)
+    {
+        $f = array($this, "get_$name");
+        return call_user_func($f);
+    }
+
+    function __isset($name)
+    {
+        $f = array($this, "get_$name");
+        return is_callable($f);
+    }
+
+    function __set($name, $value)
+    {
+        $f = array($this, "set_$name");
+        if (!is_callable($f)) {
+            return;
+        }
+        call_user_func($f, $value);
+    }
+
+//    /**
+//     * Insert the course description object into the course_description table.
+//     * 
+//     * @return bool True on success, false on failure
+//     */
+//    public function insert()
+//    {
+//        $course_id = $this->get_c_id();
+//        $description_type = $this->get_description_type();
+//        $title = Database::escape_string($this->get_title());
+//        $content = Database::escape_string($this->get_content());
+//        $progress = $this->get_progress();
+//        $progress = $progress ? $progress : '0';
+//        $session_id = $this->get_session_id();
+//
+//        $table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+//        $sql = "INSERT IGNORE INTO $table SET
+//				c_id 				= $course_id, 
+//				description_type	= $description_type, 
+//				title 				= '$title', 
+//				content 			= '$content', 
+//				progress 			= $progress, 
+//				session_id          = $session_id";
+//
+//        Database::query($sql);
+//
+//        $id = Database::insert_id();
+//        if (empty($id)) {
+//            return false;
+//        }
+//        $this->id = $id;
+//
+//        /**
+//         * @todo: course info should come from c_id 
+//         */
+//        api_item_property_update(api_get_course_info(), TOOL_COURSE_DESCRIPTION, $id, 'CourseDescriptionAdded', api_get_user_id());
+//
+//        return true;
+//    }
+//    /**
+//     * Insert a row like history inside track_e_item_property table
+//     * 
+//     * @param 	int 	description type
+//     * @return  int		affected rows
+//     */
+//    public function insert_stats($description_type)
+//    {
+//        $tbl_stats_item_property = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY);
+//        $description_id = $this->get_id_by_description_type($description_type);
+//        $course_id = api_get_real_course_id();
+//        $course_code = api_get_course_id();
+//        $item_property_id = api_get_item_property_id($course_code, TOOL_COURSE_DESCRIPTION, $description_id);
+//        $sql = "INSERT IGNORE INTO $tbl_stats_item_property SET
+//				c_id				= " . api_get_course_int_id() . ",
+//				course_id 			= '$course_id',
+//			 	item_property_id 	= '$item_property_id',
+//			 	title 				= '" . Database::escape_string($this->title) . "',
+//			 	content 			= '" . Database::escape_string($this->content) . "',
+//			 	progress 			= '" . intval($this->progress) . "',
+//			 	lastedit_date 		= '" . date('Y-m-d H:i:s') . "',
+//			 	lastedit_user_id 	= '" . api_get_user_id() . "',
+//			 	session_id			= '" . intval($this->session_id) . "'";
+//        Database::query($sql);
+//        $affected_rows = Database::affected_rows();
+//        return $affected_rows;
+//    }
+//    /**
+//     * Update a course description object to the database.
+//     * 
+//     * @return bool True on success, false on failure.
+//     */
+//    public function update()
+//    {
+//        $course_id = $this->get_c_id();
+//        $id = $this->get_id();
+//        $description_type = $this->get_description_type();
+//        $title = Database::escape_string($this->get_title());
+//        $content = Database::escape_string($this->get_content());
+//        $progress = $this->get_progress();
+//        $progress = $progress ? $progress : '0';
+//        $session_id = $this->get_session_id();
+//
+//        $table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+//        $sql = "UPDATE $table SET  
+//						title       = '$title', 
+//						content     = '$content', 
+//						progress    = $progress 
+//				WHERE 	id          = $id AND 
+//						c_id = $course_id ";
+//
+//        Database::query($sql);
+//        $result = (bool) Database::affected_rows();
+//
+//        if ($result) {
+//            //insert into item_property
+//            /**
+//             * @todo: course info should come from c_id 
+//             */
+//            api_item_property_update(api_get_course_info(), TOOL_COURSE_DESCRIPTION, $this->id, 'CourseDescriptionUpdated', api_get_user_id());
+//        }
+//        return $result;
+//    }
+//    /**
+//     * Delete a course description object from the database.
+//     * 
+//     * @return bool True on success false on failure
+//     */
+//    public function delete()
+//    {
+//        $table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+//        $course_id = $this->get_c_id();
+//        $id = $this->get_id();
+//
+//        $sql = "DELETE FROM $table WHERE c_id = $course_id AND id = $id";
+//        Database::query($sql);
+//        $result = (bool) Database::affected_rows();
+//        if ($result) {
+//            /**
+//             * @todo: should get course info from $this->c_id 
+//             */
+//            api_item_property_update(api_get_course_info(), TOOL_COURSE_DESCRIPTION, $this->id, 'CourseDescriptionDeleted', api_get_user_id());
+//        }
+//        return $result;
+//    }
+//    /**
+//     * Get description id by description type
+//     * @param int description type
+//     * @return int description id
+//     */
+//    public function get_id_by_description_type($description_type)
+//    {
+//        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+//        $course_id = api_get_course_int_id();
+//
+//        $sql = "SELECT id FROM $tbl_course_description WHERE c_id = $course_id AND description_type = '" . intval($description_type) . "'";
+//        $rs = Database::query($sql);
+//        $row = Database::fetch_array($rs);
+//        $description_id = $row['id'];
+//        return $description_id;
+//    }
+//    /**
+//     * get thematic progress in porcent for a course,
+//     * first you must set session_id property with the object CourseDescription
+//     * @param bool		true for showing a icon about the progress, false otherwise (optional)
+//     * @param int		Description type (optional)
+//     * @return string   img html
+//     */
+//    public function get_progress_porcent($with_icon = false, $description_type = THEMATIC_ADVANCE)
+//    {
+//        $tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+//        $session_id = intval($session_id);
+//        $course_id = api_get_course_int_id();
+//
+//        $sql = "SELECT progress FROM $tbl_course_description WHERE c_id = $course_id AND description_type = '" . intval($description_type) . "' AND session_id = '" . intval($this->session_id) . "' ";
+//        $rs = Database::query($sql);
+//        $progress = '';
+//        $img = '';
+//        $title = '0%';
+//        $image = 'level_0.png';
+//        if (Database::num_rows($rs) > 0) {
+//            $row = Database::fetch_array($rs);
+//            $progress = $row['progress'] . '%';
+//            $image = 'level_' . $row['progress'] . '.png';
+//        }
+//        if ($with_icon) {
+//            $img = Display::return_icon($image, get_lang('ThematicAdvance'), array('style' => 'vertical-align:middle'));
+//        }
+//        $progress = $img . $progress;
+//        return $progress;
+//    }
+//    /**
+//     * Get description titles by default
+//     * @return array
+//     */
+//    public function get_default_description_title()
+//    {
+//        $default_description_titles = array();
+//        $default_description_titles[1] = get_lang('GeneralDescription');
+//        $default_description_titles[2] = get_lang('Objectives');
+//        $default_description_titles[3] = get_lang('Topics');
+//        $default_description_titles[4] = get_lang('Methodology');
+//        $default_description_titles[5] = get_lang('CourseMaterial');
+//        $default_description_titles[6] = get_lang('HumanAndTechnicalResources');
+//        $default_description_titles[7] = get_lang('Assessment');
+//
+//        $default_description_titles[8] = get_lang('Other');
+//        return $default_description_titles;
+//    }
+
+    /**
+     * The course id. 
+     * 
+     * @see get_course() property to get access to the course object
+     * @return int
+     */
+    public function get_c_id()
+    {
+        return $this->c_id;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_c_id($value)
+    {
+        $this->c_id = intval($value);
+    }
+
+    /**
+     * The id of the object.
+     * 
+     * @return int
+     */
+    public function get_id()
+    {
+        return $this->id;
+    }
+
+    /**
+     * 
+     * @return void
+     */
+    public function set_id($value)
+    {
+        $this->id = intval($value);
+    }
+
+    /**
+     * The title of the course description.
+     * 
+     * @return string
+     */
+    public function get_title()
+    {
+        return $this->title;
+    }
+
+    /**
+     * 
+     * @return void
+     */
+    public function set_title($title)
+    {
+        $this->title = $title;
+    }
+
+    /**
+     * The content/description of the course description.
+     * @return string
+     */
+    public function get_content()
+    {
+        return $this->content;
+    }
+
+    /**
+     * 
+     * @return void
+     */
+    public function set_content($content)
+    {
+        $this->content = $content;
+    }
+
+    /**
+     * The session id the object is associated with.
+     * 
+     * @return int
+     */
+    public function get_session_id()
+    {
+        return $this->session_id;
+    }
+
+    /**
+     * 
+     * @return void
+     */
+    public function set_session_id($value)
+    {
+        $this->session_id = intval($value);
+    }
+
+    /**
+     * The type of the course description. Should match one of the id returns
+     * by CourseDescription::get_types().
+     * 
+     * @return int
+     */
+    public function get_description_type()
+    {
+        return $this->description_type;
+    }
+
+    /**
+     * 
+     * @return void
+     */
+    public function set_description_type($value)
+    {
+        $this->description_type = intval($value);
+    }
+
+    /**
+     * ???
+     * @return int
+     */
+    public function get_progress()
+    {
+        return $this->progress;
+    }
+
+    /**
+     * 
+     * @return void
+     */
+    public function set_progress($value)
+    {
+        $this->progress = intval($value);
+    }
+
+    /**
+     * Return one type from its id
+     * 
+     * @return \CourseDescription\CourseDescriptionType 
+     */
+    public function get_type()
+    {
+        $type_id = $this->get_description_type();
+        if ($this->type && $this->type->id == $type_id) {
+            return $this->type;
+        }
+        $this->type = CourseDescriptionType::repository()->find_one_by_id($type_id);
+        return $this->type;
+    }
+
+    /**
+     * Returns the course object this object is associated with.
+     * Lazy loaded from the value returned by get_c_id().
+     * @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;
+    }
+
+    /**
+     * The item property this object is associated with.
+     * 
+     * @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_COURSE_DESCRIPTION);
+        return $this->item_property;
+    }
+
+}
+
+//
+///**
+// * The common routes (urls) for course description objects: 
+// * 
+// *      - create new course description
+// *      - edit course description
+// *      - delete course description 
+// * 
+// * @author Laurent Opprecht <laurent@opprecht.info> for the University of Geneva
+// * @licence /license.txt
+// */
+//class CourseDescriptionRoutes
+//{
+//
+//    /**
+//     *
+//     * @return CourseDescriptionRoutes 
+//     */
+//    public static function instance()
+//    {
+//        static $result = null;
+//        if (empty($result)) {
+//            $result = new self();
+//        }
+//        return $result;
+//    }
+//
+//    protected function __construct()
+//    {
+//        ;
+//    }
+//
+//    /**
+//     * Returns the url used to create a new course description from a specific type.
+//     * 
+//     * @param CourseDescriptionType $type
+//     * @param bool $html True to html escape the url, false otherwise.
+//     * @return string
+//     */
+//    function create($type = null, $html = true)
+//    {
+//        $type = is_object($type) ? $type->get_id() : (int) $type;
+//
+//        $params = Uri::course_params();
+//        $params['action'] = 'add';
+//        if ($type) {
+//            $params['description_type'] = $type;
+//        }
+//        $result = Chamilo::url('/main/course_description/index.php', $params, $html);
+//        return $result;
+//    }
+//
+//    /**
+//     * The url to edit a course description object
+//     * 
+//     * @param CourseDescription $description
+//     * @param bool $html True to html escape the url, false otherwise.
+//     * @return string  
+//     */
+//    function edit($description, $html = true)
+//    {
+//        $params = array();
+//        $params['id'] = $description->get_id();
+//        $params['cidReq'] = api_get_course_id();
+//        $params['id_session'] = $description->get_session_id();
+//        $params['description_type'] = $description->get_description_type();
+//        $params['action'] = 'edit';
+//        $result = Chamilo::url('/main/course_description/index.php', $params, $html);
+//        return $result;
+//    }
+//
+//    /**
+//     * The index route to list all course descriptions for the current course/session
+//     * 
+//     * @param bool $html True to html escape the url, false otherwise.
+//     * @return type 
+//     */
+//    function index($html = true)
+//    {
+//        $params = Uri::course_params();
+//        $result = Chamilo::url('/main/course_description/index.php', $params, $html);
+//        return $result;
+//    }
+//
+//    /**
+//     * Url to delete a course description object.
+//     * 
+//     * @param CourseDescription $description
+//     * @param bool $html True to html escape the url, false otherwise.
+//     * @return string  
+//     */
+//    function delete($description, $html = true)
+//    {
+//        $params = array();
+//        $params['id'] = $description->get_id();
+//        $params['cidReq'] = api_get_course_id();
+//        $params['id_session'] = $description->get_session_id();
+//        $params['description_type'] = $description->get_description_type();
+//        $params['action'] = 'delete';
+//        $result = Chamilo::url('/main/course_description/index.php', $params, $html);
+//        return $result;
+//    }
+//
+//}
+//

+ 98 - 0
main/course_description/course_description_form.class.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace CourseDescription;
+
+use Security;
+
+/**
+ * Edit/create a course description.
+ * 
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Genevas
+ * @license /license.txt
+ */
+class CourseDescriptionForm extends \FormValidator
+{
+
+    /**
+     *
+     * @param string $action
+     * @param \CourseDescription\CourseDescription $description
+     * @return \CourseDescription\CourseDescription 
+     */
+    static function create($action, $description = null)
+    {
+        $result = new self('course_description', 'post', $action);
+        if ($description) {
+            $result->init($description);
+        }
+        return $result;
+    }
+
+    protected $course_description;
+
+    function __construct($form_name = 'course_description', $method = 'post', $action = '', $target = '', $attributes = null, $track_submit = true)
+    {
+        parent::__construct($form_name, $method, $action, $target, $attributes, $track_submit);
+    }
+
+    /**
+     *
+     * @return \CourseDescription\CourseDescription
+     * 
+     */
+    public function get_course_description()
+    {
+        return $this->course_description;
+    }
+
+    public function set_course_description($value)
+    {
+        $this->course_description = $value;
+    }
+
+    /**
+     *
+     * @param \CourseDescription\CourseDescription $description 
+     */
+    function init($description = null)
+    {
+        $this->set_course_description($description);
+
+        $defaults = array();
+        $defaults['title'] = $description->title;
+        $defaults['content'] = $description->content;
+
+        $this->add_header($description->get_title());
+        $this->add_hidden('description_type', $description->get_description_type());
+        $this->add_hidden('c_id', $description->c_id);
+        $this->add_hidden('id', $description->id);
+        $this->add_textfield('title', get_lang('Title'), true, array('size' => 'width: 350px;'));
+        $this->applyFilter('title', 'html_filter');
+        $this->add_html_editor('content', get_lang('Content'), true, false, array('ToolbarSet' => 'TrainingDescription', 'Width' => '100%', 'Height' => '200'));
+        $this->add_button('save', get_lang('Save'), 'class="save"');
+
+        $this->setDefaults($defaults);
+    }
+
+    function update_model()
+    {
+        $values = $this->exportValues();
+        $course_description = $this->get_course_description();
+
+        $course_description->title = $values['title'];
+        $course_description->title = Security::remove_XSS($course_description->title);
+
+        $course_description->content = $values['content'];
+        $course_description->content = Security::remove_XSS($course_description->content, COURSEMANAGERLOWSECURITY);
+    }
+
+    function validate()
+    {
+        $result = parent::validate();
+        if ($result) {
+            $this->update_model();
+        }
+        return $result;
+    }
+
+}

+ 266 - 0
main/course_description/course_description_repository.class.php

@@ -0,0 +1,266 @@
+<?php
+
+namespace CourseDescription;
+
+use Database;
+
+/**
+ * Description of course_description_controller
+ *
+ * @author Laurent Opprecht <laurent@opprecht.info> for the University of Geneva
+ * @licence /license.txt
+ */
+class CourseDescriptionRepository
+{
+
+    /**
+     * Return the instance of the repository.
+     * 
+     * @return \CourseDescription\CourseDescriptionRepository
+     */
+    public static function instance()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = new self();
+        }
+        return $result;
+    }
+
+    /**
+     * 
+     * 
+     * @param string $where Where filter to apply
+     * @return array 
+     */
+    public function find($where)
+    {
+        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
+        $table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
+        $tool = TOOL_COURSE_DESCRIPTION;
+
+        $sql = "SELECT des.*,         
+                       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 des, 
+                    $table_item_property AS prop
+                WHERE 
+                    (des.id = prop.ref AND
+                     des.c_id = prop.c_id AND
+                     prop.tool = '$tool')";
+
+        $sql .= $where ? "AND ($where)" : '';
+
+        $rs = Database :: query($sql);
+        while ($data = Database::fetch_object($rs)) {
+            $result[] = CourseDescription::create($data);
+        }
+        return $result;
+
+        //$result = new ResultSet($sql);
+        //return $result->return_type(__CLASS__);
+    }
+
+    /**
+     *
+     * @param string $where
+     * @return \CourseDescription\CourseDescription 
+     */
+    public function find_one($where)
+    {
+        $items = $this->find($where);
+        foreach ($items as $item) {
+            return $item;
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve one course description from its ids.
+     * 
+     * @param int|Course $c_id 
+     * @param int $id           
+     * @return \CourseDescription\CourseDescription 
+     */
+    public function find_one_by_id($c_id, $id)
+    {
+        $c_id = is_object($c_id) ? $c_id->get_id() : (int) $c_id;
+        return $this->find_one("des.c_id = $c_id AND des.id = $id");
+    }
+
+    /**
+     * Returns the list of course descriptions belonging to a specific course and
+     * session.
+     * 
+     * @param object $course
+     * @return Array 
+     */
+    public function find_by_course($course)
+    {
+        $c_id = (int)$course->c_id;
+        $session_id = isset($course->session_id) ? (int)$course->session_id : 0;
+        if (empty($c_id)) {
+            return array();
+        }
+        $condition_session = api_get_session_condition($session_id, true, true);
+        $where = "des.c_id = $c_id $condition_session";
+        return $this->find($where);
+    }
+
+    /**
+     *
+     * @param object $description
+     * @return bool
+     */
+    public function save($description)
+    {
+        $id = $description->id;
+        if (empty($id)) {
+            return $this->insert($description);
+        } else {
+            return $this->update($description);
+        }
+    }
+
+    /**
+     *
+     * @param \CourseDescription\CourseDescription $description
+     * @return bool 
+     */
+    public function insert($description)
+    {
+        $c_id = (int) $description->c_id;
+
+        $session_id = (int) $description->session_id;
+        $session_id = $session_id ? $session_id : '0';
+
+        $title = trim($description->title);
+        $title = Database::escape_string($title);
+
+        $content = trim($description->content);
+        $content = Database::escape_string($content);
+
+        $description_type = (int) $description->description_type;
+
+        $progress = (int) $description->progress;
+
+        $table = Database :: get_course_table(TABLE_COURSE_DESCRIPTION);
+        $sql = "INSERT INTO $table 
+                    (c_id, title, content, session_id, description_type, progress)
+			    VALUES 
+                    ($c_id , '$title', '$content', $session_id, $description_type, $progress)";
+        $result = (bool) Database :: query($sql);
+
+        if ($result) {
+            $id = Database::insert_id();
+            $description->id = $id;
+
+            $_course = api_get_course_info_by_id($c_id);
+            $tool = TOOL_COURSE_DESCRIPTION;
+            $user_id = api_get_user_id();
+            api_item_property_update($_course, $tool, $id, 'CourseDescriptionAdded', $user_id);
+        }
+        return $result;
+    }
+
+    /**
+     *
+     * @param \CourseDescription\CourseDescription $description
+     * @return bool 
+     */
+    function update($description)
+    {
+        $c_id = (int) $description->c_id;
+        $id = (int) $description->id;
+
+        $session_id = (int) $description->session_id;
+        $session_id = $session_id ? $session_id : '0';
+
+        $title = trim($description->title);
+        $title = Database::escape_string($title);
+
+        $content = trim($description->content);
+        $content = Database::escape_string($content);
+
+        $description_type = (int) $description->description_type;
+
+        $progress = (int) $description->progress;
+
+        $table = Database :: get_course_table(TABLE_COURSE_DESCRIPTION);
+        $sql = "UPDATE $table SET
+                    title = '$title',
+                    content = '$content',
+                    session_id = $session_id,
+                    description_type = $description_type, 
+                    progress = $progress
+			    WHERE
+                    c_id = $c_id AND
+                    id = $id";
+        $result = (bool) Database :: query($sql);
+
+        if ($result) {
+            $_course = api_get_course_info_by_id($c_id);
+            $tool = TOOL_COURSE_DESCRIPTION;
+            $user_id = api_get_user_id();
+            api_item_property_update($_course, $tool, $id, 'CourseDescriptionUpdated', $user_id);
+        }
+        return $result;
+    }
+
+    /**
+     * 
+     * @param object $description
+     * @return boolean 
+     */
+    public function remove($description)
+    {
+        $table = Database :: get_course_table(TABLE_COURSE_DESCRIPTION);
+        $c_id = (int) $description->c_id;
+        $id = (int) $description->id;
+
+        if (empty($c_id) || empty($id)) {
+            return false;
+        }
+
+        $sql = "DELETE FROM $table WHERE c_id=$c_id AND id=$id";
+        $result = Database :: query($sql);
+        if ($result) {
+            $tool = TOOL_COURSE_DESCRIPTION;
+            $tbl_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
+            $sql = "DELETE FROM $tbl_property WHERE c_id=$c_id AND ref=$id AND tool='$tool'";
+            Database :: query($sql);
+        }
+        return (bool) $result;
+    }
+
+    /**
+     *
+     * @param object $course
+     * @return int 
+     */
+    public function remove_by_course($course)
+    {
+        $items = $this->find_by_course($course);
+        foreach ($items as $item) {
+            $success = $this->remove($item);
+            if ($success) {
+                $result++;
+            }
+        }
+        return $result;
+    }
+
+}

+ 231 - 0
main/course_description/course_description_type.class.php

@@ -0,0 +1,231 @@
+<?php
+
+namespace CourseDescription;
+
+/**
+ * Course description types. A course description is used to distinguish between
+ * different types of course description. It is essentialy made of:
+ * 
+ *      - title 
+ *      - icon 
+ *      - questions that are displayed on the screen to the end user when creating a new course description
+ * 
+ * Usage:
+ * 
+ *      $type = CourseDescriptionType::instance()->find_one_by_id($id);
+ *      $description = $type->create();
+ * 
+ * @author Laurent Opprecht <laurent@opprecht.info> for the University of Geneva
+ * @licence /license.txt
+ */
+class CourseDescriptionType
+{
+    
+    /**
+     * Return the instance of the controller.
+     * 
+     * @return \CourseDescription\CourseDescriptionTypeRepository 
+     */
+    public static function repository()
+    {
+        return CourseDescriptionTypeRepository::instance();
+    }
+    
+    protected $id = 0;
+    protected $name = '';
+    protected $title = '';
+    protected $info = '';
+    protected $question = '';
+    protected $icon = '';
+    protected $is_editable = true;
+    protected $content = '';
+
+    function __construct($data = null)
+    {
+        if ($data) {
+            foreach ($this as $key => $value) {
+                if (isset($data->{$key})) {
+                    $this->{$key} = $data->{$key};
+                }
+            }
+        }
+    }
+
+    function __get($name)
+    {
+        $f = array($this, "get_$name");
+        return call_user_func($f);
+    }
+
+    function __isset($name)
+    {
+        $f = array($this, "get_$name");
+        return is_callable($f);
+    }
+
+    function __set($name, $value)
+    {
+        $f = array($this, "set_$name");
+        if (!is_callable($f)) {
+            return;
+        }
+        call_user_func($f, $value);
+    }
+
+    /**
+     * Object's id
+     * 
+     * @return int
+     */
+    public function get_id()
+    {
+        return $this->id;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_id($value)
+    {
+        $this->id = $value;
+    }
+    
+    /**
+     * Object's name. That is a human readable identifier.
+     * 
+     * @return string
+     */
+    public function get_name()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_name($value)
+    {
+        $this->name = $value;
+    }
+
+    /**
+     * Title, human readable text that identify the type.
+     * 
+     * @return string
+     */
+    public function get_title()
+    {
+        return $this->title;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_title($value)
+    {
+        $this->title = $value;
+    }
+
+    /**
+     * Not used.
+     * 
+     * @return string
+     */
+    public function get_info()
+    {
+        return $this->info;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_info($value)
+    {
+        $this->info = $value;
+    }
+
+    /**
+     * Text displayed to the end user to help him create the course description
+     * 
+     * @return string
+     */
+    public function get_question()
+    {
+        return $this->question;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_question($value)
+    {
+        $this->question = $value;
+    }
+
+    /**
+     * Name of the icon file located in /main/img/icon/{size}/.
+     * 
+     * @see /main/img/icon/{size}/
+     * @return string
+     */
+    public function get_icon()
+    {
+        return $this->icon;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_icon($value)
+    {
+        $this->icon = $value;
+    }
+
+    /**
+     * Not used.
+     * 
+     * @return string
+     */
+    public function is_editable()
+    {
+        return $this->is_editable;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_editable($value)
+    {
+        $this->is_editable = $value;
+    }
+
+    /**
+     * Not used. Intenteded to be the default content for new course descriptions.
+     * @return string
+     */
+    public function get_content()
+    {
+        return $this->content;
+    }
+
+    /**
+     * @return void
+     */
+    public function set_content($content)
+    {
+        $this->content = $content;
+    }
+
+    /**
+     * Create a new course description object.
+     * 
+     * @return \CourseDescription 
+     */
+    public function create()
+    {
+        $result = new CourseDescription();
+        $result->set_description_type($this->get_id());
+        return $result;
+    }
+
+}

+ 166 - 0
main/course_description/course_description_type_repository.class.php

@@ -0,0 +1,166 @@
+<?php
+
+namespace CourseDescription;
+
+/**
+ * Description of CourseDescriptionTypeRepository
+ *
+ * @author Laurent Opprecht <laurent@opprecht.info> for the University of Geneva
+ * @licence /license.txt
+ */
+class CourseDescriptionTypeRepository
+{
+    
+    
+    /**
+     * Return the instance of the repository.
+     * 
+     * @return \CourseDescription\CourseDescriptionTypeRepository 
+     */
+    public static function instance()
+    {
+        static $result = null;
+        if (empty($result)) {
+            $result = new self();
+        }
+        return $result;
+    }
+    
+    /**
+     * All available course description types.
+     * 
+     * @return array 
+     */
+    public function all()
+    {
+        static $result = null;
+        if (!is_null($result)) {
+            return $result;
+        }
+
+        $data = (object) array();
+        $data->id = 1;
+        $data->name = 'general';
+        $data->title = get_lang('GeneralDescription');
+        $data->is_editable = true;
+        $data->icon = 'info.png';
+        $data->question = get_lang('GeneralDescriptionQuestions');
+        $data->info = get_lang('GeneralDescriptionInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        $data = (object) array();
+        $data->id = 2;
+        $data->name = 'objectives';
+        $data->title = get_lang('Objectives');
+        $data->is_editable = true;
+        $data->icon = 'objective.png';
+        $data->question = get_lang('ObjectivesQuestions');
+        $data->info = get_lang('ObjectivesInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        $data = (object) array();
+        $data->id = 3;
+        $data->name = 'topics';
+        $data->title = get_lang('Topics');
+        $data->is_editable = true;
+        $data->icon = 'topics.png';
+        $data->question = get_lang('TopicsQuestions');
+        $data->info = get_lang('TopicsInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        $data = (object) array();
+        $data->id = 4;
+        $data->name = 'methodology';
+        $data->title = get_lang('Methodology');
+        $data->is_editable = true;
+        $data->icon = 'strategy.png';
+        $data->question = get_lang('MethodologyQuestions');
+        $data->info = get_lang('MethodologyInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        $data = (object) array();
+        $data->id = 5;
+        $data->name = 'material';
+        $data->title = get_lang('CourseMaterial');
+        $data->is_editable = true;
+        $data->icon = 'laptop.png';
+        $data->question = get_lang('CourseMaterialQuestions');
+        $data->info = get_lang('CourseMaterialInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        $data = (object) array();
+        $data->id = 6;
+        $data->name = 'hr';
+        $data->title = get_lang('HumanAndTechnicalResources');
+        $data->is_editable = true;
+        $data->icon = 'teacher.png';
+        $data->question = get_lang('HumanAndTechnicalResourcesQuestions');
+        $data->info = get_lang('HumanAndTechnicalResourcesInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        $data = (object) array();
+        $data->id = 7;
+        $data->name = 'assessment';
+        $data->title = get_lang('Assessment');
+        $data->is_editable = true;
+        $data->icon = 'assessment.png';
+        $data->question = get_lang('AssessmentQuestions');
+        $data->info = get_lang('AssessmentInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        $data = (object) array();
+        $data->id = 8;
+        $data->name = 'other';
+        $data->title = get_lang('Other');
+        $data->is_editable = true;
+        $data->icon = 'wizard.png';
+        $data->question = get_lang('AssessmentQuestions');
+        $data->info = get_lang('AssessmentInformation');
+        $result[$data->id] = new CourseDescriptionType($data);
+
+        return $result;
+    }
+
+    /**
+     * Retrieve once course description type from its id.
+     * 
+     * @param int $id
+     * @return \CourseDescription\CourseDescriptionType 
+     */
+    public function find_one_by_id($id)
+    {
+        $all = $this->all();
+        $result = isset($all[$id]) ? $all[$id] : null;
+        return $result;
+    }
+    
+    /**
+     * Retrieve once course description type from its name.
+     * 
+     * @param string $name
+     * @return \CourseDescription\CourseDescriptionType 
+     */
+    public function find_one_by_name($name)
+    {
+        $name = strtolower($name);
+        $items = $this->all();
+        foreach($items as $item){
+            if(strtolower($item->name) == $name){
+                return $item;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve once course description type from its id.
+     * 
+     * @param int $id
+     * @return \CourseDescription\CourseDescriptionType 
+     */
+    public function get($id)
+    {
+        return self::find_one_by_id($id);
+    }
+
+}

+ 88 - 0
main/course_description/course_import.class.php

@@ -0,0 +1,88 @@
+<?php
+
+namespace CourseDescription;
+
+/**
+ * Import course descriptions into a course/session.
+ * 
+ * @license /licence.txt
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class CourseImport
+{
+
+    protected $course = false;
+    protected $update_existing_entries = false;
+    protected $objects_imported = 0;
+    protected $objects_skipped = 0;
+
+    public function __construct($course)
+    {
+        $this->course = $course;
+    }
+
+    public function get_course()
+    {
+        return $this->course;
+    }
+
+    public function get_objects_imported()
+    {
+        return $this->objects_imported;
+    }
+
+    public function get_objects_skipped()
+    {
+        return $this->objects_skipped;
+    }
+
+    /**
+     *
+     * @param array $descriptions 
+     */
+    public function add($descriptions)
+    {
+        $this->objects_imported = 0;
+        $this->objects_skipped = 0;
+
+        foreach ($descriptions as $description) {
+            $title = $description->title;
+            $content = $description->content;
+            $type = $description->type;
+            if (empty($type)) {
+                $type = CourseDescriptionType::repository()->find_one_by_name('general');
+                $description->description_type = $type->id;
+            }
+
+            if (empty($title) || empty($content)) {
+                $this->objects_skipped++;
+                continue;
+            }
+
+//            $description = $this->find_by_title($title);
+//            if ($description && $this->update_existing_entries == false) {
+//                $this->objects_skipped++;
+//                continue;
+//            }
+            $description->c_id = $this->course->c_id;
+            $description->session_id = $this->course->session_id;
+            $repo = CourseDescription::repository();
+            $success = $repo->save($description);
+            if ($success) {
+                $this->objects_imported++;
+            } else {
+                $this->objects_skipped++;
+            }
+        }
+    }
+
+    function find_by_title($title)
+    {
+        $c_id = $this->c_id;
+        $session_id = $this->session_id;
+        $repo = CourseDescriptionRepository::instance();
+        $link = $repo->find_one_by_course_and_title($c_id, $session_id, $title);
+        return $link;
+    }
+
+}

+ 106 - 0
main/course_description/csv_reader.class.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace CourseDescription;
+
+/**
+ * Read a csv file and returns course descriptions contained in the file.
+ *
+ * @license /licence.txt
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class CsvReader implements \Iterator
+{
+
+    protected $path;
+    protected $items = null;
+    protected $index = 0;
+
+    public function __construct($path)
+    {
+        $this->path = $path;
+    }
+
+    public function get_path()
+    {
+        return $this->path;
+    }
+
+    public function get_items()
+    {
+        if (is_null($this->items)) {
+            $this->items = $this->read();
+        }
+        return $this->items;
+    }
+
+    /**
+     * Read file and returns an array filled up with its' content.
+     * 
+     * @return array of objects
+     */
+    protected function read()
+    {
+        $result = array();
+
+        $path = $this->path;
+        if (!is_readable($path)) {
+            return array();
+        }
+
+        $items = \Import::csv_reader($path);
+        foreach ($items as $item) {
+            $item = (object) $item;
+            $title = isset($item->title) ? trim($item->title) : '';
+            $content = isset($item->content) ? trim($item->content) : '';
+            $type = isset($item->type) ? trim($item->type) : '';
+
+            $title = \Security::remove_XSS($title);
+            $content = \Security::remove_XSS($content);
+            $type = \Security::remove_XSS($type);
+
+            $is_blank_line = empty($title) && empty($content) && empty($type);
+            if ($is_blank_line) {
+                continue;
+            }
+
+            $type = CourseDescriptionType::repository()->find_one_by_name($type);
+            $type_id = $type ? $type->id : 0;
+
+            $description = CourseDescription::create();
+            $description->title = $title;
+            $description->content = $content;
+            $description->description_type = $type_id;
+
+            $result[] = $description;
+        }
+        return $result;
+    }
+
+    public function current()
+    {
+        $items = $this->get_items();
+        return isset($items[$this->index]) ? $items[$this->index] : null;
+    }
+
+    public function key()
+    {
+        return $this->index;
+    }
+
+    public function next()
+    {
+        $this->index++;
+    }
+
+    public function rewind()
+    {
+        $this->index = 0;
+    }
+
+    public function valid()
+    {
+        $items = $this->get_items();
+        return count($items) > $this->index;
+    }
+
+}

+ 103 - 0
main/course_description/csv_writer.class.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace CourseDescription;
+
+use Chamilo;
+
+/**
+ * Write course descriptions to a file in CSV format.
+ * 
+ * @license /licence.txt
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class CsvWriter
+{
+
+    /**
+     *
+     * @return \CourseDescription\CsvWriter
+     */
+    public static function create($path = '')
+    {
+        return new self($path);
+    }
+
+    protected $path;
+    protected $writer;
+    protected $headers_written = false;
+
+    function __construct($path = '')
+    {
+        $path = $path ? $path : Chamilo::temp_file();
+        $this->path = $path;
+    }
+
+    public function get_path()
+    {
+        return $this->path;
+    }
+
+    /**
+     *
+     * @param array $descriptions 
+     */
+    public function add_all($descriptions)
+    {
+        foreach ($descriptions as $description) {
+            $this->add($description);
+        }
+    }
+
+    /**
+     *
+     * @param array|CourseDescription $description
+     */
+    public function add($description)
+    {
+        if (is_array($description)) {
+            $this->add_all($description);
+            return;
+        }
+        $this->writer_headers();
+        $data = array();
+        $data[] = $description->title;
+        $data[] = $description->content;
+        $data[] = $description->type->name;
+        $this->put($data);
+    }
+
+    /**
+     *
+     * @return \CsvWriter
+     */
+    protected function get_writer()
+    {
+        if ($this->writer) {
+            return $this->writer;
+        }
+
+        $writer = \CsvWriter::create(new \FileWriter($this->path));
+        $this->writer = $writer;
+        return $writer;
+    }
+
+    protected function writer_headers()
+    {
+        if ($this->headers_written) {
+            return;
+        }
+        $this->headers_written = true;
+        $headers = array();
+        $headers[] = 'title';
+        $headers[] = 'content';
+        $headers[] = 'type';
+        $this->put($headers);
+    }
+
+    protected function put($data)
+    {
+        $writer = $this->get_writer();
+        $writer->put($data);
+    }
+
+}

+ 124 - 0
main/course_description/request.class.php

@@ -0,0 +1,124 @@
+<?php
+
+namespace CourseDescription;
+
+/**
+ * Html request for course description.
+ * 
+ * @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Genevas
+ * @license /license.txt
+ */
+class Request extends \Request
+{
+
+    const PARAM_ID = 'id';
+    const PARAM_IDS = 'ids';
+    const PARAM_C_ID = 'c_id';
+    const PARAM_SESSION_ID = 'id_session';
+    const PARAM_ACTION = 'action';
+    const PARAM_SEC_TOKEN = 'sec_token';
+    const PARAM_IS_STUDENT_VIEW = 'isStudentView';
+    const PARAM_DESCRIPTION_TYPE = 'description_type';
+
+    /**
+     * Action to perform.      * 
+     * @return string
+     */
+    public static function get_action()
+    {
+        $result = Request::get(self::PARAM_ACTION, '');
+        return $result;
+    }
+
+    /**
+     * Returns the object id. 
+     * 
+     * @return int
+     */
+    public static function get_id()
+    {
+        $result = \Request::get(self::PARAM_ID, 0);
+        $result = intval($result);
+        return $result;
+    }
+
+    /**
+     * List of objet ids
+     * 
+     * @return array 
+     */
+    public static function get_ids()
+    {
+        $result = Request::get(self::PARAM_IDS, array());
+        if (is_array($result)) {
+            return $result;
+        }
+
+        $result = trim($result);
+        if (empty($result)) {
+            return array();
+        }
+
+        $result = explode(',', $result);
+        return $result;
+    }
+
+    /**
+     * Returns the course id. 
+     * 
+     * @return int
+     */
+    public static function get_c_id()
+    {
+        $result = Request::get(self::PARAM_C_ID, 0);
+        $result = intval($result);
+        $result = $result ? $result : api_get_real_course_id();
+        $result = $result ? $result : 0;
+        return $result;
+    }
+
+    /**
+     * Returns the session id. 
+     * 
+     * @return int
+     */
+    public static function get_session_id()
+    {
+        $result = Request::get(self::PARAM_SESSION_ID, 0);
+        $result = intval($result);
+        return $result;
+    }
+
+    /**
+     * Returns the security token. 
+     * 
+     * @return string
+     */
+    public static function get_security_token()
+    {
+        $result = Request::get(self::PARAM_SEC_TOKEN, 0);
+        return $result;
+    }
+
+    /**
+     * Returns true if the user is in "student view". False otherwise. 
+     * 
+     * @return bool
+     */
+    public static function is_student_view()
+    {
+        return Request::get(self::PARAM_IS_STUDENT_VIEW, false) == 'true';
+    }
+
+    /**
+     * Returns the description type
+     * 
+     * @return string
+     */
+    public static function get_description_type()
+    {
+        $result = Request::get(self::PARAM_DESCRIPTION_TYPE, '');
+        return $result;
+    }
+
+}

+ 44 - 0
main/course_description/resources/js/main.js

@@ -0,0 +1,44 @@
+
+function Proxy() {};
+
+Proxy.prototype.root = function(){
+    return www + '/main/inc/ajax/course_description.ajax.php';
+}
+
+Proxy.prototype.post = function(data, f){    
+    if(typeof(sec_token)!=='undefined'){  
+        data.sec_token = sec_token;
+    }
+    $.post(this.root(), data, f, 'json');
+}
+
+
+var CourseDescription = new Proxy();
+
+CourseDescription.del = function(c_id, id, f)
+{
+    var data = {
+        c_id: c_id, 
+        id: id, 
+        action: 'delete'
+    };
+    this.post(data, f);
+};
+
+CourseDescription.delete_by_course = function(c_id, session_id, f)
+{
+    var data = {
+        c_id: c_id, 
+        session_id: session_id, 
+        action: 'delete_by_course'
+    };
+    this.post(data, f);
+};
+
+
+var message = {};
+
+message.update = function(data){
+    text = typeof(data)=='string' ? data : data.message;
+    $('#messages').html(text)
+}

+ 79 - 0
main/course_description/upload_file_form.class.php

@@ -0,0 +1,79 @@
+<?php
+
+namespace CourseDescription;
+
+use Chamilo;
+
+/**
+ * Form to upload a file.
+ * 
+ * @license /licence.txt
+ * @author Laurent Opprecht <laurent@opprecht.info>
+ */
+class UploadFileForm extends \FormValidator
+{
+
+    function __construct($form_name = 'upload_file', $method = 'post', $action = '', $target = '', $attributes = null, $track_submit = true)
+    {
+        parent::__construct($form_name, $method, $action, $target, $attributes, $track_submit);
+    }
+
+    /**
+     *
+     * 
+     */
+    function init()
+    {
+        $form_name = get_lang('UploadFile');
+        $this->add_header($form_name);
+
+        $label = get_lang('File');
+        $this->add_file('file', $label);
+        $this->addRule('file', get_lang('ThisFieldIsRequired'), 'required');
+        //$this->add_checkbox('replace', '', get_lang('ReplaceExistingEntries'));
+
+        $this->add_button('save', get_lang('Save'), array('class' => 'btn save'));
+
+//        $label = get_lang('CSVMustLookLike');
+//        $label = "<h4>$label</h4>";
+//        $help = '<pre>
+//                    <strong>"url"</strong>;"title";"description";"target";"category_title";"category_description"
+//                    "http://chamilo.org";"Chamilo";"";"_self";"";""
+//                    "http://google.com";"Google";"";"_self";"Google";""
+//                    "http://mail.google.com";"Google";"";"_self";"Google";""
+//                    </pre>';
+//
+//        $this->add_html($label . $help);
+    }
+
+    /**
+     *
+     * @return object
+     */
+    public function get_file()
+    {
+        $result = Request::file('file', array());
+        if (empty($result)) {
+            return null;
+        }
+        $error = isset($result['error']) ? (bool) $result['error'] : false;
+        if ($error) {
+            return array();
+        }
+        return (object)$result;
+    }
+
+    public function validate()
+    {
+        $result = (bool) parent::validate();
+        if ($result == false) {
+            return false;
+        }
+        $file = $this->get_file();
+        if (empty($file)) {
+            return false;
+        }
+        return true;
+    }
+
+}