diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2fa7ce7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.ini diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..9e00fd9 --- /dev/null +++ b/.htaccess @@ -0,0 +1,3 @@ +RewriteEngine on +RewriteRule ^(config|res|script|style|subs)($|/) - [L] +RewriteRule ^([a-zA-Z_]+)(\/([a-zA-Z0-9_]))?$ index.php?view=$1 [L,QSA] diff --git a/config/.htaccess b/config/.htaccess new file mode 100644 index 0000000..7d3aaf1 --- /dev/null +++ b/config/.htaccess @@ -0,0 +1,2 @@ +Order allow,deny +Deny from all \ No newline at end of file diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..30f543d --- /dev/null +++ b/config/config.php @@ -0,0 +1,141 @@ +. + **/ + +/* + * Includes + */ +require_once("lib/loginManager/loginManager.php"); +require_once("lib/PasswordStorage.php"); +require_once("lib/functions.php"); + +/* + * Load config file + */ +$config=parse_ini_file("config.ini", true); + +/* + * Regionalization + */ +date_default_timezone_set($config['general']['timezone']); +mb_internal_encoding("UTF-8"); + +/* + * Load language file + */ +$lang=parse_ini_file("lang/".$config['language']['use']); + +/* + * Set up DB + */ +$db=new PDO($config['database']['type'].":host=".$config['database']['host'].";dbname=".$config['database']['name'].";charset=utf8", $config['database']['user'], $config['database']['password']); + +/* + * Byte order mark for utf8 + */ +$BOM=chr(239).chr(187).chr(191); + +/* + * Debug + */ +if($config['general']['debug']){ + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + ini_set("display_errors", true); +} + +/* + * Versioning + */ +const VERSION="0.7"; + +/* + * Set up login manager + */ +//build classes +class handler implements lmHandler{ + public function handle($state, $target=0){ + global $db; + switch($state){ + case lmStates::LOGIN_FAILED: + functions::setError(1); + functions::safeReload(); + break; + case lmStates::LOGIN_OK: + $sql=$db->prepare("SELECT id, username, fullname, accesslevel, class, perm_message FROM users WHERE id=:id"); + $sql->execute(array(":id"=>$target)); + $res=$sql->fetch(PDO::FETCH_ASSOC); + $_SESSION['id']=$res['id']; + $_SESSION['username']=$res['username']; + $_SESSION['fullname']=$res['fullname']; + $_SESSION['accesslevel']=$res['accesslevel']; + $_SESSION['class']=$res['class']; + $_SESSION['perm_message']=$res['perm_message']; + functions::safeReload(); + break; + case lmStates::CAPTCHA_FAILED: + functions::setError(2); + functions::safeReload(); + break; + case lmStates::BANNED: + functions::setError(3); + functions::safeReload(); + break; + case lmStates::FORGET_DONE: + functions::setMessage(1); + functions::safeReload(); + break; + case lmStates::LOGOUT_DONE: + functions::setMessage(2); + functions::safeReload(); + } + return; + } +} +class password implements lmPassword{ + public function verifyPassword($cleartext, $database){ + if($database==""){ + return false; + } + return PasswordStorage::verify_password($cleartext, $database); + } +} +class twoFactor implements lmTwoFactor{ + public function secondFactor($uid){ + return true; + } +} + +//build login manager +$lm=new loginManager(new lmConfig($db, $config['login']['session_lifetime'], $config['login']['captcha_enable'], $config['login']['captcha_after'], $config['login']['captcha_sitekey'], $config['login']['captcha_secretkey'], $config['login']['ban_enable'], $config['login']['ban_after'], $config['login']['ban_time'], $config['login']['look'], $config['login']['remember_enable'], $config['login']['remember_time'], lmStates::AUTH_UNAME), new handler(), new password(), new twoFactor()); +//init +$lm->init(); + +//get the current schoolyear +$schoolyear=""; +if(date("m")>=$config['general']['newYearMonth']){ + $schoolyear=date("Y")."-".date("Y", time()+31556926); +} +else{ + $schoolyear=date("Y", time()-31556926)."-".date("Y"); +} diff --git a/config/lang/hun.ini b/config/lang/hun.ini new file mode 100644 index 0000000..becc61c --- /dev/null +++ b/config/lang/hun.ini @@ -0,0 +1,139 @@ +lang_1="Magyar" +lang_2="Román" +cookie_message="Weboldalunk a megfelelő működés érdekében cookie-kat használ. Továbbá a weboldal használatával ön beleegyezik a felhasználási feltételekbe (lásd az oldal alján)." +cookie_dismiss="Megértettem!" +login="Bejelentkezés" +username="Felhasználónév" +password="Jelszó" +password_confirm="Jelszó megerősítése" +remember="Megjegyzés" +ok="Mehet!" +forget_user="Felhasználó elfelejtése" +remember_as="Üdv újra," +index="Kezdőlap" +wizard="Varázsló" +users="Felhasználók" +classes="Osztályok" +subjects="Tantárgyak" +contests="Versenyek" +phases="Szakaszok" +register="Eredmények" +profile="Profil" +admin="Adminisztrátori felület" +loading="Töltés" +logout="Kijelentkezés" +index_content="Ezen az oldalon kezelheti a diákok eredményeit.

A Felhasználók menüpont alatt a felhasználók listáját láthatja.
Az Osztályok menüpont alatt az osztályokat tekintheti át.
A Tantárgyak menüpont alatt a tantárgyakat nézheti meg és szerkesztheti.
A Versenyek menüpont alatt versenyeket adhat hozzá és távolíthat el.
A Szakaszok menüpontnál a versenyek szakaszait (pl: megyei, országos) kezelheti.
Az Eredmények menüpont alatt a diákok eredményeit nézheti meg illetve szerkesztheti.
A Varázsló menüpont alatt egy egyszerűen kezelhető eszközzel adhatja meg egy diák elért eredényeit.
A Profil menüpontnál saját felhasználójának az adatait szerkesztheti.
Az Adminisztrátori felület alól az adminisztrátori eszközökhöz férhet hozzá." +filter="Szűrők" +search="Keresés" +class="Osztály" +accesslevel="Jogszint" +empty="Üres" +apply="Alkalmaz" +reset="Visszaállítás" +new="Új hozzáadása" +fullname="Teljes név" +passwordhint="Írjon be 0-t egy véletlenszerű jelszóért!" +passwordhintedit="Hagyja üresen, ha nem szeretné megváltoztatni." +cancel="Mégse" +edit="Szerkesztés" +id="Azonosító" +perm_message="Üzenetküldési jog" +tools="Eszközök" +delete="Törlés" +edit_perm="Jogok szerkesztése" +delete_confirm="Biztosan szeretné törölni ezt az elemet?" +permission_confirm="Szeretné ha ez az ember tudna üzeneteket küldeni a belső rendszerben?" +ryes="Igen" +rno="Nem" +rowid="Sorszám" +role="Szerep" +headteacher="Osztályfőnök" +student="Diák" +name_1="Magyar megnevezés" +name_2="Román megnevezés" +subject="Tantárgy" +ministry_support="Minisztériumi támogatás" +ministry_0="Nem szerepel a listán" +ministry_1="Nem támogatott" +ministry_2="Támogatott" +nothing="Semmi" +description="Leírás" +ministry_place="Hely a minisztériumi listán" +subject_1="Tantárgy magyar megnevezése" +subject_2="Tantárgy román megnevezése" +custom="Egyedi" +mention="Megjegyzés" +newSubmit="Véglegesítés" +contest_1="Verseny magyar megnevezése" +contest_2="Verseny román megnevezése" +phase_1="Szakasz magyar megnevezése" +phase_2="Szakasz román megnevezése" +teacher="Felkészítő tanár" +place="Helyezés" +contest="Verseny" +phase="Szakasz" +place="Helyezés" +prev_found="Ezeket a bejegyzéseket találtuk, amelyeknél megegyezik a verseny és a diák a bevinni kívánt adatokkal" +do_before_new_record="Kérem nézze át az alább kilistázott bejegyzéseket és törölje azokat, amelyek nem aktuálisak. Ha ezzel végzett csak akkor véglegesítse az új bejegyzést!" +proceed="Tovább" +back="Vissza" +finalize="Véglegesítés" +selectme="Saját magam beállítása" +wizinst="Ennek az eszköznek a segítségével könnyedén hozzáadhat egy új eredményt" +wiz_step1="Diák kiválasztása" +wiz_step2="Verseny kiválasztása" +wiz_step3="Szakasz kiválasztása" +wiz_step4="Felkészítő tanár kiválasztása" +wiz_step5="Helyezés megadása" +wiz_step6="További információk megadása (nem kötelező)" +wiz_step7="Előzőleg megadott adatok felülvizsgálata, véglegesítés" +needlogout="A változtatások csak a következő bejelentkezésnél fognak megjelenni." +editpasswd="Jelszó megváltoztatása" +export="Exportálás" +exportcolumn="A következő oszlopokat tartalmazza" +contest_desc="Verseny leírása" +exported="Exportálva" +preparing_download="Letöltés előkészítése." +download_ready="A fájl készen áll a letöltésre." +download="Letöltés" +schoolyear="Tanév" +importUsers="Felhasználók importálása" +dbsize="Adatbázis mérete" +dbname="Adatbázis neve" +size="Méret" +uploading="Feltöltés" +processing="Feldolgozás" +importdone="A felhasználók importálása befejeződött." +importnotes="Az importáláshoz egy CSV fájlt (delimiter: ,) kell feltölteni. Maximális mérete 10MB lehet. A fájl nem tartalmazhat fejlécet csupán adatot, soronként egy adatelemmel. Az első oszlop a felhasználó felhasználóneve, a második oszlop a teljes neve, a harmadik oszlop a jogszintje (0: diák, 1: tanár, 2: osztályfőnök, 3: menedzser, 4: adminisztrátor), ötödik oszlop az osztály, hatodik oszlop pedig a jelszó. A CSV fájl generálásához ajánlott a Google Docs-ot használni, mivel az ismeri az UTF8-at is az Excel-lel ellentétben." +classformat="pl.: L2015F" + +;places +places[-3]="Részvétel" +places[-2]="Különdíj" +places[-1]="Dícséret" + +;errors +error[1]="Hibás felhasználónév vagy jelszó!" +error[2]="Hibásan töltötted ki a Captcha-t!" +error[3]="Túl sok hibás bejelentkezés történt erről az IP címről, ezért 10 percre letitltottuk." +error[4]="Valami rosszul ment. Kérjük próbálja meg később! Ha továbbra sem sikerül, kérjuk hagyjon üzenetet!" +error[5]="Ez a felhasználónév már foglalt." +error[6]="Nem található semmi ezzel az ID-val." +error[7]="Már létezik egy tantárgy ezzel a névvel." +error[8]="Már létezik egy verseny ezzel a névvel." +error[9]="Már létezik egy szakasz ezzel a névvel." +error[10]="A megadott jelszavak nem egyeznek meg." +error[11]="A fájl mérete nem haladhatja meg az 10MB-ot!" +error[12]="Valami hiba történt a feltöltés közben. Kérem próbálja újra!" +error[13]="Váratlan hiba a feltöltés közben. Kérem próbálja újra!" +error[401]="Önnek nincs jogosultsága ennek az oldalnak a megtekintésére!" +error[404]="A keresett oldal nem található!" +error[500]="Valami rosszul ment. Kérjük próbálja meg később, illetve jelezze üzenet formájában, hogy 500-as kódjelű hibát kapott." + +;messages +message[1]="Sikeresen elfelejtettük a felhasználódat." +message[2]="Sikeresen kijelentkeztél." +message[3]="Adat sikeresen hozzáadva!" +message[4]="Adat sikeresen törölve!" +message[5]="Adat sikeresen szerkesztve!" +message[6]="A jelszavadat sikeresen frissítettük" diff --git a/config/lib/PasswordStorage.php b/config/lib/PasswordStorage.php new file mode 100644 index 0000000..688694b --- /dev/null +++ b/config/lib/PasswordStorage.php @@ -0,0 +1,315 @@ += 1." + ); + } + + return self::slow_equals( + $pbkdf2, + self::pbkdf2( + $params[self::HASH_ALGORITHM_INDEX], + $password, + $salt_raw, + $iterations, + self::ourStrlen($pbkdf2), + true + ) + ); + } + + /** + * Compares two strings $a and $b in length-constant time. + * + * @param string $a + * @param string $b + * @return bool + */ + public static function slow_equals($a, $b) + { + if (!\is_string($a) || !\is_string($b)) { + throw new InvalidArgumentException( + "slow_equals(): expected two strings" + ); + } + if (\function_exists('hash_equals')) { + return \hash_equals($a, $b); + } + + // PHP < 5.6 polyfill: + $diff = self::ourStrlen($a) ^ self::ourStrlen($b); + for($i = 0; $i < self::ourStrlen($a) && $i < self::ourStrlen($b); $i++) { + $diff |= \ord($a[$i]) ^ \ord($b[$i]); + } + return $diff === 0; + } + + /* + * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt + * $algorithm - The hash algorithm to use. Recommended: SHA256 + * $password - The password. + * $salt - A salt that is unique to the password. + * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000. + * $key_length - The length of the derived key in bytes. + * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise. + * Returns: A $key_length-byte key derived from the password and salt. + * + * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt + * + * This implementation of PBKDF2 was originally created by https://defuse.ca + * With improvements by http://www.variations-of-shadow.com + */ + public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) + { + // Type checks: + if (!\is_string($algorithm)) { + throw new InvalidArgumentException( + "pbkdf2(): algorithm must be a string" + ); + } + if (!\is_string($password)) { + throw new InvalidArgumentException( + "pbkdf2(): password must be a string" + ); + } + if (!\is_string($salt)) { + throw new InvalidArgumentException( + "pbkdf2(): salt must be a string" + ); + } + // Coerce strings to integers with no information loss or overflow + $count += 0; + $key_length += 0; + $algorithm = \strtolower($algorithm); + if (!\in_array($algorithm, \hash_algos(), true)) { + throw new CannotPerformOperationException( + "Invalid or unsupported hash algorithm." + ); + } + // Whitelist, or we could end up with people using CRC32. + $ok_algorithms = array( + "sha1", "sha224", "sha256", "sha384", "sha512", + "ripemd160", "ripemd256", "ripemd320", "whirlpool" + ); + if (!\in_array($algorithm, $ok_algorithms, true)) { + throw new CannotPerformOperationException( + "Algorithm is not a secure cryptographic hash function." + ); + } + if ($count <= 0 || $key_length <= 0) { + throw new CannotPerformOperationException( + "Invalid PBKDF2 parameters." + ); + } + + if (\function_exists("hash_pbkdf2")) { + // The output length is in NIBBLES (4-bits) if $raw_output is false! + if (!$raw_output) { + $key_length = $key_length * 2; + } + return \hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output); + } + + $hash_length = self::ourStrlen(\hash($algorithm, "", true)); + $block_count = \ceil($key_length / $hash_length); + + $output = ""; + for($i = 1; $i <= $block_count; $i++) { + // $i encoded as 4 bytes, big endian. + $last = $salt . \pack("N", $i); + // first iteration + $last = $xorsum = \hash_hmac($algorithm, $last, $password, true); + // perform the other $count - 1 iterations + for ($j = 1; $j < $count; $j++) { + $xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true)); + } + $output .= $xorsum; + } + + if($raw_output) { + return self::ourSubstr($output, 0, $key_length); + } else { + return \bin2hex(self::ourSubstr($output, 0, $key_length)); + } + } + /* + * We need these strlen() and substr() functions because when + * 'mbstring.func_overload' is set in php.ini, the standard strlen() and + * substr() are replaced by mb_strlen() and mb_substr(). + */ + /** + * Calculate the length of a string + * + * @param string $str + * @return int + */ + private static function ourStrlen($str) + { + static $exists = null; + if ($exists === null) { + $exists = \function_exists('mb_strlen'); + } + + if (!\is_string($str)) { + throw new InvalidArgumentException( + "ourStrlen() expects a string" + ); + } + + if ($exists) { + $length = \mb_strlen($str, '8bit'); + if ($length === false) { + throw new CannotPerformOperationException(); + } + return $length; + } else { + return \strlen($str); + } + } + /** + * Substring + * + * @param string $str + * @param int $start + * @param int $length + * @return string + */ + private static function ourSubstr($str, $start, $length = null) + { + static $exists = null; + if ($exists === null) { + $exists = \function_exists('mb_substr'); + } + // Type validation: + if (!\is_string($str)) { + throw new InvalidArgumentException( + "ourSubstr() expects a string" + ); + } + + if ($exists) { + // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP + // 5.3, so we have to find the length ourselves. + if (!isset($length)) { + if ($start >= 0) { + $length = self::ourStrlen($str) - $start; + } else { + $length = -$start; + } + } + return \mb_substr($str, $start, $length, '8bit'); + } + // Unlike mb_substr(), substr() doesn't accept NULL for length + if (isset($length)) { + return \substr($str, $start, $length); + } else { + return \substr($str, $start); + } + } +} diff --git a/config/lib/functions.php b/config/lib/functions.php new file mode 100644 index 0000000..77b8eb4 --- /dev/null +++ b/config/lib/functions.php @@ -0,0 +1,274 @@ +,<"; + } + $charsetlength=strlen($charset); + $string=""; + for($i=0; $i<$length; $i++){ + $string=$string . $charset[rand(0, $charsetlength-1)]; + } + return $string; + } + + public static function get_string_between($string, $start, $end){ + $string=' ' . $string; + $ini=strpos($string, $start); + if($ini==0) return ''; + $ini+=strlen($start); + $len=strpos($string, $end, $ini) - $ini; + return substr($string, $ini, $len); + } + + public static function process_string($str, $dep){ + global $functions_accent_convert; + switch($dep){ + case 0: + { + return $str; + break; + } + case 1: + { + return strtolower($str); + break; + } + case 2: + { + return strtr($str, $functions_accent_convert); + break; + } + case 3: + { + return strtolower(strtr($str, $functions_accent_convert)); + break; + } + } + return 0; + } + + public static function validate_captcha($secretkey, $response){ + $verify=file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secretkey."&response=".$response); + $data=json_decode($verify); + if($data->success){ + return true; + } + else{ + return false; + } + } +}; + +$functions_accent_convert=array( + // Decompositions for Latin-1 Supplement + chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', + chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', + chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', + chr(195).chr(135) => 'C', chr(195).chr(136) => 'E', + chr(195).chr(137) => 'E', chr(195).chr(138) => 'E', + chr(195).chr(139) => 'E', chr(195).chr(140) => 'I', + chr(195).chr(141) => 'I', chr(195).chr(142) => 'I', + chr(195).chr(143) => 'I', chr(195).chr(145) => 'N', + chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', + chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', + chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', + chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', + chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', + chr(195).chr(159) => 's', chr(195).chr(160) => 'a', + chr(195).chr(161) => 'a', chr(195).chr(162) => 'a', + chr(195).chr(163) => 'a', chr(195).chr(164) => 'a', + chr(195).chr(165) => 'a', chr(195).chr(167) => 'c', + chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', + chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', + chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', + chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', + chr(195).chr(177) => 'n', chr(195).chr(178) => 'o', + chr(195).chr(179) => 'o', chr(195).chr(180) => 'o', + chr(195).chr(181) => 'o', chr(195).chr(182) => 'o', + chr(195).chr(182) => 'o', chr(195).chr(185) => 'u', + chr(195).chr(186) => 'u', chr(195).chr(187) => 'u', + chr(195).chr(188) => 'u', chr(195).chr(189) => 'y', + chr(195).chr(191) => 'y', + // Decompositions for Latin Extended-A + chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', + chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', + chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', + chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', + chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', + chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', + chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', + chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', + chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', + chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', + chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', + chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', + chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', + chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', + chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', + chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', + chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', + chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', + chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', + chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', + chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', + chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', + chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', + chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', + chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', + chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', + chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', + chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', + chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', + chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', + chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', + chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', + chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', + chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', + chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', + chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', + chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', + chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', + chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', + chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', + chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', + chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', + chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', + chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', + chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', + chr(197).chr(154) => 'S',chr(197).chr(155) => 's', + chr(197).chr(156) => 'S',chr(197).chr(157) => 's', + chr(197).chr(158) => 'S',chr(197).chr(159) => 's', + chr(197).chr(160) => 'S', chr(197).chr(161) => 's', + chr(197).chr(162) => 'T', chr(197).chr(163) => 't', + chr(197).chr(164) => 'T', chr(197).chr(165) => 't', + chr(197).chr(166) => 'T', chr(197).chr(167) => 't', + chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', + chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', + chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', + chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', + chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', + chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', + chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', + chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', + chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', + chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', + chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', + chr(197).chr(190) => 'z', chr(197).chr(191) => 's'); + +?> diff --git a/config/lib/loginManager/lmConfig.php b/config/lib/loginManager/lmConfig.php new file mode 100644 index 0000000..d8f8f89 --- /dev/null +++ b/config/lib/loginManager/lmConfig.php @@ -0,0 +1,82 @@ +pdo=$_pdo; + $this->session_lifetime=$_session_lifetime; + $this->captcha_enable=$_captcha_enable; + $this->captcha_after=$_captcha_after; + $this->captcha_sitekey=$_captcha_sitekey; + $this->captcha_secretkey=$_captcha_secretkey; + $this->ban_enable=$_ban_enable; + $this->ban_after=$_ban_after; + $this->ban_time=$_ban_time; + $this->look=$_look; + $this->remember_enable=$_remember_enable; + $this->remember_time=$_remember_time; + $this->auth_type=$_auth_type; + } + + private $pdo; + private $session_lifetime; + private $captcha_enable; + private $captcha_after; + private $captcha_sitekey; + private $captcha_secretkey; + private $ban_enable; + private $ban_after; + private $ban_time; + private $look; + private $remember_enable; //NOT SAFE AT ALL!!! + private $remember_time; + private $auth_type; + + public function getPDO(){ + return $this->pdo; + } + public function getSessionLifetime(){ + return $this->session_lifetime; + } + public function isCaptchaEnabled(){ + return $this->captcha_enable; + } + public function getCaptchaAfter(){ + return $this->captcha_after; + } + public function getCaptchaSitekey(){ + return $this->captcha_sitekey; + } + public function getCaptchaSecretkey(){ + return $this->captcha_secretkey; + } + public function isBanEnabled(){ + return $this->ban_enable; + } + public function getBanAfter(){ + return $this->ban_after; + } + public function getBanTime(){ + return $this->ban_time; + } + public function getLook(){ + return $this->look; + } + public function isRememberEnabled(){ + return $this->remember_enable; + } + public function getRememberTime(){ + return $this->remember_time; + } + public function getAuthType(){ + return $this->auth_type; + } +} + +?> diff --git a/config/lib/loginManager/lmHandler.php b/config/lib/loginManager/lmHandler.php new file mode 100644 index 0000000..43fbf32 --- /dev/null +++ b/config/lib/loginManager/lmHandler.php @@ -0,0 +1,14 @@ + diff --git a/config/lib/loginManager/lmPassword.php b/config/lib/loginManager/lmPassword.php new file mode 100644 index 0000000..660e4e4 --- /dev/null +++ b/config/lib/loginManager/lmPassword.php @@ -0,0 +1,14 @@ + diff --git a/config/lib/loginManager/lmStates.php b/config/lib/loginManager/lmStates.php new file mode 100644 index 0000000..8c509c3 --- /dev/null +++ b/config/lib/loginManager/lmStates.php @@ -0,0 +1,24 @@ + diff --git a/config/lib/loginManager/lmTwoFactor.php b/config/lib/loginManager/lmTwoFactor.php new file mode 100644 index 0000000..749e58a --- /dev/null +++ b/config/lib/loginManager/lmTwoFactor.php @@ -0,0 +1,14 @@ + diff --git a/config/lib/loginManager/lmUtils.php b/config/lib/loginManager/lmUtils.php new file mode 100644 index 0000000..f841de5 --- /dev/null +++ b/config/lib/loginManager/lmUtils.php @@ -0,0 +1,44 @@ +,<"; + $charsetLength=strlen($charset); + $string=""; + for($i=0; $i<$length; $i++){ + $string.=$charset[rand(0, $charsetLength-1)]; + } + return $string; + } + + /** + * validate google ReCaptcha + * @param string $secretkey secret key to captcha API + * @param string $response response of API + * @return bool + */ + public static function validateCaptcha($secretkey, $response){ + $verify=file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secretkey."&response=".$response); + $data=json_decode($verify); + if($data->success){ + return true; + } + else{ + return false; + } + } +} + +?> diff --git a/config/lib/loginManager/loginManager.php b/config/lib/loginManager/loginManager.php new file mode 100644 index 0000000..c51479f --- /dev/null +++ b/config/lib/loginManager/loginManager.php @@ -0,0 +1,393 @@ + + * + */ + + +/* + * Includes + */ +require("lmStates.php"); +require("lmConfig.php"); +require("lmHandler.php"); +require("lmPassword.php"); +require("lmTwoFactor.php"); +require("lmUtils.php"); + + +/* + * Class + */ +class loginManager{ + //constructor + + /** + * building... + * @param lmConfig $_config configuration for login Manager + * @param lmHandler $_eventHandler handler of events + * @param lmPassword $_passwordEngine engine for verifying passwords + * @return void + */ + public function __construct($_config, $_eventHandler, $_passwordEngine, $_twoFactor){ + $this->config=$_config; + $this->eventHandler=$_eventHandler; + $this->passwordEngine=$_passwordEngine; + $this->twoFactor=$_twoFactor; + return; + } + + + + //settings + + private $config; + private $eventHandler; + private $passwordEngine; + private $twoFactor; + + + + //frontend functions + + /** + * initialize session and set its lifetime + * @return bool + */ + public function init(){ + session_set_cookie_params($this->config->getSessionLifetime()); + return session_start(); + } + + /** + * prepare for login. Run this on the top of your login page! + * @return void + */ + public function loginPrepare(){ + $this->passFailedAttempts(); + return; + } + + /** + * lets start here! + * @param int/string @identifier id or username of user + * @param string @password cleartext password from input + * @param bool $remember save user fot further use + * @return void + */ + public function login($identifier, $password, $remember=false){ + global $lm_force_captcha; + + if($this->passFailedAttempts()){ //not banned + if(isset($lm_force_captcha)){ //check captcha + if(!isset($_POST['g-recaptcha-response'])){ + $captcha_failed=true; + $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); + $this->eventHandler->handle(lmStates::CAPTCHA_FAILED); + return; + } + else{ + if(!lmUtils::validateCaptcha($this->config->getCaptchaSecretkey(), $_POST['g-recaptcha-response'])){ + $captcha_failed=true; + $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); + $this->eventHandler->handle(lmStates::CAPTCHA_FAILED); + return; + } + } + } + + if(!isset($captcha_failed)){ + if($this->config->isRememberEnabled()){ //check if remembering is enabled + if($this->isRememberingUser() && $this->twoFactor->secondFactor($this->isRememberingUser())){ //remembering. + $this->permitLogin($this->isRememberingUser()); //good to go! + return; + } + } + //proceed with normal login + if($this->config->getAuthType()==lmStates::AUTH_UNAME){ //username based authentication + $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count, id, password FROM users WHERE username=:identifier and id<>1"); + } + else{ + $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count, id, password FROM users WHERE id=:identifier and id<>1"); + } + $sql->execute(array(":identifier"=>$identifier)); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + if($res['count']==0){ //user not existing + $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); + $this->eventHandler->handle(lmStates::LOGIN_FAILED); + return; + } + else{ + if($this->passwordEngine->verifyPassword($password, $res['password']) && $this->twoFactor->secondFactor($res['id'])){ + if($this->config->isRememberEnabled()){ //remember... if he wants to be insecure + if($remember){ + $this->rememberUser($res['id']); + } + } + $this->permitLogin($res['id']); //good to go! + return; + } + else{ + $this->addLoginHistory($res['id'], lmStates::LOGIN_FAILED); + $this->eventHandler->handle(lmStates::LOGIN_FAILED); + return; + } + } + } + } + return; + } + + /** + * finish it up! + * @return void + */ + public function logout(){ + $_SESSION=array(); + session_destroy(); + setcookie("lm_login_random", NULL, -1); + $this->eventHandler->handle(lmStates::LOGOUT_DONE); + return; + } + + /** + * just some formal checking + * @return bool + */ + public function validateLogin(){ + if(!isset($_SESSION['lm_id'])){ + return false; + } + else{ + $sql=$this->config->getPDO()->prepare("SELECT auth_token FROM login_history WHERE user=:id and success=1 ORDER BY id DESC LIMIT 1"); + $sql->execute(array(":id"=>$_SESSION['lm_id'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + if($res['auth_token']==$this->getSessionKey()){ + return true; + } + else{ + return false; + } + } + } + + /** + * do i know you? + * @return int + */ + public function isRememberingUser(){ + if(!$this->config->isRememberEnabled()){ + return NULL; + } + + if(is_null($this->getRememberKey())){ + return NULL; + } + else{ + $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count, user FROM login_remember WHERE remember_token=:token and until>:until"); + $sql->execute(array(":token"=>$this->getRememberKey(), ":until"=>date("Y-m-d H:i:s"))); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + if($res['count']!=1){ + $this->addLoginHistory(lmStates::NOUSER, lmStates::LOGIN_FAILED); + return NULL; + } + else{ + return $res['user']; + } + } + } + + /** + * i don't know you anymore! + * @return void + */ + public function forgetUser(){ + $sql=$this->config->getPDO()->prepare("UPDATE login_remember SET until=0 WHERE remember_token=:token"); + $sql->execute(array(":token"=>$this->getRememberKey())); + + setcookie("lm_login_remember", NULL, -1); + + $this->eventHandler->handle(lmStates::FORGET_DONE); + + return; + } + + /** + * print captcha html code if needed + * @param bool $dark use the dark theme, default false + * @return void + */ + public function printCaptcha($dark=false){ + if($this->config->isCaptchaEnabled()){ + global $lm_force_captcha; + if(isset($lm_force_captcha)){ + if($dark){ + echo "
config->getCaptchaSitekey()."\" data-theme=\"dark\">
"; + } + else{ + echo "
config->getCaptchaSitekey()."\">
"; + } + return; + } + else{ + return; + } + } + return; + } + + + + //backend functions + + protected function generateSessionKey(){ + $random=lmUtils::randomString(32); + setcookie("lm_login_random", $random, time()+$this->config->getSessionLifetime()); + $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$random); + return $hash; + } + + protected function getSessionKey(){ + if(!isset($_COOKIE['lm_login_random'])){ + return NULL; + } + else{ + $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$_COOKIE['lm_login_random']); + return $hash; + } + } + + protected function passFailedAttempts(){ + //check if no limitations are enabled + if(!$this->config->isCaptchaEnabled() && !$this->config->isBanEnabled()){ + return true; //nothing to do + } + + //check if is already banned + if($this->config->isBanEnabled()){ + $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count FROM login_bans WHERE id=:ip and until>:until"); + $sql->execute(array(":ip"=>$_SERVER['REMOTE_ADDR'], ":until"=>date("Y-m-d H:i:s"))); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + if($res['count']!=0){ + $this->eventHandler->handle(lmStates::BANNED); + return false; + } + } + + //count failed attempts + $sql=$this->config->getPDO()->prepare("SELECT COUNT(id) AS count FROM login_history WHERE ip=:ip and date>:date and success=0"); + $sql->execute(array(":ip"=>$_SERVER['REMOTE_ADDR'], ":date"=>date("Y-m-d H:i:s", time()-$this->config->getLook()))); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + //force captcha if case + if($res['count']>=$this->config->getCaptchaAfter() && $this->config->isCaptchaEnabled()){ + global $lm_force_captcha; + $lm_force_captcha=true; + } + + //bann if case + if($res['count']>=$this->config->getBanAfter() && $this->config->isBanEnabled()){ + $sql=$this->config->getPDO()->prepare("INSERT INTO login_bans (ip, until) VALUES (:ip, :until)"); + $sql->execute(array(":ip"=>$_SERVER['REMOTE_ADDR'], ":until"=>date("Y-m-d H:i:s", time()+$this->config->getBanTime()))); + global $lm_banned; + $lm_banned=true; + $this->eventHandler->handle(lmStates::BANNED); + return false; + } + + return true; + } + + protected function addLoginHistory($uid, $success=lmStates::LOGIN_FAILED, $token=""){ + $sql=$this->config->getPDO()->prepare("INSERT INTO login_history (user, date, ip, auth_token, user_agent, success) VALUES (:user, :date, :ip, :auth_token, :user_agent, :success)"); + $sql->execute(array(":user"=>$uid, ":date"=>date("Y-m-d H:i:s"), ":ip"=>$_SERVER['REMOTE_ADDR'], ":auth_token"=>$token, ":user_agent"=>$_SERVER['HTTP_USER_AGENT'], ":success"=>$success)); + return; + } + + protected function permitLogin($uid){ + $token=$this->generateSessionKey(); + $this->addLoginHistory($uid, lmStates::LOGIN_OK, $token); + + $_SESSION=array(); + $_SESSION['lm_id']=$uid; + + $this->eventHandler->handle(lmStates::LOGIN_OK, $uid); + + return; + } + + //functions for remembering + protected function generateRememberKey(){ + $random=lmUtils::randomString(32); + setcookie("lm_login_remember", $random, time()+(86000*$this->config->getRememberTime())); + $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$random); + return $hash; + } + + protected function getRememberKey(){ + if(!isset($_COOKIE['lm_login_remember'])){ + return NULL; + } + else{ + $hash=hash("sha256", $_SERVER['REMOTE_ADDR']."***".$_SERVER['HTTP_USER_AGENT']."***".$_COOKIE['lm_login_remember']); + return $hash; + } + } + + protected function rememberUser($uid){ + $sql=$this->config->getPDO()->prepare("INSERT INTO login_remember (user, remember_token, until) VALUES (:user, :token, :until)"); + $sql->execute(array(":user"=>$uid, ":token"=>$this->generateRememberKey(), ":until"=>date("Y-m-d H:i:s", time()+(86400*$this->config->getRememberTime())))); + return; + } +} diff --git a/config/setup.sql b/config/setup.sql new file mode 100644 index 0000000..b3db6dd --- /dev/null +++ b/config/setup.sql @@ -0,0 +1,118 @@ +/** + * /config/setup.sql + * @version 1.3 + * @desc sql structure file + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +CREATE TABLE `users`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `username` varchar(65) NOT NULL default '', + `fullname` varchar(65) NOT NULL default '', + `accesslevel` tinyint(1) UNSIGNED NOT NULL default 0, /* 0:student, 1:teacher; 2:head teacher; 3:manager; 4:admin */ + `class` varchar(10) NOT NULL default '', /* format: {G,L}{Year when school started}[AF] */ + `password` varchar(255) NOT NULL default '', + `perm_message` tinyint(1) NOT NULL default 1, /* 0:don't allow messaging; 1:allow messaging */ + PRIMARY KEY (`id`) +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `login_history`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `user` int(4) UNSIGNED NOT NULL default 1, + `date` timestamp NOT NULL default current_timestamp, + `ip` varchar(45) NOT NULL default '0.0.0.0', + `auth_token` varchar(65) NOT NULL default '', + `user_agent` varchar(500) NOT NULL default '', + `success` tinyint(1) NOT NULL default 0, + PRIMARY KEY (`id`), + FOREIGN KEY (`user`) REFERENCES users(`id`) ON DELETE CASCADE +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `login_remember`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `user` int(4) UNSIGNED NOT NULL default 0, + `remember_token` varchar(65) NOT NULL default '', + `until` timestamp NOT NULL default current_timestamp, + PRIMARY KEY (`id`), + FOREIGN KEY (`user`) REFERENCES users(`id`) ON DELETE CASCADE +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `login_bans`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `ip` varchar(45) NOT NULL default '0.0.0.0', + `until` timestamp NOT NULL default current_timestamp, + PRIMARY KEY (`id`) +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `subjects`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `name_1` varchar(65) NOT NULL default '', + `name_2` varchar(65) NOT NULL default '', + PRIMARY KEY (`id`) +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `contests`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `name_1` varchar(65) NOT NULL default '', + `name_2` varchar(65) NOT NULL default '', + `subject` int(4) UNSIGNED NOT NULl, + `description` text NOT NULL default '', + `ministry_support` tinyint(1) UNSIGNED NOT NULL default 0, /* 0:not listed; 1:not supported; 2:supported */ + `ministry_place` int(4) UNSIGNED NOT NULL default 0, /* place on the list of contests */ + `schoolyear` varchar(16) NOT NULL default '0000-0001', + PRIMARY KEY (`id`), + FOREIGN KEY (`subject`) REFERENCES subjects(`id`) ON DELETE CASCADE ON UPDATE CASCADE +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `phases`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `name_1` varchar(65) NOT NULl default '', + `name_2` varchar(65) NOT NULL default '', + PRIMARY KEY (`id`) +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `register`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `student` int(4) UNSIGNED NOT NULL default 1, + `contest` int(4) UNSIGNED NOT NULL, + `phase` int(4) UNSIGNED NOT NULL, + `teacher` int(4) UNSIGNED NOT NULL default 1, + `place` tinyint(1) NOT NULL default 0, /* -1:dicseret; -2:kulondij; -3:reszvetel */ + `mention` text NOT NULL default '', + `schoolyear` varchar(16) NOT NULL default '0000-0001', + PRIMARY KEY (`id`), + FOREIGN KEY (`student`) REFERENCES users(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`contest`) REFERENCES contests(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`phase`) REFERENCES phases(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`teacher`) REFERENCES users(`id`) ON DELETE CASCADE ON UPDATE CASCADE +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE `messages`( + `id` int(4) UNSIGNED NOT NULL auto_increment, + `sender` int(4) UNSIGNED NOT NULL default 1, + `recipient` int(4) UNSIGNED NOT NULL default 1, + `content` text NOT NULL default '', + `dismissed` tinyint(1) UNSIGNED NOT NULL default 0, + PRIMARY KEY (`id`), + FOREIGN KEY (`sender`) REFERENCES users(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`recipient`) REFERENCES users(`id`) ON DELETE CASCADE ON UPDATE CASCADE +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +INSERT INTO users (`id`, `username`) VALUES (1, 'nouser'); diff --git a/index.php b/index.php new file mode 100644 index 0000000..24f456d --- /dev/null +++ b/index.php @@ -0,0 +1,195 @@ +. + **/ + +require_once("config/config.php"); +require_once("subs/loader.php"); + +if(!$lm->validateLogin()){ + $lm->loginPrepare(); + if(isset($_POST['uname']) && isset($_POST['passwd'])){ + $remember=isset($_POST['remember']); + $lm->login($_POST['uname'], $_POST['passwd'], $remember); + } + if(isset($_GET['login_auto'])){ + $lm->login("", ""); + } + if(isset($_GET['forget_user'])){ + $lm->forgetUser(); + } +} +else{ + if(isset($_GET['logout'])){ + $lm->logout(); + } +} + +//select page we want to see +$view=""; +if(isset($_GET['view'])){ + $view=$_GET['view']; +} + +?> + + + + + <?php echo $config['general']['title']." - ".$config['general']['org'] ?> + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ validateLogin()): ?> + +
+
+
+ isRememberingUser()): ?> +
+ + prepare("SELECT fullname FROM users WHERE id=:id"); + $sql->execute(array(":id"=>$lm->isRememberingUser())); + $res=$sql->fetch(PDO::FETCH_ASSOC); + ?> +

+ +
+
+ +
+ +
+
+ +
+ + + + + + + + + + + + + +
" required>
" required>
+
+ + +
+
+
+ printCaptcha() ?> +
+
+ +
+
+
+ +
+
+
+ + + +
+
+ + + +
+ +
+ + + diff --git a/res/add.png b/res/add.png new file mode 100644 index 0000000..389573b Binary files /dev/null and b/res/add.png differ diff --git a/res/admin.png b/res/admin.png new file mode 100644 index 0000000..4da4902 Binary files /dev/null and b/res/admin.png differ diff --git a/res/classes.png b/res/classes.png new file mode 100644 index 0000000..0e1b7d6 Binary files /dev/null and b/res/classes.png differ diff --git a/res/contests.png b/res/contests.png new file mode 100644 index 0000000..58e17e5 Binary files /dev/null and b/res/contests.png differ diff --git a/res/icon.png b/res/icon.png new file mode 100644 index 0000000..7d9e182 Binary files /dev/null and b/res/icon.png differ diff --git a/res/index.png b/res/index.png new file mode 100644 index 0000000..0c33e5f Binary files /dev/null and b/res/index.png differ diff --git a/res/loading.gif b/res/loading.gif new file mode 100644 index 0000000..2929f58 Binary files /dev/null and b/res/loading.gif differ diff --git a/res/logout.png b/res/logout.png new file mode 100644 index 0000000..638fbd7 Binary files /dev/null and b/res/logout.png differ diff --git a/res/minus.png b/res/minus.png new file mode 100644 index 0000000..9926bed Binary files /dev/null and b/res/minus.png differ diff --git a/res/phases.png b/res/phases.png new file mode 100644 index 0000000..714f25c Binary files /dev/null and b/res/phases.png differ diff --git a/res/plus.png b/res/plus.png new file mode 100644 index 0000000..0385464 Binary files /dev/null and b/res/plus.png differ diff --git a/res/profile.png b/res/profile.png new file mode 100644 index 0000000..7cb2948 Binary files /dev/null and b/res/profile.png differ diff --git a/res/register.png b/res/register.png new file mode 100644 index 0000000..1b90d35 Binary files /dev/null and b/res/register.png differ diff --git a/res/remove.png b/res/remove.png new file mode 100644 index 0000000..e2230a1 Binary files /dev/null and b/res/remove.png differ diff --git a/res/search.png b/res/search.png new file mode 100644 index 0000000..bfe5d5f Binary files /dev/null and b/res/search.png differ diff --git a/res/subjects.png b/res/subjects.png new file mode 100644 index 0000000..4ce2d60 Binary files /dev/null and b/res/subjects.png differ diff --git a/res/users.png b/res/users.png new file mode 100644 index 0000000..6751b8e Binary files /dev/null and b/res/users.png differ diff --git a/res/wizard.png b/res/wizard.png new file mode 100644 index 0000000..0101c36 Binary files /dev/null and b/res/wizard.png differ diff --git a/script/.js b/script/.js new file mode 100644 index 0000000..d456fc3 --- /dev/null +++ b/script/.js @@ -0,0 +1,24 @@ +/** + * /script/.js + * @version 1.0 + * @desc Index javascript + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + diff --git a/script/admin.js b/script/admin.js new file mode 100644 index 0000000..903c7d2 --- /dev/null +++ b/script/admin.js @@ -0,0 +1,115 @@ +/** + * /script/admin.js + * @version 1.3 + * @desc Javascript file for the admin submenu + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function adminImportUsers(){ + //hide all status messages if any shown + $("#statuses").children().each(function(){ + $(this).css("display", "none"); + }); + + //get file + var file=$("#csvFile")[0].files[0]; + + //if greater than 10MB don't upload + if(file.size>10000000){ + $("#status_fileTooBig").slideDown(); + return; + } + else{ + $("#status_uploading").slideDown(function(){ + //generate form data + var data=new FormData(); + data.append("import_file", file); + + //run ajax upload request + $.ajax({ + url: "./subs/loader.php?load=admin&backend", + type: "POST", + data: data, + cache: false, + contentType: false, + processData: false, + xhr: function(){ + var myXHR=$.ajaxSettings.xhr(); + if(myXHR.upload){ + myXHR.upload.addEventListener("progress", function(e){ + if(e.lengthComputable){ + //calculate percent with 1 decimal precision + var percent=Math.round(e.loaded*100/e.total*10)/10; + percent=percent.toString()+"%"; + + //upload progressbar + $("#uploadStatus").children("div").css("width", percent); + $("#uploadStatus").children("div").children("span").text(percent); + } + }); + } + return myXHR; + }, + success: function(response){ + $("#status_uploading").slideUp(); + + //something went wrong during the upload + if(response=="error"){ + loadMessages(); + $("#status_uploadError").slideDown(); + } + else{ + //everything ok so far + $("#status_processing").slideDown(); + + //start listener that checks processing status (run every 500ms) + var progressChecker=setInterval(function(){ + $.ajax({ + url: "./subs/loader.php", + type: "GET", + data: {"load":"admin", "backend":true, "import_progress":response}, + success: function(response2){ + //update progressbar + $("#processStatus").children("div").css("width", response2); + $("#processStatus").children("div").children("span").text(response2); + } + }); + }, 500); + + //start processing of file with ajax + $.ajax({ + url: "./subs/loader.php?load=admin&backend", + type: "POST", + data: {"process_file":response}, + success: function(response2){ + //import complete + $("#status_processing").slideUp(); + $("#status_done").slideDown(); + + //stop progress checker + clearInterval(progressChecker); + } + }); + } + } + }); + }); + } +} diff --git a/script/classes.js b/script/classes.js new file mode 100644 index 0000000..e91764c --- /dev/null +++ b/script/classes.js @@ -0,0 +1,63 @@ +/** + * /script/classes.js + * @version 1.0 + * @desc Javascript file for the classes submenu + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function classesLoadList(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=classes&backend&list", + type: "GET", + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function classesFilterApply(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=classes&backend&list", + type: "POST", + data: $("#dd_filter_form").serialize(), + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function classesFilterReset(){ + $("#dd_filter_form")[0].reset(); + classesLoadList(); +} + +/* + * RUN + */ +//autoload list +classesLoadList(); diff --git a/script/contests.js b/script/contests.js new file mode 100644 index 0000000..064ac52 --- /dev/null +++ b/script/contests.js @@ -0,0 +1,130 @@ +/** + * /script/contests.js + * @version 1.0 + * @desc Javascript file for the contests submenu + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function contestsLoadList(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php", + data: {"load":"contests", "backend":true, "list":$("#schoolyear").val()}, + type: "GET", + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function contestsFilterApply(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=contests&backend&list="+$("#schoolyear").val(), + type: "POST", + data: $("#dd_filter_form").serialize(), + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function contestsFilterReset(){ + $("#dd_filter_form")[0].reset(); + contestsLoadList(); +} + +function contestsNew(){ + $.ajax({ + url: "./subs/loader.php?load=contests&backend", + type: "POST", + data: $("#dd_new_form").serialize(), + success: function(){ + $("#dd_new_form")[0].reset(); + loadMessages(); + contestsLoadList(); + } + }); +} + +function contestsEdit(id){ + $.ajax({ + url: "./subs/loader.php?load=contests&backend&getdata="+id, + type: "GET", + success: function(response){ + var data=JSON.parse(response); + $("#dd_edit_form input[name=edit]").val(data.id); + $("#dd_edit_form input[name=name_1]").val(data.name_1); + $("#dd_edit_form input[name=name_2]").val(data.name_2); + $("#dd_edit_form select[name=subject] option[value="+data.subject+"]").attr("selected", true); + $("#dd_edit_form textarea[name=description]").text(data.description); + $("#dd_edit_form input[name=ministry_support][value="+data.ministry_support+"]").attr("checked", true); + $("#dd_edit_form input[name=ministry_place]").val(data.ministry_place); + $("#dd_edit").slideDown(); + smoothScroll("#dd_edit"); + } + }); +} + +function contestsSubmitEdit(){ + $.ajax({ + url: "./subs/loader.php?load=contests&backend", + type: "POST", + data: $("#dd_edit_form").serialize(), + success: function(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); + loadMessages(); + contestsLoadList(); + } + }); +} + +function contestsCancelEdit(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); +} + +function contestsDelete(id, elem){ + if(confirm($("#contestsDeleteConfirm").text())){ + $.ajax({ + url: "./subs/loader.php?load=contests&backend", + type: "POST", + data: {"delete": id}, + success: function(){ + loadMessages(); + footableRemoveElem(elem); + } + }); + } +} + +/* + * RUN + */ +contestsLoadList(); diff --git a/script/js.php b/script/js.php new file mode 100644 index 0000000..bbb1844 --- /dev/null +++ b/script/js.php @@ -0,0 +1,29 @@ +. + **/ + +if(isset($_GET['load'])){ + header("Content-type: application/javascript"); + readfile($_GET['load'].".js", true); +} diff --git a/script/main.js b/script/main.js new file mode 100644 index 0000000..497eded --- /dev/null +++ b/script/main.js @@ -0,0 +1,203 @@ +/** + * /script/main.js + * @version 1.4 + * @desc Main javascript file + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +var Site=""; + +function disposeMessageOverlay(){ + $("#messageOverlay").html(""); + $("#messageOverlay").fadeOut(); +} + +function showMessage(html){ + $("#messageOverlay").html(html); + $("#messageOverlay").fadeIn(); + setTimeout(function(){ + disposeMessageOverlay() + }, 5000); +} + +function loadMessages(){ + $.ajax({ + url: "./subs/msg.php", + type: "GET", + success: function(response){ + if(response!=""){ + showMessage(response); + } + } + }); +} + +function smoothScroll(selector){ + $('html, body').stop().animate({ + scrollTop: $(selector).offset().top + }, 500); +} + +function goTo(site, pop=false){ + Site=site; + //slide up + $("#content").slideUp(function(){ + //load part + $.ajax({ + url: "./subs/loader.php", + type: "GET", + data: {"load": site}, + success: function(response){ + $("#content").html(response); + if(!pop){ + window.history.pushState({"site": site}, null, "./"+site); + } + }, + complete: function(){ + $("#content").slideDown(function(){ + //load script + $.getScript("./script/js.php?load="+site); + //prepare site + prepareSite(); + }); + } + }); + }); +} + +function prepareSite(){ + //smooth scroll + $('a[href^="#"]').on('click', function(event) { + var target = $(this.getAttribute('href')); + if( target.length ) { + event.preventDefault(); + $('html, body').stop().animate({ + scrollTop: target.offset().top + }, 500); + } + }); + + //disable ajax forms submit + $(".ajaxform").submit(function(e){ + e.preventDefault(); //prevent classic submit + }); +} + +function toggleDropdown(content, img){ + if($(content).css("display")=="none"){ + $(content).slideDown(); + $(img).attr("src", "./res/minus.png"); + } + else{ + $(content).slideUp(); + $(img).attr("src", "./res/plus.png"); + } +} + +function footableRemoveElem(elem){ + //delete from table + var button=$(elem); + var current=button.parents("tr:first"); + var remove; + //if we in the detail row or not + if(current.hasClass("footable-detail-row")){ + //get the previous row and add it with the current row to be removed later + remove = current.add(current.prev()); + } + else{ + //get the next row after the current row and check if it's a detail row + var next = current.next(); + //if the next row is a detail row or not + if (next.hasClass("footable-detail-row")){ + //get the next row and add it with the current row to be removed later + remove = current.add(next); + } + else{ + //we can't find a detail row so just remove the current row later + remove = current; + } + } + + remove.fadeOut(function(){ + remove.remove(); + }); +} + +function search(field, list){ + $(list).children("li").each(function(){ + if($(this).children("label").text().toLowerCase().indexOf($(field).val().toLowerCase())!=-1){ + $(this).css("display", "block"); + } + else{ + $(this).css("display", "none"); + } + }); +} + +function toggleRequiredFormElement(container, to=-1){ + if(to==-1){ + if($(container).css("display")=="none"){ + $(container).children("input").attr("required", true); + $(container).slideDown(); + } + else{ + $(container).slideUp(); + $(container).children("input").attr("required", false); + } + } + else{ + if(to==1){ + $(container).children("input").attr("required", true); + $(container).slideDown(); + } + else{ + $(container).slideUp(); + $(container).children("input").attr("required", false); + } + } +} + +/* + * RUN + */ +jQuery(function($){ + //get correct site + Site=window.location.pathname.substring(window.location.pathname.lastIndexOf("/")+1); + $.getScript("./script/js.php?load="+Site); + prepareSite(); + + //ajax loading functions + $(document).ajaxStart(function(){ + $("#loadingOverlay").css("display", "block"); + }); + $(document).ajaxComplete(function(){ + $("#loadingOverlay").css("display", "none"); + }); + + //go back in history + window.addEventListener("popstate", function(e){ + if(e.state!=null){ + goTo(e.state["site"], true); + } + else{ + goTo("", true); + } + }); +}); diff --git a/script/phases.js b/script/phases.js new file mode 100644 index 0000000..d8eca14 --- /dev/null +++ b/script/phases.js @@ -0,0 +1,125 @@ +/** + * /script/phases.js + * @version 1.0 + * @desc phases javascript file + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function phasesLoadList(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=phases&backend&list", + type: "GET", + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function phasesFilterApply(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=phases&backend&list", + type: "POST", + data: $("#dd_filter_form").serialize(), + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function phasesFilterReset(){ + $("#dd_filter_form")[0].reset(); + phasesLoadList(); +} + +function phasesNew(){ + $.ajax({ + url: "./subs/loader.php?load=phases&backend", + type: "POST", + data: $("#dd_new_form").serialize(), + success: function(){ + $("#dd_new_form")[0].reset(); + loadMessages(); + phasesLoadList(); + } + }); +} + +function phasesEdit(id){ + $.ajax({ + url: "./subs/loader.php?load=phases&backend&getdata="+id, + type: "GET", + success: function(response){ + var data=JSON.parse(response); + $("#dd_edit_form input[name=edit]").val(data.id); + $("#dd_edit_form input[name=name_1]").val(data.name_1); + $("#dd_edit_form input[name=name_2]").val(data.name_2); + $("#dd_edit").slideDown(); + smoothScroll("#dd_edit"); + } + }); +} + +function phasesSubmitEdit(){ + $.ajax({ + url: "./subs/loader.php?load=phases&backend", + type: "POST", + data: $("#dd_edit_form").serialize(), + success: function(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); + loadMessages(); + phasesLoadList(); + } + }); +} + +function phasesCancelEdit(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); +} + +function phasesDelete(id, elem){ + if(confirm($("#phasesDeleteConfirm").text())){ + $.ajax({ + url: "./subs/loader.php?load=phases&backend", + type: "POST", + data: {"delete": id}, + success: function(){ + loadMessages(); + footableRemoveElem(elem); + } + }); + } +} + +/* + * RUN + */ +phasesLoadList(); diff --git a/script/profile.js b/script/profile.js new file mode 100644 index 0000000..82d6242 --- /dev/null +++ b/script/profile.js @@ -0,0 +1,35 @@ +/** + * /script/profile.js + * @version 1.0 + * @desc Profile javascript file + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function profileSetNewPassword(){ + $.ajax({ + url: "./subs/loader.php?load=profile&backend", + type: "POST", + data: $("#editpasswd").serialize(), + success: function(){ + loadMessages(); + $("#editpasswd")[0].reset(); + } + }); +} diff --git a/script/register.js b/script/register.js new file mode 100644 index 0000000..a616be2 --- /dev/null +++ b/script/register.js @@ -0,0 +1,190 @@ +/** + * /script/register.js + * @version 1.4 + * @desc Register javascript file + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function registerLoadList(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php", + data: {"load":"register", "backend":true, "list":$("#schoolyear").val()}, + type: "GET", + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function registerFilterApply(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=register&backend&list", + type: "POST", + data: $("#dd_filter_form").serialize(), + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function registerFilterReset(){ + $("#dd_filter_form")[0].reset(); + registerLoadList(); +} + +function registerEdit(id){ + $.ajax({ + url: "./subs/loader.php?load=register&backend&getdata="+id, + type: "GET", + success: function(response){ + var data=JSON.parse(response); + $("#dd_edit_form input[name=edit]").val(id); + $("#dd_edit_form input[name=student][value="+data.student+"]").attr("checked", true); + $("#dd_edit_form input[name=contest][value="+data.contest+"]").attr("checked", true); + $("#dd_edit_form input[name=phase][value="+data.phase+"]").attr("checked", true); + $("#dd_edit_form input[name=teacher][value="+data.teacher+"]").attr("checked", true); + $("#dd_edit_form input[name=place][value="+(data.place<0?data.place:0)+"]").attr("checked", true); + if(data.place>0){ + $("#dd_edit_form input[name=place_c]").val(data.place); + $("#registerCustomPlaceEdit").slideDown(); + } + $("#dd_edit_form input[name=place_c]").attr("required", data.place>0); + $("#dd_edit_form textarea[name=mention]").text(data.mention); + $("#dd_edit").slideDown(); + smoothScroll("#dd_edit"); + } + }); +} + +function registerSubmitEdit(){ + $.ajax({ + url: "./subs/loader.php?load=register&backend", + type: "POST", + data: $("#dd_edit_form").serialize(), + success: function(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); + loadMessages(); + registerLoadList(); + } + }); +} + +function registerCancelEdit(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); +} + +function registerDelete(id, elem){ + if(confirm($("#registerDeleteConfirm").text())){ + $.ajax({ + url: "./subs/loader.php?load=register&backend", + type: "POST", + data: {"delete": id}, + success: function(){ + loadMessages(); + footableRemoveElem(elem); + } + }); + } +} + +function registerNew(){ + $.ajax({ + url: "./subs/loader.php?load=register&backend", + type: "POST", + data: $("#dd_new_form").serialize(), + success: function(response){ + var data=JSON.parse(response); + $("#newSubmitPrevContent tbody").html(data.prev); + $("#dd_newSubmit_form input[name=newSubmit]").val(JSON.stringify(data.params)); + $("#newSubmitPrevContent").footable(); + $("#dd_new_form")[0].reset(); + $("#dd_newSubmit").slideDown(); + smoothScroll("#dd_newSubmit"); + } + }); +} + +function registerNewSubmit(){ + $.ajax({ + url: "./subs/loader.php?load=register&backend", + type: "POST", + data: $("#dd_newSubmit_form").serialize(), + success: function(){ + $("#dd_newSubmit").slideUp(function(){ + $("#dd_newSubmit_form")[0].reset(); + $("#newSubmitPrevContent tbody").html(""); + loadMessages(); + registerLoadList(); + }); + } + }); +} + +function registerNewCancel(){ + $("#dd_newSubmit").slideUp(function(){ + $("#dd_newSubmit_form")[0].reset(); + $("#newSubmitPrevContent tbody").html(""); + }); +} + +function registerExport(){ + $.ajax({ + url: "./subs/loader.php?load=register&backend", + type: "POST", + data: $("#dd_export_form").serialize(), + beforeSend: function(){ + $("#export_progress").css("display", "block"); + }, + success: function(response){ + $("#download_file").val(response); + $("#export_progress").css("display", "none"); + $("#export_ready").css("display", "block"); + } + }); +} + +function registerExportDownload(){ + $("#export_ready").css("display", "none"); + window.location="./subs/loader.php?load=register&backend&expdownload="+$("#download_file").val(); + loadMessages(); +} + +/* + * RUN + */ +registerLoadList(); +$("#dd_new_form input[name=place]").click(function(){ + toggleRequiredFormElement("#registerCustomPlaceNew", $("#dd_new_form input[name=place][value=0]").is(":checked")); +}); +$("#dd_edit_form input[name=place]").click(function(){ + toggleRequiredFormElement("#registerCustomPlaceEdit", $("#dd_edit_form input[name=place][value=0]").is(":checked")); +}); diff --git a/script/subjects.js b/script/subjects.js new file mode 100644 index 0000000..655b0e2 --- /dev/null +++ b/script/subjects.js @@ -0,0 +1,125 @@ +/** + * /script/subjects.js + * @version 1.3 + * @desc Subjects javascript file + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function subjectsLoadList(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=subjects&backend&list", + type: "GET", + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function subjectsFilterApply(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=subjects&backend&list", + type: "POST", + data: $("#dd_filter_form").serialize(), + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function subjectsFilterReset(){ + $("#dd_filter_form")[0].reset(); + subjectsLoadList(); +} + +function subjectsNew(){ + $.ajax({ + url: "./subs/loader.php?load=subjects&backend", + type: "POST", + data: $("#dd_new_form").serialize(), + success: function(){ + $("#dd_new_form")[0].reset(); + loadMessages(); + subjectsLoadList(); + } + }); +} + +function subjectsEdit(id){ + $.ajax({ + url: "./subs/loader.php?load=subjects&backend&getdata="+id, + type: "GET", + success: function(response){ + var data=JSON.parse(response); + $("#dd_edit_form input[name=edit]").val(data.id); + $("#dd_edit_form input[name=name_1]").val(data.name_1); + $("#dd_edit_form input[name=name_2]").val(data.name_2); + $("#dd_edit").slideDown(); + smoothScroll("#dd_edit"); + } + }); +} + +function subjectsSubmitEdit(){ + $.ajax({ + url: "./subs/loader.php?load=subjects&backend", + type: "POST", + data: $("#dd_edit_form").serialize(), + success: function(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); + loadMessages(); + subjectsLoadList(); + } + }); +} + +function subjectsCancelEdit(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); +} + +function subjectsDelete(id, elem){ + if(confirm($("#subjectsDeleteConfirm").text())){ + $.ajax({ + url: "./subs/loader.php?load=subjects&backend", + type: "POST", + data: {"delete": id}, + success: function(){ + loadMessages(); + footableRemoveElem(elem); + } + }); + } +} + +/* + * RUN + */ +subjectsLoadList(); diff --git a/script/users.js b/script/users.js new file mode 100644 index 0000000..75127d3 --- /dev/null +++ b/script/users.js @@ -0,0 +1,131 @@ +/** + * /script/users.js + * @version 2.1 + * @desc javascript for users submenu + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function usersLoadList(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=users&backend&list", + type: "GET", + success: function(response){ + $("#list").html(response); + $(".table").footable(); + $("#list").slideDown(); + } + }); + }); +} + +function usersFilterApply(){ + $("#list").slideUp(function(){ + $.ajax({ + url: "./subs/loader.php?load=users&backend&list", + type: "POST", + data: $("#dd_filter_form").serialize(), + success: function(response){ + $("#list").html(response); + $("#list").slideDown(function(){ + $(".table").footable(); + }); + } + }); + }); +} + +function usersFilterReset(){ + $("#dd_filter_form")[0].reset(); + loadList(); +} + +function usersNew(){ + $.ajax({ + url: "./subs/loader.php?load=users&backend", + type: "POST", + data: $("#dd_new_form").serialize(), + success: function(){ + $("#dd_new_form")[0].reset(); + loadMessages(); + usersLoadList(); + } + }); +} + +function usersEdit(id){ + $.ajax({ + url: "./subs/loader.php?load=users&backend&getdata="+id, + type: "GET", + success: function(response){ + var data=JSON.parse(response); + $("#dd_edit_form input[name=edit]").val(data.id); + $("#dd_edit_form input[name=username]").val(data.username); + $("#dd_edit_form input[name=fullname]").val(data.fullname); + $("#dd_edit_form input[name=accesslevel]").val(data.accesslevel); + $("#dd_edit_form input[name=class]").val(data.class); + $("#dd_edit_form input[name=password]").val(""); + $("#dd_edit_form input[name=perm_message]").prop("checked", data.perm_message==1); + $("#dd_edit").slideDown(); + smoothScroll("#dd_edit"); + } + }); +} + +function usersSubmitEdit(){ + $.ajax({ + url: "./subs/loader.php?load=users&backend", + type: "POST", + data: $("#dd_edit_form").serialize(), + success: function(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); + loadMessages(); + usersLoadList(); + } + }); +} + +function usersCancelEdit(){ + $("#dd_edit").slideUp(function(){ + $("#dd_edit_form")[0].reset(); + }); +} + +function usersDelete(id, elem){ + if(confirm($("#usersDeleteConfirm").text())){ + $.ajax({ + url: "./subs/loader.php?load=users&backend", + type: "POST", + data: {"delete": id}, + success: function(){ + loadMessages(); + footableRemoveElem(elem); + } + }); + } +} + +/* + * RUN + */ +//autoload list +usersLoadList(); diff --git a/script/wizard.js b/script/wizard.js new file mode 100644 index 0000000..c8667af --- /dev/null +++ b/script/wizard.js @@ -0,0 +1,86 @@ +/** + * /script/wizard.js + * @version 1.0 + * @desc javascript for the input wizard + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +function wizardNextStep(elem){ + $(elem).parent("div").slideUp(); + $(elem).parent("div").parent("div").next().next().children("div").eq(1).slideDown(function(){ + smoothScroll($(elem).parent("div").parent("div").next().next()); + }); +} + +function wizardPrevStep(elem){ + $(elem).parent("div").slideUp(); + $(elem).parent("div").parent("div").prev().prev().children("div").eq(1).slideDown(function(){ + smoothScroll($(elem).parent("div").parent("div").prev().prev()); + }); +} + +function wizardSelectMeAsTeacher(){ + $("#wizform input[name=teacher][value="+$("#wizardCurrentId").text()+"]").attr("checked", true); +} + +function wizardLoadPrevRecords(elem){ + $.ajax({ + url: "./subs/loader.php?load=register&backend", + type: "POST", + data: $("#wizform").serialize(), + success: function(response){ + var data=JSON.parse(response); + $("input[name=recordParams]").val(JSON.stringify(data.params)); + $("#wizardPrevContent tbody").html(data.prev); + $("#wizardPrevContent").footable(); + $(elem).parent("div").slideUp(); + $("#step7_content").slideDown(function(){ + smoothScroll("#step7_content"); + }); + } + }); +} + +function wizardSubmitRecord(){ + $.ajax({ + url: "./subs/loader.php?load=register&backend", + type: "POST", + data: {"newSubmit": $("input[name=recordParams]").val()}, + success: function(){ + loadMessages(); + $(".dropdown.content").slideUp(function(){ + $("#wizform")[0].reset(); + $("#step1_content").slideDown(function(){ + smoothScroll("#step1"); + }); + }); + } + }); +} + +/* + * RUN + */ +//load register scriptbase +$.getScript("./script/js.php?load=register"); +//handler for custom place +$("#wizform input[name=place]").click(function(){ + toggleRequiredFormElement("#registerCustomPlaceNew", $("#wizform input[name=place][value=0]").is(":checked")); +}); diff --git a/style/mobile.css b/style/mobile.css new file mode 100644 index 0000000..b0293b6 --- /dev/null +++ b/style/mobile.css @@ -0,0 +1,236 @@ +/** + * /style/mobile.php + * @version 1.0 + * @desc style file for mobile all small screen devices + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +h1.title{ + text-align: left; + background: rgba(31,73,125,0.8); + color: rgb(255,255,255); + margin: auto; + padding: 0.3em 1em; +} + +button{ + background: rgba(31,73,125,0.8); + color: rgb(255,255,255); + padding: 1em; + border-radius: 0.5em; + width: 100%; + font-size: 2em; +} +button:hover{ + background: rgba(31,73,125,1); +} + +form{ + width: 100%; +} +fieldset{ + border: 5px solid rgba(31,73,125,0.8); + background: rgb(220,220,220); + border-radius: 1em; + padding: 2em; + width: 90%; + text-align: left; +} +fieldset legend{ + background: rgba(31,73,125,0.8); + color: rgb(255,255,255); + padding: 0.3em; + font-size: 2em; + border-radius: 0.5em; + box-shadow: 0 0 0 5px rgb(220,220,220); + text-align:left; + margin-left: 10%; +} + +footer{ + background: rgb(200,200,200); + border-radius: 1em; + width: 90%; + margin: auto; + font-size: 0.8em; + text-align: center; + padding: 0.3em; +} + +hr.placeholder{ + border: none; + height: 30px; +} + +input{ + border-radius: 5px; + padding: 0.5em; + border: 1px solid solid rgba(31,73,125,0.8); + font-size: 1.5em; +} +textarea{ + border-radius: 5px; + padding: 0.5em; + border: 1px solid solid rgba(31,73,125,0.8); + font-size: 1.5em; +} +select{ + border-radius: 5px; + padding: 0.5em; + border: 1px solid solid rgba(31,73,125,0.8); + font-size: 1.5em; + max-width: 90%; +} + +div.message{ + width: 90%; + padding: 1em; + border: 2px solid rgb(60, 255, 60); + border-radius: 10px; + margin: auto; + margin-top: 1.5em; + margin-bottom: 1.5em; + background: rgba(0, 255, 0, 0.5); + text-align: center; + font-size: 1.5em; +} +div.message.error{ + border: 2px solid rgb(255, 60, 60); + background: rgba(255, 0, 0, 0.5); +} + +ul.menu{ + list-style:none; + margin: 0; + background: rgba(31,73,125,0.8); + display: flex; + justify-content: stretch; + flex-wrap: wrap; + font-size: 2em; +} +ul.menu li{ + display: block; + padding: 1em; + color: rgb(255,255,255); +} +ul.menu li:hover{ + background: rgba(31,73,125,1); +} +ul.menu a{ + text-decoration: none; + width: 100%; +} + +td{ + vertical-align: top; +} + +span.password{ + background: rgb(0,0,0); + font-family: Courier New; +} +span.password:hover{ + background: inherit; +} + +table:not(.table) td{ + display: block; +} + +label{ + font-size: 1.5em; +} + +table{ + font-size: 1.5em; +} + +p{ + font-size: 1.5em; +} + +div.overlay{ + position: fixed; + display: none; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 2; +} +div.overlay.loading{ + background: rgba(0,0,0,0.7); +} +div.overlay.loading img{ + position: fixed; + max-width: 50%; + max-height: 50%; + top: 30%; + left: 40%; + padding: 1em; +} +div.overlay.messages{ + height: 30%; +} + +/* the world's fanciest checkbox */ +div.checkbox{ + width: 7em; + height: 2.5em; + background: rgb(140, 140, 140); + border-radius: 1.5em; + position: relative; +} +div.checkbox:before{ + content: 'On'; + position: absolute; + top: 30%; + left: 15%; + color: rgb(35, 200, 40); + font-size: 1em; +} +div.checkbox:after{ + content: 'Off'; + position: absolute; + top: 30%; + right: 15%; + color: rgb(15, 15, 15); + font-size: 1em; +} +div.checkbox label{ + display: block; + width: 45%; + height: 55%; + border-radius: 1.5em; + transition: 0.5s; + cursor: pointer; + position: absolute; + top: 22.5%; + left: 10%; + z-index: 1; + background: rgb(220, 220, 220); +} +div.checkbox input[type=checkbox]:checked + label{ + left: 45%; + background: rgb(35, 200, 40); +} +div.checkbox input[type=checkbox]{ + display: none; +} diff --git a/style/style.css b/style/style.css new file mode 100644 index 0000000..10a3af7 --- /dev/null +++ b/style/style.css @@ -0,0 +1,301 @@ +/** + * /style/style.css + * @version 1.5 + * @desc Main style file + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +button{ + background: rgba(38,143,84,0.8); + color: rgb(255,255,255); + padding: 1em; + border-radius: 0.5em; +} +button:hover{ + background: rgba(38,143,84,1); +} + +fieldset{ + border: 5px solid rgba(38,143,84,1); + background: rgb(220,220,220); + border-radius: 1em; + padding: 2em; + width: 60%; + text-align: left; +} +fieldset legend{ + background: rgba(38,143,84,1); + color: rgb(255,255,255); + padding: 0.3em; + font-size: 2em; + border-radius: 0.5em; + box-shadow: 0 0 0 5px rgb(220,220,220); + text-align:left; + margin-left: 10%; +} + +footer{ + background: rgb(200,200,200); + border-radius: 1em; + width: 80%; + margin: auto; + font-size: 0.8em; + text-align: center; + padding: 0.3em; +} + +hr.placeholder{ + border: none; + height: 30px; +} + +input{ + border-radius: 5px; + padding: 0.5em; + border: 1px solid solid rgba(38,143,84,0.8); +} +textarea{ + border-radius: 5px; + padding: 0.5em; + border: 1px solid solid rgba(38,143,84,0.8); +} +select{ + border-radius: 5px; + padding: 0.5em; + border: 1px solid solid rgba(38,143,84,0.8); +} + +div.message{ + width: 50%; + padding: 1em; + border: 2px solid rgb(60, 255, 60); + border-radius: 10px; + margin: auto; + margin-top: 1.5em; + margin-bottom: 1.5em; + background: rgba(0, 255, 0, 0.8); + text-align: center; +} +div.message.error{ + border: 2px solid rgb(255, 60, 60); + background: rgba(255, 0, 0, 0.8); +} + +ul.menu{ + list-style: none; + margin: 0; + background: rgba(38,143,84,0.8); + display: flex; + justify-content: space-around; + padding: 0; +} +ul.menu li{ + display: block; + padding: 1em; + color: rgb(255,255,255); +} +ul.menu li:hover{ + background: rgba(38,143,84,1); +} +ul.menu a{ + text-decoration: none; + cursor: default; +} + +td{ + vertical-align: top; +} + +span.password{ + background: rgb(0,0,0); + font-family: Courier New; +} +span.password:hover{ + background: inherit; +} + +div.overlay{ + position: fixed; + display: none; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 2; +} +div.overlay.messages{ + height: 30%; +} +div.overlay.loading{ + width: 15%; + height: 10%; + background: rgba(38,143,84,1); + text-align: center; + font-size: 1.2em; + left: auto; + right: 0; + margin: 1em; + border-radius: 10px; +} +div.overlay.loading img{ + max-height: 1em; + position: relative; + top: 40%; +} +div.overlay.loading span{ + position: relative; + top: 40%; +} + +div.content{ + width: 95%; + margin: auto; +} + +img.icon{ + max-height: 1.2em; + margin-right: 0.5em; +} + +.center{ + text-align: center; + margin: auto; +} +.selfcenter{ + margin: auto; +} + +/* the world's fanciest checkbox */ +div.checkbox{ + width: 7em; + height: 2.5em; + background: rgb(140, 140, 140); + border-radius: 1.5em; + position: relative; +} +div.checkbox:before{ + content: 'On'; + position: absolute; + top: 30%; + left: 15%; + color: rgb(35, 200, 40); + font-size: 1em; +} +div.checkbox:after{ + content: 'Off'; + position: absolute; + top: 30%; + right: 15%; + color: rgb(15, 15, 15); + font-size: 1em; +} +div.checkbox label{ + display: block; + width: 45%; + height: 55%; + border-radius: 1.5em; + transition: 0.5s; + cursor: pointer; + position: absolute; + top: 22.5%; + left: 10%; + z-index: 1; + background: rgb(220, 220, 220); +} +div.checkbox input[type=checkbox]:checked + label{ + left: 45%; + background: rgb(35, 200, 40); +} +div.checkbox input[type=checkbox]{ + display: none; +} + +/* dropdown div */ +div.dropdown{ + border: 2px solid rgba(38,143,84,1); + background: rgb(220,220,220); + border-radius: 10px; + overflow: auto; + padding: 0; +} +div.dropdown.header{ + border: none; + border-radius: 0; + height: 1.5em; + background: rgba(34,119,227,0.7); + padding: 0.3em; + font-size: 1.5em; +} +div.dropdown.content{ + border: none; + padding: 1em; + display: none; +} +div.dropdown.header a{ + text-decoration: none; + color: rgb(255,255,255); + cursor: pointer; +} + +div.searchbox{ + text-decoration: none; +} +div.searchbox input[type=text]{ + background-image: url("../res/search.png"); + background-size: contain; + background-repeat: no-repeat; + margin-bottom: 1em; + padding-left: 2.5em; +} +div.searchbox ul{ + list-style: none; + padding: 0; + margin: 0; + height: 15em; + overflow-y: scroll; +} +div.searchbox li{ + color: rgb(0,0,0); + margin-left: 0.5em; +} + +span.number{ + background: rgba(127,127,127,0.8); + border-radius: 50%; + margin-right: 1em; + padding: 0.2em; + padding-left: 0.5em; + padding-right: 0.5em; +} + +div.progressbar{ + background: rgba(100, 100, 100, 1); + border-radius: 2em; +} +div.progressbar div{ + background: rgb(0, 180, 245); + border-radius: 2em; + color: rgb(255, 255, 255); + padding-top: 0.5em; + padding-bottom: 0.2em; +} +div.progressbar span{ + margin: 1em; +} diff --git a/subs/loader.php b/subs/loader.php new file mode 100644 index 0000000..16ef4b1 --- /dev/null +++ b/subs/loader.php @@ -0,0 +1,62 @@ +. + **/ + +if(!isset($BOM)){ + require_once("../config/config.php"); +} + +function loadPart($view, $backend=false){ + global $lm, $lang, $db, $BOM, $config, $schoolyear; + if($lm->validateLogin()){ + if($view!="" && $view!="users" && $view!="classes" && $view!="subjects" && $view!="contests" && $view!="phases" && $view!="register" && $view!="wizard" && $view!="profile" && $view!="admin"){ + functions::setError(404); + $view=""; + } + + if(($view=="users") && $_SESSION['accesslevel']<3){ + functions::setError(401); + $view=""; + } + else if(($view=="classes" || $view=="subjects" || $view=="contests" || $view=="phases" || $view=="register" || $view=="wizard") && $_SESSION['accesslevel']<1){ + functions::setError(401); + $view=""; + } + else if(($view=="admin") && $_SESSION['accesslevel']<4){ + functions::setError(401); + $view=""; + } + } + + if($backend){ + include("part/".$view."_backend.php"); + } + else{ + include("part/".$view.".php"); + } +} + +if(isset($_GET['load'])){ + loadPart($_GET['load'], isset($_GET['backend'])); +} diff --git a/subs/msg.php b/subs/msg.php new file mode 100644 index 0000000..57a8e42 --- /dev/null +++ b/subs/msg.php @@ -0,0 +1,43 @@ +. + **/ + +if(!isset($BOM)){ + require_once("../config/config.php"); +} + +if(functions::isMessage()){ + foreach(functions::getMessageArray() as $m){ + echo "

".$lang['message'][$m]."

"; + echo "
"; + } +} +if(functions::isError()){ + foreach(functions::getErrorArray() as $m){ + echo "

".$lang['error'][$m]."

"; + echo "
"; + } +} + +?> diff --git a/subs/part/.htaccess b/subs/part/.htaccess new file mode 100644 index 0000000..7d3aaf1 --- /dev/null +++ b/subs/part/.htaccess @@ -0,0 +1,2 @@ +Order allow,deny +Deny from all \ No newline at end of file diff --git a/subs/part/.php b/subs/part/.php new file mode 100644 index 0000000..3a6c04b --- /dev/null +++ b/subs/part/.php @@ -0,0 +1,29 @@ +. + **/ +?> + +
+

+
diff --git a/subs/part/_backend.php b/subs/part/_backend.php new file mode 100644 index 0000000..a7a703d --- /dev/null +++ b/subs/part/_backend.php @@ -0,0 +1,24 @@ +. + **/ diff --git a/subs/part/admin.php b/subs/part/admin.php new file mode 100644 index 0000000..54f2da0 --- /dev/null +++ b/subs/part/admin.php @@ -0,0 +1,109 @@ +. + **/ + +?> + +
+ + +
+ + +
diff --git a/subs/part/admin_backend.php b/subs/part/admin_backend.php new file mode 100644 index 0000000..897bfdf --- /dev/null +++ b/subs/part/admin_backend.php @@ -0,0 +1,181 @@ +. + **/ + +try{ + + //import/step1: upload file to server + if(isset($_FILES['import_file'])){ + //time limit of 2 min + set_time_limit(120); + + //get file size and compare it if JS has sucked + $size=$_FILES['import_file']['size']; + + if($size>10000000){ + functions::setError(11); + echo "error"; + } + else{ + //get temp file to hold it + $target=tempnam(sys_get_temp_dir(), "resmanImp_"); + + //move file + if(!move_uploaded_file($_FILES['import_file']['tmp_name'], $target)){ + //something's wrong here + functions::setError(13); + echo "error"; + } + else{ + //count lines + $lines=0; + $file=fopen($target, "r"); + while(!feof($file)){ + $line=fgets($file); + $lines++; + } + fclose($file); + + //prepare session to track everything + $progress=array("total_lines"=>$lines, "lines_processed"=>0); + if(isset($_SESSION['progress'])){ + $_SESSION['progress'][$target]=$progress; + } + else{ + $_SESSION['progress']=array(); + $_SESSION['progress'][$target]=$progress; + } + + //echo file name (which is process identifier as well) to be able to track it + echo $target; + } + } + } + + //import/step2: start file procession + if(isset($_POST['process_file'])){ + //insert multiple records with the same query + $rowPerQuery=40; + + //set a looong time limit (20 min) + set_time_limit(1200); + + //don't stop execution even if the connection drops + ignore_user_abort(true); + + //open file + $file=fopen($_POST['process_file'], "r"); + + //a simple counter + $rowProcessed=0; + + //build query string + $querystr="INSERT INTO users (username, fullname, accesslevel, class, password) VALUES"; + for($i=0; $i<$rowPerQuery; $i++){ + $querystr.=" (?, ?, ?, ?, ?), "; + } + $querystr=rtrim($querystr, ", "); + + //buffer to hold before insert + $buffer=array(); + $rowBuffered=0; + + //prepare SQL query + $sql=$db->prepare($querystr); + + while($data=fgetcsv($file, 1000, ",")){ + if(count($data)!=5){ + continue; + } + else{ + //add row to buffer + array_push($buffer, $data[0], $data[1], $data[2], $data[3], $data[4]); + $rowBuffered++; + + //if needed, execute query + if($rowBuffered==$rowPerQuery){ + $sql->execute($buffer); + $buffer=array(); + $rowBuffered=0; + } + } + + //update counter + $rowProcessed++; + + //update status and close session to release write protect + if(session_status()==PHP_SESSION_NONE){ + session_start(); + } + $_SESSION['progress'][$_POST['process_file']]['lines_processed']=$rowProcessed; + session_write_close(); + } + + //if something remained in buffer + if($rowBuffered!=0){ + //build new query for the remained records + $querystr="INSERT INTO users (username, fullname, accesslevel, class, password) VALUES"; + for($i=0; $i<$rowBuffered; $i++){ + $querystr.=" (?, ?, ?, ?, ?), "; + } + $querystr=rtrim($querystr, ", "); + + $sql=$db->prepare($querystr); + + //execute everything left over + $sql->execute($buffer); + } + + //close, delete file and die (I leave the session there intentionally. I hope nobody will spam the session with 1 record imports lol) + fclose($file); + unlink($_POST['process_file']); + + //some bogus stuff that actually fixes the ERR_RESPONSE_HEADERS_TOO_BIG error, because PHP wants to send a ton of set-cookie headers + header_remove("Set-Cookie"); + + die(); + } + + //import/step3: check status + if(isset($_GET['import_progress'])){ + if(!isset($_SESSION['progress'][$_GET['import_progress']])){ + //if it does not exist, echo 0 + echo "error"; + } + else{ + $status=$_SESSION['progress'][$_GET['import_progress']]; + + //calculate percent + $percent=round($status['lines_processed']*100/$status['total_lines'], 1); + + //print percent + echo $percent."%"; + } + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/classes.php b/subs/part/classes.php new file mode 100644 index 0000000..5075368 --- /dev/null +++ b/subs/part/classes.php @@ -0,0 +1,56 @@ +. + **/ + +?> + +
+ + + + +
+
+ +
+ +
+
diff --git a/subs/part/classes_backend.php b/subs/part/classes_backend.php new file mode 100644 index 0000000..315ab7e --- /dev/null +++ b/subs/part/classes_backend.php @@ -0,0 +1,96 @@ +. + **/ + +try{ + + if(isset($_GET['list'])){ + $filter="WHERE id<>1 and class<>''"; + $filter_array=array(); + if(isset($_POST['filter'])){ + if(isset($_POST['f_search'])){ + if($_POST['f_search']!=""){ + $filter.=" and (class LIKE ? or username LIKE ? or fullname LIKE ?)"; + array_push($filter_array, "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%"); + } + } + } + + $sql=$db->prepare("SELECT fullname, accesslevel, class FROM users ".$filter." ORDER BY class ASC, accesslevel DESC, fullname ASC"); + $sql->execute($filter_array); + + //echo table(s) + $first=true; + $rid=0; + $curClass=""; + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + if($curClass!=$row['class']){ + $curClass=$row['class']; + if(!$first){ + echo " + + +
+ "; + } + if($first){ + $first=false; + } + $rid=1; + echo " +
+

".$curClass."

+
+
+ + + + + + + "; + } + echo " + + + + + + "; + $rid++; + } + echo " + +
".$lang['rowid']." + ".$lang['fullname']." + ".$lang['role']." +
".$rid."".$row['fullname']."".($row['accesslevel']>0?$lang['headteacher']:$lang['student'])."
+ "; + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/contests.php b/subs/part/contests.php new file mode 100644 index 0000000..7bda5db --- /dev/null +++ b/subs/part/contests.php @@ -0,0 +1,218 @@ +. + **/ + +$oid=0; + +?> + +
+ + + + +
+ + +
+ + +
+ +
+ +
+ +
+
+
diff --git a/subs/part/contests_backend.php b/subs/part/contests_backend.php new file mode 100644 index 0000000..06a70a2 --- /dev/null +++ b/subs/part/contests_backend.php @@ -0,0 +1,177 @@ +. + **/ + +try{ + + if(isset($_GET['list'])){ + $filter="WHERE c.id<>0 and c.schoolyear=?"; + $filter_array=array($_GET['list']); + if(isset($_POST['filter'])){ + if(isset($_POST['f_search'])){ + if($_POST['f_search']!=""){ + $filter.=" and (c.name_1 LIKE ? or c.name_2 LIKE ? or s.name_1 LIKE ? or s.name_2 LIKE ? or c.description LIKE ?)"; + array_push($filter_array, "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%"); + } + } + if(isset($_POST['f_subject'])){ + for($i=0; $iprepare("SELECT c.id, c.name_1, c.name_2, s.name_1 AS subject_1, s.name_2 AS subject_2, c.description, c.ministry_support, c.ministry_place FROM contests AS c INNER JOIN subjects AS s ON (s.id=c.subject) ".$filter." ORDER BY c.name_1 ASC, c.name_2 ASC"); + $sql->execute($filter_array); + echo " + + + + + + + + + + + + + + + + "; + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + echo " + + + + + + + + + + + + "; + } + echo " + +
".$lang['id']."".$lang['name_1']."".$lang['name_2']."".$lang['subject_1']."".$lang['subject_2']."".$lang['description']."".$lang['ministry_support']."".$lang['ministry_place']."".$lang['tools']."
".$row['id']."".$row['name_1']."".$row['name_2']."".$row['subject_1']."".$row['subject_2']."".str_replace(array("\n"), array("
"), $row['description'])."
".$lang['ministry_'.$row['ministry_support']]."".($row['ministry_support']>0?$row['ministry_place']:"-")." + + +
+ "; + } + + if(isset($_POST['new'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM contests WHERE name_1=:name_1 or name_2=:name_2"); + $sql->execute(array(":name_1"=>$_POST['name_1'], ":name_2"=>$_POST['name_2'])); + $row=$sql->fetch(PDO::FETCH_ASSOC); + if($row['count']>0){ + functions::setError(8); + } + else{ + $sql=$db->prepare("INSERT INTO contests (name_1, name_2, subject, description, ministry_support, ministry_place, schoolyear) VALUES (:n1, :n2, :subj, :desc, :ms, :mp, :sy)"); + $sql->execute(array(":n1"=>$_POST['name_1'], ":n2"=>$_POST['name_2'], ":subj"=>$_POST['subject'], ":desc"=>$_POST['description'], ":ms"=>$_POST['ministry_support'], ":mp"=>$_POST['ministry_place'], ":sy"=>$schoolyear)); + $res=$sql->rowCount(); + if($res<1){ + functions::setError(4); + } + else{ + functions::setMessage(3); + } + } + } + + if(isset($_POST['delete'])){ + $sql=$db->prepare("DELETE FROM contests WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['delete'])); + $res=$sql->rowCount(); + if($res<1){ + functions::setError(4); + } + else{ + functions::setMessage(4); + } + } + + if(isset($_GET['getdata'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count, id, name_1, name_2, subject, description, ministry_support, ministry_place FROM contests WHERE id=:id"); + $sql->execute(array(":id"=>$_GET['getdata'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + echo json_encode($res); + } + } + + if(isset($_POST['edit'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM contests WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['edit'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + $sql=$db->prepare("UPDATE contests SET name_1=:n1, name_2=:n2, subject=:subj, description=:desc, ministry_support=:ms, ministry_place=:mp WHERE id=:id"); + $sql->execute(array(":n1"=>$_POST['name_1'], ":n2"=>$_POST['name_2'], ":subj"=>$_POST['subject'], ":desc"=>$_POST['description'], ":ms"=>$_POST['ministry_support'], ":mp"=>$_POST['ministry_place'], ":id"=>$_POST['edit'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(5); + } + else{ + functions::setError(4); + } + } + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/phases.php b/subs/part/phases.php new file mode 100644 index 0000000..58977ea --- /dev/null +++ b/subs/part/phases.php @@ -0,0 +1,112 @@ +. + **/ + +?> + +
+ + + + +
+ =3): ?> + + +
+ + + +
+ +
+ +
+
diff --git a/subs/part/phases_backend.php b/subs/part/phases_backend.php new file mode 100644 index 0000000..b73fa2b --- /dev/null +++ b/subs/part/phases_backend.php @@ -0,0 +1,157 @@ +. + **/ + +try{ + + if(isset($_GET['list'])){ + $filter="WHERE id<>0"; + $filter_array=array(); + if(isset($_POST['filter'])){ + if(isset($_POST['f_search'])){ + if($_POST['f_search']!=""){ + $filter.=" and (name_1 LIKE ? or name_2 LIKE ?)"; + array_push($filter_array, "%".$_POST['f_search']."%", "%".$_POST['f_search']."%"); + } + } + } + + $sql=$db->prepare("SELECT id, name_1, name_2 FROM phases ".$filter." ORDER BY name_1 ASC, name_2 ASC"); + $sql->execute($filter_array); + + echo " + + + + + + + ".($_SESSION['accesslevel']>=3?"":"")." + + + + "; + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + echo " + + + + + ".($_SESSION['accesslevel']>=3?"":"")." + + "; + } + echo " + +
".$lang['id']."".$lang['name_1']."".$lang['name_2']."".$lang['tools']."
".$row['id']."".$row['name_1']."".$row['name_2']." + + +
+ "; + } + + if(isset($_POST['new'])){ + if($_SESSION['accesslevel']<3){ + functions::setError(401); + } + else{ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM phases WHERE name_1=:n1 or name_2=:n2"); + $sql->execute(array(":n1"=>$_POST['name_1'], ":n2"=>$_POST['name_2'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']>0){ + functions::setError(9); + } + else{ + $sql=$db->prepare("INSERT INTO phases (name_1, name_2) VALUES (:n1, :n2)"); + $sql->execute(array(":n1"=>$_POST['name_1'], ":n2"=>$_POST['name_2'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(3); + } + else{ + functions::setError(4); + } + } + } + } + + if(isset($_POST['delete'])){ + if($_SESSION['accesslevel']<3){ + functions::setError(401); + } + else{ + $sql=$db->prepare("DELETE FROM phases WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['delete'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(4); + } + else{ + functions::setError(4); + } + } + } + + if(isset($_GET['getdata'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count, id, name_1, name_2 FROM phases WHERE id=:id"); + $sql->execute(array(":id"=>$_GET['getdata'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + echo json_encode($res); + } + } + + if(isset($_POST['edit'])){ + if($_SESSION['accesslevel']<3){ + functions::setError(401); + } + else{ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM phases WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['edit'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + $sql=$db->prepare("UPDATE phases SET name_1=:n1, name_2=:n2 WHERE id=:id"); + $sql->execute(array(":n1"=>$_POST['name_1'], ":n2"=>$_POST['name_2'], ":id"=>$_POST['edit'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(5); + } + else{ + functions::setError(4); + } + } + } + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/profile.php b/subs/part/profile.php new file mode 100644 index 0000000..ec0a142 --- /dev/null +++ b/subs/part/profile.php @@ -0,0 +1,75 @@ +. + **/ + +?> + +
+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

+ + + + + + + + + +
" required>
" required>
+
+
+ +
+
diff --git a/subs/part/profile_backend.php b/subs/part/profile_backend.php new file mode 100644 index 0000000..b2c801a --- /dev/null +++ b/subs/part/profile_backend.php @@ -0,0 +1,49 @@ +. + **/ + +try{ + + if(isset($_POST['password']) && isset($_POST['password_confirm'])){ + if($_POST['password']!=$_POST['password_confirm']){ + functions::setError(10); + } + else{ + $sql=$db->prepare("UPDATE users SET password=:passwd WHERE id=:id"); + $sql->execute(array(":passwd"=>PasswordStorage::create_hash($_POST['password']), ":id"=>$_SESSION['id'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(6); + } + else{ + functions::setError(4); + } + } + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/register.php b/subs/part/register.php new file mode 100644 index 0000000..0a2f795 --- /dev/null +++ b/subs/part/register.php @@ -0,0 +1,631 @@ + the core + * @author Fándly Gergő Zoltán (fandlygergo@gmail.hu, systemtest.tk) + * @copy 2017 Fándly Gergő Zoltán + * License: + Result Manager for managing results of students in bilingual school systems. + Copyright (C) 2017 Fándly Gergő Zoltán + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + **/ + +$oid=0; +?> + +
+ + + + +
+ + +
+ + +
+ + + +
+ +
+ +
+ +
+
+
diff --git a/subs/part/register_backend.php b/subs/part/register_backend.php new file mode 100644 index 0000000..e11d851 --- /dev/null +++ b/subs/part/register_backend.php @@ -0,0 +1,382 @@ +. + **/ + +try{ + + if(isset($_GET['list'])){ + $filter="WHERE r.id<>0 and r.schoolyear=?"; + $filter_array=array($_GET['list']); + if(isset($_POST['filter'])){ + if(isset($_POST['f_search'])){ + if($_POST['f_search']!=""){ + $filter.=" and (s.fullname LIKE ? or c.name_1 LIKE ? or c.name_2 LIKE ? or p.name_1 LIKE ? or p.name_2 LIKE ? or t.fullname LIKE ?)"; + array_push($filter_array, "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%"); + } + } + + if(isset($_POST['f_student'])){ + for($i=0; $iprepare("SELECT r.id, s.fullname AS student, s.class AS class, sb.name_1 AS subject_1, sb.name_2 AS subject_2, c.name_1 AS contest_1, c.name_2 AS contest_2, c.description AS contest_desc, c.ministry_support, c.ministry_place, p.name_1 AS phase_1, p.name_2 AS phase_2, t.fullname AS teacher, r.place, r.mention FROM register AS r INNER JOIN users AS s ON (s.id=r.student) INNER JOIN contests AS c ON (c.id=r.contest) INNER JOIN subjects AS sb ON (sb.id=c.subject) INNER JOIN phases AS p ON (p.id=r.phase) INNER JOIN users AS t ON (t.id=r.teacher) ".$filter." ORDER BY id ASC"); + $sql->execute($filter_array); + + echo " + + + + + + + + + + + + + + + + + + + + + + + "; + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + echo " + + + + + + + + + + + + + + + + + + + "; + } + } + + if(isset($_POST['delete'])){ + $sql=$db->prepare("DELETE FROM register WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['delete'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(4); + } + else{ + functions::setError(4); + } + } + + if(isset($_POST['new'])){ + $new=array("student"=>$_POST['student'], "contest"=>$_POST['contest'], "phase"=>$_POST['phase'], "teacher"=>$_POST['teacher'], "place"=>($_POST['place']<0?$_POST['place']:$_POST['place_c']), "mention"=>$_POST['mention']); + + $sql=$db->prepare("SELECT r.id, p.name_1 AS phase_1, p.name_2 AS phase_2, t.fullname AS teacher, r.place, r.mention FROM register AS r INNER JOIN users AS s ON (s.id=r.student) INNER JOIN phases AS p ON (p.id=r.phase) INNER JOIN users AS t ON (t.id=r.teacher) WHERE r.student=:stud and r.contest=:cont ORDER BY r.id ASC"); + $sql->execute(array(":stud"=>$_POST['student'], ":cont"=>$_POST['contest'])); + $found=""; + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + $found.=" + + + + + + + + + + "; + } + + $exp=array("params"=>$new, "prev"=>$found); + echo json_encode($exp); + } + + if(isset($_POST['newSubmit'])){ + $data=json_decode($_POST['newSubmit']); + $sql=$db->prepare("INSERT INTO register (student, contest, phase, teacher, place, mention, schoolyear) VALUES (:stud, :cont, :phase, :teacher, :place, :mention, :sy)"); + $sql->execute(array(":stud"=>$data->student, ":cont"=>$data->contest, ":phase"=>$data->phase, ":teacher"=>$data->teacher, ":place"=>$data->place, ":mention"=>$data->mention, ":sy"=>$schoolyear)); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(3); + } + else{ + functions::setError(4); + } + } + + if(isset($_GET['getdata'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count, id, student, contest, phase, teacher, place, mention FROM register WHERE id=:id"); + $sql->execute(array(":id"=>$_GET['getdata'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + echo json_encode($res); + } + } + + if(isset($_POST['edit'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM register WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['edit'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + $sql=$db->prepare("UPDATE register SET student=:stud, contest=:cont, phase=:phase, teacher=:teacher, place=:place, mention=:mention WHERE id=:id"); + $sql->execute(array(":stud"=>$_POST['student'], ":cont"=>$_POST['contest'], ":phase"=>$_POST['phase'], ":teacher"=>$_POST['teacher'], ":place"=>($_POST['place']<0?$_POST['place']:$_POST['place_c']), ":mention"=>$_POST['mention'], ":id"=>$_POST['edit'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(5); + } + else{ + functions::setError(4); + } + } + } + + if(isset($_POST['export'])){ + //allow to run for a long time since this is a long process. 10 minute is more than enough + set_time_limit(600); + + $filter="WHERE r.id<>0"; + $filter_array=array(); + if(isset($_POST['filter'])){ + if(isset($_POST['f_search'])){ + if($_POST['f_search']!=""){ + $filter.=" and (s.fullname LIKE ? or c.name_1 LIKE ? or c.name_2 LIKE ? or p.name_1 LIKE ? or p.name_2 LIKE ? or t.fullname LIKE ?)"; + array_push($filter_array, "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%"); + } + } + + if(isset($_POST['f_student'])){ + for($i=0; $iprepare("SELECT r.id, s.fullname AS student, s.class AS class, sb.name_1 AS subject_1, sb.name_2 AS subject_2, c.name_1 AS contest_1, c.name_2 AS contest_2, c.description AS contest_desc, c.ministry_support, c.ministry_place, p.name_1 AS phase_1, p.name_2 AS phase_2, t.fullname AS teacher, r.place, r.mention, r.schoolyear FROM register AS r INNER JOIN users AS s ON (s.id=r.student) INNER JOIN contests AS c ON (c.id=r.contest) INNER JOIN subjects AS sb ON (sb.id=c.subject) INNER JOIN phases AS p ON (p.id=r.phase) INNER JOIN users AS t ON (t.id=r.teacher) ".$filter." ORDER BY r.schoolyear ASC, subject_1 ASC, contest_1 ASC, student ASC"); + $sql->execute($filter_array); + + //setting up file + $exp=$BOM; + $exp.="\"".strtr($config['general']['title'], array("\""=>"\"\""))."\"\n"; + $exp.="\"".strtr($config['general']['org'], array("\""=>"\"\""))."\"\n"; + $exp.="\"".strtr($lang['exported'], array("\""=>"\"\"")).": ".date("Y-m-d H:i:s")."\"\n\n"; + + //build header + $header=""; + foreach($_POST['export'] as $e){ + $header.="\"".strtr($lang[$e], array("\"", "\"\""))."\","; + } + $header=rtrim($header, ","); + $exp.=$header."\n"; + + //build content + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + $push=""; + foreach($_POST['export'] as $e){ + if($e=="ministry_support"){ + $push.="\"".strtr($lang['ministry_'.$row['ministry_support']], array("\""=>"\"\""))."\","; + } + else if($e=="ministry_place"){ + $push.="\"".strtr($row['ministry_support']==0?"":$row['ministry_place'], array("\""=>"\"\""))."\","; + } + else if($e=="place"){ + $push.="\"".strtr($row['place']<0?$lang['places'][$row['place']]:$row['place'], array("\""=>"\"\""))."\","; + } + else{ + $push.="\"".strtr($row[$e], array("\""=>"\"\""))."\","; + } + } + $push=rtrim($push, ","); + $exp.=$push."\n"; + } + + //save it to a temporary file + $file=tempnam(sys_get_temp_dir(), "resmanExp_"); + file_put_contents($file, $exp); + + //return file name for download + echo $file; + } + + if(isset($_GET['expdownload'])){ + if(!file_exists($_GET['expdownload'])){ + functions::setError(404); + header("Location: ".$_SERVER['HTTP_REFERER']); + } + else{ + //download exported file + header("Content-type: application/octet-stream"); + header("Content-disposition: attachment; filename='".$config['general']['title']."_export_".date("Y-m-d H-i-s").".csv'"); + readfile($_GET['expdownload']); + unlink($_GET['expdownload']); + die(); + } + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/subjects.php b/subs/part/subjects.php new file mode 100644 index 0000000..8f7765e --- /dev/null +++ b/subs/part/subjects.php @@ -0,0 +1,112 @@ +. + **/ + +?> + +
+ + + + +
+ =3): ?> + +
".$lang['id']."".$lang['student']."".$lang['class']."".$lang['subject_1']."".$lang['subject_2']."".$lang['contest_1']."".$lang['contest_2']."".$lang['contest_desc']."".$lang['ministry_support']."".$lang['ministry_place']."".$lang['phase_1']."".$lang['phase_2']."".$lang['teacher']."".$lang['place']."".$lang['mention']."".$lang['tools']."
".$row['id']."".$row['student']."".$row['class']."".$row['subject_1']."".$row['subject_2']."".$row['contest_1']."".$row['contest_2']."".$row['contest_desc']."".$lang['ministry_'.$row['ministry_support']]."".($row['ministry_support']==0?"":$row['ministry_place'])."".$row['phase_1']."".$row['phase_2']."".$row['teacher']."".($row['place']<0?$lang['places'][$row['place']]:$row['place'])."".str_replace(array("\n"), array("
"), $row['mention'])."
+ + +
".$row['id']."".$row['phase_1']."".$row['phase_2']."".$row['teacher']."".($row['place']<0?$lang['places'][$row['place']]:$row['place'])."".str_replace(array("\n"), array("
"), $row['mention'])."
+ + + + + + + + +
" required>
" required>
+
+
+ + + + + +
+ + + +
+ +
+ +
+ diff --git a/subs/part/subjects_backend.php b/subs/part/subjects_backend.php new file mode 100644 index 0000000..f6a9130 --- /dev/null +++ b/subs/part/subjects_backend.php @@ -0,0 +1,139 @@ +. + **/ + +try{ + + if(isset($_GET['list'])){ + $filter="WHERE id<>0"; + $filter_array=array(); + if(isset($_POST['filter'])){ + if(isset($_POST['f_search'])){ + $filter.=" and (name_1 LIKE ? or name_2 LIKE ?)"; + array_push($filter_array, "%".$_POST['f_search']."%", "%".$_POST['f_search']."%"); + } + } + + $sql=$db->prepare("SELECT id, name_1, name_2 FROM subjects ".$filter." ORDER BY name_1 ASC, name_2 ASC"); + $sql->execute($filter_array); + + //print list + echo " + + + + + + + ".($_SESSION['accesslevel']>=3?"":"")." + + + + "; + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + echo " + + + + + ".($_SESSION['accesslevel']>=3?"":"")." + + "; + } + } + + if(isset($_POST['new'])){ + $sql=$db->prepare("SELECT count(id) AS count FROM subjects WHERE name_1=:name_1 or name_2=:name_2"); + $sql->execute(array(":name_1"=>$_POST['name_1'], ":name_2" =>$_POST['name_2'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + if($res['count']>0){ + functions::setError(7); + } + else{ + $sql=$db->prepare("INSERT INTO subjects (name_1, name_2) VALUES (:name_1, :name_2)"); + $sql->execute(array(":name_1"=>$_POST['name_1'], ":name_2"=>$_POST['name_2'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(3); + } + else{ + functions::setError(4); + } + } + } + + if(isset($_POST['delete'])){ + $sql=$db->prepare("DELETE FROM subjects WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['delete'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(4); + } + else{ + functions::setError(4); + } + } + + if(isset($_GET['getdata'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count, id, name_1, name_2 FROM subjects WHERE id=:id"); + $sql->execute(array(":id"=>$_GET['getdata'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + echo json_encode($res); + } + } + + if(isset($_POST['edit'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM subjects WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['edit'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + if($res['count']<1){ + functions::setError(6); + } + else{ + $sql=$db->prepare("UPDATE subjects SET name_1=:name_1, name_2=:name_2 WHERE id=:id"); + $sql->execute(array(":name_1"=>$_POST['name_1'], ":name_2"=>$_POST['name_2'], ":id"=>$_POST['edit'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(5); + } + else{ + functions::setError(4); + } + } + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/users.php b/subs/part/users.php new file mode 100644 index 0000000..87955ad --- /dev/null +++ b/subs/part/users.php @@ -0,0 +1,189 @@ +. + **/ + +$oid=0; +?> + +
+ + + + +
+ +
".$lang['id']."".$lang['name_1']."".$lang['name_2']."".$lang['tools']."
".$row['id']."".$row['name_1']."".$row['name_2']." + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
">
" required>
" value=0 min=0 max=4 required>
+ +
+ +
">
+ +
+ +
">
+
+ + +
+
+
+
+ + + + + +
+ + +
+ +
+ +
+ diff --git a/subs/part/users_backend.php b/subs/part/users_backend.php new file mode 100644 index 0000000..3d50b43 --- /dev/null +++ b/subs/part/users_backend.php @@ -0,0 +1,206 @@ +. + **/ + +try{ + + if(isset($_GET['list'])){ + $filter="WHERE id<>1"; + $filter_array=array(); + if(isset($_POST['filter'])){ + if(isset($_POST['f_search'])){ + if($_POST['f_search']!=""){ + $filter.=" and (username LIKE ? or fullname LIKE ? or class LIKE ?)"; + array_push($filter_array, "%".$_POST['f_search']."%", "%".$_POST['f_search']."%", "%".$_POST['f_search']."%"); + } + } + if(isset($_POST['f_class'])){ + for($i=0; $iprepare("SELECT id, username, fullname, accesslevel, class, perm_message FROM users ".$filter." ORDER BY class ASC, fullname ASC, accesslevel ASC"); + $sql->execute($filter_array); + echo " + + + + + + + + + + + + + + "; + while($row=$sql->fetch(PDO::FETCH_ASSOC)){ + echo " + + + + + + + + + + "; + } + echo " + +
".$lang['id']."".$lang['username']."".$lang['fullname']."".$lang['accesslevel']."".$lang['class']."".$lang['perm_message']."".$lang['tools']."
".$row['id']."".$row['username']."".$row['fullname']."".$row['accesslevel']."".$row['class']."".($row['perm_message']?$lang['ryes']:$lang['rno'])." + + +
+ "; + } + + if(isset($_POST['new'])){ + if($_POST['username']!=""){ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM users WHERE username=:uname"); + $sql->execute(array(":uname"=>$_POST['username'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']>0){ + functions::setError(5); + } + } + + $password; + if($_POST['password']=="0"){ + $password=functions::randomString(6); + } + else{ + $password=$_POST['password']; + } + + $pm=isset($_POST['perm_message']); + + $sql=$db->prepare("INSERT INTO users (username, fullname, accesslevel, class, password, perm_message) VALUES (:uname, :fname, :al, :class, :passwd, :pm)"); + $sql->execute(array(":uname"=>$_POST['username'], ":fname"=>$_POST['fullname'], ":al"=>$_POST['accesslevel'], ":class"=>$_POST['class'], ":passwd"=>PasswordStorage::create_hash($_POST['password']), ":pm"=>$pm)); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(3); + } + else{ + functions::setError(4); + } + } + + if(isset($_GET['getdata'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count, id, username, fullname, accesslevel, class, perm_message FROM users WHERE id=:id"); + $sql->execute(array(":id"=>$_GET['getdata'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + if($res['count']<1){ + functions::setError(6); + } + else{ + echo json_encode($res); + } + } + + if(isset($_POST['edit'])){ + $sql=$db->prepare("SELECT COUNT(id) AS count FROM users WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['edit'])); + $res=$sql->fetch(PDO::FETCH_ASSOC); + + $pm=isset($_POST['perm_message']); + + if($res['count']<1){ + functions::setError(6); + } + else{ + $sql=$db->prepare("UPDATE users SET username=:uname, fullname=:fname, accesslevel=:al, class=:class, perm_message=:pm WHERE id=:id"); + $sql->execute(array(":uname"=>$_POST['username'], ":fname"=>$_POST['fullname'], ":al"=>$_POST['accesslevel'], ":class"=>$_POST['class'], ":pm"=>$pm, ":id"=>$_POST['edit'])); + $res1=$sql->rowCount(); + + //check if password needs update + if($_POST['password']==""){ + $res2=true; + } + else{ + $password; + if($_POST['password']=="0"){ + $password=functions::randomString(6); + } + else{ + $password=$_POST['password']; + } + + $sql=$db->prepare("UPDATE users SET password=:passwd WHERE id=:id"); + $sql->execute(array(":passwd"=>PasswordStorage::create_hash($password), ":id"=>$_POST['edit'])); + $res2=$sql->rowCount(); + } + + if($res1 && $res2){ + functions::setMessage(5); + } + else{ + functions::setError(4); + } + } + } + + if(isset($_POST['delete'])){ + $sql=$db->prepare("DELETE FROM users WHERE id=:id"); + $sql->execute(array(":id"=>$_POST['delete'])); + $res=$sql->rowCount(); + if($res>0){ + functions::setMessage(4); + } + else{ + functions::setError(4); + } + } + +} +catch(Exception $e){ + functions::setError(500); + error_log($e); +} diff --git a/subs/part/wizard.php b/subs/part/wizard.php new file mode 100644 index 0000000..c327185 --- /dev/null +++ b/subs/part/wizard.php @@ -0,0 +1,242 @@ +. + **/ + +$oid=0; + +?> + +
+ + + + +

+ +
+
+ + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+
diff --git a/subs/part/wizard_backend.php b/subs/part/wizard_backend.php new file mode 100644 index 0000000..13ba7f8 --- /dev/null +++ b/subs/part/wizard_backend.php @@ -0,0 +1,26 @@ +. + **/ + +//wizard is just an other frontend for the register. So no backend here