New version + Reworked login process to suppress unauthenticated request + reworked file persistence + added tests module and API tests + reworked how the list repositories user can access is generated (can now only access repositories that are both in his repository list AND in the config repository list, so there is no need to add every new user to all repositories) V00.01.03
authorNicolas DURAND <nicolas.durand@iri.centrepompidou.fr>
Fri, 30 Jan 2015 12:38:15 +0100
changeset 40 8c32ea41b391
parent 39 67d2ddbebf7e
child 41 1fee953e2d9d
New version + Reworked login process to suppress unauthenticated request + reworked file persistence + added tests module and API tests + reworked how the list repositories user can access is generated (can now only access repositories that are both in his repository list AND in the config repository list, so there is no need to add every new user to all repositories)
setup.py
src/catedit/__init__.py
src/catedit/catedit_tests.py
src/catedit/config.py.tmpl
src/catedit/models.py
src/catedit/persistence.py
src/catedit/resources.py
src/catedit/settings.py
src/catedit/templates/catbase.html
src/catedit/templates/cateditor.html
src/catedit/templates/catindex.html
src/catedit/templates/catlogin.html
src/catedit/templates/catmodifs.html
src/catedit/templates/catrecap.html
src/catedit/version.py
src/catedit/views.py
--- a/setup.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/setup.py	Fri Jan 30 12:38:15 2015 +0100
@@ -2,7 +2,7 @@
 
 # Will set CURRENT_VERSION to the current version string and VERSION to the
 # current version tuple
-exec(compile(open("src/catedit/version.py"), "version.py", "exec"))
+exec(compile(open("src/catedit/version.py").read(), "version.py", "exec"))
 
 setup(
     name='catedit',
--- a/src/catedit/__init__.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/__init__.py	Fri Jan 30 12:38:15 2015 +0100
@@ -38,10 +38,14 @@
 CsrfProtect(app)
 
 # Github
-app.config["GITHUB_CLIENT_ID"] = app.config["PERSISTENCE_CONFIG"] \
-                                           ["GITHUB_CLIENT_ID"]
-app.config["GITHUB_CLIENT_SECRET"] = app.config["PERSISTENCE_CONFIG"] \
-                                               ["GITHUB_CLIENT_SECRET"]
+app.config["GITHUB_CLIENT_ID"] = app.config["PERSISTENCE_CONFIG"].get(
+    "GITHUB_CLIENT_ID",
+    "local_persistence"
+)
+app.config["GITHUB_CLIENT_SECRET"] = app.config["PERSISTENCE_CONFIG"].get(
+    "GITHUB_CLIENT_SECRET",
+    "local_persistence"
+)
 github = GitHub(app)
 
 # Api
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/catedit_tests.py	Fri Jan 30 12:38:15 2015 +0100
@@ -0,0 +1,265 @@
+import catedit
+import unittest
+from catedit.models import Category
+from rdflib import Graph
+from io import StringIO
+from flask import session
+
+class CateditTestCase(unittest.TestCase):
+
+    def setUp(self):
+        catedit.app.config['TESTING'] = True
+        catedit.app.config.from_object(catedit.settings.AppSettings)
+        catedit.app.config['PERSISTENCE_CONFIG'] = {
+            "METHOD": "PersistenceToFile",
+            "FILE_SAVE_DIRECTORY": "../../files/",
+            "REPOSITORY_LIST": ["local"]
+        }
+        self.app = catedit.app.test_client()
+        self.category_api = catedit.resources.CategoryAPI()
+        self.category_changes_api = catedit.resources.CategoryChangesAPI()
+
+    def tearDown(self):
+        pass
+
+    def test_1_1_model_create_category(self):
+        pass
+
+    def test_1_2_model_access_data(self):
+        pass
+
+    def test_1_3_model_editing_category(self):
+        pass
+
+    def test_1_4_model_save_load(self):
+        pass
+
+    def test_2_1_api_empty(self):
+        with catedit.app.test_request_context():
+            session["modified_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+            session["deleted_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+            print("Testing empty category list")
+            assert self.category_api.get(repository="local") == ([], 200)
+            print("Testing empty category changes list")
+            assert self.category_changes_api.get(repository="local") == (
+                {
+                    "modified_categories": {},
+                    "deleted_categories": {}
+                },
+                200
+            )
+            print("Testing getting dummy category id")
+            assert self.category_api.get(
+                repository="local",
+                cat_id="nil"
+            ) == 404
+            print("Testing getting dummy category change id")
+            assert self.category_changes_api.get(
+                repository="local",
+                modified_cat_id="nil"
+            ) == ({
+                "type": "untouched",
+                "category": "nil"
+            }, 200)
+
+    def test_2_2_api_creation(self):
+        with catedit.app.test_request_context():
+            session["modified_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+            session["deleted_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+
+            print("Testing posting new category in changelist")
+            cat_data = {
+                "label": "new_label",
+                "description": "new_description",
+                "properties": [],
+            }
+            (cat_id, cat_serial, code) = self.category_api.post(
+                repository="local",
+                cat_data=cat_data
+            )
+            cat_graph = Graph()
+            cat_graph.parse(source=StringIO(cat_serial), format='turtle')
+            cat_object = Category(graph=cat_graph)
+            assert cat_object.label == "new_label"
+            assert cat_object.description == "new_description"
+            assert cat_object.properties == []
+            assert code == 201
+            print("Checking if new category can be found in changelist")
+            assert self.category_changes_api.get(
+                repository="local",
+                modified_cat_id=cat_id
+            ) != 404
+
+    def test_2_3_api_edit_changes(self):
+        with catedit.app.test_request_context():
+            session["modified_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+            session["deleted_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+
+            print("Creating new category changes that will be edited")
+            cat_data = {
+                "label": "new_label",
+                "description": "new_description",
+                "properties": [],
+            }
+            (cat_id, cat_serial, code) = self.category_api.post(
+                repository="local",
+                cat_data=cat_data
+            )
+            assert code == 201
+
+            print("Testing editing new category")
+            edited_cat_data = {
+                "label": "new_label_edited",
+                "description": "new_description_edited",
+                "properties": [("comment","edited_category")],
+            }
+            code = self.category_api.put(
+                repository="local",
+                cat_id=cat_id,
+                cat_data=edited_cat_data
+            )
+            assert code == 204
+
+            (cat_changes, code) = self.category_changes_api.get(
+                repository="local",
+                modified_cat_id=cat_id
+            )
+
+            cat_graph = Graph()
+            cat_graph.parse(
+                source=StringIO(cat_changes["category"][cat_id]),
+                format='turtle'
+            )
+            cat_object = Category(graph=cat_graph)
+            assert cat_object.label == "new_label_edited"
+            assert cat_object.description == "new_description_edited"
+            assert cat_object.properties == [("comment","edited_category")]
+
+    def test_2_4_api_delete_changes(self):
+        with catedit.app.test_request_context():
+            session["modified_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+            session["deleted_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+
+            print("Creating new category changes that will be deleted")
+            cat_data = {
+                "label": "new_label",
+                "description": "new_description",
+                "properties": [],
+            }
+            (cat_id, cat_serial, code) = self.category_api.post(
+                repository="local",
+                cat_data=cat_data
+            )
+            assert session["modified_categories"]["local"] != {}
+            assert code == 201
+
+            print("Deleting changes for created category")
+            code = self.category_changes_api.delete(
+                repository="local",
+                modified_cat_id=cat_id
+            )
+            assert code == 204
+            assert session["modified_categories"]["local"] == {}
+            assert session["deleted_categories"]["local"] == {}
+
+    def test_2_5_api_submit_edit_delete_category(self):
+        with catedit.app.test_request_context():
+            session["modified_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+            session["deleted_categories"] = {
+                repo: {} for repo in catedit.app.config["PERSISTENCE_CONFIG"]
+                                                       ["REPOSITORY_LIST"]
+            }
+
+            print("Creating new categories")
+            cat_data_1 = {
+                "label": "new_label_1",
+                "description": "new_description_1",
+                "properties": [],
+            }
+            (cat_id_1, cat_serial_1, code) = self.category_api.post(
+                repository="local",
+                cat_data=cat_data_1
+            )
+            assert code == 201
+
+            cat_data_2 = {
+                "label": "new_label_2",
+                "description": "new_description_2",
+                "properties": [],
+            }
+            (cat_id_2, cat_serial_2, code) = self.category_api.post(
+                repository="local",
+                cat_data=cat_data_2
+            )
+            assert code == 201
+
+            print("Submitting changes")
+            code = self.category_api.put(
+                repository="local"
+            )
+            assert code == 204
+
+            print("Deleting categories")
+            (categories, code) = self.category_api.get(
+                repository="local"
+            )
+            for category_content in categories:
+                cat_graph = Graph()
+                cat_graph.parse(
+                    source=StringIO(category_content),
+                    format='turtle'
+                )
+                cat_object = Category(graph=cat_graph)
+
+                assert cat_object.label == "new_label_1" or \
+                       cat_object.label == "new_label_2"
+                assert cat_object.description == "new_description_1" or \
+                       cat_object.description == "new_description_2"
+                code = self.category_api.delete(
+                    repository="local",
+                    deleted_cat_id=cat_object.cat_id
+                )
+                assert code == 204
+            assert session["deleted_categories"] != {}
+
+            print("Submitting deletions")
+            code = self.category_api.put(
+                repository="local"
+            )
+            assert code == 204
+            assert session["modified_categories"]["local"] == {}
+            assert session["deleted_categories"]["local"] == {}
+
+    def test_3_views(self):
+        print("Starting views.py tests...")
+        print("Completed views.py tests!")
+
+if __name__ == '__main__':
+    unittest.main()
--- a/src/catedit/config.py.tmpl	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/config.py.tmpl	Fri Jan 30 12:38:15 2015 +0100
@@ -38,7 +38,8 @@
         You then need additional parameters related to the chosen METHOD.
 
         If "PersistenceToFile" (Note: currently not supported)
-        * FILE_SAVE_PATH is the path to save the category files
+        * FILE_SAVE_DIRECTORY is the path to save the category files
+        * REPOSITORY_LIST should be set to ["local"]
 
         If "PersistenceToGithub"
         * REPOSITORY_LIST is the list of repository available for users (Note:
--- a/src/catedit/models.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/models.py	Fri Jan 30 12:38:15 2015 +0100
@@ -35,7 +35,7 @@
                  other_properties=None, graph=None):
         if not graph:
             # cat_id = .hex - Alternate method of generating ids
-            cat_id = str(uuid4())[:8]+"_"+slugify(label)
+            cat_id = slugify(label)+"_"+str(uuid4())[:8]
             self.cat_graph = Graph()
             self.this_category = URIRef(app.config["CATEGORY_NAMESPACE"] +
                                         cat_id)
@@ -212,9 +212,11 @@
             Loads a category from its id
         """
         cat_serial = self.persistence.load(name=cat_id)
-        loaded_cat_graph = Graph()
-        loaded_cat_graph.parse(source=StringIO(cat_serial), format='turtle')
-        cat = Category(graph=loaded_cat_graph)
+        cat = None
+        if cat_serial != "":
+            loaded_cat_graph = Graph()
+            loaded_cat_graph.parse(source=StringIO(cat_serial), format='turtle')
+            cat = Category(graph=loaded_cat_graph)
         return cat
 
     def save_changes(self,
--- a/src/catedit/persistence.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/persistence.py	Fri Jan 30 12:38:15 2015 +0100
@@ -69,22 +69,41 @@
         * name : name of the file to write in/read from
         * content : desired content of the file when writing
     """
+    def __init__(self, **kwargs):
+        pass
+
     @property
     def session_compliant(self):
         """
             Not session compliant: each modification is submitted
         """
-        return False
+        return True
 
     def save(self, **kwargs):
         """
             Saves to a file
+
+            Expected args:
+            * deletion_dict
+            * modification_dict
         """
-        path_to_save = app.config["PERSISTENCE_CONFIG"]["FILE_SAVE_DIRECTORY"] \
-                       + kwargs["name"]
-        file_to_save = open(path_to_save, 'wb')
-        file_to_save.write(kwargs["content"])
-        file_to_save.close()
+        modification_dict = kwargs["modification_dict"]
+        deletion_dict = kwargs["deletion_dict"]
+
+        for (file_name, file_content) in modification_dict.items():
+            path_to_save = app.config["PERSISTENCE_CONFIG"] \
+                                     ["FILE_SAVE_DIRECTORY"] \
+                           + file_name
+            print(path_to_save)
+            file_to_save = open(path_to_save, 'wb')
+            file_to_save.write(bytes(file_content, "utf-8"))
+            file_to_save.close()
+
+        for file_name in deletion_dict.keys():
+            path_to_delete = app.config["PERSISTENCE_CONFIG"] \
+                                       ["FILE_SAVE_DIRECTORY"] \
+                             + file_name
+            os.remove(path_to_delete)
 
     def load(self, **kwargs):
         """
@@ -92,9 +111,13 @@
         """
         path_to_load = app.config["PERSISTENCE_CONFIG"]["FILE_SAVE_DIRECTORY"] \
                        + kwargs["name"]
-        file_to_load = open(path_to_load, 'rb')
-        file_content = file_to_load.read()
-        file_to_load.close()
+        try:
+            file_to_load = open(path_to_load, 'rb')
+            file_content = str(file_to_load.read(), "utf-8")
+            print(file_content)
+            file_to_load.close()
+        except FileNotFoundError:
+            file_content=""
         return file_content
 
     def delete(self, **kwargs):
@@ -115,10 +138,11 @@
                                               ["FILE_SAVE_DIRECTORY"]):
             if not file_name or file_name[0] == ".":
                 continue
-            path_to_load = open(app.config["PERSISTENCE_CONFIG"]
-                                          ["FILE_SAVE_DIRECTORY"] + file_name)
-            file_content = path_to_load.read()
-            path_to_load.close()
+            path_to_load = app.config["PERSISTENCE_CONFIG"] \
+                                     ["FILE_SAVE_DIRECTORY"] + file_name
+            file_to_load = open(path_to_load, 'rb')
+            file_content = str(file_to_load.read(), "utf-8")
+            file_to_load.close()
             file_content_list.append(file_content)
         # logger.debug(file_content_list)
         return file_content_list
@@ -200,16 +224,16 @@
             )
             logger.debug(str(ref_master))
         except GitHubError as ghe:
-            logger.debug("GitHubError trying to get the reference "
+            logger.error("GitHubError trying to get the reference "
                          + "to the master branch")
-            logger.debug(
+            logger.error(
                 "Endpoint: "
                 + "repos/"
                 + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
                 + self.repository
                 + "/git/refs/heads/master"
             )
-            logger.debug(ghe.response.text)
+            logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
 
         # point 2
@@ -223,9 +247,9 @@
             )
             logger.debug(str(last_commit_master))
         except GitHubError as ghe:
-            logger.debug("GitHubError trying to get the commit associated "
+            logger.error("GitHubError trying to get the commit associated "
                          + "to the latest reference to the master branch")
-            logger.debug(ghe.response.text)
+            logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
 
         # Point 3
@@ -240,10 +264,10 @@
             )
             logger.debug(str(last_commit_tree))
         except GitHubError as ghe:
-            logger.debug("GitHubError trying to get the tree from the commit "
+            logger.error("GitHubError trying to get the tree from the commit "
                          + "associated to the latest reference to the master "
                          + "branch")
-            logger.debug(ghe.response.text)
+            logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
 
         # Point 4
@@ -300,12 +324,12 @@
                                     blob_sha = new_blob["sha"]
                                     break
                                 except GitHubError as ghe:
-                                    logger.debug(
+                                    logger.error(
                                         "GitHubError trying to post a new"
                                         + "blob with following data: "
                                         + str(new_blob_data)
                                     )
-                                    logger.debug(ghe.response.text)
+                                    logger.error(ghe.response.text)
                                 logger.debug(str(
                                     github.get("rate_limit")["resources"]
                                 ))
@@ -344,12 +368,12 @@
                         data=new_blob_data
                     )
                 except GitHubError as ghe:
-                    logger.debug(
+                    logger.error(
                         "GitHubError trying to post a new blob with following"
                         + "data: "
                         + str(new_blob_data)
                     )
-                    logger.debug(ghe.response.text)
+                    logger.error(ghe.response.text)
                 logger.debug(str(github.get("rate_limit")["resources"]))
                 new_tree_data["tree"].append({
                     "path": app.config["PERSISTENCE_CONFIG"]
@@ -370,11 +394,11 @@
                 data=new_tree_data
             )
         except GitHubError as ghe:
-            logger.debug(
+            logger.error(
                 "GitHubError trying to post a new tree with following data: "
                 + str(new_tree_data)
             )
-            logger.debug(ghe.response.text)
+            logger.error(ghe.response.text)
 
         # Point 5
         new_commit_data = {"message": kwargs["message"],
@@ -391,11 +415,11 @@
             )
             logger.debug(str(new_commit))
         except GitHubError as ghe:
-            logger.debug(
+            logger.error(
                 "GitHubError trying to post a new commit with following data: "
                 + str(new_commit_data)
             )
-            logger.debug(ghe.response.text)
+            logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
 
         # Point 6
@@ -411,12 +435,12 @@
             )
             logger.debug(str(new_head))
         except GitHubError as ghe:
-            logger.debug(
+            logger.error(
                 "GitHubError trying to edit the head to the master branch"
                 + "with the following data: "
                 + str(new_head_data)
             )
-            logger.debug(ghe.response.text)
+            logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
 
 
@@ -424,6 +448,7 @@
         """
             Loads from a Github repository
         """
+        file_content=""
         try:
             filedict = github.get("repos/"
                                   + app.config["PERSISTENCE_CONFIG"]
@@ -435,10 +460,11 @@
                                   + kwargs["name"])
             file_content = str(b64decode(filedict["content"]), "utf-8")
         except GitHubError as ghe:
-            logger.debug("Github Error trying to get file: "+kwargs["name"])
-            logger.debug("Github sent an error, if 404, either you may not "
-                         + "have access to the repository or it doesn't exist ")
-            logger.debug(ghe.response.text)
+            logger.error("Github Error trying to get file: "+kwargs["name"])
+            logger.error("Github sent an error, if 404, either you may not "
+                         + "have access to the repository or the file doesn't "
+                         + "exist ")
+            logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
         return file_content
 
@@ -449,7 +475,6 @@
             Expected kwargs are:
             * name : the name of the file to delete
             * message : the commit message for the deletion
-
         """
         pass
 
@@ -470,12 +495,11 @@
                               for github_file in files_in_repo]
             # logger.debug(filenames_list)
         except GitHubError as ghe:
-            logger.debug("Github Error trying to get the file list in the "
+            logger.error("Github Error trying to get the file list in the "
                          + "category repository")
-            logger.debug("NOTE: Github sent an error, if 404 either you "
-                         + "may not have access to the repository or it "
-                         + "doesn't exist or there isn't any files in it")
-            logger.debug(ghe.response.text)
+            logger.error("IMPORTANT: This message can mean there is no "
+                         + "category in the repository " + self.repository)
+            logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
 
         file_content_list = []
@@ -492,8 +516,8 @@
                 file_content_list.append(str(b64decode(filedict["content"]),
                                          "utf-8"))
             except GitHubError as ghe:
-                logger.debug("Github Error trying to get file: "+filename)
-                logger.debug(ghe.response.text)
+                logger.error("Github Error trying to get file: "+filename)
+                logger.error(ghe.response.text)
         logger.debug(str(github.get("rate_limit")["resources"]))
         # logger.debug(file_content_list)
         return file_content_list
--- a/src/catedit/resources.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/resources.py	Fri Jan 30 12:38:15 2015 +0100
@@ -44,13 +44,19 @@
         )
         if cat_id is not None:
             cat = cat_manager_instance.load_category(cat_id)
-            return cat.cat_graph.serialize(format='turtle').decode("utf-8")
+            if cat != None:
+                return cat.cat_graph \
+                          .serialize(format='turtle') \
+                          .decode("utf-8"), \
+                       200
+            else:
+                return 404
         else:
             response = []
             for cat in cat_manager_instance.list_categories():
                 response.append(cat.cat_graph.serialize(format='turtle')
                                              .decode("utf-8"))
-            return response
+            return response, 200
 
     # update category cat_id
     def put(self, repository, cat_id=None, cat_data=None):
@@ -93,6 +99,10 @@
                                              .get(repository, {}),
                     message=args["commit_message"]
                 )
+                session["deleted_categories"]["local"] = {}
+                session["modified_categories"]["local"] = {}
+                cache.clear()
+                return 204
         else:
             # is the edition occuring on an already modified category?
             if cat_id in session.get("modified_categories", {}) \
@@ -123,9 +133,7 @@
                 in session["deleted_categories"][repository].items()
                 if cat_name != cat.cat_id
             }
-
             logger.debug("put id: "+cat.cat_id)
-        cache.clear()
         return 204
 
     def post(self, repository, cat_data):
@@ -154,8 +162,9 @@
             )
 
         logger.debug("post id: "+cat.cat_id)
-        cache.clear()
-        return cat.cat_graph.serialize(format='turtle').decode("utf-8"), 201
+        return cat.cat_id, \
+               cat.cat_graph.serialize(format='turtle').decode("utf-8"), \
+               201
 
     def delete(self, repository, deleted_cat_id):
         """
@@ -283,7 +292,6 @@
                             )
 
         logger.debug("delete id: " + deleted_cat_id)
-        cache.clear()
         return 204
 
 
@@ -302,16 +310,14 @@
         """
             API to get the pending changes for category cat_id
         """
-        logger.debug(modified_cat_id)
-        logger.debug(str(session.get("modified_categories", {})))
-        
+
         if modified_cat_id is None:
             return {
                 "modified_categories": session.get("modified_categories", {})
                                               .get(repository, {}),
                 "deleted_categories": session.get("deleted_categories", {})
                                              .get(repository, {})
-            }, 201
+            }, 200
         else:
             if modified_cat_id in session.get("modified_categories", {}) \
                                          .get(repository, {}):
@@ -322,7 +328,7 @@
                                                 [repository]
                                                 [modified_cat_id]
                     }
-                }, 201
+                }, 200
             if modified_cat_id in session.get("deleted_categories", {}) \
                                          .get(repository, {}):
                 return {
@@ -332,11 +338,11 @@
                                                 [repository]
                                                 [modified_cat_id]
                     }
-                }, 201
+                }, 200
             return {
                 "type": "untouched",
                 "category": modified_cat_id
-            }, 201
+            }, 200
 
     def delete(self, repository, modified_cat_id=None):
         """
--- a/src/catedit/settings.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/settings.py	Fri Jan 30 12:38:15 2015 +0100
@@ -9,7 +9,7 @@
 
     WTF_CSRF_ENABLED = True
 
-    # RDF Namespace and prefixes
+    # RDF Namespace and prefixes - Must end with #
 
     ONTOLOGY_NAMESPACE = "http://ld.iri-research.org/ontology/categorisation#"
     CATEGORY_NAMESPACE = "http://ld.iri-research.org/ontology/categorisation/category#"
--- a/src/catedit/templates/catbase.html	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/templates/catbase.html	Fri Jan 30 12:38:15 2015 +0100
@@ -38,7 +38,7 @@
           <form class="navbar-form navbar-right">
             <select class="form-control select-repo" name="navrepo" onchange="window.location.href=this.form.navrepo.options[this.form.navrepo.selectedIndex].value">
               {% block repo_list %}
-                {% for repo in config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"] %}
+                {% for repo in session.get("user_repositories", []) %}
                   <option value="{{url_for('cat_recap', repository=repo)}}" {% if repo==current_repository %}selected="selected"{% endif %}>{{repo}}</option>
                 {% endfor %}
               {% endblock repo_list %}
--- a/src/catedit/templates/cateditor.html	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/templates/cateditor.html	Fri Jan 30 12:38:15 2015 +0100
@@ -1,5 +1,5 @@
 {% extends "catbase.html" %}
-{% if not session["user_logged"] or not session["user_can_edit"] %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
   {% set readonly="readonly" %}
 {% else %}
   {% set readonly=False %}
@@ -22,7 +22,7 @@
 {% endblock repo_list %}
 {% block page_content %}
 <h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
-{% if session["user_logged"] and not session["user_can_edit"] %}
+{% if session["user_logged"] and not session["user_can_edit"][current_repository] %}
 <div class="alert alert-warning" role="alert">
   <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
   <span class="sr-only">Attention:</span>
@@ -89,10 +89,8 @@
                 <tr id="properties-{{property_count}}">
                   <input id="properties-{{property_count}}-csrf_token" name="properties-{{property_count}}-csrf_token" value="{{ csrf_token() }}" type="hidden">
                   {% if config["PROPERTY_LIST"][property.property_predicate.data]["object_type"]=="uriref-category" %}
-                    {{property.property_object.data}} vs:
                     {% for cat in cat_list %}
-                      {{config["CATEGORY_NAMESPACE"]+cat.cat_id}}, 
-                      {% if property.property_object.data == config["CATEGORY_NAMESPACE"]+cat.cat_id %}
+                      {% if property.property_object.data == cat.cat_id %}
                         {% if cat.cat_id not in deleted_cat_list %}
                           {{ property.property_predicate() }}
                           {{ property.property_object() }}
--- a/src/catedit/templates/catindex.html	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/templates/catindex.html	Fri Jan 30 12:38:15 2015 +0100
@@ -1,5 +1,5 @@
 {% extends "catbase.html" %}
-{% if not session["user_logged"] or not session["user_can_edit"] %}
+{% if not session["user_logged"] %}
   {% set readonly="readonly" %}
 {% else %}
   {% set readonly=False %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/catlogin.html	Fri Jan 30 12:38:15 2015 +0100
@@ -0,0 +1,65 @@
+{% extends "catbase.html" %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
+  {% set readonly="readonly" %}
+{% else %}
+  {% set readonly=False %}
+{% endif %}
+{% block title %} Catedit: Login {% endblock title %}
+{% block head %}
+  {{ super() }}
+{% endblock head %}
+{% block navbar_items %}
+  {{ super() }}
+{% endblock navbar_items%}
+{% block repo_list %}
+  {{ super() }}
+{% endblock repo_list %}
+{% block page_content %}
+<h2> <b>CatEdit</b> - <small>Authentification</small></h2>
+{% if form.user_login.errors or form.user_password.errors %}
+<div class="alert alert-danger">
+  <strong>
+    <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+    Erreur:
+  </strong>
+  Vous n'avez pas rempli certains champs obligatoires.
+</div>
+{% endif %}
+<div class="col-md-8">
+  <p>
+    <br>
+    Veuillez entrez votre nom d'utilisateur Github et mot de passe Github.
+  </p>
+  <form method="POST" action="{{url_for('github_login')}}" id="login_form" role="form">
+    <div class="form-group">
+      {% if form.user_login.errors %}
+        {% set login_placeholder="Champ obligatoire" %}
+      {% endif %}
+      {{ form.hidden_tag() }}
+      {{form.user_login.label}}
+      {{form.user_login(class="form-control", id="user_login", placeholder=login_placeholder)}}
+      {{form.user_password.label}}
+      {{form.user_password(class="form-control", id="user_password")}}
+    </div>
+    <button type="submit" class="btn btn-default">Me connecter à CatEdit</button>
+  </form>
+{% if form.user_login.data and not(form.user_login.errors or form.user_password.errors) %}
+  <br>
+  <div class="col-md-8 alert alert-info">
+    <p>
+      Il semble que vous utilisez CatEdit pour la première fois. Veuillez cliquer
+      sur le lien suivant pour vous authentifier sur Github afin de pouvoir utiliser CatEdit.
+    </p>
+    <p>
+      Si ça n'est pas la première fois que vous utilisez CatEdit, vérifiez que vous n'avez pas entré
+      un mauvais nom d'utilisateur/mot de passe. Note: Si vous souhaitez changer d'utilisateur,
+      n'oubliez pas auparavant de vous déconnecter de l'utilisateur courant sur <a href="http://github.com">Github</a>.<br><br>
+    </p>
+    <form method="POST" action="{{url_for('github_login_confirm')}}" id="confirm_form" role="form">
+      <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
+      <button type="submit" class="btn btn-default">M'authentifier sur Github</button>
+    </form>
+  </div>
+{% endif %}
+</div>
+{% endblock page_content%}
--- a/src/catedit/templates/catmodifs.html	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/templates/catmodifs.html	Fri Jan 30 12:38:15 2015 +0100
@@ -1,5 +1,5 @@
 {% extends "catbase.html" %}
-{% if not session["user_logged"] or not session["user_can_edit"] %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
   {% set readonly="readonly" %}
 {% else %}
   {% set readonly=False %}
--- a/src/catedit/templates/catrecap.html	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/templates/catrecap.html	Fri Jan 30 12:38:15 2015 +0100
@@ -1,10 +1,5 @@
 {% extends "catbase.html" %}
-{% if not session["user_logged"] or not session["user_can_edit"] %}
-  {% set readonly="readonly" %}
-{% else %}
-  {% set readonly=False %}
-{% endif %}
-{% if not session["user_logged"] or not session["user_can_edit"] %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
   {% set readonly="readonly" %}
 {% else %}
   {% set readonly=False %}
@@ -85,7 +80,7 @@
             </td>
             {% if (cat.state != "deleted") %}
             <td class="col-md-1 text-center">
-              <a href="{{ url_for('cat_editor', cat_id=cat.cat_id, repository=current_repository)}}" title="Editer catégorie" class="btn btn-default">
+              <a href="{{ url_for('cat_editor', cat_id=cat.cat_id, repository=current_repository)}}" title="Editer catégorie" class="btn btn-default {% if readonly %}disabled{% endif %}">
                 <span class="glyphicon glyphicon glyphicon-pencil"/>
               </a>
             </td>
@@ -95,7 +90,7 @@
               <form method="POST" action="{{url_for('cat_recap', deleted_cat_id=cat.cat_id, repository=current_repository)}}" class="form-inline">
                 <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
                 <a title="Supprimer catégorie">
-                  <button class="btn btn-default" type="submit"><span class="glyphicon glyphicon-trash"/></button>
+                  <button class="btn btn-default {% if readonly %}disabled{% endif %}" type="submit"><span class="glyphicon glyphicon-trash"/></button>
                 </a>
               </form>
             </td>
--- a/src/catedit/version.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/version.py	Fri Jan 30 12:38:15 2015 +0100
@@ -6,7 +6,7 @@
 
 __all__ = ["VERSION", "get_version", "CURRENT_VERSION"]
 
-VERSION = (0, 1, 2)
+VERSION = (0, 1, 3)
 
 def get_version():
     """
--- a/src/catedit/views.py	Tue Jan 13 10:43:26 2015 +0100
+++ b/src/catedit/views.py	Fri Jan 30 12:38:15 2015 +0100
@@ -3,60 +3,23 @@
 The views functions that handle the front-end of the application
 """
 
-from catedit import app, github
+from catedit import app, github, cache
 from catedit.models import Category
+from requests import get
+from requests.auth import HTTPBasicAuth
 import catedit.persistence
 from flask import render_template, request, redirect, url_for, session, abort
 from flask.ext.github import GitHubError
 from flask_wtf import Form
 from catedit.resources import CategoryAPI, CategoryChangesAPI
-from wtforms import StringField, TextAreaField, FormField, FieldList, HiddenField
+from wtforms import StringField, TextAreaField, FormField, PasswordField, \
+                    FieldList, HiddenField
 from wtforms.validators import DataRequired, Optional, AnyOf
 from rdflib import Graph
 from io import StringIO
 
 logger = app.logger
 
-
-class PropertyForm(Form):
-    """
-        Form of a given property, each one is a couple of hidden fields that
-        can be Javascript-generated in the template
-    """
-    property_predicate = HiddenField(
-        validators=[AnyOf(values=app.config["PROPERTY_LIST"].keys())]
-    )
-    property_object = HiddenField(
-        validators=[DataRequired()]
-    )
-
-
-class CategoryForm(Form):
-    """
-        Custom form class for creating a category with the absolute minimal
-        attributes (label and description)
-    """
-    label = StringField(
-        "Nom de la categorie (obligatoire)",
-        validators=[DataRequired()]
-    )
-    description = TextAreaField(
-        "Description de la categorie (obligatoire)",
-        validators=[DataRequired()]
-    )
-    properties = FieldList(FormField(PropertyForm), validators=[Optional()])
-
-
-class CommitForm(Form):
-    """
-        Custom form class for commiting changes
-    """
-    commit_message = StringField(
-        "Message de soumission (obligatoire)",
-        validators=[DataRequired()]
-    )
-
-
 @app.route('/', methods=['GET'])
 @app.route('/index', methods=['GET'])
 def cat_index():
@@ -118,10 +81,12 @@
         modified_cat_dict = {}
         serialized_cat_list = []
         if session.get("user_logged", None) is not None:
-            serialized_cat_list = cat_api_instance.get(repository=repository)
-            cat_changes = cat_changes_api_instance.get(repository=repository)
-            modified_cat_dict = cat_changes[0]["modified_categories"]
-            deleted_cat_dict = cat_changes[0]["deleted_categories"]
+            serialized_cat_list = cat_api_instance.get(repository=repository) \
+                                                      [0]
+            cat_changes = cat_changes_api_instance.get(repository=repository) \
+                                                      [0]
+            modified_cat_dict = cat_changes["modified_categories"]
+            deleted_cat_dict = cat_changes["deleted_categories"]
         # logger.debug(serialized_cat_list)
         cat_list = []
         original_cat_list = []
@@ -159,9 +124,9 @@
         # now we must find the not yet submitted categories that were created
         cat_state = ""
         logger.debug("Edited cat list: "
-                     + str(edited_cat_list)
+                     + str([cat.label for cat in edited_cat_list])
                      + " - Original cat list: "
-                     + str(original_cat_list))
+                     + str([cat.label for cat in original_cat_list]))
         for category in edited_cat_list:
             if category.cat_id not in [cat.cat_id for
                                        cat in original_cat_list]:
@@ -180,6 +145,15 @@
                                cat_list=cat_list,
                                current_repository=repository)
 
+class CommitForm(Form):
+    """
+        Custom form class for commiting changes
+    """
+    commit_message = StringField(
+        "Message de soumission (obligatoire)",
+        validators=[DataRequired()]
+    )
+
 
 @app.route('/<string:repository>/catmodifs/delete-modifs-<deleted_modifs_id>',
            defaults={'deleted_cat_id': None},
@@ -224,7 +198,7 @@
             if session.get("user_logged", None) is not None:
                 serialized_cat_list = cat_api_instance.get(
                     repository=repository
-                )
+                )[0]
                 changes_list = cat_changes_api_instance.get(
                     repository=repository
                 )[0]
@@ -330,6 +304,36 @@
         return redirect(url_for('cat_modifs', repository=repository))
 
 
+
+class PropertyForm(Form):
+    """
+        Form of a given property, each one is a couple of hidden fields that
+        can be Javascript-generated in the template
+    """
+    property_predicate = HiddenField(
+        validators=[AnyOf(values=app.config["PROPERTY_LIST"].keys())]
+    )
+    property_object = HiddenField(
+        validators=[DataRequired()]
+    )
+
+
+class CategoryForm(Form):
+    """
+        Custom form class for creating a category with the absolute minimal
+        attributes (label and description)
+    """
+    label = StringField(
+        "Nom de la categorie (obligatoire)",
+        validators=[DataRequired()]
+    )
+    description = TextAreaField(
+        "Description de la categorie (obligatoire)",
+        validators=[DataRequired()]
+    )
+    properties = FieldList(FormField(PropertyForm), validators=[Optional()])
+
+
 @app.route('/<string:repository>/cateditor',
            methods=['GET', 'POST'])
 @app.route('/<string:repository>/cateditor/<string:cat_id>',
@@ -346,7 +350,7 @@
     cat_changes_api_instance = CategoryChangesAPI()
 
     specific_serialized_cat = ""
-    # serialization of the category of id cat_id, either from
+    # Serialization of the category of id cat_id, either from
     # CategoryChangesAPI (if it was modified) or from CategoryAPI (if it
     # was not)
 
@@ -358,6 +362,17 @@
     current_cat_properties = []
     # Args for the template, if we create a new category they are not used
 
+    cat_list = []
+    # Category list that will be used by the template
+    deleted_cat_dict = {}
+    # Deleted categories we won't append to cat_list
+    modified_cat_dict = {}
+    # Modified categories to append to cat_list in case label changed
+    serialized_cat_list = []
+    # Existing categories we get from CategoryAPI
+    cat_changes = {}
+    # Changes list we get from CategoryChangesAPI
+
     if cat_id is not None:
         if session.get("user_logged", None) is not None:
             changes_response = cat_changes_api_instance.get(
@@ -374,7 +389,7 @@
                 specific_serialized_cat = cat_api_instance.get(
                     repository=repository,
                     cat_id=cat_id
-                )
+                )[0]
         logger.debug(specific_serialized_cat)
 
         cat_rdf_graph = Graph()
@@ -386,6 +401,36 @@
         current_cat_properties = current_cat.properties
         logger.debug(current_cat.properties)
 
+    serialized_cat_list = cat_api_instance.get(repository)[0]
+    cat_changes = cat_changes_api_instance.get(repository)[0]
+    deleted_cat_dict = cat_changes["deleted_categories"]
+    modified_cat_dict = cat_changes["modified_categories"]
+    logger.debug(changes_response)
+
+    for modified_cat_name in modified_cat_dict.keys():
+        modified_cat_rdf_graph = Graph()
+        modified_cat_rdf_graph.parse(
+            source=StringIO(modified_cat_dict[modified_cat_name]),
+            format='turtle'
+        )
+        modified_cat = Category(graph=modified_cat_rdf_graph)
+        cat_list.append({"cat_label": modified_cat.label,
+                         "cat_description": modified_cat.description,
+                         "cat_id": modified_cat.cat_id,
+                         "cat_properties": modified_cat.properties})
+
+    for serialized_cat in serialized_cat_list:
+        cat_rdf_graph = Graph()
+        cat_rdf_graph.parse(source=StringIO(serialized_cat),
+                            format='turtle')
+        cat = Category(graph=cat_rdf_graph)
+        if cat.cat_id not in deleted_cat_dict.keys() and \
+           cat.cat_id not in modified_cat_dict.keys():
+            cat_list.append({"cat_label": cat.label,
+                             "cat_description": cat.description,
+                             "cat_id": cat.cat_id,
+                             "cat_properties": cat.properties})
+
     cat_form = CategoryForm(request.form)
 
     if request.method == "GET":
@@ -396,66 +441,32 @@
             cat_form.label.data = current_cat.label
             cat_form.description.data = current_cat.description
             for (cat_predicate, cat_object) in current_cat_properties:
+                if app.config["PROPERTY_LIST"] \
+                             [cat_predicate] \
+                             ["object_type"] == "uriref-category":
+                    namespace, object_id = cat_object.split("#",1)
+                else:
+                    object_id = cat_object
                 cat_form.properties.append_entry({
                     "property_predicate": cat_predicate,
-                    "property_object": cat_object
+                    "property_object": object_id
                 })
-
-        # deleted categories we won't append to cat_list
-        deleted_cat_dict = {}
-        # modified categories to append to cat_list in case label changed
-        modified_cat_dict = {}
-        # existing categories we get from CategoryAPI
-        serialized_cat_list = []
-        # changes list we get from CategoryChangesAPI
-        cat_changes = {}
-
         # list of category that will be used in property editor, list of dict
         # {"cat_id": cat.cat_id,
         #  "cat_label": cat.label,
         #  "cat_description": cat.description,
         #  "cat_properties": cat.properties}
-        cat_list = []
-
-        if session.get("user_logged", None) is not None:
-            serialized_cat_list = cat_api_instance.get(repository)
-            cat_changes = cat_changes_api_instance.get(repository)[0]
-            deleted_cat_dict = cat_changes["deleted_categories"]
-            modified_cat_dict = cat_changes["modified_categories"]
-            logger.debug(changes_response)
-
-        for modified_cat_name in modified_cat_dict.keys():
-            modified_cat_rdf_graph = Graph()
-            modified_cat_rdf_graph.parse(
-                source=StringIO(modified_cat_dict[modified_cat_name]),
-                format='turtle'
-            )
-            modified_cat = Category(graph=modified_cat_rdf_graph)
-            cat_list.append({"cat_label": modified_cat.label,
-                             "cat_description": modified_cat.description,
-                             "cat_id": modified_cat.cat_id,
-                             "cat_properties": modified_cat.properties})
-
-        for serialized_cat in serialized_cat_list:
-            cat_rdf_graph = Graph()
-            cat_rdf_graph.parse(source=StringIO(serialized_cat),
-                                format='turtle')
-            cat = Category(graph=cat_rdf_graph)
-            if cat.cat_id not in deleted_cat_dict.keys() and \
-               cat.cat_id not in modified_cat_dict.keys():
-                cat_list.append({"cat_label": cat.label,
-                                 "cat_description": cat.description,
-                                 "cat_id": cat.cat_id,
-                                 "cat_properties": cat.properties})
-
-        deleted_cat_list = list(deleted_cat_dict.keys())
-
+        logger.debug("category list that can be linked :" + str(cat_list))
+        logger.debug(
+            "deleted categories list that can't be linked :"
+            + str(list(deleted_cat_dict.keys()))
+        )
         return render_template('cateditor.html',
                                cat_id=current_cat_id,
                                cat_properties=current_cat_properties,
                                form=cat_form,
                                cat_list=cat_list,
-                               deleted_cat_list=deleted_cat_list,
+                               deleted_cat_list=list(deleted_cat_dict.keys()),
                                current_repository=repository)
     elif request.method == "POST":
         """
@@ -466,8 +477,6 @@
         if (session.get("user_logged", None) is not None and
                 session.get("user_can_edit", False) is not False):
 
-            logger.debug(cat_form.errors)
-
             if cat_form.validate_on_submit():
                 cat_data = {}
                 cat_data["label"] = cat_form.label.data
@@ -477,7 +486,6 @@
                      cat_property["property_object"])
                     for cat_property in cat_form.properties.data
                 ]
-                logger.debug(cat_data)
 
                 if cat_id is not None:
                     logger.debug(str(cat_data))
@@ -498,10 +506,12 @@
                 # changes the user did
                 return render_template('cateditor.html',
                                        cat_id=cat_id,
-                                       cat_properties=cat.properties,
+                                       cat_properties=current_cat_properties,
                                        form=cat_form,
                                        cat_list=cat_list,
-                                       deleted_cat_list=deleted_cat_list,
+                                       deleted_cat_list=list(
+                                           deleted_cat_dict.keys()
+                                       ),
                                        current_repository=repository)
 
         # If user wasn't logged or couldn't edit but somehow submitted a POST
@@ -509,7 +519,21 @@
             return redirect(url_for('cat_index'))
 
 
-@app.route('/catedit-github-login')
+class LoginForm(Form):
+    """
+        Custom form class for commiting changes
+    """
+    user_login = StringField(
+        "Nom d'utilisateur Github",
+        validators=[DataRequired()]
+    )
+    user_password = PasswordField(
+        "Mot de passe Github",
+        validators=[DataRequired()]
+    )
+
+
+@app.route('/catedit-login', methods=["GET", "POST"])
 def github_login():
     """
         Function that manages authentication (Github), login
@@ -518,10 +542,7 @@
         in local files, used for debugging), creates a mock user named
         "FileEditUser"
     """
-    if getattr(catedit.persistence,
-               app.config["PERSISTENCE_CONFIG"]
-                         ["METHOD"])().session_compliant is True:
-        session["save_user_changes"] = True
+    if not session.get("user_logged", False):
         session["modified_categories"] = {
             repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
                                            ["REPOSITORY_LIST"]
@@ -530,18 +551,81 @@
             repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
                                            ["REPOSITORY_LIST"]
         }
-    if app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToGithub":
-        logger.debug(str(github.get("rate_limit")["resources"]))
-        return github.authorize(
-            scope="repo",
-            redirect_uri=url_for('github_callback', _external=True)
-        )
-    elif app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToFile":
-        session["user_logged"] = True
-        session["user_can_edit"] = True
-        session["user_login"] = "FileEditUser"
+        if app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToGithub":
+            login_form = LoginForm(request.form)
+            if request.method == "POST":
+                if login_form.validate_on_submit():
+                    # We'll try to get the auth token for given username
+                    attempted_login = login_form.user_login.data
+                    try:
+                        auth_response = get(
+                            "https://api.github.com/"
+                            + "authorizations",
+                            auth=HTTPBasicAuth(
+                                login_form.user_login.data,
+                                login_form.user_password.data
+                            )
+                        )
+                        for auth in auth_response.json():
+                            if auth["app"]["client_id"] \
+                            == app.config["GITHUB_CLIENT_ID"]:
+                                session["user_code"] = auth["token"]
+                                session["user_logged"] = True
+                    except:
+                        logger.debug(
+                            "Error requesting authorizations for"
+                            + " user. Either the user is new to catedit, or "
+                            + "entered a wrong username/password"
+                        )
+                    logger.debug(
+                        "user token found by request: "
+                        + str(session.get("user_code", None))
+                    )
+                    if session.get("user_code", None) == None:
+                        # We didn't get it, so we direct the user to the login page
+                        # with a link to github oauth system
+                        return render_template(
+                            'catlogin.html',
+                            form = login_form
+                        )
+                    else:
+                        # we did get it, so we redirect to callback function
+                        # to wrap up user auth
+                        return redirect(url_for('github_callback'))
+                else:
+                    # form didn't validate, so we send it back to user
+                    return render_template(
+                        'catlogin.html',
+                        form = login_form
+                    )
+            elif request.method == "GET":
+                # We'll render the login form
+                return render_template(
+                    'catlogin.html',
+                    form=login_form,
+                )
+        elif app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToFile":
+            session["user_logged"] = True
+            session["user_can_edit"] = {}
+            session["user_can_edit"]["local"] = True
+            session["user_login"] = "FileEditUser"
+            return redirect(url_for('cat_index'))
+    else:
         return redirect(url_for('cat_index'))
 
+@app.route('/catedit-login-confirm', methods=["GET", "POST"])
+def github_login_confirm():
+    """
+        Function called if the user is new or revoked the auth token
+    """
+    if not session.get("user_logged", False):
+        if request.method == "POST":
+            return github.authorize(
+                scope="repo",
+                redirect_uri=url_for('github_callback', _external=True)
+            )
+    else:
+        return redirect(url_for('cat_index'))
 
 @app.route('/catedit-github-callback')
 @github.authorized_handler
@@ -550,41 +634,51 @@
         Function that handles callback from Github after succesful login
     """
     session.permanent = False
-    session["user_code"] = oauth_code
+    if session.get("user_code", None) == None:
+    # That means we got here using github callback and not the login form
+        session["user_code"] = oauth_code
+    logger.debug(session["user_code"])
     session["user_logged"] = True
     session["user_login"] = "auth-error"
     try:
+        logger.debug(
+            "after login: "
+            + str(github.get("rate_limit")["resources"])
+        )
         session["user_login"] = github.get("user")["login"]
-        logger.debug(str(github.get("rate_limit")["resources"]))
     except GitHubError as ghe:
-        logger.debug(
+        logger.error(
             "GitHubError trying to get the user login"
         )
-        logger.debug(ghe.request.text)
+        logger.error(ghe.response.text)
     try:
         repo_list = []
         repo_list = github.get("user/repos")
         logger.debug(str(github.get("rate_limit")["resources"]))
         for repo in repo_list:
             logger.debug(repo["name"])
-        session["user_can_edit"] = True
         user_repos_name = [repo["name"] for repo in repo_list]
         logger.debug(
             str(user_repos_name) + " "
             + str(app.config["PERSISTENCE_CONFIG"]
                             ["REPOSITORY_LIST"])
         )
-        if not all((repo in user_repos_name)
-                   for repo in app.config["PERSISTENCE_CONFIG"]
-                                         ["REPOSITORY_LIST"]):
-            session["user_can_edit"] = False
+        session["user_repositories"] = list(
+            set(user_repos_name).intersection(
+                app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]
+            )
+        )
+        session["user_can_edit"] = {}
+        for repo in session["user_repositories"]:
+            if repo in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
+                session["user_can_edit"][repo] = True
         logger.debug(session["user_can_edit"])
     except GitHubError as ghe:
-        logger.debug(
+        logger.error(
             "GitHubError trying to get the list of repository for user "
             + session["user_login"]
         )
-        logger.debug(ghe.response.text)
+        logger.error(ghe.response.text)
     return redirect(url_for('cat_index'))
 
 
@@ -608,11 +702,10 @@
         Github, else when logging back in, github will send the app the
         same oauth code
     """
+    session["user_logged"] = None
     session["user_code"] = None
-    session["user_logged"] = None
     session["user_login"] = None
     session["user_can_edit"] = None
-    session["save_user_changes"] = None
     session["modified_categories"] = {
         repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
                                        ["REPOSITORY_LIST"]