PageRenderTime 60ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/togglelib.php

https://github.com/gjb2048/moodle-format_topcoll
PHP | 294 lines | 151 code | 28 blank | 115 comment | 28 complexity | 83ec78fbce26b5ff57afdecca45cf5fa MD5 | raw file
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  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. /**
  17. * Collapsed Topics Information
  18. *
  19. * A topic based format that solves the issue of the 'Scroll of Death' when a course has many topics. All topics
  20. * except zero have a toggle that displays that topic. One or more topics can be displayed at any given time.
  21. * Toggles are persistent on a per browser session per course basis but can be made to persist longer by a small
  22. * code change. Full installation instructions, code adaptions and credits are included in the 'Readme.txt' file.
  23. *
  24. * @package format_topcoll
  25. * @version See the value of '$plugin->version' in version.php.
  26. * @copyright &copy; 2012-onwards G J Barnard in respect to modifications of standard topics format.
  27. * @author G J Barnard - {@link http://moodle.org/user/profile.php?id=442195}
  28. * @link http://docs.moodle.org/en/Collapsed_Topics_course_format
  29. * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  30. *
  31. */
  32. namespace format_topcoll;
  33. class togglelib {
  34. // Digits used = ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy";
  35. // Note: An ':' is 58 Ascii so to go between six digit base 2 and this then add / subtract 58.
  36. // This has been chosen to avoid digits which are in the old method.
  37. const TOGGLE_6 = 1;
  38. const TOGGLE_5 = 2;
  39. const TOGGLE_4 = 4;
  40. const TOGGLE_3 = 8;
  41. const TOGGLE_2 = 16;
  42. const TOGGLE_1 = 32;
  43. private $toggles; // Toggles state.
  44. /**
  45. * Tells us the toggle state from the DB.
  46. * string $toggles - Toggles state.
  47. * returns nothing.
  48. */
  49. public function set_toggles($toggles) {
  50. $this->toggles = $toggles;
  51. }
  52. /**
  53. * Gets toggle state stored here.
  54. * returns $toggles - Toggles state.
  55. */
  56. public function get_toggles() {
  57. return $this->toggles;
  58. }
  59. // Note: http://php.net/manual/en/language.operators.bitwise.php very useful.
  60. /**
  61. * Gets the state of the requested Toggle number.
  62. * int $togglenum - The toggle number.
  63. * returns boolean.
  64. */
  65. public function get_toggle_state($togglenum) {
  66. $togglecharpos = self::get_toggle_pos($togglenum);
  67. $toggleflag = self::get_toggle_flag($togglenum, $togglecharpos);
  68. return ((self::decode_character_to_value($this->toggles[$togglecharpos - 1]) & $toggleflag) == $toggleflag);
  69. }
  70. /**
  71. * Sets the state of the requested Toggle number.
  72. * int $togglenum - The toggle number.
  73. * boolean $state - true or false.
  74. */
  75. public function set_toggle_state($togglenum, $state) {
  76. $togglecharpos = self::get_toggle_pos($togglenum);
  77. $toggleflag = self::get_toggle_flag($togglenum, $togglecharpos);
  78. $value = self::decode_character_to_value($this->toggles[$togglecharpos - 1]);
  79. if ($state == true) {
  80. $value |= $toggleflag;
  81. } else {
  82. $value &= ~$toggleflag;
  83. }
  84. $this->toggles[$togglecharpos - 1] = self::encode_value_to_character($value);
  85. }
  86. /**
  87. * Gets the string binary representation of the given toggle state.
  88. * string $toggles - Toggles state.
  89. * returns string.
  90. */
  91. public function decode_toggle_state($toggles) {
  92. $togglestate = '';
  93. $strlen = strlen($toggles);
  94. for ($chars = 0; $chars < $strlen; $chars++) {
  95. $charval = self::decode_character_to_value($toggles[$chars]);
  96. $togglestate .= (($charval & self::TOGGLE_1) == self::TOGGLE_1) ? '1' : '0';
  97. $togglestate .= (($charval & self::TOGGLE_2) == self::TOGGLE_2) ? '1' : '0';
  98. $togglestate .= (($charval & self::TOGGLE_3) == self::TOGGLE_3) ? '1' : '0';
  99. $togglestate .= (($charval & self::TOGGLE_4) == self::TOGGLE_4) ? '1' : '0';
  100. $togglestate .= (($charval & self::TOGGLE_5) == self::TOGGLE_5) ? '1' : '0';
  101. $togglestate .= (($charval & self::TOGGLE_6) == self::TOGGLE_6) ? '1' : '0';
  102. }
  103. return $togglestate;
  104. }
  105. /**
  106. * Tells us if the toggle state from the DB is an old one.
  107. * string $pref - Toggle state.
  108. * returns boolean old preference = true and not old preference = false.
  109. */
  110. public function is_old_preference($pref) {
  111. $retr = false;
  112. $firstchar = $pref[0];
  113. if (($firstchar == '0') || ($firstchar == '1')) {
  114. $retr = true;
  115. }
  116. return $retr;
  117. }
  118. /**
  119. * Tells us the number of digits we need to store the state for the number of toggles we have.
  120. * int $numtoggles - Number of toggles.
  121. * returns int - Number of digits required.
  122. */
  123. public function get_required_digits($numtoggles) {
  124. return self::get_toggle_pos($numtoggles);
  125. }
  126. /**
  127. * Tells us the minimum character used.
  128. * returns char - Digit positionS.
  129. */
  130. public function get_min_digit() {
  131. return ':'; // 58 ':'.
  132. }
  133. /**
  134. * Tells us the maximum character used.
  135. * returns char - Digit character.
  136. */
  137. public function get_max_digit() {
  138. return 'y'; // 58 'y'.
  139. }
  140. /**
  141. * Tells us digit postion for the toggle number.
  142. * int $togglenum - Toggle number.
  143. * returns int - Digit character.
  144. */
  145. private static function get_toggle_pos($togglenum) {
  146. return intval(ceil($togglenum / 6));
  147. }
  148. /**
  149. * Tells us the position of the bit within the digit for the given toggle number and digit position in the toggles state.
  150. * int $togglenum - Toggle number.
  151. * int $togglecharpos - Digit character position.
  152. * returns int - Digit flag.
  153. */
  154. private static function get_toggle_flag($togglenum, $togglecharpos) {
  155. $toggleflagpos = $togglenum - (($togglecharpos - 1) * 6);
  156. switch ($toggleflagpos) {
  157. case 1:
  158. $flag = self::TOGGLE_1;
  159. break;
  160. case 2:
  161. $flag = self::TOGGLE_2;
  162. break;
  163. case 3:
  164. $flag = self::TOGGLE_3;
  165. break;
  166. case 4:
  167. $flag = self::TOGGLE_4;
  168. break;
  169. case 5:
  170. $flag = self::TOGGLE_5;
  171. break;
  172. case 6:
  173. $flag = self::TOGGLE_6;
  174. break;
  175. }
  176. return $flag;
  177. }
  178. /**
  179. * Converts a character to a value so that its toggle state 'bit's can be read / set.
  180. * char $char - Digit.
  181. * returns int - Character value.
  182. */
  183. private static function decode_character_to_value($char) {
  184. return ord($char) - 58;
  185. }
  186. /**
  187. * Converts a value to a digit so that can be stored in the DB.
  188. * int $val - Value.
  189. * returns char - Digit.
  190. */
  191. private static function encode_value_to_character($val) {
  192. return chr($val + 58);
  193. }
  194. /**
  195. * Returns test result as HTML.
  196. */
  197. public function test() {
  198. $retr = '<h1>A='.self::decode_character_to_value('A').' - back:'.self::encode_value_to_character(7).'</h1><br /><p>';
  199. for ($i = 0; $i < 64; $i++) {
  200. $curr = self::encode_value_to_character($i);
  201. $val = self::decode_character_to_value($curr);
  202. $back = self::encode_value_to_character($val);
  203. $retr .= $curr.'='.$val.'='.$back.' ';
  204. }
  205. $retr .= '</p>';
  206. // Toggles:...
  207. $this->toggles = 'GjB'; // 001101 110000 001000 = 18 toggles.
  208. $retr .= '<p>Toggle string of GjB which is 001101 110000 001000 is:</p><p>';
  209. for ($j = 1; $j <= 18; $j++) {
  210. $retr .= 'TG: '.$j.' = '.(int)$this->get_toggle_state($j).' - ';
  211. }
  212. $retr .= '</p>';
  213. $retr .= '<p>Now set 5, 12, 15 (already set) and 18 and clear 3 and 7 is:</p><p>';
  214. $this->set_toggle_state(5, true);
  215. $this->set_toggle_state(12, true);
  216. $this->set_toggle_state(15, true);
  217. $this->set_toggle_state(18, true);
  218. $this->set_toggle_state(3, false);
  219. $this->set_toggle_state(7, false);
  220. for ($j = 1; $j <= 18; $j++) {
  221. $retr .= 'TG: '.$j.' = '.(int)$this->get_toggle_state($j).' - ';
  222. }
  223. $retr .= '</p>';
  224. return $retr;
  225. }
  226. /**
  227. * Returns a required_param() toggle value for the named user preference.
  228. *
  229. * @param string $parname the name of the user preference we want
  230. * @return mixed
  231. * @throws coding_exception
  232. */
  233. public static function required_topcoll_param($parname) {
  234. if (empty($parname)) {
  235. throw new coding_exception('required_topcoll_param() requires $parname to be specified');
  236. }
  237. $param = required_param($parname, PARAM_RAW);
  238. return self::clean_topcoll_param($param);
  239. }
  240. /**
  241. * Used by required_topcoll_param to clean the toggle parameter.
  242. *
  243. * @param string $param the variable we are cleaning
  244. * @return mixed
  245. * @throws coding_exception
  246. */
  247. public static function clean_topcoll_param($param) {
  248. if (is_array($param)) {
  249. throw new coding_exception('clean_topcoll_param() can not process arrays.');
  250. } else if (is_object($param)) {
  251. if (method_exists($param, '__toString')) {
  252. $param = $param->__toString();
  253. } else {
  254. throw new coding_exception('clean_topcoll_param() can not process objects.');
  255. }
  256. }
  257. $chars = strlen($param);
  258. for ($i = 0; $i < $chars; $i++) {
  259. $charval = ord($param[$i]);
  260. if (($charval < 58) || ($charval > 121)) {
  261. return false;
  262. }
  263. }
  264. return $param;
  265. }
  266. }