From e88b0b492667e94c45a37aca13f0f7440eea03b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Sat, 11 Jul 2020 18:30:35 +0200
Subject: [PATCH 01/16] Moving learning path creation code to ORM classes -
 refs BT#17453

---
 main/lp/learnpath.class.php                   | 434 +++++-------------
 src/Chamilo/CoreBundle/Entity/AccessUrl.php   |  10 +-
 .../CoreBundle/Entity/AccessUrlRelCourse.php  |  12 +-
 src/Chamilo/CoreBundle/Entity/Course.php      | 318 ++++++++++++-
 .../Entity/Listener/CourseListener.php        |  40 --
 src/Chamilo/CoreBundle/Entity/Session.php     |  46 +-
 src/Chamilo/CourseBundle/Entity/CDocument.php | 183 ++++++++
 .../CourseBundle/Entity/CForumForum.php       |  65 +++
 src/Chamilo/CourseBundle/Entity/CLink.php     |  65 +++
 src/Chamilo/CourseBundle/Entity/CLp.php       | 254 ++++++++++
 .../CourseBundle/Entity/CLpCategory.php       |  68 ++-
 src/Chamilo/CourseBundle/Entity/CLpItem.php   | 250 ++++++++++
 src/Chamilo/CourseBundle/Entity/CQuiz.php     |  91 +++-
 .../CourseBundle/Entity/CQuizRelQuestion.php  |  26 ++
 src/Chamilo/CourseBundle/Entity/CTool.php     |  30 ++
 15 files changed, 1524 insertions(+), 368 deletions(-)
 delete mode 100644 src/Chamilo/CoreBundle/Entity/Listener/CourseListener.php

diff --git a/main/lp/learnpath.class.php b/main/lp/learnpath.class.php
index 0348ed919a1..32dd56b07e0 100755
--- a/main/lp/learnpath.class.php
+++ b/main/lp/learnpath.class.php
@@ -508,215 +508,91 @@ public function add_item(
         $previous = (int) $previous;
         $id = (int) $id;
         $max_time_allowed = htmlentities($max_time_allowed);
-        if (empty($max_time_allowed)) {
-            $max_time_allowed = 0;
-        }
-        $sql = "SELECT COUNT(iid) AS num
-                FROM $tbl_lp_item
-                WHERE
-                    c_id = $course_id AND
-                    lp_id = ".$this->get_id()." AND
-                    parent_item_id = $parent ";
-
-        $res_count = Database::query($sql);
-        $row = Database::fetch_array($res_count);
-        $num = $row['num'];
-
-        $tmp_previous = 0;
-        $display_order = 0;
-        $next = 0;
-        if ($num > 0) {
-            if (empty($previous)) {
-                $sql = "SELECT iid, next_item_id, display_order
-                        FROM $tbl_lp_item
-                        WHERE
-                            c_id = $course_id AND
-                            lp_id = ".$this->get_id()." AND
-                            parent_item_id = $parent AND
-                            previous_item_id = 0 OR
-                            previous_item_id = $parent";
-                $result = Database::query($sql);
-                $row = Database::fetch_array($result);
-                if ($row) {
-                    $next = $row['iid'];
-                }
-            } else {
-                $previous = (int) $previous;
-                $sql = "SELECT iid, previous_item_id, next_item_id, display_order
-						FROM $tbl_lp_item
-                        WHERE
-                            c_id = $course_id AND
-                            lp_id = ".$this->get_id()." AND
-                            id = $previous";
-                $result = Database::query($sql);
-                $row = Database::fetch_array($result);
-                if ($row) {
-                    $tmp_previous = $row['iid'];
-                    $next = $row['next_item_id'];
-                    $display_order = $row['display_order'];
-                }
-            }
-        }
-
-        $id = (int) $id;
-        $typeCleaned = Database::escape_string($type);
-        $max_score = 100;
-        if ($type === 'quiz' && $id) {
-            $sql = 'SELECT SUM(ponderation)
-                    FROM '.Database::get_course_table(TABLE_QUIZ_QUESTION).' as quiz_question
-                    INNER JOIN '.Database::get_course_table(TABLE_QUIZ_TEST_QUESTION).' as quiz_rel_question
-                    ON
-                        quiz_question.id = quiz_rel_question.question_id AND
-                        quiz_question.c_id = quiz_rel_question.c_id
-                    WHERE
-                        quiz_rel_question.exercice_id = '.$id." AND
-                        quiz_question.c_id = $course_id AND
-                        quiz_rel_question.c_id = $course_id ";
-            $rsQuiz = Database::query($sql);
-            $max_score = Database::result($rsQuiz, 0, 0);
-
-            // Disabling the exercise if we add it inside a LP
-            $exercise = new Exercise($course_id);
-            $exercise->read($id);
-            $exercise->disable();
-            $exercise->save();
-        }
-
-        $params = [
-            'c_id' => $course_id,
-            'lp_id' => $this->get_id(),
-            'item_type' => $typeCleaned,
-            'ref' => '',
-            'title' => $title,
-            'description' => $description,
-            'path' => $id,
-            'max_score' => $max_score,
-            'parent_item_id' => $parent,
-            'previous_item_id' => $previous,
-            'next_item_id' => (int) $next,
-            'display_order' => $display_order + 1,
-            'prerequisite' => $prerequisites,
-            'max_time_allowed' => $max_time_allowed,
-            'min_score' => 0,
-            'launch_data' => '',
-        ];
-
-        if ($prerequisites != 0) {
-            $params['prerequisite'] = $prerequisites;
-        }
-
-        $new_item_id = Database::insert($tbl_lp_item, $params);
-        if ($new_item_id) {
-            $sql = "UPDATE $tbl_lp_item SET id = iid WHERE iid = $new_item_id";
-            Database::query($sql);
-
-            if (!empty($next)) {
-                $sql = "UPDATE $tbl_lp_item
-                        SET previous_item_id = $new_item_id
-                        WHERE c_id = $course_id AND id = $next AND item_type != '".TOOL_LP_FINAL_ITEM."'";
-                Database::query($sql);
-            }
-
-            // Update the item that should be before the new item.
-            if (!empty($tmp_previous)) {
-                $sql = "UPDATE $tbl_lp_item
-                        SET next_item_id = $new_item_id
-                        WHERE c_id = $course_id AND id = $tmp_previous";
-                Database::query($sql);
-            }
-
-            // Update all the items after the new item.
-            $sql = "UPDATE $tbl_lp_item
-                        SET display_order = display_order + 1
-                    WHERE
-                        c_id = $course_id AND
-                        lp_id = ".$this->get_id()." AND
-                        iid <> $new_item_id AND
-                        parent_item_id = $parent AND
-                        display_order > $display_order";
-            Database::query($sql);
-
-            // Update the item that should come after the new item.
-            $sql = "UPDATE $tbl_lp_item
-                    SET ref = $new_item_id
-                    WHERE c_id = $course_id AND iid = $new_item_id";
-            Database::query($sql);
-
-            $sql = "UPDATE $tbl_lp_item
-                    SET previous_item_id = ".$this->getLastInFirstLevel()."
-                    WHERE c_id = $course_id AND lp_id = {$this->lp_id} AND item_type = '".TOOL_LP_FINAL_ITEM."'";
-            Database::query($sql);
-
-            // Upload audio.
-            if (!empty($_FILES['mp3']['name'])) {
-                // Create the audio folder if it does not exist yet.
-                $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
-                if (!is_dir($filepath.'audio')) {
-                    mkdir(
-                        $filepath.'audio',
-                        api_get_permissions_for_new_directories()
-                    );
-                    $audio_id = add_document(
-                        $_course,
-                        '/audio',
-                        'folder',
-                        0,
-                        'audio',
-                        '',
-                        0,
-                        true,
-                        null,
-                        $sessionId,
-                        $userId
-                    );
-                    api_item_property_update(
-                        $_course,
-                        TOOL_DOCUMENT,
-                        $audio_id,
-                        'FolderCreated',
-                        $userId,
-                        null,
-                        null,
-                        null,
-                        null,
-                        $sessionId
-                    );
-                    api_item_property_update(
-                        $_course,
-                        TOOL_DOCUMENT,
-                        $audio_id,
-                        'invisible',
-                        $userId,
-                        null,
-                        null,
-                        null,
-                        null,
-                        $sessionId
-                    );
-                }
-
-                $file_path = handle_uploaded_document(
+        $course = \Chamilo\CoreBundle\Entity\Course::getRepository()->find($course_id);
+        $learningPath = CLp::getRepository()->find($this->get_id());
+        $item = (new CLpItem())
+            ->setCourse($course)
+            ->setLearningPath($learningPath)
+            ->setItemType($type)
+            ->setTitle($title)
+            ->setDescription($description)
+            ->setPath($id)
+            ->setParentItemId($parent)
+            ->setPrerequisite($prerequisites)
+            ->setMaxTimeAllowed($max_time_allowed)
+        ;
+
+        // Upload audio.
+        if (!empty($_FILES['mp3']['name'])) {
+            // Create the audio folder if it does not exist yet.
+            $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
+            if (!is_dir($filepath.'audio')) {
+                mkdir(
+                    $filepath.'audio',
+                    api_get_permissions_for_new_directories()
+                );
+                $audio_id = add_document(
                     $_course,
-                    $_FILES['mp3'],
-                    api_get_path(SYS_COURSE_PATH).$_course['path'].'/document',
                     '/audio',
-                    $userId,
-                    '',
-                    '',
-                    '',
+                    'folder',
+                    0,
+                    'audio',
                     '',
-                    false
+                    0,
+                    true,
+                    null,
+                    $sessionId,
+                    $userId
+                );
+                api_item_property_update(
+                    $_course,
+                    TOOL_DOCUMENT,
+                    $audio_id,
+                    'FolderCreated',
+                    $userId,
+                    null,
+                    null,
+                    null,
+                    null,
+                    $sessionId
+                );
+                api_item_property_update(
+                    $_course,
+                    TOOL_DOCUMENT,
+                    $audio_id,
+                    'invisible',
+                    $userId,
+                    null,
+                    null,
+                    null,
+                    null,
+                    $sessionId
                 );
-
-                // Store the mp3 file in the lp_item table.
-                $sql = "UPDATE $tbl_lp_item SET
-                          audio = '".Database::escape_string($file_path)."'
-                        WHERE iid = '".intval($new_item_id)."'";
-                Database::query($sql);
             }
+
+            $file_path = handle_uploaded_document(
+                $_course,
+                $_FILES['mp3'],
+                api_get_path(SYS_COURSE_PATH).$_course['path'].'/document',
+                '/audio',
+                $userId,
+                '',
+                '',
+                '',
+                '',
+                false
+            );
+
+            // Store the mp3 file in the lp_item table.
+            $item->setAudio($file_path);
         }
 
-        return $new_item_id;
+        \Database::getManager()->persist($item);
+        \Database::getManager()->flush($item);
+
+        $learningPath->updateFinalItemsPreviousItemId();
+
+        return $item->getId();
     }
 
     /**
@@ -749,62 +625,16 @@ public static function add_lp(
     ) {
         global $charset;
 
-        if (!empty($courseCode)) {
-            $courseInfo = api_get_course_info($courseCode);
-            $course_id = $courseInfo['real_id'];
-        } else {
-            $course_id = api_get_course_int_id();
-            $courseInfo = api_get_course_info();
-        }
+        $courseInfo = api_get_course_info($courseCode);
+        $course_id = $courseInfo['real_id'];
 
-        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
-        // Check course code exists.
-        // Check lp_name doesn't exist, otherwise append something.
-        $i = 0;
-        $categoryId = (int) $categoryId;
-        // Session id.
         $session_id = api_get_session_id();
         $userId = empty($userId) ? api_get_user_id() : $userId;
 
-        if (empty($publicated_on)) {
-            $publicated_on = null;
-        } else {
-            $publicated_on = Database::escape_string(api_get_utc_datetime($publicated_on));
-        }
+        $publicated_on = api_get_utc_datetime($publicated_on, true, true);
+        $expired_on = api_get_utc_datetime($expired_on, true, true);
 
-        if (empty($expired_on)) {
-            $expired_on = null;
-        } else {
-            $expired_on = Database::escape_string(api_get_utc_datetime($expired_on));
-        }
-
-        $check_name = "SELECT * FROM $tbl_lp
-                       WHERE c_id = $course_id AND name = '".Database::escape_string($name)."'";
-        $res_name = Database::query($check_name);
-
-        while (Database::num_rows($res_name)) {
-            // There is already one such name, update the current one a bit.
-            $i++;
-            $name = $name.' - '.$i;
-            $check_name = "SELECT * FROM $tbl_lp
-                           WHERE c_id = $course_id AND name = '".Database::escape_string($name)."' ";
-            $res_name = Database::query($check_name);
-        }
-        // New name does not exist yet; keep it.
-        // Escape description.
-        // Kevin: added htmlentities().
-        $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset));
-        $type = 1;
-        switch ($learnpath) {
-            case 'guess':
-                break;
-            case 'dokeos':
-            case 'chamilo':
-                $type = 1;
-                break;
-            case 'aicc':
-                break;
-        }
+        $description = api_htmlentities($description, ENT_QUOTES, $charset);
 
         switch ($origin) {
             case 'zip':
@@ -812,74 +642,38 @@ public static function add_lp(
                 break;
             case 'manual':
             default:
-                $get_max = "SELECT MAX(display_order)
-                            FROM $tbl_lp WHERE c_id = $course_id";
-                $res_max = Database::query($get_max);
-                if (Database::num_rows($res_max) < 1) {
-                    $dsp = 1;
-                } else {
-                    $row = Database::fetch_array($res_max);
-                    $dsp = $row[0] + 1;
-                }
-
-                $params = [
-                    'c_id' => $course_id,
-                    'lp_type' => $type,
-                    'name' => $name,
-                    'description' => $description,
-                    'path' => '',
-                    'default_view_mod' => 'embedded',
-                    'default_encoding' => 'UTF-8',
-                    'display_order' => $dsp,
-                    'content_maker' => 'Chamilo',
-                    'content_local' => 'local',
-                    'js_lib' => '',
-                    'session_id' => $session_id,
-                    'created_on' => api_get_utc_datetime(),
-                    'modified_on' => api_get_utc_datetime(),
-                    'publicated_on' => $publicated_on,
-                    'expired_on' => $expired_on,
-                    'category_id' => $categoryId,
-                    'force_commit' => 0,
-                    'content_license' => '',
-                    'debug' => 0,
-                    'theme' => '',
-                    'preview_image' => '',
-                    'author' => '',
-                    'prerequisite' => 0,
-                    'hide_toc_frame' => 0,
-                    'seriousgame_mode' => 0,
-                    'autolaunch' => 0,
-                    'max_attempts' => 0,
-                    'subscribe_users' => 0,
-                    'accumulate_scorm_time' => 1,
-                ];
-                $id = Database::insert($tbl_lp, $params);
-
-                if ($id > 0) {
-                    $sql = "UPDATE $tbl_lp SET id = iid WHERE iid = $id";
-                    Database::query($sql);
-
-                    // Insert into item_property.
-                    api_item_property_update(
-                        $courseInfo,
-                        TOOL_LEARNPATH,
-                        $id,
-                        'LearnpathAdded',
-                        $userId
-                    );
-                    api_set_default_visibility(
-                        $id,
-                        TOOL_LEARNPATH,
-                        0,
-                        $courseInfo,
-                        $session_id,
-                        $userId
-                    );
+                $course = \Chamilo\CoreBundle\Entity\Course::getRepository()->find($course_id);
+                $learningPath = (new CLp())
+                    ->setCourse($course)
+                    ->setName($name)
+                    ->setDescription($description)
+                    ->setPublicatedOn($publicated_on)
+                    ->setExpiredOn($expired_on)
+                    ->setCategoryId($categoryId)
+                ;
+                Database::getManager()->persist($learningPath);
+                Database::getManager()->flush();
+
+                $id = $learningPath->getId();
+
+                // Insert into item_property.
+                api_item_property_update(
+                    $courseInfo,
+                    TOOL_LEARNPATH,
+                    $id,
+                    'LearnpathAdded',
+                    $userId
+                );
+                api_set_default_visibility(
+                    $id,
+                    TOOL_LEARNPATH,
+                    0,
+                    $courseInfo,
+                    $session_id,
+                    $userId
+                );
 
-                    return $id;
-                }
-                break;
+                return $id;
         }
     }
 
diff --git a/src/Chamilo/CoreBundle/Entity/AccessUrl.php b/src/Chamilo/CoreBundle/Entity/AccessUrl.php
index c8b4b3115fc..85b9b68a1bf 100644
--- a/src/Chamilo/CoreBundle/Entity/AccessUrl.php
+++ b/src/Chamilo/CoreBundle/Entity/AccessUrl.php
@@ -25,7 +25,7 @@ class AccessUrl
     /**
      * @ORM\OneToMany(targetEntity="AccessUrlRelCourse", mappedBy="url", cascade={"persist"}, orphanRemoval=true)
      */
-    protected $course;
+    protected $courses;
 
     /**
      * @var string
@@ -88,6 +88,14 @@ public function __construct()
         $this->createdBy = 1;
     }
 
+    /**
+     * @return Repository\AccessUrlRepository|\Doctrine\ORM\EntityRepository
+     */
+    public static function getRepository()
+    {
+        return \Database::getManager()->getRepository('ChamiloCoreBundle:AccessUrl');
+    }
+
     /**
      * @return string
      */
diff --git a/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php b/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
index a3965fb18b8..c21371dacd2 100644
--- a/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
+++ b/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
@@ -23,13 +23,21 @@ class AccessUrlRelCourse
     protected $id;
 
     /**
-     * @ORM\ManyToOne(targetEntity="Course", inversedBy="urls", cascade={"persist"})
+     * @ORM\ManyToOne(
+     *     targetEntity="Chamilo\CoreBundle\Entity\Course",
+     *     inversedBy="urls",
+     *     cascade={"persist", "remove"}
+     * )
      * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
      */
     protected $course;
 
     /**
-     * @ORM\ManyToOne(targetEntity="AccessUrl", inversedBy="course", cascade={"persist"})
+     * @ORM\ManyToOne(
+     *     targetEntity="Chamilo\CoreBundle\Entity\AccessUrl",
+     *     inversedBy="courses",
+     *     cascade={"persist", "remove"}
+     * )
      * @ORM\JoinColumn(name="access_url_id", referencedColumnName="id")
      */
     protected $url;
diff --git a/src/Chamilo/CoreBundle/Entity/Course.php b/src/Chamilo/CoreBundle/Entity/Course.php
index bda70884468..9cf7c66811f 100644
--- a/src/Chamilo/CoreBundle/Entity/Course.php
+++ b/src/Chamilo/CoreBundle/Entity/Course.php
@@ -3,11 +3,24 @@
 
 namespace Chamilo\CoreBundle\Entity;
 
+use AddCourse;
+use Chamilo\CourseBundle\Entity\CDocument;
+use Chamilo\CourseBundle\Entity\CForumForum;
+use Chamilo\CourseBundle\Entity\CLink;
+use Chamilo\CourseBundle\Entity\CLp;
+use Chamilo\CourseBundle\Entity\CLpCategory;
+use Chamilo\CourseBundle\Entity\CLpItem;
+use Chamilo\CourseBundle\Entity\CQuiz;
 use Chamilo\CourseBundle\Entity\CTool;
 use Chamilo\UserBundle\Entity\User;
+use CourseManager;
+use Database;
+use DateInterval;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Criteria;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Exception;
 use Gedmo\Mapping\Annotation as Gedmo;
 use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
 use Symfony\Component\Validator\Constraints as Assert;
@@ -27,7 +40,6 @@
  * @UniqueEntity("visualCode")
  * @UniqueEntity("directory")
  * @ORM\Entity(repositoryClass="Chamilo\CoreBundle\Entity\Repository\CourseRepository")
- * @ORM\EntityListeners({"Chamilo\CoreBundle\Entity\Listener\CourseListener"})
  */
 class Course
 {
@@ -282,6 +294,92 @@ class Course
      */
     protected $room;
 
+    /**
+     * @ORM\ManyToMany(targetEntity="AccessUrl")
+     * @ORM\JoinTable(name="access_url_rel_course",
+     *      joinColumns={@ORM\JoinColumn(name="c_id", referencedColumnName="id")},
+     *      inverseJoinColumns={@ORM\JoinColumn(name="access_url_id", referencedColumnName="id")}
+     * )
+     */
+    protected $accessUrls;
+
+    /**
+     * @var ArrayCollection|CLpCategory[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLpCategory",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     * )
+     */
+    protected $learningPathCategories;
+
+    /**
+     * @var ArrayCollection|CLpItem[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLpItem",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     * )
+     */
+    protected $learningPathItems;
+
+    /**
+     * @var ArrayCollection|CDocument[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CDocument",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     * )
+     */
+    protected $documents;
+
+    /**
+     * @var ArrayCollection|CForumForum[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CForumForum",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     *     )
+     */
+    protected $forums;
+
+    /**
+     * @var ArrayCollection|CLink[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLink",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     *     )
+     */
+    protected $links;
+
+    /**
+     * @var ArrayCollection|CQuiz[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CQuiz",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     *     )
+     */
+    protected $quizzes;
+
+    /**
+     * @var ArrayCollection|CLp[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLp",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     *     )
+     */
+    protected $learningPaths;
+
     /**
      * Constructor.
      */
@@ -289,6 +387,127 @@ public function __construct()
     {
         $this->creationDate = new \DateTime();
         $this->users = new ArrayCollection();
+        $this->accessUrls = new ArrayCollection();
+        $this->documents = new ArrayCollection();
+        $this->forums = new ArrayCollection();
+        $this->links = new ArrayCollection();
+        $this->quizzes = new ArrayCollection();
+        $this->learningPathCategories = new ArrayCollection();
+        $this->learningPaths = new ArrayCollection();
+        $this->learningPathItems = new ArrayCollection();
+    }
+
+    /**
+     * @return Repository\CourseRepository|EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCoreBundle:Course');
+    }
+
+    /**
+     * @return Course|null
+     */
+    public static function getCurrentCourse()
+    {
+        return self::getRepository()->find(api_get_course_int_id());
+    }
+
+    /**
+     * Sets sane values if still unset.
+     * Makes directory if missing.
+     *
+     * @ORM\PrePersist
+     *
+     * @throws Exception
+     */
+    public function prePersist()
+    {
+        if (empty($this->title)) {
+            throw new Exception('This course is missing a title');
+        }
+        if (empty($this->code)) {
+            $this->code = substr(
+                preg_replace('/[^A-Z0-9]/', '', strtoupper(api_replace_dangerous_char($this->title))),
+                0,
+                CourseManager::MAX_COURSE_LENGTH_CODE
+            );
+        }
+        if (empty($this->directory) || empty($this->visualCode)) {
+            $keys = AddCourse::define_course_keys($this->code, '');
+            if (!count($keys)) {
+                throw new Exception('Could not define course keys');
+            }
+            if (empty($this->directory)) {
+                $this->directory = $keys['currentCourseRepository'];
+            }
+            if (empty($this->visualCode)) {
+                $this->visualCode = $keys['currentCourseCode'];
+            }
+        }
+        if (is_null($this->courseLanguage)) {
+            $this->courseLanguage = api_get_setting('platformLanguage');
+        }
+        if (is_null($this->description)) {
+            $this->description = get_lang('CourseDescription');
+        }
+        if (is_null($this->categoryCode)) {
+            $this->categoryCode = '';
+        }
+        if (is_null($this->visibility)) {
+            $this->visibility = api_get_setting(
+                'courses_default_creation_visibility'
+            ) ?: COURSE_VISIBILITY_OPEN_PLATFORM;
+        }
+        if (is_null($this->showScore)) {
+            $this->showScore = 1;
+        }
+        if (is_null($this->diskQuota)) {
+            $this->diskQuota = api_get_setting('default_document_quotum');
+        }
+        $this->lastEdit = new \DateTime();
+        if (is_null($this->expirationDate)) {
+            $this->expirationDate = new \DateTime();
+            $this->expirationDate->add(new DateInterval('P1Y'));
+        }
+        $absolutePath = $this->getAbsolutePath();
+        if (!file_exists($absolutePath)) {
+            AddCourse::prepare_course_repository($this->directory);
+        }
+        $this->accessUrls->add(AccessUrl::getRepository()->find(api_get_current_access_url_id()));
+    }
+
+    /**
+     * Removes the course's directory.
+     *
+     * @ORM\PostRemove
+     *
+     * @throws Exception
+     */
+    public function postRemove()
+    {
+        $absolutePath = $this->getAbsolutePath();
+        if (!file_exists($absolutePath)) {
+            if (!rmdir($absolutePath)) {
+                error_log('Could not remove the course directory '.$absolutePath);
+            }
+        }
+    }
+
+    /**
+     * Builds the course's directory absolute path.
+     *
+     * @throws Exception on undefined directory
+     *
+     * @return string the course's directory absolute path
+     */
+    public function getAbsolutePath()
+    {
+        if (empty($this->directory)) {
+            throw new Exception('this course does not have a directory yet');
+        }
+
+        return api_get_path(SYS_COURSE_PATH).$this->directory;
     }
 
     /**
@@ -485,11 +704,15 @@ public function addStudent(User $user)
     /**
      * Set id.
      *
-     * @return int
+     * @param int $id
+     *
+     * @return Course
      */
     public function setId($id)
     {
         $this->id = $id;
+
+        return $this;
     }
 
     /**
@@ -1148,6 +1371,97 @@ public function setCurrentSession(Session $session)
         return $this;
     }
 
+    /**
+     * @return CLpCategory[]|ArrayCollection
+     */
+    public function getLearningPathCategories()
+    {
+        return $this->learningPathCategories;
+    }
+
+    /**
+     * @return CDocument[]|ArrayCollection
+     */
+    public function getDocuments()
+    {
+        return $this->documents;
+    }
+
+    /**
+     * @return CForumForum[]|ArrayCollection
+     */
+    public function getForums()
+    {
+        return $this->forums;
+    }
+
+    /**
+     * @return CQuiz[]|ArrayCollection
+     */
+    public function getQuizzes()
+    {
+        return $this->quizzes;
+    }
+
+    /**
+     * @return CLink[]|ArrayCollection
+     */
+    public function getLinks()
+    {
+        return $this->links;
+    }
+
+    /**
+     * @return CLp[]|ArrayCollection
+     */
+    public function getLearningPaths()
+    {
+        return $this->learningPaths;
+    }
+
+    /**
+     * Searches and returns the resource of a specific type having a specific title.
+     *
+     * @param string $type  'dir', 'document', 'quiz'… (supported values are hardcoded in this function)
+     * @param string $title the title of the specific resource to find
+     *
+     * @throws Exception when not found or more than one found
+     *
+     * @return object the resource
+     */
+    public function findResource($type, $title)
+    {
+        $collectionsAndColumns = [
+            // type          collection           column
+            'document'   => [$this->documents,   'title'],
+            'final_item' => [$this->documents,   'title'],
+            'forum'      => [$this->forums, 'forumTitle'],
+            'link'       => [$this->links,       'title'],
+            'quiz'       => [$this->quizzes,     'title'],
+        ];
+        if (!array_key_exists($type, $collectionsAndColumns)) {
+            throw new Exception(sprintf('unsupported resource type "%s"', $type));
+        }
+        list($collection, $column) = $collectionsAndColumns[$type];
+        $resources = $collection->matching(Criteria::create()->where(Criteria::expr()->eq($column, $title)));
+        if (empty($resources)) {
+            throw new Exception(sprintf('%s "%s" not found', $type, $title));
+        }
+        if (count($resources) > 1) {
+            throw new Exception(sprintf('more than one %s "%s" found', $type, $title));
+        }
+
+        return $resources[0];
+    }
+
+    /**
+     * @return CLpItem[]|ArrayCollection
+     */
+    public function getLearningPathItems()
+    {
+        return $this->learningPathItems;
+    }
+
     /**
      * @return bool
      */
diff --git a/src/Chamilo/CoreBundle/Entity/Listener/CourseListener.php b/src/Chamilo/CoreBundle/Entity/Listener/CourseListener.php
deleted file mode 100644
index a72e9fca1b8..00000000000
--- a/src/Chamilo/CoreBundle/Entity/Listener/CourseListener.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-/* For licensing terms, see /license.txt */
-
-namespace Chamilo\CoreBundle\Entity\Listener;
-
-use Chamilo\CoreBundle\Entity\Course;
-use Chamilo\CoreBundle\Entity\Tool;
-use Chamilo\CourseBundle\ToolChain;
-use Doctrine\ORM\Event\LifecycleEventArgs;
-
-/**
- * Class CourseListener
- * Course entity listener, when a course is created the tool chain is loaded.
- *
- * @package Chamilo\CoreBundle\EventListener
- */
-class CourseListener
-{
-    protected $toolChain;
-
-    public function __construct(ToolChain $toolChain)
-    {
-        $this->toolChain = $toolChain;
-    }
-
-    /**
-     * new object : prePersist
-     * edited object: preUpdate.
-     */
-    public function prePersist(Course $course, LifecycleEventArgs $args)
-    {
-        //$this->toolChain->addToolsInCourse($course);
-        /*
-        error_log('ddd');
-        $course->setDescription( ' dq sdqs dqs dqs ');
-
-        $args->getEntityManager()->persist($course);
-        $args->getEntityManager()->flush();*/
-    }
-}
diff --git a/src/Chamilo/CoreBundle/Entity/Session.php b/src/Chamilo/CoreBundle/Entity/Session.php
index a3d10dc780d..a697b194c41 100644
--- a/src/Chamilo/CoreBundle/Entity/Session.php
+++ b/src/Chamilo/CoreBundle/Entity/Session.php
@@ -3,10 +3,13 @@
 
 namespace Chamilo\CoreBundle\Entity;
 
+use Chamilo\CourseBundle\Entity\CLp;
 use Chamilo\CourseBundle\Entity\CStudentPublication;
 use Chamilo\UserBundle\Entity\User;
+use Database;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Criteria;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 
 //use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
@@ -219,6 +222,17 @@ class Session
      */
     protected $studentPublications;
 
+    /**
+     * @var ArrayCollection|CLp[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLp",
+     *     mappedBy="session",
+     *     cascade={"persist", "remove"}
+     *     )
+     */
+    // protected $learningPaths;
+
     /**
      * Constructor.
      */
@@ -243,8 +257,30 @@ public function __construct()
         $this->showDescription = false;
         $this->category = null;
         $this->studentPublications = new ArrayCollection();
+        $this->sendSubscriptionNotification = 0;
     }
 
+    /**
+     * @return Repository\SessionRepository|EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCoreBundle:Session');
+    }
+
+    /**
+     * @return Session|null
+     */
+    public static function getCurrentSession()
+    {
+        $sessionId = api_get_session_id();
+        if (0 != $sessionId) {
+            return self::getRepository()->find($sessionId);
+        }
+        return null;
+    }
+
+
     /**
      * @return string
      */
@@ -419,7 +455,7 @@ public function hasCourse(Course $course)
     public function isRelatedToCourse(Course $course)
     {
         return !is_null(
-            \Database::getManager()->getRepository('ChamiloCoreBundle:SessionRelCourse')->findOneBy([
+            Database::getManager()->getRepository('ChamiloCoreBundle:SessionRelCourse')->findOneBy([
                 'session' => $this,
                 'course' => $course,
             ])
@@ -1182,4 +1218,12 @@ public function getUsersSubscriptionsInCourse(Course $course)
 
         return $this;
     }*/
+
+    /**
+     * @return CLp[]|ArrayCollection
+     */
+    /*public function getLearningPaths()
+    {
+        return $this->learningPaths;
+    }*/
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CDocument.php b/src/Chamilo/CourseBundle/Entity/CDocument.php
index f65081b386e..48d0aa21c46 100644
--- a/src/Chamilo/CourseBundle/Entity/CDocument.php
+++ b/src/Chamilo/CourseBundle/Entity/CDocument.php
@@ -3,7 +3,14 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
+use Database;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
+use Doctrine\ORM\ORMException;
+use Doctrine\ORM\TransactionRequiredException;
+use Exception;
 
 /**
  * CDocument.
@@ -15,6 +22,7 @@
  *  }
  * )
  * @ORM\Entity
+ * @ORM\HasLifecycleCallbacks
  */
 class CDocument
 {
@@ -90,6 +98,181 @@ class CDocument
      */
     protected $sessionId;
 
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="documents")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
+    public function __construct()
+    {
+        $this->size = 0;
+        $this->readonly = 0;
+        $this->sessionId = 0;
+    }
+
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CDocument');
+    }
+
+    /**
+     * Instantiates a new CDocument by copying a file to the course.
+     *
+     * @param string $filePath     the source file to be copied to the course directory
+     * @param Course $course       the course for which the document is being created
+     * @param string $documentPath the future document's relative path
+     * @param string $title        a title for the document
+     *
+     * @throws ORMException
+     * @throws OptimisticLockException
+     * @throws TransactionRequiredException
+     * @throws Exception
+     *
+     * @return CDocument
+     */
+    public static function fromFile($filePath, $course, $documentPath, $title)
+    {
+        $instance = (new static())
+            ->setCourse($course)
+            ->setPath($documentPath)
+            ->setTitle($title);
+        $absolutePath = $instance->getAbsolutePath();
+        if (!copy($filePath, $absolutePath)) {
+            throw new Exception(
+                sprintf('Could not copy %s to %s to create a course document', $filePath, $absolutePath)
+            );
+        }
+
+        return $instance;
+    }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getDocuments()->add($this);
+
+        return $this;
+    }
+
+    /**
+     * Builds the document's absolute path from its course's own path and its (relative) path.
+     *
+     * @throws OptimisticLockException
+     * @throws TransactionRequiredException
+     * @throws ORMException
+     * @throws Exception
+     *
+     * @return string the document's absolute path
+     */
+    public function getAbsolutePath()
+    {
+        if (is_null($this->course) && $this->cId) {
+            $this->course = Database::getManager()->find('ChamiloCoreBundle:Course', $this->cId);
+        }
+        if (is_null($this->course)) {
+            throw new Exception('this document does not have a course yet');
+        }
+
+        return sprintf(
+            '%s/document/%s',
+            $this->course->getAbsolutePath(),
+            $this->path
+        );
+    }
+
+    /**
+     * Makes sure the actual file exists.
+     * Records the file type.
+     * Computes document file size if needed.
+     *
+     * @ORM\PrePersist
+     *
+     * @throws Exception
+     */
+    public function prePersist()
+    {
+        $absolutePath = $this->getAbsolutePath();
+        if (!file_exists($absolutePath)) {
+            throw new Exception('Cannot persist a document without an existing file');
+        }
+        if (empty($this->filetype)) {
+            $type = filetype($absolutePath);
+            switch ($type) {
+                case 'dir':
+                    $this->filetype = 'folder';
+                    break;
+                case 'file':
+                case 'link':
+                    $this->filetype = $type;
+                    break;
+                default:
+                    throw new Exception('unsupported file type: '.$type);
+            }
+        }
+        if (0 === $this->size && 'file' == $this->filetype) {
+            $this->size = filesize($absolutePath);
+        }
+    }
+
+    /**
+     * If id is null, copies iid to id and writes again.
+     *
+     * @ORM\PostPersist
+     *
+     * @throws Exception
+     */
+    public function postPersist()
+    {
+        if (is_null($this->id)) { // keep this test to avoid recursion
+            $this->id = $this->iid;
+            Database::getManager()->persist($this);
+            Database::getManager()->flush($this);
+        }
+    }
+
+    /**
+     * Removes the actual file, folder or link.
+     *
+     * @ORM\PostRemove
+     */
+    public function postRemove()
+    {
+        $absolutePath = '';
+        try {
+            $absolutePath = $this->getAbsolutePath();
+        } catch (Exception $exception) {
+            error_log($exception->getMessage());
+        }
+        if (!empty($absolutePath) && file_exists($absolutePath)) {
+            if ('folder' === $this->filetype && is_dir($absolutePath)) {
+                rmdir($absolutePath);
+            } elseif ('file' === $this->filetype && is_file($absolutePath)) {
+                unlink($absolutePath);
+            } elseif ('link' === $this->filetype && is_link($absolutePath)) {
+                unlink($absolutePath);
+            }
+        }
+    }
+
     /**
      * Set path.
      *
diff --git a/src/Chamilo/CourseBundle/Entity/CForumForum.php b/src/Chamilo/CourseBundle/Entity/CForumForum.php
index fd95842cb1f..57279451e3a 100644
--- a/src/Chamilo/CourseBundle/Entity/CForumForum.php
+++ b/src/Chamilo/CourseBundle/Entity/CForumForum.php
@@ -3,7 +3,11 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
+use Database;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
 
 /**
  * CForumForum.
@@ -15,6 +19,7 @@
  *  }
  * )
  * @ORM\Entity
+ * @ORM\HasLifecycleCallbacks
  */
 class CForumForum
 {
@@ -195,6 +200,66 @@ class CForumForum
      */
     protected $moderated;
 
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="forums")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
+    public function __construct()
+    {
+        $this->forumId = 0;
+        $this->locked = 0;
+        $this->sessionId = 0;
+        $this->forumImage = '';
+        $this->lpId = 0;
+    }
+
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CForumForum');
+    }
+
+    /**
+     * @ORM\PostPersist
+     *
+     * @throws OptimisticLockException
+     */
+    public function postPersist()
+    {
+        if (is_null($this->forumId)) {
+            $this->forumId = $this->iid;
+            Database::getManager()->persist($this);
+            Database::getManager()->flush($this);
+        }
+    }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getForums()->add($this);
+
+        return $this;
+    }
+
     /**
      * Set forumTitle.
      *
diff --git a/src/Chamilo/CourseBundle/Entity/CLink.php b/src/Chamilo/CourseBundle/Entity/CLink.php
index 6469587428b..0228dc60417 100644
--- a/src/Chamilo/CourseBundle/Entity/CLink.php
+++ b/src/Chamilo/CourseBundle/Entity/CLink.php
@@ -3,7 +3,11 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
+use Database;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
 
 /**
  * CLink.
@@ -16,6 +20,7 @@
  *  }
  * )
  * @ORM\Entity
+ * @ORM\HasLifecycleCallbacks
  */
 class CLink
 {
@@ -98,6 +103,66 @@ class CLink
      */
     protected $sessionId;
 
+    /**
+     * @var Course
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="links")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
+    public function __construct()
+    {
+        $this->description = '';
+        $this->categoryId = 0;
+        $this->displayOrder = 0;
+        $this->onHomepage = '0';
+        $this->target = '_self';
+        $this->sessionId = 0;
+    }
+
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CLink');
+    }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getLinks()->add($this);
+
+        return $this;
+    }
+
+    /**
+     * @ORM\PostPersist
+     *
+     * @throws OptimisticLockException
+     */
+    public function postPersist()
+    {
+        if (is_null($this->id)) {
+            $this->id = $this->iid;
+            Database::getManager()->persist($this);
+            Database::getManager()->flush($this);
+        }
+    }
+
     /**
      * Set url.
      *
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index 848adbe2e88..c76525167bc 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -3,7 +3,13 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
+use Chamilo\CoreBundle\Entity\Session;
+use Database;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
 
 /**
  * CLp.
@@ -16,6 +22,7 @@
  *  }
  * )
  * @ORM\Entity
+ * @ORM\HasLifecycleCallbacks
  */
 class CLp
 {
@@ -266,20 +273,207 @@ class CLp
      */
     protected $accumulateScormTime;
 
+    /**
+     * @var Course
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="learningPaths")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
+    /**
+     * @var Session
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Session", inversedBy="learningPaths")
+     * @ORM\JoinColumn(name="session_id", referencedColumnName="id")
+     */
+    /*protected $session;*/
+
+    /**
+     * @var CLpCategory
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CLpCategory", inversedBy="learningPaths")
+     * @ORM\JoinColumn(name="category_id", referencedColumnName="iid")
+     */
+    /*protected $category;*/
+
+    /**
+     * @var ArrayCollection|CLpItem[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="CLpItem",
+     *     mappedBy="learningPath",
+     *     orphanRemoval=true,
+     *     cascade={"persist", "remove"}
+     * )
+     */
+    protected $items;
+
     /**
      * Constructor.
      */
     public function __construct()
     {
+        $this->sessionId = api_get_session_id();
+        $this->categoryId = 0;
         $this->defaultViewMod = 'embedded';
         $this->defaultEncoding = 'UTF-8';
         $this->displayOrder = 0;
         $this->contentLocal = 'local';
         $this->preventReinit = true;
         $this->useMaxScore = 1;
+        $this->lpType = 1;
+        $this->path = '';
+        $this->forceCommit = 0;
+        $this->contentMaker = 'Chamilo';
+        $this->contentLicense = '';
+        $this->jsLib = '';
+        $this->debug = 0;
+        $this->theme = '';
+        $this->previewImage = '';
+        $this->author = '';
+        $this->prerequisite = ''; // 0 ?
+        $this->hideTocFrame = 0;
+        $this->seriousgameMode = 0;
+        $this->autolaunch = 0;
+        $this->maxAttempts = 0;
+        $this->subscribeUsers = 0;
         $this->createdOn = new \DateTime();
+        $this->modifiedOn = new \DateTime();
+        $this->accumulateScormTime = 1;
+        $this->items = new ArrayCollection();
+    }
+
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CLp');
     }
 
+    /**
+     * If course is not yet set, take the current course.
+     * Appends a number to name if it is already taken by another learning path in the same course.
+     * Computes displayOrder if still zéro.
+     *
+     * @ORM\PrePersist
+     */
+    public function prePersist()
+    {
+        if (is_null($this->course)) {
+            $this->course = Course::getCurrentCourse();
+        }
+
+        $coursesOtherLearningPaths = $this->course->getLearningPaths()->filter(function ($lp) {
+            return $this !== $lp;
+        });
+
+        $originalName = $this->name;
+        $counter = 0;
+        while ($coursesOtherLearningPaths->exists(function ($key, $lp) {
+            return $lp->name === $this->name;
+        })) {
+            $counter++;
+            $this->name = sprintf('%s - %d', $originalName, $counter);
+        }
+
+        if (0 == $this->displayOrder) {
+            $this->displayOrder = $coursesOtherLearningPaths->isEmpty()
+                ? 1
+                : (
+                    1 + max(
+                        $coursesOtherLearningPaths->map(
+                            function ($lp) {
+                                return $lp->displayOrder;
+                            }
+                        )->toArray()
+                    )
+                );
+        }
+    }
+
+    /**
+     * If id is null, copies iid to id and writes again.
+     *
+     * @throws OptimisticLockException
+     *
+     * @ORM\PostPersist
+     */
+    public function postPersist()
+    {
+        if (is_null($this->id)) {
+            $this->id = $this->iid;
+            Database::getManager()->persist($this);
+            Database::getManager()->flush($this);
+        }
+    }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getLearningPaths()->add($this);
+
+        return $this;
+    }
+
+    /**
+     * @return Session
+     */
+    /*public function getSession()
+    {
+        return $this->session;
+    }*/
+
+    /**
+     * @param Session $session
+     *
+     * @return $this
+     */
+    /*public function setSession(Session $session)
+    {
+        $this->session = $session;
+        if (!is_null($session)) {
+            $this->session->getLearningPaths()->add($this);
+        }
+
+        return $this;
+    }*/
+
+    /**
+     * @return CLpCategory
+     */
+    /*public function getCategory()
+    {
+        return $this->category;
+    }*/
+
+    /**
+     * @param CLpCategory $category
+     *
+     * @return $this
+     */
+    /*public function setCategory(CLpCategory $category)
+    {
+        $this->category = $category;
+        if (!is_null($category)) {
+            $this->category->getLearningPaths()->add($this);
+        }
+
+        return $this;
+    }*/
+
     /**
      * Set lpType.
      *
@@ -1059,4 +1253,64 @@ public function getSubscribeUsers()
     {
         return $this->subscribeUsers;
     }
+
+    /**
+     * @return ArrayCollection|CLpItem[]
+     */
+    public function getItems()
+    {
+        return $this->items;
+    }
+
+    /**
+     * Returns this learning path's final item.
+     *
+     * @return CLpItem|null the final item
+     */
+    public function getFinalItem()
+    {
+        foreach ($this->items as $item) {
+            if ($item->getItemType() == TOOL_LP_FINAL_ITEM) {
+                return $item;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns this learning path's last item in first level that is not the final item.
+     *
+     * @return CLpItem|null the last item
+     */
+    public function getLastItemInFirstLevel()
+    {
+        $last = null;
+        foreach ($this->items as $item) {
+            if (0 == $item->getParentItemId() && $item->getItemType() != TOOL_LP_FINAL_ITEM) {
+                if (is_null($last) || $last->getDisplayOrder() < $item->getDisplayOrder()) {
+                    $last = $item;
+                }
+            }
+        }
+        return $last;
+    }
+
+    /**
+     * Updates this learning path's final item previous item id.
+     * Sets it to the last item in first level.
+     *
+     * @throws OptimisticLockException
+     */
+    public function updateFinalItemsPreviousItemId()
+    {
+        $finalItem = $this->getFinalItem();
+        if (!is_null($finalItem)) {
+            $last = $this->getLastItemInFirstLevel();
+            if (!is_null($last)) {
+                $finalItem->setPreviousItemId($last->getId());
+                \Database::getManager()->persist($finalItem);
+                \Database::getManager()->flush($finalItem);
+            }
+        }
+    }
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpCategory.php b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
index 6b0d3c363da..79aa2efa8ff 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpCategory.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
@@ -3,11 +3,15 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
 use Chamilo\UserBundle\Entity\User;
+use Database;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Criteria;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 use Gedmo\Mapping\Annotation as Gedmo;
+use Gedmo\Sortable\Entity\Repository\SortableRepository;
 
 /**
  * CLpCategory.
@@ -53,7 +57,12 @@ class CLpCategory
     /**
      * @var CLpCategoryUser[]
      *
-     * @ORM\OneToMany(targetEntity="Chamilo\CourseBundle\Entity\CLpCategoryUser", mappedBy="category", cascade={"persist", "remove"}, orphanRemoval=true)
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLpCategoryUser",
+     *     mappedBy="category",
+     *     cascade={"persist", "remove"},
+     *     orphanRemoval=true
+     * )
      */
     protected $users;
 
@@ -62,15 +71,64 @@ class CLpCategory
      */
     protected $sessionId;
 
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="learningPathCategories")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
+    /**
+     * @var CLp[]|ArrayCollection
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLp",
+     *     mappedBy="category",
+     *     cascade={"persist", "remove"}
+     * )
+     */
+    /*protected $learningPaths;*/
+
     /**
      * CLpCategory constructor.
      */
     public function __construct()
     {
         $this->users = new ArrayCollection();
+        /*$this->learningPaths = new ArrayCollection();*/
         $this->sessionId = 0;
     }
 
+    /**
+     * @return EntityRepository|SortableRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CLpCategory');
+    }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getLearningPathCategories()->add($this);
+
+        return $this;
+    }
+
     /**
      * Set cId.
      *
@@ -257,4 +315,12 @@ public function removeUsers(CLpCategoryUser $user)
 
         return $this;
     }
+
+    /**
+     * @return CLp[]|ArrayCollection
+     */
+    /*public function getLearningPaths()
+    {
+        return $this->learningPaths;
+    }*/
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpItem.php b/src/Chamilo/CourseBundle/Entity/CLpItem.php
index 9515cf04275..2185fee48ce 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpItem.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpItem.php
@@ -3,7 +3,12 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
+use Database;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
+use Exception;
 
 /**
  * CLpItem.
@@ -17,6 +22,7 @@
  *  }
  * )
  * @ORM\Entity
+ * @ORM\HasLifecycleCallbacks()
  */
 class CLpItem
 {
@@ -197,12 +203,164 @@ class CLpItem
      */
     protected $prerequisiteMaxScore;
 
+    /**
+     * @var CLp $learningPath
+     *
+     * @ORM\ManyToOne(targetEntity="CLp", inversedBy="items")
+     * @ORM\JoinColumn(name="lp_id", referencedColumnName="iid")
+     */
+    protected $learningPath;
+
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="learningPathItems")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
     /**
      * CLpItem constructor.
      */
     public function __construct()
     {
+        $this->ref = 0;
+        $this->minScore = 0;
         $this->maxScore = 100.0;
+        $this->parentItemId = 0;
+        $this->previousItemId = 0;
+        $this->nextItemId = 0;
+        $this->displayOrder = 0;
+        $this->launchData = '';
+        $this->path = '';
+    }
+
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CLpItem');
+    }
+
+    /**
+     * If id is null, copies iid to id.
+     * If ref is empty or zero, copies iid to ref.
+     * if they still equal zero, computes displayOrder, previousItemId and nextItemId.
+     * If pointing to an enabled quiz, disables it and updates max score.
+     *
+     * @throws OptimisticLockException
+     * @throws Exception               on quiz not found
+     *
+     * @ORM\PostPersist
+     */
+    public function postPersist()
+    {
+        if (is_null($this->id)) {
+            $this->id = $this->iid;
+        }
+        if (is_null($this->ref) || empty($this->ref) || 0 == $this->ref) {
+            $this->ref = $this->iid;
+        }
+        if (empty($this->maxTimeAllowed)) {
+            $this->maxTimeAllowed = '0';
+        }
+
+        if (0 == $this->displayOrder) {
+            foreach ($this->getSiblings() as $sibling) {
+                if ($this->displayOrder < $sibling->displayOrder) {
+                    $this->displayOrder = $sibling->displayOrder;
+                    $this->previousItemId = 0;
+                }
+            }
+            if (0 == $this->displayOrder) {
+                $this->displayOrder = 1;
+            }
+        } else {
+            foreach ($this->getSiblings() as $sibling) {
+                if ($this->displayOrder === $sibling->displayOrder) {
+                    $sibling->displayOrder ++;
+                    Database::getManager()->persist($sibling);
+                }
+            }
+        }
+        if (0 == $this->previousItemId) {
+            $previousSibling = $this->getPreviousSibling();
+            if (!is_null($previousSibling)) {
+                $this->previousItemId = $previousSibling->iid;
+                $previousSibling->nextItemId = $this->iid;
+                Database::getManager()->persist($previousSibling);
+            }
+        }
+        if (0 == $this->nextItemId) {
+            $nextSibling = $this->getNextSibling();
+            if (!is_null($nextSibling)) {
+                $this->nextItemId = $nextSibling->iid;
+                $nextSibling->previousItemId = $this->iid;
+                Database::getManager()->persist($nextSibling);
+            }
+        }
+        if ('quiz' === $this->itemType) {
+            /** @var CQuiz $quiz */
+            $quiz = CQuiz::getRepository()->find($this->path);
+            if (is_null($quiz)) {
+                throw new Exception('no quiz has id '.$this->path);
+            }
+            $this->setMaxScore($quiz->getMaxScore());
+            if ($quiz->getActive()) {
+                $quiz->setActive(false);
+                Database::getManager()->persist($quiz);
+            }
+        }
+        Database::getManager()->persist($this);
+        Database::getManager()->flush();
+
+        $this->learningPath->updateFinalItemsPreviousItemId();
+    }
+
+    /**
+     * Computes next sibling's previousItemId and previous sibling's nextItemId.
+     *
+     * @throws OptimisticLockException
+     *
+     * @ORM\PreRemove
+     */
+    public function preRemove()
+    {
+        $previousSibling = $this->getPreviousSibling();
+        $nextSibling = $this->getNextSibling();
+        if (is_null($previousSibling)) {
+            if (!is_null($nextSibling)) {
+                $nextSibling->previousItemId = 0;
+                Database::getManager()->persist($nextSibling);
+                Database::getManager()->flush($nextSibling);
+            }
+        } else {
+            if (is_null($nextSibling)) {
+                $previousSibling->nextItemId = 0;
+                Database::getManager()->persist($previousSibling);
+                Database::getManager()->flush($previousSibling);
+            } else {
+                $previousSibling->nextItemId = $nextSibling->iid;
+                Database::getManager()->persist($previousSibling);
+                Database::getManager()->flush($previousSibling);
+                $nextSibling->previousItemId = $previousSibling->iid;
+                Database::getManager()->persist($nextSibling);
+                Database::getManager()->flush($nextSibling);
+            }
+        }
+    }
+
+    /**
+     * Updates the final item's previous item id.
+     *
+     * @ORM\PostRemove
+     *
+     * @throws OptimisticLockException
+     */
+    public function postRemove()
+    {
+        $this->learningPath->updateFinalItemsPreviousItemId();
     }
 
     /**
@@ -780,4 +938,96 @@ public function getCId()
     {
         return $this->cId;
     }
+
+    /**
+     * @param CLp $clp
+     *
+     * @return CLpItem
+     */
+    public function setLearningPath(CLp $clp)
+    {
+        $this->learningPath = $clp;
+        $clp->getItems()->add($this);
+        $this->course = $clp->getCourse();
+        $this->course->getLearningPathItems()->add($this);
+
+        return $this;
+    }
+
+    /**
+     * @return CLp
+     */
+    public function getLearningPath()
+    {
+        return $this->learningPath;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getLearningPathItems()->add($this);
+
+        return $this;
+    }
+
+    /**
+     * Retrieves the list of this instance's siblings, that is all the other children of this item's parent
+     *
+     * @return static[]
+     */
+    public function getSiblings()
+    {
+        $siblings = [];
+        foreach (self::getRepository()->findByParentItemId($this->parentItemId) as $candidate) {
+            if ($candidate !== $this) {
+                $siblings[] = $candidate;
+            }
+        }
+        return $siblings;
+    }
+
+    /**
+     * Returns the previous sibling according to displayOrders only (not looking at previousItemId)
+     *
+     * @return static|null
+     */
+    public function getPreviousSibling()
+    {
+        $previousSibling = null;
+        foreach ($this->getSiblings() as $sibling) {
+            if ($sibling->displayOrder < $this->displayOrder) {
+                if (is_null($previousSibling)) {
+                    $previousSibling = $sibling;
+                } elseif ($sibling->displayOrder > $previousSibling->displayOrder) {
+                    $previousSibling = $sibling;
+                }
+            }
+        }
+        return $previousSibling;
+    }
+
+    /**
+     * Returns the next sibling according to displayOrders only (not looking at nextItemId)
+     *
+     * @return static|null
+     */
+    public function getNextSibling()
+    {
+        $nextSibling = null;
+        foreach ($this->getSiblings() as $sibling) {
+            if ($sibling->displayOrder > $this->displayOrder) {
+                if (is_null($nextSibling)) {
+                    $nextSibling = $sibling;
+                } elseif ($sibling->displayOrder < $nextSibling->displayOrder) {
+                    $nextSibling = $sibling;
+                }
+            }
+        }
+        return $nextSibling;
+    }
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CQuiz.php b/src/Chamilo/CourseBundle/Entity/CQuiz.php
index 06d58c4c7ab..36dc20a8698 100644
--- a/src/Chamilo/CourseBundle/Entity/CQuiz.php
+++ b/src/Chamilo/CourseBundle/Entity/CQuiz.php
@@ -3,7 +3,12 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
+use Database;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
 
 /**
  * CQuiz.
@@ -16,6 +21,7 @@
  *  }
  * )
  * @ORM\Entity
+ * @ORM\HasLifecycleCallbacks
  */
 class CQuiz
 {
@@ -156,7 +162,7 @@ class CQuiz
 
     /**
      * @var bool
-     * @ORm\Column(name="save_correct_answers", type="boolean", nullable=false)
+     * @ORM\Column(name="save_correct_answers", type="boolean", nullable=false)
      */
     protected $saveCorrectAnswers;
 
@@ -216,12 +222,83 @@ class CQuiz
      */
     protected $exerciseCategoryId;
 
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="quizzes")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
+    /**
+     * @var CQuizRelQuestion[]|ArrayCollection
+     *
+     * @ORM\OneToMany(targetEntity="Chamilo\CourseBundle\Entity\CQuizRelQuestion", mappedBy="quiz"))
+     */
+    protected $relQuestions;
+
     /**
      * CQuiz constructor.
      */
     public function __construct()
     {
         $this->hideQuestionTitle = false;
+        $this->type = ONE_PER_PAGE;
+        $this->random = 0;
+        $this->randomAnswers = 0;
+        $this->active = 1;
+        $this->resultsDisabled = 0;
+        $this->maxAttempt = 1;
+        $this->feedbackType = 0;
+        $this->expiredTime = 0;
+        $this->propagateNeg = 0;
+        $this->saveCorrectAnswers = 0;
+        $this->reviewAnswers = 0;
+        $this->randomByCategory = 0;
+        $this->displayCategoryName = 0;
+    }
+
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CQuiz');
+    }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getQuizzes()->add($this);
+
+        return $this;
+    }
+
+    /**
+     * @ORM\PostPersist
+     *
+     * @throws OptimisticLockException
+     */
+    public function postPersist()
+    {
+        if (is_null($this->id)) {
+            $this->id = $this->iid;
+            Database::getManager()->persist($this);
+            Database::getManager()->flush($this);
+        }
     }
 
     /**
@@ -855,4 +932,16 @@ public function setIid($iid)
 
         return $this;
     }
+
+    /**
+     * @return int sum of question's ponderation
+     */
+    public function getMaxScore()
+    {
+        $maxScore = 0;
+        foreach ($this->relQuestions as $relQuestion) {
+            $maxScore += $relQuestion->getQuestion()->getPonderation();
+        }
+        return $maxScore;
+    }
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CQuizRelQuestion.php b/src/Chamilo/CourseBundle/Entity/CQuizRelQuestion.php
index 49fec1427cf..2e74935eacc 100644
--- a/src/Chamilo/CourseBundle/Entity/CQuizRelQuestion.php
+++ b/src/Chamilo/CourseBundle/Entity/CQuizRelQuestion.php
@@ -57,6 +57,22 @@ class CQuizRelQuestion
      */
     protected $exerciceId;
 
+    /**
+     * @var CQuiz
+     *
+     * @ORM\ManyToOne(targetEntity="CQuiz", inversedBy="relQuestions")
+     * @ORM\JoinColumn(name="exercice_id", referencedColumnName="iid")
+     */
+    protected $quiz;
+
+    /**
+     * @var CQuizQuestion
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CQuizQuestion", inversedBy="relQuiz")
+     * @ORM\JoinColumn(name="question_id", referencedColumnName="iid")
+     */
+    protected $question;
+
     /**
      * Set questionOrder.
      *
@@ -152,4 +168,14 @@ public function getExerciceId()
     {
         return $this->exerciceId;
     }
+
+    public function getQuiz()
+    {
+        return $this->quiz;
+    }
+
+    public function getQuestion()
+    {
+        return $this->question;
+    }
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CTool.php b/src/Chamilo/CourseBundle/Entity/CTool.php
index 82b5a9886e1..26e4f2771f9 100644
--- a/src/Chamilo/CourseBundle/Entity/CTool.php
+++ b/src/Chamilo/CourseBundle/Entity/CTool.php
@@ -3,6 +3,7 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
@@ -126,6 +127,14 @@ class CTool
      */
     protected $customIcon;
 
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="tools")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="iid")
+     */
+    protected $course;
+
     /**
      * @return int
      */
@@ -473,4 +482,25 @@ public function setCustomIcon($customIcon)
 
         return $this;
     }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse(Course $course)
+    {
+        $this->course = $course;
+        $this->course->getTools()->add($this);
+
+        return $this;
+    }
 }

From 985f5024ca44562203a15a566784d6cfd7eec467 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Sat, 11 Jul 2020 18:34:20 +0200
Subject: [PATCH 02/16] create_learningpath web service - refs BT#17453

---
 main/inc/lib/webservices/Rest.php             | 149 ++++++++
 .../api/tests/CreateLearningPathTest.php      | 356 ++++++++++++++++++
 main/webservices/api/v2.php                   |  18 +-
 3 files changed, 515 insertions(+), 8 deletions(-)
 create mode 100644 main/webservices/api/tests/CreateLearningPathTest.php

diff --git a/main/inc/lib/webservices/Rest.php b/main/inc/lib/webservices/Rest.php
index e95b0509310..558d55424a3 100644
--- a/main/inc/lib/webservices/Rest.php
+++ b/main/inc/lib/webservices/Rest.php
@@ -5,7 +5,9 @@
 use Chamilo\CoreBundle\Entity\Course;
 use Chamilo\CoreBundle\Entity\ExtraFieldValues;
 use Chamilo\CoreBundle\Entity\Session;
+use Chamilo\CourseBundle\Entity\CLp;
 use Chamilo\CourseBundle\Entity\CLpCategory;
+use Chamilo\CourseBundle\Entity\CLpItem;
 use Chamilo\CourseBundle\Entity\CNotebook;
 use Chamilo\CourseBundle\Entity\Repository\CNotebookRepository;
 use Chamilo\UserBundle\Entity\User;
@@ -68,6 +70,7 @@ class Rest extends WebService
     const USERNAME_EXIST = 'username_exist';
     const GET_COURSE_QUIZ_MDL_COMPAT = 'get_course_quiz_mdl_compat';
     const UPDATE_USER_PAUSE_TRAINING = 'update_user_pause_training';
+    const CREATE_LEARNINGPATH = 'create_learningpath';
 
     /**
      * @var Session
@@ -2039,6 +2042,152 @@ function (array $exercise) use ($quizIcon) {
         return [$json];
     }
 
+    /**
+     * Creates a learning path with items.
+     *
+     * @param array $spec with these keys :
+     *  session_id
+     *  course_code
+     *  lp_name (learning path name)
+     *  lp_cat_id (learning path category id)
+     *  items, a list of items which are arrays with these keys :
+     *      display_order_id (the display order number AND local item identifier, used in parent_id and prerequisite_id)
+     *      parent_id (references display_order_id)
+     *      type (c_lp_item.item_type : dir, document, quiz…)
+     *      name_to_find (course resource name)
+     *      title (learning path item title)
+     *      prerequisite_id (references display_order_id)
+     *      prerequisite_min_score
+     *      prerequisite_max_score
+     *
+     * @throws Exception if an item is not found by type and name or a parameter is missing
+     *
+     * @return CLp the new learning path
+     *
+     */
+    public function createLearningPath(array $spec)
+    {
+        foreach (['course_code', 'lp_name'] as $requiredParameter) {
+            if (!array_key_exists($requiredParameter, $spec)) {
+                throw new Exception("missing parameter '$requiredParameter': ".print_r($spec, true));
+            }
+        }
+        $courseCode = $spec['course_code'];
+        $course = Course::getRepository()->findOneByCode($courseCode);
+        if (is_null($course)) {
+            throw new Exception("no course has code '$courseCode'");
+        }
+        $learningPath = (new CLp())
+            ->setCourse($course)
+            ->setName($spec['lp_name']);
+        if (array_key_exists('session_id', $spec)) {
+            $sessionId = $spec['session_id'];
+            if (0 == $sessionId) {
+                $learningPath->setSessionId(0);
+            } else {
+                $session = Session::getRepository()->find($sessionId);
+                if (is_null($session)) {
+                    throw new Exception("no session has id '$sessionId'");
+                }
+                /*$learningPath->setSession($session);*/
+                $learningPath->setSessionId($sessionId);
+            }
+        }
+        if (array_key_exists('lp_cat_id', $spec)) {
+            $categoryId = $spec['lp_cat_id'];
+            if (0 == $categoryId) {
+                $learningPath->setCategoryId(0);
+            } else {
+                $category = CLpCategory::getRepository()->find($categoryId);
+                if (is_null($category)) {
+                    throw new Exception("no category has id '$categoryId'");
+                }
+                /*$learningPath->setCategory($category);*/
+                $learningPath->setCategoryId($categoryId);
+            }
+        }
+        if (array_key_exists('items', $spec)) {
+            $itemSpecs = $spec['items'];
+            if (!is_array($itemSpecs)) {
+                throw new Exception('parameter "items" must be an array');
+            }
+            if (!empty($itemSpecs)) {
+                $parentDisplayOrders = [0 => 0];
+                $prerequisitesDisplayOrders = [];
+                foreach ($itemSpecs as $itemSpec) {
+                    if (!array_key_exists('display_order_id', $itemSpec)) {
+                        throw new Exception(
+                            sprintf('display_order_id missing from item spec: %s', print_r($itemSpec, true))
+                        );
+                    }
+                    $displayOrder = $itemSpec['display_order_id'];
+                    $type = (array_key_exists('type', $itemSpec) ? $itemSpec['type'] : 'dir');
+                    if (!array_key_exists('title', $itemSpec)) {
+                        throw new Exception(sprintf('type missing from item spec: %s', print_r($itemSpec, true)));
+                    }
+                    $title = $itemSpec['title'];
+                    $item = (new CLpItem())
+                        ->setLearningPath($learningPath)
+                        ->setDisplayOrder($displayOrder)
+                        ->setItemType($type)
+                        ->setTitle($title);
+                    if (in_array($type, ['document', 'final_item', 'forum', 'link', 'quiz'])) {
+                        if (!array_key_exists('name_to_find', $itemSpec)) {
+                            throw new Exception(
+                                sprintf('name_to_find missing from %s spec: %s', $type, print_r($itemSpec, true))
+                            );
+                        }
+                        $resource = $course->findResource($type, $itemSpec['name_to_find']);
+                        $item->setPath('forum' === $type ? $resource->getForumId() : $resource->getId());
+                    }
+                    if (array_key_exists($displayOrder, $parentDisplayOrders)) {
+                        throw new Exception(sprintf('this item display order is not unique: %s', $displayOrder));
+                    }
+                    $parentDisplayOrders[$displayOrder] = array_key_exists('parent_id', $itemSpec)
+                        ? $itemSpec['parent_id']
+                        : 0;
+                    if (array_key_exists('prerequisite_id', $itemSpec)) {
+                        $prerequisiteId = $itemSpec['prerequisite_id'];
+                        if (!empty($prerequisiteId)) {
+                            $prerequisitesDisplayOrders[$displayOrder] = $prerequisiteId;
+                            if (array_key_exists('prerequisite_min_score', $itemSpec)) {
+                                $prerequisiteMinScore = $itemSpec['prerequisite_min_score'];
+                                if (!empty($prerequisiteMinScore)) {
+                                    $item->setPrerequisiteMinScore($prerequisiteMinScore);
+                                }
+                            }
+                            if (array_key_exists('prerequisite_max_score', $itemSpec)) {
+                                $prerequisiteMaxScore = $itemSpec['prerequisite_max_score'];
+                                if (!empty($prerequisiteMaxScore)) {
+                                    $item->setPrerequisiteMaxScore($prerequisiteMaxScore);
+                                }
+                            }
+                        }
+                    }
+                }
+                Database::getManager()->persist($learningPath);
+                Database::getManager()->flush();
+                // now that items have real identifiers, set their parent, previous, next and prerequisite item ones
+                $idFromDisplayOrder = [0 => 0];
+                /** @var CLpItem $item */
+                foreach ($learningPath->getItems() as $item) {
+                    $idFromDisplayOrder[$item->getDisplayOrder()] = $item->getId();
+                }
+                $displayOrderNumbers = array_keys($idFromDisplayOrder);
+                sort($displayOrderNumbers);
+                foreach ($learningPath->getItems() as $item) {
+                    $displayOrder = $item->getDisplayOrder();
+                    $item->setParentItemId($idFromDisplayOrder[$parentDisplayOrders[$displayOrder]]);
+                    if (!empty($prerequisitesDisplayOrders[$displayOrder])) {
+                        $item->setPrerequisite($idFromDisplayOrder[$prerequisitesDisplayOrders[$displayOrder]]);
+                    }
+                }
+            }
+        }
+        Database::getManager()->flush();
+        return $learningPath;
+    }
+
     /**
      * @param array $additionalParams Optional
      *
diff --git a/main/webservices/api/tests/CreateLearningPathTest.php b/main/webservices/api/tests/CreateLearningPathTest.php
new file mode 100644
index 00000000000..830cbe66d57
--- /dev/null
+++ b/main/webservices/api/tests/CreateLearningPathTest.php
@@ -0,0 +1,356 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use Chamilo\CoreBundle\Entity\Course;
+use Chamilo\CoreBundle\Entity\Session;
+use Chamilo\CourseBundle\Entity\CDocument;
+use Chamilo\CourseBundle\Entity\CForumForum;
+use Chamilo\CourseBundle\Entity\CLink;
+use Chamilo\CourseBundle\Entity\CLp;
+use Chamilo\CourseBundle\Entity\CLpCategory;
+use Chamilo\CourseBundle\Entity\CQuiz;
+use Doctrine\ORM\OptimisticLockException;
+use Doctrine\ORM\ORMException;
+use Doctrine\ORM\TransactionRequiredException;
+
+require_once __DIR__.'/V2TestCase.php';
+require_once __DIR__.'/../../../../vendor/autoload.php';
+
+/**
+ * Class CreateLearningPathTest
+ *
+ * SUBSCRIBE_USER_TO_SESSION_FROM_USERNAME webservice unit tests
+ */
+class CreateLearningPathTest extends V2TestCase
+{
+    /** @var Session */
+    public static $session;
+
+    /** @var Course */
+    public static $course;
+
+    /** @var CLpCategory */
+    public static $category;
+
+    /** @var CDocument */
+    public static $document;
+
+    /** @var CForumForum */
+    public static $forum;
+
+    /** @var CLink */
+    public static $link;
+
+    /** @var CQuiz */
+    public static $quiz;
+
+    /** @var CLp[] */
+    public static $learningPaths;
+
+    public function action()
+    {
+        return Rest::CREATE_LEARNINGPATH;
+    }
+
+    /**
+     * @inheritDoc
+     *
+     * @throws ORMException
+     * @throws OptimisticLockException
+     * @throws TransactionRequiredException
+     */
+    public static function setUpBeforeClass(): void
+    {
+        parent::setUpBeforeClass();
+
+        // create a test session
+        self::$session = (new Session())
+            ->setName('Test Session '.time());
+        Database::getManager()->persist(self::$session);
+
+        // create a test course
+        self::$course = (new Course())
+            ->setCode('TESTCOURSE'.time())
+            ->setTitle('Test Course '.time());
+        Database::getManager()->persist(self::$course);
+        Database::getManager()->flush(self::$course); // ensures the course directory is initialized
+
+        // create a test category
+        self::$category = (new CLpCategory())
+            ->setCourse(self::$course)
+            ->setName('Test Category '.time());
+        Database::getManager()->persist(self::$category);
+
+        // create course elements
+        self::$document = CDocument::fromFile(
+            __FILE__,
+            self::$course,
+            'test_document'.time().'.txt',
+            'Test Document'.time()
+        );
+        Database::getManager()->persist(self::$document);
+
+        self::$forum = (new CForumForum())
+            ->setCourse(self::$course)
+            ->setForumTitle('Test Forum '.time());
+        Database::getManager()->persist(self::$forum);
+
+        self::$link = (new CLink())
+            ->setCourse(self::$course)
+            ->setTitle('Test Link '.time())
+            ->setUrl('https://chamilo.org/');
+        Database::getManager()->persist(self::$link);
+
+        self::$quiz = (new CQuiz())
+            ->setCourse(self::$course)
+            ->setTitle('Test Quiz '.time());
+        Database::getManager()->persist(self::$quiz);
+
+        Database::getManager()->flush();
+
+        self::$learningPaths = [];
+    }
+
+    /**
+     * @inheritDoc
+     *
+     * @throws OptimisticLockException
+     */
+    public static function tearDownAfterClass(): void
+    {
+        parent::tearDownAfterClass();
+        foreach (self::$learningPaths as $learningPath) {
+            Database::getManager()->remove($learningPath);
+        }
+        Database::getManager()->remove(self::$quiz);
+        Database::getManager()->remove(self::$link);
+        Database::getManager()->remove(self::$forum);
+        Database::getManager()->remove(self::$document);
+        Database::getManager()->remove(self::$category);
+        Database::getManager()->remove(self::$course);
+        Database::getManager()->remove(self::$session);
+        Database::getManager()->flush();
+    }
+
+    /**
+     * creates an empty learning path
+     * asserts that the learning path was created for the right session and course, with the right name,
+     * in the right category and that it has no item
+     */
+    public function testCreateEmptyLearningPathWithoutSessionNorCategory()
+    {
+        // call the webservice to create the learning path
+        $name = 'Learning Path '.time();
+        $learningPathId = $this->integer(
+            [
+                'session_id' => 0,
+                'course_code' => self::$course->getCode(),
+                'lp_name' => $name,
+                'lp_cat_id' => 0,
+                'items' => [],
+            ]
+        );
+
+        // assert the learning path was created
+        /** @var CLp $learningPath */
+        $learningPath = CLp::getRepository()->find($learningPathId);
+        self::$learningPaths[] = $learningPath;
+
+        self::assertNotNull($learningPath);
+        // in the right course
+        self::assertEquals(0, $learningPath->getSessionId());
+        // with no session nor category
+        self::assertEquals(self::$course->getId(), $learningPath->getCId());
+        self::assertEquals(0, $learningPath->getCategoryId());
+        // with the right name
+        self::assertEquals($name, $learningPath->getName());
+        // with no item
+        self::assertEmpty($learningPath->getItems());
+    }
+
+    /**
+     * creates an empty learning path
+     * asserts that the learning path was created for the right session and course, with the right name,
+     * in the right category and that it has no item
+     */
+    public function testCreateEmptyLearningPath()
+    {
+        // call the webservice to create the learning path
+        $name = 'Learning Path '.time();
+        $learningPathId = $this->integer(
+            [
+                'session_id' => self::$session->getId(),
+                'course_code' => self::$course->getCode(),
+                'lp_name' => $name,
+                'lp_cat_id' => self::$category->getId(),
+                'items' => [],
+            ]
+        );
+
+        // assert the learning path was created
+        /** @var CLp $learningPath */
+        $learningPath = CLp::getRepository()->find($learningPathId);
+        self::$learningPaths[] = $learningPath;
+
+        self::assertNotNull($learningPath);
+        // in the right session, course and category
+        self::assertEquals(self::$session->getId(), $learningPath->getSessionId());
+        self::assertEquals(self::$course->getId(), $learningPath->getCId());
+        self::assertEquals(self::$category->getId(), $learningPath->getCategoryId());
+        // with the right name
+        self::assertEquals($name, $learningPath->getName());
+        // with no item
+        self::assertEmpty($learningPath->getItems());
+    }
+
+    /**
+     * creates a learning path with items
+     * asserts that the learning path items have the right properties
+     */
+    public function testCreateLearningPathWithItems()
+    {
+        $name = 'Learning Path '.time();
+        $items = [
+            [
+                'display_order_id' => 10,
+                'parent_id' => 0,
+                'type' => 'document',
+                'name_to_find' => self::$document->getTitle(),
+                'title' => 'Document title '.time(),
+            ],
+            [
+                'display_order_id' => 40,
+                'parent_id' => 0,
+                'type' => 'forum',
+                'name_to_find' => self::$forum->getForumTitle(),
+                'title' => 'Forum title '.time(),
+                'prerequisite_id' => 20,
+                'prerequisite_min_score' => 1,
+                'prerequisite_max_score' => 1,
+            ],
+            [
+                'display_order_id' => 20,
+                'parent_id' => 0,
+                'type' => 'link',
+                'name_to_find' => self::$link->getTitle(),
+                'title' => 'Link title '.time(),
+                'prerequisite_id' => 10,
+                'prerequisite_min_score' => 1,
+                'prerequisite_max_score' => 1,
+            ],
+            [
+                'display_order_id' => 30,
+                'parent_id' => 0,
+                'type' => 'quiz',
+                'name_to_find' => self::$quiz->getTitle(),
+                'title' => 'Quiz title '.time(),
+                'prerequisite_id' => 10,
+                'prerequisite_min_score' => 1,
+                'prerequisite_max_score' => 1,
+            ],
+            [
+                'display_order_id' => 50,
+                'parent_id' => 0,
+                'type' => TOOL_LP_FINAL_ITEM,
+                'name_to_find' => self::$document->getTitle(),
+                'title' => 'Final item title '.time(),
+                'prerequisite_id' => 40,
+                'prerequisite_min_score' => 1,
+                'prerequisite_max_score' => 1,
+            ],
+            [
+                'display_order_id' => 35,
+                'parent_id' => 0,
+                'type' => 'dir',
+                'title' => 'Chapter 1 title '.time(),
+                'prerequisite_id' => 10,
+                'prerequisite_min_score' => 1,
+                'prerequisite_max_score' => 1,
+            ],
+            [
+                'display_order_id' => 1,
+                'parent_id' => 35,
+                'type' => 'student_publication',
+                'title' => 'Student publication title '.time(),
+            ],
+            [
+                'display_order_id' => 2,
+                'parent_id' => 35,
+                'type' => 'student_publication',
+                'title' => 'Another student publication title '.time(),
+                'prerequisite_id' => 1,
+                'prerequisite_min_score' => 1,
+                'prerequisite_max_score' => 1,
+            ],
+            [
+                'display_order_id' => 3,
+                'parent_id' => 35,
+                'type' => 'dir',
+                'title' => 'Sub-chapter of chapter 1 title '.time(),
+            ],
+            [
+                'display_order_id' => 38,
+                'parent_id' => 0,
+                'type' => 'dir',
+                'title' => 'Chapter 2 title '.time(),
+            ],
+        ];
+        $learningPathId = $this->integer(
+            [
+                'session_id' => self::$session->getId(),
+                'course_code' => self::$course->getCode(),
+                'lp_name' => $name,
+                'lp_cat_id' => self::$category->getId(),
+                'items' => $items,
+            ]
+        );
+
+        // assert the learning path was created as specified
+        /** @var CLp $learningPath */
+        $learningPath = CLp::getRepository()->find($learningPathId);
+        self::$learningPaths[] = $learningPath;
+
+        self::assertNotNull($learningPath);
+        self::assertEquals(self::$session->getId(), $learningPath->getSessionId());
+        self::assertEquals(self::$course->getId(), $learningPath->getCId());
+        self::assertEquals($name, $learningPath->getName());
+        self::assertEquals(self::$category->getId(), $learningPath->getCategoryId());
+        self::assertNotEmpty($learningPath->getItems());
+
+        // assert its item list matches the input specifications
+        $realIds = [0 => 0];
+        foreach ($items as $spec) {
+            $found = false;
+            foreach ($learningPath->getItems() as $item) {
+                if ($spec['type'] === $item->getItemType() && $spec['title'] == $item->getTitle()) {
+                    $found = true;
+                    $displayOrderId = $spec['display_order_id'];
+                    $realIds[$displayOrderId] = $item->getId();
+                    self::assertEquals($displayOrderId, $item->getDisplayOrder());
+                    self::assertEquals(self::$course->getId(), $item->getCId());
+                    if ($item->getItemType() === 'document') {
+                        self::assertEquals(self::$document->getId(), $item->getPath());
+                    } elseif ($item->getItemType() === 'link') {
+                        self::assertEquals(self::$link->getId(), $item->getPath());
+                    }
+                    if (array_key_exists('prerequisite_id', $spec) && 0 != $spec['prerequisite_id']) {
+                        self::assertEquals($spec['prerequisite_min_score'], $item->getPrerequisiteMinScore());
+                        self::assertEquals($spec['prerequisite_max_score'], $item->getPrerequisiteMaxScore());
+                    }
+                    break;
+                }
+            }
+            self::assertTrue($found, sprintf('item not found: %s', print_r($spec, true)));
+        }
+        foreach ($items as $spec) {
+            foreach ($learningPath->getItems() as $item) {
+                if ($spec['type'] === $item->getItemType() && $spec['title'] == $item->getTitle()) {
+                    self::assertEquals($realIds[$spec['parent_id']], $item->getParentItemId());
+                    if (array_key_exists('prerequisite_id', $spec) && 0 != $spec['prerequisite_id']) {
+                        self::assertEquals($realIds[$spec['prerequisite_id']], $item->getPrerequisite());
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/main/webservices/api/v2.php b/main/webservices/api/v2.php
index e08a19efe2c..3661807b584 100644
--- a/main/webservices/api/v2.php
+++ b/main/webservices/api/v2.php
@@ -217,9 +217,7 @@
             $restResponse->setData($data);
             break;
         case Rest::SAVE_FORUM_POST:
-            if (
-                empty($_POST['title']) || empty($_POST['text']) || empty($_POST['thread']) || empty($_POST['forum'])
-            ) {
+            if (empty($_POST['title']) || empty($_POST['text']) || empty($_POST['thread']) || empty($_POST['forum'])) {
                 throw new Exception(get_lang('NoData'));
             }
 
@@ -310,7 +308,8 @@
                 $_POST['sessionName'],
                 $_POST['startDate'],
                 $_POST['endDate'],
-                isset($_POST['extraFields']) ? $_POST['extraFields'] : []);
+                isset($_POST['extraFields']) ? $_POST['extraFields'] : []
+            );
             $restResponse->setData([$newSessionId]);
             break;
         case Rest::SUBSCRIBE_USER_TO_SESSION_FROM_USERNAME:
@@ -342,10 +341,9 @@
             exit;
             break;
         case Rest::UPDATE_USER_PAUSE_TRAINING:
-            $allow = api_get_plugin_setting('pausetraining', 'tool_enable') === 'true';
-            $allowPauseFormation = api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation') === 'true';
-
-            if (false === $allow || false === $allowPauseFormation) {
+            if (api_get_plugin_setting('pausetraining', 'tool_enable') !== 'true'
+                ||
+                api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation') !== 'true') {
                 throw new Exception(get_lang('Plugin configured'));
             }
 
@@ -356,6 +354,10 @@
             $data = $plugin->updateUserPauseTraining($_POST['user_id'], $_POST);
             $restResponse->setData([$data]);
             break;
+        case Rest::CREATE_LEARNINGPATH:
+            $learningPath = $restApi->createLearningPath($_POST);
+            $restResponse->setData([$learningPath->getIid()]);
+            break;
         default:
             throw new Exception(get_lang('InvalidAction'));
     }

From 45556c1b1126948e46727ccde0cb35fcc60175db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Sat, 11 Jul 2020 19:28:04 +0200
Subject: [PATCH 03/16] Minor code style fix - refs BT#17453

---
 main/inc/lib/webservices/Rest.php             | 36 ++++---
 src/Chamilo/CoreBundle/Entity/AccessUrl.php   | 39 +++++---
 src/Chamilo/CoreBundle/Entity/Course.php      | 92 +++++++++++-------
 src/Chamilo/CoreBundle/Entity/Session.php     | 93 +++++++++++--------
 .../CoreBundle/Entity/SessionCategory.php     |  2 +-
 src/Chamilo/CourseBundle/Entity/CDocument.php |  6 +-
 .../CourseBundle/Entity/CForumForum.php       | 15 +--
 src/Chamilo/CourseBundle/Entity/CLink.php     |  2 +-
 src/Chamilo/CourseBundle/Entity/CLp.php       | 37 ++++----
 .../CourseBundle/Entity/CLpCategory.php       | 10 +-
 src/Chamilo/CourseBundle/Entity/CLpItem.php   | 17 ++--
 src/Chamilo/CourseBundle/Entity/CQuiz.php     | 16 ++--
 src/Chamilo/CourseBundle/Entity/CTool.php     |  2 +-
 13 files changed, 215 insertions(+), 152 deletions(-)

diff --git a/main/inc/lib/webservices/Rest.php b/main/inc/lib/webservices/Rest.php
index 558d55424a3..32a9bbc67b1 100644
--- a/main/inc/lib/webservices/Rest.php
+++ b/main/inc/lib/webservices/Rest.php
@@ -1917,7 +1917,13 @@ public function updateUserFromUserName($parameters)
                                     $fieldValue = $field['field_value'];
                                     if (!isset($fieldName) || !isset($fieldValue) ||
                                         !UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
-                                        throw new Exception(get_lang('CouldNotUpdateExtraFieldValue').': '.print_r($field, true));
+                                        throw new Exception(
+                                            sprintf(
+                                                '%s: %s',
+                                                get_lang('CouldNotUpdateExtraFieldValue'),
+                                                print_r($field, true)
+                                            )
+                                        );
                                     }
                                 }
                             } else {
@@ -2046,24 +2052,23 @@ function (array $exercise) use ($quizIcon) {
      * Creates a learning path with items.
      *
      * @param array $spec with these keys :
-     *  session_id
-     *  course_code
-     *  lp_name (learning path name)
-     *  lp_cat_id (learning path category id)
-     *  items, a list of items which are arrays with these keys :
-     *      display_order_id (the display order number AND local item identifier, used in parent_id and prerequisite_id)
-     *      parent_id (references display_order_id)
-     *      type (c_lp_item.item_type : dir, document, quiz…)
-     *      name_to_find (course resource name)
-     *      title (learning path item title)
-     *      prerequisite_id (references display_order_id)
-     *      prerequisite_min_score
-     *      prerequisite_max_score
+     *                    session_id
+     *                    course_code
+     *                    lp_name (learning path name)
+     *                    lp_cat_id (learning path category id)
+     *                    items, a list of items which are arrays with these keys :
+     *                      display_order_id (position AND local item identifier, used in parent_id and prerequisite_id)
+     *                      parent_id (references display_order_id)
+     *                      type (c_lp_item.item_type : dir, document, quiz…)
+     *                      name_to_find (course resource name)
+     *                      title (learning path item title)
+     *                      prerequisite_id (references display_order_id)
+     *                      prerequisite_min_score
+     *                      prerequisite_max_score
      *
      * @throws Exception if an item is not found by type and name or a parameter is missing
      *
      * @return CLp the new learning path
-     *
      */
     public function createLearningPath(array $spec)
     {
@@ -2185,6 +2190,7 @@ public function createLearningPath(array $spec)
             }
         }
         Database::getManager()->flush();
+
         return $learningPath;
     }
 
diff --git a/src/Chamilo/CoreBundle/Entity/AccessUrl.php b/src/Chamilo/CoreBundle/Entity/AccessUrl.php
index 85b9b68a1bf..b55636c5888 100644
--- a/src/Chamilo/CoreBundle/Entity/AccessUrl.php
+++ b/src/Chamilo/CoreBundle/Entity/AccessUrl.php
@@ -3,6 +3,9 @@
 
 namespace Chamilo\CoreBundle\Entity;
 
+use Database;
+use DateTime;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
@@ -56,7 +59,7 @@ class AccessUrl
     protected $createdBy;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="tms", type="datetime", nullable=true)
      */
@@ -70,38 +73,48 @@ class AccessUrl
     protected $urlType;
 
     /**
-     * @ORM\OneToMany(targetEntity="Chamilo\CoreBundle\Entity\SettingsCurrent", mappedBy="url", cascade={"persist"}, orphanRemoval=true)
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CoreBundle\Entity\SettingsCurrent",
+     *     mappedBy="url",
+     *     cascade={"persist"},
+     *     orphanRemoval=true
+     * )
      */
     //protected $settings;
 
     /**
-     * @ORM\OneToMany(targetEntity="Chamilo\CoreBundle\Entity\SessionCategory", mappedBy="url", cascade={"persist"}, orphanRemoval=true)
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CoreBundle\Entity\SessionCategory",
+     *     mappedBy="url",
+     *     cascade={"persist"},
+     *     orphanRemoval=true
+     * )
      */
-    protected $sessionCategory;
+    protected $sessionCategories;
 
     /**
      * AccessUrl constructor.
      */
     public function __construct()
     {
-        $this->tms = new \DateTime();
+        $this->tms = new DateTime();
         $this->createdBy = 1;
     }
 
     /**
-     * @return Repository\AccessUrlRepository|\Doctrine\ORM\EntityRepository
+     * @return string
      */
-    public static function getRepository()
+    public function __toString()
     {
-        return \Database::getManager()->getRepository('ChamiloCoreBundle:AccessUrl');
+        return (string) $this->getUrl();
     }
 
     /**
-     * @return string
+     * @return Repository\AccessUrlRepository|EntityRepository
      */
-    public function __toString()
+    public static function getRepository()
     {
-        return (string) $this->getUrl();
+        return Database::getManager()->getRepository('ChamiloCoreBundle:AccessUrl');
     }
 
     /**
@@ -213,7 +226,7 @@ public function getCreatedBy()
     /**
      * Set tms.
      *
-     * @param \DateTime $tms
+     * @param DateTime $tms
      *
      * @return AccessUrl
      */
@@ -227,7 +240,7 @@ public function setTms($tms)
     /**
      * Get tms.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getTms()
     {
diff --git a/src/Chamilo/CoreBundle/Entity/Course.php b/src/Chamilo/CoreBundle/Entity/Course.php
index 9cf7c66811f..81614eacb73 100644
--- a/src/Chamilo/CoreBundle/Entity/Course.php
+++ b/src/Chamilo/CoreBundle/Entity/Course.php
@@ -16,6 +16,7 @@
 use CourseManager;
 use Database;
 use DateInterval;
+use DateTime;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Criteria;
 use Doctrine\ORM\EntityRepository;
@@ -200,28 +201,28 @@ class Course
     protected $diskQuota;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="last_visit", type="datetime", nullable=true, unique=false)
      */
     protected $lastVisit;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="last_edit", type="datetime", nullable=true, unique=false)
      */
     protected $lastEdit;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="creation_date", type="datetime", nullable=true, unique=false)
      */
     protected $creationDate;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="expiration_date", type="datetime", nullable=true, unique=false)
      */
@@ -385,7 +386,7 @@ class Course
      */
     public function __construct()
     {
-        $this->creationDate = new \DateTime();
+        $this->creationDate = new DateTime();
         $this->users = new ArrayCollection();
         $this->accessUrls = new ArrayCollection();
         $this->documents = new ArrayCollection();
@@ -397,6 +398,14 @@ public function __construct()
         $this->learningPathItems = new ArrayCollection();
     }
 
+    /**
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->getTitle();
+    }
+
     /**
      * @return Repository\CourseRepository|EntityRepository
      */
@@ -465,9 +474,9 @@ public function prePersist()
         if (is_null($this->diskQuota)) {
             $this->diskQuota = api_get_setting('default_document_quotum');
         }
-        $this->lastEdit = new \DateTime();
+        $this->lastEdit = new DateTime();
         if (is_null($this->expirationDate)) {
-            $this->expirationDate = new \DateTime();
+            $this->expirationDate = new DateTime();
             $this->expirationDate->add(new DateInterval('P1Y'));
         }
         $absolutePath = $this->getAbsolutePath();
@@ -510,14 +519,6 @@ public function getAbsolutePath()
         return api_get_path(SYS_COURSE_PATH).$this->directory;
     }
 
-    /**
-     * @return string
-     */
-    public function __toString()
-    {
-        return (string) $this->getTitle();
-    }
-
     /**
      * @return ArrayCollection
      */
@@ -644,9 +645,11 @@ public function addUsers(CourseRelUser $courseRelUser)
     }
 
     /**
+     * @param User $user
+     *
      * @return bool
      */
-    public function hasUser(User $user)
+    public function hasUser($user)
     {
         $criteria = Criteria::create()->where(
             Criteria::expr()->eq("user", $user)
@@ -656,9 +659,11 @@ public function hasUser(User $user)
     }
 
     /**
+     * @param User $user
+     *
      * @return bool
      */
-    public function hasStudent(User $user)
+    public function hasStudent($user)
     {
         $criteria = Criteria::create()->where(
             Criteria::expr()->eq("user", $user)
@@ -668,9 +673,11 @@ public function hasStudent(User $user)
     }
 
     /**
+     * @param User $user
+     *
      * @return bool
      */
-    public function hasTeacher(User $user)
+    public function hasTeacher($user)
     {
         $criteria = Criteria::create()->where(
             Criteria::expr()->eq("user", $user)
@@ -681,8 +688,10 @@ public function hasTeacher(User $user)
 
     /**
      * Remove $user.
+     *
+     * @param CourseRelUser $user
      */
-    public function removeUsers(CourseRelUser $user)
+    public function removeUsers($user)
     {
         foreach ($this->users as $key => $value) {
             if ($value->getId() == $user->getId()) {
@@ -691,12 +700,18 @@ public function removeUsers(CourseRelUser $user)
         }
     }
 
-    public function addTeacher(User $user)
+    /**
+     * @param User $user
+     */
+    public function addTeacher($user)
     {
         $this->addUser($user, 0, "Trainer", User::COURSE_MANAGER);
     }
 
-    public function addStudent(User $user)
+    /**
+     * @param User $user
+     */
+    public function addStudent($user)
     {
         $this->addUser($user, 0, "", User::STUDENT);
     }
@@ -1055,7 +1070,7 @@ public function getDiskQuota()
     /**
      * Set lastVisit.
      *
-     * @param \DateTime $lastVisit
+     * @param DateTime $lastVisit
      *
      * @return Course
      */
@@ -1069,7 +1084,7 @@ public function setLastVisit($lastVisit)
     /**
      * Get lastVisit.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getLastVisit()
     {
@@ -1079,7 +1094,7 @@ public function getLastVisit()
     /**
      * Set lastEdit.
      *
-     * @param \DateTime $lastEdit
+     * @param DateTime $lastEdit
      *
      * @return Course
      */
@@ -1093,7 +1108,7 @@ public function setLastEdit($lastEdit)
     /**
      * Get lastEdit.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getLastEdit()
     {
@@ -1103,7 +1118,7 @@ public function getLastEdit()
     /**
      * Set creationDate.
      *
-     * @param \DateTime $creationDate
+     * @param DateTime $creationDate
      *
      * @return Course
      */
@@ -1117,7 +1132,7 @@ public function setCreationDate($creationDate)
     /**
      * Get creationDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getCreationDate()
     {
@@ -1127,7 +1142,7 @@ public function getCreationDate()
     /**
      * Set expirationDate.
      *
-     * @param \DateTime $expirationDate
+     * @param DateTime $expirationDate
      *
      * @return Course
      */
@@ -1141,7 +1156,7 @@ public function setExpirationDate($expirationDate)
     /**
      * Get expirationDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getExpirationDate()
     {
@@ -1359,9 +1374,11 @@ public function getCurrentSession()
     }
 
     /**
+     * @param Session $session
+     *
      * @return $this
      */
-    public function setCurrentSession(Session $session)
+    public function setCurrentSession($session)
     {
         // If the session is registered in the course session list.
         if ($this->getSessions()->contains($session->getId())) {
@@ -1433,11 +1450,11 @@ public function findResource($type, $title)
     {
         $collectionsAndColumns = [
             // type          collection           column
-            'document'   => [$this->documents,   'title'],
+            'document' => [$this->documents,   'title'],
             'final_item' => [$this->documents,   'title'],
-            'forum'      => [$this->forums, 'forumTitle'],
-            'link'       => [$this->links,       'title'],
-            'quiz'       => [$this->quizzes,     'title'],
+            'forum' => [$this->forums, 'forumTitle'],
+            'link' => [$this->links,       'title'],
+            'quiz' => [$this->quizzes,     'title'],
         ];
         if (!array_key_exists($type, $collectionsAndColumns)) {
             throw new Exception(sprintf('unsupported resource type "%s"', $type));
@@ -1463,9 +1480,11 @@ public function getLearningPathItems()
     }
 
     /**
+     * @param CourseRelUser $subscription
+     *
      * @return bool
      */
-    protected function hasSubscription(CourseRelUser $subscription)
+    protected function hasSubscription($subscription)
     {
         if ($this->getUsers()->count()) {
             $criteria = Criteria::create()->where(
@@ -1485,11 +1504,12 @@ protected function hasSubscription(CourseRelUser $subscription)
     }
 
     /**
+     * @param User   $user
      * @param string $relationType
      * @param string $role
      * @param string $status
      */
-    protected function addUser(User $user, $relationType, $role, $status)
+    protected function addUser($user, $relationType, $role, $status)
     {
         $courseRelUser = new CourseRelUser();
         $courseRelUser->setCourse($this);
diff --git a/src/Chamilo/CoreBundle/Entity/Session.php b/src/Chamilo/CoreBundle/Entity/Session.php
index a697b194c41..cb07b2ec503 100644
--- a/src/Chamilo/CoreBundle/Entity/Session.php
+++ b/src/Chamilo/CoreBundle/Entity/Session.php
@@ -7,10 +7,12 @@
 use Chamilo\CourseBundle\Entity\CStudentPublication;
 use Chamilo\UserBundle\Entity\User;
 use Database;
+use Datetime;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Criteria;
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Exception;
 
 //use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
 //use Gedmo\Mapping\Annotation as Gedmo;
@@ -63,7 +65,12 @@ class Session
 
     /**
      * @var ArrayCollection
-     * @ORM\OneToMany(targetEntity="SessionRelCourseRelUser", mappedBy="session", cascade={"persist"}, orphanRemoval=true)
+     * @ORM\OneToMany(
+     *     targetEntity="SessionRelCourseRelUser",
+     *     mappedBy="session",
+     *     cascade={"persist"},
+     *     orphanRemoval=true
+     * )
      */
     protected $userCourseSubscriptions;
 
@@ -143,42 +150,42 @@ class Session
     protected $promotionId;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="display_start_date", type="datetime", nullable=true, unique=false)
      */
     protected $displayStartDate;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="display_end_date", type="datetime", nullable=true, unique=false)
      */
     protected $displayEndDate;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="access_start_date", type="datetime", nullable=true, unique=false)
      */
     protected $accessStartDate;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="access_end_date", type="datetime", nullable=true, unique=false)
      */
     protected $accessEndDate;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="coach_access_start_date", type="datetime", nullable=true, unique=false)
      */
     protected $coachAccessStartDate;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="coach_access_end_date", type="datetime", nullable=true, unique=false)
      */
@@ -218,7 +225,12 @@ class Session
 
     /**
      * @var ArrayCollection
-     * @ORM\OneToMany(targetEntity="Chamilo\CourseBundle\Entity\CStudentPublication", mappedBy="session", cascade={"persist"}, orphanRemoval=true)
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CStudentPublication",
+     *     mappedBy="session",
+     *     cascade={"persist"},
+     *     orphanRemoval=true
+     * )
      */
     protected $studentPublications;
 
@@ -243,12 +255,12 @@ public function __construct()
         $this->nbrClasses = 0;
         $this->nbrUsers = 0;
 
-        $this->displayStartDate = new \DateTime();
-        $this->displayEndDate = new \DateTime();
-        $this->accessStartDate = new \DateTime();
-        $this->accessEndDate = new \DateTime();
-        $this->coachAccessStartDate = new \DateTime();
-        $this->coachAccessEndDate = new \DateTime();
+        $this->displayStartDate = new DateTime();
+        $this->displayEndDate = new DateTime();
+        $this->accessStartDate = new DateTime();
+        $this->accessEndDate = new DateTime();
+        $this->coachAccessStartDate = new DateTime();
+        $this->coachAccessEndDate = new DateTime();
         $this->visibility = 1;
 
         $this->courses = new ArrayCollection();
@@ -260,6 +272,14 @@ public function __construct()
         $this->sendSubscriptionNotification = 0;
     }
 
+    /**
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->getName();
+    }
+
     /**
      * @return Repository\SessionRepository|EntityRepository
      */
@@ -277,16 +297,8 @@ public static function getCurrentSession()
         if (0 != $sessionId) {
             return self::getRepository()->find($sessionId);
         }
-        return null;
-    }
-
 
-    /**
-     * @return string
-     */
-    public function __toString()
-    {
-        return (string) $this->getName();
+        return null;
     }
 
     /**
@@ -479,8 +491,11 @@ public function removeCourses($course)
     /**
      * Remove course subscription for a user.
      * If user status in session is student, then decrease number of course users.
+     *
+     * @param User   $user
+     * @param Course $course
      */
-    public function removeUserCourseSubscription(User $user, Course $course)
+    public function removeUserCourseSubscription($user, $course)
     {
         /** @var SessionRelCourseRelUser $courseSubscription */
         foreach ($this->userCourseSubscriptions as $i => $courseSubscription) {
@@ -745,7 +760,7 @@ public function getPromotionId()
     /**
      * Set displayStartDate.
      *
-     * @param \DateTime $displayStartDate
+     * @param DateTime $displayStartDate
      *
      * @return Session
      */
@@ -759,7 +774,7 @@ public function setDisplayStartDate($displayStartDate)
     /**
      * Get displayStartDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getDisplayStartDate()
     {
@@ -769,7 +784,7 @@ public function getDisplayStartDate()
     /**
      * Set displayEndDate.
      *
-     * @param \DateTime $displayEndDate
+     * @param DateTime $displayEndDate
      *
      * @return Session
      */
@@ -783,7 +798,7 @@ public function setDisplayEndDate($displayEndDate)
     /**
      * Get displayEndDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getDisplayEndDate()
     {
@@ -793,7 +808,7 @@ public function getDisplayEndDate()
     /**
      * Set accessStartDate.
      *
-     * @param \DateTime $accessStartDate
+     * @param DateTime $accessStartDate
      *
      * @return Session
      */
@@ -807,7 +822,7 @@ public function setAccessStartDate($accessStartDate)
     /**
      * Get accessStartDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getAccessStartDate()
     {
@@ -817,7 +832,7 @@ public function getAccessStartDate()
     /**
      * Set accessEndDate.
      *
-     * @param \DateTime $accessEndDate
+     * @param DateTime $accessEndDate
      *
      * @return Session
      */
@@ -831,7 +846,7 @@ public function setAccessEndDate($accessEndDate)
     /**
      * Get accessEndDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getAccessEndDate()
     {
@@ -841,7 +856,7 @@ public function getAccessEndDate()
     /**
      * Set coachAccessStartDate.
      *
-     * @param \DateTime $coachAccessStartDate
+     * @param DateTime $coachAccessStartDate
      *
      * @return Session
      */
@@ -855,7 +870,7 @@ public function setCoachAccessStartDate($coachAccessStartDate)
     /**
      * Get coachAccessStartDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getCoachAccessStartDate()
     {
@@ -865,7 +880,7 @@ public function getCoachAccessStartDate()
     /**
      * Set coachAccessEndDate.
      *
-     * @param \DateTime $coachAccessEndDate
+     * @param DateTime $coachAccessEndDate
      *
      * @return Session
      */
@@ -879,7 +894,7 @@ public function setCoachAccessEndDate($coachAccessEndDate)
     /**
      * Get coachAccessEndDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getCoachAccessEndDate()
     {
@@ -945,7 +960,7 @@ public static function getStatusList()
      */
     public function isActive()
     {
-        $now = new \Datetime('now');
+        $now = new Datetime('now');
 
         return $now > $this->getAccessStartDate();
     }
@@ -959,8 +974,8 @@ public function isActive()
     public function isCurrentlyAccessible()
     {
         try {
-            $now = new \Datetime();
-        } catch (\Exception $exception) {
+            $now = new Datetime();
+        } catch (Exception $exception) {
             return false;
         }
 
diff --git a/src/Chamilo/CoreBundle/Entity/SessionCategory.php b/src/Chamilo/CoreBundle/Entity/SessionCategory.php
index f5c41be87d8..7260221b4ce 100644
--- a/src/Chamilo/CoreBundle/Entity/SessionCategory.php
+++ b/src/Chamilo/CoreBundle/Entity/SessionCategory.php
@@ -14,7 +14,7 @@
 class SessionCategory
 {
     /**
-     * @ORM\ManyToOne(targetEntity="AccessUrl", inversedBy="sessionCategory", cascade={"persist"})
+     * @ORM\ManyToOne(targetEntity="AccessUrl", inversedBy="sessionCategories", cascade={"persist"})
      * @ORM\JoinColumn(name="access_url_id", referencedColumnName="id")
      */
     protected $url;
diff --git a/src/Chamilo/CourseBundle/Entity/CDocument.php b/src/Chamilo/CourseBundle/Entity/CDocument.php
index 48d0aa21c46..18460438236 100644
--- a/src/Chamilo/CourseBundle/Entity/CDocument.php
+++ b/src/Chamilo/CourseBundle/Entity/CDocument.php
@@ -144,9 +144,7 @@ public static function fromFile($filePath, $course, $documentPath, $title)
             ->setTitle($title);
         $absolutePath = $instance->getAbsolutePath();
         if (!copy($filePath, $absolutePath)) {
-            throw new Exception(
-                sprintf('Could not copy %s to %s to create a course document', $filePath, $absolutePath)
-            );
+            throw new Exception(sprintf('Could not copy course document file %s to %s', $filePath, $absolutePath));
         }
 
         return $instance;
@@ -165,7 +163,7 @@ public function getCourse()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getDocuments()->add($this);
diff --git a/src/Chamilo/CourseBundle/Entity/CForumForum.php b/src/Chamilo/CourseBundle/Entity/CForumForum.php
index 57279451e3a..4e2e824db38 100644
--- a/src/Chamilo/CourseBundle/Entity/CForumForum.php
+++ b/src/Chamilo/CourseBundle/Entity/CForumForum.php
@@ -5,6 +5,7 @@
 
 use Chamilo\CoreBundle\Entity\Course;
 use Database;
+use DateTime;
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 use Doctrine\ORM\OptimisticLockException;
@@ -173,14 +174,14 @@ class CForumForum
     protected $forumImage;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="start_time", type="datetime", nullable=true)
      */
     protected $startTime;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="end_time", type="datetime", nullable=true)
      */
@@ -252,7 +253,7 @@ public function getCourse()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getForums()->add($this);
@@ -691,7 +692,7 @@ public function getForumImage()
     /**
      * Set startTime.
      *
-     * @param \DateTime $startTime
+     * @param DateTime $startTime
      *
      * @return CForumForum
      */
@@ -705,7 +706,7 @@ public function setStartTime($startTime)
     /**
      * Get startTime.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getStartTime()
     {
@@ -715,7 +716,7 @@ public function getStartTime()
     /**
      * Set endTime.
      *
-     * @param \DateTime $endTime
+     * @param DateTime $endTime
      *
      * @return CForumForum
      */
@@ -729,7 +730,7 @@ public function setEndTime($endTime)
     /**
      * Get endTime.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getEndTime()
     {
diff --git a/src/Chamilo/CourseBundle/Entity/CLink.php b/src/Chamilo/CourseBundle/Entity/CLink.php
index 0228dc60417..1b5bb3f31b4 100644
--- a/src/Chamilo/CourseBundle/Entity/CLink.php
+++ b/src/Chamilo/CourseBundle/Entity/CLink.php
@@ -141,7 +141,7 @@ public function getCourse()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getLinks()->add($this);
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index c76525167bc..cbdcde1a147 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -6,6 +6,7 @@
 use Chamilo\CoreBundle\Entity\Course;
 use Chamilo\CoreBundle\Entity\Session;
 use Database;
+use DateTime;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
@@ -239,28 +240,28 @@ class CLp
     protected $subscribeUsers;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="created_on", type="datetime", nullable=false)
      */
     protected $createdOn;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="modified_on", type="datetime", nullable=false)
      */
     protected $modifiedOn;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="publicated_on", type="datetime", nullable=true)
      */
     protected $publicatedOn;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="expired_on", type="datetime", nullable=true)
      */
@@ -336,8 +337,8 @@ public function __construct()
         $this->autolaunch = 0;
         $this->maxAttempts = 0;
         $this->subscribeUsers = 0;
-        $this->createdOn = new \DateTime();
-        $this->modifiedOn = new \DateTime();
+        $this->createdOn = new DateTime();
+        $this->modifiedOn = new DateTime();
         $this->accumulateScormTime = 1;
         $this->items = new ArrayCollection();
     }
@@ -420,7 +421,7 @@ public function getCourse()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getLearningPaths()->add($this);
@@ -1053,7 +1054,7 @@ public function getAutolaunch()
     /**
      * Set createdOn.
      *
-     * @param \DateTime $createdOn
+     * @param DateTime $createdOn
      *
      * @return CLp
      */
@@ -1067,7 +1068,7 @@ public function setCreatedOn($createdOn)
     /**
      * Get createdOn.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getCreatedOn()
     {
@@ -1077,7 +1078,7 @@ public function getCreatedOn()
     /**
      * Set modifiedOn.
      *
-     * @param \DateTime $modifiedOn
+     * @param DateTime $modifiedOn
      *
      * @return CLp
      */
@@ -1091,7 +1092,7 @@ public function setModifiedOn($modifiedOn)
     /**
      * Get modifiedOn.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getModifiedOn()
     {
@@ -1101,7 +1102,7 @@ public function getModifiedOn()
     /**
      * Set publicatedOn.
      *
-     * @param \DateTime $publicatedOn
+     * @param DateTime $publicatedOn
      *
      * @return CLp
      */
@@ -1115,7 +1116,7 @@ public function setPublicatedOn($publicatedOn)
     /**
      * Get publicatedOn.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getPublicatedOn()
     {
@@ -1125,7 +1126,7 @@ public function getPublicatedOn()
     /**
      * Set expiredOn.
      *
-     * @param \DateTime $expiredOn
+     * @param DateTime $expiredOn
      *
      * @return CLp
      */
@@ -1139,7 +1140,7 @@ public function setExpiredOn($expiredOn)
     /**
      * Get expiredOn.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getExpiredOn()
     {
@@ -1274,6 +1275,7 @@ public function getFinalItem()
                 return $item;
             }
         }
+
         return null;
     }
 
@@ -1292,6 +1294,7 @@ public function getLastItemInFirstLevel()
                 }
             }
         }
+
         return $last;
     }
 
@@ -1308,8 +1311,8 @@ public function updateFinalItemsPreviousItemId()
             $last = $this->getLastItemInFirstLevel();
             if (!is_null($last)) {
                 $finalItem->setPreviousItemId($last->getId());
-                \Database::getManager()->persist($finalItem);
-                \Database::getManager()->flush($finalItem);
+                Database::getManager()->persist($finalItem);
+                Database::getManager()->flush($finalItem);
             }
         }
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpCategory.php b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
index 79aa2efa8ff..0ba5938f0ff 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpCategory.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
@@ -121,7 +121,7 @@ public function getCourse()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getLearningPathCategories()->add($this);
@@ -269,9 +269,11 @@ public function addUser(CLpCategoryUser $categoryUser)
     }
 
     /**
+     * @param CLpCategoryUser $categoryUser
+     *
      * @return bool
      */
-    public function hasUser(CLpCategoryUser $categoryUser)
+    public function hasUser($categoryUser)
     {
         if ($this->getUsers()->count()) {
             $criteria = Criteria::create()->where(
@@ -307,9 +309,11 @@ public function hasUserAdded($user)
     }
 
     /**
+     * @param CLpCategoryUser $user
+     *
      * @return $this
      */
-    public function removeUsers(CLpCategoryUser $user)
+    public function removeUsers($user)
     {
         $this->users->removeElement($user);
 
diff --git a/src/Chamilo/CourseBundle/Entity/CLpItem.php b/src/Chamilo/CourseBundle/Entity/CLpItem.php
index 2185fee48ce..4a1f1c78c4e 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpItem.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpItem.php
@@ -204,7 +204,7 @@ class CLpItem
     protected $prerequisiteMaxScore;
 
     /**
-     * @var CLp $learningPath
+     * @var CLp
      *
      * @ORM\ManyToOne(targetEntity="CLp", inversedBy="items")
      * @ORM\JoinColumn(name="lp_id", referencedColumnName="iid")
@@ -279,7 +279,7 @@ public function postPersist()
         } else {
             foreach ($this->getSiblings() as $sibling) {
                 if ($this->displayOrder === $sibling->displayOrder) {
-                    $sibling->displayOrder ++;
+                    $sibling->displayOrder++;
                     Database::getManager()->persist($sibling);
                 }
             }
@@ -940,8 +940,6 @@ public function getCId()
     }
 
     /**
-     * @param CLp $clp
-     *
      * @return CLpItem
      */
     public function setLearningPath(CLp $clp)
@@ -967,7 +965,7 @@ public function getLearningPath()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getLearningPathItems()->add($this);
@@ -976,7 +974,7 @@ public function setCourse(Course $course)
     }
 
     /**
-     * Retrieves the list of this instance's siblings, that is all the other children of this item's parent
+     * Retrieves the list of this instance's siblings, that is all the other children of this item's parent.
      *
      * @return static[]
      */
@@ -988,11 +986,12 @@ public function getSiblings()
                 $siblings[] = $candidate;
             }
         }
+
         return $siblings;
     }
 
     /**
-     * Returns the previous sibling according to displayOrders only (not looking at previousItemId)
+     * Returns the previous sibling according to displayOrders only (not looking at previousItemId).
      *
      * @return static|null
      */
@@ -1008,11 +1007,12 @@ public function getPreviousSibling()
                 }
             }
         }
+
         return $previousSibling;
     }
 
     /**
-     * Returns the next sibling according to displayOrders only (not looking at nextItemId)
+     * Returns the next sibling according to displayOrders only (not looking at nextItemId).
      *
      * @return static|null
      */
@@ -1028,6 +1028,7 @@ public function getNextSibling()
                 }
             }
         }
+
         return $nextSibling;
     }
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CQuiz.php b/src/Chamilo/CourseBundle/Entity/CQuiz.php
index 36dc20a8698..cda0350180c 100644
--- a/src/Chamilo/CourseBundle/Entity/CQuiz.php
+++ b/src/Chamilo/CourseBundle/Entity/CQuiz.php
@@ -5,6 +5,7 @@
 
 use Chamilo\CoreBundle\Entity\Course;
 use Database;
+use DateTime;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
@@ -119,14 +120,14 @@ class CQuiz
     protected $maxAttempt;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="start_time", type="datetime", nullable=true)
      */
     protected $startTime;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="end_time", type="datetime", nullable=true)
      */
@@ -279,7 +280,7 @@ public function getCourse()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getQuizzes()->add($this);
@@ -544,7 +545,7 @@ public function getMaxAttempt()
     /**
      * Set startTime.
      *
-     * @param \DateTime $startTime
+     * @param DateTime $startTime
      *
      * @return CQuiz
      */
@@ -558,7 +559,7 @@ public function setStartTime($startTime)
     /**
      * Get startTime.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getStartTime()
     {
@@ -568,7 +569,7 @@ public function getStartTime()
     /**
      * Set endTime.
      *
-     * @param \DateTime $endTime
+     * @param DateTime $endTime
      *
      * @return CQuiz
      */
@@ -582,7 +583,7 @@ public function setEndTime($endTime)
     /**
      * Get endTime.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getEndTime()
     {
@@ -942,6 +943,7 @@ public function getMaxScore()
         foreach ($this->relQuestions as $relQuestion) {
             $maxScore += $relQuestion->getQuestion()->getPonderation();
         }
+
         return $maxScore;
     }
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CTool.php b/src/Chamilo/CourseBundle/Entity/CTool.php
index 26e4f2771f9..ad59820f0cc 100644
--- a/src/Chamilo/CourseBundle/Entity/CTool.php
+++ b/src/Chamilo/CourseBundle/Entity/CTool.php
@@ -496,7 +496,7 @@ public function getCourse()
      *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
         $this->course->getTools()->add($this);

From f9c228f88ae308733b635b5762a728637f488a0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Sun, 12 Jul 2020 10:54:44 +0200
Subject: [PATCH 04/16] Minor code style fix (Scrutinizer feedback) - refs
 BT#17453

---
 main/inc/lib/webservices/Rest.php             | 16 ++--
 src/Chamilo/CoreBundle/Entity/Session.php     | 82 +++++++++++++------
 src/Chamilo/CourseBundle/Entity/CDocument.php |  2 +-
 src/Chamilo/CourseBundle/Entity/CLp.php       | 15 ++--
 src/Chamilo/CourseBundle/Entity/CQuiz.php     | 14 ++--
 5 files changed, 83 insertions(+), 46 deletions(-)

diff --git a/main/inc/lib/webservices/Rest.php b/main/inc/lib/webservices/Rest.php
index 32a9bbc67b1..3cb417e0e7b 100644
--- a/main/inc/lib/webservices/Rest.php
+++ b/main/inc/lib/webservices/Rest.php
@@ -2057,14 +2057,14 @@ function (array $exercise) use ($quizIcon) {
      *                    lp_name (learning path name)
      *                    lp_cat_id (learning path category id)
      *                    items, a list of items which are arrays with these keys :
-     *                      display_order_id (position AND local item identifier, used in parent_id and prerequisite_id)
-     *                      parent_id (references display_order_id)
-     *                      type (c_lp_item.item_type : dir, document, quiz…)
-     *                      name_to_find (course resource name)
-     *                      title (learning path item title)
-     *                      prerequisite_id (references display_order_id)
-     *                      prerequisite_min_score
-     *                      prerequisite_max_score
+     *                    * display_order_id (position AND local item identifier, used in parent_id and prerequisite_id)
+     *                    * parent_id (references display_order_id)
+     *                    * type (c_lp_item.item_type : dir, document, quiz…)
+     *                    * name_to_find (course resource name)
+     *                    * title (learning path item title)
+     *                    * prerequisite_id (references display_order_id)
+     *                    * prerequisite_min_score
+     *                    * prerequisite_max_score
      *
      * @throws Exception if an item is not found by type and name or a parameter is missing
      *
diff --git a/src/Chamilo/CoreBundle/Entity/Session.php b/src/Chamilo/CoreBundle/Entity/Session.php
index cb07b2ec503..5adedfd1d29 100644
--- a/src/Chamilo/CoreBundle/Entity/Session.php
+++ b/src/Chamilo/CoreBundle/Entity/Session.php
@@ -9,6 +9,7 @@
 use Database;
 use Datetime;
 use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
 use Doctrine\Common\Collections\Criteria;
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
@@ -250,7 +251,7 @@ class Session
      */
     public function __construct()
     {
-        $this->items = new ArrayCollection();
+        //$this->items = new ArrayCollection();
 
         $this->nbrClasses = 0;
         $this->nbrUsers = 0;
@@ -269,7 +270,7 @@ public function __construct()
         $this->showDescription = false;
         $this->category = null;
         $this->studentPublications = new ArrayCollection();
-        $this->sendSubscriptionNotification = 0;
+        $this->sendSubscriptionNotification = false;
     }
 
     /**
@@ -383,8 +384,10 @@ public function addUser(SessionRelUser $user)
 
     /**
      * @param int $status
+     *
+     * @param User $user
      */
-    public function addUserInSession($status, User $user)
+    public function addUserInSession($status, $user)
     {
         $sessionRelUser = new SessionRelUser();
         $sessionRelUser->setSession($this);
@@ -395,9 +398,10 @@ public function addUserInSession($status, User $user)
     }
 
     /**
+     * @param SessionRelUser $subscription
      * @return bool
      */
-    public function hasUser(SessionRelUser $subscription)
+    public function hasUser($subscription)
     {
         if ($this->getUsers()->count()) {
             $criteria = Criteria::create()->where(
@@ -443,9 +447,11 @@ public function addCourses(SessionRelCourse $course)
     }
 
     /**
+     * @param Course $course
+     *
      * @return bool
      */
-    public function hasCourse(Course $course)
+    public function hasCourse($course)
     {
         if ($this->getCourses()->count()) {
             $criteria = Criteria::create()->where(
@@ -462,9 +468,11 @@ public function hasCourse(Course $course)
     /**
      * Check for existence of a relation (SessionRelCourse) between a course and this session.
      *
+     * @param Course $course
+     *
      * @return bool whether the course is related to this session
      */
-    public function isRelatedToCourse(Course $course)
+    public function isRelatedToCourse($course)
     {
         return !is_null(
             Database::getManager()->getRepository('ChamiloCoreBundle:SessionRelCourse')->findOneBy([
@@ -515,12 +523,13 @@ public function removeUserCourseSubscription($user, $course)
     }
 
     /**
-     * @param int $status if not set it will check if the user is registered
-     *                    with any status
+     * @param User   $user
+     * @param Course $course
+     * @param int    $status if not set it will check if the user is registered with any status
      *
      * @return bool
      */
-    public function hasUserInCourse(User $user, Course $course, $status = null)
+    public function hasUserInCourse($user, $course, $status = null)
     {
         $relation = $this->getUserInCourse($user, $course, $status);
 
@@ -528,27 +537,35 @@ public function hasUserInCourse(User $user, Course $course, $status = null)
     }
 
     /**
+     * @param User $user
+     * @param Course $course
+     *
      * @return bool
      */
-    public function hasStudentInCourse(User $user, Course $course)
+    public function hasStudentInCourse($user, $course)
     {
         return $this->hasUserInCourse($user, $course, self::STUDENT);
     }
 
     /**
+     * @param User   $user
+     * @param Course $course
+     *
      * @return bool
      */
-    public function hasCoachInCourseWithStatus(User $user, Course $course)
+    public function hasCoachInCourseWithStatus($user, $course)
     {
         return $this->hasUserInCourse($user, $course, self::COACH);
     }
 
     /**
+     * @param User $user
+     * @param Course $course
      * @param string $status
      *
-     * @return \Doctrine\Common\Collections\Collection|static
+     * @return Collection|static
      */
-    public function getUserInCourse(User $user, Course $course, $status = null)
+    public function getUserInCourse($user, $course, $status = null)
     {
         $criteria = Criteria::create()->where(
             Criteria::expr()->eq('course', $course)
@@ -1041,9 +1058,11 @@ public function addUserCourseSubscription(SessionRelCourseRelUser $subscription)
     }
 
     /**
+     * @param Course $course
+     *
      * @return SessionRelCourse
      */
-    public function getCourseSubscription(Course $course)
+    public function getCourseSubscription($course)
     {
         $criteria = Criteria::create()->where(
             Criteria::expr()->eq('course', $course)
@@ -1061,9 +1080,11 @@ public function getCourseSubscription(Course $course)
      * Add a user course subscription.
      * If user status in session is student, then increase number of course users.
      *
-     * @param int $status
+     * @param int    $status
+     * @param User   $user
+     * @param Course $course
      */
-    public function addUserInCourse($status, User $user, Course $course)
+    public function addUserInCourse($status, $user, $course)
     {
         $userRelCourseRelSession = new SessionRelCourseRelUser();
         $userRelCourseRelSession->setCourse($course);
@@ -1082,9 +1103,11 @@ public function addUserInCourse($status, User $user, Course $course)
     }
 
     /**
+     * @param SessionRelCourseRelUser $subscription
+     *
      * @return bool
      */
-    public function hasUserCourseSubscription(SessionRelCourseRelUser $subscription)
+    public function hasUserCourseSubscription($subscription)
     {
         if ($this->getUserCourseSubscriptions()->count()) {
             $criteria = Criteria::create()->where(
@@ -1111,9 +1134,11 @@ public function getCurrentCourse()
     }
 
     /**
+     * @param Course $course
+     *
      * @return $this
      */
-    public function setCurrentCourse(Course $course)
+    public function setCurrentCourse($course)
     {
         // If the session is registered in the course session list.
         if ($this->getCourses()->contains($course->getId())) {
@@ -1128,7 +1153,7 @@ public function setCurrentCourse(Course $course)
      *
      * @param bool $sendNotification
      *
-     * @return \Chamilo\CoreBundle\Entity\Session
+     * @return Session
      */
     public function setSendSubscriptionNotification($sendNotification)
     {
@@ -1150,11 +1175,12 @@ public function getSendSubscriptionNotification()
     /**
      * Get user from course by status.
      *
-     * @param int $status
+     * @param Course $course
+     * @param int    $status
      *
-     * @return \Doctrine\Common\Collections\ArrayCollection|\Doctrine\Common\Collections\Collection
+     * @return ArrayCollection|Collection
      */
-    public function getUserCourseSubscriptionsByStatus(Course $course, $status)
+    public function getUserCourseSubscriptionsByStatus($course, $status)
     {
         $criteria = Criteria::create()
             ->where(
@@ -1168,9 +1194,11 @@ public function getUserCourseSubscriptionsByStatus(Course $course, $status)
     }
 
     /**
+     * @param ArrayCollection $studentPublications
+     *
      * @return Session
      */
-    public function setStudentPublications(ArrayCollection $studentPublications)
+    public function setStudentPublications($studentPublications)
     {
         $this->studentPublications = new ArrayCollection();
 
@@ -1182,9 +1210,11 @@ public function setStudentPublications(ArrayCollection $studentPublications)
     }
 
     /**
+     * @param CStudentPublication $studentPublication
+     *
      * @return Session
      */
-    public function addStudentPublication(CStudentPublication $studentPublication)
+    public function addStudentPublication($studentPublication)
     {
         $this->studentPublications[] = $studentPublication;
 
@@ -1202,9 +1232,11 @@ public function getStudentPublications()
     }
 
     /**
+     * @param Course $course
+     *
      * @return ArrayCollection
      */
-    public function getUsersSubscriptionsInCourse(Course $course)
+    public function getUsersSubscriptionsInCourse($course)
     {
         $criteria = Criteria::create()
             ->where(
diff --git a/src/Chamilo/CourseBundle/Entity/CDocument.php b/src/Chamilo/CourseBundle/Entity/CDocument.php
index 18460438236..1fa3a2d1bee 100644
--- a/src/Chamilo/CourseBundle/Entity/CDocument.php
+++ b/src/Chamilo/CourseBundle/Entity/CDocument.php
@@ -109,7 +109,7 @@ class CDocument
     public function __construct()
     {
         $this->size = 0;
-        $this->readonly = 0;
+        $this->readonly = false;
         $this->sessionId = 0;
     }
 
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index cbdcde1a147..526816f4c0f 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -11,6 +11,7 @@
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 use Doctrine\ORM\OptimisticLockException;
+use Exception;
 
 /**
  * CLp.
@@ -323,17 +324,17 @@ public function __construct()
         $this->useMaxScore = 1;
         $this->lpType = 1;
         $this->path = '';
-        $this->forceCommit = 0;
+        $this->forceCommit = false;
         $this->contentMaker = 'Chamilo';
         $this->contentLicense = '';
         $this->jsLib = '';
-        $this->debug = 0;
+        $this->debug = false;
         $this->theme = '';
         $this->previewImage = '';
         $this->author = '';
-        $this->prerequisite = ''; // 0 ?
-        $this->hideTocFrame = 0;
-        $this->seriousgameMode = 0;
+        $this->prerequisite = 0;
+        $this->hideTocFrame = false;
+        $this->seriousgameMode = false;
         $this->autolaunch = 0;
         $this->maxAttempts = 0;
         $this->subscribeUsers = 0;
@@ -357,11 +358,15 @@ public static function getRepository()
      * Computes displayOrder if still zéro.
      *
      * @ORM\PrePersist
+     * @throws Exception
      */
     public function prePersist()
     {
         if (is_null($this->course)) {
             $this->course = Course::getCurrentCourse();
+            if (is_null($this->course)) {
+                throw new Exception('cannot persist a leaning path without course');
+            }
         }
 
         $coursesOtherLearningPaths = $this->course->getLearningPaths()->filter(function ($lp) {
diff --git a/src/Chamilo/CourseBundle/Entity/CQuiz.php b/src/Chamilo/CourseBundle/Entity/CQuiz.php
index cda0350180c..c4ca2536cad 100644
--- a/src/Chamilo/CourseBundle/Entity/CQuiz.php
+++ b/src/Chamilo/CourseBundle/Entity/CQuiz.php
@@ -71,9 +71,9 @@ class CQuiz
     protected $sound;
 
     /**
-     * @var bool
+     * @var int
      *
-     * @ORM\Column(name="type", type="boolean", nullable=false)
+     * @ORM\Column(name="type", type="integer", nullable=false)
      */
     protected $type;
 
@@ -246,14 +246,14 @@ public function __construct()
         $this->hideQuestionTitle = false;
         $this->type = ONE_PER_PAGE;
         $this->random = 0;
-        $this->randomAnswers = 0;
-        $this->active = 1;
+        $this->randomAnswers = false;
+        $this->active = true;
         $this->resultsDisabled = 0;
         $this->maxAttempt = 1;
         $this->feedbackType = 0;
         $this->expiredTime = 0;
         $this->propagateNeg = 0;
-        $this->saveCorrectAnswers = 0;
+        $this->saveCorrectAnswers = false;
         $this->reviewAnswers = 0;
         $this->randomByCategory = 0;
         $this->displayCategoryName = 0;
@@ -377,7 +377,7 @@ public function getSound()
     /**
      * Set type.
      *
-     * @param bool $type
+     * @param int $type
      *
      * @return CQuiz
      */
@@ -391,7 +391,7 @@ public function setType($type)
     /**
      * Get type.
      *
-     * @return bool
+     * @return int
      */
     public function getType()
     {

From e2fa09759eab79f60ee75a270a42108d67bf9d05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Mon, 13 Jul 2020 14:34:16 +0200
Subject: [PATCH 05/16] reusing 'Test Course' if already created - refs
 BT#17453

---
 .../api/tests/CreateLearningPathTest.php      | 126 ++++++++----------
 1 file changed, 59 insertions(+), 67 deletions(-)

diff --git a/main/webservices/api/tests/CreateLearningPathTest.php b/main/webservices/api/tests/CreateLearningPathTest.php
index 830cbe66d57..af59401fa81 100644
--- a/main/webservices/api/tests/CreateLearningPathTest.php
+++ b/main/webservices/api/tests/CreateLearningPathTest.php
@@ -19,7 +19,7 @@
 /**
  * Class CreateLearningPathTest
  *
- * SUBSCRIBE_USER_TO_SESSION_FROM_USERNAME webservice unit tests
+ * CREATE_LEARNINGPATH webservice unit tests
  */
 class CreateLearningPathTest extends V2TestCase
 {
@@ -44,9 +44,6 @@ class CreateLearningPathTest extends V2TestCase
     /** @var CQuiz */
     public static $quiz;
 
-    /** @var CLp[] */
-    public static $learningPaths;
-
     public function action()
     {
         return Rest::CREATE_LEARNINGPATH;
@@ -63,73 +60,71 @@ public static function setUpBeforeClass(): void
     {
         parent::setUpBeforeClass();
 
-        // create a test session
-        self::$session = (new Session())
-            ->setName('Test Session '.time());
-        Database::getManager()->persist(self::$session);
-
-        // create a test course
-        self::$course = (new Course())
-            ->setCode('TESTCOURSE'.time())
-            ->setTitle('Test Course '.time());
-        Database::getManager()->persist(self::$course);
-        Database::getManager()->flush(self::$course); // ensures the course directory is initialized
-
-        // create a test category
-        self::$category = (new CLpCategory())
-            ->setCourse(self::$course)
-            ->setName('Test Category '.time());
-        Database::getManager()->persist(self::$category);
-
-        // create course elements
-        self::$document = CDocument::fromFile(
-            __FILE__,
-            self::$course,
-            'test_document'.time().'.txt',
-            'Test Document'.time()
-        );
-        Database::getManager()->persist(self::$document);
+        self::$session = Session::getRepository()->findOneByName('Test Session');
+        if (is_null(self::$session)) {
+            self::$session = (new Session())
+                ->setName('Test Session');
+            Database::getManager()->persist(self::$session);
+            Database::getManager()->flush();
+        }
 
-        self::$forum = (new CForumForum())
-            ->setCourse(self::$course)
-            ->setForumTitle('Test Forum '.time());
-        Database::getManager()->persist(self::$forum);
+        self::$course = Course::getRepository()->findOneByCode('TESTCOURSE');
+        if (is_null(self::$course)) {
+            self::$course = (new Course())
+                ->setCode('TESTCOURSE')
+                ->setTitle('Test Course');
+            Database::getManager()->persist(self::$course);
+            Database::getManager()->flush();
+        }
 
-        self::$link = (new CLink())
-            ->setCourse(self::$course)
-            ->setTitle('Test Link '.time())
-            ->setUrl('https://chamilo.org/');
-        Database::getManager()->persist(self::$link);
+        self::$category = CLpCategory::getRepository()->findOneByName('Test Category');
+        if (is_null(self::$category)) {
+            self::$category = (new CLpCategory())
+                ->setCourse(self::$course)
+                ->setName('Test Category');
+            Database::getManager()->persist(self::$category);
+            Database::getManager()->flush();
+        }
 
-        self::$quiz = (new CQuiz())
-            ->setCourse(self::$course)
-            ->setTitle('Test Quiz '.time());
-        Database::getManager()->persist(self::$quiz);
+        self::$document = CDocument::getRepository()->findOneByTitle('Test Document');
+        if (is_null(self::$document)) {
+            self::$document = CDocument::fromFile(
+                __FILE__,
+                self::$course,
+                'test_document.txt',
+                'Test Document'
+            );
+            Database::getManager()->persist(self::$document);
+            Database::getManager()->flush();
+        }
 
-        Database::getManager()->flush();
+        self::$forum = CForumForum::getRepository()->findOneByForumTitle('Test Forum');
+        if (is_null(self::$forum)) {
+            self::$forum = (new CForumForum())
+                ->setCourse(self::$course)
+                ->setForumTitle('Test Forum');
+            Database::getManager()->persist(self::$forum);
+            Database::getManager()->flush();
+        }
 
-        self::$learningPaths = [];
-    }
+        self::$link = Clink::getRepository()->findOneByTitle('Test Link');
+        if (is_null(self::$link)) {
+            self::$link = (new CLink())
+                ->setCourse(self::$course)
+                ->setTitle('Test Link ')
+                ->setUrl('https://chamilo.org/');
+            Database::getManager()->persist(self::$link);
+            Database::getManager()->flush();
+        }
 
-    /**
-     * @inheritDoc
-     *
-     * @throws OptimisticLockException
-     */
-    public static function tearDownAfterClass(): void
-    {
-        parent::tearDownAfterClass();
-        foreach (self::$learningPaths as $learningPath) {
-            Database::getManager()->remove($learningPath);
+        self::$quiz = CQuiz::getRepository()->findOneByTitle('Test Quiz');
+        if (is_null(self::$quiz)) {
+            self::$quiz = (new CQuiz())
+                ->setCourse(self::$course)
+                ->setTitle('Test Quiz ');
+            Database::getManager()->persist(self::$quiz);
+            Database::getManager()->flush();
         }
-        Database::getManager()->remove(self::$quiz);
-        Database::getManager()->remove(self::$link);
-        Database::getManager()->remove(self::$forum);
-        Database::getManager()->remove(self::$document);
-        Database::getManager()->remove(self::$category);
-        Database::getManager()->remove(self::$course);
-        Database::getManager()->remove(self::$session);
-        Database::getManager()->flush();
     }
 
     /**
@@ -154,7 +149,6 @@ public function testCreateEmptyLearningPathWithoutSessionNorCategory()
         // assert the learning path was created
         /** @var CLp $learningPath */
         $learningPath = CLp::getRepository()->find($learningPathId);
-        self::$learningPaths[] = $learningPath;
 
         self::assertNotNull($learningPath);
         // in the right course
@@ -190,7 +184,6 @@ public function testCreateEmptyLearningPath()
         // assert the learning path was created
         /** @var CLp $learningPath */
         $learningPath = CLp::getRepository()->find($learningPathId);
-        self::$learningPaths[] = $learningPath;
 
         self::assertNotNull($learningPath);
         // in the right session, course and category
@@ -308,7 +301,6 @@ public function testCreateLearningPathWithItems()
         // assert the learning path was created as specified
         /** @var CLp $learningPath */
         $learningPath = CLp::getRepository()->find($learningPathId);
-        self::$learningPaths[] = $learningPath;
 
         self::assertNotNull($learningPath);
         self::assertEquals(self::$session->getId(), $learningPath->getSessionId());

From 8081540c8509c778bfe581b8392ae0eef71b0296 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Mon, 13 Jul 2020 14:36:08 +0200
Subject: [PATCH 06/16] Course and LP can be created through ORM classes - refs
 BT#17453

Partialy simplified and already improved performance of these API functions:
 api_item_property_update
 api_max_sort_value
 CourseManager::create_course
 AddCourse::register_course
 CourseManager::fillCourse
 AddCourse::fill_db_course
 learnpath::add_lp
---
 main/inc/lib/add_course.lib.inc.php           | 682 ++++--------------
 main/inc/lib/api.lib.php                      | 350 ++-------
 main/inc/lib/course.lib.php                   |  51 +-
 main/lp/learnpath.class.php                   |  73 +-
 .../CoreBundle/Entity/AccessUrlRelCourse.php  |   7 +-
 src/Chamilo/CoreBundle/Entity/Course.php      | 365 +++++++++-
 .../CoreBundle/Entity/CourseRelUser.php       |  20 +-
 src/Chamilo/CoreBundle/Entity/Session.php     |  16 +-
 .../CoreBundle/Entity/UserCourseCategory.php  |  10 +
 .../CourseBundle/Entity/CCourseSetting.php    |  35 +
 .../CourseBundle/Entity/CGroupInfo.php        |  10 +
 .../CourseBundle/Entity/CItemProperty.php     | 118 ++-
 src/Chamilo/CourseBundle/Entity/CLp.php       |  47 +-
 src/Chamilo/CourseBundle/Entity/CLpItem.php   |  32 +-
 src/Chamilo/CourseBundle/Entity/CTool.php     |  30 +-
 src/Chamilo/UserBundle/Entity/User.php        |  56 +-
 16 files changed, 911 insertions(+), 991 deletions(-)

diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index d553779dfcb..7b63cbff213 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -1,7 +1,11 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use Chamilo\CoreBundle\Entity\AccessUrl;
+use Chamilo\CoreBundle\Entity\AccessUrlRelCourse;
+use Chamilo\CoreBundle\Entity\CourseRelUser;
 use Chamilo\CourseBundle\Entity\CToolIntro;
+use Doctrine\ORM\OptimisticLockException;
 
 /**
  * Class AddCourse.
@@ -84,122 +88,6 @@ public static function define_course_keys(
         return $keys;
     }
 
-    /**
-     * Initializes a file repository for a newly created course.
-     *
-     * @param string Course repository
-     * @param string Course code
-     *
-     * @return int
-     * @assert (null,null) === false
-     */
-    public static function prepare_course_repository($course_repository)
-    {
-        $perm = api_get_permissions_for_new_directories();
-        $perm_file = api_get_permissions_for_new_files();
-        $htmlpage = "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Not authorized</title>\n  </head>\n  <body>\n  </body>\n</html>";
-        $cp = api_get_path(SYS_COURSE_PATH).$course_repository;
-
-        //Creating document folder
-        mkdir($cp, $perm);
-        mkdir($cp.'/document', $perm);
-        $cpt = $cp.'/document/index.html';
-        $fd = fopen($cpt, 'w');
-        fwrite($fd, $htmlpage);
-        fclose($fd);
-
-        /*
-        @chmod($cpt, $perm_file);
-        @copy($cpt, $cp . '/document/index.html');
-        mkdir($cp . '/document/images', $perm);
-        @copy($cpt, $cp . '/document/images/index.html');
-        mkdir($cp . '/document/images/gallery/', $perm);
-        @copy($cpt, $cp . '/document/images/gallery/index.html');
-        mkdir($cp . '/document/shared_folder/', $perm);
-        @copy($cpt, $cp . '/document/shared_folder/index.html');
-        mkdir($cp . '/document/audio', $perm);
-        @copy($cpt, $cp . '/document/audio/index.html');
-        mkdir($cp . '/document/flash', $perm);
-        @copy($cpt, $cp . '/document/flash/index.html');
-        mkdir($cp . '/document/video', $perm);
-        @copy($cpt, $cp . '/document/video/index.html');    */
-
-        //Creatind dropbox folder
-        mkdir($cp.'/dropbox', $perm);
-        $cpt = $cp.'/dropbox/index.html';
-        $fd = fopen($cpt, 'w');
-        fwrite($fd, $htmlpage);
-        fclose($fd);
-        @chmod($cpt, $perm_file);
-        mkdir($cp.'/group', $perm);
-        @copy($cpt, $cp.'/group/index.html');
-        mkdir($cp.'/page', $perm);
-        @copy($cpt, $cp.'/page/index.html');
-        mkdir($cp.'/scorm', $perm);
-        @copy($cpt, $cp.'/scorm/index.html');
-        mkdir($cp.'/upload', $perm);
-        @copy($cpt, $cp.'/upload/index.html');
-        mkdir($cp.'/upload/forum', $perm);
-        @copy($cpt, $cp.'/upload/forum/index.html');
-        mkdir($cp.'/upload/forum/images', $perm);
-        @copy($cpt, $cp.'/upload/forum/images/index.html');
-        mkdir($cp.'/upload/test', $perm);
-        @copy($cpt, $cp.'/upload/test/index.html');
-        mkdir($cp.'/upload/blog', $perm);
-        @copy($cpt, $cp.'/upload/blog/index.html');
-        mkdir($cp.'/upload/learning_path', $perm);
-        @copy($cpt, $cp.'/upload/learning_path/index.html');
-        mkdir($cp.'/upload/learning_path/images', $perm);
-        @copy($cpt, $cp.'/upload/learning_path/images/index.html');
-        mkdir($cp.'/upload/calendar', $perm);
-        @copy($cpt, $cp.'/upload/calendar/index.html');
-        mkdir($cp.'/upload/calendar/images', $perm);
-        @copy($cpt, $cp.'/upload/calendar/images/index.html');
-        mkdir($cp.'/work', $perm);
-        @copy($cpt, $cp.'/work/index.html');
-        mkdir($cp.'/upload/announcements', $perm);
-        @copy($cpt, $cp.'/upload/announcements/index.html');
-        mkdir($cp.'/upload/announcements/images', $perm);
-        @copy($cpt, $cp.'/upload/announcements/images/index.html');
-
-        //Oral expression question type
-        mkdir($cp.'/exercises', $perm);
-        @copy($cpt, $cp.'/exercises/index.html');
-
-        // Create .htaccess in the dropbox directory.
-        $fp = fopen($cp.'/dropbox/.htaccess', 'w');
-        fwrite(
-            $fp,
-            "AuthName AllowLocalAccess
-                       AuthType Basic
-
-                       order deny,allow
-                       deny from all
-
-                       php_flag zlib.output_compression off"
-        );
-        fclose($fp);
-
-        // Build index.php of the course.
-        /*$fd = fopen($cp . '/index.php', 'w');
-
-        // str_replace() removes \r that cause squares to appear at the end of each line
-        //@todo fix the harcoded include
-        $string = str_replace(
-            "\r",
-            "",
-            "<?" . "php
-        \$cidReq = \"$course_code\";
-        \$dbname = \"$course_code\";
-
-        include(\"" . api_get_path(SYS_CODE_PATH) . "course_home/course_home.php\");
-        ?>"
-        );
-        fwrite($fd, $string);
-        @chmod($cp . '/index.php', $perm_file);*/
-        return 0;
-    }
-
     /**
      * Gets an array with all the course tables (deprecated?).
      *
@@ -430,204 +318,12 @@ public static function fill_db_course(
         $courseInfo = api_get_course_info_by_id($course_id);
         $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
 
-        $tbl_course_homepage = Database::get_course_table(TABLE_TOOL_LIST);
         $TABLEGROUPCATEGORIES = Database::get_course_table(TABLE_GROUP_CATEGORY);
         $TABLEITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
         $TABLETOOLDOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
         $TABLESETTING = Database::get_course_table(TABLE_COURSE_SETTING);
         $TABLEGRADEBOOK = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
         $TABLEGRADEBOOKLINK = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
-        $visible_for_course_admin = 0;
-        /*    Course tools  */
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 1, '".TOOL_COURSE_DESCRIPTION."','course_description/index.php','info.gif','".self::string2binary(
-                api_get_setting(
-                    'course_create_active_tools',
-                    'course_description'
-                )
-            )."','0','squaregrey.gif', 0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 2, '".TOOL_CALENDAR_EVENT."','calendar/agenda.php','agenda.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'agenda')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage  (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 3, '".TOOL_DOCUMENT."','document/document.php','folder_document.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'documents')
-            )."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 4, '".TOOL_LEARNPATH."','lp/lp_controller.php','scorms.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'learning_path')
-            )."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-             VALUES ($course_id, 5, '".TOOL_LINK."','link/link.php','links.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'links')
-            )."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage  (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES  ($course_id, 6, '".TOOL_QUIZ."','exercise/exercise.php','quiz.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'quiz')
-            )."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 7, '".TOOL_ANNOUNCEMENT."','announcements/announcements.php','valves.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'announcements')
-            )."','0','squaregrey.gif', 0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 8, '".TOOL_FORUM."','forum/index.php','forum.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'forums')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 9, '".TOOL_DROPBOX."','dropbox/index.php','dropbox.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'dropbox')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 10, '".TOOL_USER."','user/user.php','members.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'users')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 11, '".TOOL_GROUP."','group/group.php','group.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'groups')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 12, '".TOOL_CHAT."','chat/chat.php','chat.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'chat')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 13, '".TOOL_STUDENTPUBLICATION."','work/work.php','works.gif','".self::string2binary(
-                api_get_setting(
-                    'course_create_active_tools',
-                    'student_publications'
-                )
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 14, '".TOOL_SURVEY."','survey/survey_list.php','survey.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'survey')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 15, '".TOOL_WIKI."','wiki/index.php','wiki.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'wiki')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 16, '".TOOL_GRADEBOOK."','gradebook/index.php','gradebook.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'gradebook')
-            )."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 17, '".TOOL_GLOSSARY."','glossary/index.php','glossary.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'glossary')
-            )."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 18, '".TOOL_NOTEBOOK."','notebook/index.php','notebook.gif','".self::string2binary(
-                api_get_setting('course_create_active_tools', 'notebook')
-            )."','0','squaregrey.gif',0,'_self','interaction','0')"
-        );
-        if (api_get_configuration_value('allow_portfolio_tool')) {
-            $tId = Database::insert(
-                $tbl_course_homepage,
-                [
-                    'c_id' => $course_id,
-                    'name' => 'portfolio',
-                    'link' => 'portfolio/index.php',
-                    'image' => 'wiki_task.png',
-                    'visibility' => api_get_setting('course_create_active_tools', 'portfolio') == 'true' ? 1 : 0,
-                    'admin' => 0,
-                    'address' => 'squaregrey.gif',
-                    'added_tool' => 0,
-                    'target' => '_self',
-                    'category' => 'interaction',
-                    'session_id' => 0,
-                ]
-            );
-            Database::update(
-                $tbl_course_homepage,
-                ['id' => $tId],
-                ['iid = ?' => $tId]
-            );
-        }
-
-        $setting = intval(self::string2binary(
-            api_get_setting('course_create_active_tools', 'attendances')
-        ));
-
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 19, '".TOOL_ATTENDANCE."','attendance/index.php','attendance.gif','".$setting."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-
-        $setting = intval(self::string2binary(
-            api_get_setting('course_create_active_tools', 'course_progress')
-        ));
-
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 20, '".TOOL_COURSE_PROGRESS."','course_progress/index.php','course_progress.gif','".$setting."','0','squaregrey.gif',0,'_self','authoring','0')"
-        );
-
-        if (api_get_setting('search_enabled') === 'true') {
-            Database::query(
-                "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-                VALUES ($course_id, 23, '".TOOL_SEARCH."','search/','info.gif','".self::string2binary(
-                    api_get_setting(
-                        'course_create_active_tools',
-                        'enable_search'
-                    )
-                )."','0','search.gif',0,'_self','authoring','0')"
-            );
-        }
-
-        $sql = "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-                VALUES ($course_id, 24,'".TOOL_BLOGS."','blog/blog_admin.php','blog_admin.gif','".intval(
-            self::string2binary(
-                api_get_setting('course_create_active_tools', 'blogs')
-        )
-            )."','1','squaregrey.gif',0,'_self','admin','0')";
-        Database::query($sql);
-
-        /*  Course homepage tools for course admin only  */
-        Database::query(
-            "INSERT INTO $tbl_course_homepage  (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 25, '".TOOL_TRACKING."','tracking/courseLog.php','statistics.gif','$visible_for_course_admin','1','', 0,'_self','admin','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 26, '".TOOL_COURSE_SETTING."','course_info/infocours.php','reference.gif','$visible_for_course_admin','1','', 0,'_self','admin','0')"
-        );
-        Database::query(
-            "INSERT INTO $tbl_course_homepage (c_id, id, name, link, image, visibility, admin, address, added_tool, target, category, session_id)
-            VALUES ($course_id, 27, '".TOOL_COURSE_MAINTENANCE."','course_info/maintenance.php','backup.gif','$visible_for_course_admin','1','',0,'_self', 'admin','0')"
-        );
 
         $alert = api_get_setting('email_alert_manager_on_new_quiz');
         if ($alert === 'true') {
@@ -1200,240 +896,180 @@ public static function string2binary($variable)
      */
     public static function register_course($params, $accessUrlId = 1)
     {
-        global $error_msg, $firstExpirationDelay;
-        $title = $params['title'];
-        // Fix amp
-        $title = str_replace('&amp;', '&', $title);
-        $code = $params['code'];
-        $visual_code = $params['visual_code'];
-        $directory = $params['directory'];
-        $tutor_name = isset($params['tutor_name']) ? $params['tutor_name'] : null;
-        $category_code = isset($params['course_category']) ? $params['course_category'] : '';
-        $course_language = isset($params['course_language']) && !empty($params['course_language']) ? $params['course_language'] : api_get_setting(
-            'platformLanguage'
-        );
-        $user_id = empty($params['user_id']) ? api_get_user_id() : (int) $params['user_id'];
-        $department_name = isset($params['department_name']) ? $params['department_name'] : null;
-        $department_url = isset($params['department_url']) ? $params['department_url'] : null;
-        $disk_quota = isset($params['disk_quota']) ? $params['disk_quota'] : null;
-
-        if (!isset($params['visibility'])) {
-            $default_course_visibility = api_get_setting(
-                'courses_default_creation_visibility'
-            );
-            if (isset($default_course_visibility)) {
-                $visibility = $default_course_visibility;
-            } else {
-                $visibility = COURSE_VISIBILITY_OPEN_PLATFORM;
+        $title = str_replace('&amp;', '&', $params['title']);
+        $code = array_key_exists('code', $params) ? $params['code'] : null;
+        $visualCode = array_key_exists('visual_code', $params) ? $params['visual_code'] : null;
+        $directory = array_key_exists('directory', $params) ? $params['directory'] : null;
+        $tutorName = array_key_exists('tutor_name', $params) ? $params['tutor_name'] : null;
+        $categoryCode = array_key_exists('course_category', $params) ? $params['course_category'] : '';
+        $courseLanguage = array_key_exists('course_language', $params) && !empty($params['course_language'])
+            ? $params['course_language']
+            : api_get_setting('platformLanguage');
+        $userId = empty($params['user_id']) ? api_get_user_id() : (int) $params['user_id'];
+        $departmentName = array_key_exists('department_name', $params) ? $params['department_name'] : null;
+        $departmentUrl = array_key_exists('department_url', $params) ? $params['department_url'] : null;
+        $diskQuota = array_key_exists('disk_quota', $params) ? $params['disk_quota'] : null;
+        $visibility = array_key_exists('visibility', $params) ? $params['visibility'] : null;
+        $subscribe = array_key_exists('subscribe', $params) ? $params['subscribe'] : null;
+        $unsubscribe = array_key_exists('unsubscribe', $params) ? $params['unsubscribe'] : null;
+        $teachers = array_key_exists('teachers', $params) ? $params['teachers'] : null;
+
+        $expirationDate = null;
+        if (array_key_exists('expiration_date', $params)) {
+            $date = $params['expiration_date'];
+            try {
+                $expirationDate = new DateTime(api_get_utc_datetime($date), new DateTimeZone('utc'));
+            } catch (Exception $exception) {
+                error_log(sprintf('expiration_date "%s" is invalid', $date));
+                return 0;
             }
-        } else {
-            $visibility = $params['visibility'];
-        }
-
-        if (isset($params['subscribe'])) {
-            $subscribe = (int) $params['subscribe'];
-        } else {
-            $subscribe = $visibility == COURSE_VISIBILITY_OPEN_PLATFORM ? 1 : 0;
         }
-        $unsubscribe = isset($params['unsubscribe']) ? (int) $params['unsubscribe'] : 0;
-        $expiration_date = isset($params['expiration_date']) ? $params['expiration_date'] : null;
-        $teachers = isset($params['teachers']) ? $params['teachers'] : null;
-        $status = isset($params['status']) ? $params['status'] : null;
-
-        $TABLECOURSE = Database::get_main_table(TABLE_MAIN_COURSE);
-        $TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
-
-        $ok_to_register_course = true;
 
-        // Check whether all the needed parameters are present.
-        if (empty($code)) {
-            $error_msg[] = 'courseSysCode is missing';
-            $ok_to_register_course = false;
-        }
-        if (empty($visual_code)) {
-            $error_msg[] = 'courseScreenCode is missing';
-            $ok_to_register_course = false;
-        }
-        if (empty($directory)) {
-            $error_msg[] = 'courseRepository is missing';
-            $ok_to_register_course = false;
+        $user = api_get_user_entity($userId);
+        if (is_null($user)) {
+            error_log(sprintf('user_id "%s" is invalid', $userId));
+            return 0;
         }
 
-        if (empty($title)) {
-            $error_msg[] = 'title is missing';
-            $ok_to_register_course = false;
+        $course = (new \Chamilo\CoreBundle\Entity\Course())
+            ->setCode($code)
+            ->setDirectory($directory)
+            ->setCourseLanguage($courseLanguage)
+            ->setTitle($title)
+            ->setCategoryCode($categoryCode)
+            ->setVisibility($visibility)
+            ->setDiskQuota($diskQuota)
+            ->setExpirationDate($expirationDate)
+            ->setTutorName($tutorName)
+            ->setDepartmentName($departmentName)
+            ->setDepartmentUrl($departmentUrl)
+            ->setSubscribe($subscribe)
+            ->setUnsubscribe($unsubscribe)
+            ->setVisualCode($visualCode)
+        ;
+        Database::getManager()->persist($course);
+
+        $addTeacher = isset($params['add_user_as_teacher']) ? $params['add_user_as_teacher'] : true;
+        if ($addTeacher) {
+            $iCourseSort = CourseManager::userCourseSort($userId, $code);
+            $courseRelTutor = (new CourseRelUser())
+                ->setCourse($course)
+                ->setUser($user)
+                ->setStatus(true)
+                ->setTutor(true)
+                ->setSort($iCourseSort)
+                ->setRelationType(0)
+                ->setUserCourseCat(0)
+            ;
+            Database::getManager()->persist($courseRelTutor);
         }
 
-        if (empty($expiration_date)) {
-            $expiration_date = api_get_utc_datetime(
-                time() + $firstExpirationDelay
-            );
-        } else {
-            $expiration_date = api_get_utc_datetime($expiration_date);
+        if (!empty($teachers)) {
+            $sort = $user->getMaxSortValue();
+            if (!is_array($teachers)) {
+                $teachers = [$teachers];
+            }
+            foreach ($teachers as $key) {
+                // Just in case.
+                if ($key == $userId) {
+                    continue;
+                }
+                if (empty($key)) {
+                    continue;
+                }
+                $teacher = api_get_user_entity($key);
+                if (is_null($teacher)) {
+                    continue;
+                }
+                $courseRelTeacher = (new CourseRelUser())
+                    ->setCourse($course)
+                    ->setUser($teacher)
+                    ->setStatus(true)
+                    ->setTutor(false)
+                    ->setSort($sort + 1)
+                    ->setRelationType(0)
+                    ->setUserCourseCat(0)
+                ;
+                Database::getManager()->persist($courseRelTeacher);
+            }
         }
 
-        if ($visibility < 0 || $visibility > 4) {
-            $error_msg[] = 'visibility is invalid';
-            $ok_to_register_course = false;
+        // Adding the course to an URL.
+        $url = AccessUrl::getRepository()->find($accessUrlId);
+        if (!is_null($url)) {
+            $urlRelCourse = (new AccessUrlRelCourse())
+                ->setCourse($course)
+                ->setUrl($url);
+            Database::getManager()->persist($urlRelCourse);
         }
 
-        if (empty($disk_quota)) {
-            $disk_quota = api_get_setting('default_document_quotum');
+        try {
+            Database::getManager()->flush();
+        } catch (OptimisticLockException $exception) {
+            error_log($exception);
+            return 0;
         }
 
-        $time = api_get_utc_datetime();
+        $courseId = $course->getId();
+
+        // Add event to the system log.
+        $userId = api_get_user_id();
+        Event::addEvent(
+            LOG_COURSE_CREATE,
+            LOG_COURSE_CODE,
+            $code,
+            api_get_utc_datetime(),
+            $userId,
+            $courseId
+        );
 
-        if (stripos($department_url, 'http://') === false && stripos(
-                $department_url,
-                'https://'
-            ) === false
-        ) {
-            $department_url = 'http://'.$department_url;
-        }
-        // Just in case
-        if ($department_url === 'http://') {
-            $department_url = '';
-        }
-        $course_id = 0;
+        $send_mail_to_admin = api_get_setting('send_email_to_admin_when_create_course');
 
-        if ($ok_to_register_course) {
-            // Here we must add 2 fields.
-            $course_id = Database::insert(
-                $TABLECOURSE,
-                [
-                    'code' => $code,
-                    'directory' => $directory,
-                    'course_language' => $course_language,
-                    'title' => $title,
-                    'description' => get_lang('CourseDescription'),
-                    'category_code' => $category_code,
-                    'visibility' => $visibility,
-                    'show_score' => 1,
-                    'disk_quota' => (int) $disk_quota,
-                    'creation_date' => $time,
-                    'expiration_date' => $expiration_date,
-                    'last_edit' => $time,
-                    'last_visit' => null,
-                    'tutor_name' => $tutor_name,
-                    'department_name' => $department_name,
-                    'department_url' => $department_url,
-                    'subscribe' => $subscribe,
-                    'unsubscribe' => $unsubscribe,
-                    'visual_code' => $visual_code,
-                ]
+        // @todo Improve code to send to all current portal administrators.
+        if ($send_mail_to_admin === 'true') {
+            $siteName = api_get_setting('siteName');
+            $recipient_email = api_get_setting('emailAdministrator');
+            $recipient_name = api_get_person_name(
+                api_get_setting('administratorName'),
+                api_get_setting('administratorSurname')
             );
+            $iname = api_get_setting('Institution');
+            $subject = get_lang('NewCourseCreatedIn').' '.$siteName.' - '.$iname;
+            $message = get_lang(
+                    'Dear'
+                ).' '.$recipient_name.",\n\n".get_lang(
+                    'MessageOfNewCourseToAdmin'
+                ).' '.$siteName.' - '.$iname."\n";
+            $message .= get_lang('CourseName').' '.$title."\n";
+            $message .= get_lang(
+                    'Category'
+                ).' '.$categoryCode."\n";
+            $message .= get_lang('Tutor').' '.$tutorName."\n";
+            $message .= get_lang('Language').' '.$courseLanguage;
+
+            $userInfo = api_get_user_info($userId);
+            $additionalParameters = [
+                'smsType' => SmsPlugin::NEW_COURSE_BEEN_CREATED,
+                'userId' => $userId,
+                'courseName' => $title,
+                'creatorUsername' => $userInfo['username'],
+            ];
 
-            if ($course_id) {
-                $sort = api_max_sort_value('0', api_get_user_id());
-                // Default true
-                $addTeacher = isset($params['add_user_as_teacher']) ? $params['add_user_as_teacher'] : true;
-                if ($addTeacher) {
-                    $i_course_sort = CourseManager:: userCourseSort(
-                        $user_id,
-                        $code
-                    );
-                    if (!empty($user_id)) {
-                        $sql = "INSERT INTO $TABLECOURSUSER SET
-                                c_id = $course_id,
-                                user_id         = '".intval($user_id)."',
-                                status          = '1',
-                                is_tutor        = '0',
-                                sort            = '".($i_course_sort)."',
-                                relation_type = 0,
-                                user_course_cat = '0'";
-                        Database::query($sql);
-                    }
-                }
-
-                if (!empty($teachers)) {
-                    if (!is_array($teachers)) {
-                        $teachers = [$teachers];
-                    }
-                    foreach ($teachers as $key) {
-                        // Just in case.
-                        if ($key == $user_id) {
-                            continue;
-                        }
-                        if (empty($key)) {
-                            continue;
-                        }
-                        $sql = "INSERT INTO ".$TABLECOURSUSER." SET
-                            c_id     = '".Database::escape_string($course_id)."',
-                            user_id         = '".Database::escape_string($key)."',
-                            status          = '1',
-                            is_tutor        = '0',
-                            sort            = '".($sort + 1)."',
-                            relation_type = 0,
-                            user_course_cat = '0'";
-                        Database::query($sql);
-                    }
-                }
-
-                // Adding the course to an URL.
-                UrlManager::add_course_to_url($course_id, $accessUrlId);
-
-                // Add event to the system log.
-                $user_id = api_get_user_id();
-                Event::addEvent(
-                    LOG_COURSE_CREATE,
-                    LOG_COURSE_CODE,
-                    $code,
-                    api_get_utc_datetime(),
-                    $user_id,
-                    $course_id
-                );
-
-                $send_mail_to_admin = api_get_setting('send_email_to_admin_when_create_course');
-
-                // @todo Improve code to send to all current portal administrators.
-                if ($send_mail_to_admin === 'true') {
-                    $siteName = api_get_setting('siteName');
-                    $recipient_email = api_get_setting('emailAdministrator');
-                    $recipient_name = api_get_person_name(
-                        api_get_setting('administratorName'),
-                        api_get_setting('administratorSurname')
-                    );
-                    $iname = api_get_setting('Institution');
-                    $subject = get_lang(
-                            'NewCourseCreatedIn'
-                        ).' '.$siteName.' - '.$iname;
-                    $message = get_lang(
-                            'Dear'
-                        ).' '.$recipient_name.",\n\n".get_lang(
-                            'MessageOfNewCourseToAdmin'
-                        ).' '.$siteName.' - '.$iname."\n";
-                    $message .= get_lang('CourseName').' '.$title."\n";
-                    $message .= get_lang(
-                            'Category'
-                        ).' '.$category_code."\n";
-                    $message .= get_lang('Tutor').' '.$tutor_name."\n";
-                    $message .= get_lang('Language').' '.$course_language;
-
-                    $userInfo = api_get_user_info($user_id);
-                    $additionalParameters = [
-                        'smsType' => SmsPlugin::NEW_COURSE_BEEN_CREATED,
-                        'userId' => $user_id,
-                        'courseName' => $title,
-                        'creatorUsername' => $userInfo['username'],
-                    ];
-
-                    api_mail_html(
-                        $recipient_name,
-                        $recipient_email,
-                        $subject,
-                        $message,
-                        $siteName,
-                        $recipient_email,
-                        null,
-                        null,
-                        null,
-                        $additionalParameters
-                    );
-                }
-            }
+            api_mail_html(
+                $recipient_name,
+                $recipient_email,
+                $subject,
+                $message,
+                $siteName,
+                $recipient_email,
+                null,
+                null,
+                null,
+                $additionalParameters
+            );
         }
 
-        return $course_id;
+        return $courseId;
     }
 
     /**
diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php
index f9eef2c0868..5a521671841 100644
--- a/main/inc/lib/api.lib.php
+++ b/main/inc/lib/api.lib.php
@@ -2,9 +2,12 @@
 /* For licensing terms, see /license.txt */
 
 use Chamilo\CoreBundle\Entity\SettingsCurrent;
+use Chamilo\CoreBundle\Entity\UserCourseCategory;
+use Chamilo\CourseBundle\Entity\CGroupInfo;
 use Chamilo\CourseBundle\Entity\CItemProperty;
 use Chamilo\UserBundle\Entity\User;
 use ChamiloSession as Session;
+use Doctrine\Common\Collections\Criteria;
 use PHPMailer\PHPMailer\PHPMailer as PHPMailer;
 use Symfony\Component\Finder\Finder;
 
@@ -4193,289 +4196,66 @@ function api_item_property_update(
     $end_visible = '',
     $session_id = 0
 ) {
-    if (empty($_course)) {
+    if (!array_key_exists('real_id', $_course)) {
         return false;
     }
-
-    $course_id = $_course['real_id'];
-
-    if (empty($course_id)) {
+    $course = \Chamilo\CoreBundle\Entity\Course::getRepository()->find($_course['real_id']);
+    if (is_null($course)) {
         return false;
     }
 
-    $to_group_id = 0;
-    if (!empty($groupInfo) && isset($groupInfo['iid'])) {
-        $to_group_id = (int) $groupInfo['iid'];
-    }
-
-    $em = Database::getManager();
-
-    // Definition of variables.
-    $tool = Database::escape_string($tool);
-    $item_id = (int) $item_id;
-    $lastEditTypeNoFilter = $last_edit_type;
-    $last_edit_type = Database::escape_string($last_edit_type);
-    $user_id = (int) $user_id;
-
-    $startVisible = "NULL";
-    if (!empty($start_visible)) {
-        $start_visible = Database::escape_string($start_visible);
-        $startVisible = "'$start_visible'";
-    }
-
-    $endVisible = "NULL";
-    if (!empty($end_visible)) {
-        $end_visible = Database::escape_string($end_visible);
-        $endVisible = "'$end_visible'";
-    }
-
-    $to_filter = '';
-    $time = api_get_utc_datetime();
-
-    if (!empty($session_id)) {
-        $session_id = (int) $session_id;
-    } else {
-        $session_id = api_get_session_id();
-    }
-
-    // Definition of tables.
-    $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
-
-    if ($to_user_id <= 0) {
-        $to_user_id = null; // No to_user_id set
-    }
-
-    if (!is_null($to_user_id)) {
-        // $to_user_id has more priority than $to_group_id
-        $to_user_id = (int) $to_user_id;
-        $to_field = 'to_user_id';
-        $to_value = $to_user_id;
-    } else {
-        // $to_user_id is not set.
-        $to_field = 'to_group_id';
-        $to_value = $to_group_id;
-    }
-
-    $toValueCondition = empty($to_value) ? 'NULL' : "'$to_value'";
-    // Set filters for $to_user_id and $to_group_id, with priority for $to_user_id
-    $condition_session = " AND session_id = $session_id ";
-    if (empty($session_id)) {
-        $condition_session = ' AND (session_id = 0 OR session_id IS NULL) ';
-    }
-
-    $filter = " c_id = $course_id AND tool = '$tool' AND ref = $item_id $condition_session ";
-
-    // Check whether $to_user_id and $to_group_id are passed in the function call.
-    // If both are not passed (both are null) then it is a message for everybody and $to_group_id should be 0 !
-    if (is_null($to_user_id) && is_null($to_group_id)) {
-        $to_group_id = 0;
-    }
-
-    if (!is_null($to_user_id)) {
-        // Set filter to intended user.
-        $to_filter = " AND to_user_id = $to_user_id $condition_session";
-    } else {
-        // Set filter to intended group.
-        if (($to_group_id != 0) && $to_group_id == strval(intval($to_group_id))) {
-            $to_filter = " AND to_group_id = $to_group_id $condition_session";
-        }
-    }
-
-    // Adding filter if set.
-    $filter .= $to_filter;
-
-    // Update if possible
-    $set_type = '';
-
-    switch ($lastEditTypeNoFilter) {
-        case 'delete':
-            // delete = make item only visible for the platform admin.
-            $visibility = '2';
-            if (!empty($session_id)) {
-                // Check whether session id already exist into item_properties for updating visibility or add it.
-                $sql = "SELECT session_id FROM $tableItemProperty
-                        WHERE
-                            c_id = $course_id AND
-                            tool = '$tool' AND
-                            ref = $item_id AND
-                            session_id = $session_id";
-                $rs = Database::query($sql);
-                if (Database::num_rows($rs) > 0) {
-                    $sql = "UPDATE $tableItemProperty
-                            SET lastedit_type       = '".str_replace('_', '', ucwords($tool))."Deleted',
-                                lastedit_date       = '$time',
-                                lastedit_user_id    = $user_id,
-                                visibility          = $visibility,
-                                session_id          = $session_id $set_type
-                            WHERE $filter";
-                    $result = Database::query($sql);
-                } else {
-                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id, $to_field, visibility, start_visible, end_visible, session_id)
-                            VALUES ($course_id, '$tool',$item_id, '$time', $user_id, '$time', '$last_edit_type',$user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
-                    $result = Database::query($sql);
-                    $id = Database::insert_id();
-                    if ($id) {
-                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
-                        Database::query($sql);
-                    }
-                }
-            } else {
-                $sql = "UPDATE $tableItemProperty
-                        SET
-                            lastedit_type='".str_replace('_', '', ucwords($tool))."Deleted',
-                            lastedit_date='$time',
-                            lastedit_user_id = $user_id,
-                            visibility = $visibility $set_type
-                        WHERE $filter";
-                $result = Database::query($sql);
-            }
-            break;
-        case 'visible': // Change item to visible.
-            $visibility = '1';
-            if (!empty($session_id)) {
-                // Check whether session id already exist into item_properties for updating visibility or add it.
-                $sql = "SELECT session_id FROM $tableItemProperty
-                        WHERE
-                            c_id = $course_id AND
-                            tool = '$tool' AND
-                            ref = $item_id AND
-                            session_id = $session_id";
-                $rs = Database::query($sql);
-                if (Database::num_rows($rs) > 0) {
-                    $sql = "UPDATE $tableItemProperty
-                            SET
-                                lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
-                                lastedit_date='$time',
-                                lastedit_user_id = $user_id,
-                                visibility = $visibility,
-                                session_id = $session_id $set_type
-                            WHERE $filter";
-                    $result = Database::query($sql);
-                } else {
-                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id, $to_field, visibility, start_visible, end_visible, session_id)
-                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
-                    $result = Database::query($sql);
-                    $id = Database::insert_id();
-                    if ($id) {
-                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
-                        Database::query($sql);
-                    }
-                }
-            } else {
-                $sql = "UPDATE $tableItemProperty
-                        SET
-                            lastedit_type='".str_replace('_', '', ucwords($tool))."Visible',
-                            lastedit_date='$time',
-                            lastedit_user_id = $user_id,
-                            visibility = $visibility $set_type
-                        WHERE $filter";
-                $result = Database::query($sql);
-            }
-            break;
-        case 'invisible': // Change item to invisible.
-            $visibility = '0';
-            if (!empty($session_id)) {
-                // Check whether session id already exist into item_properties for updating visibility or add it
-                $sql = "SELECT session_id FROM $tableItemProperty
-                        WHERE
-                            c_id = $course_id AND
-                            tool = '$tool' AND
-                            ref = $item_id AND
-                            session_id = $session_id";
-                $rs = Database::query($sql);
-                if (Database::num_rows($rs) > 0) {
-                    $sql = "UPDATE $tableItemProperty
-                            SET
-                                lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
-                                lastedit_date = '$time',
-                                lastedit_user_id = $user_id,
-                                visibility = $visibility,
-                                session_id = $session_id $set_type
-                            WHERE $filter";
-                    $result = Database::query($sql);
-                } else {
-                    $sql = "INSERT INTO $tableItemProperty (c_id, tool, ref, insert_date, insert_user_id, lastedit_date, lastedit_type, lastedit_user_id,$to_field, visibility, start_visible, end_visible, session_id)
-                            VALUES ($course_id, '$tool', $item_id, '$time', $user_id, '$time', '$last_edit_type', $user_id, $toValueCondition, $visibility, $startVisible, $endVisible, $session_id)";
-                    $result = Database::query($sql);
-                    $id = Database::insert_id();
-                    if ($id) {
-                        $sql = "UPDATE $tableItemProperty SET id = iid WHERE iid = $id";
-                        Database::query($sql);
-                    }
-                }
-            } else {
-                $sql = "UPDATE $tableItemProperty
-                        SET
-                            lastedit_type = '".str_replace('_', '', ucwords($tool))."Invisible',
-                            lastedit_date = '$time',
-                            lastedit_user_id = $user_id,
-                            visibility = $visibility $set_type
-                        WHERE $filter";
-                $result = Database::query($sql);
-            }
-            break;
-        default: // The item will be added or updated.
-            $set_type = ", lastedit_type = '$last_edit_type' ";
-            $visibility = '1';
-            //$filter .= $to_filter; already added
-            $sql = "UPDATE $tableItemProperty
-                    SET
-                      lastedit_date = '$time',
-                      lastedit_user_id = $user_id $set_type
-                    WHERE $filter";
-            $result = Database::query($sql);
-    }
-
-    // Insert if no entries are found (can only happen in case of $last_edit_type switch is 'default').
-    if ($result == false || Database::affected_rows($result) == 0) {
-        $objCourse = $em->find('ChamiloCoreBundle:Course', intval($course_id));
-        $objTime = new DateTime('now', new DateTimeZone('UTC'));
-        $objUser = api_get_user_entity($user_id);
-        if (empty($objUser)) {
-            // Use anonymous
-            $user_id = api_get_anonymous_id();
-            $objUser = api_get_user_entity($user_id);
-        }
-
-        $objGroup = null;
-        if (!empty($to_group_id)) {
-            $objGroup = $em->find('ChamiloCourseBundle:CGroupInfo', $to_group_id);
-        }
-
-        $objToUser = api_get_user_entity($to_user_id);
-        $objSession = $em->find('ChamiloCoreBundle:Session', intval($session_id));
-
-        $startVisibleDate = !empty($start_visible) ? new DateTime($start_visible, new DateTimeZone('UTC')) : null;
-        $endVisibleDate = !empty($endVisibleDate) ? new DateTime($endVisibleDate, new DateTimeZone('UTC')) : null;
-
-        $cItemProperty = new CItemProperty($objCourse);
-        $cItemProperty
-            ->setTool($tool)
-            ->setRef($item_id)
-            ->setInsertDate($objTime)
-            ->setInsertUser($objUser)
-            ->setLasteditDate($objTime)
-            ->setLasteditType($last_edit_type)
-            ->setGroup($objGroup)
-            ->setToUser($objToUser)
-            ->setVisibility($visibility)
-            ->setStartVisible($startVisibleDate)
-            ->setEndVisible($endVisibleDate)
-            ->setSession($objSession);
-
-        $em->persist($cItemProperty);
-        $em->flush();
-
-        $id = $cItemProperty->getIid();
+    $toUser = api_get_user_entity($to_user_id);
+    $toGroup = array_key_exists('iid', $groupInfo) ? CGroupInfo::getRepository()->find($groupInfo['iid']) : null;
+    $user = api_get_user_entity($user_id) ?: api_get_user_entity(api_get_anonymous_id());
+    $session = \Chamilo\CoreBundle\Entity\Session::getRepository()->find($session_id ?: api_get_session_id());
 
-        if ($id) {
-            $cItemProperty->setId($id);
-            $em->merge($cItemProperty);
-            $em->flush();
+    $startVisibleDate = empty($start_visible)
+        ? null
+        : new DateTime($start_visible, new DateTimeZone('UTC'));
+    $endVisibleDate = empty($end_visible)
+        ? null
+        : new DateTime($end_visible, new DateTimeZone('UTC'));
 
-            return false;
-        }
-    }
+    $visibilityReference = [
+        'delete' => 2, // only site admin
+        'visible' => 1,
+        'invisible' => 0,
+    ];
+    $visibility = array_key_exists($last_edit_type, $visibilityReference)
+        ? $visibilityReference[$last_edit_type]
+        : 1;
+    $lastEditTypeFinal = array_key_exists($last_edit_type, $visibilityReference)
+        ? sprintf('%s%s', str_replace('_', '', ucwords($tool)), ucfirst($last_edit_type))
+        : $last_edit_type;
+
+    /** @var CItemProperty|null $itemProperty */
+    $itemProperty = (
+        $course->getItemProperties()->matching(
+            Criteria::create()
+                ->where(Criteria::expr()->eq('tool', $tool))
+                ->andWhere(Criteria::expr()->eq('ref', $item_id))
+                ->andWhere(Criteria::expr()->eq('session', $session))
+                ->andWhere(
+                    is_null($toUser)
+                        ? Criteria::expr()->eq('group', $toGroup)
+                        : Criteria::expr()->eq('toUser', $toUser)
+                )
+        )->first() ?: (
+            (new CItemProperty($course))
+                ->setTool($tool)
+                ->setRef($item_id)
+                ->setToUser($toUser)
+                ->setGroup($toGroup)
+                ->setSession($session)
+        )
+    )
+        ->setLasteditType($lastEditTypeFinal)
+        ->setLasteditUserId($user->getId())
+        ->setVisibility($visibility)
+        ->setStartVisible($startVisibleDate)
+        ->setEndVisible($endVisibleDate);
+    Database::getManager()->persist($itemProperty);
+    Database::getManager()->flush($itemProperty);
 
     return true;
 }
@@ -5260,20 +5040,10 @@ function api_get_themes($getOnlyThemeFromVirtualInstance = false)
  */
 function api_max_sort_value($user_course_category, $user_id)
 {
-    $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
-    $sql = "SELECT max(sort) as max_sort FROM $tbl_course_user
-            WHERE
-                user_id='".intval($user_id)."' AND
-                relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
-                user_course_cat='".intval($user_course_category)."'";
-    $result_max = Database::query($sql);
-    if (Database::num_rows($result_max) == 1) {
-        $row_max = Database::fetch_array($result_max);
-
-        return $row_max['max_sort'];
-    }
-
-    return 0;
+    /** @var User $user */
+    $user = User::getRepository()->find($user_id);
+    $userCourseCategory = UserCourseCategory::getRepository()->find($user_course_category);
+    return is_null($user) ? 0 : $user->getMaxSortValue($userCourseCategory);
 }
 
 /**
diff --git a/main/inc/lib/course.lib.php b/main/inc/lib/course.lib.php
index a89b6eff893..7775c34ee42 100755
--- a/main/inc/lib/course.lib.php
+++ b/main/inc/lib/course.lib.php
@@ -48,11 +48,6 @@ public static function create_course($params, $authorId = 0, $accessUrlId = 0)
 
         $hook = HookCreateCourse::create();
 
-        // Check portal limits
-        $accessUrlId = empty($accessUrlId)
-            ? (api_get_multiple_access_url() ? api_get_current_access_url_id() : 1)
-            : $accessUrlId;
-
         $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
 
         if (isset($_configuration[$accessUrlId]) && is_array($_configuration[$accessUrlId])) {
@@ -76,45 +71,18 @@ public static function create_course($params, $authorId = 0, $accessUrlId = 0)
             }
         }
 
-        if (empty($params['title'])) {
-            return false;
-        }
-
-        if (empty($params['wanted_code'])) {
-            $params['wanted_code'] = $params['title'];
-            // Check whether the requested course code has already been occupied.
-            $substring = api_substr($params['title'], 0, self::MAX_COURSE_LENGTH_CODE);
-            if ($substring === false || empty($substring)) {
-                return false;
-            } else {
-                $params['wanted_code'] = self::generate_course_code($substring);
-            }
-        }
-
-        // Create the course keys
-        $keys = AddCourse::define_course_keys($params['wanted_code']);
         $params['exemplary_content'] = isset($params['exemplary_content']) ? $params['exemplary_content'] : false;
 
-        if (count($keys)) {
-            $params['code'] = $keys['currentCourseCode'];
-            $params['visual_code'] = $keys['currentCourseId'];
-            $params['directory'] = $keys['currentCourseRepository'];
-            $course_info = api_get_course_info($params['code']);
-            if (empty($course_info)) {
-                $course_id = AddCourse::register_course($params, $accessUrlId);
-                $course_info = api_get_course_info_by_id($course_id);
-
-                if ($hook) {
-                    $hook->setEventData(['course_info' => $course_info]);
-                    $hook->notifyCreateCourse(HOOK_EVENT_TYPE_POST);
-                }
-
-                if (!empty($course_info)) {
-                    self::fillCourse($course_info, $params, $authorId);
-
-                    return $course_info;
-                }
+        $courseId = AddCourse::register_course($params, $accessUrlId);
+        if ($courseId) {
+            $courseInfo = api_get_course_info_by_id($courseId);
+            if ($hook) {
+                $hook->setEventData(['course_info' => $courseInfo]);
+                $hook->notifyCreateCourse(HOOK_EVENT_TYPE_POST);
             }
+            self::fillCourse($courseInfo, $params, $authorId);
+
+            return $courseInfo;
         }
 
         return false;
@@ -6990,7 +6958,6 @@ private static function fillCourse($courseInfo, $params, $authorId = 0)
     {
         $authorId = empty($authorId) ? api_get_user_id() : (int) $authorId;
 
-        AddCourse::prepare_course_repository($courseInfo['directory']);
         AddCourse::fill_db_course(
             $courseInfo['real_id'],
             $courseInfo['directory'],
diff --git a/main/lp/learnpath.class.php b/main/lp/learnpath.class.php
index 32dd56b07e0..c67e304f1f0 100755
--- a/main/lp/learnpath.class.php
+++ b/main/lp/learnpath.class.php
@@ -601,13 +601,13 @@ public function add_item(
      * @param string $courseCode
      * @param string $name
      * @param string $description
-     * @param string $learnpath
-     * @param string $origin
-     * @param string $zipname       Zip file containing the learnpath or directory containing the learnpath
+     * @param string $learnpath     (unused)
+     * @param string $origin        (if 'zip', this function does nothing)
+     * @param string $zipname       (unused)
      * @param string $publicated_on
      * @param string $expired_on
      * @param int    $categoryId
-     * @param int    $userId
+     * @param int    $userId        (unused)
      *
      * @return int The new learnpath ID on success, 0 on failure
      */
@@ -625,56 +625,27 @@ public static function add_lp(
     ) {
         global $charset;
 
-        $courseInfo = api_get_course_info($courseCode);
-        $course_id = $courseInfo['real_id'];
-
-        $session_id = api_get_session_id();
-        $userId = empty($userId) ? api_get_user_id() : $userId;
-
-        $publicated_on = api_get_utc_datetime($publicated_on, true, true);
-        $expired_on = api_get_utc_datetime($expired_on, true, true);
-
-        $description = api_htmlentities($description, ENT_QUOTES, $charset);
-
-        switch ($origin) {
-            case 'zip':
-                // Check zip name string. If empty, we are currently creating a new Chamilo learnpath.
-                break;
-            case 'manual':
-            default:
+        if ('zip' !== $origin) {
+            $course_id = api_get_course_int_id($courseCode);
+            if ($course_id) {
                 $course = \Chamilo\CoreBundle\Entity\Course::getRepository()->find($course_id);
-                $learningPath = (new CLp())
-                    ->setCourse($course)
-                    ->setName($name)
-                    ->setDescription($description)
-                    ->setPublicatedOn($publicated_on)
-                    ->setExpiredOn($expired_on)
-                    ->setCategoryId($categoryId)
-                ;
-                Database::getManager()->persist($learningPath);
-                Database::getManager()->flush();
-
-                $id = $learningPath->getId();
-
-                // Insert into item_property.
-                api_item_property_update(
-                    $courseInfo,
-                    TOOL_LEARNPATH,
-                    $id,
-                    'LearnpathAdded',
-                    $userId
-                );
-                api_set_default_visibility(
-                    $id,
-                    TOOL_LEARNPATH,
-                    0,
-                    $courseInfo,
-                    $session_id,
-                    $userId
-                );
+                if (!is_null($course)) {
+                    $learningPath = (new CLp())
+                        ->setCourse($course)
+                        ->setName($name)
+                        ->setDescription(api_htmlentities($description, ENT_QUOTES, $charset))
+                        ->setPublicatedOn(api_get_utc_datetime($publicated_on, true, true))
+                        ->setExpiredOn(api_get_utc_datetime($expired_on, true, true))
+                        ->setCategoryId($categoryId);
+                    Database::getManager()->persist($learningPath);
+                    Database::getManager()->flush();
 
-                return $id;
+                    return $learningPath->getId();
+                }
+            }
         }
+
+        return 0;
     }
 
     /**
diff --git a/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php b/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
index c21371dacd2..e055259af73 100644
--- a/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
+++ b/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
@@ -83,11 +83,16 @@ public function getUrl()
     }
 
     /**
-     * @param $course
+     * @param Course $course
+     *
+     * @return AccessUrlRelCourse
      */
     public function setCourse($course)
     {
         $this->course = $course;
+        $this->course->getUrls()->add($this);
+
+        return $this;
     }
 
     /**
diff --git a/src/Chamilo/CoreBundle/Entity/Course.php b/src/Chamilo/CoreBundle/Entity/Course.php
index 81614eacb73..ab09ecb3084 100644
--- a/src/Chamilo/CoreBundle/Entity/Course.php
+++ b/src/Chamilo/CoreBundle/Entity/Course.php
@@ -3,9 +3,10 @@
 
 namespace Chamilo\CoreBundle\Entity;
 
-use AddCourse;
+use Chamilo\CourseBundle\Entity\CCourseSetting;
 use Chamilo\CourseBundle\Entity\CDocument;
 use Chamilo\CourseBundle\Entity\CForumForum;
+use Chamilo\CourseBundle\Entity\CItemProperty;
 use Chamilo\CourseBundle\Entity\CLink;
 use Chamilo\CourseBundle\Entity\CLp;
 use Chamilo\CourseBundle\Entity\CLpCategory;
@@ -17,6 +18,7 @@
 use Database;
 use DateInterval;
 use DateTime;
+use DateTimeZone;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Criteria;
 use Doctrine\ORM\EntityRepository;
@@ -60,16 +62,30 @@ class Course
     protected $id;
 
     /**
+     * @var CourseRelUser|ArrayCollection
+     *
      * "orphanRemoval" is needed to delete the CourseRelUser relation
      * in the CourseAdmin class. The setUsers, getUsers, removeUsers and
      * addUsers methods need to be added.
      *
-     * @ORM\OneToMany(targetEntity="CourseRelUser", mappedBy="course", cascade={"persist"}, orphanRemoval=true)
+     * @ORM\OneToMany(
+     *     targetEntity="CourseRelUser",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"},
+     *     orphanRemoval=true
+     * )
      */
     protected $users;
 
     /**
-     * @ORM\OneToMany(targetEntity="AccessUrlRelCourse", mappedBy="course", cascade={"persist"}, orphanRemoval=true)
+     * @var AccessUrlRelCourse[]|ArrayCollection
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="AccessUrlRelCourse",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"},
+     *     orphanRemoval=true
+     * )
      */
     protected $urls;
 
@@ -84,9 +100,15 @@ class Course
     protected $sessionUserSubscriptions;
 
     /**
-     * @ORM\OneToMany(targetEntity="Chamilo\CourseBundle\Entity\CItemProperty", mappedBy="course")
+     * @var ArrayCollection|CItemProperty[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CItemProperty",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     * )
      */
-    //protected $items;
+    protected $itemProperties;
 
     /**
      * @ORM\OneToMany(targetEntity="Chamilo\CourseBundle\Entity\CTool", mappedBy="course", cascade={"persist"})
@@ -304,6 +326,17 @@ class Course
      */
     protected $accessUrls;
 
+    /**
+     * @var ArrayCollection|CCourseSetting[]
+     *
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CCourseSetting",
+     *     mappedBy="course",
+     *     cascade={"persist", "remove"}
+     * )
+     */
+    protected $settings;
+
     /**
      * @var ArrayCollection|CLpCategory[]
      *
@@ -383,19 +416,30 @@ class Course
 
     /**
      * Constructor.
+     *
+     * @throws Exception
      */
     public function __construct()
     {
-        $this->creationDate = new DateTime();
-        $this->users = new ArrayCollection();
+        $this->activateLegal = false;
+        $this->addTeachersToSessionsCourses = false;
+        $this->creationDate = new DateTime('now', new DateTimeZone('utc'));
+        $this->lastVisit = null;
+        $this->showScore = 1;
+        $this->unsubscribe = false;
         $this->accessUrls = new ArrayCollection();
         $this->documents = new ArrayCollection();
         $this->forums = new ArrayCollection();
-        $this->links = new ArrayCollection();
-        $this->quizzes = new ArrayCollection();
+        $this->itemProperties = new ArrayCollection();
         $this->learningPathCategories = new ArrayCollection();
-        $this->learningPaths = new ArrayCollection();
         $this->learningPathItems = new ArrayCollection();
+        $this->learningPaths = new ArrayCollection();
+        $this->links = new ArrayCollection();
+        $this->quizzes = new ArrayCollection();
+        $this->settings = new ArrayCollection();
+        $this->tools = new ArrayCollection();
+        $this->urls = new ArrayCollection();
+        $this->users = new ArrayCollection();
     }
 
     /**
@@ -403,7 +447,7 @@ public function __construct()
      */
     public function __toString()
     {
-        return (string) $this->getTitle();
+        return sprintf('course %s ("%s")', $this->id, $this->getName());
     }
 
     /**
@@ -442,17 +486,27 @@ public function prePersist()
                 CourseManager::MAX_COURSE_LENGTH_CODE
             );
         }
-        if (empty($this->directory) || empty($this->visualCode)) {
-            $keys = AddCourse::define_course_keys($this->code, '');
-            if (!count($keys)) {
-                throw new Exception('Could not define course keys');
-            }
-            if (empty($this->directory)) {
-                $this->directory = $keys['currentCourseRepository'];
-            }
-            if (empty($this->visualCode)) {
-                $this->visualCode = $keys['currentCourseCode'];
-            }
+        $originalCode = $this->code;
+        $counter = 1;
+        while (self::getRepository()->matching(
+            Criteria::create()->where(
+                Criteria::expr()->eq('code', $this->code)
+            )
+        )->exists(function ($other) {
+            return $other !== $this;
+        })) {
+            $this->code = sprintf('%s_%d', $originalCode, $counter++);
+        }
+        if (empty($this->visualCode)) {
+            $this->visualCode = $this->code;
+        }
+        if (empty($this->directory)) {
+            $this->directory = $this->code;
+        }
+        $originalDirectory = $this->directory;
+        $counter = 1;
+        while (file_exists($this->getAbsolutePath())) {
+            $this->directory = sprintf('%s_%d', $originalDirectory, $counter++);
         }
         if (is_null($this->courseLanguage)) {
             $this->courseLanguage = api_get_setting('platformLanguage');
@@ -467,23 +521,30 @@ public function prePersist()
             $this->visibility = api_get_setting(
                 'courses_default_creation_visibility'
             ) ?: COURSE_VISIBILITY_OPEN_PLATFORM;
+        } elseif ($this->visibility < 0 || $this->visibility > 4) {
+            throw new Exception('This course visibility in invalid:'.$this->visibility);
         }
-        if (is_null($this->showScore)) {
-            $this->showScore = 1;
+        if (is_null($this->subscribe)) {
+            $this->subscribe = (COURSE_VISIBILITY_OPEN_PLATFORM == $this->visibility);
         }
         if (is_null($this->diskQuota)) {
             $this->diskQuota = api_get_setting('default_document_quotum');
         }
-        $this->lastEdit = new DateTime();
+        $this->lastEdit = new DateTime('now', new DateTimeZone('utc'));
         if (is_null($this->expirationDate)) {
-            $this->expirationDate = new DateTime();
-            $this->expirationDate->add(new DateInterval('P1Y'));
+            global $firstExpirationDelay;
+            $this->expirationDate = new DateTime('now', new DateTimeZone('utc'));
+            $this->expirationDate->add(new DateInterval(sprintf('PT%dS', $firstExpirationDelay)));
         }
-        $absolutePath = $this->getAbsolutePath();
-        if (!file_exists($absolutePath)) {
-            AddCourse::prepare_course_repository($this->directory);
+        if (!empty($this->departmentUrl) && !preg_match("@^https?://@", $this->departmentUrl)) {
+            $this->departmentUrl = 'https://'.$this->departmentUrl;
+        }
+        if ($this->accessUrls->isEmpty()) {
+            $this->accessUrls->add(AccessUrl::getRepository()->find(api_get_current_access_url_id()));
         }
-        $this->accessUrls->add(AccessUrl::getRepository()->find(api_get_current_access_url_id()));
+        $this->prepareRepository();
+        $this->createTools();
+        $this->createSettings();
     }
 
     /**
@@ -568,7 +629,7 @@ public function addTools(CTool $tool)
     }
 
     /**
-     * @return ArrayCollection
+     * @return AccessUrlRelCourse[]|ArrayCollection
      */
     public function getUrls()
     {
@@ -580,8 +641,6 @@ public function getUrls()
      */
     public function setUrls($urls)
     {
-        $this->urls = new ArrayCollection();
-
         foreach ($urls as $url) {
             $this->addUrls($url);
         }
@@ -594,7 +653,7 @@ public function addUrls(AccessUrlRelCourse $url)
     }
 
     /**
-     * @return ArrayCollection
+     * @return CourseRelUser|ArrayCollection
      */
     public function getUsers()
     {
@@ -1479,6 +1538,14 @@ public function getLearningPathItems()
         return $this->learningPathItems;
     }
 
+    /**
+     * @return CItemProperty[]|ArrayCollection
+     */
+    public function getItemProperties()
+    {
+        return $this->itemProperties;
+    }
+
     /**
      * @param CourseRelUser $subscription
      *
@@ -1519,4 +1586,232 @@ protected function addUser($user, $relationType, $role, $status)
         $courseRelUser->setStatus($status);
         $this->addUsers($courseRelUser);
     }
+
+    /**
+     * @return CCourseSetting[]|ArrayCollection
+     */
+    public function getSettings()
+    {
+        return $this->settings;
+    }
+
+    /**
+     * Initializes the course's file repository.
+     * Replaces \AddCourse::prepare_course_repository
+     *
+     * @throws Exception
+     */
+    private function prepareRepository()
+    {
+        $dirPermissions = api_get_permissions_for_new_directories();
+        $filePermissions = api_get_permissions_for_new_files();
+        $indexHtmlContents = '<!DOCTYPE html>
+<html lang="en"><head><meta charset="utf-8"><title>Not authorized</title></head><body></body></html>';
+        $repositoryPath = $this->getAbsolutePath();
+        if (!file_exists($repositoryPath)) {
+            if (!mkdir($repositoryPath, $dirPermissions)) {
+                throw new Exception(sprintf('Could not create course repository "%s"', $repositoryPath));
+            }
+        }
+        foreach ([
+                     'document',
+                     'dropbox',
+                     'exercises',
+                     'group',
+                     'page',
+                     'scorm',
+                     'upload',
+                     'upload/announcements',
+                     'upload/announcements/images',
+                     'upload/blog',
+                     'upload/calendar',
+                     'upload/calendar/images',
+                     'upload/forum',
+                     'upload/forum/images',
+                     'upload/learning_path',
+                     'upload/learning_path/images',
+                     'upload/test',
+                     'work',
+                 ] as $relativePath) {
+            $subfolderPath = $repositoryPath.'/'.$relativePath;
+            if (!file_exists($subfolderPath)) {
+                if (!mkdir($subfolderPath, $dirPermissions)) {
+                    throw new Exception(sprintf('Could not create course repository subfolder "%s"', $subfolderPath));
+                }
+            }
+            $indexHtmlFilePath = $subfolderPath.'/index.html';
+            if (!file_exists($indexHtmlFilePath)) {
+                $indexHtmlFile = fopen($indexHtmlFilePath, 'w');
+                if (false === $indexHtmlFile) {
+                    throw new Exception(sprintf(
+                        'Could not create course repository subfolder index file "%s"',
+                        $indexHtmlFilePath
+                    ));
+                }
+                if (false === fwrite($indexHtmlFile, $indexHtmlContents)) {
+                    throw new Exception(sprintf(
+                        'Could not write to course repository subfolder index file "%s"',
+                        $indexHtmlFilePath
+                    ));
+                }
+                if (!fclose($indexHtmlFile)) {
+                    throw new Exception(sprintf(
+                        'Could not close course repository subfolder index file "%s"',
+                        $indexHtmlFilePath
+                    ));
+                }
+                @chmod($indexHtmlFile, $filePermissions);
+            }
+        }
+
+        // Create .htaccess in the dropbox directory.
+        $dropboxHtAccessFilePath = $repositoryPath.'/dropbox/.htaccess';
+        $dropboxHtAccessFile = fopen($dropboxHtAccessFilePath, 'w');
+        if (false === $dropboxHtAccessFile) {
+            throw new Exception(sprintf(
+                'Could not create course repository dropbox subfolder access control file "%s"',
+                $dropboxHtAccessFilePath
+            ));
+        }
+        if (!fwrite(
+            $dropboxHtAccessFile,
+            "AuthName AllowLocalAccess
+AuthType Basic
+
+order deny,allow
+deny from all
+
+php_flag zlib.output_compression off"
+        )) {
+            throw new Exception(sprintf(
+                'Could not write to course repository dropbox subfolder access control file "%s"',
+                $dropboxHtAccessFilePath
+            ));
+        }
+        if (!fclose($dropboxHtAccessFile)) {
+            throw new Exception(sprintf(
+                'Could not close course repository dropbox subfolder access control file "%s"',
+                $dropboxHtAccessFilePath
+            ));
+        }
+    }
+
+    private function createTools()
+    {
+        $toolReference = [
+            [TOOL_COURSE_DESCRIPTION, 'course_description/index.php', 'info.gif', 'course_description', 'authoring'],
+            [TOOL_CALENDAR_EVENT, 'calendar/agenda.php', 'agenda.gif', 'agenda', 'interaction'],
+            [TOOL_DOCUMENT, 'document/document.php', 'folder_document.gif', 'documents', 'authoring'],
+            [TOOL_LEARNPATH, 'lp/lp_controller.php', 'scorms.gif', 'learning_path', 'authoring'],
+            [TOOL_LINK, 'link/link.php', 'links.gif', 'links', 'authoring'],
+            [TOOL_QUIZ, 'exercise/exercise.php', 'quiz.gif', 'quiz', 'authoring'],
+            [TOOL_ANNOUNCEMENT, 'announcements/announcements.php', 'valves.gif', 'announcements', 'authoring'],
+            [TOOL_FORUM, 'forum/index.php', 'forum.gif', 'forums', 'interaction'],
+            [TOOL_DROPBOX, 'dropbox/index.php', 'dropbox.gif', 'dropbox', 'interaction'],
+            [TOOL_USER, 'user/user.php', 'members.gif', 'users', 'interaction'],
+            [TOOL_GROUP, 'group/group.php', 'group.gif', 'groups', 'interaction'],
+            [TOOL_CHAT, 'chat/chat.php', 'chat.gif', 'chat', 'interaction'],
+            [TOOL_STUDENTPUBLICATION, 'work/work.php', 'works.gif', 'student_publications', 'interaction'],
+            [TOOL_SURVEY, 'survey/survey_list.php', 'survey.gif', 'survey', 'interaction'],
+            [TOOL_WIKI, 'wiki/index.php', 'wiki.gif', 'wiki', 'interaction'],
+            [TOOL_GRADEBOOK, 'gradebook/index.php', 'gradebook.gif' , 'gradebook', 'authoring'],
+            [TOOL_GLOSSARY, 'glossary/index.php', 'glossary.gif', 'glossary', 'authoring'],
+            [TOOL_NOTEBOOK, 'notebook/index.php', 'notebook.gif', 'notebook', 'interaction'],
+        ];
+        if (api_get_configuration_value('allow_portfolio_tool')) {
+            $toolReference[] = [TOOL_PORTFOLIO, 'portfolio/index.php', 'wiki_task.png', 'portfolio', 'interaction'];
+        }
+        $toolReference[] = [TOOL_ATTENDANCE, 'attendance/index.php', 'attendance.gif', 'attendances', 'authoring'];
+        $toolReference[] =
+            [TOOL_COURSE_PROGRESS, 'course_progress/index.php', 'course_progress.gif', 'course_progress', 'authoring'];
+        $counter = 1;
+        foreach ($toolReference as list($name, $link, $image, $key, $category)) {
+            (new CTool())
+                ->setCourse($this)
+                ->setId($counter++)
+                ->setName($name)
+                ->setLink($link)
+                ->setImage($image)
+                ->setVisibility('true' === api_get_setting('course_create_active_tools', $key))
+                ->setCategory($category);
+        }
+        if (api_get_setting('search_enabled') === 'true') {
+            (new CTool())
+                ->setCourse($this)
+                ->setId($counter++)
+                ->setName(TOOL_SEARCH)
+                ->setLink('search/')
+                ->setImage('info.gif')
+                ->setVisibility('true' === api_get_setting('course_create_active_tools', 'enable_search'))
+                ->setCategory('authoring')
+                ->setAddress('search.gif');
+        }
+        (new CTool())
+            ->setCourse($this)
+            ->setId($counter++)
+            ->setName(TOOL_BLOGS)
+            ->setLink('blog/blog_admin.php')
+            ->setImage('blog_admin.gif')
+            ->setVisibility('true' === api_get_setting('course_create_active_tools', 'blogs'))
+            ->setCategory('admin')
+            ->setAdmin('1');
+        foreach ([
+                     [TOOL_TRACKING, 'tracking/courseLog.php', 'statistics.gif'],
+                     [TOOL_COURSE_SETTING, 'course_info/infocours.php', 'reference.gif'],
+                     [TOOL_COURSE_MAINTENANCE, 'course_info/maintenance.php', 'backup.gif'],
+                 ] as list($name, $link, $image)) {
+            (new CTool())
+                ->setCourse($this)
+                ->setId($counter++)
+                ->setName($name)
+                ->setLink($link)
+                ->setImage($image)
+                ->setVisibility(false)
+                ->setCategory('admin')
+                ->setAdmin('1');
+        }
+    }
+
+    private function createSettings()
+    {
+        $settings = [
+            'email_alert_manager_on_new_doc' => ['default' => 0, 'category' => 'work'],
+            'email_alert_on_new_doc_dropbox' => ['default' => 0, 'category' => 'dropbox'],
+            'allow_user_edit_agenda' => ['default' => 0, 'category' => 'agenda'],
+            'allow_user_edit_announcement' => ['default' => 0, 'category' => 'announcement'],
+            'email_alert_manager_on_new_quiz' => [
+                'default' => (api_get_setting('email_alert_manager_on_new_quiz') === 'true') ? 1 : 0,
+                'category' => 'quiz'
+            ],
+            'allow_user_image_forum' => ['default' => 1, 'category' => 'forum'],
+            'course_theme' => ['default' => '', 'category' => 'theme'],
+            'allow_learning_path_theme' => ['default' => 1, 'category' => 'theme'],
+            'allow_open_chat_window' => ['default' => 1, 'category' => 'chat'],
+            'email_alert_to_teacher_on_new_user_in_course' => ['default' => 0, 'category' => 'registration'],
+            'allow_user_view_user_list' => ['default' => 1, 'category' => 'user'],
+            'display_info_advance_inside_homecourse' => ['default' => 1, 'category' => 'thematic_advance'],
+            'email_alert_students_on_new_homework' => ['default' => 0, 'category' => 'work'],
+            'enable_lp_auto_launch' => ['default' => 0, 'category' => 'learning_path'],
+            'enable_exercise_auto_launch' => ['default' => 0, 'category' => 'exercise'],
+            'enable_document_auto_launch' => ['default' => 0, 'category' => 'document'],
+            'pdf_export_watermark_text' => ['default' => '', 'category' => 'learning_path'],
+            'allow_public_certificates' => [
+                'default' => api_get_setting('allow_public_certificates') === 'true' ? 1 : '',
+                'category' => 'certificates',
+            ],
+            'documents_default_visibility' => ['default' => 'visible', 'category' => 'document'],
+            'show_course_in_user_language' => ['default' => 2, 'category' => null],
+            'email_to_teachers_on_new_work_feedback' => ['default' => 1, 'category' => null],
+        ];
+
+        $counter = 1;
+        foreach ($settings as $variable => $setting) {
+            (new CCourseSetting())
+                ->setId($counter++)
+                ->setCourse($this)
+                ->setVariable($variable)
+                ->setValue($setting['default'])
+                ->setCategory($setting['category']);
+        }
+    }
 }
diff --git a/src/Chamilo/CoreBundle/Entity/CourseRelUser.php b/src/Chamilo/CoreBundle/Entity/CourseRelUser.php
index d9ad822f11e..f321d4afff3 100644
--- a/src/Chamilo/CoreBundle/Entity/CourseRelUser.php
+++ b/src/Chamilo/CoreBundle/Entity/CourseRelUser.php
@@ -4,6 +4,8 @@
 namespace Chamilo\CoreBundle\Entity;
 
 use Chamilo\UserBundle\Entity\User;
+use Database;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
@@ -95,6 +97,14 @@ public function __construct()
         $this->userCourseCat = 0;
     }
 
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCoreBundle:CourseRelUser');
+    }
+
     /**
      * @return string
      */
@@ -120,11 +130,14 @@ public function setId($id)
     }
 
     /**
+     * @param Course $course
+     *
      * @return $this
      */
-    public function setCourse(Course $course)
+    public function setCourse($course)
     {
         $this->course = $course;
+        $this->course->getUsers()->add($this);
 
         return $this;
     }
@@ -147,6 +160,7 @@ public function getCourse()
     public function setUser($user)
     {
         $this->user = $user;
+        $this->user->getCourses()->add($this);
 
         return $this;
     }
@@ -243,10 +257,14 @@ public function isTutor()
 
     /**
      * @param bool $tutor
+     *
+     * @return CourseRelUser
      */
     public function setTutor($tutor)
     {
         $this->tutor = $tutor;
+
+        return $this;
     }
 
     /**
diff --git a/src/Chamilo/CoreBundle/Entity/Session.php b/src/Chamilo/CoreBundle/Entity/Session.php
index 5adedfd1d29..e43e14b18c9 100644
--- a/src/Chamilo/CoreBundle/Entity/Session.php
+++ b/src/Chamilo/CoreBundle/Entity/Session.php
@@ -202,9 +202,14 @@ class Session
     //protected $position;
 
     /**
-     * @ORM\OneToMany(targetEntity="Chamilo\CourseBundle\Entity\CItemProperty", mappedBy="session")
+     * @var Session[]|ArrayCollection
+     * @ORM\OneToMany(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CItemProperty",
+     *     mappedBy="session",
+     *     cascade={"persist","remove"},
+     * )
      */
-    //protected $items;
+    protected $itemProperties;
 
     /**
      * @ORM\ManyToOne(targetEntity="Chamilo\UserBundle\Entity\User", inversedBy="sessionAsGeneralCoach")
@@ -251,7 +256,7 @@ class Session
      */
     public function __construct()
     {
-        //$this->items = new ArrayCollection();
+        $this->itemProperties = new ArrayCollection();
 
         $this->nbrClasses = 0;
         $this->nbrUsers = 0;
@@ -1246,6 +1251,11 @@ public function getUsersSubscriptionsInCourse($course)
         return $this->userCourseSubscriptions->matching($criteria);
     }
 
+    public function getItemProperties()
+    {
+        return $this->itemProperties;
+    }
+
     /**
      * @return int
      */
diff --git a/src/Chamilo/CoreBundle/Entity/UserCourseCategory.php b/src/Chamilo/CoreBundle/Entity/UserCourseCategory.php
index 7ba359a9b53..f8eced5dedb 100644
--- a/src/Chamilo/CoreBundle/Entity/UserCourseCategory.php
+++ b/src/Chamilo/CoreBundle/Entity/UserCourseCategory.php
@@ -3,6 +3,8 @@
 
 namespace Chamilo\CoreBundle\Entity;
 
+use Database;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
@@ -43,6 +45,14 @@ class UserCourseCategory
      */
     protected $sort;
 
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCoreBundle:UserCourseCategory');
+    }
+
     /**
      * Set userId.
      *
diff --git a/src/Chamilo/CourseBundle/Entity/CCourseSetting.php b/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
index 50577716c6c..7cf72721520 100644
--- a/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
+++ b/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
@@ -3,6 +3,7 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\Course;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
@@ -97,6 +98,19 @@ class CCourseSetting
      */
     protected $subkeytext;
 
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="settings")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+     */
+    protected $course;
+
+    public function __construct()
+    {
+        $this->title = '';
+    }
+
     /**
      * Set variable.
      *
@@ -336,4 +350,25 @@ public function getCId()
     {
         return $this->cId;
     }
+
+    /**
+     * @return Course
+     */
+    public function getCourse()
+    {
+        return $this->course;
+    }
+
+    /**
+     * @param Course $course
+     *
+     * @return $this
+     */
+    public function setCourse($course)
+    {
+        $this->course = $course;
+        $this->course->getSettings()->add($this);
+
+        return $this;
+    }
 }
diff --git a/src/Chamilo/CourseBundle/Entity/CGroupInfo.php b/src/Chamilo/CourseBundle/Entity/CGroupInfo.php
index b37a3c2f87d..69e23854117 100644
--- a/src/Chamilo/CourseBundle/Entity/CGroupInfo.php
+++ b/src/Chamilo/CourseBundle/Entity/CGroupInfo.php
@@ -3,6 +3,8 @@
 
 namespace Chamilo\CourseBundle\Entity;
 
+use Database;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
@@ -154,6 +156,14 @@ class CGroupInfo
      */
     protected $sessionId;
 
+    /**
+     * @return EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CGroupInfo');
+    }
+
     /**
      * @var int needed for setting['group_document_access']
      *
diff --git a/src/Chamilo/CourseBundle/Entity/CItemProperty.php b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
index f66ddf81ea0..e8b662e8f30 100644
--- a/src/Chamilo/CourseBundle/Entity/CItemProperty.php
+++ b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
@@ -4,14 +4,23 @@
 namespace Chamilo\CourseBundle\Entity;
 
 use Chamilo\CoreBundle\Entity\Course;
+use Chamilo\CoreBundle\Entity\Repository\ItemPropertyRepository;
+use Chamilo\CoreBundle\Entity\Session;
 use Chamilo\UserBundle\Entity\User;
+use Database;
+use DateTime;
+use DateTimeZone;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
+use Exception;
 
 /**
  * CItemProperty.
  *
  * @ORM\Table(name="c_item_property", indexes={@ORM\Index(name="idx_item_property_toolref", columns={"tool", "ref"})})
  * @ORM\Entity(repositoryClass="Chamilo\CoreBundle\Entity\Repository\ItemPropertyRepository")
+ * @ORM\HasLifecycleCallbacks
  */
 class CItemProperty
 {
@@ -31,13 +40,19 @@ class CItemProperty
      */
     protected $id;
 
-    /** //, inversedBy="users",.
-     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", cascade={"persist"})
-     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
+    /**
+     * @var Course
+     *
+     * @ORM\ManyToOne(
+     *     targetEntity="Chamilo\CoreBundle\Entity\Course",
+     *     inversedBy="itemProperties",
+     *     cascade={"persist"}
+     * )
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id", nullable=true)
      */
     protected $course;
 
-    /** //, inversedBy="users",.
+    /**
      * @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CGroupInfo", cascade={"persist"})
      * @ORM\JoinColumn(name="to_group_id", referencedColumnName="iid")
      */
@@ -55,9 +70,15 @@ class CItemProperty
      */
     protected $insertUser;
 
-    /** //, inversedBy="users",.
-     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Session", cascade={"persist"})
-     * @ORM\JoinColumn(name="session_id", referencedColumnName="id")
+    /**
+     * @var Session
+     *
+     * @ORM\ManyToOne(
+     *     targetEntity="Chamilo\CoreBundle\Entity\Session",
+     *     inversedBy="itemProperties",
+     *     cascade={"persist", "remove"}
+     * )
+     * @ORM\JoinColumn(name="session_id", referencedColumnName="id", nullable=true)
      */
     protected $session;
 
@@ -69,14 +90,14 @@ class CItemProperty
     protected $tool;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="insert_date", type="datetime", nullable=false)
      */
     protected $insertDate;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="lastedit_date", type="datetime", nullable=false)
      */
@@ -111,14 +132,14 @@ class CItemProperty
     protected $visibility;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="start_visible", type="datetime", nullable=true)
      */
     protected $startVisible;
 
     /**
-     * @var \DateTime
+     * @var DateTime
      *
      * @ORM\Column(name="end_visible", type="datetime", nullable=true)
      */
@@ -126,13 +147,50 @@ class CItemProperty
 
     /**
      * CItemProperty constructor.
+     *
+     * @param Course $course
+     *
+     * @throws Exception
      */
-    public function __construct(Course $course)
+    public function __construct($course)
     {
         $this->visibility = 1;
-        $this->course = $course;
-        $this->insertDate = new \DateTime('now', new \DateTimeZone('UTC'));
-        $this->lasteditDate = new \DateTime('now', new \DateTimeZone('UTC'));
+        $this->setCourse($course);
+        $this->insertDate = new DateTime('now', new DateTimeZone('UTC'));
+        $this->lasteditDate = new DateTime('now', new DateTimeZone('UTC'));
+        $this->lasteditUserId = api_get_user_entity(api_get_user_id() ?: api_get_anonymous_id());
+    }
+
+    /**
+     * @return ItemPropertyRepository|EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloCourseBundle:CItemProperty');
+    }
+
+    /**
+     * @ORM\PreUpdate
+     * @throws Exception
+     */
+    public function preUpdate()
+    {
+        $this->lasteditDate = new DateTime('now', new DateTimeZone('UTC'));
+    }
+
+    /**
+     * Copies iid to id if not set yet.
+     *
+     * @ORM\PostPersist
+     * @throws OptimisticLockException
+     */
+    public function postPersist()
+    {
+        if (is_null($this->id)) {
+            $this->id = $this->iid;
+            Database::getManager()->persist($this);
+            Database::getManager()->flush();
+        }
     }
 
     /**
@@ -162,7 +220,7 @@ public function getTool()
     /**
      * Set insertDate.
      *
-     * @param \DateTime $insertDate
+     * @param DateTime $insertDate
      *
      * @return CItemProperty
      */
@@ -176,7 +234,7 @@ public function setInsertDate($insertDate)
     /**
      * Get insertDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getInsertDate()
     {
@@ -186,9 +244,11 @@ public function getInsertDate()
     /**
      * Set lasteditDate.
      *
+     * @param DateTime $lasteditDate
+     *
      * @return CItemProperty
      */
-    public function setLasteditDate(\DateTime $lasteditDate)
+    public function setLasteditDate($lasteditDate)
     {
         $this->lasteditDate = $lasteditDate;
 
@@ -198,7 +258,7 @@ public function setLasteditDate(\DateTime $lasteditDate)
     /**
      * Get lasteditDate.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getLasteditDate()
     {
@@ -304,11 +364,11 @@ public function getVisibility()
     /**
      * Set startVisible.
      *
-     * @param \DateTime $startVisible
+     * @param DateTime $startVisible
      *
      * @return CItemProperty
      */
-    public function setStartVisible(\DateTime $startVisible = null)
+    public function setStartVisible(DateTime $startVisible = null)
     {
         $this->startVisible = $startVisible;
 
@@ -318,7 +378,7 @@ public function setStartVisible(\DateTime $startVisible = null)
     /**
      * Get startVisible.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getStartVisible()
     {
@@ -328,11 +388,11 @@ public function getStartVisible()
     /**
      * Set endVisible.
      *
-     * @param \DateTime $endVisible
+     * @param DateTime $endVisible
      *
      * @return CItemProperty
      */
-    public function setEndVisible(\DateTime $endVisible = null)
+    public function setEndVisible(DateTime $endVisible = null)
     {
         $this->endVisible = $endVisible;
 
@@ -342,7 +402,7 @@ public function setEndVisible(\DateTime $endVisible = null)
     /**
      * Get endVisible.
      *
-     * @return \DateTime
+     * @return DateTime
      */
     public function getEndVisible()
     {
@@ -389,6 +449,9 @@ public function getSession()
     public function setSession($session)
     {
         $this->session = $session;
+        if (!is_null($session)) {
+            $session->getItemProperties()->add($this);
+        }
 
         return $this;
     }
@@ -409,6 +472,7 @@ public function getCourse()
     public function setCourse($course)
     {
         $this->course = $course;
+        $this->course->getItemProperties()->add($this);
 
         return $this;
     }
@@ -462,9 +526,11 @@ public function getInsertUser()
     }
 
     /**
+     * @param User $insertUser
+     *
      * @return $this
      */
-    public function setInsertUser(User $insertUser)
+    public function setInsertUser($insertUser)
     {
         $this->insertUser = $insertUser;
         $this->lasteditUserId = $insertUser->getId();
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index 526816f4c0f..6c56a1ea628 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -7,6 +7,7 @@
 use Chamilo\CoreBundle\Entity\Session;
 use Database;
 use DateTime;
+use DateTimeZone;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Mapping as ORM;
@@ -277,7 +278,11 @@ class CLp
 
     /**
      * @var Course
-     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="learningPaths")
+     * @ORM\ManyToOne(
+     *     targetEntity="Chamilo\CoreBundle\Entity\Course",
+     *     inversedBy="learningPaths",
+     *     cascade={"persist", "remove"}
+     * )
      * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
      */
     protected $course;
@@ -303,14 +308,15 @@ class CLp
      * @ORM\OneToMany(
      *     targetEntity="CLpItem",
      *     mappedBy="learningPath",
-     *     orphanRemoval=true,
-     *     cascade={"persist", "remove"}
+     *     orphanRemoval=true
      * )
      */
     protected $items;
 
     /**
      * Constructor.
+     *
+     * @throws Exception
      */
     public function __construct()
     {
@@ -338,12 +344,17 @@ public function __construct()
         $this->autolaunch = 0;
         $this->maxAttempts = 0;
         $this->subscribeUsers = 0;
-        $this->createdOn = new DateTime();
-        $this->modifiedOn = new DateTime();
+        $this->createdOn = new DateTime('now', new DateTimeZone('utc'));
+        $this->modifiedOn = new DateTime('now', new DateTimeZone('utc'));
         $this->accumulateScormTime = 1;
         $this->items = new ArrayCollection();
     }
 
+    public function __toString()
+    {
+        return sprintf('learning path %s ("%s") of %s', $this->id, $this->name, $this->course->__toString());
+    }
+
     /**
      * @return EntityRepository
      */
@@ -399,6 +410,7 @@ function ($lp) {
 
     /**
      * If id is null, copies iid to id and writes again.
+     * Updates item properties.
      *
      * @throws OptimisticLockException
      *
@@ -411,6 +423,23 @@ public function postPersist()
             Database::getManager()->persist($this);
             Database::getManager()->flush($this);
         }
+        $courseInfo = api_get_course_info_by_id($this->course->getId());
+        $userId = api_get_user_id();
+        api_item_property_update(
+            $courseInfo,
+            TOOL_LEARNPATH,
+            $this->getId(),
+            'LearnpathAdded',
+            $userId
+        );
+        api_set_default_visibility(
+            $this->getId(),
+            TOOL_LEARNPATH,
+            0,
+            $courseInfo,
+            $this->getSessionId(),
+            $userId
+        );
     }
 
     /**
@@ -1307,9 +1336,11 @@ public function getLastItemInFirstLevel()
      * Updates this learning path's final item previous item id.
      * Sets it to the last item in first level.
      *
+     * @param bool $andFlush flush after persist
+     *
      * @throws OptimisticLockException
      */
-    public function updateFinalItemsPreviousItemId()
+    public function updateFinalItemsPreviousItemId($andFlush = true)
     {
         $finalItem = $this->getFinalItem();
         if (!is_null($finalItem)) {
@@ -1317,7 +1348,9 @@ public function updateFinalItemsPreviousItemId()
             if (!is_null($last)) {
                 $finalItem->setPreviousItemId($last->getId());
                 Database::getManager()->persist($finalItem);
-                Database::getManager()->flush($finalItem);
+                if ($andFlush) {
+                    Database::getManager()->flush($finalItem);
+                }
             }
         }
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpItem.php b/src/Chamilo/CourseBundle/Entity/CLpItem.php
index 4a1f1c78c4e..6204e29a987 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpItem.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpItem.php
@@ -206,8 +206,15 @@ class CLpItem
     /**
      * @var CLp
      *
-     * @ORM\ManyToOne(targetEntity="CLp", inversedBy="items")
-     * @ORM\JoinColumn(name="lp_id", referencedColumnName="iid")
+     * @ORM\ManyToOne(
+     *     targetEntity="Chamilo\CourseBundle\Entity\CLp",
+     *     inversedBy="items",
+     *     cascade={"persist", "remove"}
+     * )
+     * @ORM\JoinColumn(
+     *     name="lp_id",
+     *     referencedColumnName="iid",
+     * )
      */
     protected $learningPath;
 
@@ -235,6 +242,17 @@ public function __construct()
         $this->path = '';
     }
 
+    public function __toString()
+    {
+        return sprintf(
+            'item %s (%s "%s") of %s',
+            $this->id,
+            $this->itemType,
+            $this->title,
+            $this->learningPath
+        );
+    }
+
     /**
      * @return EntityRepository
      */
@@ -313,9 +331,8 @@ public function postPersist()
             }
         }
         Database::getManager()->persist($this);
+        $this->learningPath->updateFinalItemsPreviousItemId(false);
         Database::getManager()->flush();
-
-        $this->learningPath->updateFinalItemsPreviousItemId();
     }
 
     /**
@@ -940,14 +957,17 @@ public function getCId()
     }
 
     /**
+     * Sets learning path AND course (copying the learning path's)
+     *
+     * @param CLp $clp
+     *
      * @return CLpItem
      */
     public function setLearningPath(CLp $clp)
     {
         $this->learningPath = $clp;
         $clp->getItems()->add($this);
-        $this->course = $clp->getCourse();
-        $this->course->getLearningPathItems()->add($this);
+        $this->setCourse($clp->getCourse());
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CTool.php b/src/Chamilo/CourseBundle/Entity/CTool.php
index ad59820f0cc..50d44cc10ef 100644
--- a/src/Chamilo/CourseBundle/Entity/CTool.php
+++ b/src/Chamilo/CourseBundle/Entity/CTool.php
@@ -4,7 +4,9 @@
 namespace Chamilo\CourseBundle\Entity;
 
 use Chamilo\CoreBundle\Entity\Course;
+use Database;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\OptimisticLockException;
 
 /**
  * CTool.
@@ -17,6 +19,7 @@
  *  }
  * )
  * @ORM\Entity
+ * @ORM\HasLifecycleCallbacks
  */
 class CTool
 {
@@ -131,10 +134,35 @@ class CTool
      * @var Course
      *
      * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course", inversedBy="tools")
-     * @ORM\JoinColumn(name="c_id", referencedColumnName="iid")
+     * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
      */
     protected $course;
 
+    public function __construct()
+    {
+        $this->admin = 0;
+        $this->address = 'squaregrey.gif';
+        $this->addedTool = false;
+        $this->target = '_self';
+        $this->sessionId = 0;
+    }
+
+    /**
+     * If id is null, copies iid to id and writes again.
+     *
+     * @ORM\PostPersist
+     *
+     * @throws OptimisticLockException
+     */
+    public function postPersist()
+    {
+        if (is_null($this->id)) { // keep this test to avoid recursion
+            $this->id = $this->iid;
+            Database::getManager()->persist($this);
+            Database::getManager()->flush($this);
+        }
+    }
+
     /**
      * @return int
      */
diff --git a/src/Chamilo/UserBundle/Entity/User.php b/src/Chamilo/UserBundle/Entity/User.php
index e3591a0ede3..b2f3ddd2faf 100644
--- a/src/Chamilo/UserBundle/Entity/User.php
+++ b/src/Chamilo/UserBundle/Entity/User.php
@@ -3,11 +3,18 @@
 
 namespace Chamilo\UserBundle\Entity;
 
+use Chamilo\CoreBundle\Entity\CourseRelUser;
 use Chamilo\CoreBundle\Entity\ExtraFieldValues;
+use Chamilo\CoreBundle\Entity\Session;
 use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
 use Chamilo\CoreBundle\Entity\Skill;
+use Chamilo\CoreBundle\Entity\UserCourseCategory;
 use Chamilo\CoreBundle\Entity\UsergroupRelUser;
+use Chamilo\UserBundle\Repository\UserRepository;
+use Database;
 use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Criteria;
+use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Event\LifecycleEventArgs;
 use Doctrine\ORM\Mapping as ORM;
 use FOS\UserBundle\Model\GroupInterface;
@@ -449,6 +456,14 @@ public function __construct()
         $this->credentialsExpired = false;
     }
 
+    /**
+     * @return UserRepository|EntityRepository
+     */
+    public static function getRepository()
+    {
+        return Database::getManager()->getRepository('ChamiloUserBundle:User');
+    }
+
     /**
      * @return string
      */
@@ -2578,12 +2593,12 @@ public function getPictureLegacy()
      *
      * @param int $relationType \Chamilo\CoreBundle\Entity\SessionRelUser::relationTypeList key
      *
-     * @return \Chamilo\CoreBundle\Entity\Session[]
+     * @return Session[]
      */
     public function getSessions($relationType)
     {
         $sessions = [];
-        foreach (\Database::getManager()->getRepository('ChamiloCoreBundle:SessionRelUser')->findBy([
+        foreach (Database::getManager()->getRepository('ChamiloCoreBundle:SessionRelUser')->findBy([
             'user' => $this,
         ]) as $sessionRelUser) {
             if ($sessionRelUser->getRelationType() == $relationType) {
@@ -2597,7 +2612,7 @@ public function getSessions($relationType)
     /**
      * Retreives this user's related student sessions.
      *
-     * @return \Chamilo\CoreBundle\Entity\Session[]
+     * @return Session[]
      */
     public function getStudentSessions()
     {
@@ -2607,7 +2622,7 @@ public function getStudentSessions()
     /**
      * Retreives this user's related DRH sessions.
      *
-     * @return \Chamilo\CoreBundle\Entity\Session[]
+     * @return Session[]
      */
     public function getDRHSessions()
     {
@@ -2619,7 +2634,7 @@ public function getDRHSessions()
      *
      * @param int $relationType \Chamilo\CoreBundle\Entity\SessionRelUser::relationTypeList key
      *
-     * @return \Chamilo\CoreBundle\Entity\Session[]
+     * @return Session[]
      */
     public function getCurrentlyAccessibleSessions($relationType = 0)
     {
@@ -2632,4 +2647,35 @@ public function getCurrentlyAccessibleSessions($relationType = 0)
 
         return $sessions;
     }
+
+    /**
+     * Find the largest sort value in a given UserCourseCategory
+     * This method is used when we are moving a course to a different category
+     * and also when a user subscribes to courses (the new course is added at the end of the main category).
+     *
+     * Used to be implemented in global function \api_max_sort_value.
+     * Reimplemented using the ORM cache.
+     *
+     * @param UserCourseCategory|null $userCourseCategory the user_course_category
+     *
+     * @return int|mixed
+     */
+    public function getMaxSortValue($userCourseCategory = null)
+    {
+        $categoryCourses = $this->courses->matching(
+            Criteria::create()
+                ->where(Criteria::expr()->neq('relationType', COURSE_RELATION_TYPE_RRHH))
+                ->andWhere(Criteria::expr()->eq('userCourseCat', $userCourseCategory))
+        );
+        return $categoryCourses->isEmpty()
+            ? 0
+            : max(
+                $categoryCourses->map(
+                    /** @var CourseRelUser $courseRelUser */
+                    function ($courseRelUser) {
+                        return $courseRelUser->getSort();
+                    }
+                )->toArray()
+            );
+    }
 }

From e635976ed71f8c9b9a8fd268710fc2f681c4e0d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Mon, 13 Jul 2020 15:07:33 +0200
Subject: [PATCH 07/16] Minor code fix (flintci) - refs BT#17453

---
 main/inc/lib/add_course.lib.inc.php           |  3 ++
 main/inc/lib/api.lib.php                      |  1 +
 main/inc/lib/webservices/Rest.php             | 16 ++----
 src/Chamilo/CoreBundle/Entity/Course.php      | 52 ++++++-------------
 .../CoreBundle/Entity/CourseRelUser.php       | 12 ++---
 src/Chamilo/CoreBundle/Entity/Session.php     |  8 +--
 .../CourseBundle/Entity/CItemProperty.php     |  2 +
 src/Chamilo/CourseBundle/Entity/CLp.php       |  1 +
 src/Chamilo/CourseBundle/Entity/CLpItem.php   |  4 +-
 src/Chamilo/UserBundle/Entity/User.php        | 13 ++---
 10 files changed, 46 insertions(+), 66 deletions(-)

diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index 7b63cbff213..eb71a710455 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -921,6 +921,7 @@ public static function register_course($params, $accessUrlId = 1)
                 $expirationDate = new DateTime(api_get_utc_datetime($date), new DateTimeZone('utc'));
             } catch (Exception $exception) {
                 error_log(sprintf('expiration_date "%s" is invalid', $date));
+
                 return 0;
             }
         }
@@ -928,6 +929,7 @@ public static function register_course($params, $accessUrlId = 1)
         $user = api_get_user_entity($userId);
         if (is_null($user)) {
             error_log(sprintf('user_id "%s" is invalid', $userId));
+
             return 0;
         }
 
@@ -1007,6 +1009,7 @@ public static function register_course($params, $accessUrlId = 1)
             Database::getManager()->flush();
         } catch (OptimisticLockException $exception) {
             error_log($exception);
+
             return 0;
         }
 
diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php
index 5a521671841..8c88ae6cc5e 100644
--- a/main/inc/lib/api.lib.php
+++ b/main/inc/lib/api.lib.php
@@ -5043,6 +5043,7 @@ function api_max_sort_value($user_course_category, $user_id)
     /** @var User $user */
     $user = User::getRepository()->find($user_id);
     $userCourseCategory = UserCourseCategory::getRepository()->find($user_course_category);
+
     return is_null($user) ? 0 : $user->getMaxSortValue($userCourseCategory);
 }
 
diff --git a/main/inc/lib/webservices/Rest.php b/main/inc/lib/webservices/Rest.php
index 3cb417e0e7b..8c389fcf31b 100644
--- a/main/inc/lib/webservices/Rest.php
+++ b/main/inc/lib/webservices/Rest.php
@@ -1917,13 +1917,7 @@ public function updateUserFromUserName($parameters)
                                     $fieldValue = $field['field_value'];
                                     if (!isset($fieldName) || !isset($fieldValue) ||
                                         !UserManager::update_extra_field_value($userId, $fieldName, $fieldValue)) {
-                                        throw new Exception(
-                                            sprintf(
-                                                '%s: %s',
-                                                get_lang('CouldNotUpdateExtraFieldValue'),
-                                                print_r($field, true)
-                                            )
-                                        );
+                                        throw new Exception(sprintf('%s: %s', get_lang('CouldNotUpdateExtraFieldValue'), print_r($field, true)));
                                     }
                                 }
                             } else {
@@ -2121,9 +2115,7 @@ public function createLearningPath(array $spec)
                 $prerequisitesDisplayOrders = [];
                 foreach ($itemSpecs as $itemSpec) {
                     if (!array_key_exists('display_order_id', $itemSpec)) {
-                        throw new Exception(
-                            sprintf('display_order_id missing from item spec: %s', print_r($itemSpec, true))
-                        );
+                        throw new Exception(sprintf('display_order_id missing from item spec: %s', print_r($itemSpec, true)));
                     }
                     $displayOrder = $itemSpec['display_order_id'];
                     $type = (array_key_exists('type', $itemSpec) ? $itemSpec['type'] : 'dir');
@@ -2138,9 +2130,7 @@ public function createLearningPath(array $spec)
                         ->setTitle($title);
                     if (in_array($type, ['document', 'final_item', 'forum', 'link', 'quiz'])) {
                         if (!array_key_exists('name_to_find', $itemSpec)) {
-                            throw new Exception(
-                                sprintf('name_to_find missing from %s spec: %s', $type, print_r($itemSpec, true))
-                            );
+                            throw new Exception(sprintf('name_to_find missing from %s spec: %s', $type, print_r($itemSpec, true)));
                         }
                         $resource = $course->findResource($type, $itemSpec['name_to_find']);
                         $item->setPath('forum' === $type ? $resource->getForumId() : $resource->getId());
diff --git a/src/Chamilo/CoreBundle/Entity/Course.php b/src/Chamilo/CoreBundle/Entity/Course.php
index ab09ecb3084..4a200cfc70f 100644
--- a/src/Chamilo/CoreBundle/Entity/Course.php
+++ b/src/Chamilo/CoreBundle/Entity/Course.php
@@ -1546,6 +1546,14 @@ public function getItemProperties()
         return $this->itemProperties;
     }
 
+    /**
+     * @return CCourseSetting[]|ArrayCollection
+     */
+    public function getSettings()
+    {
+        return $this->settings;
+    }
+
     /**
      * @param CourseRelUser $subscription
      *
@@ -1587,17 +1595,9 @@ protected function addUser($user, $relationType, $role, $status)
         $this->addUsers($courseRelUser);
     }
 
-    /**
-     * @return CCourseSetting[]|ArrayCollection
-     */
-    public function getSettings()
-    {
-        return $this->settings;
-    }
-
     /**
      * Initializes the course's file repository.
-     * Replaces \AddCourse::prepare_course_repository
+     * Replaces \AddCourse::prepare_course_repository.
      *
      * @throws Exception
      */
@@ -1643,22 +1643,13 @@ private function prepareRepository()
             if (!file_exists($indexHtmlFilePath)) {
                 $indexHtmlFile = fopen($indexHtmlFilePath, 'w');
                 if (false === $indexHtmlFile) {
-                    throw new Exception(sprintf(
-                        'Could not create course repository subfolder index file "%s"',
-                        $indexHtmlFilePath
-                    ));
+                    throw new Exception(sprintf('Could not create course repository subfolder index file "%s"', $indexHtmlFilePath));
                 }
                 if (false === fwrite($indexHtmlFile, $indexHtmlContents)) {
-                    throw new Exception(sprintf(
-                        'Could not write to course repository subfolder index file "%s"',
-                        $indexHtmlFilePath
-                    ));
+                    throw new Exception(sprintf('Could not write to course repository subfolder index file "%s"', $indexHtmlFilePath));
                 }
                 if (!fclose($indexHtmlFile)) {
-                    throw new Exception(sprintf(
-                        'Could not close course repository subfolder index file "%s"',
-                        $indexHtmlFilePath
-                    ));
+                    throw new Exception(sprintf('Could not close course repository subfolder index file "%s"', $indexHtmlFilePath));
                 }
                 @chmod($indexHtmlFile, $filePermissions);
             }
@@ -1668,10 +1659,7 @@ private function prepareRepository()
         $dropboxHtAccessFilePath = $repositoryPath.'/dropbox/.htaccess';
         $dropboxHtAccessFile = fopen($dropboxHtAccessFilePath, 'w');
         if (false === $dropboxHtAccessFile) {
-            throw new Exception(sprintf(
-                'Could not create course repository dropbox subfolder access control file "%s"',
-                $dropboxHtAccessFilePath
-            ));
+            throw new Exception(sprintf('Could not create course repository dropbox subfolder access control file "%s"', $dropboxHtAccessFilePath));
         }
         if (!fwrite(
             $dropboxHtAccessFile,
@@ -1683,16 +1671,10 @@ private function prepareRepository()
 
 php_flag zlib.output_compression off"
         )) {
-            throw new Exception(sprintf(
-                'Could not write to course repository dropbox subfolder access control file "%s"',
-                $dropboxHtAccessFilePath
-            ));
+            throw new Exception(sprintf('Could not write to course repository dropbox subfolder access control file "%s"', $dropboxHtAccessFilePath));
         }
         if (!fclose($dropboxHtAccessFile)) {
-            throw new Exception(sprintf(
-                'Could not close course repository dropbox subfolder access control file "%s"',
-                $dropboxHtAccessFilePath
-            ));
+            throw new Exception(sprintf('Could not close course repository dropbox subfolder access control file "%s"', $dropboxHtAccessFilePath));
         }
     }
 
@@ -1714,7 +1696,7 @@ private function createTools()
             [TOOL_STUDENTPUBLICATION, 'work/work.php', 'works.gif', 'student_publications', 'interaction'],
             [TOOL_SURVEY, 'survey/survey_list.php', 'survey.gif', 'survey', 'interaction'],
             [TOOL_WIKI, 'wiki/index.php', 'wiki.gif', 'wiki', 'interaction'],
-            [TOOL_GRADEBOOK, 'gradebook/index.php', 'gradebook.gif' , 'gradebook', 'authoring'],
+            [TOOL_GRADEBOOK, 'gradebook/index.php', 'gradebook.gif', 'gradebook', 'authoring'],
             [TOOL_GLOSSARY, 'glossary/index.php', 'glossary.gif', 'glossary', 'authoring'],
             [TOOL_NOTEBOOK, 'notebook/index.php', 'notebook.gif', 'notebook', 'interaction'],
         ];
@@ -1781,7 +1763,7 @@ private function createSettings()
             'allow_user_edit_announcement' => ['default' => 0, 'category' => 'announcement'],
             'email_alert_manager_on_new_quiz' => [
                 'default' => (api_get_setting('email_alert_manager_on_new_quiz') === 'true') ? 1 : 0,
-                'category' => 'quiz'
+                'category' => 'quiz',
             ],
             'allow_user_image_forum' => ['default' => 1, 'category' => 'forum'],
             'course_theme' => ['default' => '', 'category' => 'theme'],
diff --git a/src/Chamilo/CoreBundle/Entity/CourseRelUser.php b/src/Chamilo/CoreBundle/Entity/CourseRelUser.php
index f321d4afff3..d7710ed29c4 100644
--- a/src/Chamilo/CoreBundle/Entity/CourseRelUser.php
+++ b/src/Chamilo/CoreBundle/Entity/CourseRelUser.php
@@ -98,19 +98,19 @@ public function __construct()
     }
 
     /**
-     * @return EntityRepository
+     * @return string
      */
-    public static function getRepository()
+    public function __toString()
     {
-        return Database::getManager()->getRepository('ChamiloCoreBundle:CourseRelUser');
+        return (string) $this->getCourse()->getCode();
     }
 
     /**
-     * @return string
+     * @return EntityRepository
      */
-    public function __toString()
+    public static function getRepository()
     {
-        return (string) $this->getCourse()->getCode();
+        return Database::getManager()->getRepository('ChamiloCoreBundle:CourseRelUser');
     }
 
     /**
diff --git a/src/Chamilo/CoreBundle/Entity/Session.php b/src/Chamilo/CoreBundle/Entity/Session.php
index e43e14b18c9..539ebd9800c 100644
--- a/src/Chamilo/CoreBundle/Entity/Session.php
+++ b/src/Chamilo/CoreBundle/Entity/Session.php
@@ -388,8 +388,7 @@ public function addUser(SessionRelUser $user)
     }
 
     /**
-     * @param int $status
-     *
+     * @param int  $status
      * @param User $user
      */
     public function addUserInSession($status, $user)
@@ -404,6 +403,7 @@ public function addUserInSession($status, $user)
 
     /**
      * @param SessionRelUser $subscription
+     *
      * @return bool
      */
     public function hasUser($subscription)
@@ -542,7 +542,7 @@ public function hasUserInCourse($user, $course, $status = null)
     }
 
     /**
-     * @param User $user
+     * @param User   $user
      * @param Course $course
      *
      * @return bool
@@ -564,7 +564,7 @@ public function hasCoachInCourseWithStatus($user, $course)
     }
 
     /**
-     * @param User $user
+     * @param User   $user
      * @param Course $course
      * @param string $status
      *
diff --git a/src/Chamilo/CourseBundle/Entity/CItemProperty.php b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
index e8b662e8f30..f6e4c48eaab 100644
--- a/src/Chamilo/CourseBundle/Entity/CItemProperty.php
+++ b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
@@ -171,6 +171,7 @@ public static function getRepository()
 
     /**
      * @ORM\PreUpdate
+     *
      * @throws Exception
      */
     public function preUpdate()
@@ -182,6 +183,7 @@ public function preUpdate()
      * Copies iid to id if not set yet.
      *
      * @ORM\PostPersist
+     *
      * @throws OptimisticLockException
      */
     public function postPersist()
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index 6c56a1ea628..2d254235c7a 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -369,6 +369,7 @@ public static function getRepository()
      * Computes displayOrder if still zéro.
      *
      * @ORM\PrePersist
+     *
      * @throws Exception
      */
     public function prePersist()
diff --git a/src/Chamilo/CourseBundle/Entity/CLpItem.php b/src/Chamilo/CourseBundle/Entity/CLpItem.php
index 6204e29a987..4b1dc8c9e00 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpItem.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpItem.php
@@ -957,13 +957,13 @@ public function getCId()
     }
 
     /**
-     * Sets learning path AND course (copying the learning path's)
+     * Sets learning path AND course (copying the learning path's).
      *
      * @param CLp $clp
      *
      * @return CLpItem
      */
-    public function setLearningPath(CLp $clp)
+    public function setLearningPath($clp)
     {
         $this->learningPath = $clp;
         $clp->getItems()->add($this);
diff --git a/src/Chamilo/UserBundle/Entity/User.php b/src/Chamilo/UserBundle/Entity/User.php
index b2f3ddd2faf..af98989dbe7 100644
--- a/src/Chamilo/UserBundle/Entity/User.php
+++ b/src/Chamilo/UserBundle/Entity/User.php
@@ -457,19 +457,19 @@ public function __construct()
     }
 
     /**
-     * @return UserRepository|EntityRepository
+     * @return string
      */
-    public static function getRepository()
+    public function __toString()
     {
-        return Database::getManager()->getRepository('ChamiloUserBundle:User');
+        return $this->getUsername();
     }
 
     /**
-     * @return string
+     * @return UserRepository|EntityRepository
      */
-    public function __toString()
+    public static function getRepository()
     {
-        return $this->getUsername();
+        return Database::getManager()->getRepository('ChamiloUserBundle:User');
     }
 
     /**
@@ -2667,6 +2667,7 @@ public function getMaxSortValue($userCourseCategory = null)
                 ->where(Criteria::expr()->neq('relationType', COURSE_RELATION_TYPE_RRHH))
                 ->andWhere(Criteria::expr()->eq('userCourseCat', $userCourseCategory))
         );
+
         return $categoryCourses->isEmpty()
             ? 0
             : max(

From 2ff29d30e503b9536f2530a3ebe418fab1c20459 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Mon, 13 Jul 2020 15:18:20 +0200
Subject: [PATCH 08/16] Minor code fixes (scrutinizer) - refs BT#17453

---
 src/Chamilo/CoreBundle/Entity/Course.php          | 10 ++++++----
 src/Chamilo/CourseBundle/Entity/CItemProperty.php |  2 +-
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/Chamilo/CoreBundle/Entity/Course.php b/src/Chamilo/CoreBundle/Entity/Course.php
index 4a200cfc70f..90f94530148 100644
--- a/src/Chamilo/CoreBundle/Entity/Course.php
+++ b/src/Chamilo/CoreBundle/Entity/Course.php
@@ -62,7 +62,7 @@ class Course
     protected $id;
 
     /**
-     * @var CourseRelUser|ArrayCollection
+     * @var CourseRelUser[]|ArrayCollection
      *
      * "orphanRemoval" is needed to delete the CourseRelUser relation
      * in the CourseAdmin class. The setUsers, getUsers, removeUsers and
@@ -421,7 +421,7 @@ class Course
      */
     public function __construct()
     {
-        $this->activateLegal = false;
+        $this->activateLegal = 0;
         $this->addTeachersToSessionsCourses = false;
         $this->creationDate = new DateTime('now', new DateTimeZone('utc'));
         $this->lastVisit = null;
@@ -653,7 +653,7 @@ public function addUrls(AccessUrlRelCourse $url)
     }
 
     /**
-     * @return CourseRelUser|ArrayCollection
+     * @return CourseRelUser[]|ArrayCollection
      */
     public function getUsers()
     {
@@ -1651,7 +1651,9 @@ private function prepareRepository()
                 if (!fclose($indexHtmlFile)) {
                     throw new Exception(sprintf('Could not close course repository subfolder index file "%s"', $indexHtmlFilePath));
                 }
-                @chmod($indexHtmlFile, $filePermissions);
+                if (!@chmod($indexHtmlFile, $filePermissions)) {
+                    // never mind, on some platforms it is not possible anyway
+                }
             }
         }
 
diff --git a/src/Chamilo/CourseBundle/Entity/CItemProperty.php b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
index f6e4c48eaab..b5b1f81bcf1 100644
--- a/src/Chamilo/CourseBundle/Entity/CItemProperty.php
+++ b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
@@ -158,7 +158,7 @@ public function __construct($course)
         $this->setCourse($course);
         $this->insertDate = new DateTime('now', new DateTimeZone('UTC'));
         $this->lasteditDate = new DateTime('now', new DateTimeZone('UTC'));
-        $this->lasteditUserId = api_get_user_entity(api_get_user_id() ?: api_get_anonymous_id());
+        $this->lasteditUserId = api_get_user_id() ?: api_get_anonymous_id();
     }
 
     /**

From 18232d54bf0919d3e7bae734e92c4524361cddc7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Tue, 14 Jul 2020 11:01:30 +0200
Subject: [PATCH 09/16] avoid integrity constraint violations on c_id - refs
 BT#17453

Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'c_id' cannot be null
---
 src/Chamilo/CourseBundle/Entity/CCourseSetting.php | 3 +++
 src/Chamilo/CourseBundle/Entity/CDocument.php      | 3 +++
 src/Chamilo/CourseBundle/Entity/CForumForum.php    | 3 +++
 src/Chamilo/CourseBundle/Entity/CLink.php          | 3 +++
 src/Chamilo/CourseBundle/Entity/CLp.php            | 3 +++
 src/Chamilo/CourseBundle/Entity/CLpCategory.php    | 3 +++
 src/Chamilo/CourseBundle/Entity/CLpItem.php        | 3 +++
 src/Chamilo/CourseBundle/Entity/CQuiz.php          | 3 +++
 src/Chamilo/CourseBundle/Entity/CTool.php          | 3 +++
 9 files changed, 27 insertions(+)

diff --git a/src/Chamilo/CourseBundle/Entity/CCourseSetting.php b/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
index 7cf72721520..961b4aa32f5 100644
--- a/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
+++ b/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
@@ -330,6 +330,8 @@ public function getId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CCourseSetting
@@ -337,6 +339,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CDocument.php b/src/Chamilo/CourseBundle/Entity/CDocument.php
index 1fa3a2d1bee..1630c9f2c0f 100644
--- a/src/Chamilo/CourseBundle/Entity/CDocument.php
+++ b/src/Chamilo/CourseBundle/Entity/CDocument.php
@@ -466,6 +466,8 @@ public function getId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CDocument
@@ -473,6 +475,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CForumForum.php b/src/Chamilo/CourseBundle/Entity/CForumForum.php
index 4e2e824db38..db4cc9d77d4 100644
--- a/src/Chamilo/CourseBundle/Entity/CForumForum.php
+++ b/src/Chamilo/CourseBundle/Entity/CForumForum.php
@@ -764,6 +764,8 @@ public function getForumId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CForumForum
@@ -771,6 +773,7 @@ public function getForumId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLink.php b/src/Chamilo/CourseBundle/Entity/CLink.php
index 1b5bb3f31b4..fbe3e782e4e 100644
--- a/src/Chamilo/CourseBundle/Entity/CLink.php
+++ b/src/Chamilo/CourseBundle/Entity/CLink.php
@@ -382,6 +382,8 @@ public function getId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CLink
@@ -389,6 +391,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index 2d254235c7a..8a4ca375bce 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -1209,6 +1209,8 @@ public function getId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CLp
@@ -1216,6 +1218,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpCategory.php b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
index 0ba5938f0ff..4a25ee14a6c 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpCategory.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
@@ -132,6 +132,8 @@ public function setCourse($course)
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CLpCategory
@@ -139,6 +141,7 @@ public function setCourse($course)
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpItem.php b/src/Chamilo/CourseBundle/Entity/CLpItem.php
index 4b1dc8c9e00..2a0aa4b5a82 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpItem.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpItem.php
@@ -935,6 +935,8 @@ public function getId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CLpItem
@@ -942,6 +944,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CQuiz.php b/src/Chamilo/CourseBundle/Entity/CQuiz.php
index c4ca2536cad..92fcf012e89 100644
--- a/src/Chamilo/CourseBundle/Entity/CQuiz.php
+++ b/src/Chamilo/CourseBundle/Entity/CQuiz.php
@@ -853,6 +853,8 @@ public function getId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CQuiz
@@ -860,6 +862,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CTool.php b/src/Chamilo/CourseBundle/Entity/CTool.php
index 50d44cc10ef..2dacbbd5f2d 100644
--- a/src/Chamilo/CourseBundle/Entity/CTool.php
+++ b/src/Chamilo/CourseBundle/Entity/CTool.php
@@ -450,6 +450,8 @@ public function getId()
     /**
      * Set cId.
      *
+     * @deprecated use setCourse wherever possible
+     *
      * @param int $cId
      *
      * @return CTool
@@ -457,6 +459,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
+        $this->setCourse(Course::getRepository()->find($cId));
 
         return $this;
     }

From 6fdd9179d087a645fe241bae1542a55d14b91d98 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Tue, 14 Jul 2020 11:11:12 +0200
Subject: [PATCH 10/16] course settings creation moved to
 Course::createSettings - refs BT#17453

Course::prePersist calls Course::createSettings
---
 main/inc/lib/add_course.lib.inc.php | 41 -----------------------------
 1 file changed, 41 deletions(-)

diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index eb71a710455..80ed353e5c2 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -321,7 +321,6 @@ public static function fill_db_course(
         $TABLEGROUPCATEGORIES = Database::get_course_table(TABLE_GROUP_CATEGORY);
         $TABLEITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
         $TABLETOOLDOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
-        $TABLESETTING = Database::get_course_table(TABLE_COURSE_SETTING);
         $TABLEGRADEBOOK = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
         $TABLEGRADEBOOKLINK = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
 
@@ -332,46 +331,6 @@ public static function fill_db_course(
             $defaultEmailExerciseAlert = 0;
         }
 
-        /* course_setting table (courseinfo tool)   */
-        $settings = [
-            'email_alert_manager_on_new_doc' => ['title' => '', 'default' => 0, 'category' => 'work'],
-            'email_alert_on_new_doc_dropbox' => ['default' => 0, 'category' => 'dropbox'],
-            'allow_user_edit_agenda' => ['default' => 0, 'category' => 'agenda'],
-            'allow_user_edit_announcement' => ['default' => 0, 'category' => 'announcement'],
-            'email_alert_manager_on_new_quiz' => ['default' => $defaultEmailExerciseAlert, 'category' => 'quiz'],
-            'allow_user_image_forum' => ['default' => 1, 'category' => 'forum'],
-            'course_theme' => ['default' => '', 'category' => 'theme'],
-            'allow_learning_path_theme' => ['default' => 1, 'category' => 'theme'],
-            'allow_open_chat_window' => ['default' => 1, 'category' => 'chat'],
-            'email_alert_to_teacher_on_new_user_in_course' => ['default' => 0, 'category' => 'registration'],
-            'allow_user_view_user_list' => ['default' => 1, 'category' => 'user'],
-            'display_info_advance_inside_homecourse' => ['default' => 1, 'category' => 'thematic_advance'],
-            'email_alert_students_on_new_homework' => ['default' => 0, 'category' => 'work'],
-            'enable_lp_auto_launch' => ['default' => 0, 'category' => 'learning_path'],
-            'enable_exercise_auto_launch' => ['default' => 0, 'category' => 'exercise'],
-            'enable_document_auto_launch' => ['default' => 0, 'category' => 'document'],
-            'pdf_export_watermark_text' => ['default' => '', 'category' => 'learning_path'],
-            'allow_public_certificates' => [
-                'default' => api_get_setting('allow_public_certificates') === 'true' ? 1 : '',
-                'category' => 'certificates',
-            ],
-            'documents_default_visibility' => ['default' => 'visible', 'category' => 'document'],
-            'show_course_in_user_language' => ['default' => 2, 'category' => null],
-            'email_to_teachers_on_new_work_feedback' => ['default' => 1, 'category' => null],
-        ];
-
-        $counter = 1;
-        foreach ($settings as $variable => $setting) {
-            $title = isset($setting['title']) ? $setting['title'] : '';
-            Database::query(
-                "INSERT INTO $TABLESETTING (id, c_id, title, variable, value, category)
-                 VALUES ($counter, $course_id, '".$title."', '".$variable."', '".$setting['default']."', '".$setting['category']."')"
-            );
-            $counter++;
-        }
-
-        /* Course homepage tools for platform admin only */
-
         /* Group tool */
         Database::insert(
             $TABLEGROUPCATEGORIES,

From 18f433b740447d783e0c9a0733b7cde30bdf0e51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Wed, 15 Jul 2020 15:45:28 +0200
Subject: [PATCH 11/16] api_get_*_entity instead of EntityRepository::find -
 refs BT#17453

---
 main/admin/course_edit.php                    |  2 +-
 main/auth/profile.php                         |  5 +--
 main/course_home/course_home.php              |  4 +-
 main/cron/import_csv.php                      |  2 +-
 .../resend_email_with_new_password.php        |  3 +-
 main/inc/ajax/skill.ajax.php                  |  4 +-
 main/inc/lib/add_course.lib.inc.php           |  2 +-
 main/inc/lib/api.lib.php                      | 43 ++++++++++++++++---
 main/inc/lib/course.lib.php                   | 10 +----
 main/inc/lib/usermanager.lib.php              |  9 ++--
 main/inc/lib/webservices/Rest.php             |  5 +--
 main/lp/learnpath.class.php                   | 12 +++---
 main/lp/lp_subscribe_users.php                |  6 +--
 main/lp/lp_subscribe_users_to_category.php    |  2 +-
 main/lp/lp_view.php                           |  2 +-
 main/mySpace/lp_tracking.php                  |  2 +-
 main/session/resume_session.php               |  5 +--
 .../api/tests/CreateLearningPathTest.php      |  7 ++-
 .../api/tests/CreateSessionFromModelTest.php  |  3 +-
 main/webservices/registration.soap.php        | 28 +++++-------
 plugin/buycourses/src/service_process.php     |  9 ++--
 plugin/oauth2/src/OAuth2.php                  |  5 +--
 src/Chamilo/CoreBundle/Entity/Course.php      | 10 +----
 .../Repository/ItemPropertyRepository.php     |  3 +-
 .../Entity/Repository/SequenceRepository.php  | 10 +----
 .../Repository/SequenceResourceRepository.php | 12 ++----
 src/Chamilo/CoreBundle/Entity/Session.php     | 20 +--------
 .../CourseBundle/Entity/CCourseSetting.php    |  2 +-
 src/Chamilo/CourseBundle/Entity/CDocument.php |  4 +-
 .../CourseBundle/Entity/CForumForum.php       |  2 +-
 src/Chamilo/CourseBundle/Entity/CLink.php     |  2 +-
 src/Chamilo/CourseBundle/Entity/CLp.php       |  4 +-
 .../CourseBundle/Entity/CLpCategory.php       |  2 +-
 src/Chamilo/CourseBundle/Entity/CLpItem.php   |  2 +-
 src/Chamilo/CourseBundle/Entity/CQuiz.php     |  2 +-
 src/Chamilo/CourseBundle/Entity/CTool.php     |  2 +-
 src/Chamilo/UserBundle/Entity/User.php        |  2 +-
 37 files changed, 107 insertions(+), 142 deletions(-)

diff --git a/main/admin/course_edit.php b/main/admin/course_edit.php
index 49eecb54946..5be43554488 100755
--- a/main/admin/course_edit.php
+++ b/main/admin/course_edit.php
@@ -186,7 +186,7 @@
 $courseTeacherNames = [];
 foreach ($course_teachers as $courseTeacherId) {
     /** @var User $courseTeacher */
-    $courseTeacher = UserManager::getRepository()->find($courseTeacherId);
+    $courseTeacher = api_get_user_entity($courseTeacherId);
     $courseTeacherNames[$courseTeacher->getId()] = UserManager::formatUserFullName($courseTeacher, true);
 }
 
diff --git a/main/auth/profile.php b/main/auth/profile.php
index 23e6e29811d..9c3b57f78e2 100755
--- a/main/auth/profile.php
+++ b/main/auth/profile.php
@@ -400,8 +400,7 @@ function show_image(image,width,height) {
 
     $wrong_current_password = false;
     $user_data = $form->getSubmitValues(1);
-    /** @var User $user */
-    $user = UserManager::getRepository()->find(api_get_user_id());
+    $user = api_get_user_entity();
 
     // set password if a new one was provided
     $validPassword = false;
@@ -664,7 +663,7 @@ function show_image(image,width,height) {
 
     if ($hook) {
         Database::getManager()->clear(User::class); // Avoid cache issue (user entity is used before)
-        $user = api_get_user_entity(api_get_user_id()); // Get updated user info for hook event
+        $user = api_get_user_entity(); // Get updated user info for hook event
         $hook->setEventData(['user' => $user]);
         $hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
     }
diff --git a/main/course_home/course_home.php b/main/course_home/course_home.php
index 1809c13cddc..485171c9d20 100755
--- a/main/course_home/course_home.php
+++ b/main/course_home/course_home.php
@@ -241,7 +241,7 @@ function buttonForAllShowHide()
                 /**
                  * @var Chamilo\UserBundle\Entity\User
                  */
-                $user = UserManager::getRepository()->find(api_get_user_id());
+                $user = api_get_user_entity();
                 if ($user) {
                     foreach ($user->getCurrentlyAccessibleSessions() as $session) {
                         $redirectionTarget = api_get_self().'?id_session='.$session->getId();
@@ -253,7 +253,7 @@ function buttonForAllShowHide()
             /**
              * @var Chamilo\UserBundle\Entity\User
              */
-            $user = UserManager::getRepository()->find(api_get_user_id());
+            $user = api_get_user_entity();
             if ($user && !$user->getCurrentlyAccessibleSessions()) {
                 // subscription was probably refused because user session expired, go back to page "about"
                 $redirectionTarget = api_get_path(WEB_PATH).'course/'.$courseId.'/about';
diff --git a/main/cron/import_csv.php b/main/cron/import_csv.php
index eece6915bbf..f09fe064d84 100755
--- a/main/cron/import_csv.php
+++ b/main/cron/import_csv.php
@@ -1286,7 +1286,7 @@ private function importCalendarStatic($file, $moveFile = true)
                         ];
                         /** @var CItemProperty $itemProperty */
                         $itemProperty = $em->getRepository('ChamiloCourseBundle:CItemProperty')->findOneBy($criteria);
-                        $courseEntity = $em->getRepository('ChamiloCoreBundle:Course')->find($courseInfo['real_id']);
+                        $courseEntity = api_get_course_entity($courseInfo['real_id']);
                         if ($itemProperty && $courseEntity) {
                             $itemProperty->setCourse($courseEntity);
                             $em->persist($itemProperty);
diff --git a/main/cron/user_import/resend_email_with_new_password.php b/main/cron/user_import/resend_email_with_new_password.php
index 8854b17fa48..791681e7e8c 100755
--- a/main/cron/user_import/resend_email_with_new_password.php
+++ b/main/cron/user_import/resend_email_with_new_password.php
@@ -44,8 +44,7 @@
         $pass = api_substr($row['username'], 0, 4).rand(0, 9).rand(0, 9);
 
         if ($user) {
-            /** @var User $user */
-            $user = $repository->find($row['user_id']);
+            $user = api_get_user_entity($row['user_id']);
             $user->setPlainPassword($pass);
             $userManager->updateUser($user, true);
         } else {
diff --git a/main/inc/ajax/skill.ajax.php b/main/inc/ajax/skill.ajax.php
index 0e672456c7c..610ef01fa3d 100755
--- a/main/inc/ajax/skill.ajax.php
+++ b/main/inc/ajax/skill.ajax.php
@@ -420,7 +420,7 @@
                 exit;
             }
 
-            $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId);
+            $session = api_get_session_entity($sessionId);
             /** @var \Chamilo\SkillBundle\Entity\SkillRelItem $skillRelItem */
             $skillRelItem = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findOneBy(
                 ['itemId' => $itemId, 'itemType' => $typeId, 'skill' => $skillId]
@@ -498,7 +498,7 @@
             $course = api_get_course_entity($courseId);
             $skillUser->setCourse($course);
             if (!empty($sessionId)) {
-                $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId);
+                $session = api_get_session_entity($sessionId);
                 $skillUser->setSession($session);
             }
 
diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index 80ed353e5c2..bdad9755340 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -956,7 +956,7 @@ public static function register_course($params, $accessUrlId = 1)
         }
 
         // Adding the course to an URL.
-        $url = AccessUrl::getRepository()->find($accessUrlId);
+        $url = api_get_access_url_entity($accessUrlId);
         if (!is_null($url)) {
             $urlRelCourse = (new AccessUrlRelCourse())
                 ->setCourse($course)
diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php
index 8c88ae6cc5e..f6618a0ba3d 100644
--- a/main/inc/lib/api.lib.php
+++ b/main/inc/lib/api.lib.php
@@ -1,10 +1,12 @@
 <?php
 /* For licensing terms, see /license.txt */
 
+use Chamilo\CoreBundle\Entity\AccessUrl;
 use Chamilo\CoreBundle\Entity\SettingsCurrent;
 use Chamilo\CoreBundle\Entity\UserCourseCategory;
 use Chamilo\CourseBundle\Entity\CGroupInfo;
 use Chamilo\CourseBundle\Entity\CItemProperty;
+use Chamilo\CourseBundle\Entity\CLp;
 use Chamilo\UserBundle\Entity\User;
 use ChamiloSession as Session;
 use Doctrine\Common\Collections\Criteria;
@@ -1795,9 +1797,9 @@ function api_get_user_info(
  *
  * @return User
  */
-function api_get_user_entity($userId)
+function api_get_user_entity($userId = 0)
 {
-    $userId = (int) $userId;
+    $userId = (int) $userId ?: api_get_user_id();
     $repo = UserManager::getRepository();
 
     /** @var User $user */
@@ -2214,6 +2216,16 @@ function api_get_session_entity($id = 0)
     return Database::getManager()->getRepository('ChamiloCoreBundle:Session')->find($id);
 }
 
+/**
+ * @param int $id the learning path identifier
+ *
+ * @return CLp|null
+ */
+function api_get_lp_entity($id)
+{
+    return Database::getManager()->getRepository('ChamiloCourseBundle:CLp')->find($id);
+}
+
 /**
  * Returns the current course info array.
 
@@ -2614,6 +2626,16 @@ function api_get_group_id()
     return Session::read('_gid', 0);
 }
 
+/**
+ * @param int $id the group identifier
+ *
+ * @return CGroupInfo|object|null
+ */
+function api_get_group_entity($id = 0)
+{
+    return Database::getManager()->getRepository('ChamiloCourseBundle:CGroupInfo')->find($id ?: api_get_group_id());
+}
+
 /**
  * Gets the current or given session name.
  *
@@ -4199,15 +4221,15 @@ function api_item_property_update(
     if (!array_key_exists('real_id', $_course)) {
         return false;
     }
-    $course = \Chamilo\CoreBundle\Entity\Course::getRepository()->find($_course['real_id']);
+    $course = api_get_course_entity($_course['real_id']);
     if (is_null($course)) {
         return false;
     }
 
     $toUser = api_get_user_entity($to_user_id);
-    $toGroup = array_key_exists('iid', $groupInfo) ? CGroupInfo::getRepository()->find($groupInfo['iid']) : null;
+    $toGroup = array_key_exists('iid', $groupInfo) ? api_get_group_entity($groupInfo['iid']) : null;
     $user = api_get_user_entity($user_id) ?: api_get_user_entity(api_get_anonymous_id());
-    $session = \Chamilo\CoreBundle\Entity\Session::getRepository()->find($session_id ?: api_get_session_id());
+    $session = api_get_session_entity($session_id);
 
     $startVisibleDate = empty($start_visible)
         ? null
@@ -6387,6 +6409,17 @@ function api_get_access_url_from_user($user_id)
     return $list;
 }
 
+/**
+ * @param int $id the access url identifier
+ *
+ * @return AccessUrl|null
+ */
+function api_get_access_url_entity($id = 0) {
+    return Database::getManager()->getRepository('ChamiloCoreBundle:AccessUrl')->find(
+        $id ?: api_get_current_access_url_id()
+    );
+}
+
 /**
  * Gets the status of a user in a course.
  *
diff --git a/main/inc/lib/course.lib.php b/main/inc/lib/course.lib.php
index 7775c34ee42..965e1a1cc0f 100755
--- a/main/inc/lib/course.lib.php
+++ b/main/inc/lib/course.lib.php
@@ -565,10 +565,7 @@ public static function autoSubscribeToCourse($courseCode, $status = STUDENT)
         $userId = api_get_user_id();
 
         if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
-            /**
-             * @var Chamilo\UserBundle\Entity\User
-             */
-            $user = UserManager::getRepository()->find($userId);
+            $user = api_get_user_entity($userId);
             $sessions = $user->getCurrentlyAccessibleSessions();
             if (empty($sessions)) {
                 // user has no accessible session
@@ -1196,10 +1193,7 @@ public static function is_user_subscribed_in_course(
             if (is_null($course)) {
                 return false;
             }
-            /**
-             * @var \Chamilo\UserBundle\Entity\User
-             */
-            $user = UserManager::getRepository()->find($user_id);
+            $user = api_get_user_entity($user_id);
             if (is_null($user)) {
                 return false;
             }
diff --git a/main/inc/lib/usermanager.lib.php b/main/inc/lib/usermanager.lib.php
index ebc4c52f0c4..b9f3a6ec802 100755
--- a/main/inc/lib/usermanager.lib.php
+++ b/main/inc/lib/usermanager.lib.php
@@ -146,9 +146,7 @@ public static function encryptPassword($raw, User $user)
      */
     public static function updatePassword($userId, $password)
     {
-        $repository = self::getRepository();
-        /** @var User $user */
-        $user = $repository->find($userId);
+        $user = api_get_user_entity($userId);
         $userManager = self::getManager();
         $user->setPlainPassword($password);
         $userManager->updateUser($user, true);
@@ -714,7 +712,7 @@ public static function casUserLoginName($casUser)
                 // (extra field values of a deleted user might remain)
                 foreach ($itemList as $item) {
                     $userId = intval($item['item_id']);
-                    $user = UserManager::getRepository()->find($userId);
+                    $user = api_get_user_entity($userId);
                     if (!is_null($user)) {
                         if (false === $loginName) {
                             $loginName = $user->getUsername();
@@ -1366,8 +1364,7 @@ public static function update_user(
         }
 
         $userManager = self::getManager();
-        /** @var User $user */
-        $user = self::getRepository()->find($user_id);
+        $user = api_get_user_entity($user_id);
 
         if (empty($user)) {
             return false;
diff --git a/main/inc/lib/webservices/Rest.php b/main/inc/lib/webservices/Rest.php
index 8c389fcf31b..97cbd04fa29 100644
--- a/main/inc/lib/webservices/Rest.php
+++ b/main/inc/lib/webservices/Rest.php
@@ -1805,8 +1805,7 @@ public function updateUserFromUserName($parameters)
         if (is_null($userId)) {
             throw new Exception(get_lang('NoData'));
         }
-        /** @var User $user */
-        $user = UserManager::getRepository()->find($userId);
+        $user = api_get_user_entity($userId);
         if (empty($user)) {
             throw new Exception(get_lang('CouldNotLoadUser'));
         }
@@ -2084,7 +2083,7 @@ public function createLearningPath(array $spec)
             if (0 == $sessionId) {
                 $learningPath->setSessionId(0);
             } else {
-                $session = Session::getRepository()->find($sessionId);
+                $session = api_get_session_entity($sessionId);
                 if (is_null($session)) {
                     throw new Exception("no session has id '$sessionId'");
                 }
diff --git a/main/lp/learnpath.class.php b/main/lp/learnpath.class.php
index c67e304f1f0..205bfd3fb7b 100755
--- a/main/lp/learnpath.class.php
+++ b/main/lp/learnpath.class.php
@@ -508,8 +508,8 @@ public function add_item(
         $previous = (int) $previous;
         $id = (int) $id;
         $max_time_allowed = htmlentities($max_time_allowed);
-        $course = \Chamilo\CoreBundle\Entity\Course::getRepository()->find($course_id);
-        $learningPath = CLp::getRepository()->find($this->get_id());
+        $course = api_get_course_entity($course_id);
+        $learningPath = api_get_lp_entity($this->get_id());
         $item = (new CLpItem())
             ->setCourse($course)
             ->setLearningPath($learningPath)
@@ -628,7 +628,7 @@ public static function add_lp(
         if ('zip' !== $origin) {
             $course_id = api_get_course_int_id($courseCode);
             if ($course_id) {
-                $course = \Chamilo\CoreBundle\Entity\Course::getRepository()->find($course_id);
+                $course = api_get_course_entity($course_id);
                 if (!is_null($course)) {
                     $learningPath = (new CLp())
                         ->setCourse($course)
@@ -4423,14 +4423,12 @@ public static function categoryIsVisibleForStudent(
         /** @var ItemPropertyRepository $itemRepo */
         $itemRepo = $em->getRepository('ChamiloCourseBundle:CItemProperty');
 
-        /** @var CourseRepository $courseRepo */
-        $courseRepo = $em->getRepository('ChamiloCoreBundle:Course');
         $session = null;
         if (!empty($sessionId)) {
-            $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId);
+            $session = api_get_session_entity($sessionId);
         }
 
-        $course = $courseRepo->find($courseId);
+        $course = api_get_course_entity($courseId);
 
         if ($courseId != 0) {
             // Subscribed groups to a LP
diff --git a/main/lp/lp_subscribe_users.php b/main/lp/lp_subscribe_users.php
index ccd9a31b996..577d59a39ee 100644
--- a/main/lp/lp_subscribe_users.php
+++ b/main/lp/lp_subscribe_users.php
@@ -57,10 +57,10 @@
 /** @var Session $session */
 $session = null;
 if (!empty($sessionId)) {
-    $session = $em->getRepository('ChamiloCoreBundle:Session')->find($sessionId);
+    $session = api_get_session_entity($sessionId);
 }
 
-$course = $courseRepo->find($courseId);
+$course = api_get_course_entity($courseId);
 $subscribedUsers = [];
 
 // Getting subscribe users to the course.
@@ -159,7 +159,7 @@
 }
 $form->setDefaults($defaults);
 
-$currentUser = api_get_user_entity(api_get_user_id());
+$currentUser = api_get_user_entity();
 
 if ($form->validate()) {
     $values = $form->getSubmitValues();
diff --git a/main/lp/lp_subscribe_users_to_category.php b/main/lp/lp_subscribe_users_to_category.php
index 6932863d2a0..7ed68ce9c1f 100644
--- a/main/lp/lp_subscribe_users_to_category.php
+++ b/main/lp/lp_subscribe_users_to_category.php
@@ -176,7 +176,7 @@
 
         foreach ($users as $userId) {
             $categoryUser = new CLpCategoryUser();
-            $user = UserManager::getRepository()->find($userId);
+            $user = api_get_user_entity($userId);
             if ($user) {
                 $categoryUser->setUser($user);
                 $category->addUser($categoryUser);
diff --git a/main/lp/lp_view.php b/main/lp/lp_view.php
index 5987a847080..d314beb9887 100755
--- a/main/lp/lp_view.php
+++ b/main/lp/lp_view.php
@@ -86,7 +86,7 @@
         $category = $em->getRepository('ChamiloCourseBundle:CLpCategory')->find($categoryId);
         $block = false;
         if ($category) {
-            $user = UserManager::getRepository()->find($user_id);
+            $user = api_get_user_entity($user_id);
             $users = $category->getUsers();
             if (!empty($users) && $users->count() > 0) {
                 if ($user && !$category->hasUserAdded($user)) {
diff --git a/main/mySpace/lp_tracking.php b/main/mySpace/lp_tracking.php
index 25496e3ba67..ba134909e2c 100755
--- a/main/mySpace/lp_tracking.php
+++ b/main/mySpace/lp_tracking.php
@@ -99,7 +99,7 @@
         }
 
         $view = $em->getRepository('ChamiloCourseBundle:CLpView')->find($itemView->getLpViewId());
-        $lp = $em->getRepository('ChamiloCourseBundle:CLp')->find($view->getLpId());
+        $lp = api_get_lp_entity($view->getLpId());
 
         $duration = learnpathItem::getScormTimeFromParameter('js', $itemView->getTotalTime());
         $endTime = $itemView->getStartTime() + $itemView->getTotalTime();
diff --git a/main/session/resume_session.php b/main/session/resume_session.php
index 913b1a8dddf..278a3e74430 100644
--- a/main/session/resume_session.php
+++ b/main/session/resume_session.php
@@ -48,10 +48,7 @@
 
 $em = Database::getManager();
 $sessionInfo = api_get_session_info($sessionId);
-/** @var SessionRepository $sessionRepository */
-$sessionRepository = $em->getRepository('ChamiloCoreBundle:Session');
-/** @var Session $session */
-$session = $sessionRepository->find($sessionId);
+$session = api_get_session_entity($sessionId);
 $sessionCategory = $session->getCategory();
 
 $action = isset($_GET['action']) ? $_GET['action'] : null;
diff --git a/main/webservices/api/tests/CreateLearningPathTest.php b/main/webservices/api/tests/CreateLearningPathTest.php
index af59401fa81..ced01427e28 100644
--- a/main/webservices/api/tests/CreateLearningPathTest.php
+++ b/main/webservices/api/tests/CreateLearningPathTest.php
@@ -148,7 +148,7 @@ public function testCreateEmptyLearningPathWithoutSessionNorCategory()
 
         // assert the learning path was created
         /** @var CLp $learningPath */
-        $learningPath = CLp::getRepository()->find($learningPathId);
+        $learningPath = api_get_lp_entity($learningPathId);
 
         self::assertNotNull($learningPath);
         // in the right course
@@ -182,8 +182,7 @@ public function testCreateEmptyLearningPath()
         );
 
         // assert the learning path was created
-        /** @var CLp $learningPath */
-        $learningPath = CLp::getRepository()->find($learningPathId);
+        $learningPath = api_get_lp_entity($learningPathId);
 
         self::assertNotNull($learningPath);
         // in the right session, course and category
@@ -300,7 +299,7 @@ public function testCreateLearningPathWithItems()
 
         // assert the learning path was created as specified
         /** @var CLp $learningPath */
-        $learningPath = CLp::getRepository()->find($learningPathId);
+        $learningPath = api_get_lp_entity($learningPathId);
 
         self::assertNotNull($learningPath);
         self::assertEquals(self::$session->getId(), $learningPath->getSessionId());
diff --git a/main/webservices/api/tests/CreateSessionFromModelTest.php b/main/webservices/api/tests/CreateSessionFromModelTest.php
index 967d52ff07f..45e46e50816 100644
--- a/main/webservices/api/tests/CreateSessionFromModelTest.php
+++ b/main/webservices/api/tests/CreateSessionFromModelTest.php
@@ -53,8 +53,7 @@ public function testFromASimpleModel()
 
         // assert the session was created and given the returned session id
         $entityManager = Database::getManager();
-        $repository = $entityManager->getRepository('ChamiloCoreBundle:Session');
-        $newSession = $repository->find($newSessionId);
+        $newSession = api_get_session_entity($newSessionId);
         $this->assertIsObject($newSession);
 
         // assert the new session got the right data
diff --git a/main/webservices/registration.soap.php b/main/webservices/registration.soap.php
index 0c163d25162..a0ab1f2e7bf 100755
--- a/main/webservices/registration.soap.php
+++ b/main/webservices/registration.soap.php
@@ -304,8 +304,7 @@ function WSCreateUsers($params)
             $original_user_id_name
         );
         if ($user_id > 0) {
-            /** @var User $user */
-            $user = $userRepository->find($user_id);
+            $user = api_get_user_entity($user_id);
 
             if ($user && $user->isActive() == false) {
                 if (!is_null($password)) {
@@ -532,8 +531,7 @@ function WSCreateUser($params)
     $userRepository = UserManager::getRepository();
 
     if ($user_id > 0) {
-        /** @var User $user */
-        $user = $userRepository->find($user_id);
+        $user = api_get_user_entity($user_id);
         if ($user && $user->isActive() == false) {
             if (!is_null($password)) {
                 $user->setPlainPassword($password);
@@ -867,7 +865,7 @@ function WSCreateUsersPasswordCrypted($params)
         $count_row = Database::num_rows($res);
         if ($count_row > 0) {
             // Check if user is not active.
-            $sql = "SELECT user_id FROM $table_user 
+            $sql = "SELECT user_id FROM $table_user
                     WHERE user_id ='".$row[1]."' AND active= '0'";
             $resu = Database::query($sql);
             $r_check_user = Database::fetch_row($resu);
@@ -1389,7 +1387,7 @@ function WSCreateUserPasswordCrypted($params)
                     phone='".Database::escape_string($phone)."',
                     expiration_date='".Database::escape_string($expiration_date)."',
                     active='1',
-                    hr_dept_id=".intval($hr_dept_id)." 
+                    hr_dept_id=".intval($hr_dept_id)."
                 WHERE user_id='".$r_check_user[0]."'";
 
             Database::query($sql);
@@ -1459,7 +1457,7 @@ function WSCreateUserPasswordCrypted($params)
             phone               = '".Database::escape_string($phone)."',
             language            = '".Database::escape_string($language)."',
             registration_date   = '".api_get_utc_datetime()."',
-            roles = 'a:0:{}', 
+            roles = 'a:0:{}',
             ".$queryExpirationDate."
             hr_dept_id          = '".Database::escape_string($hr_dept_id)."',
             active              = '".Database::escape_string($active)."'";
@@ -1657,8 +1655,7 @@ function WSEditUserCredentials($params)
         return 0;
     }
 
-    /** @var User $user */
-    $user = $userRepository->find($user_id);
+    $user = api_get_user_entity($user_id);
     if ($user) {
         $user->setUsername($username);
         if (!is_null($password)) {
@@ -1787,8 +1784,7 @@ function WSEditUsers($params)
         }
         // Edit lastname and firstname only if not empty
 
-        /** @var User $user */
-        $user = $userRepository->find($user_id);
+        $user = api_get_user_entity($user_id);
 
         if (!empty($lastname)) {
             $user->setLastname($lastname);
@@ -1968,8 +1964,7 @@ function WSEditUser($params)
         return 0;
     }
 
-    /** @var User $user */
-    $user = $userRepository->find($user_id);
+    $user = api_get_user_entity($user_id);
 
     if (!empty($lastname)) {
         $user->setLastname($lastname);
@@ -2139,7 +2134,7 @@ function WSEditUserWithPicture($params)
     }
 
     // Check whether username already exits.
-    $sql = "SELECT username FROM $table_user 
+    $sql = "SELECT username FROM $table_user
             WHERE username = '$username' AND id <> $user_id";
     $res_un = Database::query($sql);
     $r_username = Database::fetch_row($res_un);
@@ -2148,8 +2143,7 @@ function WSEditUserWithPicture($params)
         return 0;
     }
 
-    /** @var User $user */
-    $user = $userRepository->find($user_id);
+    $user = api_get_user_entity($user_id);
 
     if (!empty($lastname)) {
         $user->setLastname($lastname);
@@ -4768,7 +4762,7 @@ function WSSubscribeUserToCourseSimple($params)
                 error_log('Try to register: user_id= '.$user_id.' to course: '.$course_data['code']);
             }
             if (!CourseManager::subscribeUser($user_id, $course_data['code'], $status, 0, false, false)) {
-                $result = 'User was not registered possible reasons: User already registered to the course, 
+                $result = 'User was not registered possible reasons: User already registered to the course,
                            Course visibility doesnt allow user subscriptions ';
                 if ($debug) {
                     error_log($result);
diff --git a/plugin/buycourses/src/service_process.php b/plugin/buycourses/src/service_process.php
index e22c8d5de02..27255a5ae1a 100644
--- a/plugin/buycourses/src/service_process.php
+++ b/plugin/buycourses/src/service_process.php
@@ -104,8 +104,7 @@
     }
     $form->addSelect('info_select', get_lang('User'), $selectOptions);
 } elseif ($typeCourse) {
-    /** @var User $user */
-    $user = UserManager::getRepository()->find($currentUserId);
+    $user = api_get_user_entity($currentUserId);
     $courses = $user->getCourses();
     $checker = false;
     foreach ($courses as $course) {
@@ -123,8 +122,7 @@
     $form->addSelect('info_select', get_lang('Course'), $selectOptions);
 } elseif ($typeSession) {
     $sessions = [];
-    /** @var User $user */
-    $user = UserManager::getRepository()->find($currentUserId);
+    $user = api_get_user_entity($currentUserId);
     $userSubscriptions = $user->getSessionCourseSubscriptions();
 
     /** @var SessionRelCourseRelUser $userSubscription */
@@ -146,8 +144,7 @@
     }
 } elseif ($typeFinalLp) {
     // We need here to check the current user courses first
-    /** @var User $user */
-    $user = UserManager::getRepository()->find($currentUserId);
+    $user = api_get_user_entity($currentUserId);
     $courses = $user->getCourses();
     $courseLpList = [];
     $sessionLpList = [];
diff --git a/plugin/oauth2/src/OAuth2.php b/plugin/oauth2/src/OAuth2.php
index 9439dc86f8d..7ae89cd81f6 100644
--- a/plugin/oauth2/src/OAuth2.php
+++ b/plugin/oauth2/src/OAuth2.php
@@ -294,10 +294,7 @@ private function getValuesByKey(array $data, $key, $default = [])
 
     private function updateUser($userId, $response)
     {
-        /**
-         * @var $user Chamilo\UserBundle\Entity\User
-         */
-        $user = UserManager::getRepository()->find($userId);
+        $user = api_get_user_entity($userId);
         $user->setFirstname(
             $this->getValueByKey($response, $this->get(
                 self::SETTING_RESPONSE_RESOURCE_OWNER_FIRSTNAME
diff --git a/src/Chamilo/CoreBundle/Entity/Course.php b/src/Chamilo/CoreBundle/Entity/Course.php
index 90f94530148..114fb57db04 100644
--- a/src/Chamilo/CoreBundle/Entity/Course.php
+++ b/src/Chamilo/CoreBundle/Entity/Course.php
@@ -458,14 +458,6 @@ public static function getRepository()
         return Database::getManager()->getRepository('ChamiloCoreBundle:Course');
     }
 
-    /**
-     * @return Course|null
-     */
-    public static function getCurrentCourse()
-    {
-        return self::getRepository()->find(api_get_course_int_id());
-    }
-
     /**
      * Sets sane values if still unset.
      * Makes directory if missing.
@@ -540,7 +532,7 @@ public function prePersist()
             $this->departmentUrl = 'https://'.$this->departmentUrl;
         }
         if ($this->accessUrls->isEmpty()) {
-            $this->accessUrls->add(AccessUrl::getRepository()->find(api_get_current_access_url_id()));
+            $this->accessUrls->add(api_get_access_url_entity());
         }
         $this->prepareRepository();
         $this->createTools();
diff --git a/src/Chamilo/CoreBundle/Entity/Repository/ItemPropertyRepository.php b/src/Chamilo/CoreBundle/Entity/Repository/ItemPropertyRepository.php
index 6e48e9f6a93..b8209ad01fb 100644
--- a/src/Chamilo/CoreBundle/Entity/Repository/ItemPropertyRepository.php
+++ b/src/Chamilo/CoreBundle/Entity/Repository/ItemPropertyRepository.php
@@ -213,7 +213,6 @@ public function subscribeUsersToItem(
         $newUserList = []
     ) {
         $em = $this->getEntityManager();
-        $user = $em->getRepository('ChamiloUserBundle:User');
 
         $usersSubscribedToItem = $this->getUsersSubscribedToItem(
             $tool,
@@ -248,7 +247,7 @@ public function subscribeUsersToItem(
 
         foreach ($newUserList as $userId) {
             if (!in_array($userId, $alreadyAddedUsers)) {
-                $userObj = $user->find($userId);
+                $userObj = api_get_user_entity($userId);
 
                 $item = new CItemProperty($course);
                 $item
diff --git a/src/Chamilo/CoreBundle/Entity/Repository/SequenceRepository.php b/src/Chamilo/CoreBundle/Entity/Repository/SequenceRepository.php
index f06a1b69954..8973f5bf471 100644
--- a/src/Chamilo/CoreBundle/Entity/Repository/SequenceRepository.php
+++ b/src/Chamilo/CoreBundle/Entity/Repository/SequenceRepository.php
@@ -53,19 +53,13 @@ public function getItem($itemId, $type)
         $resource = null;
         switch ($type) {
             case SequenceResource::COURSE_TYPE:
-                $repo = $this->getEntityManager()->getRepository('ChamiloCoreBundle:Course');
-
+                $resource = api_get_course_entity($itemId);
                 break;
             case SequenceResource::SESSION_TYPE:
-                $repo = $this->getEntityManager()->getRepository('ChamiloCoreBundle:Session');
-
+                $resource = api_get_session_entity($itemId);
                 break;
         }
 
-        if ($repo) {
-            $resource = $repo->find($itemId);
-        }
-
         return $resource;
     }
 
diff --git a/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php b/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php
index 894bddcfd62..1dbfd9b144a 100644
--- a/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php
+++ b/src/Chamilo/CoreBundle/Entity/Repository/SequenceResourceRepository.php
@@ -132,14 +132,10 @@ public function getRequirements($resourceId, $type)
                 $resource = null;
                 switch ($type) {
                     case SequenceResource::SESSION_TYPE:
-                        $repo = $em->getRepository('ChamiloCoreBundle:Session');
-                        $resource = $repo->find($vertexId);
-
+                        $resource = api_get_session_entity($vertexId);
                         break;
                     case SequenceResource::COURSE_TYPE:
-                        $repo = $em->getRepository('ChamiloCoreBundle:Course');
-                        $resource = $repo->find($vertexId);
-
+                        $resource = api_get_course_entity($vertexId);
                         break;
                 }
 
@@ -389,10 +385,10 @@ protected function findVerticesEdges(Vertices $verticesEdges, $type)
             $vertexId = $supVertex->getId();
             switch ($type) {
                 case SequenceResource::SESSION_TYPE:
-                    $resource = $em->getRepository('ChamiloCoreBundle:Session')->find($vertexId);
+                    $resource = api_get_session_entity($vertexId);
                     break;
                 case SequenceResource::COURSE_TYPE:
-                    $resource = $em->getRepository('ChamiloCoreBundle:Course')->find($vertexId);
+                    $resource = api_get_course_entity($vertexId);
                     break;
             }
 
diff --git a/src/Chamilo/CoreBundle/Entity/Session.php b/src/Chamilo/CoreBundle/Entity/Session.php
index 539ebd9800c..dd742cdf72c 100644
--- a/src/Chamilo/CoreBundle/Entity/Session.php
+++ b/src/Chamilo/CoreBundle/Entity/Session.php
@@ -294,19 +294,6 @@ public static function getRepository()
         return Database::getManager()->getRepository('ChamiloCoreBundle:Session');
     }
 
-    /**
-     * @return Session|null
-     */
-    public static function getCurrentSession()
-    {
-        $sessionId = api_get_session_id();
-        if (0 != $sessionId) {
-            return self::getRepository()->find($sessionId);
-        }
-
-        return null;
-    }
-
     /**
      * @return int
      */
@@ -479,12 +466,7 @@ public function hasCourse($course)
      */
     public function isRelatedToCourse($course)
     {
-        return !is_null(
-            Database::getManager()->getRepository('ChamiloCoreBundle:SessionRelCourse')->findOneBy([
-                'session' => $this,
-                'course' => $course,
-            ])
-        );
+        return $this->courses->contains($course);
     }
 
     /**
diff --git a/src/Chamilo/CourseBundle/Entity/CCourseSetting.php b/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
index 961b4aa32f5..126e14e50c2 100644
--- a/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
+++ b/src/Chamilo/CourseBundle/Entity/CCourseSetting.php
@@ -339,7 +339,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CDocument.php b/src/Chamilo/CourseBundle/Entity/CDocument.php
index 1630c9f2c0f..a657775a5f1 100644
--- a/src/Chamilo/CourseBundle/Entity/CDocument.php
+++ b/src/Chamilo/CourseBundle/Entity/CDocument.php
@@ -184,7 +184,7 @@ public function setCourse($course)
     public function getAbsolutePath()
     {
         if (is_null($this->course) && $this->cId) {
-            $this->course = Database::getManager()->find('ChamiloCoreBundle:Course', $this->cId);
+            $this->course = api_get_course_entity($this->cId);
         }
         if (is_null($this->course)) {
             throw new Exception('this document does not have a course yet');
@@ -475,7 +475,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CForumForum.php b/src/Chamilo/CourseBundle/Entity/CForumForum.php
index db4cc9d77d4..7ec55b4c5f3 100644
--- a/src/Chamilo/CourseBundle/Entity/CForumForum.php
+++ b/src/Chamilo/CourseBundle/Entity/CForumForum.php
@@ -773,7 +773,7 @@ public function getForumId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLink.php b/src/Chamilo/CourseBundle/Entity/CLink.php
index fbe3e782e4e..eb787696879 100644
--- a/src/Chamilo/CourseBundle/Entity/CLink.php
+++ b/src/Chamilo/CourseBundle/Entity/CLink.php
@@ -391,7 +391,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index 8a4ca375bce..3e75b303373 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -375,7 +375,7 @@ public static function getRepository()
     public function prePersist()
     {
         if (is_null($this->course)) {
-            $this->course = Course::getCurrentCourse();
+            $this->course = api_get_course_entity();
             if (is_null($this->course)) {
                 throw new Exception('cannot persist a leaning path without course');
             }
@@ -1218,7 +1218,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpCategory.php b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
index 4a25ee14a6c..a026451c68d 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpCategory.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpCategory.php
@@ -141,7 +141,7 @@ public function setCourse($course)
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CLpItem.php b/src/Chamilo/CourseBundle/Entity/CLpItem.php
index 2a0aa4b5a82..abb863b4f50 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpItem.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpItem.php
@@ -944,7 +944,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CQuiz.php b/src/Chamilo/CourseBundle/Entity/CQuiz.php
index 92fcf012e89..ce6019922d8 100644
--- a/src/Chamilo/CourseBundle/Entity/CQuiz.php
+++ b/src/Chamilo/CourseBundle/Entity/CQuiz.php
@@ -862,7 +862,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/CourseBundle/Entity/CTool.php b/src/Chamilo/CourseBundle/Entity/CTool.php
index 2dacbbd5f2d..600a99187a6 100644
--- a/src/Chamilo/CourseBundle/Entity/CTool.php
+++ b/src/Chamilo/CourseBundle/Entity/CTool.php
@@ -459,7 +459,7 @@ public function getId()
     public function setCId($cId)
     {
         $this->cId = $cId;
-        $this->setCourse(Course::getRepository()->find($cId));
+        $this->setCourse(api_get_course_entity($cId));
 
         return $this;
     }
diff --git a/src/Chamilo/UserBundle/Entity/User.php b/src/Chamilo/UserBundle/Entity/User.php
index af98989dbe7..17588c0a03d 100644
--- a/src/Chamilo/UserBundle/Entity/User.php
+++ b/src/Chamilo/UserBundle/Entity/User.php
@@ -2589,7 +2589,7 @@ public function getPictureLegacy()
     }
 
     /**
-     * Retreives this user's related sessions.
+     * Retrieves this user's related sessions.
      *
      * @param int $relationType \Chamilo\CoreBundle\Entity\SessionRelUser::relationTypeList key
      *

From 3980a1ae0ab39b84289de9fe0c3f2be0921f0131 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Wed, 15 Jul 2020 16:02:02 +0200
Subject: [PATCH 12/16] Minor code style fix (FlintCI) - refs BT#17453

---
 main/inc/lib/add_course.lib.inc.php | 1 -
 main/inc/lib/api.lib.php            | 3 ++-
 main/lp/learnpath.class.php         | 1 -
 main/session/resume_session.php     | 1 -
 4 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index bdad9755340..fb88a7b35db 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -1,7 +1,6 @@
 <?php
 /* For licensing terms, see /license.txt */
 
-use Chamilo\CoreBundle\Entity\AccessUrl;
 use Chamilo\CoreBundle\Entity\AccessUrlRelCourse;
 use Chamilo\CoreBundle\Entity\CourseRelUser;
 use Chamilo\CourseBundle\Entity\CToolIntro;
diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php
index f6618a0ba3d..95b06f007be 100644
--- a/main/inc/lib/api.lib.php
+++ b/main/inc/lib/api.lib.php
@@ -6414,7 +6414,8 @@ function api_get_access_url_from_user($user_id)
  *
  * @return AccessUrl|null
  */
-function api_get_access_url_entity($id = 0) {
+function api_get_access_url_entity($id = 0)
+{
     return Database::getManager()->getRepository('ChamiloCoreBundle:AccessUrl')->find(
         $id ?: api_get_current_access_url_id()
     );
diff --git a/main/lp/learnpath.class.php b/main/lp/learnpath.class.php
index 205bfd3fb7b..221d24229a0 100755
--- a/main/lp/learnpath.class.php
+++ b/main/lp/learnpath.class.php
@@ -2,7 +2,6 @@
 
 /* For licensing terms, see /license.txt */
 
-use Chamilo\CoreBundle\Entity\Repository\CourseRepository;
 use Chamilo\CoreBundle\Entity\Repository\ItemPropertyRepository;
 use Chamilo\CourseBundle\Component\CourseCopy\CourseArchiver;
 use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
diff --git a/main/session/resume_session.php b/main/session/resume_session.php
index 278a3e74430..8a709dc87ee 100644
--- a/main/session/resume_session.php
+++ b/main/session/resume_session.php
@@ -4,7 +4,6 @@
 
 use Chamilo\CoreBundle\Entity\Course;
 use Chamilo\CoreBundle\Entity\Repository\SequenceResourceRepository;
-use Chamilo\CoreBundle\Entity\Repository\SessionRepository;
 use Chamilo\CoreBundle\Entity\SequenceResource;
 use Chamilo\CoreBundle\Entity\Session;
 use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;

From 90d6cb05d3fbe7b5f1e0432d9f686389e69d0ca5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Mon, 20 Jul 2020 10:18:49 +0200
Subject: [PATCH 13/16] Fixed resume_session.php bug introduced in commit
 18f433 - refs BT#17453

---
 main/session/resume_session.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/main/session/resume_session.php b/main/session/resume_session.php
index 8a709dc87ee..e26866ac213 100644
--- a/main/session/resume_session.php
+++ b/main/session/resume_session.php
@@ -161,6 +161,7 @@
 } else {
     $count = 0;
     $courseItem = '';
+    $sessionRepository = $em->getRepository('ChamiloCoreBundle:Session');
     $courses = $sessionRepository->getCoursesOrderedByPosition($session);
 
     $allowSkills = api_get_configuration_value('allow_skill_rel_items');

From b4b163deecf73f83e07bc6f2e1239a20b4b98968 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Thu, 30 Jul 2020 09:24:34 +0200
Subject: [PATCH 14/16] learning path removal does not cascade-remove its
 course - refs BT#17453

learning path item removal does not cascade-remove its learning path
AccessUrlRelCourse removal does not cascade-remove its course and url
CItemProperty removal does not cascade-remove its session
---
 src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php | 4 ++--
 src/Chamilo/CourseBundle/Entity/CItemProperty.php    | 2 +-
 src/Chamilo/CourseBundle/Entity/CLp.php              | 2 +-
 src/Chamilo/CourseBundle/Entity/CLpItem.php          | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php b/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
index e055259af73..7ba04f6d535 100644
--- a/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
+++ b/src/Chamilo/CoreBundle/Entity/AccessUrlRelCourse.php
@@ -26,7 +26,7 @@ class AccessUrlRelCourse
      * @ORM\ManyToOne(
      *     targetEntity="Chamilo\CoreBundle\Entity\Course",
      *     inversedBy="urls",
-     *     cascade={"persist", "remove"}
+     *     cascade={"persist"}
      * )
      * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
      */
@@ -36,7 +36,7 @@ class AccessUrlRelCourse
      * @ORM\ManyToOne(
      *     targetEntity="Chamilo\CoreBundle\Entity\AccessUrl",
      *     inversedBy="courses",
-     *     cascade={"persist", "remove"}
+     *     cascade={"persist"}
      * )
      * @ORM\JoinColumn(name="access_url_id", referencedColumnName="id")
      */
diff --git a/src/Chamilo/CourseBundle/Entity/CItemProperty.php b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
index b5b1f81bcf1..d5ca5113cae 100644
--- a/src/Chamilo/CourseBundle/Entity/CItemProperty.php
+++ b/src/Chamilo/CourseBundle/Entity/CItemProperty.php
@@ -76,7 +76,7 @@ class CItemProperty
      * @ORM\ManyToOne(
      *     targetEntity="Chamilo\CoreBundle\Entity\Session",
      *     inversedBy="itemProperties",
-     *     cascade={"persist", "remove"}
+     *     cascade={"persist"}
      * )
      * @ORM\JoinColumn(name="session_id", referencedColumnName="id", nullable=true)
      */
diff --git a/src/Chamilo/CourseBundle/Entity/CLp.php b/src/Chamilo/CourseBundle/Entity/CLp.php
index 3e75b303373..25abde7e1b3 100644
--- a/src/Chamilo/CourseBundle/Entity/CLp.php
+++ b/src/Chamilo/CourseBundle/Entity/CLp.php
@@ -281,7 +281,7 @@ class CLp
      * @ORM\ManyToOne(
      *     targetEntity="Chamilo\CoreBundle\Entity\Course",
      *     inversedBy="learningPaths",
-     *     cascade={"persist", "remove"}
+     *     cascade={"persist"}
      * )
      * @ORM\JoinColumn(name="c_id", referencedColumnName="id")
      */
diff --git a/src/Chamilo/CourseBundle/Entity/CLpItem.php b/src/Chamilo/CourseBundle/Entity/CLpItem.php
index abb863b4f50..a23be25fa30 100644
--- a/src/Chamilo/CourseBundle/Entity/CLpItem.php
+++ b/src/Chamilo/CourseBundle/Entity/CLpItem.php
@@ -209,7 +209,7 @@ class CLpItem
      * @ORM\ManyToOne(
      *     targetEntity="Chamilo\CourseBundle\Entity\CLp",
      *     inversedBy="items",
-     *     cascade={"persist", "remove"}
+     *     cascade={"persist"}
      * )
      * @ORM\JoinColumn(
      *     name="lp_id",

From eaa8cf33cde351b2dc65902ea9fee97acf7466c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Mon, 17 Aug 2020 12:05:27 +0200
Subject: [PATCH 15/16] prevent AddCourse::register_course() from creating an
 extra AccessUrlRelCourse

---
 main/inc/lib/add_course.lib.inc.php | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index fb88a7b35db..b8526729d64 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -957,10 +957,13 @@ public static function register_course($params, $accessUrlId = 1)
         // Adding the course to an URL.
         $url = api_get_access_url_entity($accessUrlId);
         if (!is_null($url)) {
-            $urlRelCourse = (new AccessUrlRelCourse())
-                ->setCourse($course)
-                ->setUrl($url);
-            Database::getManager()->persist($urlRelCourse);
+            // Course::PrePersist() has already created the AccessUrlRelCourse, but with the default AccessUrl
+            foreach ($course->getUrls() as $urlRelCourse) {
+                if ($urlRelCourse->getUrl() != $url) {
+                    $urlRelCourse->setUrl($url);
+                    Database::getManager()->persist($urlRelCourse);
+                }
+            }
         }
 
         try {

From 57a6d3e51198abcd33ab0c7b344640993adbb805 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Ducoulombier?= <seb@ldd.fr>
Date: Mon, 17 Aug 2020 13:02:16 +0200
Subject: [PATCH 16/16] Prevent AddCourse::register_course() from creating a
 wrong AccessUrlRelCourse - refs BT#17507

---
 main/inc/lib/add_course.lib.inc.php      | 18 ++++++------------
 src/Chamilo/CoreBundle/Entity/Course.php |  8 ++++++++
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index b8526729d64..9d8538374c0 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -907,6 +907,12 @@ public static function register_course($params, $accessUrlId = 1)
             ->setUnsubscribe($unsubscribe)
             ->setVisualCode($visualCode)
         ;
+
+        $url = api_get_access_url_entity($accessUrlId);
+        if (!is_null($url)) {
+            $course->getAccessUrls()->add($url);
+        }
+
         Database::getManager()->persist($course);
 
         $addTeacher = isset($params['add_user_as_teacher']) ? $params['add_user_as_teacher'] : true;
@@ -954,18 +960,6 @@ public static function register_course($params, $accessUrlId = 1)
             }
         }
 
-        // Adding the course to an URL.
-        $url = api_get_access_url_entity($accessUrlId);
-        if (!is_null($url)) {
-            // Course::PrePersist() has already created the AccessUrlRelCourse, but with the default AccessUrl
-            foreach ($course->getUrls() as $urlRelCourse) {
-                if ($urlRelCourse->getUrl() != $url) {
-                    $urlRelCourse->setUrl($url);
-                    Database::getManager()->persist($urlRelCourse);
-                }
-            }
-        }
-
         try {
             Database::getManager()->flush();
         } catch (OptimisticLockException $exception) {
diff --git a/src/Chamilo/CoreBundle/Entity/Course.php b/src/Chamilo/CoreBundle/Entity/Course.php
index 114fb57db04..6ffbf92cf44 100644
--- a/src/Chamilo/CoreBundle/Entity/Course.php
+++ b/src/Chamilo/CoreBundle/Entity/Course.php
@@ -1546,6 +1546,14 @@ public function getSettings()
         return $this->settings;
     }
 
+    /**
+     * @return AccessUrl[]|ArrayCollection
+     */
+    public function getAccessUrls()
+    {
+        return $this->accessUrls;
+    }
+
     /**
      * @param CourseRelUser $subscription
      *