category.class.php 84 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Chamilo\CoreBundle\Entity\GradebookCategory;
  4. use ChamiloSession as Session;
  5. /**
  6. * Class Category
  7. * Defines a gradebook Category object.
  8. *
  9. * @package chamilo.gradebook
  10. */
  11. class Category implements GradebookItem
  12. {
  13. public $studentList;
  14. public $evaluations;
  15. public $links;
  16. public $subCategories;
  17. /** @var GradebookCategory */
  18. public $entity;
  19. private $id;
  20. private $name;
  21. private $description;
  22. private $user_id;
  23. private $course_code;
  24. private $parent;
  25. private $weight;
  26. private $visible;
  27. private $certificate_min_score;
  28. private $session_id;
  29. private $skills = [];
  30. private $grade_model_id;
  31. private $generateCertificates;
  32. private $isRequirement;
  33. private $courseDependency;
  34. private $minimumToValidate;
  35. private $documentId;
  36. /** @var int */
  37. private $gradeBooksToValidateInDependence;
  38. /**
  39. * Consctructor.
  40. */
  41. public function __construct()
  42. {
  43. $this->id = 0;
  44. $this->name = null;
  45. $this->description = null;
  46. $this->user_id = 0;
  47. $this->course_code = null;
  48. $this->parent = 0;
  49. $this->weight = 0;
  50. $this->visible = false;
  51. $this->certificate_min_score = 0;
  52. $this->session_id = 0;
  53. $this->grade_model_id = 0;
  54. $this->generateCertificates = false;
  55. $this->isRequirement = false;
  56. $this->courseDependency = [];
  57. $this->documentId = 0;
  58. $this->minimumToValidate = null;
  59. }
  60. /**
  61. * @return int
  62. */
  63. public function get_id()
  64. {
  65. return $this->id;
  66. }
  67. /**
  68. * @return string
  69. */
  70. public function get_name()
  71. {
  72. return $this->name;
  73. }
  74. /**
  75. * @return string
  76. */
  77. public function get_description()
  78. {
  79. return $this->description;
  80. }
  81. /**
  82. * @return int
  83. */
  84. public function get_user_id()
  85. {
  86. return $this->user_id;
  87. }
  88. /**
  89. * @return int|null
  90. */
  91. public function getCertificateMinScore()
  92. {
  93. if (!empty($this->certificate_min_score)) {
  94. return $this->certificate_min_score;
  95. }
  96. return null;
  97. }
  98. /**
  99. * @return string
  100. */
  101. public function get_course_code()
  102. {
  103. return $this->course_code;
  104. }
  105. /**
  106. * @return int
  107. */
  108. public function get_parent_id()
  109. {
  110. return $this->parent;
  111. }
  112. /**
  113. * @return int
  114. */
  115. public function get_weight()
  116. {
  117. return $this->weight;
  118. }
  119. /**
  120. * @return bool
  121. */
  122. public function is_locked()
  123. {
  124. return isset($this->locked) && $this->locked == 1 ? true : false;
  125. }
  126. /**
  127. * @return bool
  128. */
  129. public function is_visible()
  130. {
  131. return $this->visible;
  132. }
  133. /**
  134. * Get $isRequirement.
  135. *
  136. * @return int
  137. */
  138. public function getIsRequirement()
  139. {
  140. return $this->isRequirement;
  141. }
  142. /**
  143. * @param int $id
  144. */
  145. public function set_id($id)
  146. {
  147. $this->id = $id;
  148. }
  149. /**
  150. * @param string $name
  151. */
  152. public function set_name($name)
  153. {
  154. $this->name = $name;
  155. }
  156. /**
  157. * @param string $description
  158. */
  159. public function set_description($description)
  160. {
  161. $this->description = $description;
  162. }
  163. /**
  164. * @param int $user_id
  165. */
  166. public function set_user_id($user_id)
  167. {
  168. $this->user_id = $user_id;
  169. }
  170. /**
  171. * @param string $course_code
  172. */
  173. public function set_course_code($course_code)
  174. {
  175. $this->course_code = $course_code;
  176. }
  177. /**
  178. * @param float $min_score
  179. */
  180. public function set_certificate_min_score($min_score = null)
  181. {
  182. $this->certificate_min_score = $min_score;
  183. }
  184. /**
  185. * @param int $parent
  186. */
  187. public function set_parent_id($parent)
  188. {
  189. $this->parent = (int) $parent;
  190. }
  191. /**
  192. * Filters to int and sets the session ID.
  193. *
  194. * @param int The session ID from the Dokeos course session
  195. */
  196. public function set_session_id($session_id = 0)
  197. {
  198. $this->session_id = (int) $session_id;
  199. }
  200. /**
  201. * @param $weight
  202. */
  203. public function set_weight($weight)
  204. {
  205. $this->weight = $weight;
  206. }
  207. /**
  208. * @param $visible
  209. */
  210. public function set_visible($visible)
  211. {
  212. $this->visible = $visible;
  213. }
  214. /**
  215. * @param int $id
  216. */
  217. public function set_grade_model_id($id)
  218. {
  219. $this->grade_model_id = $id;
  220. }
  221. /**
  222. * @param $locked
  223. */
  224. public function set_locked($locked)
  225. {
  226. $this->locked = $locked;
  227. }
  228. /**
  229. * Set $isRequirement.
  230. *
  231. * @param int $isRequirement
  232. */
  233. public function setIsRequirement($isRequirement)
  234. {
  235. $this->isRequirement = $isRequirement;
  236. }
  237. /**
  238. * @param $value
  239. */
  240. public function setCourseListDependency($value)
  241. {
  242. $this->courseDependency = [];
  243. $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
  244. if (false !== $unserialized) {
  245. $this->courseDependency = $unserialized;
  246. }
  247. }
  248. /**
  249. * Course id list.
  250. *
  251. * @return array
  252. */
  253. public function getCourseListDependency()
  254. {
  255. return $this->courseDependency;
  256. }
  257. /**
  258. * @param int $value
  259. */
  260. public function setMinimumToValidate($value)
  261. {
  262. $this->minimumToValidate = $value;
  263. }
  264. public function getMinimumToValidate()
  265. {
  266. return $this->minimumToValidate;
  267. }
  268. /**
  269. * @return int|null
  270. */
  271. public function get_grade_model_id()
  272. {
  273. if ($this->grade_model_id < 0) {
  274. return null;
  275. }
  276. return $this->grade_model_id;
  277. }
  278. /**
  279. * @return string
  280. */
  281. public function get_type()
  282. {
  283. return 'category';
  284. }
  285. /**
  286. * @param bool $from_db
  287. *
  288. * @return array|resource
  289. */
  290. public function get_skills($from_db = true)
  291. {
  292. if ($from_db) {
  293. $categoryId = $this->get_id();
  294. $gradebook = new Gradebook();
  295. $skills = $gradebook->getSkillsByGradebook($categoryId);
  296. } else {
  297. $skills = $this->skills;
  298. }
  299. return $skills;
  300. }
  301. /**
  302. * @return array
  303. */
  304. public function getSkillsForSelect()
  305. {
  306. $skills = $this->get_skills();
  307. $skill_select = [];
  308. if (!empty($skills)) {
  309. foreach ($skills as $skill) {
  310. $skill_select[$skill['id']] = $skill['name'];
  311. }
  312. }
  313. return $skill_select;
  314. }
  315. /**
  316. * Set the generate_certificates value.
  317. *
  318. * @param int $generateCertificates
  319. */
  320. public function setGenerateCertificates($generateCertificates)
  321. {
  322. $this->generateCertificates = $generateCertificates;
  323. }
  324. /**
  325. * Get the generate_certificates value.
  326. *
  327. * @return int
  328. */
  329. public function getGenerateCertificates()
  330. {
  331. return $this->generateCertificates;
  332. }
  333. /**
  334. * @param int $id
  335. * @param int $session_id
  336. *
  337. * @return array
  338. */
  339. public static function loadSessionCategories(
  340. $id = null,
  341. $session_id = null
  342. ) {
  343. if (isset($id) && (int) $id === 0) {
  344. $cats = [];
  345. $cats[] = self::create_root_category();
  346. return $cats;
  347. }
  348. $courseInfo = api_get_course_info_by_id(api_get_course_int_id());
  349. $courseCode = $courseInfo['code'];
  350. $session_id = (int) $session_id;
  351. if (!empty($session_id)) {
  352. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  353. $sql = 'SELECT id, course_code
  354. FROM '.$table.'
  355. WHERE session_id = '.$session_id;
  356. $result_session = Database::query($sql);
  357. if (Database::num_rows($result_session) > 0) {
  358. $categoryList = [];
  359. while ($data_session = Database::fetch_array($result_session)) {
  360. $parent_id = $data_session['id'];
  361. if ($data_session['course_code'] == $courseCode) {
  362. $categories = self::load($parent_id);
  363. $categoryList = array_merge($categoryList, $categories);
  364. }
  365. }
  366. return $categoryList;
  367. }
  368. }
  369. }
  370. /**
  371. * Retrieve categories and return them as an array of Category objects.
  372. *
  373. * @param int $id category id
  374. * @param int $user_id (category owner)
  375. * @param string $course_code
  376. * @param int $parent_id parent category
  377. * @param bool $visible
  378. * @param int $session_id (in case we are in a session)
  379. * @param bool $order_by Whether to show all "session"
  380. * categories (true) or hide them (false) in case there is no session id
  381. *
  382. * @return array
  383. */
  384. public static function load(
  385. $id = null,
  386. $user_id = null,
  387. $course_code = null,
  388. $parent_id = null,
  389. $visible = null,
  390. $session_id = null,
  391. $order_by = null
  392. ) {
  393. //if the category given is explicitly 0 (not null), then create
  394. // a root category object (in memory)
  395. if (isset($id) && (int) $id === 0) {
  396. $cats = [];
  397. $cats[] = self::create_root_category();
  398. return $cats;
  399. }
  400. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  401. $sql = 'SELECT * FROM '.$table;
  402. $paramcount = 0;
  403. if (isset($id)) {
  404. $sql .= ' WHERE id = '.intval($id);
  405. $paramcount++;
  406. }
  407. if (isset($user_id)) {
  408. $user_id = intval($user_id);
  409. if ($paramcount != 0) {
  410. $sql .= ' AND';
  411. } else {
  412. $sql .= ' WHERE';
  413. }
  414. $sql .= ' user_id = '.intval($user_id);
  415. $paramcount++;
  416. }
  417. if (isset($course_code)) {
  418. if ($paramcount != 0) {
  419. $sql .= ' AND';
  420. } else {
  421. $sql .= ' WHERE';
  422. }
  423. if ($course_code == '0') {
  424. $sql .= ' course_code is null ';
  425. } else {
  426. $sql .= " course_code = '".Database::escape_string($course_code)."'";
  427. }
  428. /*if ($show_session_categories !== true) {
  429. // a query on the course should show all
  430. // the categories inside sessions for this course
  431. // otherwise a special parameter is given to ask explicitely
  432. $sql .= " AND (session_id IS NULL OR session_id = 0) ";
  433. } else {*/
  434. if (empty($session_id)) {
  435. $sql .= ' AND (session_id IS NULL OR session_id = 0) ';
  436. } else {
  437. $sql .= ' AND session_id = '.(int) $session_id.' ';
  438. }
  439. //}
  440. $paramcount++;
  441. }
  442. if (isset($parent_id)) {
  443. if ($paramcount != 0) {
  444. $sql .= ' AND ';
  445. } else {
  446. $sql .= ' WHERE ';
  447. }
  448. $sql .= ' parent_id = '.intval($parent_id);
  449. $paramcount++;
  450. }
  451. if (isset($visible)) {
  452. if ($paramcount != 0) {
  453. $sql .= ' AND';
  454. } else {
  455. $sql .= ' WHERE';
  456. }
  457. $sql .= ' visible = '.intval($visible);
  458. }
  459. if (!empty($order_by)) {
  460. if (!empty($order_by) && $order_by != '') {
  461. $sql .= ' '.$order_by;
  462. }
  463. }
  464. $result = Database::query($sql);
  465. $categories = [];
  466. if (Database::num_rows($result) > 0) {
  467. $categories = self::create_category_objects_from_sql_result($result);
  468. }
  469. return $categories;
  470. }
  471. /**
  472. * Create a category object from a GradebookCategory entity.
  473. *
  474. * @param GradebookCategory $gradebookCategory The entity
  475. *
  476. * @return \Category
  477. */
  478. public static function createCategoryObjectFromEntity(GradebookCategory $gradebookCategory)
  479. {
  480. $category = new Category();
  481. $category->set_id($gradebookCategory->getId());
  482. $category->set_name($gradebookCategory->getName());
  483. $category->set_description($gradebookCategory->getDescription());
  484. $category->set_user_id($gradebookCategory->getUserId());
  485. $category->set_course_code($gradebookCategory->getCourseCode());
  486. $category->set_parent_id($gradebookCategory->getParentId());
  487. $category->set_weight($gradebookCategory->getWeight());
  488. $category->set_visible($gradebookCategory->getVisible());
  489. $category->set_session_id($gradebookCategory->getSessionId());
  490. $category->set_certificate_min_score($gradebookCategory->getCertifMinScore());
  491. $category->set_grade_model_id($gradebookCategory->getGradeModelId());
  492. $category->set_locked($gradebookCategory->getLocked());
  493. $category->setGenerateCertificates($gradebookCategory->getGenerateCertificates());
  494. $category->setIsRequirement($gradebookCategory->getIsRequirement());
  495. return $category;
  496. }
  497. /**
  498. * Insert this category into the database.
  499. */
  500. public function add()
  501. {
  502. if (isset($this->name) && '-1' == $this->name) {
  503. return false;
  504. }
  505. if (isset($this->name) && isset($this->user_id)) {
  506. $em = Database::getManager();
  507. $category = new GradebookCategory();
  508. $category->setName($this->name);
  509. $category->setDescription($this->description);
  510. $category->setUserId($this->user_id);
  511. $category->setCourseCode($this->course_code);
  512. $category->setParentId($this->parent);
  513. $category->setWeight($this->weight);
  514. $category->setVisible($this->visible);
  515. $category->setCertifMinScore($this->certificate_min_score);
  516. $category->setSessionId($this->session_id);
  517. $category->setGenerateCertificates($this->generateCertificates);
  518. $category->setGradeModelId($this->grade_model_id);
  519. $category->setIsRequirement($this->isRequirement);
  520. $category->setLocked(false);
  521. $em->persist($category);
  522. $em->flush();
  523. $id = $category->getId();
  524. $this->set_id($id);
  525. if (!empty($id)) {
  526. $parent_id = $this->get_parent_id();
  527. $grade_model_id = $this->get_grade_model_id();
  528. if ($parent_id == 0) {
  529. //do something
  530. if (isset($grade_model_id) &&
  531. !empty($grade_model_id) &&
  532. $grade_model_id != '-1'
  533. ) {
  534. $obj = new GradeModel();
  535. $components = $obj->get_components($grade_model_id);
  536. $default_weight_setting = api_get_setting('gradebook_default_weight');
  537. $default_weight = 100;
  538. if (isset($default_weight_setting)) {
  539. $default_weight = $default_weight_setting;
  540. }
  541. foreach ($components as $component) {
  542. $gradebook = new Gradebook();
  543. $params = [];
  544. $params['name'] = $component['acronym'];
  545. $params['description'] = $component['title'];
  546. $params['user_id'] = api_get_user_id();
  547. $params['parent_id'] = $id;
  548. $params['weight'] = $component['percentage'] / 100 * $default_weight;
  549. $params['session_id'] = api_get_session_id();
  550. $params['course_code'] = $this->get_course_code();
  551. $gradebook->save($params);
  552. }
  553. }
  554. }
  555. }
  556. $gradebook = new Gradebook();
  557. $gradebook->updateSkillsToGradeBook(
  558. $this->id,
  559. $this->get_skills(false)
  560. );
  561. return $id;
  562. }
  563. }
  564. /**
  565. * Update the properties of this category in the database.
  566. *
  567. * @todo fix me
  568. */
  569. public function save()
  570. {
  571. $em = Database::getManager();
  572. /** @var GradebookCategory $gradebookCategory */
  573. $gradebookCategory = $em
  574. ->getRepository('ChamiloCoreBundle:GradebookCategory')
  575. ->find($this->id);
  576. if (empty($gradebookCategory)) {
  577. return false;
  578. }
  579. $gradebookCategory->setName($this->name);
  580. $gradebookCategory->setDescription($this->description);
  581. $gradebookCategory->setUserId($this->user_id);
  582. $gradebookCategory->setCourseCode($this->course_code);
  583. $gradebookCategory->setParentId($this->parent);
  584. $gradebookCategory->setWeight($this->weight);
  585. $gradebookCategory->setVisible($this->visible);
  586. $gradebookCategory->setCertifMinScore($this->certificate_min_score);
  587. $gradebookCategory->setGenerateCertificates(
  588. $this->generateCertificates
  589. );
  590. $gradebookCategory->setGradeModelId($this->grade_model_id);
  591. $gradebookCategory->setIsRequirement($this->isRequirement);
  592. $em->merge($gradebookCategory);
  593. $em->flush();
  594. if (!empty($this->id)) {
  595. $parent_id = $this->get_parent_id();
  596. $grade_model_id = $this->get_grade_model_id();
  597. if ($parent_id == 0) {
  598. if (isset($grade_model_id) &&
  599. !empty($grade_model_id) &&
  600. $grade_model_id != '-1'
  601. ) {
  602. $obj = new GradeModel();
  603. $components = $obj->get_components($grade_model_id);
  604. $default_weight_setting = api_get_setting('gradebook_default_weight');
  605. $default_weight = 100;
  606. if (isset($default_weight_setting)) {
  607. $default_weight = $default_weight_setting;
  608. }
  609. $final_weight = $this->get_weight();
  610. if (!empty($final_weight)) {
  611. $default_weight = $this->get_weight();
  612. }
  613. foreach ($components as $component) {
  614. $gradebook = new Gradebook();
  615. $params = [];
  616. $params['name'] = $component['acronym'];
  617. $params['description'] = $component['title'];
  618. $params['user_id'] = api_get_user_id();
  619. $params['parent_id'] = $this->id;
  620. $params['weight'] = $component['percentage'] / 100 * $default_weight;
  621. $params['session_id'] = api_get_session_id();
  622. $params['course_code'] = $this->get_course_code();
  623. $gradebook->save($params);
  624. }
  625. }
  626. }
  627. }
  628. $gradebook = new Gradebook();
  629. $gradebook->updateSkillsToGradeBook(
  630. $this->id,
  631. $this->get_skills(false),
  632. true
  633. );
  634. }
  635. /**
  636. * Update link weights see #5168.
  637. *
  638. * @param type $new_weight
  639. */
  640. public function updateChildrenWeight($new_weight)
  641. {
  642. $links = $this->get_links();
  643. $old_weight = $this->get_weight();
  644. if (!empty($links)) {
  645. foreach ($links as $link_item) {
  646. if (isset($link_item)) {
  647. $new_item_weight = $new_weight * $link_item->get_weight() / $old_weight;
  648. $link_item->set_weight($new_item_weight);
  649. $link_item->save();
  650. }
  651. }
  652. }
  653. }
  654. /**
  655. * Delete this evaluation from the database.
  656. */
  657. public function delete()
  658. {
  659. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  660. $sql = 'DELETE FROM '.$table.' WHERE id = '.intval($this->id);
  661. Database::query($sql);
  662. }
  663. /**
  664. * Delete the gradebook categories from a course, including course sessions.
  665. *
  666. * @param string $courseCode
  667. */
  668. public static function deleteFromCourse($courseCode)
  669. {
  670. $em = Database::getManager();
  671. $categories = $em
  672. ->createQuery(
  673. 'SELECT DISTINCT gc.sessionId
  674. FROM ChamiloCoreBundle:GradebookCategory gc WHERE gc.courseCode = :code'
  675. )
  676. ->setParameter('code', $courseCode)
  677. ->getResult();
  678. foreach ($categories as $category) {
  679. $cats = self::load(
  680. null,
  681. null,
  682. $courseCode,
  683. null,
  684. null,
  685. (int) $category['sessionId']
  686. );
  687. if (!empty($cats)) {
  688. /** @var self $cat */
  689. foreach ($cats as $cat) {
  690. $cat->delete_all();
  691. }
  692. }
  693. }
  694. }
  695. /**
  696. * Show message resource delete.
  697. *
  698. * @param string $courseCode
  699. *
  700. * @return mixed
  701. */
  702. public function show_message_resource_delete($courseCode)
  703. {
  704. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  705. $sql = 'SELECT count(*) AS num
  706. FROM '.$table.'
  707. WHERE
  708. course_code = "'.Database::escape_string($courseCode).'" AND
  709. visible = 3';
  710. $res = Database::query($sql);
  711. $option = Database::fetch_array($res, 'ASSOC');
  712. if ($option['num'] >= 1) {
  713. return '&nbsp;&nbsp;<span class="resource-deleted">(&nbsp;'.get_lang('ResourceDeleted').'&nbsp;)</span>';
  714. } else {
  715. return false;
  716. }
  717. }
  718. /**
  719. * Shows all information of an category.
  720. *
  721. * @param int $categoryId
  722. *
  723. * @return array
  724. */
  725. public function showAllCategoryInfo($categoryId)
  726. {
  727. $categoryId = (int) $categoryId;
  728. if (empty($categoryId)) {
  729. return [];
  730. }
  731. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  732. $sql = 'SELECT * FROM '.$table.'
  733. WHERE id = '.$categoryId;
  734. $result = Database::query($sql);
  735. $row = Database::fetch_array($result, 'ASSOC');
  736. return $row;
  737. }
  738. /**
  739. * Checks if the certificate is available for the given user in this category.
  740. *
  741. * @param int $user_id User ID
  742. *
  743. * @return bool True if conditions match, false if fails
  744. */
  745. public function is_certificate_available($user_id)
  746. {
  747. $score = $this->calc_score(
  748. $user_id,
  749. null,
  750. $this->course_code,
  751. $this->session_id
  752. );
  753. if (isset($score) && isset($score[0])) {
  754. // Get a percentage score to compare to minimum certificate score
  755. // $certification_score = $score[0] / $score[1] * 100;
  756. // Get real score not a percentage.
  757. $certification_score = $score[0];
  758. if ($certification_score >= $this->certificate_min_score) {
  759. return true;
  760. }
  761. }
  762. return false;
  763. }
  764. /**
  765. * Is this category a course ?
  766. * A category is a course if it has a course code and no parent category.
  767. */
  768. public function is_course()
  769. {
  770. return isset($this->course_code) && !empty($this->course_code)
  771. && (!isset($this->parent) || $this->parent == 0);
  772. }
  773. /**
  774. * Calculate the score of this category.
  775. *
  776. * @param int $stud_id student id (default: all students - then the average is returned)
  777. * @param $type
  778. * @param string $course_code
  779. * @param int $session_id
  780. *
  781. * @return array (score sum, weight sum)
  782. * or null if no scores available
  783. */
  784. public function calc_score(
  785. $stud_id = null,
  786. $type = null,
  787. $course_code = '',
  788. $session_id = null
  789. ) {
  790. $key = 'category:'.$this->id.'student:'.(int) $stud_id.'type:'.$type.'course:'.$course_code.'session:'.(int) $session_id;
  791. $useCache = api_get_configuration_value('gradebook_use_apcu_cache');
  792. $cacheAvailable = api_get_configuration_value('apc') && $useCache;
  793. if ($cacheAvailable) {
  794. $cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
  795. if ($cacheDriver->contains($key)) {
  796. return $cacheDriver->fetch($key);
  797. }
  798. }
  799. // Classic
  800. if (!empty($stud_id) && $type == '') {
  801. if (!empty($course_code)) {
  802. $cats = $this->get_subcategories(
  803. $stud_id,
  804. $course_code,
  805. $session_id
  806. );
  807. $evals = $this->get_evaluations($stud_id, false, $course_code);
  808. $links = $this->get_links($stud_id, false, $course_code);
  809. } else {
  810. $cats = $this->get_subcategories($stud_id);
  811. $evals = $this->get_evaluations($stud_id);
  812. $links = $this->get_links($stud_id);
  813. }
  814. // Calculate score
  815. $count = 0;
  816. $ressum = 0;
  817. $weightsum = 0;
  818. if (!empty($cats)) {
  819. /** @var Category $cat */
  820. foreach ($cats as $cat) {
  821. $cat->set_session_id($session_id);
  822. $cat->set_course_code($course_code);
  823. $cat->setStudentList($this->getStudentList());
  824. $score = $cat->calc_score(
  825. $stud_id,
  826. null,
  827. $course_code,
  828. $session_id
  829. );
  830. $catweight = 0;
  831. if ($cat->get_weight() != 0) {
  832. $catweight = $cat->get_weight();
  833. $weightsum += $catweight;
  834. }
  835. if (isset($score) && !empty($score[1]) && !empty($catweight)) {
  836. $ressum += $score[0] / $score[1] * $catweight;
  837. }
  838. }
  839. }
  840. $students = [];
  841. if (!empty($evals)) {
  842. /** @var Evaluation $eval */
  843. foreach ($evals as $eval) {
  844. $eval->setStudentList($this->getStudentList());
  845. $evalres = $eval->calc_score($stud_id);
  846. if (isset($evalres) && $eval->get_weight() != 0) {
  847. $evalweight = $eval->get_weight();
  848. $weightsum += $evalweight;
  849. if (!empty($evalres[1])) {
  850. $ressum += $evalres[0] / $evalres[1] * $evalweight;
  851. }
  852. } else {
  853. if ($eval->get_weight() != 0) {
  854. $evalweight = $eval->get_weight();
  855. $weightsum += $evalweight;
  856. }
  857. }
  858. }
  859. }
  860. if (!empty($links)) {
  861. /** @var EvalLink|ExerciseLink $link */
  862. foreach ($links as $link) {
  863. $link->setStudentList($this->getStudentList());
  864. if ($session_id) {
  865. $link->set_session_id($session_id);
  866. }
  867. $linkres = $link->calc_score($stud_id, null);
  868. if (!empty($linkres) && $link->get_weight() != 0) {
  869. $linkweight = $link->get_weight();
  870. $link_res_denom = $linkres[1] == 0 ? 1 : $linkres[1];
  871. $weightsum += $linkweight;
  872. $ressum += $linkres[0] / $link_res_denom * $linkweight;
  873. } else {
  874. // Adding if result does not exists
  875. if ($link->get_weight() != 0) {
  876. $linkweight = $link->get_weight();
  877. $weightsum += $linkweight;
  878. }
  879. }
  880. }
  881. }
  882. } else {
  883. if (!empty($course_code)) {
  884. $cats = $this->get_subcategories(
  885. null,
  886. $course_code,
  887. $session_id
  888. );
  889. $evals = $this->get_evaluations(null, false, $course_code);
  890. $links = $this->get_links(null, false, $course_code);
  891. } else {
  892. $cats = $this->get_subcategories(null);
  893. $evals = $this->get_evaluations(null);
  894. $links = $this->get_links(null);
  895. }
  896. // Calculate score
  897. $ressum = 0;
  898. $weightsum = 0;
  899. $bestResult = 0;
  900. $totalScorePerStudent = [];
  901. if (!empty($cats)) {
  902. /** @var Category $cat */
  903. foreach ($cats as $cat) {
  904. $cat->setStudentList($this->getStudentList());
  905. $score = $cat->calc_score(
  906. null,
  907. $type,
  908. $course_code,
  909. $session_id
  910. );
  911. $catweight = 0;
  912. if ($cat->get_weight() != 0) {
  913. $catweight = $cat->get_weight();
  914. $weightsum += $catweight;
  915. }
  916. if (isset($score) && !empty($score[1]) && !empty($catweight)) {
  917. $ressum += $score[0] / $score[1] * $catweight;
  918. if ($ressum > $bestResult) {
  919. $bestResult = $ressum;
  920. }
  921. }
  922. }
  923. }
  924. if (!empty($evals)) {
  925. if ($type === 'best') {
  926. $studentList = $this->getStudentList();
  927. foreach ($studentList as $student) {
  928. $studentId = $student['user_id'];
  929. foreach ($evals as $eval) {
  930. $linkres = $eval->calc_score($studentId, null);
  931. $linkweight = $eval->get_weight();
  932. $link_res_denom = $linkres[1] == 0 ? 1 : $linkres[1];
  933. $ressum = $linkres[0] / $link_res_denom * $linkweight;
  934. if (!isset($totalScorePerStudent[$studentId])) {
  935. $totalScorePerStudent[$studentId] = 0;
  936. }
  937. $totalScorePerStudent[$studentId] += $ressum;
  938. }
  939. }
  940. } else {
  941. /** @var Evaluation $eval */
  942. foreach ($evals as $eval) {
  943. $evalres = $eval->calc_score(null, $type);
  944. $eval->setStudentList($this->getStudentList());
  945. if (isset($evalres) && $eval->get_weight() != 0) {
  946. $evalweight = $eval->get_weight();
  947. $weightsum += $evalweight;
  948. if (!empty($evalres[1])) {
  949. $ressum += $evalres[0] / $evalres[1] * $evalweight;
  950. }
  951. if ($ressum > $bestResult) {
  952. $bestResult = $ressum;
  953. }
  954. } else {
  955. if ($eval->get_weight() != 0) {
  956. $evalweight = $eval->get_weight();
  957. $weightsum += $evalweight;
  958. }
  959. }
  960. }
  961. }
  962. }
  963. if (!empty($links)) {
  964. $studentList = $this->getStudentList();
  965. if ($type === 'best') {
  966. foreach ($studentList as $student) {
  967. $studentId = $student['user_id'];
  968. foreach ($links as $link) {
  969. $linkres = $link->calc_score($studentId, null);
  970. $linkweight = $link->get_weight();
  971. $link_res_denom = $linkres[1] == 0 ? 1 : $linkres[1];
  972. $ressum = $linkres[0] / $link_res_denom * $linkweight;
  973. if (!isset($totalScorePerStudent[$studentId])) {
  974. $totalScorePerStudent[$studentId] = 0;
  975. }
  976. $totalScorePerStudent[$studentId] += $ressum;
  977. }
  978. }
  979. } else {
  980. /** @var EvalLink|ExerciseLink $link */
  981. foreach ($links as $link) {
  982. $link->setStudentList($this->getStudentList());
  983. if ($session_id) {
  984. $link->set_session_id($session_id);
  985. }
  986. $linkres = $link->calc_score($stud_id, $type);
  987. if (!empty($linkres) && $link->get_weight() != 0) {
  988. $linkweight = $link->get_weight();
  989. $link_res_denom = $linkres[1] == 0 ? 1 : $linkres[1];
  990. $weightsum += $linkweight;
  991. $ressum += $linkres[0] / $link_res_denom * $linkweight;
  992. if ($ressum > $bestResult) {
  993. $bestResult = $ressum;
  994. }
  995. } else {
  996. // Adding if result does not exists
  997. if ($link->get_weight() != 0) {
  998. $linkweight = $link->get_weight();
  999. $weightsum += $linkweight;
  1000. }
  1001. }
  1002. }
  1003. }
  1004. }
  1005. }
  1006. switch ($type) {
  1007. case 'best':
  1008. arsort($totalScorePerStudent);
  1009. $maxScore = current($totalScorePerStudent);
  1010. return [$maxScore, $this->get_weight()];
  1011. if (empty($bestResult)) {
  1012. if ($cacheAvailable) {
  1013. $cacheDriver->save($key, null);
  1014. }
  1015. return null;
  1016. }
  1017. if ($cacheAvailable) {
  1018. $cacheDriver->save($key, [$bestResult, $weightsum]);
  1019. }
  1020. return [$bestResult, $weightsum];
  1021. break;
  1022. case 'average':
  1023. if (empty($ressum)) {
  1024. if ($cacheAvailable) {
  1025. $cacheDriver->save($key, null);
  1026. }
  1027. return null;
  1028. }
  1029. if ($cacheAvailable) {
  1030. $cacheDriver->save($key, [$ressum, $weightsum]);
  1031. }
  1032. return [$ressum, $weightsum];
  1033. break;
  1034. case 'ranking':
  1035. // category ranking is calculated in gradebook_data_generator.class.php
  1036. // function get_data
  1037. return null;
  1038. return AbstractLink::getCurrentUserRanking($stud_id, []);
  1039. break;
  1040. default:
  1041. if ($cacheAvailable) {
  1042. $cacheDriver->save($key, [$ressum, $weightsum]);
  1043. }
  1044. return [$ressum, $weightsum];
  1045. break;
  1046. }
  1047. }
  1048. /**
  1049. * Delete this category and every subcategory, evaluation and result inside.
  1050. */
  1051. public function delete_all()
  1052. {
  1053. $cats = self::load(null, null, $this->course_code, $this->id, null);
  1054. $evals = Evaluation::load(
  1055. null,
  1056. null,
  1057. $this->course_code,
  1058. $this->id,
  1059. null
  1060. );
  1061. $links = LinkFactory::load(
  1062. null,
  1063. null,
  1064. null,
  1065. null,
  1066. $this->course_code,
  1067. $this->id,
  1068. null
  1069. );
  1070. if (!empty($cats)) {
  1071. /** @var Category $cat */
  1072. foreach ($cats as $cat) {
  1073. $cat->delete_all();
  1074. $cat->delete();
  1075. }
  1076. }
  1077. if (!empty($evals)) {
  1078. /** @var Evaluation $eval */
  1079. foreach ($evals as $eval) {
  1080. $eval->delete_with_results();
  1081. }
  1082. }
  1083. if (!empty($links)) {
  1084. /** @var AbstractLink $link */
  1085. foreach ($links as $link) {
  1086. $link->delete();
  1087. }
  1088. }
  1089. $this->delete();
  1090. }
  1091. /**
  1092. * Return array of Category objects where a student is subscribed to.
  1093. *
  1094. * @param int $stud_id
  1095. * @param string $course_code
  1096. * @param int $session_id
  1097. *
  1098. * @return array
  1099. */
  1100. public function get_root_categories_for_student(
  1101. $stud_id,
  1102. $course_code = null,
  1103. $session_id = null
  1104. ) {
  1105. $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1106. $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
  1107. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1108. $course_code = Database::escape_string($course_code);
  1109. $session_id = (int) $session_id;
  1110. $sql = "SELECT * FROM $table WHERE parent_id = 0";
  1111. if (!api_is_allowed_to_edit()) {
  1112. $sql .= ' AND visible = 1';
  1113. //proceed with checks on optional parameters course & session
  1114. if (!empty($course_code)) {
  1115. // TODO: considering it highly improbable that a user would get here
  1116. // if he doesn't have the rights to view this course and this
  1117. // session, we don't check his registration to these, but this
  1118. // could be an improvement
  1119. if (!empty($session_id)) {
  1120. $sql .= " AND course_code = '".$course_code."' AND session_id = ".$session_id;
  1121. } else {
  1122. $sql .= " AND course_code = '".$course_code."' AND session_id is null OR session_id=0";
  1123. }
  1124. } else {
  1125. //no optional parameter, proceed as usual
  1126. $sql .= ' AND course_code in
  1127. (
  1128. SELECT c.code
  1129. FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
  1130. ON (cu.c_id = c.id)
  1131. WHERE cu.user_id = '.intval($stud_id).'
  1132. AND cu.status = '.STUDENT.'
  1133. )';
  1134. }
  1135. } elseif (api_is_allowed_to_edit() && !api_is_platform_admin()) {
  1136. //proceed with checks on optional parameters course & session
  1137. if (!empty($course_code)) {
  1138. // TODO: considering it highly improbable that a user would get here
  1139. // if he doesn't have the rights to view this course and this
  1140. // session, we don't check his registration to these, but this
  1141. // could be an improvement
  1142. $sql .= " AND course_code = '".$course_code."'";
  1143. if (!empty($session_id)) {
  1144. $sql .= " AND session_id = ".$session_id;
  1145. } else {
  1146. $sql .= 'AND session_id IS NULL OR session_id = 0';
  1147. }
  1148. } else {
  1149. $sql .= ' AND course_code IN
  1150. (
  1151. SELECT c.code
  1152. FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
  1153. ON (cu.c_id = c.id)
  1154. WHERE
  1155. cu.user_id = '.api_get_user_id().' AND
  1156. cu.status = '.COURSEMANAGER.'
  1157. )';
  1158. }
  1159. } elseif (api_is_platform_admin()) {
  1160. if (isset($session_id) && $session_id != 0) {
  1161. $sql .= ' AND session_id='.$session_id;
  1162. } else {
  1163. $sql .= ' AND coalesce(session_id,0)=0';
  1164. }
  1165. }
  1166. $result = Database::query($sql);
  1167. $cats = self::create_category_objects_from_sql_result($result);
  1168. // course independent categories
  1169. if (empty($course_code)) {
  1170. $cats = $this->getIndependentCategoriesWithStudentResult(
  1171. 0,
  1172. $stud_id,
  1173. $cats
  1174. );
  1175. }
  1176. return $cats;
  1177. }
  1178. /**
  1179. * Return array of Category objects where a teacher is admin for.
  1180. *
  1181. * @param int $user_id (to return everything, use 'null' here)
  1182. * @param string $course_code (optional)
  1183. * @param int $session_id (optional)
  1184. *
  1185. * @return array
  1186. */
  1187. public function get_root_categories_for_teacher(
  1188. $user_id,
  1189. $course_code = null,
  1190. $session_id = null
  1191. ) {
  1192. if ($user_id == null) {
  1193. return self::load(null, null, $course_code, 0, null, $session_id);
  1194. }
  1195. $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
  1196. $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1197. $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1198. $sql = 'SELECT * FROM '.$tbl_grade_categories.'
  1199. WHERE parent_id = 0 ';
  1200. if (!empty($course_code)) {
  1201. $sql .= " AND course_code = '".Database::escape_string($course_code)."' ";
  1202. if (!empty($session_id)) {
  1203. $sql .= " AND session_id = ".(int) $session_id;
  1204. }
  1205. } else {
  1206. $sql .= ' AND course_code in
  1207. (
  1208. SELECT c.code
  1209. FROM '.$main_course_user_table.' cu
  1210. INNER JOIN '.$courseTable.' c
  1211. ON (cu.c_id = c.id)
  1212. WHERE user_id = '.intval($user_id).'
  1213. )';
  1214. }
  1215. $result = Database::query($sql);
  1216. $cats = self::create_category_objects_from_sql_result($result);
  1217. // course independent categories
  1218. if (isset($course_code)) {
  1219. $indcats = self::load(
  1220. null,
  1221. $user_id,
  1222. $course_code,
  1223. 0,
  1224. null,
  1225. $session_id
  1226. );
  1227. $cats = array_merge($cats, $indcats);
  1228. }
  1229. return $cats;
  1230. }
  1231. /**
  1232. * Can this category be moved to somewhere else ?
  1233. * The root and courses cannot be moved.
  1234. *
  1235. * @return bool
  1236. */
  1237. public function is_movable()
  1238. {
  1239. return !(!isset($this->id) || $this->id == 0 || $this->is_course());
  1240. }
  1241. /**
  1242. * Generate an array of possible categories where this category can be moved to.
  1243. * Notice: its own parent will be included in the list: it's up to the frontend
  1244. * to disable this element.
  1245. *
  1246. * @return array 2-dimensional array - every element contains 3 subelements (id, name, level)
  1247. */
  1248. public function get_target_categories()
  1249. {
  1250. // the root or a course -> not movable
  1251. if (!$this->is_movable()) {
  1252. return null;
  1253. } else {
  1254. // otherwise:
  1255. // - course independent category
  1256. // -> movable to root or other independent categories
  1257. // - category inside a course
  1258. // -> movable to root, independent categories or categories inside the course
  1259. $user = api_is_platform_admin() ? null : api_get_user_id();
  1260. $targets = [];
  1261. $level = 0;
  1262. $root = [0, get_lang('RootCat'), $level];
  1263. $targets[] = $root;
  1264. if (isset($this->course_code) && !empty($this->course_code)) {
  1265. $crscats = self::load(null, null, $this->course_code, 0);
  1266. foreach ($crscats as $cat) {
  1267. if ($this->can_be_moved_to_cat($cat)) {
  1268. $targets[] = [
  1269. $cat->get_id(),
  1270. $cat->get_name(),
  1271. $level + 1,
  1272. ];
  1273. $targets = $this->addTargetSubcategories(
  1274. $targets,
  1275. $level + 1,
  1276. $cat->get_id()
  1277. );
  1278. }
  1279. }
  1280. }
  1281. $indcats = self::load(null, $user, 0, 0);
  1282. foreach ($indcats as $cat) {
  1283. if ($this->can_be_moved_to_cat($cat)) {
  1284. $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
  1285. $targets = $this->addTargetSubcategories(
  1286. $targets,
  1287. $level + 1,
  1288. $cat->get_id()
  1289. );
  1290. }
  1291. }
  1292. return $targets;
  1293. }
  1294. }
  1295. /**
  1296. * Move this category to the given category.
  1297. * If this category moves from inside a course to outside,
  1298. * its course code must be changed, as well as the course code
  1299. * of all underlying categories and evaluations. All links will
  1300. * be deleted as well !
  1301. */
  1302. public function move_to_cat($cat)
  1303. {
  1304. $this->set_parent_id($cat->get_id());
  1305. if ($this->get_course_code() != $cat->get_course_code()) {
  1306. $this->set_course_code($cat->get_course_code());
  1307. $this->applyCourseCodeToChildren();
  1308. }
  1309. $this->save();
  1310. }
  1311. /**
  1312. * Generate an array of all categories the user can navigate to.
  1313. */
  1314. public function get_tree()
  1315. {
  1316. $targets = [];
  1317. $level = 0;
  1318. $root = [0, get_lang('RootCat'), $level];
  1319. $targets[] = $root;
  1320. // course or platform admin
  1321. if (api_is_allowed_to_edit()) {
  1322. $user = api_is_platform_admin() ? null : api_get_user_id();
  1323. $cats = self::get_root_categories_for_teacher($user);
  1324. foreach ($cats as $cat) {
  1325. $targets[] = [
  1326. $cat->get_id(),
  1327. $cat->get_name(),
  1328. $level + 1,
  1329. ];
  1330. $targets = $this->add_subtree(
  1331. $targets,
  1332. $level + 1,
  1333. $cat->get_id(),
  1334. null
  1335. );
  1336. }
  1337. } else {
  1338. // student
  1339. $cats = $this->get_root_categories_for_student(api_get_user_id());
  1340. foreach ($cats as $cat) {
  1341. $targets[] = [
  1342. $cat->get_id(),
  1343. $cat->get_name(),
  1344. $level + 1,
  1345. ];
  1346. $targets = $this->add_subtree(
  1347. $targets,
  1348. $level + 1,
  1349. $cat->get_id(),
  1350. 1
  1351. );
  1352. }
  1353. }
  1354. return $targets;
  1355. }
  1356. /**
  1357. * Generate an array of courses that a teacher hasn't created a category for.
  1358. *
  1359. * @param int $user_id
  1360. *
  1361. * @return array 2-dimensional array - every element contains 2 subelements (code, title)
  1362. */
  1363. public function get_not_created_course_categories($user_id)
  1364. {
  1365. $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
  1366. $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1367. $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1368. $user_id = (int) $user_id;
  1369. $sql = 'SELECT DISTINCT(code), title
  1370. FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
  1371. WHERE
  1372. cc.id = cu.c_id AND
  1373. cu.status = '.COURSEMANAGER;
  1374. if (!api_is_platform_admin()) {
  1375. $sql .= ' AND cu.user_id = '.$user_id;
  1376. }
  1377. $sql .= ' AND cc.code NOT IN
  1378. (
  1379. SELECT course_code FROM '.$tbl_grade_categories.'
  1380. WHERE
  1381. parent_id = 0 AND
  1382. course_code IS NOT NULL
  1383. )';
  1384. $result = Database::query($sql);
  1385. $cats = [];
  1386. while ($data = Database::fetch_array($result)) {
  1387. $cats[] = [$data['code'], $data['title']];
  1388. }
  1389. return $cats;
  1390. }
  1391. /**
  1392. * Generate an array of all courses that a teacher is admin of.
  1393. *
  1394. * @param int $user_id
  1395. *
  1396. * @return array 2-dimensional array - every element contains 2 subelements (code, title)
  1397. */
  1398. public function get_all_courses($user_id)
  1399. {
  1400. $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
  1401. $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1402. $sql = 'SELECT DISTINCT(code), title
  1403. FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
  1404. WHERE cc.id = cu.c_id AND cu.status = '.COURSEMANAGER;
  1405. if (!api_is_platform_admin()) {
  1406. $sql .= ' AND cu.user_id = '.intval($user_id);
  1407. }
  1408. $result = Database::query($sql);
  1409. $cats = [];
  1410. while ($data = Database::fetch_array($result)) {
  1411. $cats[] = [$data['code'], $data['title']];
  1412. }
  1413. return $cats;
  1414. }
  1415. /**
  1416. * Apply the same visibility to every subcategory, evaluation and link.
  1417. */
  1418. public function apply_visibility_to_children()
  1419. {
  1420. $cats = self::load(null, null, null, $this->id, null);
  1421. $evals = Evaluation::load(null, null, null, $this->id, null);
  1422. $links = LinkFactory::load(
  1423. null,
  1424. null,
  1425. null,
  1426. null,
  1427. null,
  1428. $this->id,
  1429. null
  1430. );
  1431. if (!empty($cats)) {
  1432. foreach ($cats as $cat) {
  1433. $cat->set_visible($this->is_visible());
  1434. $cat->save();
  1435. $cat->apply_visibility_to_children();
  1436. }
  1437. }
  1438. if (!empty($evals)) {
  1439. foreach ($evals as $eval) {
  1440. $eval->set_visible($this->is_visible());
  1441. $eval->save();
  1442. }
  1443. }
  1444. if (!empty($links)) {
  1445. foreach ($links as $link) {
  1446. $link->set_visible($this->is_visible());
  1447. $link->save();
  1448. }
  1449. }
  1450. }
  1451. /**
  1452. * Check if a category contains evaluations with a result for a given student.
  1453. *
  1454. * @param int $studentId
  1455. *
  1456. * @return bool
  1457. */
  1458. public function hasEvaluationsWithStudentResults($studentId)
  1459. {
  1460. $evals = Evaluation::get_evaluations_with_result_for_student(
  1461. $this->id,
  1462. $studentId
  1463. );
  1464. if (count($evals) != 0) {
  1465. return true;
  1466. } else {
  1467. $cats = self::load(
  1468. null,
  1469. null,
  1470. null,
  1471. $this->id,
  1472. api_is_allowed_to_edit() ? null : 1
  1473. );
  1474. /** @var Category $cat */
  1475. foreach ($cats as $cat) {
  1476. if ($cat->hasEvaluationsWithStudentResults($studentId)) {
  1477. return true;
  1478. }
  1479. }
  1480. return false;
  1481. }
  1482. }
  1483. /**
  1484. * Retrieve all categories inside a course independent category
  1485. * that should be visible to a student.
  1486. *
  1487. * @param int $categoryId parent category
  1488. * @param int $studentId
  1489. * @param array $cats optional: if defined, the categories will be added to this array
  1490. *
  1491. * @return array
  1492. */
  1493. public function getIndependentCategoriesWithStudentResult(
  1494. $categoryId,
  1495. $studentId,
  1496. $cats = []
  1497. ) {
  1498. $creator = api_is_allowed_to_edit() && !api_is_platform_admin() ? api_get_user_id() : null;
  1499. $categories = self::load(
  1500. null,
  1501. $creator,
  1502. '0',
  1503. $categoryId,
  1504. api_is_allowed_to_edit() ? null : 1
  1505. );
  1506. if (!empty($categories)) {
  1507. /** @var Category $category */
  1508. foreach ($categories as $category) {
  1509. if ($category->hasEvaluationsWithStudentResults($studentId)) {
  1510. $cats[] = $category;
  1511. }
  1512. }
  1513. }
  1514. return $cats;
  1515. }
  1516. /**
  1517. * Return the session id (in any case, even if it's null or 0).
  1518. *
  1519. * @return int Session id (can be null)
  1520. */
  1521. public function get_session_id()
  1522. {
  1523. return $this->session_id;
  1524. }
  1525. /**
  1526. * Get appropriate subcategories visible for the user (and optionally the course and session).
  1527. *
  1528. * @param int $studentId student id (default: all students)
  1529. * @param string $course_code Course code (optional)
  1530. * @param int $session_id Session ID (optional)
  1531. * @param bool $order
  1532. *
  1533. * @return array Array of subcategories
  1534. */
  1535. public function get_subcategories(
  1536. $studentId = null,
  1537. $course_code = null,
  1538. $session_id = null,
  1539. $order = null
  1540. ) {
  1541. // 1 student
  1542. if (isset($studentId)) {
  1543. // Special case: this is the root
  1544. if ($this->id == 0) {
  1545. return $this->get_root_categories_for_student($studentId, $course_code, $session_id);
  1546. } else {
  1547. return self::load(
  1548. null,
  1549. null,
  1550. $course_code,
  1551. $this->id,
  1552. api_is_allowed_to_edit() ? null : 1,
  1553. $session_id,
  1554. $order
  1555. );
  1556. }
  1557. } else {
  1558. // All students
  1559. // Course admin
  1560. if (api_is_allowed_to_edit() && !api_is_platform_admin()) {
  1561. // root
  1562. if ($this->id == 0) {
  1563. // inside a course
  1564. return $this->get_root_categories_for_teacher(
  1565. api_get_user_id(),
  1566. $course_code,
  1567. $session_id,
  1568. false
  1569. );
  1570. } elseif (!empty($this->course_code)) {
  1571. return self::load(
  1572. null,
  1573. null,
  1574. $this->course_code,
  1575. $this->id,
  1576. null,
  1577. $session_id,
  1578. $order
  1579. );
  1580. } elseif (!empty($course_code)) {
  1581. // course independent
  1582. return self::load(
  1583. null,
  1584. null,
  1585. $course_code,
  1586. $this->id,
  1587. null,
  1588. $session_id,
  1589. $order
  1590. );
  1591. } else {
  1592. return self::load(
  1593. null,
  1594. api_get_user_id(),
  1595. 0,
  1596. $this->id,
  1597. null
  1598. );
  1599. }
  1600. } elseif (api_is_platform_admin()) {
  1601. // platform admin
  1602. // we explicitly avoid listing subcats from another session
  1603. return self::load(
  1604. null,
  1605. null,
  1606. $course_code,
  1607. $this->id,
  1608. null,
  1609. $session_id,
  1610. $order
  1611. );
  1612. }
  1613. }
  1614. return [];
  1615. }
  1616. /**
  1617. * Get appropriate evaluations visible for the user.
  1618. *
  1619. * @param int $studentId student id (default: all students)
  1620. * @param bool $recursive process subcategories (default: no recursion)
  1621. * @param string $course_code
  1622. * @param int $sessionId
  1623. *
  1624. * @return array
  1625. */
  1626. public function get_evaluations(
  1627. $studentId = null,
  1628. $recursive = false,
  1629. $course_code = '',
  1630. $sessionId = 0
  1631. ) {
  1632. $evals = [];
  1633. $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
  1634. $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
  1635. // 1 student
  1636. if (isset($studentId) && !empty($studentId)) {
  1637. // Special case: this is the root
  1638. if ($this->id == 0) {
  1639. $evals = Evaluation::get_evaluations_with_result_for_student(
  1640. 0,
  1641. $studentId
  1642. );
  1643. } else {
  1644. $evals = Evaluation::load(
  1645. null,
  1646. null,
  1647. $course_code,
  1648. $this->id,
  1649. api_is_allowed_to_edit() ? null : 1
  1650. );
  1651. }
  1652. } else {
  1653. // All students
  1654. // course admin
  1655. if ((api_is_allowed_to_edit() || api_is_drh() || api_is_session_admin()) &&
  1656. !api_is_platform_admin()
  1657. ) {
  1658. // root
  1659. if ($this->id == 0) {
  1660. $evals = Evaluation::load(
  1661. null,
  1662. api_get_user_id(),
  1663. null,
  1664. $this->id,
  1665. null
  1666. );
  1667. } elseif (isset($this->course_code) &&
  1668. !empty($this->course_code)
  1669. ) {
  1670. // inside a course
  1671. $evals = Evaluation::load(
  1672. null,
  1673. null,
  1674. $course_code,
  1675. $this->id,
  1676. null
  1677. );
  1678. } else {
  1679. // course independent
  1680. $evals = Evaluation::load(
  1681. null,
  1682. api_get_user_id(),
  1683. null,
  1684. $this->id,
  1685. null
  1686. );
  1687. }
  1688. } else {
  1689. $evals = Evaluation::load(
  1690. null,
  1691. null,
  1692. $course_code,
  1693. $this->id,
  1694. null
  1695. );
  1696. }
  1697. }
  1698. if ($recursive) {
  1699. $subcats = $this->get_subcategories(
  1700. $studentId,
  1701. $course_code,
  1702. $sessionId
  1703. );
  1704. if (!empty($subcats)) {
  1705. foreach ($subcats as $subcat) {
  1706. $subevals = $subcat->get_evaluations(
  1707. $studentId,
  1708. true,
  1709. $course_code
  1710. );
  1711. $evals = array_merge($evals, $subevals);
  1712. }
  1713. }
  1714. }
  1715. return $evals;
  1716. }
  1717. /**
  1718. * Get appropriate links visible for the user.
  1719. *
  1720. * @param int $studentId student id (default: all students)
  1721. * @param bool $recursive process subcategories (default: no recursion)
  1722. * @param string $course_code
  1723. * @param int $sessionId
  1724. *
  1725. * @return array
  1726. */
  1727. public function get_links(
  1728. $studentId = null,
  1729. $recursive = false,
  1730. $course_code = '',
  1731. $sessionId = 0
  1732. ) {
  1733. $links = [];
  1734. $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
  1735. $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
  1736. // no links in root or course independent categories
  1737. if ($this->id == 0) {
  1738. } elseif (isset($studentId)) {
  1739. // 1 student $studentId
  1740. $links = LinkFactory::load(
  1741. null,
  1742. null,
  1743. null,
  1744. null,
  1745. $course_code,
  1746. $this->id,
  1747. api_is_allowed_to_edit() ? null : 1
  1748. );
  1749. } else {
  1750. // All students -> only for course/platform admin
  1751. $links = LinkFactory::load(
  1752. null,
  1753. null,
  1754. null,
  1755. null,
  1756. $course_code,
  1757. $this->id,
  1758. null
  1759. );
  1760. }
  1761. if ($recursive) {
  1762. $subcats = $this->get_subcategories(
  1763. $studentId,
  1764. $course_code,
  1765. $sessionId
  1766. );
  1767. if (!empty($subcats)) {
  1768. /** @var Category $subcat */
  1769. foreach ($subcats as $subcat) {
  1770. $sublinks = $subcat->get_links(
  1771. $studentId,
  1772. false,
  1773. $course_code,
  1774. $sessionId
  1775. );
  1776. $links = array_merge($links, $sublinks);
  1777. }
  1778. }
  1779. }
  1780. return $links;
  1781. }
  1782. /**
  1783. * Get all the categories from with the same given direct parent.
  1784. *
  1785. * @param int $catId Category parent ID
  1786. *
  1787. * @return array Array of Category objects
  1788. */
  1789. public function getCategories($catId)
  1790. {
  1791. $catId = (int) $catId;
  1792. $tblGradeCategories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1793. $sql = 'SELECT * FROM '.$tblGradeCategories.'
  1794. WHERE parent_id = '.$catId;
  1795. $result = Database::query($sql);
  1796. $categories = self::create_category_objects_from_sql_result($result);
  1797. return $categories;
  1798. }
  1799. /**
  1800. * Gets the type for the current object.
  1801. *
  1802. * @return string 'C' to represent "Category" object type
  1803. */
  1804. public function get_item_type()
  1805. {
  1806. return 'C';
  1807. }
  1808. /**
  1809. * @param array $skills
  1810. */
  1811. public function set_skills($skills)
  1812. {
  1813. $this->skills = $skills;
  1814. }
  1815. public function get_date()
  1816. {
  1817. return null;
  1818. }
  1819. /**
  1820. * @return string
  1821. */
  1822. public function get_icon_name()
  1823. {
  1824. return 'cat';
  1825. }
  1826. /**
  1827. * Find category by name.
  1828. *
  1829. * @param string $name_mask search string
  1830. *
  1831. * @return array category objects matching the search criterium
  1832. */
  1833. public function find_category($name_mask, $allcat)
  1834. {
  1835. $categories = [];
  1836. foreach ($allcat as $search_cat) {
  1837. if (!(strpos(strtolower($search_cat->get_name()), strtolower($name_mask)) === false)) {
  1838. $categories[] = $search_cat;
  1839. }
  1840. }
  1841. return $categories;
  1842. }
  1843. /**
  1844. * This function, locks a category , only one who can unlock it is
  1845. * the platform administrator.
  1846. *
  1847. * @param int locked 1 or unlocked 0
  1848. *
  1849. * @return bool|null
  1850. * */
  1851. public function lock($locked)
  1852. {
  1853. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1854. $sql = "UPDATE $table SET locked = '".intval($locked)."'
  1855. WHERE id='".intval($this->id)."'";
  1856. Database::query($sql);
  1857. }
  1858. /**
  1859. * @param $locked
  1860. */
  1861. public function lockAllItems($locked)
  1862. {
  1863. if (api_get_setting('gradebook_locking_enabled') == 'true') {
  1864. $this->lock($locked);
  1865. $evals_to_lock = $this->get_evaluations();
  1866. if (!empty($evals_to_lock)) {
  1867. foreach ($evals_to_lock as $item) {
  1868. $item->lock($locked);
  1869. }
  1870. }
  1871. $link_to_lock = $this->get_links();
  1872. if (!empty($link_to_lock)) {
  1873. foreach ($link_to_lock as $item) {
  1874. $item->lock($locked);
  1875. }
  1876. }
  1877. $event_type = LOG_GRADEBOOK_UNLOCKED;
  1878. if ($locked == 1) {
  1879. $event_type = LOG_GRADEBOOK_LOCKED;
  1880. }
  1881. Event::addEvent($event_type, LOG_GRADEBOOK_ID, $this->id);
  1882. }
  1883. }
  1884. /**
  1885. * Generates a certificate for this user if everything matches.
  1886. *
  1887. * @param int $category_id gradebook id
  1888. * @param int $user_id
  1889. * @param bool $sendNotification
  1890. *
  1891. * @return array
  1892. */
  1893. public static function generateUserCertificate(
  1894. $category_id,
  1895. $user_id,
  1896. $sendNotification = false
  1897. ) {
  1898. $user_id = (int) $user_id;
  1899. $category_id = (int) $category_id;
  1900. // Generating the total score for a course
  1901. $category = self::load(
  1902. $category_id,
  1903. null,
  1904. null,
  1905. null,
  1906. null,
  1907. null,
  1908. false
  1909. );
  1910. /** @var Category $category */
  1911. $category = $category[0];
  1912. if (empty($category)) {
  1913. return false;
  1914. }
  1915. $sessionId = $category->get_session_id();
  1916. $courseCode = $category->get_course_code();
  1917. $courseInfo = api_get_course_info($courseCode);
  1918. $courseId = $courseInfo['real_id'];
  1919. $userFinishedCourse = self::userFinishedCourse(
  1920. $user_id,
  1921. $category,
  1922. true
  1923. );
  1924. if (!$userFinishedCourse) {
  1925. return false;
  1926. }
  1927. $skillToolEnabled = Skill::hasAccessToUserSkill(
  1928. api_get_user_id(),
  1929. $user_id
  1930. );
  1931. $userHasSkills = false;
  1932. if ($skillToolEnabled) {
  1933. $skill = new Skill();
  1934. $skill->addSkillToUser(
  1935. $user_id,
  1936. $category,
  1937. $courseId,
  1938. $sessionId
  1939. );
  1940. $objSkillRelUser = new SkillRelUser();
  1941. $userSkills = $objSkillRelUser->getUserSkills(
  1942. $user_id,
  1943. $courseId,
  1944. $sessionId
  1945. );
  1946. $userHasSkills = !empty($userSkills);
  1947. }
  1948. // Block certification links depending gradebook configuration (generate certifications)
  1949. if (empty($category->getGenerateCertificates())) {
  1950. if ($userHasSkills) {
  1951. return [
  1952. 'badge_link' => Display::toolbarButton(
  1953. get_lang('ExportBadges'),
  1954. api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
  1955. 'external-link'
  1956. ),
  1957. ];
  1958. }
  1959. return false;
  1960. }
  1961. $scoretotal = $category->calc_score($user_id);
  1962. // Do not remove this the gradebook/lib/fe/gradebooktable.class.php
  1963. // file load this variable as a global
  1964. $scoredisplay = ScoreDisplay::instance();
  1965. $my_score_in_gradebook = $scoredisplay->display_score(
  1966. $scoretotal,
  1967. SCORE_SIMPLE
  1968. );
  1969. $my_certificate = GradebookUtils::get_certificate_by_user_id(
  1970. $category_id,
  1971. $user_id
  1972. );
  1973. if (empty($my_certificate)) {
  1974. GradebookUtils::registerUserInfoAboutCertificate(
  1975. $category_id,
  1976. $user_id,
  1977. $my_score_in_gradebook,
  1978. api_get_utc_datetime()
  1979. );
  1980. $my_certificate = GradebookUtils::get_certificate_by_user_id(
  1981. $category_id,
  1982. $user_id
  1983. );
  1984. }
  1985. $html = [];
  1986. if (!empty($my_certificate)) {
  1987. $certificate_obj = new Certificate(
  1988. $my_certificate['id'],
  1989. 0,
  1990. $sendNotification
  1991. );
  1992. $fileWasGenerated = $certificate_obj->isHtmlFileGenerated();
  1993. // Fix when using custom certificate BT#15937
  1994. if (api_get_plugin_setting('customcertificate', 'enable_plugin_customcertificate') === 'true') {
  1995. $infoCertificate = CustomCertificatePlugin::getCertificateData($my_certificate['id'], $user_id);
  1996. if (!empty($infoCertificate)) {
  1997. $fileWasGenerated = true;
  1998. }
  1999. }
  2000. if (!empty($fileWasGenerated)) {
  2001. $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$my_certificate['id'].'&user_id='.$user_id;
  2002. $certificates = Display::toolbarButton(
  2003. get_lang('DisplayCertificate'),
  2004. $url,
  2005. 'eye',
  2006. 'primary',
  2007. ['target' => '_blank']
  2008. );
  2009. $exportToPDF = Display::url(
  2010. Display::return_icon(
  2011. 'pdf.png',
  2012. get_lang('ExportToPDF'),
  2013. [],
  2014. ICON_SIZE_MEDIUM
  2015. ),
  2016. "$url&action=export"
  2017. );
  2018. $hideExportLink = api_get_setting('hide_certificate_export_link');
  2019. $hideExportLinkStudent = api_get_setting('hide_certificate_export_link_students');
  2020. if ($hideExportLink === 'true' || (api_is_student() && $hideExportLinkStudent === 'true')) {
  2021. $exportToPDF = null;
  2022. }
  2023. $html = [
  2024. 'certificate_link' => $certificates,
  2025. 'pdf_link' => $exportToPDF,
  2026. 'pdf_url' => "$url&action=export",
  2027. ];
  2028. }
  2029. if ($skillToolEnabled && $userHasSkills) {
  2030. $html['badge_link'] = Display::toolbarButton(
  2031. get_lang('ExportBadges'),
  2032. api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
  2033. 'external-link'
  2034. );
  2035. }
  2036. return $html;
  2037. }
  2038. }
  2039. /**
  2040. * @param int $catId
  2041. * @param array $userList
  2042. */
  2043. public static function generateCertificatesInUserList($catId, $userList)
  2044. {
  2045. if (!empty($userList)) {
  2046. foreach ($userList as $userInfo) {
  2047. self::generateUserCertificate($catId, $userInfo['user_id']);
  2048. }
  2049. }
  2050. }
  2051. /**
  2052. * @param int $catId
  2053. * @param array $userList
  2054. */
  2055. public static function exportAllCertificates(
  2056. $catId,
  2057. $userList = []
  2058. ) {
  2059. $orientation = api_get_configuration_value('certificate_pdf_orientation');
  2060. $params['orientation'] = 'landscape';
  2061. if (!empty($orientation)) {
  2062. $params['orientation'] = $orientation;
  2063. }
  2064. $params['left'] = 0;
  2065. $params['right'] = 0;
  2066. $params['top'] = 0;
  2067. $params['bottom'] = 0;
  2068. $page_format = $params['orientation'] == 'landscape' ? 'A4-L' : 'A4';
  2069. $pdf = new PDF($page_format, $params['orientation'], $params);
  2070. $certificate_list = GradebookUtils::get_list_users_certificates($catId, $userList);
  2071. $certificate_path_list = [];
  2072. if (!empty($certificate_list)) {
  2073. foreach ($certificate_list as $index => $value) {
  2074. $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
  2075. $value['user_id'],
  2076. $catId
  2077. );
  2078. foreach ($list_certificate as $value_certificate) {
  2079. $certificate_obj = new Certificate($value_certificate['id']);
  2080. $certificate_obj->generate(['hide_print_button' => true]);
  2081. if ($certificate_obj->isHtmlFileGenerated()) {
  2082. $certificate_path_list[] = $certificate_obj->html_file;
  2083. }
  2084. }
  2085. }
  2086. }
  2087. if (!empty($certificate_path_list)) {
  2088. // Print certificates (without the common header/footer/watermark
  2089. // stuff) and return as one multiple-pages PDF
  2090. $pdf->html_to_pdf(
  2091. $certificate_path_list,
  2092. get_lang('Certificates'),
  2093. null,
  2094. false,
  2095. false
  2096. );
  2097. }
  2098. }
  2099. /**
  2100. * @param int $catId
  2101. */
  2102. public static function deleteAllCertificates($catId)
  2103. {
  2104. $certificate_list = GradebookUtils::get_list_users_certificates($catId);
  2105. if (!empty($certificate_list)) {
  2106. foreach ($certificate_list as $index => $value) {
  2107. $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
  2108. $value['user_id'],
  2109. $catId
  2110. );
  2111. foreach ($list_certificate as $value_certificate) {
  2112. $certificate_obj = new Certificate($value_certificate['id']);
  2113. $certificate_obj->delete(true);
  2114. }
  2115. }
  2116. }
  2117. }
  2118. /**
  2119. * Check whether a user has finished a course by its gradebook.
  2120. *
  2121. * @param int $userId The user ID
  2122. * @param \Category $category Optional. The gradebook category.
  2123. * To check by the gradebook category
  2124. * @param bool $recalculateScore Whether recalculate the score
  2125. *
  2126. * @return bool
  2127. */
  2128. public static function userFinishedCourse(
  2129. $userId,
  2130. \Category $category,
  2131. $recalculateScore = false
  2132. ) {
  2133. if (empty($category)) {
  2134. return false;
  2135. }
  2136. $currentScore = self::getCurrentScore(
  2137. $userId,
  2138. $category,
  2139. $recalculateScore
  2140. );
  2141. $minCertificateScore = $category->getCertificateMinScore();
  2142. $passedCourse = $currentScore >= $minCertificateScore;
  2143. return $passedCourse;
  2144. }
  2145. /**
  2146. * Get the current score (as percentage) on a gradebook category for a user.
  2147. *
  2148. * @param int $userId The user id
  2149. * @param Category $category The gradebook category
  2150. * @param bool $recalculate
  2151. *
  2152. * @return float The score
  2153. */
  2154. public static function getCurrentScore(
  2155. $userId,
  2156. $category,
  2157. $recalculate = false
  2158. ) {
  2159. if (empty($category)) {
  2160. return 0;
  2161. }
  2162. if ($recalculate) {
  2163. return self::calculateCurrentScore(
  2164. $userId,
  2165. $category
  2166. );
  2167. }
  2168. $resultData = Database::select(
  2169. '*',
  2170. Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
  2171. [
  2172. 'where' => [
  2173. 'category_id = ? AND user_id = ?' => [$category->get_id(), $userId],
  2174. ],
  2175. 'order' => 'registered_at DESC',
  2176. 'limit' => '1',
  2177. ],
  2178. 'first'
  2179. );
  2180. if (empty($resultData)) {
  2181. return 0;
  2182. }
  2183. return $resultData['score'];
  2184. }
  2185. /**
  2186. * Register the current score for a user on a category gradebook.
  2187. *
  2188. * @param float $score The achieved score
  2189. * @param int $userId The user id
  2190. * @param int $categoryId The gradebook category
  2191. *
  2192. * @return int The insert id
  2193. */
  2194. public static function registerCurrentScore($score, $userId, $categoryId)
  2195. {
  2196. return Database::insert(
  2197. Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
  2198. [
  2199. 'category_id' => intval($categoryId),
  2200. 'user_id' => intval($userId),
  2201. 'score' => api_float_val($score),
  2202. 'registered_at' => api_get_utc_datetime(),
  2203. ]
  2204. );
  2205. }
  2206. /**
  2207. * @return array
  2208. */
  2209. public function getStudentList()
  2210. {
  2211. return $this->studentList;
  2212. }
  2213. /**
  2214. * @param array $list
  2215. */
  2216. public function setStudentList($list)
  2217. {
  2218. $this->studentList = $list;
  2219. }
  2220. /**
  2221. * @return string
  2222. */
  2223. public static function getUrl()
  2224. {
  2225. $url = Session::read('gradebook_dest');
  2226. if (empty($url)) {
  2227. // We guess the link
  2228. $courseInfo = api_get_course_info();
  2229. if (!empty($courseInfo)) {
  2230. return api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
  2231. } else {
  2232. return api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
  2233. }
  2234. }
  2235. return $url;
  2236. }
  2237. /**
  2238. * Destination is index.php or gradebook.php.
  2239. *
  2240. * @param string $url
  2241. */
  2242. public static function setUrl($url)
  2243. {
  2244. switch ($url) {
  2245. case 'gradebook.php':
  2246. $url = api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
  2247. break;
  2248. case 'index.php':
  2249. $url = api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
  2250. break;
  2251. }
  2252. Session::write('gradebook_dest', $url);
  2253. }
  2254. /**
  2255. * @return int
  2256. */
  2257. public function getGradeBooksToValidateInDependence()
  2258. {
  2259. return $this->gradeBooksToValidateInDependence;
  2260. }
  2261. /**
  2262. * @param int $value
  2263. *
  2264. * @return Category
  2265. */
  2266. public function setGradeBooksToValidateInDependence($value)
  2267. {
  2268. $this->gradeBooksToValidateInDependence = $value;
  2269. return $this;
  2270. }
  2271. /**
  2272. * Return HTML code with links to download and view certificate.
  2273. *
  2274. * @param array $certificate
  2275. *
  2276. * @return string
  2277. */
  2278. public static function getDownloadCertificateBlock(array $certificate)
  2279. {
  2280. if (!isset($certificate['pdf_url'])) {
  2281. return '';
  2282. }
  2283. $downloadLink = Display::toolbarButton(
  2284. get_lang('DownloadCertificatePdf'),
  2285. $certificate['pdf_url'],
  2286. 'file-pdf-o'
  2287. );
  2288. $viewLink = $certificate['certificate_link'];
  2289. return "
  2290. <div class='panel panel-default'>
  2291. <div class='panel-body'>
  2292. <h3 class='text-center'>".get_lang('NowDownloadYourCertificateClickHere')."</h3>
  2293. <div class='text-center'>$downloadLink $viewLink</div>
  2294. </div>
  2295. </div>
  2296. ";
  2297. }
  2298. /**
  2299. * Find a gradebook category by the certificate ID.
  2300. *
  2301. * @param int $id certificate id
  2302. *
  2303. * @throws \Doctrine\ORM\NonUniqueResultException
  2304. *
  2305. * @return Category|null
  2306. */
  2307. public static function findByCertificate($id)
  2308. {
  2309. $category = Database::getManager()
  2310. ->createQuery('SELECT c.catId FROM ChamiloCoreBundle:GradebookCertificate c WHERE c.id = :id')
  2311. ->setParameters(['id' => $id])
  2312. ->getOneOrNullResult();
  2313. if (empty($category)) {
  2314. return null;
  2315. }
  2316. $category = self::load($category['catId']);
  2317. if (empty($category)) {
  2318. return null;
  2319. }
  2320. return $category[0];
  2321. }
  2322. /**
  2323. * @param int $value
  2324. */
  2325. public function setDocumentId($value)
  2326. {
  2327. $this->documentId = (int) $value;
  2328. }
  2329. /**
  2330. * @return int
  2331. */
  2332. public function getDocumentId()
  2333. {
  2334. return $this->documentId;
  2335. }
  2336. /**
  2337. * @return Category
  2338. */
  2339. private static function create_root_category()
  2340. {
  2341. $cat = new Category();
  2342. $cat->set_id(0);
  2343. $cat->set_name(get_lang('RootCat'));
  2344. $cat->set_description(null);
  2345. $cat->set_user_id(0);
  2346. $cat->set_course_code(null);
  2347. $cat->set_parent_id(null);
  2348. $cat->set_weight(0);
  2349. $cat->set_visible(1);
  2350. $cat->setGenerateCertificates(0);
  2351. $cat->setIsRequirement(false);
  2352. return $cat;
  2353. }
  2354. /**
  2355. * @param Doctrine\DBAL\Driver\Statement|null $result
  2356. *
  2357. * @return array
  2358. */
  2359. private static function create_category_objects_from_sql_result($result)
  2360. {
  2361. $categories = [];
  2362. $allow = api_get_configuration_value('allow_gradebook_stats');
  2363. if ($allow) {
  2364. $em = Database::getManager();
  2365. $repo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
  2366. }
  2367. while ($data = Database::fetch_array($result)) {
  2368. $cat = new Category();
  2369. $cat->set_id($data['id']);
  2370. $cat->set_name($data['name']);
  2371. $cat->set_description($data['description']);
  2372. $cat->set_user_id($data['user_id']);
  2373. $cat->set_course_code($data['course_code']);
  2374. $cat->set_parent_id($data['parent_id']);
  2375. $cat->set_weight($data['weight']);
  2376. $cat->set_visible($data['visible']);
  2377. $cat->set_session_id($data['session_id']);
  2378. $cat->set_certificate_min_score($data['certif_min_score']);
  2379. $cat->set_grade_model_id($data['grade_model_id']);
  2380. $cat->set_locked($data['locked']);
  2381. $cat->setGenerateCertificates($data['generate_certificates']);
  2382. $cat->setIsRequirement($data['is_requirement']);
  2383. $cat->setCourseListDependency(isset($data['depends']) ? $data['depends'] : []);
  2384. $cat->setMinimumToValidate(isset($data['minimum_to_validate']) ? $data['minimum_to_validate'] : null);
  2385. $cat->setGradeBooksToValidateInDependence(isset($data['gradebooks_to_validate_in_dependence']) ? $data['gradebooks_to_validate_in_dependence'] : null);
  2386. $cat->setDocumentId($data['document_id']);
  2387. if ($allow) {
  2388. $cat->entity = $repo->find($data['id']);
  2389. }
  2390. $categories[] = $cat;
  2391. }
  2392. return $categories;
  2393. }
  2394. /**
  2395. * Internal function used by get_target_categories().
  2396. *
  2397. * @param array $targets
  2398. * @param int $level
  2399. * @param int $catid
  2400. *
  2401. * @return array
  2402. */
  2403. private function addTargetSubcategories($targets, $level, $catid)
  2404. {
  2405. $subcats = self::load(null, null, null, $catid);
  2406. foreach ($subcats as $cat) {
  2407. if ($this->can_be_moved_to_cat($cat)) {
  2408. $targets[] = [
  2409. $cat->get_id(),
  2410. $cat->get_name(),
  2411. $level + 1,
  2412. ];
  2413. $targets = $this->addTargetSubcategories(
  2414. $targets,
  2415. $level + 1,
  2416. $cat->get_id()
  2417. );
  2418. }
  2419. }
  2420. return $targets;
  2421. }
  2422. /**
  2423. * Internal function used by get_target_categories() and addTargetSubcategories()
  2424. * Can this category be moved to the given category ?
  2425. * Impossible when origin and target are the same... children won't be processed
  2426. * either. (a category can't be moved to one of its own children).
  2427. */
  2428. private function can_be_moved_to_cat($cat)
  2429. {
  2430. return $cat->get_id() != $this->get_id();
  2431. }
  2432. /**
  2433. * Internal function used by move_to_cat().
  2434. */
  2435. private function applyCourseCodeToChildren()
  2436. {
  2437. $cats = self::load(null, null, null, $this->id, null);
  2438. $evals = Evaluation::load(null, null, null, $this->id, null);
  2439. $links = LinkFactory::load(
  2440. null,
  2441. null,
  2442. null,
  2443. null,
  2444. null,
  2445. $this->id,
  2446. null
  2447. );
  2448. /** @var Category $cat */
  2449. foreach ($cats as $cat) {
  2450. $cat->set_course_code($this->get_course_code());
  2451. $cat->save();
  2452. $cat->applyCourseCodeToChildren();
  2453. }
  2454. foreach ($evals as $eval) {
  2455. $eval->set_course_code($this->get_course_code());
  2456. $eval->save();
  2457. }
  2458. foreach ($links as $link) {
  2459. $link->delete();
  2460. }
  2461. }
  2462. /**
  2463. * Internal function used by get_tree().
  2464. *
  2465. * @param int $level
  2466. * @param int|null $visible
  2467. *
  2468. * @return array
  2469. */
  2470. private function add_subtree($targets, $level, $catid, $visible)
  2471. {
  2472. $subcats = self::load(null, null, null, $catid, $visible);
  2473. if (!empty($subcats)) {
  2474. foreach ($subcats as $cat) {
  2475. $targets[] = [
  2476. $cat->get_id(),
  2477. $cat->get_name(),
  2478. $level + 1,
  2479. ];
  2480. $targets = self::add_subtree(
  2481. $targets,
  2482. $level + 1,
  2483. $cat->get_id(),
  2484. $visible
  2485. );
  2486. }
  2487. }
  2488. return $targets;
  2489. }
  2490. /**
  2491. * Calculate the current score on a gradebook category for a user.
  2492. *
  2493. * @param int $userId The user id
  2494. * @param Category $category The gradebook category
  2495. *
  2496. * @return float The score
  2497. */
  2498. private static function calculateCurrentScore(
  2499. $userId,
  2500. $category
  2501. ) {
  2502. if (empty($category)) {
  2503. return 0;
  2504. }
  2505. $courseEvaluations = $category->get_evaluations(
  2506. $userId,
  2507. true
  2508. );
  2509. $courseLinks = $category->get_links($userId, true);
  2510. $evaluationsAndLinks = array_merge($courseEvaluations, $courseLinks);
  2511. $categoryScore = 0;
  2512. for ($i = 0; $i < count($evaluationsAndLinks); $i++) {
  2513. $item = $evaluationsAndLinks[$i];
  2514. $score = $item->calc_score($userId);
  2515. $itemValue = 0;
  2516. if (!empty($score)) {
  2517. $divider = $score[1] == 0 ? 1 : $score[1];
  2518. $itemValue = $score[0] / $divider * $item->get_weight();
  2519. }
  2520. $categoryScore += $itemValue;
  2521. }
  2522. return api_float_val($categoryScore);
  2523. }
  2524. }