Bladeren bron

Merge branch 'master' into Teams

Iñigo Valentin 6 jaren geleden
bovenliggende
commit
d32f5257f4

+ 71 - 24
application/Controller.php

@@ -26,7 +26,7 @@
          *
          * @param Array $params POST parameters of the request.
          */
-        public function __construct($uid, $params){
+        public function __construct($params){
 
             global $path;
             global $UID;
@@ -52,11 +52,32 @@
                         action($db);
                         return;
                         break;
+                    case "LOGOUT":
+                        require_once($path["action"] . "logout.php");
+                        action($db);
+                        return;
+                        break;
                     case "NEW_TEAM":
                         require_once($path["action"] . "new_team.php");
                         action($db);
                         return;
                         break;
+                    case "SAVE_RUN_TEAM":
+                        require_once($path["action"] . "save_run_team.php");
+                        action($db);
+                        return;
+                        break;
+                    case "EDIT_PROFILE":
+                        require_once($path["action"] . "edit_profile.php");
+                        $response = action($db);
+                        if (strlen($response) > 1){
+                            echo($response);
+                            return;
+                        }
+                        else{
+                            return;
+                        }
+                        break;
                     default:
                         require_once($path["page"] . "Error_Page.php");
                         $page = new Error_Page($db, 404);
@@ -64,33 +85,55 @@
                 }
             }
 
-            // TODO: Validate private/public user with cookie and redirect
-            $UID = $uid;
-            if (preg_match("/\d{7}/", $UID)){
-                $q = $db->query("SELECT count(uid) AS c FROM player WHERE uid = $UID;");
-                $r = $q->fetchArray(SQLITE3_ASSOC);
-                if ($r["c"] == 0){
-                    require_once($path["page"] . "Landing_Page.php");
-                    $page = new Landing_Page($db);
-                    require_once($page->view);
-                    return;
-                }
+            if (isset($_COOKIE["uid"]) && isset($_SESSION["uid"]) && $_COOKIE["uid"] != $_COOKIE["uid"]){
+                // Both cookie and session are set, but they are different. Delete them both.
+                setcookie("uid", "", time() - 3600);
+                $_SESSION["session"] = false;
+                $_SESSION["uid"] = "";
+                require_once($path["page"] . "Landing_Page.php");
+                $page = new Landing_Page($db);
+                require_once($page->view);
+                return;
+            }
+            elseif (isset($_COOKIE["uid"]) && !isset($_SESSION["uid"])){
+                $_SESSION["session"] = true;
+                $_SESSION["uid"] = $_COOKIE["uid"];
+            }
+            elseif (!isset($_COOKIE["uid"]) && isset($_SESSION["uid"])){
+                setcookie("uid", $_SESSION["uid"], time() + 5 * 24 * 60 * 60);
+            }
+
+            // Validate session
+            $UID = $_SESSION["uid"];
+            $q = $db->query("SELECT count(uid) AS c FROM player WHERE uid = '$UID';");
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            if ($r["c"] == 0){
+                require_once($path["page"] . "Landing_Page.php");
+                $page = new Landing_Page($db);
+                require_once($page->view);
+                return;
             }
             else{
-                if ($_SESSION['session'] == true && isset($_SESSION['uid'])){
-                    $UID = $_SESSION['uid'];
-                    header("Location: /$UID" . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH));
-                    return;
-                }
-                else{
-                    require_once($path["page"] . "Landing_Page.php");
-                    $page = new Landing_Page($db);
-                    require_once($page->view);
-                    return;
+                // Reset cokie and renew session.
+                setcookie("uid", $_SESSION["uid"], time() + 5 * 24 * 60 * 60);
+                $_SESSION["session"] = true;
+                $_SESSION["uid"] = $UID;
+                // Recover username if lost
+                if (!isset($_SESSION["name"]) || strlen($_SESSION["name"]) == 0){
+                    $q = $db->query("SELECT name FROM player WHERE uid = '$UID';");
+                    $r = $q->fetchArray(SQLITE3_ASSOC);
+                    $_SESSION["name"] = $r["name"];
                 }
             }
 
+            // If UID not in URL, redirect and end.
+            if ($pars[0] != $UID){
+                header("Location: /$UID" . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH));
+                return;
+            }
+
             // Select the model to load.
+            array_shift($pars); // Remove first one, should be UID
             if (sizeof($pars) == 0){
                 require_once($path["page"] . "Home_Page.php");
                 $page = new Home_Page($db);
@@ -116,6 +159,10 @@
                     require_once($path["page"] . "Teams_Page.php");
                     $page = new Teams_Page($db);
                 }
+                elseif (strtoupper($pars[0]) == "PROFILE"){
+                    require_once($path["page"] . "Profile_Page.php");
+                    $page = new Profile_Page($db);
+                }
                 elseif (strtoupper($pars[0]) == "MONSTERS"){
                     if (count($pars) == 1){
                         require_once($path["page"] . "Monsters_Page.php");
@@ -146,11 +193,11 @@
                         $page = new Report_Skillup_Page($db, $pars[1]);
                     }
                     elseif (strtoupper($pars[1]) == "RUNEUPGRADE"){
-                    require_once($path["page"] . "Report_Runeupgrade_Page.php");
+                        require_once($path["page"] . "Report_Runeupgrade_Page.php");
                         $page = new Report_Runeupgrade_Page($db, $pars[1]);
                     }
                     elseif (strtoupper($pars[1]) == "RUNECRAFT"){
-                    require_once($path["page"] . "Report_Runecraft_Page.php");
+                        require_once($path["page"] . "Report_Runecraft_Page.php");
                         $page = new Report_Runecraft_Page($db, $pars[1]);
                     }
 

+ 1 - 3
application/Filter.php

@@ -3,8 +3,6 @@
     global $QUALITY;
     global $RUNE_STAT;
 
-    $a = "VARIALEA";
-
     $FILTER_CATALOG = [
         "element" => 0,
         "name" => "",
@@ -143,7 +141,7 @@
         "helper" => false,
         "number" => 20,
     ];
-    
+
     $FILTER_TEAM = [
         "name" => "",
         "area" => null

+ 69 - 0
application/action/edit_profile.php

@@ -0,0 +1,69 @@
+<?php
+
+    function generateApi($length = 16) {
+        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+        $charactersLength = strlen($characters);
+        $randomString = '';
+        for ($i = 0; $i < $length; $i++) {
+            $randomString .= $characters[rand(0, $charactersLength - 1)];
+        }
+        return $randomString;
+    }
+
+    function action($db = null){
+        global $AREA_TYPE;
+        global $DIFFICULTY;
+        global $SCENARIO;
+        global $DUNGEON;
+        global $ELEMENTAL_RIFT_DUNGEON;
+        global $LABYRINTH_STAGES;
+        global $UID;
+
+        $response = null;
+
+        $player = filter_input(INPUT_POST, 'uid');
+
+        if (filter_input(INPUT_POST, "mail")){
+            $mail = filter_input(INPUT_POST, 'mail');
+            // TODO: Validate mail
+            $s = $db->prepare('UPDATE player SET mail = :mail WHERE uid = :uid;');
+error_log("UPDATE player SET mail = '$mail' WHERE uid = '$player';");
+            $s->bindValue(':uid', $player);
+            $s->bindValue(':mail', $mail);
+            if(!$s->execute()){
+                return -1;
+            }
+        }
+
+        if (filter_input(INPUT_POST, "pass")){
+            $pass = sha1(filter_input(INPUT_POST, 'pass'));
+            $currentPass = sha1(filter_input(INPUT_POST, 'currentPass'));
+            // TODO: Validate current
+            $s = $db->prepare('UPDATE player SET password = :pass WHERE uid = :uid AND password = :currentPass;');
+            $s->bindValue(':uid', $player);
+            $s->bindValue(':pass', $mail);
+            $s->bindValue(':currentPass', $currentPass);
+            if(!$s->execute()){
+                return -2;
+            }
+        }
+
+        if (filter_input(INPUT_POST, "api")){
+            $api = generateApi();
+            $s = $db->prepare('UPDATE player SET api_key = :api WHERE uid = :uid;');
+            $s->bindValue(':uid', $player);
+            $s->bindValue(':api', $api);
+            if(!$s->execute()){
+                return -3;
+            }
+            $response = $api;
+        }
+
+        if ($response == null){
+            return 0;
+        }
+        else{
+            return $response;
+        }
+    }
+?>

+ 1 - 0
application/action/login.php

@@ -18,6 +18,7 @@
             return -4;
         }
         $uid = $r["uid"];
+        setcookie("uid", $uid, time() + 5 * 24 * 60 * 60); // 5 Days
         session_regenerate_id();
         $_SESSION['session'] = true;
         $_SESSION['name'] = $uname;

+ 14 - 0
application/action/logout.php

@@ -0,0 +1,14 @@
+<?php
+    function action($db = null){
+        global $UID;
+        $UID = null;
+        setcookie("uid", null, time() - 3600);
+        session_regenerate_id();
+        $_SESSION['session'] = false;
+        $_SESSION['name'] = "";
+        $_SESSION['uid'] = "";
+        header("Location: /");
+        die();
+        return 0;
+    }
+?>

+ 41 - 0
application/action/save_run_team.php

@@ -0,0 +1,41 @@
+<?php
+    function action($db = null){
+        $player = filter_input(INPUT_POST, 'uid');
+        $run = filter_input(INPUT_POST, 'run');
+        $name = filter_input(INPUT_POST, 'name');
+        $description = filter_input(INPUT_POST, 'description');
+        if ($name == null || strlen($name) == 0){
+            return -1;
+        }
+        $s = "SELECT CASE WHEN MAX(id) IS NULL THEN 1 ELSE MAX(id) + 1 END AS id FROM team;";
+        $q = $db->query($s);
+        $r = $q->fetchArray(SQLITE3_ASSOC);
+        $team_id = $r["id"];
+        $s = "SELECT run.area AS area, run.stage AS stage, run.difficulty AS difficulty, k_area.type AS area_type FROM run, k_area WHERE run.uid = '$player' AND run.id = '$run' AND k_area.id = run.area AND run.helper = 0;";
+        $q = $db->query($s);
+        $r = $q->fetchArray(SQLITE3_ASSOC);
+        if (!$r){
+            return -2;
+        }
+        $statement = $db->prepare('INSERT INTO team (uid, id, name, description, area_type, area, stage, difficulty) VALUES (:uid, :id, :name, :description, :area_type, :area, :stage, :difficulty);');
+        $statement->bindValue(':uid', $player);
+        $statement->bindValue(':id', $team_id);
+        $statement->bindValue(':name', $name);
+        $statement->bindValue(':description', $description);
+        $statement->bindValue(':area_type', $r["area_type"]);
+        $statement->bindValue(':area', $r["area"]);
+        $statement->bindValue(':stage', $r["stage"]);
+        $statement->bindValue(':difficulty', $r["difficulty"]);
+        $res = $statement->execute();
+        if (!$res){
+            return -3;
+        }
+        $s = "SELECT unit FROM run_party WHERE run = $run AND unit IS NOT NULL;";
+        $q = $db->query($s);
+        while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+            $s = "INSERT INTO team_unit VALUES ('$team_id', '" . $r["unit"] . "');";
+            $db->query($s);
+        }
+        return 0;
+    }
+?>

+ 37 - 25
application/bin/log-profile.py

@@ -81,22 +81,6 @@ def openDatabase(name):
     print('Configuring database...')
     try:
         db = sqlite3.connect(name)
-        cursor = db.cursor()
-        cursor.execute('PRAGMA foreign_keys = OFF;')
-        cursor.execute('DELETE FROM scenario')
-        cursor.execute('DELETE FROM defense')
-        cursor.execute('DELETE FROM unit')
-        cursor.execute('DELETE FROM unit_skill')
-        cursor.execute('DELETE FROM rune')
-        cursor.execute('DELETE FROM building')
-        cursor.execute('DELETE FROM decoration')
-        cursor.execute('DELETE FROM inventory')
-        cursor.execute('DELETE FROM summon_special')
-        cursor.execute('DELETE FROM guild')
-        cursor.execute('DELETE FROM guild_member')
-        cursor.execute('DELETE FROM rune_craft')
-        db.commit()
-        cursor.close()
         return db
     except sqlite3.IntegrityError as e:
         print("Error executing statement: " + str(e))
@@ -105,6 +89,41 @@ def openDatabase(name):
         print("I/O Error creating database " + name + ": " + str(e))
         raise
 
+"""
+Clears the user tables.
+
+It also disables referencial integrity.
+
+:param db: Sqlite database connection.
+:param data: Data in json format.
+:raises IntegrityError: The queryes couldn't bre executed.
+"""
+def clearData(db, data):
+    print('Clearing previous data...')
+    try:
+        uid = data["wizard_info"]["wizard_id"]
+        cursor = db.cursor()
+        cursor.execute('PRAGMA foreign_keys = OFF;')
+        cursor.execute('DELETE FROM scenario WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM defense WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM unit WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM unit_skill WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM rune WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM building WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM decoration WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM inventory WHERE uid = ?', (uid))
+        #cursor.execute('DELETE FROM summon_special')
+        #cursor.execute('DELETE FROM guild')
+        cursor.execute('DELETE FROM guild_member WHERE uid = ?', (uid))
+        cursor.execute('DELETE FROM rune_craft WHERE uid = ?', (uid))
+        db.commit()
+        cursor.close();
+    except sqlite3.IntegrityError as e:
+        print("Error executing statement: " + str(e))
+        raise
+    return
+
+
 """
 Closes the database.
 
@@ -659,7 +678,7 @@ def parseRunes(db, data):
             substat_3_enchant = 0
             substat_3_grind = 0
         if len(rune["sec_eff"]) > 3:
-            substat_4 = rune["sec_eff"][0][0]
+            substat_4 = rune["sec_eff"][3][0]
             substat_4_value = rune["sec_eff"][3][1]
             substat_4_enchant = rune["sec_eff"][3][2]
             substat_4_grind = rune["sec_eff"][3][3]
@@ -929,14 +948,6 @@ def get_max_efficiency(substats, efficiency, level, stars, quality):
     substat_upgrades_remaining = (4 - math.floor((min(level, 12) / 3))) - max((3 - quality), 0)
     new_stats = max(min(4 - len(substats), substat_upgrades_remaining), 0)
     old_stats = substat_upgrades_remaining - new_stats
-    
-    #if id == 16042097882:
-    #    print("substat_upgrades_remaining: " + str(substat_upgrades_remaining))
-    #    print("quality: " + str(quality))
-    #    print("level: " + str(level))
-    #    print("math.floor((min(level, 12) / 3)): " + str(math.floor((min(level, 12) / 3))))
-    #    print("old_stats: " + str(old_stats))
-    #    print("new_stats: " + str(new_stats))
 
     if old_stats > 0:
         # we can repeatedly upgrade the most value of the existing stats
@@ -1053,6 +1064,7 @@ if verifyKey(db, data, key) == False:
     print("Invalid API KEY...")
     sys.exit(-1)
 else:
+    clearData(db, data)
     parsePlayer(db, data)
     parseScenarios(db, data)
     parseDefense(db, data)

+ 75 - 0
application/entity/Custom_Filter.php

@@ -0,0 +1,75 @@
+<?php
+
+    require_once($path["entity"] . "Entity.php");
+
+    /**
+     * A custom_Filter.
+     *
+     * Represents an object from the table 'filter'.
+     */
+    class Custom_Filter extends Entity{
+
+        /**
+         * Player identifier.
+         */
+        public $uid;
+
+        /**
+         * Filter identifier.
+         */
+        public $id;
+
+        /**
+         * Name of the pge the filter is for.
+         */
+        public $page;
+
+        /**
+         * Filter name.
+         */
+        public $name;
+
+        /**
+         * Filter description.
+         */
+        public $description;
+
+        /**
+         * Filter condition.
+         */
+        public $condition;
+
+        /**
+         * Constructor.
+         *
+         * Searches the database and retrieves the information about the
+         * run, populating it and it's items.
+         *
+         * @param SQLite3 $db Connection to the database.
+         * @param int $id Filter identifier.
+         */
+        public function __construct($db, $id){
+            parent::__construct($db);
+            $s = "
+                SELECT
+                  uid,
+                  id,
+                  page,
+                  name,
+                  description,
+                  condition
+                FROM filter
+                WHERE id = $id;
+            ";
+            $q = $this->db->query($s);
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $this->id = $r["id"];
+            $this->uid = $r["uid"];
+            $this->page = $r["page"];
+            $this->name = $r["name"];
+            $this->description = $r["description"];
+            $this->condition = $r["condition"];
+        }
+
+    }
+?>

+ 48 - 0
application/page/Profile_Page.php

@@ -0,0 +1,48 @@
+ <?php
+
+    require_once($path["page"] . "Page.php");
+
+
+    /**
+     * User profile page.
+     */
+    class Profile_Page extends Page{
+
+        public $name;
+        public $mail;
+        public $api_key;
+
+        /**
+         * Constructor.
+         *
+         * Retrieves the data and initializes the variables.
+         *
+         * @param SQLite3 $db Connection to the database.
+         */
+        public function __construct($db){
+            global $path;
+            global $root;
+            global $UID;
+            parent::__construct($db);
+            $this->view = $path["view"] . "profile.php";
+            $s = "
+              SELECT
+                name,
+                mail,
+                api_key
+              FROM player
+              WHERE uid = '$UID';
+            ";
+            $q = $this->db->query($s);
+            if ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                $this->name = $r["name"];
+                $this->mail = $r["mail"];
+                $this->api_key = $r["api_key"];
+            }
+            $this->title = $this->name ." - SWDB";
+            $this->description = $this->name . " user profile";
+            $this->canonical = $root . "profile/";
+        }
+
+    }
+?>

+ 58 - 2
application/page/Runes_Page.php

@@ -1,6 +1,7 @@
  <?php
 
     require_once($path["page"] . "Page.php");
+    require_once($path["entity"] . "Custom_Filter.php");
     require_once($path["entity"] . "Rune.php");
     require_once($path["entity"] . "Unit.php");
 
@@ -16,6 +17,11 @@
          */
         public $runes = [];
 
+        /**
+         * Custom filters for this page.
+         */
+        public $custom_filters = [];
+
         /**
          * The filters the page can handle.
          */
@@ -50,10 +56,17 @@
         public function __construct($db){
             global $path;
             global $root;
+            global $UID;
             parent::__construct($db);
             $this->view = $path["view"] . "runes.php";
             $this->parse_filters();
-            $s = $this->build_query();
+            if (!isset($_GET["custom_filter"])){
+                $s = $this->build_query();
+                $this->filters["group"] = 1;
+            }
+            else{
+                $s = $this->build_custom_query($_GET["custom_filter"]);
+            }
             $q = $this->db->query($s);
             while ($r = $q->fetchArray(SQLITE3_ASSOC)){
                 $rune = new Rune($db, $r["id"]);
@@ -61,7 +74,18 @@
                     $rune->assigned_to = new Unit($this->db, $rune->assigned_to, false);
                 }
                 array_push($this->runes, $rune);
-        }
+            }
+            $s = "
+              SELECT id
+              FROM filter
+              WHERE
+                uid = '$UID' AND
+                page = 'RUNES';
+            ";
+            $q = $this->db->query($s);
+            while ($r = $q->fetchArray(SQLITE3_ASSOC)){
+                array_push($this->custom_filters, new Custom_Filter($this->db, $r["id"]));
+            }
             $this->title = "Runes - SWDB";
             $this->description = "Rune inventory";
             $this->canonical = $root . "runes/";
@@ -144,6 +168,38 @@
             return;
         }
 
+        /**
+         * Builds the query to the rune table using the a custom filter.
+         * 
+         * @param int $filter Custom filter ID.
+         * @return The query to be executed.
+         */
+        private function build_custom_query($filter){
+            global $UID;
+            $s = "
+              SELECT condition
+              FROM filter
+              WHERE
+                uid = '$UID' AND
+                id = $filter;
+            ";
+            $q = $this->db->query($s);
+            $r = $q->fetchArray(SQLITE3_ASSOC);
+            $cond = $r["condition"];
+            $s = "
+              SELECT
+                id,
+                assigned_to
+              FROM rune
+              WHERE uid = '$UID' AND (
+                $cond
+              )
+              ORDER BY  slot, type, stars DESC, original_quality DESC, quality DESC, level;
+            ";
+            error_log("CUSTOM QUERY: " . $s);
+            return $s;
+        }
+
         /**
          * Builds the query to the rune table using the selected or default
          * filters.

+ 53 - 0
application/view/inc/filter_runes.php

@@ -3,7 +3,60 @@
     global $RUNE_STAT;
     global $QUALITY;
 ?>
+<?php
+    if (count($custom_filters) > 0){
+?>
+        <script>
+            /**
+             * Changes the filter description.
+             *
+             * @param id Filter id.
+             */
+            function customFilterDescription(id){
+                switch (id){
+<?php
+                    foreach($custom_filters AS $filter){
+?>
+                        case '<?=$filter->id?>':
+                            document.getElementById("custom_filter_description").style.display = 'block';
+                            document.getElementById("custom_filter_description").innerHTML = '<?=$filter->description?>';
+                            break;
+<?php
+                    }
+?>
+                }
+            }
+        </script>
+        <form action='/<?=$UID?>/runes/' method='get' id='custom_filter_runes' class='custom_filter'>
+            <span class='custom_filter_title'>Custom filters:</span>
+            <select name='custom_filter' onChange='customFilterDescription(this.value);' onload='alert("LOAD");customFilterDescription("<?=$_GET["custom_filter"]?>");'>
+                <option> </option>
+<?php
+                 foreach($custom_filters AS $filter){
+                    if ($_GET["custom_filter"] == $filter->id){
+                        $selected = "selected='selected'";
+                    }
+                    else{
+                        $selected = "";
+                    }
+?>
+                    <option value='<?=$filter->id?>' <?=$selected?>><?=$filter->name?></option>
+<?php
+                 }
+?>
+            </select>
+            <input type='submit' value='Apply'/>
+            <p id='custom_filter_description'> </p>
+            <script>
+                customFilterDescription("<?=$_GET["custom_filter"]?>");
+            </script>
+        </form>
+<?php
+    }
+?>
+</form>
 <form action='/<?=$UID?>/runes/' method='get' id='filter_runes' class='filter'>
+    <div class='custom_filters'>
     <table>
         <tr>
             <td class='filter' rowspan='3'>

+ 279 - 0
application/view/profile.php

@@ -0,0 +1,279 @@
+<!DOCTYPE html>
+<html lang='en'>
+    <head>
+        <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
+        <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
+        <title><?=$page->title?></title>
+        <link rel='shortcut icon' href='<?=$page->favicon?>'/>
+        <!-- CSS files -->
+        <link rel='stylesheet' type='text/css' href='<?=$path["css"]?>ui.css'/>
+        <link rel='stylesheet' type='text/css' href='<?=$path["css"]?>profile.css'/>
+        <!-- Meta tags -->
+        <link rel='canonical' href='<?=$page->canonical?>'/>
+        <link rel='author' href='<?=$page->author?>'/>
+        <link rel='publisher' href='<?=$page->author?>'/>
+        <meta name='description' content='<?=$page->description?>'/>
+        <meta property='og:title' content='<?=$page->title?>'/>
+        <meta property='og:url' content='<?=$page->canonical?>'/>
+        <meta property='og:description' content='<?=$page->description?>'/>
+        <meta property='og:image' content='<?=$page->icon?>'/>
+        <meta property='og:site_name' content='<?=$page->name?>'/>
+        <meta property='og:type' content='website'/>
+        <meta property='og:locale' content='en'/>
+        <meta name='twitter:card' content='summary'/>
+        <meta name='twitter:title' content='<?=$page->title?>'/>
+        <meta name='twitter:description' content='<?=$page->description?>'/>
+        <meta name='twitter:image' content='<?=$page->icon?>'/>
+        <meta name='twitter:url' content='<?=$page->canonical?>'/>
+        <meta name='robots' content='index follow'/>
+        <script>
+
+            /**
+             * Redirects to the logout action.
+             */
+            function logout(){
+                window.location = '/action/logout/';
+            }
+
+            /**
+             * Shows an error window.
+             *
+             * @param msg Error message
+             */
+            function showError(msg){
+                document.getElementById('error_window').style.display = 'block';
+                document.getElementById('error_msg').innerHTML = msg;
+            }
+
+            /**
+             * Closes the error window.
+             */
+            function closeError(){
+                document.getElementById('error_window').style.display = 'none';
+                document.getElementById('error_msg').innerHTML = ' ';
+            }
+
+            /**
+             * Shows the mail change form.
+             */
+            function changeMail(){
+                document.getElementById('static_mail').style.display = 'none';
+                document.getElementById('change_mail').style.display = 'none';
+                document.getElementById('edit_mail').style.display = 'block';
+                document.getElementById('save_mail').style.display = 'block';
+            }
+
+            /**
+             * Saves an email change.
+             * 
+             * Validates the new email format and makes an AJAX request. If
+             * succesfull, hides the email change form.
+             */
+            function saveMail(){
+                var http = new XMLHttpRequest();
+                var mail = document.getElementById('edit_mail').value;
+                if (mail.length < 1){
+                    showError('Please enter a new email.');
+                    return;
+                }
+                if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(mail) == false){
+                    showError('Invalid email');
+                    return;
+                }
+                http.open('POST', '/action/edit_profile', true);
+                http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+                http.onreadystatechange = function(){
+                    if (this.readyState == 4){
+                        if (this.status == 200 || this.status == 204){
+                            document.getElementById('static_mail').innerHTML = mail;
+                            document.getElementById('static_mail').style.display = 'block';
+                            document.getElementById('change_mail').style.display = 'block';
+                            document.getElementById('edit_mail').style.display = 'none';
+                            document.getElementById('save_mail').style.display = 'none';
+                        }
+                        else{
+                            showError('An error ocurred: ' + this.responseText + ' (' + this.status + ').');
+                        }
+                    }
+                }
+                http.send('uid=<?=$UID?>&mail=' + mail);
+            }
+
+            /**
+             * Shows the password change form.
+             */
+            function changePass(){
+                document.getElementById('static_pass').style.display = 'none';
+                document.getElementById('change_pass').style.display = 'none';
+                document.getElementById('edit_pass_current').value = '';
+                document.getElementById('edit_pass_current').value = '';
+                document.getElementById('edit_pass_new').value = '';
+                document.getElementById('edit_pass_repeat').style.display = 'block';
+                document.getElementById('edit_pass_new').style.display = 'block';
+                document.getElementById('edit_pass_repeat').style.display = 'block';
+                document.getElementById('save_pass').style.display = 'block';
+            }
+
+            /**
+             * Saves a password change.
+             * 
+             * Validates the new password and makes an AJAX request. If
+             * succesfull, hides the password change form.
+             */
+            function savePass(){
+                var http = new XMLHttpRequest();
+                var pass = document.getElementById('edit_pass_new').value;
+                var currentPass = document.getElementById('edit_pass_current').value;
+                var repeatPass = document.getElementById('edit_pass_repeat').value;
+                if (pass.length < 1){
+                    showError('Please enter a new password.');
+                    return;
+                }
+                if (mail.length < 6){
+                    showError('Minimum password length is 6 characters.');
+                    return;
+                }
+                if (currentPass.length < 1){
+                    showError('Please enter your current password.');
+                    return;
+                }
+                if (pass != repeatPass){
+                    showError('Entered passwords do not match.');
+                    return;
+                }
+                http.open('POST', '/action/edit_profile', true);
+                http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+                http.onreadystatechange = function(){
+                    if (this.readyState == 4){
+                        if (this.status == 200 || this.status == 204){
+                            document.getElementById('static_pass').style.display = 'block';
+                            document.getElementById('change_pass').style.display = 'block';
+                            document.getElementById('edit_pass_current').style.display = 'none';
+                            document.getElementById('edit_pass_new').style.display = 'none';
+                            document.getElementById('edit_pass_repeat').style.display = 'none';
+                            document.getElementById('save_pass').style.display = 'none';
+                        }
+                        else{
+                            showError('An error ocurred: ' + this.responseText + ' (' + this.status + ').');
+                        }
+                    }
+                }
+                http.send('uid=<?=$UID?>&pass=' + pass + '&currentPass=' + currentPass);
+            }
+
+            /**
+             * Generates a new random API key.
+             *
+             * Makes a request. On success, updates the API in the UI.
+             */
+            function regenerateApi(){
+                var http = new XMLHttpRequest();
+                http.open('POST', '/action/edit_profile', true);
+                http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+                http.onreadystatechange = function(){
+                    if (this.readyState == 4){
+                        if (this.status == 200 || this.status == 204){
+                            document.getElementById('api_key').innerHTML = this.responseText;
+                        }
+                        else{
+                            showError('An error ocurred: ' + this.responseText + ' (' + this.status + ').');
+                        }
+                    }
+                }
+                http.send('uid=<?=$UID?>&api=1');
+            }
+        </script>
+    </head>
+    <body>
+<?php
+        include __DIR__ . "/inc/header.php";
+?>
+        <section id='monster' class='content'>
+            <h2>
+                <span>
+                    <?=$page->name?>
+                </span>
+            </h2>
+            <article>
+                <table id='profile'>
+                    <tr>
+                        <td class='title'>
+                            Username:
+                        </td>
+                        <td class='value'>
+                            <span>
+                                <?=$page->name?>
+                            </span>
+                        </td>
+                        <td class='action'>
+                            <input onclick='logout();' type='button' value='Logout'/>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='title'>
+                            eMail:
+                        </td>
+                        <td class='value'>
+                            <span id='static_mail'>
+                                <?=$page->mail?>
+                            </span>
+                            <input id='edit_mail' type='text' value='<?=$page->mail?>'/>
+                        </td>
+                        <td class='action'>
+                            <input id='change_mail' onclick='changeMail();' type='button' value='Change'/>
+                            <input id='save_mail' onclick='saveMail();' type='button' value='Save'/>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='title'>
+                            API key:
+                        </td>
+                        <td class='value'>
+                            <span id='api_key'>
+                                <?=$page->api_key?>
+                            </span>
+                        </td>
+                        <td class='action'>
+                            <input id='regenerate_api' onclick='regenerateApi();' type='button' value='Regenerate'/>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class='title'>
+                            Password:
+                        </td>
+                        <td class='value'>
+                            <span id='static_pass'>
+                                ********
+                            </span>
+                            <input id='edit_pass_current' type='password' placeholder='Currrent password'/>
+                            <input id='edit_pass_new' type='password' placeholder='New password'/>
+                            <input id='edit_pass_repeat' type='password' placeholder='Repeat password'/>
+                        </td>
+                        <td class='action'>
+                            <input id='change_pass' onclick='changePass();' type='button' value='Change'/>
+                            <input id='save_pass' onclick='savePass();' type='button' value='Save'/>
+                        </td>
+                    </tr>
+                </table>
+            </article>
+        </section> <!-- runes -->
+        <div id='error_window'>
+            <section class='content'>
+                <h2>
+                    <span>
+                        Error
+                    </span>
+                </h2>
+                <article>
+                    <p id='error_msg'>
+                    </p>
+                    <input type='button' value='Close' onclick='closeError();'/>
+                </article>
+            </section>
+        </div>
+
+<?php
+        include __DIR__ . "/inc/footer.php";
+?>
+    </body>
+</html>

+ 1 - 0
application/view/runes.php

@@ -40,6 +40,7 @@
                 </h2>
 <?php
                 $filters = $page->filters;
+                $custom_filters = $page->custom_filters;
                 include __DIR__ . "/inc/filter_runes.php";
 ?>
 

+ 78 - 0
application/view/runs.php

@@ -1,6 +1,7 @@
 <?php
     global $AREA_TYPE;
     global $SCENARIO;
+    global $UID;
 ?>
 <!DOCTYPE html>
 <html lang='en'>
@@ -33,6 +34,36 @@
         <meta name='twitter:image' content='<?=$page->icon?>'/>
         <meta name='twitter:url' content='<?=$page->canonical?>'/>
         <meta name='robots' content='index follow'/>
+        <script>
+            function saveForm(id, show){
+                if (show === true){
+                    document.getElementById('save_team').style.display = 'block';
+                    document.getElementById('save_id').value = id;
+                }
+                else{
+                    document.getElementById('save_team').style.display = 'none';
+                }
+            }
+
+            function saveTeam(){
+                var id = document.getElementById('save_id').value;
+                var name = document.getElementById('save_team_name').value;
+                var description = document.getElementById('save_team_description').value;
+                if (name == ''){
+                    alert('Name required')
+                    return;
+                }
+                var xhttp = new XMLHttpRequest();
+                xhttp.onreadystatechange = function() {
+                    if (this.readyState == 4 && this.status == 200) {
+                        saveForm(null, false);
+                    }
+                };
+                xhttp.open("POST", "/action/save_run_team/", true);
+                xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+                xhttp.send('uid=<?=$UID?>&run=' + id + '&name=' + encodeURI(name) + '&description=' + encodeURI(description));
+            }
+        </script>
     </head>
     <body>
 <?php
@@ -50,6 +81,9 @@
 
             </section> <!-- #filters -->
             <section class='content'>
+                <h2>
+                    Runs
+                </h2>
                 <article>
                     <table id='runs'>
                         <tr>
@@ -71,6 +105,7 @@
                         </tr>
 <?php
                         foreach ($page->runs as $run){
+                            $enable_save = true;
 ?>
                             <tr>
                                 <td class='area'>
@@ -108,6 +143,7 @@
                                 <td class='team'>
 <?php
                                     if ($run->helper == 1){
+                                        $enable_save = false;
 ?>
                                         <div class='monster_panel helper'>
                                             <img class='monster' title='Friend/Menstor' src='<?=$path["img"]["currency"]?>socialpoint.png'/>
@@ -116,6 +152,7 @@
                                     }
                                     foreach ($run->party as $party){
                                         if ($party instanceof K_Unit){
+                                            $enable_save = false;
                                             $disabled = "disabled";
                                         }
                                         else{
@@ -233,6 +270,15 @@
                                         </div>
 <?php
                                     }
+?>
+                                </td>
+                                <td class='action'>
+<?php
+                                    if ($enable_save){
+?>
+                                        <img alt='Save Team' class='save_team' onclick='saveForm(<?=$run->id?>, true);' src='<?=$path["img"]["icon"]?>save.png'/>
+<?php
+                                    }
 ?>
                                 </td>
                             </tr>
@@ -242,6 +288,38 @@
                     </table>
                 </article>
             </section>
+            <section id='save_team' class='content'>
+                <h2>
+                    Save run
+                </h2>
+                <article>
+                    <input id='save_id' type='hidden' value=''/>
+                    <table>
+                        <tr>
+                            <td class='save_title'>
+                                Name
+                            </td>
+                            <td class='save_values'>
+                                <input type='text' id='save_team_name' name='name'/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class='save_title'>
+                                Description
+                            </td>
+                            <td class='save_values'>
+                                <textarea id='save_team_description' name='description'></textarea>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td colspan='2'>
+                                <input type='button' value='Save' onClick='saveTeam();'/>
+                                <input type='button' value='Close' onClick='saveForm(null, false);'/>
+                            </td>
+                        </tr>
+                    </table>
+                </article>
+            </section>
 <?php
         include __DIR__ . "/inc/footer.php";
 ?>

+ 23 - 3
application/view/teams.php

@@ -142,13 +142,11 @@
                 var xhttp = new XMLHttpRequest();
                 xhttp.onreadystatechange = function() {
                     if (this.readyState == 4 && this.status == 200) {
-                        alert("200");
-                        //location.reload(); 
+                        location.reload(); 
                     }
                 };
                 xhttp.open("POST", "/action/new_team/", true);
                 xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
-                console.log('uid=<?=$UID?>&ids=' + ids + '&area_type=' + area_type + '&area=' + area + '&difficulty=' + difficulty + '&stage=' + stage + '&name=' + encodeURI(name) + '&description=' + encodeURI(description));
                 xhttp.send('uid=<?=$UID?>&ids=' + ids + '&area_type=' + area_type + '&area=' + area + '&difficulty=' + difficulty + '&stage=' + stage + '&name=' + encodeURI(name) + '&description=' + encodeURI(description));
 
             }
@@ -209,6 +207,8 @@
                 if (name != null){
                     var monsterPanel = document.createElement('div');
                     monsterPanel.setAttribute('class', 'monster_panel');
+                    monsterPanel.setAttribute('id', 'monster_panel_' + id);
+                    monsterPanel.setAttribute('onclick', 'deleteUnit(' + id + ')');
                     var monsterStars = document.createElement('span');
                     monsterStars.setAttribute('class', 'stars');
                     for (var i = 0; i < stars; i ++){
@@ -253,6 +253,24 @@
              * @param id Unit id.
              */
             function deleteUnit(id){
+                var position = -1;
+                for (var i = 0; i < units.length; i ++) {
+                    if (units[i].id == id){
+                        position = i;
+                        break;
+                    }
+                }
+                if (position >= 0 && position < units.length){
+                    units.splice(position, 1);
+                    document.getElementById('new_team_units').removeChild(document.getElementById('monster_panel_' + id));
+                    selected_units --;
+                    if (selected_units >= max_units){
+                        document.getElementById('new_unit').style.display = 'none';
+                    }
+                    else{
+                        document.getElementById('new_unit').style.display = 'block';
+                    }
+                }
             }
 
             /**
@@ -681,8 +699,10 @@
                     <tr>
                         <td colspan='2'>
                             <input type='button' value='Save' onClick='saveTeam();'/>
+                            <input type='button' value='Close' onClick='showNewTeam(false);'/>
                         </td>
                     </tr>
+                </table>
             </article>
         </section>
     </body>

File diff suppressed because it is too large
+ 574 - 133
install_data/setup.sql


+ 15 - 1
public/css/filters.css

@@ -10,9 +10,23 @@ section.filters{
     }
 }
 
+form.custom_filter{
+    border-bottom: 0.1em solid #ffffff;
+    padding-bottom: 0.5em;
+    margin: 0 1em 1em 1em;
+}
+
+form.custom_filter p#custom_filter_description{
+    display: none;
+    font-size: 70%;
+    margin: 0.2em;
+    background-color: #886644;
+    border-radius: 0.4em;
+    border: 0.1em solid #000000;
+}
+
 form.filter{
     text-align:center;
-    background-color: #aa8866;
 }
 
 form.filter table{

+ 69 - 0
public/css/profile.css

@@ -0,0 +1,69 @@
+table#profile{
+    margin: auto;
+    border-collapse: collapse;
+    border: 0.1em solid #000000;
+}
+
+table#profile td{
+    padding: 0.3em;
+    border-top: 0.1em solid #000000;
+    border-bottom: 0.1em solid #000000;
+}
+
+table#profile td.title{
+    text-align: right;
+    width: 6em;
+}
+
+table#profile td.value{
+    width: 10em;
+    text-align: left;
+}
+
+table#profile td.action{
+    width: 6em;
+    text-align: left;
+}
+
+table#profile td input, span{
+    width: 100%;
+}
+
+input#edit_mail,
+input#edit_pass_current,
+input#edit_pass_new,
+input#edit_pass_repeat,
+input#save_mail,
+input#save_pass{
+    display: none;
+}
+
+input#change_mail,
+span#static_mail,
+span#static_pass,
+span#api_key{
+    display: block;
+}
+
+div#error_window{
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: #00000088;
+    text-align: center;
+    display: none;
+}
+
+div#error_window h2{
+    text-align: left;
+}
+
+div#error_window section{
+    margin: 5em auto auto auto;
+    min-height: 6em;
+    width: 60%;
+    text-align: center;
+}
+

+ 33 - 0
public/css/runs.css

@@ -113,3 +113,36 @@ table#runs td.reward div.item_rune table.rune{
 table#runs td.reward div.item_rune:hover table.rune{
     display: block;
 }
+
+table#runs td.action img.save_team{
+    width: 1em;
+    height: 1em;
+    cursor: pointer;
+}
+
+
+section#save_team{
+    z-index: 1;
+    display: none;
+    position: fixed;
+    width: 80%;
+    height: 70%;
+    margin: 0 10%;
+    top: 10%;
+    filter: drop-shadow(0 0 10em #000000) drop-shadow(0 0 6em #000000);
+}
+
+section#save_team table{
+    width: 100%;
+}
+
+section#save_team table td.save_title{
+    width: 10em;
+    text-align: right;
+    padding-right: 2em;
+}
+
+section#save_team table td.save_values{
+    text-align: left;
+    padding-right: 2em;
+}

+ 11 - 1
public/css/teams.css

@@ -79,6 +79,7 @@ section#new_team div#new_team_units{
 
 section#new_team div#new_team_units div.monster_panel{
     font-size: 50%;
+    cursor: not-allowed;
 }
 
 section#new_team div.new_unit{
@@ -96,9 +97,18 @@ section#new_team div.new_unit div.new_unit_autocomplete{
 
 section#new_team div.new_unit div.new_unit_autocomplete div{
     border: 0.05em solid #000000;
-    margin: 0.1em;
     text-align: left;
+    text-transform: capitalize;
+    font-variant: small-caps;
     cursor: pointer;
+    color: var(--input-color);
+    font-weight: var(--input-font-weight);
+    padding: var(--input-padding);
+    background: var(--input-background);
+    border: var(--input-border);
+    border-radius: var(--input-border-radius);
+    text-shadow: var(--input-text-shadow);
+    box-shadow: var(--input-box-shadow);
 }
 
 section#new_team div.new_unit div.new_unit_autocomplete div span{

+ 1 - 9
public/index.php

@@ -2,15 +2,7 @@
     include __DIR__ . "/../application/config.php";
     include_once($path["controller"]);
 
-    //$request = $_SERVER['REQUEST_URI'];
     $request = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
     $params = explode('/', $request);
-    array_shift($params);
-    $uid = "";
-    if (count($params) > 0 && preg_match("/\d{7}/", $params[0])){
-        $uid = $params[0];
-        array_shift($params);
-    }
-
-    $controller = new Controller($uid, $params);
+    $controller = new Controller($params);
 ?>

Some files were not shown because too many files changed in this diff