From d45c7f14891d951f8a6987cc6492073b97e05b5b Mon Sep 17 00:00:00 2001 From: Nicolas BERNSTEIN Date: Sun, 20 Nov 2011 13:39:47 +0100 Subject: Added the bit list study system --- lib/JSON/JSON.php | 806 ++++++++++++++++++++++++++++++++++++++++++++++ lib/JSON/inc_json.php | 22 ++ lib/conf/apps.php | 18 ++ lib/deck/edit.php | 2 +- lib/list/addbatch.php | 43 +++ lib/list/edbatch.php | 47 +++ lib/list/edit.php | 43 +++ lib/list/inc_process.php | 28 ++ lib/list/index.php | 25 ++ lib/list/new.php | 33 ++ lib/list/rmbatch.php | 20 ++ lib/list/view.php | 31 ++ lib/study/batch.php | 31 ++ lib/study/batchreview.php | 23 ++ lib/study/brresults.php | 35 ++ lib/study/list.php | 37 +++ lib/study/listadd.php | 13 + lib/study/listrm.php | 16 + 18 files changed, 1272 insertions(+), 1 deletion(-) create mode 100644 lib/JSON/JSON.php create mode 100644 lib/JSON/inc_json.php create mode 100644 lib/list/addbatch.php create mode 100644 lib/list/edbatch.php create mode 100644 lib/list/edit.php create mode 100644 lib/list/inc_process.php create mode 100644 lib/list/index.php create mode 100644 lib/list/new.php create mode 100644 lib/list/rmbatch.php create mode 100644 lib/list/view.php create mode 100644 lib/study/batch.php create mode 100644 lib/study/batchreview.php create mode 100644 lib/study/brresults.php create mode 100644 lib/study/list.php create mode 100644 lib/study/listadd.php create mode 100644 lib/study/listrm.php (limited to 'lib') diff --git a/lib/JSON/JSON.php b/lib/JSON/JSON.php new file mode 100644 index 0000000..0cddbdd --- /dev/null +++ b/lib/JSON/JSON.php @@ -0,0 +1,806 @@ + + * @author Matt Knapp + * @author Brett Stimmerman + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON($use = 0) + { + $this->use = $use; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, 'encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} + +?> diff --git a/lib/JSON/inc_json.php b/lib/JSON/inc_json.php new file mode 100644 index 0000000..b36c2a4 --- /dev/null +++ b/lib/JSON/inc_json.php @@ -0,0 +1,22 @@ +decode($content); + } +} + +if (!function_exists('json_encode')) { + function json_encode($content) { + require_once 'lib/JSON/JSON.php'; + $json = new Services_JSON; + return $json->encode($content); + } +} diff --git a/lib/conf/apps.php b/lib/conf/apps.php index ef491ee..d8cb1c2 100644 --- a/lib/conf/apps.php +++ b/lib/conf/apps.php @@ -40,6 +40,17 @@ $apps = array( "edent" => 1, // edit card "mvent" => 1, // move card ), + "list" => array( + "index" => 0, + "view" => 0, + "new" => 1, + "edit" => 1, // edit global info = name, desc, ... + + "addbatch" => 1, + "edbatch" => 1, + "rmbatch" => 1, + ), + "study" => array ( "index" => 1, "deckadd" => 1, @@ -47,6 +58,13 @@ $apps = array( "deckrm" => 1, "setrate" => 1, "setcard" => 1, + + "listadd" => 1, // create list_study + "list" => 1, // !!!!!! show global progress for a list_study + "listrm" => 1, // delete list_study + "batch" => 1, // (JS) shows progress for a batch_study + "batchreview" => 1, // (JS) batch review app + "brresults" => 1, ), ); diff --git a/lib/deck/edit.php b/lib/deck/edit.php index 3d893a2..d4102bb 100644 --- a/lib/deck/edit.php +++ b/lib/deck/edit.php @@ -32,7 +32,7 @@ if (isset($_POST['name']) && isset($_POST['comment'])) { } } -$title = "Edit : " . $deck['name']; +$title = "Edit deck : " . $deck['name']; $fields = array( array("label" => "Name : ", "name" => "name", "value" => $deck_name), array("label" => "Comment : ", "name" => "comment", "type" => "textarea", "value" => $deck_comment), diff --git a/lib/list/addbatch.php b/lib/list/addbatch.php new file mode 100644 index 0000000..5cb5fb6 --- /dev/null +++ b/lib/list/addbatch.php @@ -0,0 +1,43 @@ + "Name : ", "name" => "name", "value" => $batch_name), + array("label" => "Columns : ", "name" => "model", "value" => $batch_model), + array("label" => "Contents : ", "name" => "contents", "type" => "textarea", "value" => $batch_contents) +); +$validate = "Add batch"; + +require("tpl/list/ef.php"); diff --git a/lib/list/edbatch.php b/lib/list/edbatch.php new file mode 100644 index 0000000..380ecf2 --- /dev/null +++ b/lib/list/edbatch.php @@ -0,0 +1,47 @@ + $batch['listid'], 'name' => $batch['listname']); + +$batch_name = $batch['name']; +$batch_model = $batch['model']; +$batch_contents = $batch['contents']; +if (isset($_POST['name']) && isset($_POST['model']) && isset($_POST['contents'])) { + $batch_name = esca($_POST['name']); + $batch_model = esca($_POST['model']); + $batch_contents = esca($_POST['contents']); + $batch_json = mk_batch_json($batch_model, $batch_contents); + if ($batch_name == "") { + $error = "You must give a name to this batch."; + } else if (mysql_fetch_assoc(sql("SELECT id FROM batches WHERE list = " . $list['id'] . " AND name = '" . escs($batch_name) . "' AND id != $batchid"))) { + $error = "You already have a batch using that name."; + } else { + sql( + "UPDATE batches SET name = '" . escs($batch_name) . "', model = '" . escs($batch_model) . "', ". + "contents = '" . escs($batch_contents) . "', json_data = '" . escs($batch_json) . "' WHERE id = $batchid" + ); + header("Location: view-list-" . $list['id']); + die(); + } +} + +$title = "Edit batch in " . $list['name']; +$fields = array( + array("label" => "Name : ", "name" => "name", "value" => $batch_name), + array("label" => "Columns : ", "name" => "model", "value" => $batch_model), + array("label" => "Contents : ", "name" => "contents", "type" => "textarea", "value" => $batch_contents) +); +$validate = "Edit batch"; + +require("tpl/list/ef.php"); diff --git a/lib/list/edit.php b/lib/list/edit.php new file mode 100644 index 0000000..64394be --- /dev/null +++ b/lib/list/edit.php @@ -0,0 +1,43 @@ + "Name : ", "name" => "name", "value" => $list_name), + array("label" => "Comment : ", "name" => "comment", "type" => "textarea", "value" => $list_comment), +); +$validate = "Edit list"; + +require("tpl/list/ef.php"); + diff --git a/lib/list/inc_process.php b/lib/list/inc_process.php new file mode 100644 index 0000000..d4656b5 --- /dev/null +++ b/lib/list/inc_process.php @@ -0,0 +1,28 @@ + array(), "items" => array()); + + $columns = explode('|', $models); + foreach ($columns as $c) { + if ($c[0] == '!') { + $data['columns'][] = array("question" => false, "name" => substr($c, 1)); + } else { + $data['columns'][] = array("question" => true, "name" => $c); + } + } + + $items = explode("\n", $contents); + foreach($items as $i) { + $ii = explode('|', str_replace("\r", '', $i)); + if (count($ii) == count($columns)) { + $data['items'][] = $ii; + } + } + + return json_encode($data); +} + + diff --git a/lib/list/index.php b/lib/list/index.php new file mode 100644 index 0000000..cbcad67 --- /dev/null +++ b/lib/list/index.php @@ -0,0 +1,25 @@ + array ( + "nbUsers" => "popularity", + "name" => "name", + "owner" => "author", + ), + "way" => $ord_ways, +); +$fdefaults = array ( + "order" => "nbUsers", + "way" => "DESC", +); + +$lists = array(); +$n = sql( + "SELECT lists.id AS id, lists.name AS name, account.login AS owner, COUNT(list_study.id) AS nbUsers ". + "FROM lists LEFT JOIN account ON lists.owner = account.id LEFT JOIN list_study ON list_study.list = lists.id ". + "GROUP BY lists.id ORDER BY " . get_filter("order") . " " . get_filter("way") + ); +while ($nn = mysql_fetch_assoc($n)) $lists[] = $nn; + +require("tpl/list/index.php"); + diff --git a/lib/list/new.php b/lib/list/new.php new file mode 100644 index 0000000..9a9c801 --- /dev/null +++ b/lib/list/new.php @@ -0,0 +1,33 @@ + "Name : ", "name" => "name", "value" => $list_name), + array("label" => "Comment : ", "name" => "comment", "type" => "textarea", "value" => $list_comment), + ); +$validate = "Create list"; + +require("tpl/list/new.php"); + diff --git a/lib/list/rmbatch.php b/lib/list/rmbatch.php new file mode 100644 index 0000000..90ea370 --- /dev/null +++ b/lib/list/rmbatch.php @@ -0,0 +1,20 @@ += 3, 'list'); +$batchid = intval($args[2]); + +$batch = mysql_fetch_assoc(sql( + "SELECT lists.id AS listid, lists.owner AS listowner, lists.name AS listname, batches.name AS name, ". + "batches.model AS model, batches.contents AS contents ". + "FROM batches LEFT JOIN lists ON lists.id = batches.list ". + "WHERE batches.id = $batchid")); +assert_error($batch && $batch['listowner'] == $user['id'], + "this batch does not exist, or you are not allowed to edit it."); + +token_validate("Do you really want to delete this batch ?", "view-list-" . $batch['listid']); + +sql("DELETE FROM batches WHERE id = $batchid"); +sql("DELETE FROM batch_study WHERE batch = $batchid"); +sql("DELETE FROM batch_review WHERE batch = $batchid"); +header("Location: view-list-" . $batch['listid']); +die(); diff --git a/lib/list/view.php b/lib/list/view.php new file mode 100644 index 0000000..7a05bf2 --- /dev/null +++ b/lib/list/view.php @@ -0,0 +1,31 @@ + array( + "name" => "name", + "lr_score" => "last score" + ), + "way" => $ord_ways, +); +$fdefaults = array( + "order" => "name", + "way" => "ASC", +); + +$batches = array(); +$n = sql( + "SELECT batches.id AS id, batches.name AS name, ". + "batch_study.id AS bs_id, batch_review.date AS lr_date, batch_review.score AS lr_score ". + "FROM batches ". + "LEFT JOIN batch_study ON batch_study.batch = batches.id AND batch_study.user = " . $user['id'] . " " . + "LEFT JOIN batch_review ON batch_review.id = batch_study.last_review ". + "WHERE batches.list = " . $study['listid'] . " " . + "ORDER BY " . get_filter("order") . " " . get_filter("way") + ); +while ($b = mysql_fetch_assoc($n)) $batches[] = $b; + +require("tpl/study/list.php"); diff --git a/lib/study/listadd.php b/lib/study/listadd.php new file mode 100644 index 0000000..536ab1d --- /dev/null +++ b/lib/study/listadd.php @@ -0,0 +1,13 @@ += 3, 'study'); +$studyid = intval($args[2]); + +$study = mysql_fetch_assoc(sql( + "SELECT ". + " list_study.user AS learn_user ". + "FROM list_study ". + "WHERE list_study.id = $studyid")); +assert_error($study && $study['learn_user'] == $user['id'], "You are not at the right place here."); + +token_validate("Do you really want to remove this list from your studies ? Your progress will not be lost.", "list-study-$studyid"); +sql("DELETE FROM list_study WHERE id = $studyid"); +header("Location: study"); +die(); -- cgit v1.2.3