link.lib.php 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Chamilo\CourseBundle\Entity\CLink;
  4. /**
  5. * Function library for the links tool.
  6. *
  7. * This is a complete remake of the original link tool.
  8. * New features:
  9. * - Organize links into categories;
  10. * - favorites/bookmarks interface;
  11. * - move links up/down within a category;
  12. * - move categories up/down;
  13. * - expand/collapse all categories;
  14. * - add link to 'root' category => category-less link is always visible.
  15. *
  16. * @author Patrick Cool, complete remake (December 2003 - January 2004)
  17. * @author René Haentjens, CSV file import (October 2004)
  18. * @package chamilo.link
  19. */
  20. class Link extends Model
  21. {
  22. public $table;
  23. public $is_course_model = true;
  24. public $columns = array(
  25. 'id',
  26. 'c_id',
  27. 'url',
  28. 'title',
  29. 'description',
  30. 'category_id',
  31. 'display_order',
  32. 'on_homepage',
  33. 'target',
  34. 'session_id'
  35. );
  36. public $required = array('url', 'title');
  37. private $course;
  38. /**
  39. * Link constructor.
  40. */
  41. public function __construct()
  42. {
  43. $this->table = Database::get_course_table(TABLE_LINK);
  44. }
  45. /**
  46. * @param array $course
  47. */
  48. public function setCourse($course)
  49. {
  50. $this->course = $course;
  51. }
  52. /**
  53. * @return array
  54. */
  55. public function getCourse()
  56. {
  57. return !empty($this->course) ? $this->course : api_get_course_info();
  58. }
  59. /**
  60. * Organize the saving of a link, using the parent's save method and
  61. * updating the item_property table
  62. * @param array $params
  63. * @param boolean $show_query Whether to show the query in logs when
  64. * calling parent's save method
  65. *
  66. * @return bool True if link could be saved, false otherwise
  67. */
  68. public function save($params, $show_query = null)
  69. {
  70. $course_info = $this->getCourse();
  71. $courseId = $course_info['real_id'];
  72. $params['session_id'] = api_get_session_id();
  73. $params['category_id'] = isset($params['category_id']) ? $params['category_id'] : 0;
  74. $sql = "SELECT MAX(display_order)
  75. FROM ".$this->table."
  76. WHERE
  77. c_id = $courseId AND
  78. category_id = '".intval($params['category_id'])."'";
  79. $result = Database:: query($sql);
  80. list ($orderMax) = Database:: fetch_row($result);
  81. $order = $orderMax + 1;
  82. $params['display_order'] = $order;
  83. $id = parent::save($params, $show_query);
  84. if (!empty($id)) {
  85. // iid
  86. $sql = "UPDATE ".$this->table." SET id = iid WHERE iid = $id";
  87. Database::query($sql);
  88. api_item_property_update(
  89. $course_info,
  90. TOOL_LINK,
  91. $id,
  92. 'LinkAdded',
  93. api_get_user_id()
  94. );
  95. api_set_default_visibility($id, TOOL_LINK);
  96. }
  97. return $id;
  98. }
  99. /**
  100. * Update a link in the database
  101. * @param int $linkId The ID of the link to update
  102. * @param string $linkUrl The new URL to be saved
  103. * @param int $courseId
  104. * @param int $sessionId
  105. * @return bool
  106. */
  107. public function updateLink(
  108. $linkId,
  109. $linkUrl,
  110. $courseId = null,
  111. $sessionId = null
  112. ) {
  113. $tblLink = Database::get_course_table(TABLE_LINK);
  114. $linkUrl = Database::escape_string($linkUrl);
  115. $linkId = intval($linkId);
  116. if (is_null($courseId)) {
  117. $courseId = api_get_course_int_id();
  118. }
  119. $courseId = intval($courseId);
  120. if (is_null($sessionId)) {
  121. $sessionId = api_get_session_id();
  122. }
  123. $sessionId = intval($sessionId);
  124. if ($linkUrl != '') {
  125. $sql = "UPDATE $tblLink SET
  126. url = '$linkUrl'
  127. WHERE id = $linkId AND c_id = $courseId AND session_id = $sessionId";
  128. $resLink = Database::query($sql);
  129. return $resLink;
  130. }
  131. return false;
  132. }
  133. /**
  134. * Used to add a link or a category
  135. * @param string $type , "link" or "category"
  136. * @todo replace strings by constants
  137. * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  138. * @return bool True on success, false on failure
  139. */
  140. public static function addlinkcategory($type)
  141. {
  142. $ok = true;
  143. $_course = api_get_course_info();
  144. $course_id = $_course['real_id'];
  145. $session_id = api_get_session_id();
  146. if ($type == 'link') {
  147. $title = Security::remove_XSS(stripslashes($_POST['title']));
  148. $urllink = Security::remove_XSS($_POST['url']);
  149. $description = Security::remove_XSS($_POST['description']);
  150. $selectcategory = Security::remove_XSS($_POST['category_id']);
  151. if (!isset($_POST['on_homepage'])) {
  152. $onhomepage = 0;
  153. } else {
  154. $onhomepage = Security::remove_XSS($_POST['on_homepage']);
  155. }
  156. if (empty($_POST['target'])) {
  157. $target = '_self'; // Default target.
  158. } else {
  159. $target = Security::remove_XSS($_POST['target']);
  160. }
  161. $urllink = trim($urllink);
  162. $title = trim($title);
  163. $description = trim($description);
  164. // We ensure URL to be absolute.
  165. if (strpos($urllink, '://') === false) {
  166. $urllink = 'http://'.$urllink;
  167. }
  168. // If the title is empty, we use the URL as title.
  169. if ($title == '') {
  170. $title = $urllink;
  171. }
  172. // If the URL is invalid, an error occurs.
  173. if (!api_valid_url($urllink, true)) {
  174. // A check against an absolute URL
  175. Display::addFlash(Display::return_message(get_lang('GiveURL'), 'error'));
  176. return false;
  177. } else {
  178. // Looking for the largest order number for this category.
  179. $link = new Link();
  180. $params = [
  181. 'c_id' => $course_id,
  182. 'url' => $urllink,
  183. 'title' => $title,
  184. 'description' => $description,
  185. 'category_id' => $selectcategory,
  186. 'on_homepage' => $onhomepage,
  187. 'target' => $target,
  188. 'session_id' => $session_id,
  189. ];
  190. $link_id = $link->save($params);
  191. if ((api_get_setting('search_enabled') == 'true') &&
  192. $link_id && extension_loaded('xapian')
  193. ) {
  194. require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
  195. $course_int_id = $_course['real_id'];
  196. $courseCode = $_course['code'];
  197. $specific_fields = get_specific_field_list();
  198. $ic_slide = new IndexableChunk();
  199. // Add all terms to db.
  200. $all_specific_terms = '';
  201. foreach ($specific_fields as $specific_field) {
  202. if (isset($_REQUEST[$specific_field['code']])) {
  203. $sterms = trim($_REQUEST[$specific_field['code']]);
  204. if (!empty($sterms)) {
  205. $all_specific_terms .= ' '.$sterms;
  206. $sterms = explode(',', $sterms);
  207. foreach ($sterms as $sterm) {
  208. $ic_slide->addTerm(
  209. trim($sterm),
  210. $specific_field['code']
  211. );
  212. add_specific_field_value(
  213. $specific_field['id'],
  214. $courseCode,
  215. TOOL_LINK,
  216. $link_id,
  217. $sterm
  218. );
  219. }
  220. }
  221. }
  222. }
  223. // Build the chunk to index.
  224. $ic_slide->addValue('title', $title);
  225. $ic_slide->addCourseId($courseCode);
  226. $ic_slide->addToolId(TOOL_LINK);
  227. $xapian_data = array(
  228. SE_COURSE_ID => $courseCode,
  229. SE_TOOL_ID => TOOL_LINK,
  230. SE_DATA => array(
  231. 'link_id' => (int) $link_id
  232. ),
  233. SE_USER => (int) api_get_user_id(),
  234. );
  235. $ic_slide->xapian_data = serialize($xapian_data);
  236. $description = $all_specific_terms.' '.$description;
  237. $ic_slide->addValue('content', $description);
  238. // Add category name if set.
  239. if (isset($selectcategory) && $selectcategory > 0) {
  240. $table_link_category = Database::get_course_table(
  241. TABLE_LINK_CATEGORY
  242. );
  243. $sql_cat = 'SELECT * FROM %s WHERE id=%d AND c_id = %d LIMIT 1';
  244. $sql_cat = sprintf(
  245. $sql_cat,
  246. $table_link_category,
  247. (int) $selectcategory,
  248. $course_int_id
  249. );
  250. $result = Database:: query($sql_cat);
  251. if (Database:: num_rows($result) == 1) {
  252. $row = Database:: fetch_array($result);
  253. $ic_slide->addValue(
  254. 'category',
  255. $row['category_title']
  256. );
  257. }
  258. }
  259. $di = new ChamiloIndexer();
  260. isset($_POST['language']) ? $lang = Database:: escape_string(
  261. $_POST['language']
  262. ) : $lang = 'english';
  263. $di->connectDb(null, null, $lang);
  264. $di->addChunk($ic_slide);
  265. // Index and return search engine document id.
  266. $did = $di->index();
  267. if ($did) {
  268. // Save it to db.
  269. $tbl_se_ref = Database::get_main_table(
  270. TABLE_MAIN_SEARCH_ENGINE_REF
  271. );
  272. $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
  273. VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  274. $sql = sprintf(
  275. $sql,
  276. $tbl_se_ref,
  277. $course_int_id,
  278. $courseCode,
  279. TOOL_LINK,
  280. $link_id,
  281. $did
  282. );
  283. Database:: query($sql);
  284. }
  285. }
  286. Display::addFlash(Display::return_message(get_lang('LinkAdded')));
  287. }
  288. } elseif ($type == 'category') {
  289. $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
  290. $category_title = trim($_POST['category_title']);
  291. $description = trim($_POST['description']);
  292. if (empty($category_title)) {
  293. echo Display::return_message(get_lang('GiveCategoryName'), 'error');
  294. $ok = false;
  295. } else {
  296. // Looking for the largest order number for this category.
  297. $result = Database:: query(
  298. "SELECT MAX(display_order) FROM $tbl_categories
  299. WHERE c_id = $course_id "
  300. );
  301. list ($orderMax) = Database:: fetch_row($result);
  302. $order = $orderMax + 1;
  303. $order = intval($order);
  304. $session_id = api_get_session_id();
  305. $params = [
  306. 'c_id' => $course_id,
  307. 'category_title' => $category_title,
  308. 'description' => $description,
  309. 'display_order' => $order,
  310. 'session_id' => $session_id
  311. ];
  312. $linkId = Database::insert($tbl_categories, $params);
  313. if ($linkId) {
  314. // iid
  315. $sql = "UPDATE $tbl_categories SET id = iid WHERE iid = $linkId";
  316. Database:: query($sql);
  317. // add link_category visibility
  318. // course ID is taken from context in api_set_default_visibility
  319. //api_set_default_visibility($linkId, TOOL_LINK_CATEGORY);
  320. api_item_property_update(
  321. $_course,
  322. TOOL_LINK_CATEGORY,
  323. $linkId,
  324. 'LinkCategoryAdded',
  325. api_get_user_id()
  326. );
  327. api_set_default_visibility($linkId, TOOL_LINK_CATEGORY);
  328. }
  329. Display::addFlash(Display::return_message(get_lang('CategoryAdded')));
  330. }
  331. }
  332. return $ok;
  333. }
  334. /**
  335. * Used to delete a link or a category
  336. * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  337. * @param int $id
  338. * @param string $type The type of item to delete
  339. * @return bool
  340. */
  341. public static function deletelinkcategory($id, $type)
  342. {
  343. $courseInfo = api_get_course_info();
  344. $tbl_link = Database::get_course_table(TABLE_LINK);
  345. $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
  346. $course_id = $courseInfo['real_id'];
  347. $id = intval($id);
  348. if (empty($id)) {
  349. return false;
  350. }
  351. $result = false;
  352. switch ($type) {
  353. case 'link':
  354. // -> Items are no longer physically deleted,
  355. // but the visibility is set to 2 (in item_property).
  356. // This will make a restore function possible for the platform administrator.
  357. $sql = "UPDATE $tbl_link SET on_homepage='0'
  358. WHERE c_id = $course_id AND id='".$id."'";
  359. Database:: query($sql);
  360. api_item_property_update(
  361. $courseInfo,
  362. TOOL_LINK,
  363. $id,
  364. 'delete',
  365. api_get_user_id()
  366. );
  367. self::delete_link_from_search_engine(api_get_course_id(), $id);
  368. Display::addFlash(Display::return_message(get_lang('LinkDeleted')));
  369. $result = true;
  370. break;
  371. case 'category':
  372. // First we delete the category itself and afterwards all the links of this category.
  373. $sql = "DELETE FROM ".$tbl_categories."
  374. WHERE c_id = $course_id AND id='".$id."'";
  375. Database:: query($sql);
  376. $sql = "DELETE FROM ".$tbl_link."
  377. WHERE c_id = $course_id AND category_id='".$id."'";
  378. Database:: query($sql);
  379. api_item_property_update(
  380. $courseInfo,
  381. TOOL_LINK_CATEGORY,
  382. $id,
  383. 'delete',
  384. api_get_user_id()
  385. );
  386. Display::addFlash(Display::return_message(get_lang('CategoryDeleted')));
  387. $result = true;
  388. break;
  389. }
  390. return $result;
  391. }
  392. /**
  393. * Removes a link from search engine database
  394. * @param string $course_id Course code
  395. * @param int $link_id Document id to delete
  396. * @return void
  397. */
  398. public static function delete_link_from_search_engine($course_id, $link_id)
  399. {
  400. // Remove from search engine if enabled.
  401. if (api_get_setting('search_enabled') === 'true') {
  402. $tbl_se_ref = Database::get_main_table(
  403. TABLE_MAIN_SEARCH_ENGINE_REF
  404. );
  405. $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
  406. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
  407. $res = Database:: query($sql);
  408. if (Database:: num_rows($res) > 0) {
  409. $row = Database::fetch_array($res);
  410. $di = new ChamiloIndexer();
  411. $di->remove_document($row['search_did']);
  412. }
  413. $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
  414. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_LINK, $link_id);
  415. Database:: query($sql);
  416. // Remove terms from db.
  417. require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
  418. delete_all_values_for_item($course_id, TOOL_DOCUMENT, $link_id);
  419. }
  420. }
  421. /**
  422. *
  423. * Get link info
  424. * @param int $id
  425. * @return array link info
  426. *
  427. **/
  428. public static function getLinkInfo($id)
  429. {
  430. $tbl_link = Database::get_course_table(TABLE_LINK);
  431. $course_id = api_get_course_int_id();
  432. if (empty($id) || empty($course_id)) {
  433. return [];
  434. }
  435. $sql = "SELECT * FROM $tbl_link
  436. WHERE c_id = $course_id AND id='".intval($id)."' ";
  437. $result = Database::query($sql);
  438. $data = array();
  439. if (Database::num_rows($result)) {
  440. $data = Database::fetch_array($result);
  441. }
  442. return $data;
  443. }
  444. /**
  445. * @param int $id
  446. * @param array $values
  447. */
  448. public static function editLink($id, $values = array())
  449. {
  450. $tbl_link = Database::get_course_table(TABLE_LINK);
  451. $_course = api_get_course_info();
  452. $course_id = $_course['real_id'];
  453. $id = intval($id);
  454. $values['url'] = trim($values['url']);
  455. $values['title'] = trim($values['title']);
  456. $values['description'] = trim($values['description']);
  457. $values['target'] = empty($values['target']) ? '_self' : $values['target'];
  458. $values['on_homepage'] = isset($values['on_homepage']) ? $values['on_homepage'] : '';
  459. $categoryId = intval($values['category_id']);
  460. // We ensure URL to be absolute.
  461. if (strpos($values['url'], '://') === false) {
  462. $values['url'] = 'http://'.$_POST['url'];
  463. }
  464. // If the title is empty, we use the URL as title.
  465. if ($values['title'] == '') {
  466. $values['title'] = $values['url'];
  467. }
  468. // If the URL is invalid, an error occurs.
  469. if (!api_valid_url($values['url'], true)) {
  470. Display::addFlash(
  471. Display::return_message(get_lang('GiveURL'), 'error')
  472. );
  473. return false;
  474. }
  475. if (empty($id) || empty($course_id)) {
  476. return false;
  477. }
  478. // Finding the old category_id.
  479. $sql = "SELECT * FROM $tbl_link
  480. WHERE c_id = $course_id AND id='".$id."'";
  481. $result = Database:: query($sql);
  482. $row = Database:: fetch_array($result);
  483. $category_id = $row['category_id'];
  484. if ($category_id != $values['category_id']) {
  485. $sql = "SELECT MAX(display_order)
  486. FROM $tbl_link
  487. WHERE
  488. c_id = $course_id AND
  489. category_id='".intval($values['category_id'])."'";
  490. $result = Database:: query($sql);
  491. list ($max_display_order) = Database:: fetch_row($result);
  492. $max_display_order++;
  493. } else {
  494. $max_display_order = $row['display_order'];
  495. }
  496. $params = [
  497. 'url' => $values['url'],
  498. 'title' => $values['title'],
  499. 'description' => $values['description'],
  500. 'category_id' => $values['category_id'],
  501. 'display_order' => $max_display_order,
  502. 'on_homepage' => $values['on_homepage'],
  503. 'target' => $values['target'],
  504. 'category_id' => $values['category_id']
  505. ];
  506. Database::update(
  507. $tbl_link,
  508. $params,
  509. ['c_id = ? AND id = ?' => [$course_id, $id]]
  510. );
  511. // Update search enchine and its values table if enabled.
  512. if (api_get_setting('search_enabled') == 'true') {
  513. $course_int_id = api_get_course_int_id();
  514. $course_id = api_get_course_id();
  515. $link_title = Database:: escape_string($values['title']);
  516. $link_description = Database:: escape_string($values['description']);
  517. // Actually, it consists on delete terms from db,
  518. // insert new ones, create a new search engine document, and remove the old one.
  519. // Get search_did.
  520. $tbl_se_ref = Database::get_main_table(
  521. TABLE_MAIN_SEARCH_ENGINE_REF
  522. );
  523. $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
  524. $sql = sprintf(
  525. $sql,
  526. $tbl_se_ref,
  527. $course_id,
  528. TOOL_LINK,
  529. $id
  530. );
  531. $res = Database:: query($sql);
  532. if (Database:: num_rows($res) > 0) {
  533. require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
  534. $se_ref = Database:: fetch_array($res);
  535. $specific_fields = get_specific_field_list();
  536. $ic_slide = new IndexableChunk();
  537. $all_specific_terms = '';
  538. foreach ($specific_fields as $specific_field) {
  539. delete_all_specific_field_value(
  540. $course_id,
  541. $specific_field['id'],
  542. TOOL_LINK,
  543. $id
  544. );
  545. if (isset($_REQUEST[$specific_field['code']])) {
  546. $sterms = trim(
  547. $_REQUEST[$specific_field['code']]
  548. );
  549. if (!empty($sterms)) {
  550. $all_specific_terms .= ' '.$sterms;
  551. $sterms = explode(',', $sterms);
  552. foreach ($sterms as $sterm) {
  553. $ic_slide->addTerm(
  554. trim($sterm),
  555. $specific_field['code']
  556. );
  557. add_specific_field_value(
  558. $specific_field['id'],
  559. $course_id,
  560. TOOL_LINK,
  561. $id,
  562. $sterm
  563. );
  564. }
  565. }
  566. }
  567. }
  568. // Build the chunk to index.
  569. $ic_slide->addValue("title", $link_title);
  570. $ic_slide->addCourseId($course_id);
  571. $ic_slide->addToolId(TOOL_LINK);
  572. $xapian_data = array(
  573. SE_COURSE_ID => $course_id,
  574. SE_TOOL_ID => TOOL_LINK,
  575. SE_DATA => array(
  576. 'link_id' => (int) $id
  577. ),
  578. SE_USER => (int) api_get_user_id(),
  579. );
  580. $ic_slide->xapian_data = serialize($xapian_data);
  581. $link_description = $all_specific_terms.' '.$link_description;
  582. $ic_slide->addValue('content', $link_description);
  583. // Add category name if set.
  584. if (isset($categoryId) && $categoryId > 0) {
  585. $table_link_category = Database::get_course_table(
  586. TABLE_LINK_CATEGORY
  587. );
  588. $sql_cat = 'SELECT * FROM %s WHERE id=%d and c_id = %d LIMIT 1';
  589. $sql_cat = sprintf(
  590. $sql_cat,
  591. $table_link_category,
  592. $categoryId,
  593. $course_int_id
  594. );
  595. $result = Database:: query($sql_cat);
  596. if (Database:: num_rows($result) == 1) {
  597. $row = Database:: fetch_array($result);
  598. $ic_slide->addValue(
  599. 'category',
  600. $row['category_title']
  601. );
  602. }
  603. }
  604. $di = new ChamiloIndexer();
  605. isset ($_POST['language']) ? $lang = Database:: escape_string($_POST['language']) : $lang = 'english';
  606. $di->connectDb(null, null, $lang);
  607. $di->remove_document($se_ref['search_did']);
  608. $di->addChunk($ic_slide);
  609. // Index and return search engine document id.
  610. $did = $di->index();
  611. if ($did) {
  612. // Save it to db.
  613. $sql = 'DELETE FROM %s
  614. WHERE course_code=\'%s\'
  615. AND tool_id=\'%s\'
  616. AND ref_id_high_level=\'%s\'';
  617. $sql = sprintf(
  618. $sql,
  619. $tbl_se_ref,
  620. $course_id,
  621. TOOL_LINK,
  622. $id
  623. );
  624. Database:: query($sql);
  625. $sql = 'INSERT INTO %s (c_id, id, course_code, tool_id, ref_id_high_level, search_did)
  626. VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  627. $sql = sprintf(
  628. $sql,
  629. $tbl_se_ref,
  630. $course_int_id,
  631. $course_id,
  632. TOOL_LINK,
  633. $id,
  634. $did
  635. );
  636. Database:: query($sql);
  637. }
  638. }
  639. }
  640. // "WHAT'S NEW" notification: update table last_toolEdit.
  641. api_item_property_update(
  642. $_course,
  643. TOOL_LINK,
  644. $id,
  645. 'LinkUpdated',
  646. api_get_user_id()
  647. );
  648. Display::addFlash(Display::return_message(get_lang('LinkModded')));
  649. }
  650. /**
  651. * @param int $id
  652. * @param array $values
  653. * @return bool
  654. */
  655. public static function editCategory($id, $values)
  656. {
  657. $table = Database::get_course_table(TABLE_LINK_CATEGORY);
  658. $course_id = api_get_course_int_id();
  659. $id = intval($id);
  660. // This is used to put the modified info of the category-form into the database.
  661. $params = [
  662. 'category_title' => $values['category_title'],
  663. 'description' => $values['description']
  664. ];
  665. Database::update(
  666. $table,
  667. $params,
  668. ['c_id = ? AND id = ?' => [$course_id, $id]]
  669. );
  670. Display::addFlash(Display::return_message(get_lang('CategoryModded')));
  671. return true;
  672. }
  673. /**
  674. * Changes the visibility of a link
  675. * @todo add the changing of the visibility of a course
  676. * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  677. */
  678. public static function change_visibility_link($id, $scope)
  679. {
  680. $_course = api_get_course_info();
  681. $_user = api_get_user_info();
  682. if ($scope == TOOL_LINK) {
  683. api_item_property_update(
  684. $_course,
  685. TOOL_LINK,
  686. $id,
  687. $_GET['action'],
  688. $_user['user_id']
  689. );
  690. Display::addFlash(Display::return_message(get_lang('VisibilityChanged')));
  691. } elseif ($scope == TOOL_LINK_CATEGORY) {
  692. api_item_property_update(
  693. $_course,
  694. TOOL_LINK_CATEGORY,
  695. $id,
  696. $_GET['action'],
  697. $_user['user_id']
  698. );
  699. Display::addFlash(Display::return_message(get_lang('VisibilityChanged')));
  700. }
  701. }
  702. /**
  703. * Generate SQL to select all the links categories in the current course and
  704. * session
  705. * @param int $courseId
  706. * @param int $sessionId
  707. * @param bool $withBaseContent
  708. *
  709. * @return array
  710. */
  711. public static function getLinkCategories($courseId, $sessionId, $withBaseContent = true)
  712. {
  713. $tblLinkCategory = Database::get_course_table(TABLE_LINK_CATEGORY);
  714. $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
  715. $courseId = intval($courseId);
  716. $courseInfo = api_get_course_info_by_id($courseId);
  717. // Condition for the session.
  718. $sessionCondition = api_get_session_condition(
  719. $sessionId,
  720. true,
  721. $withBaseContent,
  722. 'linkcat.session_id'
  723. );
  724. // Getting links
  725. $sql = "SELECT *, linkcat.id
  726. FROM $tblLinkCategory linkcat
  727. WHERE
  728. linkcat.c_id = $courseId
  729. $sessionCondition
  730. ORDER BY linkcat.display_order DESC";
  731. $result = Database::query($sql);
  732. $categories = Database::store_result($result);
  733. $sql = "SELECT *, linkcat.id
  734. FROM $tblLinkCategory linkcat
  735. INNER JOIN $tblItemProperty ip
  736. ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
  737. WHERE
  738. ip.tool = '".TOOL_LINK_CATEGORY."' AND
  739. (ip.visibility = '0' OR ip.visibility = '1')
  740. $sessionCondition AND
  741. linkcat.c_id = ".$courseId."
  742. ORDER BY linkcat.display_order DESC";
  743. $result = Database::query($sql);
  744. $categoryInItemProperty = array();
  745. if (Database::num_rows($result)) {
  746. while ($row = Database::fetch_array($result, 'ASSOC')) {
  747. $categoryInItemProperty[$row['id']] = $row;
  748. }
  749. }
  750. foreach ($categories as & $category) {
  751. if (!isset($categoryInItemProperty[$category['id']])) {
  752. api_item_property_update(
  753. $courseInfo,
  754. TOOL_LINK_CATEGORY,
  755. $category['id'],
  756. 'LinkCategoryAdded',
  757. api_get_user_id()
  758. );
  759. //api_set_default_visibility($category['id'], TOOL_LINK_CATEGORY);
  760. }
  761. }
  762. $sql = "SELECT DISTINCT linkcat.*, visibility
  763. FROM $tblLinkCategory linkcat
  764. INNER JOIN $tblItemProperty ip
  765. ON (linkcat.id = ip.ref AND linkcat.c_id = ip.c_id)
  766. WHERE
  767. ip.tool = '".TOOL_LINK_CATEGORY."' AND
  768. (ip.visibility = '0' OR ip.visibility = '1')
  769. $sessionCondition AND
  770. linkcat.c_id = ".$courseId."
  771. ORDER BY linkcat.display_order DESC
  772. ";
  773. $result = Database::query($sql);
  774. return Database::store_result($result, 'ASSOC');
  775. }
  776. /**
  777. * @param integer $categoryId
  778. * @param $courseId
  779. * @param $sessionId
  780. * @param bool $withBaseContent
  781. *
  782. * @return array
  783. */
  784. public static function getLinksPerCategory(
  785. $categoryId,
  786. $courseId,
  787. $sessionId,
  788. $withBaseContent = true
  789. ) {
  790. $tbl_link = Database::get_course_table(TABLE_LINK);
  791. $TABLE_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
  792. $courseId = (int) $courseId;
  793. $sessionId = (int) $sessionId;
  794. $categoryId = (int) $categoryId;
  795. // Condition for the session.
  796. $condition_session = api_get_session_condition(
  797. $sessionId,
  798. true,
  799. false,
  800. 'ip.session_id'
  801. );
  802. if (!empty($sessionId)) {
  803. $conditionBaseSession = api_get_session_condition(
  804. 0,
  805. true,
  806. $withBaseContent,
  807. 'ip.session_id'
  808. );
  809. $condition = " AND
  810. (
  811. (ip.visibility = '1' $conditionBaseSession) OR
  812. (
  813. (ip.visibility = '0' OR ip.visibility = '1')
  814. $condition_session
  815. )
  816. )
  817. ";
  818. } else {
  819. $condition = api_get_session_condition(
  820. 0,
  821. true,
  822. false,
  823. 'ip.session_id'
  824. );
  825. $condition .= " AND (ip.visibility = '0' OR ip.visibility = '1') $condition ";
  826. }
  827. $sql = "SELECT
  828. link.id,
  829. ip.session_id,
  830. link.session_id link_session_id,
  831. url,
  832. category_id,
  833. visibility,
  834. description,
  835. title,
  836. target,
  837. on_homepage
  838. FROM $tbl_link link
  839. INNER JOIN $TABLE_ITEM_PROPERTY ip
  840. ON (link.id = ip.ref AND link.c_id = ip.c_id)
  841. WHERE
  842. ip.tool = '".TOOL_LINK."' AND
  843. link.category_id = '" . $categoryId."' AND
  844. link.c_id = $courseId AND
  845. ip.c_id = $courseId
  846. $condition
  847. ORDER BY link.display_order ASC, ip.session_id DESC";
  848. $result = Database:: query($sql);
  849. return Database::store_result($result);
  850. }
  851. /**
  852. * Displays all the links of a given category.
  853. * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  854. * @author Julio Montoya
  855. *
  856. * @param $catid
  857. * @param integer $courseId
  858. * @param integer $session_id
  859. * @return string
  860. */
  861. public static function showLinksPerCategory($catid, $courseId, $session_id)
  862. {
  863. global $token;
  864. $_user = api_get_user_info();
  865. $catid = intval($catid);
  866. $links = self::getLinksPerCategory($catid, $courseId, $session_id);
  867. $content = '';
  868. $numberOfLinks = count($links);
  869. if (!empty($links)) {
  870. $content .= '<div class="link list-group">';
  871. $i = 1;
  872. $linksAdded = [];
  873. foreach ($links as $myrow) {
  874. $linkId = $myrow['id'];
  875. if (in_array($linkId, $linksAdded)) {
  876. continue;
  877. }
  878. $linksAdded[] = $linkId;
  879. $categoryId = $myrow['category_id'];
  880. // Validation when belongs to a session.
  881. $session_img = api_get_session_image(
  882. $myrow['link_session_id'],
  883. $_user['status']
  884. );
  885. $toolbar = '';
  886. $link_validator = '';
  887. if (api_is_allowed_to_edit(null, true)) {
  888. $toolbar .= Display::toolbarButton(
  889. '',
  890. 'javascript:void(0);',
  891. 'check-circle-o',
  892. 'default btn-sm',
  893. array(
  894. 'onclick' => "check_url('".$linkId."', '".addslashes($myrow['url'])."');",
  895. 'title' => get_lang('CheckURL')
  896. )
  897. );
  898. $link_validator .= Display::span(
  899. '',
  900. array(
  901. 'id' => 'url_id_'.$linkId,
  902. 'class' => 'check-link'
  903. )
  904. );
  905. if ($session_id == $myrow['link_session_id']) {
  906. $url = api_get_self().'?'.api_get_cidreq().'&action=editlink&id='.$linkId;
  907. $title = get_lang('Edit');
  908. $toolbar .= Display::toolbarButton(
  909. '',
  910. $url,
  911. 'pencil',
  912. 'default btn-sm',
  913. array(
  914. 'title' => $title
  915. )
  916. );
  917. }
  918. $urlVisibility = api_get_self().'?'.api_get_cidreq().
  919. '&sec_token='.$token.
  920. '&id='.$linkId.
  921. '&scope=link&category_id='.$categoryId;
  922. switch ($myrow['visibility']) {
  923. case '1':
  924. $urlVisibility .= '&action=invisible';
  925. $title = get_lang('MakeInvisible');
  926. $toolbar .= Display::toolbarButton(
  927. '',
  928. $urlVisibility,
  929. 'eye',
  930. 'default btn-sm',
  931. array(
  932. 'title' => $title
  933. )
  934. );
  935. break;
  936. case '0':
  937. $urlVisibility .= '&action=visible';
  938. $title = get_lang('MakeVisible');
  939. $toolbar .= Display::toolbarButton(
  940. '',
  941. $urlVisibility,
  942. 'eye-slash',
  943. 'primary btn-sm',
  944. array(
  945. 'title' => $title
  946. )
  947. );
  948. break;
  949. }
  950. if ($session_id == $myrow['link_session_id']) {
  951. $moveLinkParams = [
  952. 'id' => $linkId,
  953. 'scope' => 'category',
  954. 'category_id' => $categoryId,
  955. 'action' => 'move_link_up'
  956. ];
  957. $toolbar .= Display::toolbarButton(
  958. get_lang('MoveUp'),
  959. api_get_self().'?'.api_get_cidreq().'&'.http_build_query($moveLinkParams),
  960. 'level-up',
  961. 'default',
  962. ['class' => 'btn-sm '.($i === 1 ? 'disabled' : '')],
  963. false
  964. );
  965. $moveLinkParams['action'] = 'move_link_down';
  966. $toolbar .= Display::toolbarButton(
  967. get_lang('MoveDown'),
  968. api_get_self().'?'.api_get_cidreq().'&'.http_build_query($moveLinkParams),
  969. 'level-down',
  970. 'default',
  971. ['class' => 'btn-sm '.($i === $numberOfLinks ? 'disabled' : '')],
  972. false
  973. );
  974. $url .= api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=deletelink&id='.$linkId.'&category_id='.$categoryId;
  975. $event = "javascript: if(!confirm('".get_lang('LinkDelconfirm')."'))return false;";
  976. $title = get_lang('Delete');
  977. $toolbar .= Display::toolbarButton(
  978. '',
  979. $url,
  980. 'trash',
  981. 'default btn-sm',
  982. array(
  983. 'onclick' => $event,
  984. 'title' => $title
  985. )
  986. );
  987. }
  988. }
  989. $showLink = false;
  990. $titleClass = '';
  991. if ($myrow['visibility'] == '1') {
  992. $showLink = true;
  993. } else {
  994. if (api_is_allowed_to_edit(null, true)) {
  995. $showLink = true;
  996. $titleClass = 'text-muted';
  997. }
  998. }
  999. if ($showLink) {
  1000. $iconLink = Display::return_icon(
  1001. 'url.png',
  1002. get_lang('Link'),
  1003. null,
  1004. ICON_SIZE_SMALL
  1005. );
  1006. $url = api_get_path(WEB_CODE_PATH).'link/link_goto.php?'.api_get_cidreq().'&link_id='.$linkId.'&link_url='.urlencode($myrow['url']);
  1007. $content .= '<div class="list-group-item">';
  1008. $content .= '<div class="pull-right"><div class="btn-group">'.$toolbar.'</div></div>';
  1009. $content .= '<h4 class="list-group-item-heading">';
  1010. $content .= $iconLink;
  1011. $content .= Display::tag(
  1012. 'a',
  1013. Security::remove_XSS($myrow['title']),
  1014. array(
  1015. 'href' => $url,
  1016. 'target' => $myrow['target'],
  1017. 'class' => $titleClass
  1018. )
  1019. );
  1020. $content .= $link_validator;
  1021. $content .= $session_img;
  1022. $content .= '</h4>';
  1023. $content .= '<p class="list-group-item-text">'.$myrow['description'].'</p>';
  1024. $content .= '</div>';
  1025. }
  1026. $i++;
  1027. }
  1028. $content .= '</div>';
  1029. }
  1030. return $content;
  1031. }
  1032. /**
  1033. * Displays the edit, delete and move icons
  1034. * @param int Category ID
  1035. * @param integer $currentCategory
  1036. * @param integer $countCategories
  1037. * @return string
  1038. *
  1039. * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  1040. */
  1041. public static function showCategoryAdminTools($category, $currentCategory, $countCategories)
  1042. {
  1043. $categoryId = $category['id'];
  1044. $token = null;
  1045. $tools = '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=editcategory&id='.$categoryId.'&category_id='.$categoryId.'" title='.get_lang('Modify').'">'.
  1046. Display:: return_icon(
  1047. 'edit.png',
  1048. get_lang('Modify'),
  1049. array(),
  1050. ICON_SIZE_SMALL
  1051. ).'</a>';
  1052. // DISPLAY MOVE UP COMMAND only if it is not the top link.
  1053. if ($currentCategory != 0) {
  1054. $tools .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=up&up='.$categoryId.'&category_id='.$categoryId.'" title="'.get_lang('Up').'">'.
  1055. Display:: return_icon(
  1056. 'up.png',
  1057. get_lang('Up'),
  1058. array(),
  1059. ICON_SIZE_SMALL
  1060. ).'</a>';
  1061. } else {
  1062. $tools .= Display:: return_icon(
  1063. 'up_na.png',
  1064. get_lang('Up'),
  1065. array(),
  1066. ICON_SIZE_SMALL
  1067. ).'</a>';
  1068. }
  1069. // DISPLAY MOVE DOWN COMMAND only if it is not the bottom link.
  1070. if ($currentCategory < $countCategories - 1) {
  1071. $tools .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=down&down='.$categoryId.'&category_id='.$categoryId.'">'.
  1072. Display:: return_icon(
  1073. 'down.png',
  1074. get_lang('Down'),
  1075. array(),
  1076. ICON_SIZE_SMALL
  1077. ).'</a>';
  1078. } else {
  1079. $tools .= Display:: return_icon(
  1080. 'down_na.png',
  1081. get_lang('Down'),
  1082. array(),
  1083. ICON_SIZE_SMALL
  1084. ).'</a>';
  1085. }
  1086. $tools .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&sec_token='.$token.'&action=deletecategory&id='.$categoryId."&category_id=$categoryId\"
  1087. onclick=\"javascript: if(!confirm('".get_lang('CategoryDelconfirm')."')) return false;\">".
  1088. Display:: return_icon(
  1089. 'delete.png',
  1090. get_lang('Delete'),
  1091. array(),
  1092. ICON_SIZE_SMALL
  1093. ).'</a>';
  1094. return $tools;
  1095. }
  1096. /**
  1097. * move a link or a linkcategory up or down
  1098. * @param int Category ID
  1099. * @param int Course ID
  1100. * @param int Session ID
  1101. * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  1102. * @todo support sessions
  1103. */
  1104. public static function movecatlink($action, $catlinkid, $courseId = null, $sessionId = null)
  1105. {
  1106. $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
  1107. if (is_null($courseId)) {
  1108. $courseId = api_get_course_int_id();
  1109. }
  1110. $courseId = intval($courseId);
  1111. if (is_null($sessionId)) {
  1112. $sessionId = api_get_session_id();
  1113. }
  1114. $sessionId = intval($sessionId);
  1115. $thiscatlinkId = intval($catlinkid);
  1116. if ($action == 'down') {
  1117. $sortDirection = 'DESC';
  1118. }
  1119. if ($action == 'up') {
  1120. $sortDirection = 'ASC';
  1121. }
  1122. $movetable = $tbl_categories;
  1123. if (!empty($sortDirection)) {
  1124. if (!in_array(trim(strtoupper($sortDirection)), array('ASC', 'DESC'))) {
  1125. $sortDirection = 'ASC';
  1126. }
  1127. $sql = "SELECT id, display_order FROM $movetable
  1128. WHERE c_id = $courseId
  1129. ORDER BY display_order $sortDirection";
  1130. $linkresult = Database:: query($sql);
  1131. $thislinkOrder = 1;
  1132. while ($sortrow = Database:: fetch_array($linkresult)) {
  1133. // STEP 2 : FOUND THE NEXT LINK ID AND ORDER, COMMIT SWAP
  1134. // This part seems unlogic, but it isn't . We first look for the current link with the querystring ID
  1135. // and we know the next iteration of the while loop is the next one. These should be swapped.
  1136. if (isset($thislinkFound) && $thislinkFound) {
  1137. $nextlinkId = $sortrow['id'];
  1138. $nextlinkOrder = $sortrow['display_order'];
  1139. Database:: query(
  1140. "UPDATE ".$movetable."
  1141. SET display_order = '$nextlinkOrder'
  1142. WHERE c_id = $courseId AND id = '$thiscatlinkId'"
  1143. );
  1144. Database:: query(
  1145. "UPDATE ".$movetable."
  1146. SET display_order = '$thislinkOrder'
  1147. WHERE c_id = $courseId AND id = '$nextlinkId'"
  1148. );
  1149. break;
  1150. }
  1151. if ($sortrow['id'] == $thiscatlinkId) {
  1152. $thislinkOrder = $sortrow['display_order'];
  1153. $thislinkFound = true;
  1154. }
  1155. }
  1156. }
  1157. Display::addFlash(Display::return_message(get_lang('LinkMoved')));
  1158. }
  1159. /**
  1160. * CSV file import functions
  1161. * @author René Haentjens , Ghent University
  1162. * @param string $catname
  1163. * @return int
  1164. */
  1165. public static function get_cat($catname)
  1166. {
  1167. // Get category id (existing or make new).
  1168. $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
  1169. $course_id = api_get_course_int_id();
  1170. $result = Database:: query(
  1171. "SELECT id FROM ".$tbl_categories."
  1172. WHERE c_id = $course_id AND category_title='".Database::escape_string($catname)."'"
  1173. );
  1174. if (Database:: num_rows($result) >= 1 && ($row = Database:: fetch_array($result))) {
  1175. return $row['id']; // Several categories with same name: take the first.
  1176. }
  1177. $result = Database:: query(
  1178. "SELECT MAX(display_order) FROM ".$tbl_categories." WHERE c_id = $course_id "
  1179. );
  1180. list ($max_order) = Database:: fetch_row($result);
  1181. $params = [
  1182. 'c_id' => $course_id,
  1183. 'category_title' => $catname,
  1184. 'description' => '',
  1185. 'display_order' => $max_order + 1
  1186. ];
  1187. $id = Database::insert($tbl_categories, $params);
  1188. return $id;
  1189. }
  1190. /**
  1191. * CSV file import functions
  1192. * @author René Haentjens , Ghent University
  1193. * @param string $url
  1194. * @param string $title
  1195. * @param string $description
  1196. * @param string $on_homepage
  1197. * @param string $hidden
  1198. */
  1199. public static function put_link($url, $cat, $title, $description, $on_homepage, $hidden)
  1200. {
  1201. $_course = api_get_course_info();
  1202. $_user = api_get_user_info();
  1203. $tbl_link = Database::get_course_table(TABLE_LINK);
  1204. $course_id = api_get_course_int_id();
  1205. $urleq = "url='".Database:: escape_string($url)."'";
  1206. $cateq = "category_id=".intval($cat);
  1207. $result = Database:: query("
  1208. SELECT id FROM $tbl_link
  1209. WHERE c_id = $course_id AND ".$urleq.' AND '.$cateq
  1210. );
  1211. if (Database:: num_rows($result) >= 1 && ($row = Database:: fetch_array($result))) {
  1212. $sql = "UPDATE $tbl_link SET
  1213. title = '".Database:: escape_string($title)."',
  1214. description = '" . Database:: escape_string($description)."'
  1215. WHERE c_id = $course_id AND id='".Database:: escape_string($row['id'])."'";
  1216. Database:: query($sql);
  1217. $ipu = 'LinkUpdated';
  1218. $rv = 1; // 1 = upd
  1219. } else {
  1220. // Add new link
  1221. $result = Database:: query(
  1222. "SELECT MAX(display_order) FROM $tbl_link
  1223. WHERE c_id = $course_id AND category_id='".intval($cat)."'"
  1224. );
  1225. list ($max_order) = Database:: fetch_row($result);
  1226. Database:: query(
  1227. "INSERT INTO $tbl_link (c_id, url, title, description, category_id, display_order, on_homepage)
  1228. VALUES (".api_get_course_int_id().",
  1229. '" . Database:: escape_string($url)."',
  1230. '" . Database:: escape_string($title)."',
  1231. '" . Database:: escape_string($description)."',
  1232. '" . intval($cat)."','".(intval($max_order) + 1)."',
  1233. '" . intval($on_homepage).
  1234. "')"
  1235. );
  1236. $id = Database:: insert_id();
  1237. $ipu = 'LinkAdded';
  1238. $rv = 2; // 2 = new
  1239. }
  1240. api_item_property_update(
  1241. $_course,
  1242. TOOL_LINK,
  1243. $id,
  1244. $ipu,
  1245. $_user['user_id']
  1246. );
  1247. if ($hidden && $ipu == 'LinkAdded') {
  1248. api_item_property_update(
  1249. $_course,
  1250. TOOL_LINK,
  1251. $id,
  1252. 'invisible',
  1253. $_user['user_id']
  1254. );
  1255. }
  1256. return $rv;
  1257. }
  1258. /**
  1259. * CSV file import functions
  1260. * @author René Haentjens , Ghent University
  1261. */
  1262. public static function import_link($linkdata)
  1263. {
  1264. // url, category_id, title, description, ...
  1265. // Field names used in the uploaded file
  1266. $known_fields = array(
  1267. 'url',
  1268. 'category',
  1269. 'title',
  1270. 'description',
  1271. 'on_homepage',
  1272. 'hidden'
  1273. );
  1274. $hide_fields = array(
  1275. 'kw',
  1276. 'kwd',
  1277. 'kwds',
  1278. 'keyword',
  1279. 'keywords'
  1280. );
  1281. // All other fields are added to description, as "name:value".
  1282. // Only one hide_field is assumed to be present, <> is removed from value.
  1283. if (!($url = trim($linkdata['url'])) || !($title = trim($linkdata['title']))) {
  1284. return 0; // 0 = fail
  1285. }
  1286. $cat = ($catname = trim($linkdata['category'])) ? self::get_cat($catname) : 0;
  1287. $regs = array(); // Will be passed to ereg()
  1288. $d = '';
  1289. foreach ($linkdata as $key => $value) {
  1290. if (!in_array($key, $known_fields)) {
  1291. if (in_array($key, $hide_fields) && ereg(
  1292. '^<?([^>]*)>?$',
  1293. $value,
  1294. $regs
  1295. )
  1296. ) { // possibly in <...>
  1297. if (($kwlist = trim($regs[1])) != '') {
  1298. $kw = '<i kw="'.htmlspecialchars($kwlist).'">';
  1299. } else {
  1300. $kw = '';
  1301. }
  1302. // i.e. assume only one of the $hide_fields will be present
  1303. // and if found, hide the value as expando property of an <i> tag
  1304. } elseif (trim($value)) {
  1305. $d .= ', '.$key.':'.$value;
  1306. }
  1307. }
  1308. }
  1309. if (!empty($d)) {
  1310. $d = substr($d, 2).' - ';
  1311. }
  1312. return self::put_link(
  1313. $url,
  1314. $cat,
  1315. $title,
  1316. $kw.ereg_replace(
  1317. '\[((/?(b|big|i|small|sub|sup|u))|br/)\]',
  1318. '<\\1>',
  1319. htmlspecialchars($d.$linkdata['description'])
  1320. ).($kw ? '</i>' : ''),
  1321. $linkdata['on_homepage'] ? '1' : '0',
  1322. $linkdata['hidden'] ? '1' : '0'
  1323. );
  1324. // i.e. allow some BBcode tags, e.g. [b]...[/b]
  1325. }
  1326. /**
  1327. * This function checks if the url is a vimeo link
  1328. * @author Julio Montoya
  1329. * @version 1.0
  1330. */
  1331. public static function isVimeoLink($url)
  1332. {
  1333. $isLink = strrpos($url, "vimeo.com");
  1334. return $isLink;
  1335. }
  1336. /**
  1337. * Get vimeo id from URL
  1338. * @param string $url
  1339. * @return bool|mixed
  1340. */
  1341. public static function getVimeoLinkId($url)
  1342. {
  1343. $possibleUrls = array(
  1344. 'http://www.vimeo.com/',
  1345. 'http://vimeo.com/',
  1346. 'https://www.vimeo.com/',
  1347. 'https://vimeo.com/'
  1348. );
  1349. $url = str_replace($possibleUrls, '', $url);
  1350. if (is_numeric($url)) {
  1351. return $url;
  1352. }
  1353. return false;
  1354. }
  1355. /**
  1356. * This function checks if the url is a youtube link
  1357. * @author Jorge Frisancho
  1358. * @author Julio Montoya - Fixing code
  1359. * @version 1.0
  1360. */
  1361. public static function is_youtube_link($url)
  1362. {
  1363. $is_youtube_link = strrpos($url, "youtube") || strrpos(
  1364. $url,
  1365. "youtu.be"
  1366. );
  1367. return $is_youtube_link;
  1368. }
  1369. /**
  1370. * Get youtube id from an URL
  1371. * @param string $url
  1372. * @return string
  1373. */
  1374. public static function get_youtube_video_id($url)
  1375. {
  1376. // This is the length of YouTube's video IDs
  1377. $len = 11;
  1378. // The ID string starts after "v=", which is usually right after
  1379. // "youtube.com/watch?" in the URL
  1380. $pos = strpos($url, "v=");
  1381. $id = '';
  1382. //If false try other options
  1383. if ($pos === false) {
  1384. $url_parsed = parse_url($url);
  1385. //Youtube shortener
  1386. //http://youtu.be/ID
  1387. $pos = strpos($url, "youtu.be");
  1388. if ($pos == false) {
  1389. $id = '';
  1390. } else {
  1391. return substr($url_parsed['path'], 1);
  1392. }
  1393. //if empty try the youtube.com/embed/ID
  1394. if (empty($id)) {
  1395. $pos = strpos($url, "embed");
  1396. if ($pos === false) {
  1397. return '';
  1398. } else {
  1399. return substr($url_parsed['path'], 7);
  1400. }
  1401. }
  1402. } else {
  1403. // Offset the start location to match the beginning of the ID string
  1404. $pos += 2;
  1405. // Get the ID string and return it
  1406. $id = substr($url, $pos, $len);
  1407. return $id;
  1408. }
  1409. }
  1410. /**
  1411. * @param int $course_id
  1412. * @param int $session_id
  1413. * @param int $categoryId
  1414. * @param string $show
  1415. * @param null $token
  1416. */
  1417. public static function listLinksAndCategories(
  1418. $course_id,
  1419. $session_id,
  1420. $categoryId,
  1421. $show = 'none',
  1422. $token = null
  1423. ) {
  1424. $tbl_link = Database::get_course_table(TABLE_LINK);
  1425. $tblCIP = Database::get_course_table(TABLE_ITEM_PROPERTY);
  1426. $categoryId = intval($categoryId);
  1427. /* Action Links */
  1428. echo '<div class="actions">';
  1429. if (api_is_allowed_to_edit(null, true)) {
  1430. echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=addlink&category_id='.$categoryId.'">'.
  1431. Display::return_icon('new_link.png', get_lang('LinkAdd'), '', ICON_SIZE_MEDIUM).'</a>';
  1432. echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=addcategory&category_id='.$categoryId.'">'.
  1433. Display::return_icon('new_folder.png', get_lang('CategoryAdd'), '', ICON_SIZE_MEDIUM).'</a>';
  1434. }
  1435. $categories = self::getLinkCategories($course_id, $session_id);
  1436. $countCategories = count($categories);
  1437. if (!empty($countCategories)) {
  1438. echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=list&show=none">';
  1439. echo Display::return_icon('forum_listview.png', get_lang('FlatView'), '', ICON_SIZE_MEDIUM).' </a>';
  1440. echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=list&show=all">';
  1441. echo Display::return_icon('forum_nestedview.png', get_lang('NestedView'), '', ICON_SIZE_MEDIUM).'</a>';
  1442. }
  1443. echo '</div>';
  1444. $linksPerCategory = self::showLinksPerCategory(0, $course_id, $session_id);
  1445. if (empty($countCategories)) {
  1446. echo $linksPerCategory;
  1447. } else {
  1448. if (!empty($linksPerCategory)) {
  1449. echo Display::panel($linksPerCategory, get_lang('NoCategory'));
  1450. }
  1451. }
  1452. $counter = 0;
  1453. foreach ($categories as $myrow) {
  1454. // Student don't see invisible categories.
  1455. if (!api_is_allowed_to_edit(null, true)) {
  1456. if ($myrow['visibility'] == 0) {
  1457. continue;
  1458. }
  1459. }
  1460. // Validation when belongs to a session
  1461. $showChildren = $categoryId == $myrow['id'] || $show == 'all';
  1462. $myrow['description'] = $myrow['description'];
  1463. $strVisibility = '';
  1464. $visibilityClass = null;
  1465. if ($myrow['visibility'] == '1') {
  1466. $strVisibility = '<a href="link.php?'.api_get_cidreq().'&sec_token='.$token.'&action=invisible&id='.$myrow['id'].'&scope='.TOOL_LINK_CATEGORY.'" title="'.get_lang('Hide').'">'.
  1467. Display::return_icon('visible.png', get_lang('Hide'), array(), ICON_SIZE_SMALL).'</a>';
  1468. } elseif ($myrow['visibility'] == '0') {
  1469. $visibilityClass = 'text-muted';
  1470. $strVisibility = ' <a href="link.php?'.api_get_cidreq().'&sec_token='.$token.'&action=visible&id='.$myrow['id'].'&scope='.TOOL_LINK_CATEGORY.'" title="'.get_lang('Show').'">'.
  1471. Display::return_icon('invisible.png', get_lang('Show'), array(), ICON_SIZE_SMALL).'</a>';
  1472. }
  1473. $header = '';
  1474. if ($showChildren) {
  1475. $header .= '<a class="'.$visibilityClass.'" href="'.api_get_self().'?'.api_get_cidreq().'&category_id=">';
  1476. $header .= Display::return_icon('forum_nestedview.png');
  1477. } else {
  1478. $header .= '<a class="'.$visibilityClass.'" href="'.api_get_self().'?'.api_get_cidreq().'&category_id='.$myrow['id'].'">';
  1479. $header .= Display::return_icon('forum_listview.png');
  1480. }
  1481. $header .= Security::remove_XSS($myrow['category_title']).'</a>';
  1482. $header .= '<div class="pull-right">';
  1483. if (api_is_allowed_to_edit(null, true)) {
  1484. if ($session_id == $myrow['session_id']) {
  1485. $header .= $strVisibility;
  1486. $header .= self::showCategoryAdminTools($myrow, $counter, count($categories));
  1487. } else {
  1488. $header .= get_lang('EditionNotAvailableFromSession');
  1489. }
  1490. }
  1491. $childrenContent = '';
  1492. if ($showChildren) {
  1493. $childrenContent = self::showLinksPerCategory(
  1494. $myrow['id'],
  1495. api_get_course_int_id(),
  1496. api_get_session_id()
  1497. );
  1498. }
  1499. echo Display::panel($myrow['description'].$childrenContent, $header);
  1500. $counter++;
  1501. }
  1502. }
  1503. /**
  1504. * @param int $linkId
  1505. * @param $action
  1506. * @param null $token
  1507. *
  1508. * @return FormValidator
  1509. */
  1510. public static function getLinkForm($linkId, $action, $token = null)
  1511. {
  1512. $course_id = api_get_course_int_id();
  1513. $session_id = api_get_session_id();
  1514. $linkInfo = self::getLinkInfo($linkId);
  1515. $categoryId = isset($linkInfo['category_id']) ? $linkInfo['category_id'] : '';
  1516. $lpId = isset($_GET['lp_id']) ? Security::remove_XSS($_GET['lp_id']) : null;
  1517. $form = new FormValidator(
  1518. 'link',
  1519. 'post',
  1520. api_get_self().'?action='.$action.
  1521. '&category_id='.$categoryId.
  1522. '&'.api_get_cidreq().
  1523. '&id='.$linkId.
  1524. '&sec_token='.$token
  1525. );
  1526. if ($action == 'addlink') {
  1527. $form->addHeader(get_lang('LinkAdd'));
  1528. } else {
  1529. $form->addHeader(get_lang('LinkMod'));
  1530. }
  1531. $target_link = '_blank';
  1532. $title = '';
  1533. $category = '';
  1534. $onhomepage = '';
  1535. $description = '';
  1536. if (!empty($linkInfo)) {
  1537. $urllink = $linkInfo['url'];
  1538. $title = $linkInfo['title'];
  1539. $description = $linkInfo['description'];
  1540. $category = $linkInfo['category_id'];
  1541. if ($linkInfo['on_homepage'] != 0) {
  1542. $onhomepage = 1;
  1543. }
  1544. $target_link = $linkInfo['target'];
  1545. }
  1546. $form->addHidden('id', $linkId);
  1547. $form->addText('url', 'URL');
  1548. $form->addRule('url', get_lang('GiveURL'), 'url');
  1549. $form->addText('title', get_lang('LinkName'));
  1550. $form->addTextarea('description', get_lang('Description'));
  1551. $resultcategories = self::getLinkCategories($course_id, $session_id);
  1552. $options = ['0' => '--'];
  1553. if (!empty($resultcategories)) {
  1554. foreach ($resultcategories as $myrow) {
  1555. $options[$myrow['id']] = $myrow['category_title'];
  1556. }
  1557. }
  1558. $form->addSelect('category_id', get_lang('Category'), $options);
  1559. $form->addCheckBox('on_homepage', null, get_lang('OnHomepage'));
  1560. $targets = array(
  1561. '_self' => get_lang('LinkOpenSelf'),
  1562. '_blank' => get_lang('LinkOpenBlank'),
  1563. '_parent' => get_lang('LinkOpenParent'),
  1564. '_top' => get_lang('LinkOpenTop')
  1565. );
  1566. $form->addSelect(
  1567. 'target',
  1568. array(
  1569. get_lang('LinkTarget'),
  1570. get_lang('AddTargetOfLinkOnHomepage')
  1571. ),
  1572. $targets
  1573. );
  1574. $defaults = array(
  1575. 'url' => empty($urllink) ? 'http://' : Security::remove_XSS($urllink),
  1576. 'title' => Security::remove_XSS($title),
  1577. 'category_id' => $category,
  1578. 'on_homepage' => $onhomepage,
  1579. 'description' => $description,
  1580. 'target' => $target_link
  1581. );
  1582. if (api_get_setting('search_enabled') == 'true') {
  1583. require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
  1584. $specific_fields = get_specific_field_list();
  1585. $form->addCheckBox('index_document', get_lang('SearchFeatureDoIndexLink'), get_lang('Yes'));
  1586. foreach ($specific_fields as $specific_field) {
  1587. $default_values = '';
  1588. if ($action == 'editlink') {
  1589. $filter = array(
  1590. 'field_id' => $specific_field['id'],
  1591. 'ref_id' => intval($_GET['id']),
  1592. 'tool_id' => '\''.TOOL_LINK.'\''
  1593. );
  1594. $values = get_specific_field_values_list($filter, array('value'));
  1595. if (!empty($values)) {
  1596. $arr_str_values = array();
  1597. foreach ($values as $value) {
  1598. $arr_str_values[] = $value['value'];
  1599. }
  1600. $default_values = implode(', ', $arr_str_values);
  1601. }
  1602. }
  1603. $form->addText($specific_field['name'], $specific_field['code']);
  1604. $defaults[$specific_field['name']] = $default_values;
  1605. }
  1606. }
  1607. $form->addHidden('lp_id', $lpId);
  1608. $form->addButtonSave(get_lang('SaveLink'), 'submitLink');
  1609. $form->setDefaults($defaults);
  1610. return $form;
  1611. }
  1612. /**
  1613. * @param int $id
  1614. * @param string $action
  1615. *
  1616. * @return FormValidator
  1617. */
  1618. public static function getCategoryForm($id, $action)
  1619. {
  1620. $form = new FormValidator(
  1621. 'category',
  1622. 'post',
  1623. api_get_self().'?action='.$action.'&'.api_get_cidreq()
  1624. );
  1625. $defaults = [];
  1626. if ($action == 'addcategory') {
  1627. $form->addHeader(get_lang('CategoryAdd'));
  1628. $my_cat_title = get_lang('CategoryAdd');
  1629. } else {
  1630. $form->addHeader(get_lang('CategoryMod'));
  1631. $my_cat_title = get_lang('CategoryMod');
  1632. $defaults = self::getCategory($id);
  1633. }
  1634. $form->addHidden('id', $id);
  1635. $form->addText('category_title', get_lang('CategoryName'));
  1636. $form->addTextarea('description', get_lang('Description'));
  1637. $form->addButtonSave($my_cat_title, 'submitCategory');
  1638. $form->setDefaults($defaults);
  1639. return $form;
  1640. }
  1641. /**
  1642. * @param int $id
  1643. * @return array
  1644. */
  1645. public static function getCategory($id)
  1646. {
  1647. $table = Database::get_course_table(TABLE_LINK_CATEGORY);
  1648. $id = intval($id);
  1649. $courseId = api_get_course_int_id();
  1650. if (empty($id) || empty($courseId)) {
  1651. return [];
  1652. }
  1653. $sql = "SELECT * FROM $table
  1654. WHERE id = $id AND c_id = $courseId";
  1655. $result = Database::query($sql);
  1656. $category = Database::fetch_array($result, 'ASSOC');
  1657. return $category;
  1658. }
  1659. /**
  1660. * Move a link inside its category (display_order field)
  1661. * @param int $id The link ID
  1662. * @param string $direction The direction to sort the links
  1663. * @return bool
  1664. */
  1665. private static function moveLinkDisplayOrder($id, $direction)
  1666. {
  1667. $em = Database::getManager();
  1668. /** @var CLink $link */
  1669. $link = $em->find('ChamiloCourseBundle:CLink', $id);
  1670. if (!$link) {
  1671. return false;
  1672. }
  1673. $compareLinks = $em
  1674. ->getRepository('ChamiloCourseBundle:CLink')
  1675. ->findBy(
  1676. [
  1677. 'cId' => $link->getCId(),
  1678. 'categoryId' => $link->getCategoryId()
  1679. ],
  1680. ['displayOrder' => $direction]
  1681. );
  1682. /** @var CLink $prevLink */
  1683. $prevLink = null;
  1684. /** @var CLink $compareLink */
  1685. foreach ($compareLinks as $compareLink) {
  1686. if ($compareLink->getId() !== $link->getId()) {
  1687. $prevLink = $compareLink;
  1688. continue;
  1689. }
  1690. if (!$prevLink) {
  1691. return false;
  1692. }
  1693. $newPrevLinkDisplayOrder = $link->getDisplayOrder();
  1694. $newLinkDisplayOrder = $prevLink->getDisplayOrder();
  1695. $link->setDisplayOrder($newLinkDisplayOrder);
  1696. $prevLink->setDisplayOrder($newPrevLinkDisplayOrder);
  1697. $em->merge($prevLink);
  1698. $em->merge($link);
  1699. break;
  1700. }
  1701. $em->flush();
  1702. return true;
  1703. }
  1704. /**
  1705. * Move a link up in its category
  1706. * @param int $id
  1707. * @return bool
  1708. */
  1709. public static function moveLinkUp($id)
  1710. {
  1711. return self::moveLinkDisplayOrder($id, 'ASC');
  1712. }
  1713. /**
  1714. * Move a link down in its category
  1715. * @param int $id
  1716. * @return bool
  1717. */
  1718. public static function moveLinkDown($id)
  1719. {
  1720. return self::moveLinkDisplayOrder($id, 'DESC');
  1721. }
  1722. /**
  1723. * @param string $url
  1724. * @return bool
  1725. */
  1726. public static function checkUrl($url)
  1727. {
  1728. // Check if curl is available.
  1729. if (!in_array('curl', get_loaded_extensions())) {
  1730. return false;
  1731. }
  1732. // set URL and other appropriate options
  1733. $defaults = array(
  1734. CURLOPT_URL => $url,
  1735. CURLOPT_FOLLOWLOCATION => true, // follow redirects accept youtube.com
  1736. CURLOPT_HEADER => 0,
  1737. CURLOPT_RETURNTRANSFER => true,
  1738. CURLOPT_TIMEOUT => 4
  1739. );
  1740. $proxySettings = api_get_configuration_value('proxy_settings');
  1741. if (!empty($proxySettings) &&
  1742. isset($proxySettings['curl_setopt_array'])
  1743. ) {
  1744. $defaults[CURLOPT_PROXY] = $proxySettings['curl_setopt_array']['CURLOPT_PROXY'];
  1745. $defaults[CURLOPT_PROXYPORT] = $proxySettings['curl_setopt_array']['CURLOPT_PROXYPORT'];
  1746. }
  1747. // Create a new cURL resource
  1748. $ch = curl_init();
  1749. curl_setopt_array($ch, $defaults);
  1750. // grab URL and pass it to the browser
  1751. ob_start();
  1752. $result = curl_exec($ch);
  1753. ob_get_clean();
  1754. // close cURL resource, and free up system resources
  1755. curl_close($ch);
  1756. return $result;
  1757. }
  1758. }