core_renderer.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <?php
  2. // This file is part of The Bootstrap 3 Moodle theme
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. defined('MOODLE_INTERNAL') || die();
  17. /**
  18. * Renderers to align Moodle's HTML with that expected by Bootstrap
  19. *
  20. * @package theme_bootstrap
  21. * @copyright 2012
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. class theme_bootstrap_core_renderer extends core_renderer {
  25. public function notification($message, $classes = 'notifyproblem') {
  26. $message = clean_text($message);
  27. if ($classes == 'notifyproblem') {
  28. return html_writer::div($message, 'alert alert-danger');
  29. }
  30. if ($classes == 'notifywarning') {
  31. return html_writer::div($message, 'alert alert-warning');
  32. }
  33. if ($classes == 'notifysuccess') {
  34. return html_writer::div($message, 'alert alert-success');
  35. }
  36. if ($classes == 'notifymessage') {
  37. return html_writer::div($message, 'alert alert-info');
  38. }
  39. if ($classes == 'redirectmessage') {
  40. return html_writer::div($message, 'alert alert-block alert-info');
  41. }
  42. if ($classes == 'notifytiny') {
  43. // Not an appropriate semantic alert class!
  44. return $this->debug_listing($message);
  45. }
  46. return html_writer::div($message, $classes);
  47. }
  48. private function debug_listing($message) {
  49. $message = str_replace('<ul style', '<ul class="list-unstyled" style', $message);
  50. return html_writer::tag('pre', $message, array('class' => 'alert alert-info'));
  51. }
  52. public function navbar() {
  53. $breadcrumbs = '';
  54. foreach ($this->page->navbar->get_items() as $item) {
  55. $item->hideicon = true;
  56. $breadcrumbs .= '<li>'.$this->render($item).'</li>';
  57. }
  58. return "<ol class=breadcrumb>$breadcrumbs</ol>";
  59. }
  60. public function custom_menu($custommenuitems = '') {
  61. // The custom menu is always shown, even if no menu items
  62. // are configured in the global theme settings page.
  63. global $CFG;
  64. if (!empty($CFG->custommenuitems)) {
  65. $custommenuitems .= $CFG->custommenuitems;
  66. }
  67. $custommenu = new custom_menu($custommenuitems, current_language());
  68. return $this->render_custom_menu($custommenu);
  69. }
  70. protected function render_custom_menu(custom_menu $menu) {
  71. global $CFG, $USER;
  72. // TODO: eliminate this duplicated logic, it belongs in core, not
  73. // here. See MDL-39565.
  74. $content = '<ul class="nav navbar-nav">';
  75. foreach ($menu->get_children() as $item) {
  76. $content .= $this->render_custom_menu_item($item, 1);
  77. }
  78. return $content.'</ul>';
  79. }
  80. public function user_menu() {
  81. global $CFG;
  82. $usermenu = new custom_menu('', current_language());
  83. return $this->render_user_menu($usermenu);
  84. }
  85. protected function render_user_menu(custom_menu $menu) {
  86. global $CFG, $USER, $DB;
  87. $addusermenu = true;
  88. $addlangmenu = true;
  89. $addmessagemenu = true;
  90. if (!isloggedin() || isguestuser()) {
  91. $addmessagemenu = false;
  92. }
  93. if ($addmessagemenu) {
  94. $messages = $this->get_user_messages();
  95. $messagecount = count($messages);
  96. $messagemenu = $menu->add(
  97. $messagecount . ' ' . get_string('messages', 'message'),
  98. new moodle_url('/message/index.php', array('viewing' => 'recentconversations')),
  99. get_string('messages', 'message'),
  100. 9999
  101. );
  102. foreach ($messages as $message) {
  103. if (!$message->from) { // Workaround for issue #103.
  104. continue;
  105. }
  106. $senderpicture = new user_picture($message->from);
  107. $senderpicture->link = false;
  108. $senderpicture = $this->render($senderpicture);
  109. $messagecontent = $senderpicture;
  110. $messagecontent .= html_writer::start_span('msg-body');
  111. $messagecontent .= html_writer::start_span('msg-title');
  112. $messagecontent .= html_writer::span($message->from->firstname . ': ', 'msg-sender');
  113. $messagecontent .= $message->text;
  114. $messagecontent .= html_writer::end_span();
  115. $messagecontent .= html_writer::start_span('msg-time');
  116. $messagecontent .= html_writer::tag('i', '', array('class' => 'icon-time'));
  117. $messagecontent .= html_writer::span($message->date);
  118. $messagecontent .= html_writer::end_span();
  119. $messageurl = new moodle_url('/message/index.php', array('user1' => $USER->id, 'user2' => $message->from->id));
  120. $messagemenu->add($messagecontent, $messageurl, $message->state);
  121. }
  122. }
  123. $langs = get_string_manager()->get_list_of_translations();
  124. if (count($langs) < 2
  125. or empty($CFG->langmenu)
  126. or ($this->page->course != SITEID and !empty($this->page->course->lang))) {
  127. $addlangmenu = false;
  128. }
  129. if ($addlangmenu) {
  130. $language = $menu->add(get_string('language'), new moodle_url('#'), get_string('language'), 10000);
  131. foreach ($langs as $langtype => $langname) {
  132. $language->add($langname, new moodle_url($this->page->url, array('lang' => $langtype)), $langname);
  133. }
  134. }
  135. if ($addusermenu) {
  136. if (isloggedin()) {
  137. $usermenu = $menu->add(fullname($USER), new moodle_url('#'), fullname($USER), 10001);
  138. $usermenu->add(
  139. '<span class="glyphicon glyphicon-off"></span>' . get_string('logout'),
  140. new moodle_url('/login/logout.php', array('sesskey' => sesskey(), 'alt' => 'logout')),
  141. get_string('logout')
  142. );
  143. $usermenu->add(
  144. '<span class="glyphicon glyphicon-user"></span>' . get_string('viewprofile'),
  145. new moodle_url('/user/profile.php', array('id' => $USER->id)),
  146. get_string('viewprofile')
  147. );
  148. $usermenu->add(
  149. '<span class="glyphicon glyphicon-cog"></span>' . get_string('editmyprofile'),
  150. new moodle_url('/user/edit.php', array('id' => $USER->id)),
  151. get_string('editmyprofile')
  152. );
  153. } else {
  154. $usermenu = $menu->add(get_string('login'), new moodle_url('/login/index.php'), get_string('login'), 10001);
  155. }
  156. }
  157. $content = '<ul class="nav navbar-nav navbar-right">';
  158. foreach ($menu->get_children() as $item) {
  159. $content .= $this->render_custom_menu_item($item, 1);
  160. }
  161. return $content.'</ul>';
  162. }
  163. protected function process_user_messages() {
  164. $messagelist = array();
  165. foreach ($usermessages as $message) {
  166. $cleanmsg = new stdClass();
  167. $cleanmsg->from = fullname($message);
  168. $cleanmsg->msguserid = $message->id;
  169. $userpicture = new user_picture($message);
  170. $userpicture->link = false;
  171. $picture = $this->render($userpicture);
  172. $cleanmsg->text = $picture . ' ' . $cleanmsg->text;
  173. $messagelist[] = $cleanmsg;
  174. }
  175. return $messagelist;
  176. }
  177. protected function get_user_messages() {
  178. global $USER, $DB;
  179. $messagelist = array();
  180. $maxmessages = 5;
  181. $readmessagesql = "SELECT id, smallmessage, useridfrom, useridto, timecreated, fullmessageformat, notification
  182. FROM {message_read}
  183. WHERE useridto = :userid
  184. ORDER BY timecreated DESC
  185. LIMIT $maxmessages";
  186. $newmessagesql = "SELECT id, smallmessage, useridfrom, useridto, timecreated, fullmessageformat, notification
  187. FROM {message}
  188. WHERE useridto = :userid";
  189. $readmessages = $DB->get_records_sql($readmessagesql, array('userid' => $USER->id));
  190. $newmessages = $DB->get_records_sql($newmessagesql, array('userid' => $USER->id));
  191. foreach ($newmessages as $message) {
  192. $messagelist[] = $this->bootstrap_process_message($message, 'new');
  193. }
  194. foreach ($readmessages as $message) {
  195. $messagelist[] = $this->bootstrap_process_message($message, 'old');
  196. }
  197. return $messagelist;
  198. }
  199. protected function bootstrap_process_message($message, $state) {
  200. global $DB;
  201. $messagecontent = new stdClass();
  202. if ($message->notification) {
  203. $messagecontent->text = get_string('unreadnewnotification', 'message');
  204. } else {
  205. if ($message->fullmessageformat == FORMAT_HTML) {
  206. $message->smallmessage = html_to_text($message->smallmessage);
  207. }
  208. if (core_text::strlen($message->smallmessage) > 15) {
  209. $messagecontent->text = core_text::substr($message->smallmessage, 0, 15).'...';
  210. } else {
  211. $messagecontent->text = $message->smallmessage;
  212. }
  213. }
  214. if ((time() - $message->timecreated ) <= (3600 * 3)) {
  215. $messagecontent->date = format_time(time() - $message->timecreated);
  216. } else {
  217. $messagecontent->date = userdate($message->timecreated, get_string('strftimetime', 'langconfig'));
  218. }
  219. $messagecontent->from = $DB->get_record('user', array('id' => $message->useridfrom));
  220. $messagecontent->state = $state;
  221. return $messagecontent;
  222. }
  223. protected function render_custom_menu_item(custom_menu_item $menunode, $level = 0 ) {
  224. static $submenucount = 0;
  225. if ($menunode->has_children()) {
  226. if ($level == 1) {
  227. $dropdowntype = 'dropdown';
  228. } else {
  229. $dropdowntype = 'dropdown-submenu';
  230. }
  231. $content = html_writer::start_tag('li', array('class' => $dropdowntype));
  232. // If the child has menus render it as a sub menu.
  233. $submenucount++;
  234. if ($menunode->get_url() !== null) {
  235. $url = $menunode->get_url();
  236. } else {
  237. $url = '#cm_submenu_'.$submenucount;
  238. }
  239. $linkattributes = array(
  240. 'href' => $url,
  241. 'class' => 'dropdown-toggle',
  242. 'data-toggle' => 'dropdown',
  243. 'title' => $menunode->get_title(),
  244. );
  245. $content .= html_writer::start_tag('a', $linkattributes);
  246. $content .= $menunode->get_text();
  247. if ($level == 1) {
  248. $content .= '<b class="caret"></b>';
  249. }
  250. $content .= '</a>';
  251. $content .= '<ul class="dropdown-menu">';
  252. foreach ($menunode->get_children() as $menunode) {
  253. $content .= $this->render_custom_menu_item($menunode, 0);
  254. }
  255. $content .= '</ul>';
  256. } else {
  257. $content = '<li>';
  258. // The node doesn't have children so produce a final menuitem.
  259. if ($menunode->get_url() !== null) {
  260. $url = $menunode->get_url();
  261. } else {
  262. $url = '#';
  263. }
  264. $content .= html_writer::link($url, $menunode->get_text(), array('title' => $menunode->get_title()));
  265. }
  266. return $content;
  267. }
  268. protected function render_tabtree(tabtree $tabtree) {
  269. if (empty($tabtree->subtree)) {
  270. return '';
  271. }
  272. $firstrow = $secondrow = '';
  273. foreach ($tabtree->subtree as $tab) {
  274. $firstrow .= $this->render($tab);
  275. if (($tab->selected || $tab->activated) && !empty($tab->subtree) && $tab->subtree !== array()) {
  276. $secondrow = $this->tabtree($tab->subtree);
  277. }
  278. }
  279. return html_writer::tag('ul', $firstrow, array('class' => 'nav nav-tabs nav-justified')) . $secondrow;
  280. }
  281. protected function render_tabobject(tabobject $tab) {
  282. if ($tab->selected or $tab->activated) {
  283. return html_writer::tag('li', html_writer::tag('a', $tab->text), array('class' => 'active'));
  284. } else if ($tab->inactive) {
  285. return html_writer::tag('li', html_writer::tag('a', $tab->text), array('class' => 'disabled'));
  286. } else {
  287. if (!($tab->link instanceof moodle_url)) {
  288. // Backward compatibility when link was passed as quoted string.
  289. $link = "<a href=\"$tab->link\" title=\"$tab->title\">$tab->text</a>";
  290. } else {
  291. $link = html_writer::link($tab->link, $tab->text, array('title' => $tab->title));
  292. }
  293. return html_writer::tag('li', $link);
  294. }
  295. }
  296. protected function render_pix_icon(pix_icon $icon) {
  297. if ($this->page->theme->settings->fonticons === '1'
  298. && $icon->attributes["alt"] === '') {
  299. $iconout = $this->replace_moodle_icon($icon->pix);
  300. if ($iconout !== false) {
  301. return $iconout;
  302. }
  303. }
  304. return parent::render_pix_icon($icon);
  305. }
  306. protected function replace_moodle_icon($name) {
  307. $icons = array(
  308. 'add' => 'plus',
  309. 'book' => 'book',
  310. 'chapter' => 'file',
  311. 'docs' => 'question-sign',
  312. 'generate' => 'gift',
  313. 'i/backup' => 'download',
  314. 't/backup' => 'download',
  315. 'i/checkpermissions' => 'user',
  316. 'i/edit' => 'pencil',
  317. 'i/filter' => 'filter',
  318. 'i/grades' => 'grades',
  319. 'i/group' => 'user',
  320. 'i/hide' => 'eye-open',
  321. 'i/import' => 'upload',
  322. 'i/info' => 'info',
  323. 'i/move_2d' => 'move',
  324. 'i/navigationitem' => 'chevron-right',
  325. 'i/publish' => 'globe',
  326. 'i/reload' => 'refresh',
  327. 'i/report' => 'list-alt',
  328. 'i/restore' => 'upload',
  329. 't/restore' => 'upload',
  330. 'i/return' => 'repeat',
  331. 'i/roles' => 'user',
  332. 'i/settings' => 'cog',
  333. 'i/show' => 'eye-close',
  334. 'i/switchrole' => 'user',
  335. 'i/user' => 'user',
  336. 'i/users' => 'user',
  337. 'spacer' => 'spacer',
  338. 't/add' => 'plus',
  339. 't/assignroles' => 'user',
  340. 't/copy' => 'plus-sign',
  341. 't/delete' => 'remove',
  342. 't/down' => 'arrow-down',
  343. 't/edit' => 'edit',
  344. 't/editstring' => 'tag',
  345. 't/hide' => 'eye-open',
  346. 't/left' => 'arrow-left',
  347. 't/move' => 'resize-vertical',
  348. 't/right' => 'arrow-right',
  349. 't/show' => 'eye-close',
  350. 't/switch_minus' => 'minus-sign',
  351. 't/switch_plus' => 'plus-sign',
  352. 't/up' => 'arrow-up',
  353. );
  354. if (isset($icons[$name])) {
  355. return '<span class="glyphicon glyphicon-'.$icons[$name].'"></span> ';
  356. } else {
  357. return false;
  358. }
  359. }
  360. public function box($contents, $classes = 'generalbox', $id = null, $attributes = array()) {
  361. if (isset($attributes['data-rel']) && $attributes['data-rel'] === 'fatalerror') {
  362. return html_writer::div($contents, 'alert alert-danger', $attributes);
  363. }
  364. return parent::box($contents, $classes, $id, $attributes);
  365. }
  366. }