category.class.php 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763
  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. $students[$stud_id] = $linkres[0];
  870. $linkweight = $link->get_weight();
  871. $link_res_denom = $linkres[1] == 0 ? 1 : $linkres[1];
  872. $count++;
  873. $weightsum += $linkweight;
  874. $ressum += $linkres[0] / $link_res_denom * $linkweight;
  875. } else {
  876. // Adding if result does not exists
  877. if ($link->get_weight() != 0) {
  878. $linkweight = $link->get_weight();
  879. $weightsum += $linkweight;
  880. }
  881. }
  882. }
  883. }
  884. } else {
  885. if (!empty($course_code)) {
  886. $cats = $this->get_subcategories(
  887. null,
  888. $course_code,
  889. $session_id
  890. );
  891. $evals = $this->get_evaluations(null, false, $course_code);
  892. $links = $this->get_links(null, false, $course_code);
  893. } else {
  894. $cats = $this->get_subcategories(null);
  895. $evals = $this->get_evaluations(null);
  896. $links = $this->get_links(null);
  897. }
  898. // Calculate score
  899. $count = 0;
  900. $ressum = 0;
  901. $weightsum = 0;
  902. $bestResult = 0;
  903. if (!empty($cats)) {
  904. /** @var Category $cat */
  905. foreach ($cats as $cat) {
  906. $cat->setStudentList($this->getStudentList());
  907. $score = $cat->calc_score(
  908. null,
  909. $type,
  910. $course_code,
  911. $session_id
  912. );
  913. $catweight = 0;
  914. if ($cat->get_weight() != 0) {
  915. $catweight = $cat->get_weight();
  916. $weightsum += $catweight;
  917. }
  918. if (isset($score) && !empty($score[1]) && !empty($catweight)) {
  919. $ressum += $score[0] / $score[1] * $catweight;
  920. if ($ressum > $bestResult) {
  921. $bestResult = $ressum;
  922. }
  923. }
  924. }
  925. }
  926. if (!empty($evals)) {
  927. /** @var Evaluation $eval */
  928. foreach ($evals as $eval) {
  929. $evalres = $eval->calc_score(null, $type);
  930. $eval->setStudentList($this->getStudentList());
  931. if (isset($evalres) && $eval->get_weight() != 0) {
  932. $evalweight = $eval->get_weight();
  933. $weightsum += $evalweight;
  934. $count++;
  935. if (!empty($evalres[1])) {
  936. $ressum += $evalres[0] / $evalres[1] * $evalweight;
  937. }
  938. if ($ressum > $bestResult) {
  939. $bestResult = $ressum;
  940. }
  941. } else {
  942. if ($eval->get_weight() != 0) {
  943. $evalweight = $eval->get_weight();
  944. $weightsum += $evalweight;
  945. }
  946. }
  947. }
  948. }
  949. if (!empty($links)) {
  950. /** @var EvalLink|ExerciseLink $link */
  951. foreach ($links as $link) {
  952. $link->setStudentList($this->getStudentList());
  953. if ($session_id) {
  954. $link->set_session_id($session_id);
  955. }
  956. $linkres = $link->calc_score($stud_id, $type);
  957. if (!empty($linkres) && $link->get_weight() != 0) {
  958. $students[$stud_id] = $linkres[0];
  959. $linkweight = $link->get_weight();
  960. $link_res_denom = $linkres[1] == 0 ? 1 : $linkres[1];
  961. $count++;
  962. $weightsum += $linkweight;
  963. $ressum += $linkres[0] / $link_res_denom * $linkweight;
  964. if ($ressum > $bestResult) {
  965. $bestResult = $ressum;
  966. }
  967. } else {
  968. // Adding if result does not exists
  969. if ($link->get_weight() != 0) {
  970. $linkweight = $link->get_weight();
  971. $weightsum += $linkweight;
  972. }
  973. }
  974. }
  975. }
  976. }
  977. switch ($type) {
  978. case 'best':
  979. if (empty($bestResult)) {
  980. if ($cacheAvailable) {
  981. $cacheDriver->save($key, null);
  982. }
  983. return null;
  984. }
  985. if ($cacheAvailable) {
  986. $cacheDriver->save($key, [$bestResult, $weightsum]);
  987. }
  988. return [$bestResult, $weightsum];
  989. break;
  990. case 'average':
  991. if (empty($ressum)) {
  992. if ($cacheAvailable) {
  993. $cacheDriver->save($key, null);
  994. }
  995. return null;
  996. }
  997. if ($cacheAvailable) {
  998. $cacheDriver->save($key, [$ressum, $weightsum]);
  999. }
  1000. return [$ressum, $weightsum];
  1001. break;
  1002. case 'ranking':
  1003. // category ranking is calculated in gradebook_data_generator.class.php
  1004. // function get_data
  1005. return null;
  1006. return AbstractLink::getCurrentUserRanking($stud_id, []);
  1007. break;
  1008. default:
  1009. if ($cacheAvailable) {
  1010. $cacheDriver->save($key, [$ressum, $weightsum]);
  1011. }
  1012. return [$ressum, $weightsum];
  1013. break;
  1014. }
  1015. }
  1016. /**
  1017. * Delete this category and every subcategory, evaluation and result inside.
  1018. */
  1019. public function delete_all()
  1020. {
  1021. $cats = self::load(null, null, $this->course_code, $this->id, null);
  1022. $evals = Evaluation::load(
  1023. null,
  1024. null,
  1025. $this->course_code,
  1026. $this->id,
  1027. null
  1028. );
  1029. $links = LinkFactory::load(
  1030. null,
  1031. null,
  1032. null,
  1033. null,
  1034. $this->course_code,
  1035. $this->id,
  1036. null
  1037. );
  1038. if (!empty($cats)) {
  1039. /** @var Category $cat */
  1040. foreach ($cats as $cat) {
  1041. $cat->delete_all();
  1042. $cat->delete();
  1043. }
  1044. }
  1045. if (!empty($evals)) {
  1046. /** @var Evaluation $eval */
  1047. foreach ($evals as $eval) {
  1048. $eval->delete_with_results();
  1049. }
  1050. }
  1051. if (!empty($links)) {
  1052. /** @var AbstractLink $link */
  1053. foreach ($links as $link) {
  1054. $link->delete();
  1055. }
  1056. }
  1057. $this->delete();
  1058. }
  1059. /**
  1060. * Return array of Category objects where a student is subscribed to.
  1061. *
  1062. * @param int $stud_id
  1063. * @param string $course_code
  1064. * @param int $session_id
  1065. *
  1066. * @return array
  1067. */
  1068. public function get_root_categories_for_student(
  1069. $stud_id,
  1070. $course_code = null,
  1071. $session_id = null
  1072. ) {
  1073. $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1074. $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
  1075. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1076. $course_code = Database::escape_string($course_code);
  1077. $session_id = (int) $session_id;
  1078. $sql = "SELECT * FROM $table WHERE parent_id = 0";
  1079. if (!api_is_allowed_to_edit()) {
  1080. $sql .= ' AND visible = 1';
  1081. //proceed with checks on optional parameters course & session
  1082. if (!empty($course_code)) {
  1083. // TODO: considering it highly improbable that a user would get here
  1084. // if he doesn't have the rights to view this course and this
  1085. // session, we don't check his registration to these, but this
  1086. // could be an improvement
  1087. if (!empty($session_id)) {
  1088. $sql .= " AND course_code = '".$course_code."' AND session_id = ".$session_id;
  1089. } else {
  1090. $sql .= " AND course_code = '".$course_code."' AND session_id is null OR session_id=0";
  1091. }
  1092. } else {
  1093. //no optional parameter, proceed as usual
  1094. $sql .= ' AND course_code in
  1095. (
  1096. SELECT c.code
  1097. FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
  1098. ON (cu.c_id = c.id)
  1099. WHERE cu.user_id = '.intval($stud_id).'
  1100. AND cu.status = '.STUDENT.'
  1101. )';
  1102. }
  1103. } elseif (api_is_allowed_to_edit() && !api_is_platform_admin()) {
  1104. //proceed with checks on optional parameters course & session
  1105. if (!empty($course_code)) {
  1106. // TODO: considering it highly improbable that a user would get here
  1107. // if he doesn't have the rights to view this course and this
  1108. // session, we don't check his registration to these, but this
  1109. // could be an improvement
  1110. $sql .= " AND course_code = '".$course_code."'";
  1111. if (!empty($session_id)) {
  1112. $sql .= " AND session_id = ".$session_id;
  1113. } else {
  1114. $sql .= 'AND session_id IS NULL OR session_id = 0';
  1115. }
  1116. } else {
  1117. $sql .= ' AND course_code IN
  1118. (
  1119. SELECT c.code
  1120. FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
  1121. ON (cu.c_id = c.id)
  1122. WHERE
  1123. cu.user_id = '.api_get_user_id().' AND
  1124. cu.status = '.COURSEMANAGER.'
  1125. )';
  1126. }
  1127. } elseif (api_is_platform_admin()) {
  1128. if (isset($session_id) && $session_id != 0) {
  1129. $sql .= ' AND session_id='.$session_id;
  1130. } else {
  1131. $sql .= ' AND coalesce(session_id,0)=0';
  1132. }
  1133. }
  1134. $result = Database::query($sql);
  1135. $cats = self::create_category_objects_from_sql_result($result);
  1136. // course independent categories
  1137. if (empty($course_code)) {
  1138. $cats = $this->getIndependentCategoriesWithStudentResult(
  1139. 0,
  1140. $stud_id,
  1141. $cats
  1142. );
  1143. }
  1144. return $cats;
  1145. }
  1146. /**
  1147. * Return array of Category objects where a teacher is admin for.
  1148. *
  1149. * @param int $user_id (to return everything, use 'null' here)
  1150. * @param string $course_code (optional)
  1151. * @param int $session_id (optional)
  1152. *
  1153. * @return array
  1154. */
  1155. public function get_root_categories_for_teacher(
  1156. $user_id,
  1157. $course_code = null,
  1158. $session_id = null
  1159. ) {
  1160. if ($user_id == null) {
  1161. return self::load(null, null, $course_code, 0, null, $session_id);
  1162. }
  1163. $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
  1164. $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1165. $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1166. $sql = 'SELECT * FROM '.$tbl_grade_categories.'
  1167. WHERE parent_id = 0 ';
  1168. if (!empty($course_code)) {
  1169. $sql .= " AND course_code = '".Database::escape_string($course_code)."' ";
  1170. if (!empty($session_id)) {
  1171. $sql .= " AND session_id = ".(int) $session_id;
  1172. }
  1173. } else {
  1174. $sql .= ' AND course_code in
  1175. (
  1176. SELECT c.code
  1177. FROM '.$main_course_user_table.' cu
  1178. INNER JOIN '.$courseTable.' c
  1179. ON (cu.c_id = c.id)
  1180. WHERE user_id = '.intval($user_id).'
  1181. )';
  1182. }
  1183. $result = Database::query($sql);
  1184. $cats = self::create_category_objects_from_sql_result($result);
  1185. // course independent categories
  1186. if (isset($course_code)) {
  1187. $indcats = self::load(
  1188. null,
  1189. $user_id,
  1190. $course_code,
  1191. 0,
  1192. null,
  1193. $session_id
  1194. );
  1195. $cats = array_merge($cats, $indcats);
  1196. }
  1197. return $cats;
  1198. }
  1199. /**
  1200. * Can this category be moved to somewhere else ?
  1201. * The root and courses cannot be moved.
  1202. *
  1203. * @return bool
  1204. */
  1205. public function is_movable()
  1206. {
  1207. return !(!isset($this->id) || $this->id == 0 || $this->is_course());
  1208. }
  1209. /**
  1210. * Generate an array of possible categories where this category can be moved to.
  1211. * Notice: its own parent will be included in the list: it's up to the frontend
  1212. * to disable this element.
  1213. *
  1214. * @return array 2-dimensional array - every element contains 3 subelements (id, name, level)
  1215. */
  1216. public function get_target_categories()
  1217. {
  1218. // the root or a course -> not movable
  1219. if (!$this->is_movable()) {
  1220. return null;
  1221. } else {
  1222. // otherwise:
  1223. // - course independent category
  1224. // -> movable to root or other independent categories
  1225. // - category inside a course
  1226. // -> movable to root, independent categories or categories inside the course
  1227. $user = api_is_platform_admin() ? null : api_get_user_id();
  1228. $targets = [];
  1229. $level = 0;
  1230. $root = [0, get_lang('RootCat'), $level];
  1231. $targets[] = $root;
  1232. if (isset($this->course_code) && !empty($this->course_code)) {
  1233. $crscats = self::load(null, null, $this->course_code, 0);
  1234. foreach ($crscats as $cat) {
  1235. if ($this->can_be_moved_to_cat($cat)) {
  1236. $targets[] = [
  1237. $cat->get_id(),
  1238. $cat->get_name(),
  1239. $level + 1,
  1240. ];
  1241. $targets = $this->addTargetSubcategories(
  1242. $targets,
  1243. $level + 1,
  1244. $cat->get_id()
  1245. );
  1246. }
  1247. }
  1248. }
  1249. $indcats = self::load(null, $user, 0, 0);
  1250. foreach ($indcats as $cat) {
  1251. if ($this->can_be_moved_to_cat($cat)) {
  1252. $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
  1253. $targets = $this->addTargetSubcategories(
  1254. $targets,
  1255. $level + 1,
  1256. $cat->get_id()
  1257. );
  1258. }
  1259. }
  1260. return $targets;
  1261. }
  1262. }
  1263. /**
  1264. * Move this category to the given category.
  1265. * If this category moves from inside a course to outside,
  1266. * its course code must be changed, as well as the course code
  1267. * of all underlying categories and evaluations. All links will
  1268. * be deleted as well !
  1269. */
  1270. public function move_to_cat($cat)
  1271. {
  1272. $this->set_parent_id($cat->get_id());
  1273. if ($this->get_course_code() != $cat->get_course_code()) {
  1274. $this->set_course_code($cat->get_course_code());
  1275. $this->applyCourseCodeToChildren();
  1276. }
  1277. $this->save();
  1278. }
  1279. /**
  1280. * Generate an array of all categories the user can navigate to.
  1281. */
  1282. public function get_tree()
  1283. {
  1284. $targets = [];
  1285. $level = 0;
  1286. $root = [0, get_lang('RootCat'), $level];
  1287. $targets[] = $root;
  1288. // course or platform admin
  1289. if (api_is_allowed_to_edit()) {
  1290. $user = api_is_platform_admin() ? null : api_get_user_id();
  1291. $cats = self::get_root_categories_for_teacher($user);
  1292. foreach ($cats as $cat) {
  1293. $targets[] = [
  1294. $cat->get_id(),
  1295. $cat->get_name(),
  1296. $level + 1,
  1297. ];
  1298. $targets = $this->add_subtree(
  1299. $targets,
  1300. $level + 1,
  1301. $cat->get_id(),
  1302. null
  1303. );
  1304. }
  1305. } else {
  1306. // student
  1307. $cats = $this->get_root_categories_for_student(api_get_user_id());
  1308. foreach ($cats as $cat) {
  1309. $targets[] = [
  1310. $cat->get_id(),
  1311. $cat->get_name(),
  1312. $level + 1,
  1313. ];
  1314. $targets = $this->add_subtree(
  1315. $targets,
  1316. $level + 1,
  1317. $cat->get_id(),
  1318. 1
  1319. );
  1320. }
  1321. }
  1322. return $targets;
  1323. }
  1324. /**
  1325. * Generate an array of courses that a teacher hasn't created a category for.
  1326. *
  1327. * @param int $user_id
  1328. *
  1329. * @return array 2-dimensional array - every element contains 2 subelements (code, title)
  1330. */
  1331. public function get_not_created_course_categories($user_id)
  1332. {
  1333. $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
  1334. $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1335. $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1336. $user_id = (int) $user_id;
  1337. $sql = 'SELECT DISTINCT(code), title
  1338. FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
  1339. WHERE
  1340. cc.id = cu.c_id AND
  1341. cu.status = '.COURSEMANAGER;
  1342. if (!api_is_platform_admin()) {
  1343. $sql .= ' AND cu.user_id = '.$user_id;
  1344. }
  1345. $sql .= ' AND cc.code NOT IN
  1346. (
  1347. SELECT course_code FROM '.$tbl_grade_categories.'
  1348. WHERE
  1349. parent_id = 0 AND
  1350. course_code IS NOT NULL
  1351. )';
  1352. $result = Database::query($sql);
  1353. $cats = [];
  1354. while ($data = Database::fetch_array($result)) {
  1355. $cats[] = [$data['code'], $data['title']];
  1356. }
  1357. return $cats;
  1358. }
  1359. /**
  1360. * Generate an array of all courses that a teacher is admin of.
  1361. *
  1362. * @param int $user_id
  1363. *
  1364. * @return array 2-dimensional array - every element contains 2 subelements (code, title)
  1365. */
  1366. public function get_all_courses($user_id)
  1367. {
  1368. $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
  1369. $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  1370. $sql = 'SELECT DISTINCT(code), title
  1371. FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
  1372. WHERE cc.id = cu.c_id AND cu.status = '.COURSEMANAGER;
  1373. if (!api_is_platform_admin()) {
  1374. $sql .= ' AND cu.user_id = '.intval($user_id);
  1375. }
  1376. $result = Database::query($sql);
  1377. $cats = [];
  1378. while ($data = Database::fetch_array($result)) {
  1379. $cats[] = [$data['code'], $data['title']];
  1380. }
  1381. return $cats;
  1382. }
  1383. /**
  1384. * Apply the same visibility to every subcategory, evaluation and link.
  1385. */
  1386. public function apply_visibility_to_children()
  1387. {
  1388. $cats = self::load(null, null, null, $this->id, null);
  1389. $evals = Evaluation::load(null, null, null, $this->id, null);
  1390. $links = LinkFactory::load(
  1391. null,
  1392. null,
  1393. null,
  1394. null,
  1395. null,
  1396. $this->id,
  1397. null
  1398. );
  1399. if (!empty($cats)) {
  1400. foreach ($cats as $cat) {
  1401. $cat->set_visible($this->is_visible());
  1402. $cat->save();
  1403. $cat->apply_visibility_to_children();
  1404. }
  1405. }
  1406. if (!empty($evals)) {
  1407. foreach ($evals as $eval) {
  1408. $eval->set_visible($this->is_visible());
  1409. $eval->save();
  1410. }
  1411. }
  1412. if (!empty($links)) {
  1413. foreach ($links as $link) {
  1414. $link->set_visible($this->is_visible());
  1415. $link->save();
  1416. }
  1417. }
  1418. }
  1419. /**
  1420. * Check if a category contains evaluations with a result for a given student.
  1421. *
  1422. * @param int $studentId
  1423. *
  1424. * @return bool
  1425. */
  1426. public function hasEvaluationsWithStudentResults($studentId)
  1427. {
  1428. $evals = Evaluation::get_evaluations_with_result_for_student(
  1429. $this->id,
  1430. $studentId
  1431. );
  1432. if (count($evals) != 0) {
  1433. return true;
  1434. } else {
  1435. $cats = self::load(
  1436. null,
  1437. null,
  1438. null,
  1439. $this->id,
  1440. api_is_allowed_to_edit() ? null : 1
  1441. );
  1442. /** @var Category $cat */
  1443. foreach ($cats as $cat) {
  1444. if ($cat->hasEvaluationsWithStudentResults($studentId)) {
  1445. return true;
  1446. }
  1447. }
  1448. return false;
  1449. }
  1450. }
  1451. /**
  1452. * Retrieve all categories inside a course independent category
  1453. * that should be visible to a student.
  1454. *
  1455. * @param int $categoryId parent category
  1456. * @param int $studentId
  1457. * @param array $cats optional: if defined, the categories will be added to this array
  1458. *
  1459. * @return array
  1460. */
  1461. public function getIndependentCategoriesWithStudentResult(
  1462. $categoryId,
  1463. $studentId,
  1464. $cats = []
  1465. ) {
  1466. $creator = api_is_allowed_to_edit() && !api_is_platform_admin() ? api_get_user_id() : null;
  1467. $categories = self::load(
  1468. null,
  1469. $creator,
  1470. '0',
  1471. $categoryId,
  1472. api_is_allowed_to_edit() ? null : 1
  1473. );
  1474. if (!empty($categories)) {
  1475. /** @var Category $category */
  1476. foreach ($categories as $category) {
  1477. if ($category->hasEvaluationsWithStudentResults($studentId)) {
  1478. $cats[] = $category;
  1479. }
  1480. }
  1481. }
  1482. return $cats;
  1483. }
  1484. /**
  1485. * Return the session id (in any case, even if it's null or 0).
  1486. *
  1487. * @return int Session id (can be null)
  1488. */
  1489. public function get_session_id()
  1490. {
  1491. return $this->session_id;
  1492. }
  1493. /**
  1494. * Get appropriate subcategories visible for the user (and optionally the course and session).
  1495. *
  1496. * @param int $studentId student id (default: all students)
  1497. * @param string $course_code Course code (optional)
  1498. * @param int $session_id Session ID (optional)
  1499. * @param bool $order
  1500. *
  1501. * @return array Array of subcategories
  1502. */
  1503. public function get_subcategories(
  1504. $studentId = null,
  1505. $course_code = null,
  1506. $session_id = null,
  1507. $order = null
  1508. ) {
  1509. // 1 student
  1510. if (isset($studentId)) {
  1511. // Special case: this is the root
  1512. if ($this->id == 0) {
  1513. return $this->get_root_categories_for_student($studentId, $course_code, $session_id);
  1514. } else {
  1515. return self::load(
  1516. null,
  1517. null,
  1518. $course_code,
  1519. $this->id,
  1520. api_is_allowed_to_edit() ? null : 1,
  1521. $session_id,
  1522. $order
  1523. );
  1524. }
  1525. } else {
  1526. // All students
  1527. // Course admin
  1528. if (api_is_allowed_to_edit() && !api_is_platform_admin()) {
  1529. // root
  1530. if ($this->id == 0) {
  1531. // inside a course
  1532. return $this->get_root_categories_for_teacher(
  1533. api_get_user_id(),
  1534. $course_code,
  1535. $session_id,
  1536. false
  1537. );
  1538. } elseif (!empty($this->course_code)) {
  1539. return self::load(
  1540. null,
  1541. null,
  1542. $this->course_code,
  1543. $this->id,
  1544. null,
  1545. $session_id,
  1546. $order
  1547. );
  1548. } elseif (!empty($course_code)) {
  1549. // course independent
  1550. return self::load(
  1551. null,
  1552. null,
  1553. $course_code,
  1554. $this->id,
  1555. null,
  1556. $session_id,
  1557. $order
  1558. );
  1559. } else {
  1560. return self::load(
  1561. null,
  1562. api_get_user_id(),
  1563. 0,
  1564. $this->id,
  1565. null
  1566. );
  1567. }
  1568. } elseif (api_is_platform_admin()) {
  1569. // platform admin
  1570. // we explicitly avoid listing subcats from another session
  1571. return self::load(
  1572. null,
  1573. null,
  1574. $course_code,
  1575. $this->id,
  1576. null,
  1577. $session_id,
  1578. $order
  1579. );
  1580. }
  1581. }
  1582. return [];
  1583. }
  1584. /**
  1585. * Get appropriate evaluations visible for the user.
  1586. *
  1587. * @param int $studentId student id (default: all students)
  1588. * @param bool $recursive process subcategories (default: no recursion)
  1589. * @param string $course_code
  1590. * @param int $sessionId
  1591. *
  1592. * @return array
  1593. */
  1594. public function get_evaluations(
  1595. $studentId = null,
  1596. $recursive = false,
  1597. $course_code = '',
  1598. $sessionId = 0
  1599. ) {
  1600. $evals = [];
  1601. $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
  1602. $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
  1603. // 1 student
  1604. if (isset($studentId) && !empty($studentId)) {
  1605. // Special case: this is the root
  1606. if ($this->id == 0) {
  1607. $evals = Evaluation::get_evaluations_with_result_for_student(
  1608. 0,
  1609. $studentId
  1610. );
  1611. } else {
  1612. $evals = Evaluation::load(
  1613. null,
  1614. null,
  1615. $course_code,
  1616. $this->id,
  1617. api_is_allowed_to_edit() ? null : 1
  1618. );
  1619. }
  1620. } else {
  1621. // All students
  1622. // course admin
  1623. if ((api_is_allowed_to_edit() || api_is_drh() || api_is_session_admin()) &&
  1624. !api_is_platform_admin()
  1625. ) {
  1626. // root
  1627. if ($this->id == 0) {
  1628. $evals = Evaluation::load(
  1629. null,
  1630. api_get_user_id(),
  1631. null,
  1632. $this->id,
  1633. null
  1634. );
  1635. } elseif (isset($this->course_code) &&
  1636. !empty($this->course_code)
  1637. ) {
  1638. // inside a course
  1639. $evals = Evaluation::load(
  1640. null,
  1641. null,
  1642. $course_code,
  1643. $this->id,
  1644. null
  1645. );
  1646. } else {
  1647. // course independent
  1648. $evals = Evaluation::load(
  1649. null,
  1650. api_get_user_id(),
  1651. null,
  1652. $this->id,
  1653. null
  1654. );
  1655. }
  1656. } else {
  1657. $evals = Evaluation::load(
  1658. null,
  1659. null,
  1660. $course_code,
  1661. $this->id,
  1662. null
  1663. );
  1664. }
  1665. }
  1666. if ($recursive) {
  1667. $subcats = $this->get_subcategories(
  1668. $studentId,
  1669. $course_code,
  1670. $sessionId
  1671. );
  1672. if (!empty($subcats)) {
  1673. foreach ($subcats as $subcat) {
  1674. $subevals = $subcat->get_evaluations(
  1675. $studentId,
  1676. true,
  1677. $course_code
  1678. );
  1679. $evals = array_merge($evals, $subevals);
  1680. }
  1681. }
  1682. }
  1683. return $evals;
  1684. }
  1685. /**
  1686. * Get appropriate links visible for the user.
  1687. *
  1688. * @param int $studentId student id (default: all students)
  1689. * @param bool $recursive process subcategories (default: no recursion)
  1690. * @param string $course_code
  1691. * @param int $sessionId
  1692. *
  1693. * @return array
  1694. */
  1695. public function get_links(
  1696. $studentId = null,
  1697. $recursive = false,
  1698. $course_code = '',
  1699. $sessionId = 0
  1700. ) {
  1701. $links = [];
  1702. $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
  1703. $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
  1704. // no links in root or course independent categories
  1705. if ($this->id == 0) {
  1706. } elseif (isset($studentId)) {
  1707. // 1 student $studentId
  1708. $links = LinkFactory::load(
  1709. null,
  1710. null,
  1711. null,
  1712. null,
  1713. $course_code,
  1714. $this->id,
  1715. api_is_allowed_to_edit() ? null : 1
  1716. );
  1717. } else {
  1718. // All students -> only for course/platform admin
  1719. $links = LinkFactory::load(
  1720. null,
  1721. null,
  1722. null,
  1723. null,
  1724. $course_code,
  1725. $this->id,
  1726. null
  1727. );
  1728. }
  1729. if ($recursive) {
  1730. $subcats = $this->get_subcategories(
  1731. $studentId,
  1732. $course_code,
  1733. $sessionId
  1734. );
  1735. if (!empty($subcats)) {
  1736. /** @var Category $subcat */
  1737. foreach ($subcats as $subcat) {
  1738. $sublinks = $subcat->get_links(
  1739. $studentId,
  1740. false,
  1741. $course_code,
  1742. $sessionId
  1743. );
  1744. $links = array_merge($links, $sublinks);
  1745. }
  1746. }
  1747. }
  1748. return $links;
  1749. }
  1750. /**
  1751. * Get all the categories from with the same given direct parent.
  1752. *
  1753. * @param int $catId Category parent ID
  1754. *
  1755. * @return array Array of Category objects
  1756. */
  1757. public function getCategories($catId)
  1758. {
  1759. $catId = (int) $catId;
  1760. $tblGradeCategories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1761. $sql = 'SELECT * FROM '.$tblGradeCategories.'
  1762. WHERE parent_id = '.$catId;
  1763. $result = Database::query($sql);
  1764. $categories = self::create_category_objects_from_sql_result($result);
  1765. return $categories;
  1766. }
  1767. /**
  1768. * Gets the type for the current object.
  1769. *
  1770. * @return string 'C' to represent "Category" object type
  1771. */
  1772. public function get_item_type()
  1773. {
  1774. return 'C';
  1775. }
  1776. /**
  1777. * @param array $skills
  1778. */
  1779. public function set_skills($skills)
  1780. {
  1781. $this->skills = $skills;
  1782. }
  1783. public function get_date()
  1784. {
  1785. return null;
  1786. }
  1787. /**
  1788. * @return string
  1789. */
  1790. public function get_icon_name()
  1791. {
  1792. return 'cat';
  1793. }
  1794. /**
  1795. * Find category by name.
  1796. *
  1797. * @param string $name_mask search string
  1798. *
  1799. * @return array category objects matching the search criterium
  1800. */
  1801. public function find_category($name_mask, $allcat)
  1802. {
  1803. $categories = [];
  1804. foreach ($allcat as $search_cat) {
  1805. if (!(strpos(strtolower($search_cat->get_name()), strtolower($name_mask)) === false)) {
  1806. $categories[] = $search_cat;
  1807. }
  1808. }
  1809. return $categories;
  1810. }
  1811. /**
  1812. * This function, locks a category , only one who can unlock it is
  1813. * the platform administrator.
  1814. *
  1815. * @param int locked 1 or unlocked 0
  1816. *
  1817. * @return bool|null
  1818. * */
  1819. public function lock($locked)
  1820. {
  1821. $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
  1822. $sql = "UPDATE $table SET locked = '".intval($locked)."'
  1823. WHERE id='".intval($this->id)."'";
  1824. Database::query($sql);
  1825. }
  1826. /**
  1827. * @param $locked
  1828. */
  1829. public function lockAllItems($locked)
  1830. {
  1831. if (api_get_setting('gradebook_locking_enabled') == 'true') {
  1832. $this->lock($locked);
  1833. $evals_to_lock = $this->get_evaluations();
  1834. if (!empty($evals_to_lock)) {
  1835. foreach ($evals_to_lock as $item) {
  1836. $item->lock($locked);
  1837. }
  1838. }
  1839. $link_to_lock = $this->get_links();
  1840. if (!empty($link_to_lock)) {
  1841. foreach ($link_to_lock as $item) {
  1842. $item->lock($locked);
  1843. }
  1844. }
  1845. $event_type = LOG_GRADEBOOK_UNLOCKED;
  1846. if ($locked == 1) {
  1847. $event_type = LOG_GRADEBOOK_LOCKED;
  1848. }
  1849. Event::addEvent($event_type, LOG_GRADEBOOK_ID, $this->id);
  1850. }
  1851. }
  1852. /**
  1853. * Generates a certificate for this user if everything matches.
  1854. *
  1855. * @param int $category_id gradebook id
  1856. * @param int $user_id
  1857. * @param bool $sendNotification
  1858. *
  1859. * @return array
  1860. */
  1861. public static function generateUserCertificate(
  1862. $category_id,
  1863. $user_id,
  1864. $sendNotification = false
  1865. ) {
  1866. $user_id = (int) $user_id;
  1867. $category_id = (int) $category_id;
  1868. // Generating the total score for a course
  1869. $category = self::load(
  1870. $category_id,
  1871. null,
  1872. null,
  1873. null,
  1874. null,
  1875. null,
  1876. false
  1877. );
  1878. /** @var Category $category */
  1879. $category = $category[0];
  1880. if (empty($category)) {
  1881. return false;
  1882. }
  1883. $sessionId = $category->get_session_id();
  1884. $courseCode = $category->get_course_code();
  1885. $courseInfo = api_get_course_info($courseCode);
  1886. $courseId = $courseInfo['real_id'];
  1887. $userFinishedCourse = self::userFinishedCourse(
  1888. $user_id,
  1889. $category,
  1890. true
  1891. );
  1892. if (!$userFinishedCourse) {
  1893. return false;
  1894. }
  1895. $skillToolEnabled = Skill::hasAccessToUserSkill(
  1896. api_get_user_id(),
  1897. $user_id
  1898. );
  1899. $userHasSkills = false;
  1900. if ($skillToolEnabled) {
  1901. $skill = new Skill();
  1902. $skill->addSkillToUser(
  1903. $user_id,
  1904. $category,
  1905. $courseId,
  1906. $sessionId
  1907. );
  1908. $objSkillRelUser = new SkillRelUser();
  1909. $userSkills = $objSkillRelUser->getUserSkills(
  1910. $user_id,
  1911. $courseId,
  1912. $sessionId
  1913. );
  1914. $userHasSkills = !empty($userSkills);
  1915. if ($userHasSkills) {
  1916. return [
  1917. 'badge_link' => Display::toolbarButton(
  1918. get_lang('ExportBadges'),
  1919. api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
  1920. 'external-link'
  1921. ),
  1922. ];
  1923. }
  1924. }
  1925. // Block certification links depending gradebook configuration (generate certifications)
  1926. if (empty($category->getGenerateCertificates())) {
  1927. return false;
  1928. }
  1929. $scoretotal = $category->calc_score($user_id);
  1930. // Do not remove this the gradebook/lib/fe/gradebooktable.class.php
  1931. // file load this variable as a global
  1932. $scoredisplay = ScoreDisplay::instance();
  1933. $my_score_in_gradebook = $scoredisplay->display_score(
  1934. $scoretotal,
  1935. SCORE_SIMPLE
  1936. );
  1937. $my_certificate = GradebookUtils::get_certificate_by_user_id(
  1938. $category_id,
  1939. $user_id
  1940. );
  1941. if (empty($my_certificate)) {
  1942. GradebookUtils::registerUserInfoAboutCertificate(
  1943. $category_id,
  1944. $user_id,
  1945. $my_score_in_gradebook,
  1946. api_get_utc_datetime()
  1947. );
  1948. $my_certificate = GradebookUtils::get_certificate_by_user_id(
  1949. $category_id,
  1950. $user_id
  1951. );
  1952. }
  1953. $html = [];
  1954. if (!empty($my_certificate)) {
  1955. $certificate_obj = new Certificate(
  1956. $my_certificate['id'],
  1957. 0,
  1958. $sendNotification
  1959. );
  1960. $fileWasGenerated = $certificate_obj->isHtmlFileGenerated();
  1961. // Fix when using custom certificate BT#15937
  1962. if (api_get_plugin_setting('customcertificate', 'enable_plugin_customcertificate') === 'true') {
  1963. $infoCertificate = CustomCertificatePlugin::getCertificateData($my_certificate['id'], $user_id);
  1964. if (!empty($infoCertificate)) {
  1965. $fileWasGenerated = true;
  1966. }
  1967. }
  1968. if (!empty($fileWasGenerated)) {
  1969. $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$my_certificate['id'].'&user_id='.$user_id;
  1970. $certificates = Display::toolbarButton(
  1971. get_lang('DisplayCertificate'),
  1972. $url,
  1973. 'eye',
  1974. 'primary',
  1975. ['target' => '_blank']
  1976. );
  1977. $exportToPDF = Display::url(
  1978. Display::return_icon(
  1979. 'pdf.png',
  1980. get_lang('ExportToPDF'),
  1981. [],
  1982. ICON_SIZE_MEDIUM
  1983. ),
  1984. "$url&action=export"
  1985. );
  1986. $hideExportLink = api_get_setting('hide_certificate_export_link');
  1987. $hideExportLinkStudent = api_get_setting('hide_certificate_export_link_students');
  1988. if ($hideExportLink === 'true' || (api_is_student() && $hideExportLinkStudent === 'true')) {
  1989. $exportToPDF = null;
  1990. }
  1991. $html = [
  1992. 'certificate_link' => $certificates,
  1993. 'pdf_link' => $exportToPDF,
  1994. 'pdf_url' => "$url&action=export",
  1995. ];
  1996. if ($skillToolEnabled && $userHasSkills) {
  1997. $html['badge_link'] = Display::toolbarButton(
  1998. get_lang('ExportBadges'),
  1999. api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
  2000. 'external-link'
  2001. );
  2002. }
  2003. }
  2004. return $html;
  2005. }
  2006. }
  2007. /**
  2008. * @param int $catId
  2009. * @param array $userList
  2010. */
  2011. public static function generateCertificatesInUserList($catId, $userList)
  2012. {
  2013. if (!empty($userList)) {
  2014. foreach ($userList as $userInfo) {
  2015. self::generateUserCertificate($catId, $userInfo['user_id']);
  2016. }
  2017. }
  2018. }
  2019. /**
  2020. * @param int $catId
  2021. * @param array $userList
  2022. */
  2023. public static function exportAllCertificates(
  2024. $catId,
  2025. $userList = []
  2026. ) {
  2027. $orientation = api_get_configuration_value('certificate_pdf_orientation');
  2028. $params['orientation'] = 'landscape';
  2029. if (!empty($orientation)) {
  2030. $params['orientation'] = $orientation;
  2031. }
  2032. $params['left'] = 0;
  2033. $params['right'] = 0;
  2034. $params['top'] = 0;
  2035. $params['bottom'] = 0;
  2036. $page_format = $params['orientation'] == 'landscape' ? 'A4-L' : 'A4';
  2037. $pdf = new PDF($page_format, $params['orientation'], $params);
  2038. $certificate_list = GradebookUtils::get_list_users_certificates($catId, $userList);
  2039. $certificate_path_list = [];
  2040. if (!empty($certificate_list)) {
  2041. foreach ($certificate_list as $index => $value) {
  2042. $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
  2043. $value['user_id'],
  2044. $catId
  2045. );
  2046. foreach ($list_certificate as $value_certificate) {
  2047. $certificate_obj = new Certificate($value_certificate['id']);
  2048. $certificate_obj->generate(['hide_print_button' => true]);
  2049. if ($certificate_obj->isHtmlFileGenerated()) {
  2050. $certificate_path_list[] = $certificate_obj->html_file;
  2051. }
  2052. }
  2053. }
  2054. }
  2055. if (!empty($certificate_path_list)) {
  2056. // Print certificates (without the common header/footer/watermark
  2057. // stuff) and return as one multiple-pages PDF
  2058. $pdf->html_to_pdf(
  2059. $certificate_path_list,
  2060. get_lang('Certificates'),
  2061. null,
  2062. false,
  2063. false
  2064. );
  2065. }
  2066. }
  2067. /**
  2068. * @param int $catId
  2069. */
  2070. public static function deleteAllCertificates($catId)
  2071. {
  2072. $certificate_list = GradebookUtils::get_list_users_certificates($catId);
  2073. if (!empty($certificate_list)) {
  2074. foreach ($certificate_list as $index => $value) {
  2075. $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
  2076. $value['user_id'],
  2077. $catId
  2078. );
  2079. foreach ($list_certificate as $value_certificate) {
  2080. $certificate_obj = new Certificate($value_certificate['id']);
  2081. $certificate_obj->delete(true);
  2082. }
  2083. }
  2084. }
  2085. }
  2086. /**
  2087. * Check whether a user has finished a course by its gradebook.
  2088. *
  2089. * @param int $userId The user ID
  2090. * @param \Category $category Optional. The gradebook category.
  2091. * To check by the gradebook category
  2092. * @param bool $recalculateScore Whether recalculate the score
  2093. *
  2094. * @return bool
  2095. */
  2096. public static function userFinishedCourse(
  2097. $userId,
  2098. \Category $category,
  2099. $recalculateScore = false
  2100. ) {
  2101. if (empty($category)) {
  2102. return false;
  2103. }
  2104. $currentScore = self::getCurrentScore(
  2105. $userId,
  2106. $category,
  2107. $recalculateScore
  2108. );
  2109. $minCertificateScore = $category->getCertificateMinScore();
  2110. $passedCourse = $currentScore >= $minCertificateScore;
  2111. return $passedCourse;
  2112. }
  2113. /**
  2114. * Get the current score (as percentage) on a gradebook category for a user.
  2115. *
  2116. * @param int $userId The user id
  2117. * @param Category $category The gradebook category
  2118. * @param bool $recalculate
  2119. *
  2120. * @return float The score
  2121. */
  2122. public static function getCurrentScore(
  2123. $userId,
  2124. $category,
  2125. $recalculate = false
  2126. ) {
  2127. if (empty($category)) {
  2128. return 0;
  2129. }
  2130. if ($recalculate) {
  2131. return self::calculateCurrentScore(
  2132. $userId,
  2133. $category
  2134. );
  2135. }
  2136. $resultData = Database::select(
  2137. '*',
  2138. Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
  2139. [
  2140. 'where' => [
  2141. 'category_id = ? AND user_id = ?' => [$category->get_id(), $userId],
  2142. ],
  2143. 'order' => 'registered_at DESC',
  2144. 'limit' => '1',
  2145. ],
  2146. 'first'
  2147. );
  2148. if (empty($resultData)) {
  2149. return 0;
  2150. }
  2151. return $resultData['score'];
  2152. }
  2153. /**
  2154. * Register the current score for a user on a category gradebook.
  2155. *
  2156. * @param float $score The achieved score
  2157. * @param int $userId The user id
  2158. * @param int $categoryId The gradebook category
  2159. *
  2160. * @return int The insert id
  2161. */
  2162. public static function registerCurrentScore($score, $userId, $categoryId)
  2163. {
  2164. return Database::insert(
  2165. Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
  2166. [
  2167. 'category_id' => intval($categoryId),
  2168. 'user_id' => intval($userId),
  2169. 'score' => api_float_val($score),
  2170. 'registered_at' => api_get_utc_datetime(),
  2171. ]
  2172. );
  2173. }
  2174. /**
  2175. * @return array
  2176. */
  2177. public function getStudentList()
  2178. {
  2179. return $this->studentList;
  2180. }
  2181. /**
  2182. * @param array $list
  2183. */
  2184. public function setStudentList($list)
  2185. {
  2186. $this->studentList = $list;
  2187. }
  2188. /**
  2189. * @return string
  2190. */
  2191. public static function getUrl()
  2192. {
  2193. $url = Session::read('gradebook_dest');
  2194. if (empty($url)) {
  2195. // We guess the link
  2196. $courseInfo = api_get_course_info();
  2197. if (!empty($courseInfo)) {
  2198. return api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
  2199. } else {
  2200. return api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
  2201. }
  2202. }
  2203. return $url;
  2204. }
  2205. /**
  2206. * Destination is index.php or gradebook.php.
  2207. *
  2208. * @param string $url
  2209. */
  2210. public static function setUrl($url)
  2211. {
  2212. switch ($url) {
  2213. case 'gradebook.php':
  2214. $url = api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
  2215. break;
  2216. case 'index.php':
  2217. $url = api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
  2218. break;
  2219. }
  2220. Session::write('gradebook_dest', $url);
  2221. }
  2222. /**
  2223. * @return int
  2224. */
  2225. public function getGradeBooksToValidateInDependence()
  2226. {
  2227. return $this->gradeBooksToValidateInDependence;
  2228. }
  2229. /**
  2230. * @param int $value
  2231. *
  2232. * @return Category
  2233. */
  2234. public function setGradeBooksToValidateInDependence($value)
  2235. {
  2236. $this->gradeBooksToValidateInDependence = $value;
  2237. return $this;
  2238. }
  2239. /**
  2240. * Return HTML code with links to download and view certificate.
  2241. *
  2242. * @param array $certificate
  2243. *
  2244. * @return string
  2245. */
  2246. public static function getDownloadCertificateBlock(array $certificate)
  2247. {
  2248. if (!isset($certificate['pdf_url'])) {
  2249. return '';
  2250. }
  2251. $downloadLink = Display::toolbarButton(
  2252. get_lang('DownloadCertificatePdf'),
  2253. $certificate['pdf_url'],
  2254. 'file-pdf-o'
  2255. );
  2256. $viewLink = $certificate['certificate_link'];
  2257. return "
  2258. <div class='panel panel-default'>
  2259. <div class='panel-body'>
  2260. <h3 class='text-center'>".get_lang('NowDownloadYourCertificateClickHere')."</h3>
  2261. <div class='text-center'>$downloadLink $viewLink</div>
  2262. </div>
  2263. </div>
  2264. ";
  2265. }
  2266. /**
  2267. * Find a gradebook category by the certificate ID.
  2268. *
  2269. * @param int $id certificate id
  2270. *
  2271. * @throws \Doctrine\ORM\NonUniqueResultException
  2272. *
  2273. * @return Category|null
  2274. */
  2275. public static function findByCertificate($id)
  2276. {
  2277. $category = Database::getManager()
  2278. ->createQuery('SELECT c.catId FROM ChamiloCoreBundle:GradebookCertificate c WHERE c.id = :id')
  2279. ->setParameters(['id' => $id])
  2280. ->getOneOrNullResult();
  2281. if (empty($category)) {
  2282. return null;
  2283. }
  2284. $category = self::load($category['catId']);
  2285. if (empty($category)) {
  2286. return null;
  2287. }
  2288. return $category[0];
  2289. }
  2290. /**
  2291. * @param int $value
  2292. */
  2293. public function setDocumentId($value)
  2294. {
  2295. $this->documentId = (int) $value;
  2296. }
  2297. /**
  2298. * @return int
  2299. */
  2300. public function getDocumentId()
  2301. {
  2302. return $this->documentId;
  2303. }
  2304. /**
  2305. * @return Category
  2306. */
  2307. private static function create_root_category()
  2308. {
  2309. $cat = new Category();
  2310. $cat->set_id(0);
  2311. $cat->set_name(get_lang('RootCat'));
  2312. $cat->set_description(null);
  2313. $cat->set_user_id(0);
  2314. $cat->set_course_code(null);
  2315. $cat->set_parent_id(null);
  2316. $cat->set_weight(0);
  2317. $cat->set_visible(1);
  2318. $cat->setGenerateCertificates(0);
  2319. $cat->setIsRequirement(false);
  2320. return $cat;
  2321. }
  2322. /**
  2323. * @param Doctrine\DBAL\Driver\Statement|null $result
  2324. *
  2325. * @return array
  2326. */
  2327. private static function create_category_objects_from_sql_result($result)
  2328. {
  2329. $categories = [];
  2330. $allow = api_get_configuration_value('allow_gradebook_stats');
  2331. if ($allow) {
  2332. $em = Database::getManager();
  2333. $repo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
  2334. }
  2335. while ($data = Database::fetch_array($result)) {
  2336. $cat = new Category();
  2337. $cat->set_id($data['id']);
  2338. $cat->set_name($data['name']);
  2339. $cat->set_description($data['description']);
  2340. $cat->set_user_id($data['user_id']);
  2341. $cat->set_course_code($data['course_code']);
  2342. $cat->set_parent_id($data['parent_id']);
  2343. $cat->set_weight($data['weight']);
  2344. $cat->set_visible($data['visible']);
  2345. $cat->set_session_id($data['session_id']);
  2346. $cat->set_certificate_min_score($data['certif_min_score']);
  2347. $cat->set_grade_model_id($data['grade_model_id']);
  2348. $cat->set_locked($data['locked']);
  2349. $cat->setGenerateCertificates($data['generate_certificates']);
  2350. $cat->setIsRequirement($data['is_requirement']);
  2351. $cat->setCourseListDependency(isset($data['depends']) ? $data['depends'] : []);
  2352. $cat->setMinimumToValidate(isset($data['minimum_to_validate']) ? $data['minimum_to_validate'] : null);
  2353. $cat->setGradeBooksToValidateInDependence(isset($data['gradebooks_to_validate_in_dependence']) ? $data['gradebooks_to_validate_in_dependence'] : null);
  2354. $cat->setDocumentId($data['document_id']);
  2355. if ($allow) {
  2356. $cat->entity = $repo->find($data['id']);
  2357. }
  2358. $categories[] = $cat;
  2359. }
  2360. return $categories;
  2361. }
  2362. /**
  2363. * Internal function used by get_target_categories().
  2364. *
  2365. * @param array $targets
  2366. * @param int $level
  2367. * @param int $catid
  2368. *
  2369. * @return array
  2370. */
  2371. private function addTargetSubcategories($targets, $level, $catid)
  2372. {
  2373. $subcats = self::load(null, null, null, $catid);
  2374. foreach ($subcats as $cat) {
  2375. if ($this->can_be_moved_to_cat($cat)) {
  2376. $targets[] = [
  2377. $cat->get_id(),
  2378. $cat->get_name(),
  2379. $level + 1,
  2380. ];
  2381. $targets = $this->addTargetSubcategories(
  2382. $targets,
  2383. $level + 1,
  2384. $cat->get_id()
  2385. );
  2386. }
  2387. }
  2388. return $targets;
  2389. }
  2390. /**
  2391. * Internal function used by get_target_categories() and addTargetSubcategories()
  2392. * Can this category be moved to the given category ?
  2393. * Impossible when origin and target are the same... children won't be processed
  2394. * either. (a category can't be moved to one of its own children).
  2395. */
  2396. private function can_be_moved_to_cat($cat)
  2397. {
  2398. return $cat->get_id() != $this->get_id();
  2399. }
  2400. /**
  2401. * Internal function used by move_to_cat().
  2402. */
  2403. private function applyCourseCodeToChildren()
  2404. {
  2405. $cats = self::load(null, null, null, $this->id, null);
  2406. $evals = Evaluation::load(null, null, null, $this->id, null);
  2407. $links = LinkFactory::load(
  2408. null,
  2409. null,
  2410. null,
  2411. null,
  2412. null,
  2413. $this->id,
  2414. null
  2415. );
  2416. /** @var Category $cat */
  2417. foreach ($cats as $cat) {
  2418. $cat->set_course_code($this->get_course_code());
  2419. $cat->save();
  2420. $cat->applyCourseCodeToChildren();
  2421. }
  2422. foreach ($evals as $eval) {
  2423. $eval->set_course_code($this->get_course_code());
  2424. $eval->save();
  2425. }
  2426. foreach ($links as $link) {
  2427. $link->delete();
  2428. }
  2429. }
  2430. /**
  2431. * Internal function used by get_tree().
  2432. *
  2433. * @param int $level
  2434. * @param int|null $visible
  2435. *
  2436. * @return array
  2437. */
  2438. private function add_subtree($targets, $level, $catid, $visible)
  2439. {
  2440. $subcats = self::load(null, null, null, $catid, $visible);
  2441. if (!empty($subcats)) {
  2442. foreach ($subcats as $cat) {
  2443. $targets[] = [
  2444. $cat->get_id(),
  2445. $cat->get_name(),
  2446. $level + 1,
  2447. ];
  2448. $targets = self::add_subtree(
  2449. $targets,
  2450. $level + 1,
  2451. $cat->get_id(),
  2452. $visible
  2453. );
  2454. }
  2455. }
  2456. return $targets;
  2457. }
  2458. /**
  2459. * Calculate the current score on a gradebook category for a user.
  2460. *
  2461. * @param int $userId The user id
  2462. * @param Category $category The gradebook category
  2463. *
  2464. * @return float The score
  2465. */
  2466. private static function calculateCurrentScore(
  2467. $userId,
  2468. $category
  2469. ) {
  2470. if (empty($category)) {
  2471. return 0;
  2472. }
  2473. $courseEvaluations = $category->get_evaluations(
  2474. $userId,
  2475. true
  2476. );
  2477. $courseLinks = $category->get_links($userId, true);
  2478. $evaluationsAndLinks = array_merge($courseEvaluations, $courseLinks);
  2479. $categoryScore = 0;
  2480. for ($i = 0; $i < count($evaluationsAndLinks); $i++) {
  2481. $item = $evaluationsAndLinks[$i];
  2482. $score = $item->calc_score($userId);
  2483. $itemValue = 0;
  2484. if (!empty($score)) {
  2485. $divider = $score[1] == 0 ? 1 : $score[1];
  2486. $itemValue = $score[0] / $divider * $item->get_weight();
  2487. }
  2488. $categoryScore += $itemValue;
  2489. }
  2490. return api_float_val($categoryScore);
  2491. }
  2492. }