V0.1.4: Implemented github social functions into CatEdit: refactored template and views hierarchy, names, views submodule, created macro to generate category tables + small esthetic changes (buttons to display categories/tables switch state, possibility to hide tables) + views modules have been converted into Flask Blueprints
--- a/src/catedit/__init__.py Fri Jan 30 12:41:40 2015 +0100
+++ b/src/catedit/__init__.py Fri Feb 13 17:22:21 2015 +0100
@@ -48,6 +48,16 @@
)
github = GitHub(app)
+@github.access_token_getter
+def token_getter():
+ """
+ Utility function for github-flask module to get user token when
+ making authenticated requests
+ """
+ if session.get("user_logged", None):
+ # logger.debug("I made an authentified request")
+ return session["user_code"]
+
# Api
api = Api(app)
@@ -56,10 +66,11 @@
app.config["CURRENT_VERSION"] = CURRENT_VERSION
# Views and APIs
-from catedit.views import cat_editor, cat_recap, github_login,\
- github_callback, logout
-
+from catedit.views import home, categories, social
from catedit.resources import CategoryAPI, CategoryChangesAPI
+app.register_blueprint(home.module)
+app.register_blueprint(categories.module)
+app.register_blueprint(social.module)
api.add_resource(CategoryAPI,
--- a/src/catedit/persistence.py Fri Jan 30 12:41:40 2015 +0100
+++ b/src/catedit/persistence.py Fri Feb 13 17:22:21 2015 +0100
@@ -450,14 +450,14 @@
"""
file_content=""
try:
- filedict = github.get("repos/"
- + app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_OWNER"] + "/"
- + self.repository
- + "/contents/"
- + app.config["PERSISTENCE_CONFIG"]
- ["CATEGORIES_PATH"]
- + kwargs["name"])
+ filedict = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + self.repository
+ + "/contents/"
+ + app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"]
+ + kwargs["name"]
+ )
file_content = str(b64decode(filedict["content"]), "utf-8")
except GitHubError as ghe:
logger.error("Github Error trying to get file: "+kwargs["name"])
@@ -484,13 +484,13 @@
"""
filenames_list = []
try:
- files_in_repo = github.get("repos/"
- + app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_OWNER"] + "/"
- + self.repository
- + "/contents/"
- + app.config["PERSISTENCE_CONFIG"]
- ["CATEGORIES_PATH"])
+ files_in_repo = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + self.repository
+ + "/contents/"
+ + app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"] + "?per_page=100"
+ )
filenames_list = [github_file["name"]
for github_file in files_in_repo]
# logger.debug(filenames_list)
@@ -505,14 +505,14 @@
file_content_list = []
for filename in filenames_list:
try:
- filedict = github.get("repos/"
- + app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_OWNER"] + "/"
- + self.repository
- + "/contents/"
- + app.config["PERSISTENCE_CONFIG"]
- ["CATEGORIES_PATH"]
- + filename)
+ filedict = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + self.repository
+ + "/contents/"
+ + app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"]
+ + filename
+ )
file_content_list.append(str(b64decode(filedict["content"]),
"utf-8"))
except GitHubError as ghe:
--- a/src/catedit/resources.py Fri Jan 30 12:41:40 2015 +0100
+++ b/src/catedit/resources.py Fri Feb 13 17:22:21 2015 +0100
@@ -45,17 +45,17 @@
if cat_id is not None:
cat = cat_manager_instance.load_category(cat_id)
if cat != None:
- return cat.cat_graph \
- .serialize(format='turtle') \
- .decode("utf-8"), \
- 200
+ 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"))
+ response.append(
+ cat.cat_graph.serialize(format='turtle').decode("utf-8")
+ )
return response, 200
# update category cat_id
@@ -86,17 +86,26 @@
args = cat_parser.parse_args()
if cat_id is None:
if cat_manager_instance.persistence.session_compliant is True:
- logger.debug("Submitting - deleted categories are:"
- + str(session.get("deleted_categories", {})
- .get(repository, {}))
- + " and modified categories are:"
- + str(session.get("modified_categories", {})
- .get(repository, {})))
+ logger.debug(
+ "Submitting - deleted categories are:"
+ + str(
+ session.get(
+ "deleted_categories", {}
+ ).get(repository, {})
+ ) + " and modified categories are:"
+ + str(
+ session.get(
+ "modified_categories", {}
+ ).get(repository, {})
+ )
+ )
cat_manager_instance.save_changes(
- deleted_cat_dict=session.get("deleted_categories", {})
- .get(repository, {}),
- modified_cat_dict=session.get("modified_categories", {})
- .get(repository, {}),
+ deleted_cat_dict=session.get(
+ "deleted_categories", {}
+ ).get(repository, {}),
+ modified_cat_dict=session.get(
+ "modified_categories", {}
+ ).get(repository, {}),
message=args["commit_message"]
)
session["deleted_categories"]["local"] = {}
@@ -105,13 +114,14 @@
return 204
else:
# is the edition occuring on an already modified category?
- if cat_id in session.get("modified_categories", {}) \
- .get(repository, {}).keys():
+ if (cat_id in session.get(
+ "modified_categories", {}
+ ).get(repository, {}).keys()):
cat_graph = Graph()
cat_graph.parse(
- source=StringIO(session["modified_categories"]
- [repository]
- [cat_id]),
+ source=StringIO(
+ session["modified_categories"][repository][cat_id]
+ ),
format="turtle"
)
cat = Category(graph=cat_graph)
@@ -213,18 +223,21 @@
)
cat_list = cat_manager_instance.list_categories()
# first we edit what was already modified before the deletion
- logger.debug(session.get("modified_categories", {})
- .get(repository, {}))
- if deleted_cat_id in session["deleted_categories"] \
- [repository].keys():
+ logger.debug(
+ session.get("modified_categories", {}).get(repository, {})
+ )
+ if (
+ deleted_cat_id in
+ session["deleted_categories"][repository].keys()
+ ):
for element in session.get("modified_categories", {}) \
.get(repository, {}).keys():
logger.debug(str(element))
modified_cat_graph = Graph()
modified_cat_graph.parse(
- source=StringIO(session["modified_categories"]
- [repository]
- [element]),
+ source=StringIO(
+ session["modified_categories"][repository][element]
+ ),
format="turtle"
)
modified_cat = Category(graph=modified_cat_graph)
@@ -233,12 +246,16 @@
new_property_list = []
for (predicate, obj) in modified_cat.properties:
if not (
- app.config["PROPERTY_LIST"]
- [predicate]
- ["object_type"] == "uriref-category"
- and
- (obj == (app.config["CATEGORY_NAMESPACE"] +
- deleted_cat_id))
+ app.config["PROPERTY_LIST"]
+ [predicate]
+ ["object_type"]
+ == "uriref-category"
+ and (
+ obj == (
+ app.config["CATEGORY_NAMESPACE"]
+ + deleted_cat_id
+ )
+ )
):
new_property_list.append((predicate, obj))
--- a/src/catedit/static/css/style.css Fri Jan 30 12:41:40 2015 +0100
+++ b/src/catedit/static/css/style.css Fri Feb 13 17:22:21 2015 +0100
@@ -1,11 +1,3 @@
-.property-row{
- margin-top: 4px;
- margin-bottom: 4px;
-}
-
-.property-delete-button{
- margin-left: 4px;
-}
.visible{
display:block;
--- a/src/catedit/templates/catbase.html Fri Jan 30 12:41:40 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-<!DOCTYPE html>
-<html lang="fr">
-<head>
- {% block head %}
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>{% block title %}{% endblock title %}</title>
- <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
- <link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
- {% endblock head %}
-</head>
-<body>
- <div class="navbar navbar-inverse" role="navigation">
- <div class="container">
- <div class="navbar-header">
- <a class="navbar-brand" href="{{ url_for('cat_index') }}">
- <img alt="Brand" src="{{ url_for('static', filename='img/catedit_brand.png') }}" class="navbar-img" width="32" height="32">
- </a>
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
- <span class="sr-only">Toggle navigation</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- </div>
- <div class="collapse navbar-collapse">
- <ul class="nav navbar-nav">
- {% block navbar_items %}
- <li><a href="{{ url_for('cat_index') }}">Page d'accueil</a></li>
- {% endblock navbar_items %}
- </ul>
- <div class="navbar-text navbar-right">
- {% if not session.get("user_logged", None)%} Non authentifié - <a href="{{ url_for('github_login') }}" class="navbar-link">S'authentifier</a>
- {% else %} Authentifié: {{ session["user_login"] }} - <a href="{{ url_for('logout') }}" class="navbar-link">Quitter</a>{% endif %}
- </div>
- {% if session["user_logged"] %}
- <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 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 %}
- </select>
- </form>
- {% endif %}
- </div>
- </div>
- </div>
- <div class="container">
- {% block page_content %}
- {% endblock page_content %}
- </div>
- <footer class="footer">
- <div class="text-right footer-notes">
- CatEdit - Prototype v{{config["CURRENT_VERSION"]}}
- <br><br>
- </div>
- </footer>
-</body>
--- a/src/catedit/templates/cateditor.html Fri Jan 30 12:41:40 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-{% 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 %} {{ current_repository}}: Editeur {% endblock title %}
-{% block head %}
- {{ super() }}
-{% endblock head %}
-{% block navbar_items %}
- {{ super() }}
- {% if session.get("user_logged", None) %}
- <li><a class="navbar-decorative">></a></li>
- <li><a href="{{ url_for('cat_recap', repository=current_repository) }}">Atelier</a></li>
- <li><a class="navbar-decorative">></a></li>
- <li class="active"><a>Editeur</a></li>
- {% endif %}
-{% endblock navbar_items%}
-{% block repo_list %}
- {{ super() }}
-{% 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"][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>
- Vous n'avez pas accès en écriture au repository contenant les catégories - Vous ne pourrez pas les modifier.
-</div>
-{% endif %}
-{% if not session["user_logged"] %}
-<div class="alert alert-warning" role="alert">
- <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
- <span class="sr-only">Attention:</span>
- Vous devez être authentifié pour modifier les catégories.
-</div> {% endif %}
-{% if form.label.errors or form.description.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 %}
- <h3>{% if cat_id: %} Edition : <small>Catégorie existante</small>{% else %}Création : <small>Nouvelle catégorie</small>{% endif %}</h3>
- {% if readonly %} <fieldset disabled> {% endif %}
- <form method="POST" action="{% if cat_id %}{{ url_for('cat_editor', cat_id=cat_id, repository=current_repository) }}{% else %}{{ url_for('cat_editor', repository=current_repository) }}{% endif %}" id="cat_form" role="form">
- {{ form.hidden_tag() }}
- {% if form.label.errors %}
- {% set label_placeholder="Champ obligatoire" %}
- {% endif %}
- {% if form.description.errors %}
- {% set description_placeholder="Champ obligatoire" %}
- {% endif %}
- {{ form.label.label }} <br> {{ form.label(size=40, class="form-control", readonly=readonly, placeholder=label_placeholder) }} <br>
- {{ form.description.label }} <br> {{ form.description(size=150, class="form-control", readonly=readonly, placeholder=description_placeholder) }} <br>
- <label>Propriétés </label>
- <div class="form-inline">
- <select id="property_selector" class="form-control" onChange="CatEditScripts.displayCorrespondingField();" {{readonly}}>
- <option label="property_type_default" selected="selected">
- Liste des propriétés ...
- </option>
- {% for predicate in config["PROPERTY_LIST"] %}
- <option value='{{ predicate }}' label={{ config["PROPERTY_LIST"][predicate]["object_type"] }} >{{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}</option>
- {% endfor %}
- </select>
- <input type="text" id="literal-field" class="hidden form-control">
- <input type="text" id="uriref-link-field" placeholder="http://my-example.com" class="hidden form-control">
- <select class="hidden form-control" id="uriref-category-field">
- <option value="default" selected="selected"> Liste des catégories </option>
- {% for cat in cat_list %}
- <option value="{{ cat.cat_id }}"> {{ cat.cat_label }} </option>
- {% endfor %}
- </select>
- <input type="button" value="Ajouter propriété" onClick="CatEditScripts.addProperty('properties_div','property_table_body','{{ csrf_token() }}');" class="btn btn-default" {{ readonly }}>
- </div>
- <div class="alert alert-warning hidden" role="alert" id="uriref-category-field-text">
- </div>
- <div id="properties_div">
- <div class="row">
- <div class="col-md-6">
- <br>
- <table id="property_table" class="table table-condensed">
- <tbody id="property_table_body">
- {% set property_count=0 %}
- {% for property in form.properties %}
- <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" %}
- {% for cat in cat_list %}
- {% if property.property_object.data == cat.cat_id %}
- {% if cat.cat_id not in deleted_cat_list %}
- {{ property.property_predicate() }}
- {{ property.property_object() }}
- <td id="predicate_td{{ property_count }}">
- <strong>{{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }}</strong>
- </td>
- <td id="object_td{{property_count-1}}">
- {{ cat.cat_label }}
- </td>
- <td id="delete_button_td{{property_count-1}}" class="text-center">
- <input type="button" id="property_delete_button{{property_count-1}}" class="btn btn-default property-delete-button" onClick="CatEditScripts.removeProperty({{ property_count }}, 'properties_div')" value="Supprimer">
- </td>
- {% endif %}
- {% endif %}
- {% endfor %}
- {% else %}
- {{ property.property_predicate() }}
- {{ property.property_object() }}
- <td id="predicate_td{{ property_count }}">
- <strong>{{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }}</strong>
- </td>
- <td id="object_td{{ property_count }}">
- {% if config["PROPERTY_LIST"][property.property_predicate.data]["object_type"]=="uriref-link" %}
- <a href="{{ property.property_object.data }}">{{ property.property_object.data }}</a>
- {% else %}
- {{ property.property_object.data }}
- {% endif %}
- </td>
- <td id="delete_button_td{{property_count-1}}" class="text-center">
- <input type="button" id="property_delete_button{{property_count-1}}" class="btn btn-default property-delete-button" onClick="CatEditScripts.removeProperty({{ property_count }}, 'properties_div')" value="Supprimer">
- </td>
- {% endif %}
- {% set property_count=property_count+1 %}
- </tr>
- {% endfor %}
- </tbody>
- </table>
- </div>
- </div>
- </div><br>
- <input type="submit" value="Sauvegarder" class="btn btn-default">
- <a href="{{ url_for('cat_recap', repository=current_repository)}}"class="btn btn-default">Annuler</a>
- </form>
- {% if readonly %} </fieldset> {% endif %}
- <script src="{{ url_for('static', filename='js/property_functions.js') }}" language="Javascript" type="text/javascript"></script>
- {% if cat_id %}
- <script type=text/javascript>
- CatEditScripts.initPropertyCount('properties_div',{{cat_properties|length}});
- CatEditScripts.initCreatedProperties({{cat_properties|length}});
- </script>
- {% endif %}
-{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/categories/editor.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,146 @@
+{% extends "layout.html" %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
+ {% set readonly="readonly" %}
+{% else %}
+ {% set readonly=False %}
+{% endif %}
+{% block title %} {{ current_repository}}: Editeur {% endblock title %}
+{% block head %}
+ {{ super() }}
+{% endblock head %}
+{% block navbar_items %}
+ {{ super() }}
+ {% if session.get("user_logged", None) %}
+ <li><a class="navbar-decorative">></a></li>
+ <li><a href="{{ url_for('categories.workshop', repository=current_repository) }}">Atelier</a></li>
+ <li><a class="navbar-decorative">></a></li>
+ <li class="active"><a>Editeur</a></li>
+ {% endif %}
+{% endblock navbar_items%}
+{% block repo_list %}
+ {{ super() }}
+{% endblock repo_list %}
+{% block page_content %}
+<h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
+{% if not session["user_logged"] or 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>
+ Vous n'avez pas accès en écriture au repository contenant les catégories - Vous ne pourrez pas les modifier.
+</div>
+{% endif %}
+{% if not session["user_logged"] %}
+<div class="alert alert-warning" role="alert">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ <span class="sr-only">Attention:</span>
+ Vous devez être authentifié pour modifier les catégories.
+</div> {% endif %}
+{% if form.label.errors or form.description.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 %}
+ <h3>{% if cat_id: %} Edition : <small>Catégorie existante</small>{% else %}Création : <small>Nouvelle catégorie</small>{% endif %}</h3>
+ {% if readonly %} <fieldset disabled> {% endif %}
+ <form method="POST" action="{% if cat_id %}{{ url_for('categories.editor', cat_id=cat_id, repository=current_repository) }}{% else %}{{ url_for('categories.editor', repository=current_repository) }}{% endif %}" id="cat_form" role="form">
+ {{ form.hidden_tag() }}
+ {% if form.label.errors %}
+ {% set label_placeholder="Champ obligatoire" %}
+ {% endif %}
+ {% if form.description.errors %}
+ {% set description_placeholder="Champ obligatoire" %}
+ {% endif %}
+ {{ form.label.label }} <br> {{ form.label(size=40, class="form-control", readonly=readonly, placeholder=label_placeholder) }} <br>
+ {{ form.description.label }} <br> {{ form.description(size=150, class="form-control", readonly=readonly, placeholder=description_placeholder) }} <br>
+ <label>Propriétés </label>
+ <div class="form-inline">
+ <select id="property_selector" class="form-control" onChange="CatEditScripts.displayCorrespondingField();" {{readonly}}>
+ <option label="property_type_default" selected="selected">
+ Liste des propriétés ...
+ </option>
+ {% for predicate in config["PROPERTY_LIST"] %}
+ <option value='{{ predicate }}' label={{ config["PROPERTY_LIST"][predicate]["object_type"] }} >{{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}</option>
+ {% endfor %}
+ </select>
+ <input type="text" id="literal-field" class="hidden form-control">
+ <input type="text" id="uriref-link-field" placeholder="http://my-example.com" class="hidden form-control">
+ <select class="hidden form-control" id="uriref-category-field">
+ <option value="default" selected="selected"> Liste des catégories </option>
+ {% for cat in cat_list %}
+ <option value="{{ cat.cat_id }}"> {{ cat.cat_label }} </option>
+ {% endfor %}
+ </select>
+ <input type="button" value="Ajouter propriété" onClick="CatEditScripts.addProperty('properties_div','property_table_body','{{ csrf_token() }}');" class="btn btn-default" {{ readonly }}>
+ </div>
+ <div class="alert alert-warning hidden" role="alert" id="uriref-category-field-text">
+ </div>
+ <div id="properties_div">
+ <div class="row">
+ <div class="col-md-6">
+ <br>
+ <table id="property_table" class="table table-condensed">
+ <tbody id="property_table_body">
+ {% set property_count=0 %}
+ {% for property in form.properties %}
+ <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" %}
+ {% for cat in cat_list %}
+ {% if property.property_object.data == cat.cat_id %}
+ {% if cat.cat_id not in deleted_cat_list %}
+ {{ property.property_predicate() }}
+ {{ property.property_object() }}
+ <td id="predicate_td{{ property_count }}">
+ <strong>{{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }}</strong>
+ </td>
+ <td id="object_td{{property_count-1}}">
+ {{ cat.cat_label }}
+ </td>
+ <td id="delete_button_td{{property_count-1}}" class="text-center">
+ <input type="button" id="property_delete_button{{property_count-1}}" class="btn btn-default property-delete-button" onClick="CatEditScripts.removeProperty({{ property_count }}, 'properties_div')" value="Supprimer">
+ </td>
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+ {% else %}
+ {{ property.property_predicate() }}
+ {{ property.property_object() }}
+ <td id="predicate_td{{ property_count }}">
+ <strong>{{ config["PROPERTY_LIST"][property.property_predicate.data]["descriptive_label_fr"] }}</strong>
+ </td>
+ <td id="object_td{{ property_count }}">
+ {% if config["PROPERTY_LIST"][property.property_predicate.data]["object_type"]=="uriref-link" %}
+ <a href="{{ property.property_object.data }}">{{ property.property_object.data }}</a>
+ {% else %}
+ {{ property.property_object.data }}
+ {% endif %}
+ </td>
+ <td id="delete_button_td{{property_count-1}}" class="text-center">
+ <input type="button" id="property_delete_button{{property_count-1}}" class="btn btn-default property-delete-button" onClick="CatEditScripts.removeProperty({{ property_count }}, 'properties_div')" value="Supprimer">
+ </td>
+ {% endif %}
+ {% set property_count=property_count+1 %}
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div><br>
+ <input type="submit" value="Sauvegarder" class="btn btn-default">
+ <a href="{{ url_for('categories.workshop', repository=current_repository)}}"class="btn btn-default">Annuler</a>
+ </form>
+ {% if readonly %} </fieldset> {% endif %}
+ <script src="{{ url_for('static', filename='js/property_functions.js') }}" language="Javascript" type="text/javascript"></script>
+ {% if cat_id %}
+ <script type=text/javascript>
+ CatEditScripts.initPropertyCount('properties_div',{{cat_properties|length}});
+ CatEditScripts.initCreatedProperties({{cat_properties|length}});
+ </script>
+ {% endif %}
+{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/categories/submit.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,156 @@
+{% extends "layout.html" %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
+ {% set readonly="readonly" %}
+{% else %}
+ {% set readonly=False %}
+{% endif %}
+{% block title %}{{current_repository}}: Soumission{% endblock title %}
+{% block head %}
+ {{ super() }}
+ <script src="{{ url_for('static', filename='js/jquery-1.11.1.min.js') }}" language="Javascript" type="text/javascript"></script>
+ <script>
+ $(document).ready(function(){
+ $(".cat-delete-div").hide();
+ $(".cat-info-div").hide();
+ $(".cat-table-toggle").click(function(evt){
+ $(".cat-table").slideToggle(function(){
+ $(".cat-table-toggle").children().toggleClass("glyphicon-chevron-up");
+ $(".cat-table-toggle").children().toggleClass("glyphicon-chevron-down");
+ });
+ });
+ $(".cat-delete-toggle").click(function(evt){
+ $("#delete_confirm_"+evt.target.id.split('_').slice(2,5).join('_')).slideToggle("fast");
+ });
+ $(".cat-info-toggle").click(function(evt){
+ $("#properties_"+evt.target.id.split('_').slice(2,5).join('_')).slideToggle(function(){
+ $("#info_button_"+evt.target.id.split('_').slice(2,5).join('_')).children().toggleClass("glyphicon-plus-sign");
+ $("#info_button_"+evt.target.id.split('_').slice(2,5).join('_')).children().toggleClass("glyphicon-minus-sign");
+ });
+ });
+ });
+ </script>
+{% endblock head %}
+{% block navbar_items %}
+ {{ super() }}
+ {% if session.get("user_logged", None) %}
+ <li><a class="navbar-decorative">></a></li>
+ <li><a href="{{ url_for('categories.workshop', repository=current_repository) }}">Atelier</a></li>
+ <li><a class="navbar-decorative">></a></li>
+ <li class="active"><a>Soumission</a></li>
+ {% endif %}
+{% endblock navbar_items %}
+{% block repo_list %}
+ {{ super() }}
+{% endblock repo_list %}
+{% block page_content %}
+ <h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
+ <h3> Catégories existantes <a title="Afficher/Cacher tableau" class="btn btn-default cat-table-toggle">
+ <span class="glyphicon glyphicon-chevron-up"/>
+ </a></h3>
+ <div class="cat-table">
+ <table class="table table-bordered table-condensed">
+ <thead>
+ <tr class="active">
+ <th class="col-md-2"><b>Nom de la catégorie</b></th>
+ <th class="col-md-10" colspan="5"><b>Description de la catégorie</b></th>
+ </tr>
+ </thead>
+ <tbody>
+ {% if not session["user_logged"] %}
+ <tr>
+ <td class="col-md-12" colspan="5">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ <span class="sr-only">Attention:</span>
+ Veuillez vous identifier pour visualiser les catégories
+ </td>
+ </tr>
+ {% else %}
+ {% if original_cat_count == 0 %}
+ <tr>
+ <td class="col-md-12" colspan="3">Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}<a href="{{ url_for('categories.editor', repository=current_repository) }}">Créer une catégorie</a>{% endif %}</td>
+ </tr>
+ {% else %}
+ {% import "macros.html" as macros %}
+ {{ macros.category_table(original_cat_list, current_repository, state_list=["original"], target="categories.submit") }}
+ {% endif %}
+ {% endif %}
+ </tbody>
+ </table>
+ </div>
+
+ <h3> Mes modifications </h3>
+ <table class="table table-bordered table-condensed">
+ <thead>
+ <tr class="active">
+ <th class="col-md-2"><b>Nom de la catégorie</b></th>
+ <th class="col-md-10" colspan="4"><b>Description de la catégorie</b></th>
+ </tr>
+ </thead>
+ <tbody>
+ {% if not session["user_logged"] %}
+ <tr>
+ <td class="col-md-12" colspan="5">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ <span class="sr-only">Attention:</span>
+ Veuillez vous identifier pour visualiser les modifications
+ </td>
+ </tr>
+ {% else %}
+ <tr class="success">
+ <td class="col-md-12" colspan="5">
+ <b> Catégories ajoutées</b>
+ </td>
+ </tr>
+ {% if created_cat_count == 0 %}
+ <tr>
+ <td class="col-md-12" colspan="5">Aucune catégorie n'a été ajoutée pour l'instant.</td>
+ </tr>
+ {% else %}
+ {% import "macros.html" as macros %}
+ {{ macros.category_table(cat_list, current_repository, state_list=["created"], target="categories.submit") }}
+ {% endif %}
+ <tr class="warning">
+ <td class="col-md-12" colspan="5">
+ <b> Catégories modifiées</b>
+ </td>
+ </tr>
+ {% if edited_cat_count == 0 %}
+ <tr>
+ <td class="col-md-12" colspan="5">Aucune catégorie n'a été modifiée pour l'instant.</td>
+ </tr>
+ {% else %}
+ {% import "macros.html" as macros %}
+ {{ macros.category_table(cat_list, current_repository, state_list=["modified"], target="categories.submit") }}
+ {% endif %}
+
+ <tr class="danger">
+ <td class="col-md-12" colspan="5">
+ <b> Catégories supprimées</b>
+ </td>
+ </tr>
+ {% if deleted_cat_count == 0 %}
+ <tr>
+ <td class="col-md-12" colspan="5">Aucune catégorie n'a été supprimée pour l'instant.</td>
+ </tr>
+ {% else %}
+ {% import "macros.html" as macros %}
+ {{ macros.category_table(cat_list, current_repository, state_list=["deleted"], target="categories.submit") }}
+ {% endif %}
+ {% endif %}
+ </tbody>
+ </table>
+ <h3> Soumettre mes changements </h3>
+ <div class="col-md-12">
+ <form method="POST" action="{{ url_for('categories.submit', repository=current_repository)}}">
+ <fieldset {% if readonly %}disabled{% endif %}>
+ {{ commit_form.hidden_tag() }}
+ <div class="form-group">
+ {{ commit_form.commit_message.label }}
+ {{ commit_form.commit_message(size=40, class="form-control", readonly=readonly) }}
+ </div>
+ <button type="submit" class="btn btn-default">Soumettre modifications</button>
+ <a href="{{ url_for('categories.workshop', repository=current_repository)}}"class="btn btn-default">Retour</a>
+ </fieldset>
+ </form><br>
+ </div>
+{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/categories/workshop.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,101 @@
+{% extends "layout.html" %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
+ {% set readonly="readonly" %}
+{% else %}
+ {% set readonly=False %}
+{% endif %}
+{% block title %}{{current_repository}}: Atelier{% endblock title %}
+{% block head %}
+ {{ super() }}
+ <script src="{{ url_for('static', filename='js/jquery-1.11.1.min.js') }}" language="Javascript" type="text/javascript"></script>
+ <script>
+ $(document).ready(function(){
+ $(".cat-delete-div").hide();
+ $(".cat-info-div").hide();
+ $(".cat-table-toggle").click(function(evt){
+ $(".cat-table").slideToggle(function(){
+ $(".cat-table-toggle").children().toggleClass("glyphicon-chevron-up");
+ $(".cat-table-toggle").children().toggleClass("glyphicon-chevron-down");
+ });
+ });
+ $(".cat-delete-toggle").click(function(evt){
+ $("#delete_confirm_"+evt.target.id.split('_').slice(2,5).join('_')).slideToggle();
+ });
+ $(".cat-info-toggle").click(function(evt){
+ $("#properties_"+evt.target.id.split('_').slice(2,5).join('_')).slideToggle(function(){
+ $("#info_button_"+evt.target.id.split('_').slice(2,5).join('_')).children().toggleClass("glyphicon-plus-sign");
+ $("#info_button_"+evt.target.id.split('_').slice(2,5).join('_')).children().toggleClass("glyphicon-minus-sign");
+ });
+ });
+ });
+ </script>
+{% endblock head %}
+{% block navbar_items %}
+ {{ super() }}
+ {% if session.get("user_logged", None) %}
+ <li><a class="navbar-decorative">></a></li>
+ <li class="active"><a>Atelier</a></li>
+ {% endif %}
+{% endblock navbar_items %}
+{% block repo_list %}
+ {{ super() }}
+{% endblock repo_list %}
+{% block page_content %}
+ <h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
+ <h3> Créer une catégorie : <a href="{{url_for('categories.editor', repository=current_repository)}}" title="Créer catégorie" class="btn btn-default {% if readonly %}disabled{% endif %}"><span class="glyphicon glyphicon-plus"/></a></h3>
+ {% if session["user_logged"] and not session["user_can_edit"] %}
+ <div class="alert alert-warning" role="alert">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ <span class="sr-only">Attention:</span>
+ Vous n'avez pas accès en écriture au repository contenant les catégories - Vous ne pourrez pas les modifier.
+ </div>
+ {% endif %}
+ <h3>Mes catégories <a title="Afficher/Cacher tableau" class="btn btn-default cat-table-toggle">
+ <span class="glyphicon glyphicon-chevron-up"/>
+ </a></h3>
+ <div class="cat-table">
+ <table class="table table-bordered table-condensed">
+ <thead>
+ <tr class="active">
+ <th class="col-md-2"><b>Nom de la catégorie</b></th>
+ <th class="col-md-10"><b>Description de la catégorie</b></th>
+ </tr>
+ </thead>
+ <tbody>
+ {% if not session["user_logged"] %}
+ <tr>
+ <td class="col-md-12" colspan="2">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ <span class="sr-only">Attention:</span>
+ Veuillez vous identifier pour visualiser les catégories
+ </td>
+ </tr>
+ {% else %}
+ {% if cat_list|length == 0 %}
+ <tr>
+ <td class="col-md-12" colspan="2">Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}<a href="{{ url_for('categories.editor', repository=current_repository) }}">Créer une catégorie</a>{% endif %}</td>
+ </tr>
+ {% else %}
+ {% import "macros.html" as macros %}
+ {{ macros.category_table(cat_list, current_repository, target="categories.workshop") }}
+ {% endif %}
+ {% endif %}
+ </tbody>
+ </table>
+ </div>
+ {% if session.get("user_logged") %}
+ <form method="POST" action="{{url_for('categories.workshop', repository=current_repository)}}" class="form-inline">
+ <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
+ <h4> Annuler tous mes changements actuels :
+ <a title="Supprimer changements">
+ <button type="submit" class="btn btn-default" {% if readonly %}disabled{% endif %}>
+ <span class="glyphicon glyphicon-remove"/>
+ </button>
+ </a>
+ </h4>
+ </form>
+ <h4> Soumettre mes changements actuels : <a href="{{ url_for('categories.submit', repository=current_repository)}}" title="Soumettre changements" class="btn btn-default" {% if readonly %}disabled{% endif %}><span class="glyphicon glyphicon-share"/></a>
+ </h4>
+ {% endif %}
+ <h3> Discussions sur cet ensemble de catégories : <a href="{{ url_for('social.index', repository=current_repository)}}" title="Aller sur la liste des discussions" class="btn btn-default"><span class="glyphicon glyphicon-comment"/></a></h3>
+{% endblock page_content %}
--- a/src/catedit/templates/catindex.html Fri Jan 30 12:41:40 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-{% extends "catbase.html" %}
-{% if not session["user_logged"] %}
- {% set readonly="readonly" %}
-{% else %}
- {% set readonly=False %}
-{% endif %}
-{% block title %}Page d'accueil{% endblock title %}
-{% block head %}
- {{ super() }}
-{% endblock head %}
-{% block navbar_items %}
- <li class="active"><a>Page d'accueil</a></li>
-{% endblock navbar_items %}
-{% block repo_list %}
- <option value="" selected="selected"> Sélectionnez ensemble... </option>
- {{ super() }}
-{% endblock repo_list %}
-{% block page_content %}
- <h2> <b>CatEdit</b>: Editeur de Catégories</h2>
- <h3> Mode d'emploi </h3>
- <div class="col-md-8">
- <p>
- CatEdit est un outil permettant de créer et d'éditer des catégories.
- Vous aurez besoin d'un compte Github pour utiliser CatEdit:
- <a href="https://github.com/">s'inscrire sur Github</a>
- </p>
- <p>
- Une fois authentifié, choisissez un ensemble de catégorie dans la liste se trouvant
- dans la barre de navigation pour être redirigé vers l'atelier correspondant.
- Vous pourrez y trouver la liste des catégories existantes pour cet ensemble,
- présentée dans un tableau qui initialement ne comprend que les catégories de l'état courant.
- </p>
- <p>
- L'état courant de l'ensemble de catégorie est la base à partir de laquelle
- vous pourrez créer et éditer des catégories. Une catégorie consiste en un label, unique
- et non-vide, une description, unique et non-vide et un nombre variable de propriétés.
- </p>
- <h4><b>La liste de catégories</b></h4>
- <p>
- A chaque fois que vous faites des modifications, elles sont stockées
- de manière temporaire tant que vous restez authentifié. La liste de catégories
- dans l'Atelier est mise à jour au fil de vos modifications selon un code couleur
- vous permettant de visualiser vos changements.
- </p>
- <ul>
- <li>
- Une ligne de tableau <b>blanche</b> représente une catégorie de l'état courant
- que vous n'avez ni modifié ni supprimé.
- </li>
- <li>
- Une ligne de tableau <b class="text-success">verte</b> représente une catégorie que vous avez créée de zéro
- et qui n'existait pas précédemment dans l'état courant.
- </li>
- <li>
- Une ligne de tableau <b class="text-warning">orange</b> représente une catégorie qui existait
- initialement et que vous avez modifié.
- </li>
- <li>
- Une ligne de tableau <b class="text-danger">rouge</b> représente une catégorie de l'état courant que
- vous avez supprimé.
- </li>
- </ul>
- </p>
- <p>
- Pour que vos modifications soient permanentes et qu'elles deviennent le nouvel état courant
- de l'ensemble de catégories, vous devez soumettre vos modifications en suivant le lien dans l'Atelier.
- Vous trouverez une page vous présentant l'état initial d'une part et vos changements d'autre part.
- </p>
- <p>
- Vous devez obligatoirement renseigner une explication de vos changements avant soumission.
- </p>
- <h3> Interface de discussion </h3>
- <p>
- Chaque soumission de modifications ouvrira sur GitHub un fil de discussion ayant pour titre le message
- de soumission. Vous pouvez accéder à la liste de changements via un lien dans l'Atelier. Cela vous
- redirigera vers la page GitHub présentant tous les changements, et vous pouvez accéder à la discussion
- d'un changement donné en cliquant sur son titre.
- </p>
- <p>
- Il est aussi possible de créer des discussions générales sur un ensemble de catégories. Un lien est disponible
- dans l'atelier vers la page "Issues" de l'ensemble de catégories sur GitHub, où vous pouvez commenter et créer
- de nouvelles "Issues" pour alimenter la discussion sur l'ensemble de catégories considéré.
- </p>
- <br><br>
- </div>
-{% endblock page_content %}
--- a/src/catedit/templates/catlogin.html Fri Jan 30 12:41:40 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-{% 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 Fri Jan 30 12:41:40 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-{% 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 %}{{current_repository}}: Soumission{% endblock title %}
-{% block head %}
- {{ super() }}
- <script src="{{ url_for('static', filename='js/jquery-1.11.1.min.js') }}" language="Javascript" type="text/javascript"></script>
- <script>
- $(document).ready(function(){
- {% for cat in existing_cat_list %}
- $("#properties_{{cat.cat_id}}").hide();
- $("#info_button_{{cat.cat_id}}").click(function(){
- $("#properties_{{cat.cat_id}}").slideToggle();
- });
- {% endfor %}
- {% for cat in modified_cat_list %}
- $("#properties_modified_{{cat.cat_id}}").hide();
- $("#info_button_modified_{{cat.cat_id}}").click(function(){
- $("#properties_modified_{{cat.cat_id}}").slideToggle();
- });
- $("#delete_modified_{{cat.cat_id}}").hide();
- $("#remove_modifs_modified_{{cat.cat_id}}").click(function(){
- $("#delete_modified_{{cat.cat_id}}").slideToggle();
- });
- {% endfor %}
- {% for cat in created_cat_list %}
- $("#properties_created_{{cat.cat_id}}").hide();
- $("#info_button_created_{{cat.cat_id}}").click(function(){
- $("#properties_created_{{cat.cat_id}}").slideToggle();
- });
- $("#delete_created_{{cat.cat_id}}").hide();
- $("#remove_modifs_created_{{cat.cat_id}}").click(function(){
- $("#delete_created_{{cat.cat_id}}").slideToggle();
- });
- {% endfor %}
- });
- </script>
-{% endblock head %}
-{% block navbar_items %}
- {{ super() }}
- {% if session.get("user_logged", None) %}
- <li><a class="navbar-decorative">></a></li>
- <li><a href="{{ url_for('cat_recap', repository=current_repository) }}">Atelier</a></li>
- <li><a class="navbar-decorative">></a></li>
- <li class="active"><a>Soumission</a></li>
- {% endif %}
-{% endblock navbar_items %}
-{% block repo_list %}
- {{ super() }}
-{% endblock repo_list %}
-{% block page_content %}
- <h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
- <h3> Catégories existantes </h3>
- <table class="table table-bordered table-condensed">
- <thead>
- <tr class="active">
- <th class="col-md-2"><b>Nom de la catégorie</b></th>
- <th class="col-md-10"><b>Description de la catégorie</b></th>
- </tr>
- </thead>
- <tbody>
- {% if not session["user_logged"] %}
- <tr>
- <td class="col-md-12" colspan="2">
- <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
- <span class="sr-only">Attention:</span>
- Veuillez vous identifier pour visualiser les catégories
- </td>
- </tr>
- {% else %}
- {% if existing_cat_list|length == 0 %}
- <tr>
- <td class="col-md-12" colspan="2">Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}<a href="{{ url_for('cat_editor', repository=current_repository) }}">Créer une catégorie</a>{% endif %}</td>
- </tr>
- {% else %}
- {% for cat in existing_cat_list %}
- <tr>
- <td class="col-md-2">{{ cat.cat_label }}</td>
- <td class="col-md-8">{{ cat.cat_description}}</td>
- <td class="text-center">
- <a title="Détails catégorie"><button class="btn btn-default" id="info_button_{{ cat.cat_id }}"><span class="glyphicon glyphicon glyphicon-plus-sign"/></button></a>
- </td>
- {% if cat.cat_id not in deleted_cat_namelist and cat.cat_id not in modified_cat_namelist %}
- <td class="text-center">
- <a href="{{ url_for('cat_editor', cat_id=cat.cat_id, repository=current_repository)}}" title="Editer catégorie" class="btn btn-default"><span class="glyphicon glyphicon glyphicon-pencil"/></a>
- </td>
- <td class="text-center">
- <a title="Supprimer catégorie">
- <form method="POST" action="{{ url_for('cat_modifs', deleted_cat_id=cat.cat_id, repository=current_repository) }}" class="form-inline form-button">
- <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
- <fieldset {% if readonly %}disabled{% endif %}>
- <button type="submit" class="btn btn-default">
- <span class="glyphicon glyphicon-trash" title="Supprimer catégorie"/>
- </button>
- </fieldset>
- </form>
- </a>
- </td>
- {% else %}
- <td colspan="2">
- </td>
- {% endif %}
- </tr>
- <tr>
- <td colspan="2">
- <div id="properties_{{cat.cat_id}}">
- <dl class="dl-horizontal">
- {% if cat.cat_properties|length == 0 %} <dt></dt><dd>Aucune autre propriété</dd>
- {% else %}
- {% for (predicate, object) in cat.cat_properties %}
- <dt>{{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}</dt>
- <dd>
- {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %}
- {% for cat in existing_cat_list %}
- {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %}
- {{ cat.cat_label }}
- {% endif %}
- {% endfor %}
- {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %}
- <a href="{{ object }}">{{ object }}</a>
- {% else %}
- {{ object }}
- {% endif %}
- </dd>
- {% endfor %}
- {% endif %}
- </dl>
- </div>
- </td>
- </tr>
- {% endfor %}
- {% endif %}
- {% endif %}
- </tbody>
- </table>
-
- <h3> Mes modifications </h3>
- <table class="table table-bordered table-condensed">
- <thead>
- <tr class="active">
- <th class="col-md-2"><b>Nom de la catégorie</b></th>
- <th class="col-md-10" colspan="2"><b>Description de la catégorie</b></th>
- </tr>
- </thead>
- <tbody>
- {% if not session["user_logged"] %}
- <tr>
- <td class="col-md-12" colspan="3">
- <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
- <span class="sr-only">Attention:</span>
- Veuillez vous identifier pour visualiser les modifications
- </td>
- </tr>
- {% else %}
- <tr class="success">
- <td class="col-md-12" colspan="3">
- <b> Catégories ajoutées</b>
- </td>
- </tr>
- {% if created_cat_list|length == 0 %}
- <tr>
- <td class="col-md-12" colspan="3">Aucune catégorie n'a été ajoutée pour l'instant.</td>
- </tr>
- {% else %}
- {% for cat in created_cat_list %}
- <tr class="success">
- <td class="col-md-2">{{ cat.cat_label }}</td>
- <td class="col-md-8">{{ cat.cat_description}}</td>
- <td class="col-md-2 text-center">
- <a title="Détails catégorie"><button class="btn btn-default" id="info_button_created_{{ cat.cat_id }}"><span class="glyphicon glyphicon-plus-sign"/></button></a>
- <a href="{{ url_for('cat_editor', cat_id=cat.cat_id, repository=current_repository)}}" title="Editer catégorie" class="btn btn-default"><span class="glyphicon glyphicon glyphicon-pencil"/></a>
- <a title="Supprimer modifications"><button class="btn btn-default" id="remove_modifs_created_{{ cat.cat_id }}"><span class="glyphicon glyphicon-remove-sign"/></button></a>
- </td>
- </tr>
- <tr class="success">
- <td colspan="3">
- <div id="properties_created_{{cat.cat_id}}">
- <dl class="dl-horizontal">
- {% if cat.cat_properties|length == 0 %} <dt></dt><dd>Aucune autre propriété</dd>
- {% else %}
- {% for (predicate, object) in cat.cat_properties %}
- <dt>{{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}</dt>
- <dd>
- {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %}
- {% for cat in total_cat_list %}
- {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %}
- {{ cat.cat_label }}
- {% endif %}
- {% endfor %}
- {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %}
- <a href="{{ object }}">{{ object }}</a>
- {% else %}
- {{ object }}
- {% endif %}
- </dd>
- {% endfor %}
- {% endif %}
- </dl>
- </div>
- <div id="delete_created_{{cat.cat_id}}">
- <form method="POST" action="{{ url_for('cat_modifs', deleted_modifs_id=cat.cat_id, repository=current_repository) }}" class="form-inline align-center">
- <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
- <fieldset {% if readonly %}disabled{% endif %}>
- <div class="input-group">
- <div class="input-group-addon">
- Vous allez supprimer les changements faits sur cette catégorie.
- </div>
- <input type="submit" class="btn btn-default" value="Supprimer">
- </div>
- </fieldset>
- </form>
- </div>
- </td>
- </tr>
- {% endfor %}
- {% endif %}
- <tr class="warning">
- <td class="col-md-12" colspan="3">
- <b> Catégories modifiées</b>
- </td>
- </tr>
- {% if modified_cat_list|length == 0 %}
- <tr>
- <td class="col-md-12" colspan="3">Aucune catégorie n'a été modifiée pour l'instant.</td>
- </tr>
- {% else %}
- {% for cat in modified_cat_list %}
- <tr class="warning">
- <td class="col-md-2">{{ cat.cat_label }}</td>
- <td class="col-md-8">{{ cat.cat_description}}</td>
- <td class="col-md-2 text-center">
- <a title="Détails catégorie"><button class="btn btn-default" id="info_button_modified_{{ cat.cat_id }}"><span class="glyphicon glyphicon-plus-sign"/></button></a>
- <a href="{{ url_for('cat_editor', cat_id=cat.cat_id, repository=current_repository)}}" title="Editer catégorie" class="btn btn-default"><span class="glyphicon glyphicon glyphicon-pencil"/></a>
- <a title="Supprimer modifications"><button class="btn btn-default" id="remove_modifs_modified_{{ cat.cat_id }}"><span class="glyphicon glyphicon-remove-sign"/></button></a>
- </td>
- </tr>
- <tr class="warning">
- <td colspan="3">
- <div id="properties_modified_{{cat.cat_id}}">
- <dl class="dl-horizontal">
- {% if cat.cat_properties|length == 0 %} <dt></dt><dd>Aucune autre propriété</dd>
- {% else %}
- {% for (predicate, object) in cat.cat_properties %}
- <dt>{{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}</dt>
- <dd>
- {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %}
- {% for cat in total_cat_list %}
- {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %}
- {{ cat.cat_label }}
- {% endif %}
- {% endfor %}
- {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %}
- <a href="{{ object }}">{{ object }}</a>
- {% else %}
- {{ object }}
- {% endif %}
- </dd>
- {% endfor %}
- {% endif %}
- </dl>
- </div>
- <div id="delete_modified_{{cat.cat_id}}">
- <form method="POST" action="{{ url_for('cat_modifs', deleted_modifs_id=cat.cat_id, repository=current_repository) }}" class="form-inline align-center">
- <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
- <fieldset {% if readonly %}disabled{% endif %}>
- <div class="input-group">
- <div class="input-group-addon">
- Vous allez supprimer les changements faits sur cette catégorie.
- </div>
- <input type="submit" class="btn btn-default" value="Supprimer">
- </div>
- </fieldset>
- </form>
- </div>
- </td>
- </tr>
- {% endfor %}
- {% endif %}
-
- <tr class="danger">
- <td class="col-md-12" colspan="3">
- <b> Catégories supprimées</b>
- </td>
- </tr>
- {% if deleted_cat_namelist|length == 0 %}
- <tr>
- <td class="col-md-12" colspan="3">Aucune catégorie n'a été supprimée pour l'instant.</td>
- </tr>
- {% else %}
- {% for deleted_cat in deleted_cat_namelist %}
- {% for existing_cat in existing_cat_list %}
- {% if existing_cat.cat_id == deleted_cat %}
- <tr class="danger">
- <td class="col-md-2">{{ existing_cat.cat_label }}</td>
- <td class="col-md-8"><i>Cette catégorie va être supprimée quand vous soumettrez vos modifications.</i></td>
- <td class="col-md-2 text-center">
- <form method="POST" action="{{ url_for('cat_modifs', deleted_cat_id=deleted_cat, repository=current_repository) }}">
- <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
- <fieldset {% if readonly %}disabled{% endif %}>
- <input type="submit" class="btn btn-default" value="Restaurer">
- </fieldset>
- </form>
- </td>
- </tr>
- {% endif %}
- {% endfor %}
- {% endfor %}
- {% endif %}
- {% endif %}
- </tbody>
- </table>
- <h3> Soumettre mes changements </h3>
- <div class="col-md-12">
- <form method="POST" action="{{ url_for('cat_modifs', repository=current_repository)}}">
- <fieldset {% if readonly %}disabled{% endif %}>
- {{ commit_form.hidden_tag() }}
- <div class="form-group">
- {{ commit_form.commit_message.label }}
- {{ commit_form.commit_message(size=40, class="form-control", readonly=readonly) }}
- </div>
- <button type="submit" class="btn btn-default">Soumettre modifications</button>
- <a href="{{ url_for('cat_recap', repository=current_repository)}}"class="btn btn-default">Retour</a>
- </fieldset>
- </form><br>
- </div>
-{% endblock page_content %}
--- a/src/catedit/templates/catrecap.html Fri Jan 30 12:41:40 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,172 +0,0 @@
-{% 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 %}{{current_repository}}: Atelier{% endblock title %}
-{% block head %}
- {{ super() }}
- <script src="{{ url_for('static', filename='js/jquery-1.11.1.min.js') }}" language="Javascript" type="text/javascript"></script>
- <script>
- $(document).ready(function(){
- {% for cat in cat_list %}
- $("#properties_{{cat.cat_id}}").hide();
- $("#info_button_{{cat.cat_id}}").click(function(){
- $("#properties_{{cat.cat_id}}").slideToggle();
- });
- {% endfor %}
- });
- </script>
-{% endblock head %}
-{% block navbar_items %}
- {{ super() }}
- {% if session.get("user_logged", None) %}
- <li><a class="navbar-decorative">></a></li>
- <li class="active"><a>Atelier</a></li>
- {% endif %}
-{% endblock navbar_items %}
-{% block repo_list %}
- {{ super() }}
-{% endblock repo_list %}
-{% block page_content %}
- <h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
- <h3> Créer une catégorie : <a href="{{url_for('cat_editor', repository=current_repository)}}" title="Créer catégorie" class="btn btn-default {% if readonly %}disabled{% endif %}"><span class="glyphicon glyphicon-plus"/></a></h3>
- {% if session["user_logged"] and not session["user_can_edit"] %}
- <div class="alert alert-warning" role="alert">
- <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
- <span class="sr-only">Attention:</span>
- Vous n'avez pas accès en écriture au repository contenant les catégories - Vous ne pourrez pas les modifier.
- </div>
- {% endif %}
- <h3>Mes catégories</h3>
- <table class="table table-bordered table-condensed">
- <thead>
- <tr class="active">
- <th class="col-md-2"><b>Nom de la catégorie</b></th>
- <th class="col-md-10"><b>Description de la catégorie</b></th>
- </tr>
- </thead>
- <tbody>
- {% if not session["user_logged"] %}
- <tr>
- <td class="col-md-12" colspan="2">
- <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
- <span class="sr-only">Attention:</span>
- Veuillez vous identifier pour visualiser les catégories
- </td>
- </tr>
- {% else %}
- {% if cat_list|length == 0 %}
- <tr>
- <td class="col-md-12" colspan="2">Aucune catégorie n'a été créée pour l'instant. {% if not readonly %}<a href="{{ url_for('cat_editor', repository=current_repository) }}">Créer une catégorie</a>{% endif %}</td>
- </tr>
- {% else %}
- {% for cat in cat_list %}
- <tr
- {% if cat.state == "created" %}
- class="success"
- {% elif cat.state == "modified" %}
- class="warning"
- {% elif cat.state == "deleted" %}
- class="danger"
- {% endif %}>
- <td class="col-md-2">{{ cat.cat_label }}</td>
- <td class="col-md-8">{{ cat.cat_description}}</td>
- <td class="col-md-1 text-center">
- <a title="Détails catégorie">
- <button class="btn btn-default" id="info_button_{{ cat.cat_id }}"><span class="glyphicon glyphicon-plus-sign"/></button>
- </a>
- </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 {% if readonly %}disabled{% endif %}">
- <span class="glyphicon glyphicon glyphicon-pencil"/>
- </a>
- </td>
- {% endif %}
- {% if (cat.state == "untouched") %}
- <td class="col-md-1 text-center">
- <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 {% if readonly %}disabled{% endif %}" type="submit"><span class="glyphicon glyphicon-trash"/></button>
- </a>
- </form>
- </td>
- {% elif (cat.state == "created" or cat.state == "modified") %}
- <td class="col-md-1 text-center">
- <form method="POST" action="{{url_for('cat_recap', deleted_modifs_id=cat.cat_id, repository=current_repository)}}" class="form-inline">
- <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
- <a title="Supprimer changements">
- <button class="btn btn-default"><span class="glyphicon glyphicon-remove-sign"/></button>
- </a>
- </form>
- </td>
- {% else %}
- <td colspan="2">
- <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="Restaurer catégorie">
- <button class="btn btn-default" type="submit">Restaurer</button>
- </a>
- </form>
- </td>
- {% endif %}
- </tr>
- <tr
- {% if cat.state == "created" %}
- class="success"
- {% elif cat.state == "modified" %}
- class="warning"
- {% elif cat.state == "deleted" %}
- class="danger"
- {% endif %}>
- <td colspan="5">
- <div id="properties_{{cat.cat_id}}">
- <dl class="dl-horizontal">
- {% if cat.cat_properties|length == 0 %} <dt></dt><dd>Aucune autre propriété</dd>
- {% else %}
- {% for (predicate, object) in cat.cat_properties %}
- <dt>{{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}</dt>
- <dd>
- {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %}
- {% for cat in cat_list %}
- {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %}
- {{ cat.cat_label }}
- {% endif %}
- {% endfor %}
- {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %}
- <a href="{{ object }}">{{ object }}</a>
- {% else %}
- {{ object }}
- {% endif %}
- </dd>
- {% endfor %}
- {% endif %}
- </dl>
- </div>
- </td>
- </tr>
- {% endfor %}
- {% endif %}
- {% endif %}
- </tbody>
- </table>
- {% if session.get("user_logged") %}
- <form method="POST" action="{{url_for('cat_recap', repository=current_repository)}}" class="form-inline">
- <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
- <h4> Annuler tous mes changements actuels :
- <a title="Supprimer changements">
- <button type="submit" class="btn btn-default" {% if readonly %}disabled{% endif %}>
- <span class="glyphicon glyphicon-remove"/>
- </button>
- </a>
- </h4>
- </form>
- <h4> Soumettre mes changements actuels : <a href="{{ url_for('cat_modifs', repository=current_repository)}}" title="Soumettre changements" class="btn btn-default" {% if readonly %}disabled{% endif %}><span class="glyphicon glyphicon-share"/></a>
- </h4>
- {% endif %}
- <h3> Consulter l'historique des modifications : <a href="https://github.com/{{config['PERSISTENCE_CONFIG']['REPOSITORY_OWNER']}}/{{current_repository}}/commits/master" title="Aller à l'historique des modifications sur Github" class="btn btn-default"><span class="glyphicon glyphicon-list"/></a></h3>
- <h3> Consulter les discussions générales sur cet ensemble de catégorie (Issues) : <a href="https://github.com/{{config['PERSISTENCE_CONFIG']['REPOSITORY_OWNER']}}/{{current_repository}}/issues" title="Aller à la page de discussion sur l'ensemble de catégories" class="btn btn-default"><span class="glyphicon glyphicon-comment"/></a></h3>
-{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/home/index.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,86 @@
+{% extends "layout.html" %}
+{% if not session["user_logged"] %}
+ {% set readonly="readonly" %}
+{% else %}
+ {% set readonly=False %}
+{% endif %}
+{% block title %}Page d'accueil{% endblock title %}
+{% block head %}
+ {{ super() }}
+{% endblock head %}
+{% block navbar_items %}
+ <li class="active"><a>Page d'accueil</a></li>
+{% endblock navbar_items %}
+{% block repo_list %}
+ <option value="" selected="selected"> Sélectionnez ensemble... </option>
+ {{ super() }}
+{% endblock repo_list %}
+{% block page_content %}
+ <h2> <b>CatEdit</b>: Editeur de Catégories</h2>
+ <h3> Mode d'emploi </h3>
+ <div class="col-md-8">
+ <p>
+ CatEdit est un outil permettant de créer et d'éditer des catégories.
+ Vous aurez besoin d'un compte Github pour utiliser CatEdit:
+ <a href="https://github.com/">s'inscrire sur Github</a>
+ </p>
+ <p>
+ Une fois authentifié, choisissez un ensemble de catégorie dans la liste se trouvant
+ dans la barre de navigation pour être redirigé vers l'atelier correspondant.
+ Vous pourrez y trouver la liste des catégories existantes pour cet ensemble,
+ présentée dans un tableau qui initialement ne comprend que les catégories de l'état courant.
+ </p>
+ <p>
+ L'état courant de l'ensemble de catégorie est la base à partir de laquelle
+ vous pourrez créer et éditer des catégories. Une catégorie consiste en un label, unique
+ et non-vide, une description, unique et non-vide et un nombre variable de propriétés.
+ </p>
+ <h4><b>La liste de catégories</b></h4>
+ <p>
+ A chaque fois que vous faites des modifications, elles sont stockées
+ de manière temporaire tant que vous restez authentifié. La liste de catégories
+ dans l'Atelier est mise à jour au fil de vos modifications selon un code couleur
+ vous permettant de visualiser vos changements.
+ </p>
+ <ul>
+ <li>
+ Une ligne de tableau <b>blanche</b> représente une catégorie de l'état courant
+ que vous n'avez ni modifié ni supprimé.
+ </li>
+ <li>
+ Une ligne de tableau <b class="text-success">verte</b> représente une catégorie que vous avez créée de zéro
+ et qui n'existait pas précédemment dans l'état courant.
+ </li>
+ <li>
+ Une ligne de tableau <b class="text-warning">orange</b> représente une catégorie qui existait
+ initialement et que vous avez modifié.
+ </li>
+ <li>
+ Une ligne de tableau <b class="text-danger">rouge</b> représente une catégorie de l'état courant que
+ vous avez supprimé.
+ </li>
+ </ul>
+ </p>
+ <p>
+ Pour que vos modifications soient permanentes et qu'elles deviennent le nouvel état courant
+ de l'ensemble de catégories, vous devez soumettre vos modifications en suivant le lien dans l'Atelier.
+ Vous trouverez une page vous présentant l'état initial d'une part et vos changements d'autre part.
+ </p>
+ <p>
+ Vous devez obligatoirement renseigner une explication de vos changements avant soumission.
+ </p>
+ <h3> Interface de discussion </h3>
+ <p>
+ Chaque soumission de modifications ouvrira sur GitHub un fil de discussion ayant pour titre le message
+ de soumission. Vous pouvez accéder à la liste de changements via un lien dans l'Atelier. Cela vous
+ redirigera vers la page GitHub présentant tous les changements, et vous pouvez accéder à la discussion
+ d'un changement donné en cliquant sur son titre.
+ </p>
+ <p>
+ Il est aussi possible de créer des discussions générales sur un ensemble de catégories. Un lien est disponible
+ dans l'atelier vers la page "Issues" de l'ensemble de catégories sur GitHub, où vous pouvez commenter et créer
+ de nouvelles "Issues" pour alimenter la discussion sur l'ensemble de catégories considéré.
+ </p>
+ <br><br>
+ </div>
+{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/home/login.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,67 @@
+{% extends "layout.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() }}
+ <li><a class="navbar-decorative">></a></li>
+ <li class="active"><a>Authentification</a></li>
+{% 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('home.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('home.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%}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/layout.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+ {% block head %}
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>{% block title %}{% endblock title %}</title>
+ <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
+ <link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
+ {% endblock head %}
+</head>
+<body>
+ <div class="navbar navbar-inverse" role="navigation">
+ <div class="container">
+ <div class="navbar-header">
+ <a class="navbar-brand" href="{{ url_for('home.index') }}">
+ <img alt="Brand" src="{{ url_for('static', filename='img/catedit_brand.png') }}" class="navbar-img" width="32" height="32">
+ </a>
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <div class="collapse navbar-collapse">
+ <ul class="nav navbar-nav">
+ {% block navbar_items %}
+ <li><a href="{{ url_for('home.index') }}">Page d'accueil</a></li>
+ {% endblock navbar_items %}
+ </ul>
+ <div class="navbar-text navbar-right">
+ {% if not session.get("user_logged", None)%} Non authentifié - <a href="{{ url_for('home.login') }}" class="navbar-link">S'authentifier</a>
+ {% else %} Authentifié: {{ session["user_login"] }} - <a href="{{ url_for('home.logout') }}" class="navbar-link">Quitter</a>{% endif %}
+ </div>
+ {% if session["user_logged"] %}
+ <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 session.get("user_repositories", []) %}
+ <option value="{{url_for('categories.workshop', repository=repo)}}" {% if repo==current_repository %}selected="selected"{% endif %}>{{repo}}</option>
+ {% endfor %}
+ {% endblock repo_list %}
+ </select>
+ </form>
+ {% endif %}
+ </div>
+ </div>
+ </div>
+ <div class="container">
+ {% block page_content %}
+ {% endblock page_content %}
+ </div>
+ <footer class="footer">
+ <div class="text-right footer-notes">
+ CatEdit - Prototype v{{config["CURRENT_VERSION"]}}
+ <br><br>
+ </div>
+ </footer>
+</body>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/macros.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,104 @@
+{% macro category_table(cat_list, current_repository, state_list=["mixed"], target="", interactive=True) -%}
+ {% for cat in cat_list %}
+ {% if state_list == ["mixed"] or (cat.state in state_list) %}
+ <tr {% if cat.state == "created" %}
+ class="success"
+ {% elif cat.state == "modified" %}
+ class="warning"
+ {% elif cat.state == "deleted" %}
+ class="danger"
+ {% endif %}>
+ <td class="col-md-2">{{ cat.cat_label }}</td>
+ <td class="col-md-7">{{ cat.cat_description}}</td>
+ <td class="col-md-1 text-center">
+ <a title="Détails catégorie">
+ <button class="btn btn-default cat-info-toggle" id="info_button_{% if ((cat.state != 'untouched') and (cat.state != 'original')) %}edited_{% endif %}{{ cat.cat_id }}"><span class="glyphicon glyphicon-plus-sign"/></button>
+ </a>
+ </td>
+ {% if interactive %}
+ {% if (cat.state != "deleted") %}
+ <td class="col-md-1 text-center">
+ <a href="{{ url_for('categories.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>
+ {% endif %}
+ {% if (cat.state == "untouched") or (cat.state == "original") %}
+ <td class="col-md-1 text-center">
+ <a title="Supprimer catégorie">
+ <button class="btn btn-default cat-delete-toggle {% if readonly %}disabled{% endif %}" id="delete_button_{{ cat.cat_id }}"><span class="glyphicon glyphicon-trash"/></button>
+ </a>
+ </td>
+ {% elif (cat.state == "created" or cat.state == "modified") %}
+ <td class="col-md-1 text-center">
+ <a title="Supprimer changements">
+ <button class="btn btn-default cat-delete-toggle {% if readonly %}disabled{% endif %}" id="delete_button_edited_{{ cat.cat_id }}"><span class="glyphicon glyphicon-remove-sign"/></button>
+ </a>
+ </td>
+ {% else %}
+ <td colspan="2" class="text-center">
+ <form method="POST" action="{{url_for(target, deleted_cat_id=cat.cat_id, repository=current_repository)}}" class="form-inline">
+ <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
+ <a title="Restaurer catégorie">
+ <button class="btn btn-default" type="submit">Restaurer</button>
+ </a>
+ </form>
+ </td>
+ {% endif %}
+ {% endif %}
+ </tr>
+ <tr
+ {% if cat.state == "created" %}
+ class="success"
+ {% elif cat.state == "modified" %}
+ class="warning"
+ {% elif cat.state == "deleted" %}
+ class="danger"
+ {% endif %}>
+ <td colspan="5">
+ <div class="cat-info-div" id="properties_{% if (cat.state != 'untouched') and (cat.state != 'original') %}edited_{% endif %}{{cat.cat_id}}">
+ <dl class="dl-horizontal">
+ {% if cat.cat_properties|length == 0 %} <dt></dt><dd>Aucune autre propriété</dd>
+ {% else %}
+ {% for (predicate, object) in cat.cat_properties %}
+ <dt>{{ config["PROPERTY_LIST"][predicate]["descriptive_label_fr"] }}</dt>
+ <dd>
+ {% if config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-category" %}
+ {% for cat in cat_list %}
+ {% if object == config["CATEGORY_NAMESPACE"]+cat.cat_id %}
+ {{ cat.cat_label }}
+ {% endif %}
+ {% endfor %}
+ {% elif config["PROPERTY_LIST"][predicate]["object_type"]=="uriref-link" %}
+ <a href="{{ object }}">{{ object }}</a>
+ {% else %}
+ {{ object }}
+ {% endif %}
+ </dd>
+ {% endfor %}
+ {% endif %}
+ </dl>
+ </div>
+ {% if interactive %}
+ {% if cat.state != "deleted" %}
+ <div class="cat-delete-div" id="delete_confirm_{% if ((cat.state != 'untouched') and (cat.state != 'original')) %}edited_{% endif %}{{cat.cat_id}}">
+ <form method="POST" action=
+ "{% if cat.state == 'modified' %}
+ {{url_for(target, deleted_changes_id=cat.cat_id, repository=current_repository)}}
+ {% else %}
+ {{url_for(target, deleted_cat_id=cat.cat_id, repository=current_repository)}}
+ {% endif %}" class="form-inline">
+ <label> Vous allez supprimer {% if (cat.state == "modified") or (cat.state == "created") %} les changements sur {% endif %} la catégorie. </label>
+ <input name="csrf_token" value="{{ csrf_token() }}" type="hidden">
+ <a title="Supprimer {% if cat.state == 'modified' %}changements{% else %}catégorie{% endif %}">
+ <button class="btn btn-default {% if readonly %}disabled{% endif %}" type="submit">Confirmer suppression</button>
+ </a>
+ </form>
+ </div>
+ {% endif %}
+ {% endif %}
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+{%- endmacro %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/social/changeset.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,74 @@
+{% extends "social/comment_thread_layout.html" %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
+ {% set readonly="readonly" %}
+{% else %}
+ {% set readonly=False %}
+{% endif %}
+{% block title %}{{super()}}{% endblock title %}
+{% block head %}
+ {{super()}}
+ <script src="{{ url_for('static', filename='js/jquery-1.11.1.min.js') }}" language="Javascript" type="text/javascript"></script>
+ <script>
+ $(document).ready(function(){
+ $(".cat-info-div").hide();
+ $(".cat-table-toggle").click(function(evt){
+ $(".cat-table").slideToggle(function(){
+ $(".cat-table-toggle").children().toggleClass("glyphicon-chevron-up");
+ $(".cat-table-toggle").children().toggleClass("glyphicon-chevron-down");
+ });
+ });
+ $(".cat-info-toggle").click(function(evt){
+ $("#properties_"+evt.target.id.split('_').slice(2,5).join('_')).slideToggle(function(){
+ $("#info_button_"+evt.target.id.split('_').slice(2,5).join('_')).children().toggleClass("glyphicon-plus-sign");
+ $("#info_button_"+evt.target.id.split('_').slice(2,5).join('_')).children().toggleClass("glyphicon-minus-sign");
+ });
+ });
+ });
+ </script>
+{% endblock head%}
+{% block navbar_items %}
+ {{super()}}
+ <li><a class="navbar-decorative">></a></li>
+ <li class="active"><a>Discussion: changements</a></li>
+{% endblock navbar_items %}
+{% block additional_content %}
+<h3><strong>Etat de l'ensemble des catégories pour ces modifications </strong>
+ <a title="Afficher/Cacher tableau" class="btn btn-default cat-table-toggle">
+ <span class="glyphicon glyphicon-chevron-up"/>
+ </a>
+</h3>
+<div class="cat-table">
+ <table class="table table-condensed table-bordered">
+ <thead>
+ <tr class="active">
+ <th class="col-md-2"><b>Nom de la catégorie</b></th>
+ <th class="col-md-10" colspan="2"><b>Description de la catégorie</b></th>
+ </tr>
+ </thead>
+ <tbody>
+ {% if not session["user_logged"] %}
+ <tr>
+ <td class="col-md-12" colspan="2">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ <span class="sr-only">Attention:</span>
+ Veuillez vous identifier pour visualiser les catégories
+ </td>
+ </tr>
+ {% else %}
+ {% if cat_list|length == 0 %}
+ <tr>
+ <td class="col-md-12" colspan="2">Aucune catégorie n'existait suite à ces changements. {% if not readonly %}<a href="{{ url_for('categories.editor', repository=current_repository) }}">Créer une catégorie</a>{% endif %}</td>
+ </tr>
+ {% else %}
+ {% import "macros.html" as macros %}
+ {{ macros.category_table(cat_list, current_repository, state_list=["original"], interactive=False) }}
+ {% endif %}
+ {% endif %}
+ </tbody>
+ </table>
+</div>
+{% endblock additional_content %}
+{% block comment_posting_target %}{{url_for("social.changeset", changeset_id=changeset_id, repository=current_repository)}}{% endblock %}
+{% block page_content %}
+ {{super()}}
+{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/social/comment_thread_layout.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,76 @@
+{% extends "layout.html" %}
+{% block title %}{{ current_repository }}: Discussion{% endblock title %}
+{% block head %}
+ {{ super ()}}
+{% endblock head %}
+{% block navbar_items %}
+ {{ super() }}
+ {% if session.get("user_logged", None) %}
+ <li><a class="navbar-decorative">></a></li>
+ <li><a href="{{ url_for('categories.workshop', repository=current_repository) }}">Atelier</a></li>
+ <li><a class="navbar-decorative">></a></li>
+ <li><a href="{{ url_for('social.index', repository=current_repository) }}">Social</a></li>
+ {% endif %}
+{% endblock navbar_items %}
+{% block page_content %}
+<h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
+{% block additional_content %}
+{% endblock additional_content %}
+<h3><strong>Discussion</strong></h3>
+ <small></small>
+</h3>
+{% if comment_form.comment_field.errors %}
+<div class="alert alert-danger">
+ <strong>
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ Erreur:
+ </strong>
+ Votre commentaire est vide.
+</div>
+{% endif %}
+<table class="table table-striped">
+ <theader>
+ <tr class="info">
+ <th class="col-md-2"> {{comments["author"]}} </td>
+ <th class="col-md-2"> {{comments["opening_date"]}} </td>
+ <th class="col-md-8"> <strong>Titre: {{comments["title"]}}</strong> </td>
+ </tr>
+ <tr>
+ <td colspan="2"/>
+ <td>{{comments["opening_post"]}}</td>
+ </tr>
+ </theader>
+ <tbody>
+ {% if comments["comment_list"]|length == 0 %}
+ <tr>
+ <td colspan="3"> Aucun commentaire n'a été posté pour le moment </td>
+ </tr>
+ {% else %}
+ {% for comment in comments["comment_list"] %}
+ <tr>
+ <td class="col-md-2"> {{comment["author"]}} </td>
+ <td class="col-md-2"> {{comment["date"]}} </td>
+ <td class="col-md-8"> {{comment["body"]}} </td>
+ </tr>
+ {% endfor %}
+ {% endif %}
+ <tr>
+ <td colspan="2" class="text-right">
+ {{ comment_form.comment_field.label }}
+ </td>
+ <td>
+ <form method="POST" action="{% block comment_posting_target %}{% endblock %}">
+ <fieldset {% if readonly %}disabled{% endif %}>
+ {{ comment_form.hidden_tag() }}
+ <div class="form-group">
+ {{ comment_form.comment_field(class="form-control", readonly=readonly) }}
+ </div>
+ <button type="submit" class="btn btn-default">Envoyer commentaire</button>
+ <a href="{{ url_for('social.index', repository=current_repository)}}"class="btn btn-default">Retour</a>
+ </fieldset>
+ </form><br>
+ </td>
+ </tr>
+ </tbody>
+</table>
+{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/social/discussion.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,48 @@
+{% extends "social/comment_thread_layout.html" %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
+ {% set readonly="readonly" %}
+{% else %}
+ {% set readonly=False %}
+{% endif %}
+{% block title %}{{super()}}{% endblock title %}
+{% block head %}
+ {{super()}}
+{% endblock head%}
+{% block navbar_items %}
+ {{super()}}
+ <li><a class="navbar-decorative">></a></li>
+ <li class="active"><a>Discussion</a></li>
+{% endblock navbar_items %}
+{% block comment_posting_target %}{{url_for("social.discussion", discussion_id=discussion_id, repository=current_repository)}}{% endblock %}
+{% block page_content %}
+ {% if discussion_id == "new" %}
+ <h2><b>CatEdit</b> - <small>{{current_repository}}</small></h2>
+ <h3>Nouvelle discussion</h3>
+ {% if comment_form.comment_field.errors or comment_form.discussion_title.errors %}
+ <div class="alert alert-danger">
+ <strong>
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ Erreur:
+ </strong>
+ Vous devez renseigner un titre pour votre discussion.
+ </div>
+ {% endif %}
+ <form method="POST" action="{{url_for('social.discussion', discussion_id='new', repository=current_repository)}}">
+ <fieldset {% if readonly %}disabled{% endif %}>
+ {{ comment_form.hidden_tag() }}
+ <div class="form-group">
+ <br>
+ {{ comment_form.discussion_title.label }}
+ {{ comment_form.discussion_title(class="form-control", readonly=readonly) }}
+ <br>
+ {{ comment_form.comment_field.label }}
+ {{ comment_form.comment_field(class="form-control", readonly=readonly) }}
+ </div>
+ <button type="submit" class="btn btn-default">Enregistrer</button>
+ <a href="{{ url_for('social.index', repository=current_repository)}}"class="btn btn-default">Retour</a>
+ </fieldset>
+ </form>
+ {% else %}
+ {{super()}}
+ {% endif %}
+{% endblock page_content %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/templates/social/index.html Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,99 @@
+{% extends "layout.html" %}
+{% if not session["user_logged"] or not session["user_can_edit"][current_repository] %}
+ {% set readonly="readonly" %}
+{% else %}
+ {% set readonly=False %}
+{% endif %}
+{% block title %} {{ current_repository}}: Espaces de discussion {% endblock title %}
+{% block head %}
+ {{ super() }}
+{% endblock head %}
+{% block navbar_items %}
+ {{ super() }}
+ {% if session.get("user_logged", None) %}
+ <li><a class="navbar-decorative">></a></li>
+ <li><a href="{{ url_for('categories.workshop', repository=current_repository) }}">Atelier</a></li>
+ <li><a class="navbar-decorative">></a></li>
+ <li class="active"><a>Social</a></li>
+ {% endif %}
+{% endblock navbar_items %}
+{% block repo_list %}
+ {{ super() }}
+{% endblock repo_list %}
+{% block page_content %}
+ <h2> <b>CatEdit</b> - <small>{{current_repository}}</small></h2>
+ {% if readonly %}
+ <div class="alert alert-warning" role="alert">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+ <span class="sr-only">Attention:</span>
+ Vous n'avez pas accès en écriture au repository contenant les catégories - Vous ne pourrez pas poster de commentaires
+ </div>
+ {% endif %}
+ <h3>Historique des modifications</h3>
+ <table class="table table-condensed">
+ <thead>
+ <tr>
+ <th>Auteur</th>
+ <th>Date</th>
+ <th>Message de soumission</th>
+ <th>Commentaires</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for changeset in changeset_list %}
+ <tr>
+ <td class="col-md-1">{{changeset["author"]}}</td>
+ <td class="col-md-2">{{changeset["date"]}}</td>
+ <td class="col-md-6">{{changeset["title"]}}</td>
+ <td class="col-md-2">{{changeset["comment_count"]}}</td>
+ <td class="col-md-1">
+ <a href="{{ url_for('social.changeset', repository=current_repository, changeset_id=changeset['id'])}}" title="Voir modifications" class="btn btn-default">
+ <span class=" glyphicon glyphicon-log-in"/>
+ </a>
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ <h3>Discussions générales</h3>
+ <h4> Créer une nouvelle discussion :
+ <a href="{{ url_for('social.discussion', repository=current_repository, discussion_id='new')}}" title="Créer discussion" class="btn btn-default">
+ <span class=" glyphicon glyphicon-plus"/>
+ </a>
+ </h4>
+ <table class="table table-condensed">
+ <thead>
+ <tr>
+ <th>Auteur</th>
+ <th>Date</th>
+ <th>Titre</th>
+ <th>Commentaires</th>
+ <th>Dernier commentaire</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% if not discussion_list %}
+ <tr>
+ <td colspan="">
+ Il n'y a pas encore de discussion pour cette ensemble de catégories. <a href="#">Créer une discussion</a>
+ </td>
+ </tr>
+ {% else %}
+ {% for discussion in discussion_list %}
+ <tr>
+ <td class="col-md-1">{{discussion["author"]}}</td>
+ <td class="col-md-2">{{discussion["opening_date"]}}</td>
+ <td class="col-md-5">{{discussion["title"]}}</td>
+ <td class="col-md-1">{{discussion["comment_count"]}}</td>
+ <td class="col-md-2">{{discussion["last_updated"]}}</td>
+ <td class="col-md-1">
+ <a href="{{ url_for('social.discussion', repository=current_repository, discussion_id=discussion['id'])}}" title="Voir la discussion" class="btn btn-default">
+ <span class=" glyphicon glyphicon-log-in"/>
+ </a>
+ </td>
+ </tr>
+ {% endfor %}
+ {% endif %}
+ </tbody>
+ </table>
+{% endblock page_content%}
--- a/src/catedit/version.py Fri Jan 30 12:41:40 2015 +0100
+++ b/src/catedit/version.py Fri Feb 13 17:22:21 2015 +0100
@@ -6,7 +6,7 @@
__all__ = ["VERSION", "get_version", "CURRENT_VERSION"]
-VERSION = (0, 1, 3)
+VERSION = (0, 1, 4)
def get_version():
"""
--- a/src/catedit/views.py Fri Jan 30 12:41:40 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,717 +0,0 @@
-"""
-views.py:
-The views functions that handle the front-end of the application
-"""
-
-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, PasswordField, \
- FieldList, HiddenField
-from wtforms.validators import DataRequired, Optional, AnyOf
-from rdflib import Graph
-from io import StringIO
-
-logger = app.logger
-
-@app.route('/', methods=['GET'])
-@app.route('/index', methods=['GET'])
-def cat_index():
- """
- View for index page (for now it's only a readme text so no computing
- is required)
- """
- return render_template("catindex.html")
-
-
-@app.route('/<string:repository>/catrecap',
- defaults={'deleted_cat_id': None, 'deleted_modifs_id': None},
- methods=['GET', 'POST'])
-@app.route('/<string:repository>/catrecap/delete-modifs-<deleted_modifs_id>',
- defaults={'deleted_cat_id': None},
- methods=['POST'])
-@app.route('/<string:repository>/catrecap/delete-<deleted_cat_id>',
- defaults={'deleted_modifs_id': None},
- methods=['POST'])
-def cat_recap(repository, deleted_cat_id, deleted_modifs_id):
- """
- View that has a list of all categories available. Template is
- catrecap.html, located in src/templates/
-
- Note: it also handles category deletion from the same page.
- """
- if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
- abort(404)
-
- cat_api_instance = CategoryAPI()
- cat_changes_api_instance = CategoryChangesAPI()
-
- if request.method == "POST":
- if (session.get("user_logged", None) is not None and
- session.get("user_can_edit", False) is not False):
- if deleted_modifs_id is None and deleted_cat_id is None:
- cat_changes_api_instance.delete(repository=repository)
- elif deleted_modifs_id is not None:
- logger.debug("Changes for category "
- + deleted_modifs_id
- + " will be deleted.")
- cat_changes_api_instance.delete(
- repository=repository,
- modified_cat_id=deleted_modifs_id
- )
- # We identify if we want to delete a category
- elif deleted_cat_id is not None:
- logger.debug("Category "
- + deleted_cat_id
- + " will be deleted.")
- cat_api_instance.delete(
- repository=repository,
- deleted_cat_id=deleted_cat_id
- )
- return redirect(url_for('cat_recap', repository=repository))
- elif request.method == "GET":
-
- deleted_cat_dict = {}
- modified_cat_dict = {}
- serialized_cat_list = []
- if session.get("user_logged", None) is not None:
- 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 = []
- for serialized_cat in serialized_cat_list:
- cat_rdf_graph = Graph()
- cat_rdf_graph.parse(source=StringIO(serialized_cat),
- format='turtle')
- original_cat_list.append(Category(graph=cat_rdf_graph))
-
- edited_cat_list = []
- for modified_cat_name in modified_cat_dict.keys():
- new_cat_rdf_graph = Graph()
- new_cat_rdf_graph.parse(
- source=StringIO(
- modified_cat_dict[modified_cat_name]
- ),
- format='turtle'
- )
- edited_cat_list.append(Category(graph=new_cat_rdf_graph))
- # first we find the untouched, edited and deleted categories
- cat_state = ""
- for category in original_cat_list:
- if category.cat_id not in modified_cat_dict.keys():
- if category.cat_id in deleted_cat_dict.keys():
- cat_state = "deleted"
- else:
- cat_state = "untouched"
-
- cat_list.append({"cat_label": category.label,
- "cat_description": category.description,
- "cat_id": category.cat_id,
- "cat_properties": category.properties,
- "state": cat_state})
-
- # now we must find the not yet submitted categories that were created
- cat_state = ""
- logger.debug("Edited cat list: "
- + str([cat.label for cat in edited_cat_list])
- + " - 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]:
- cat_state = "created"
- else:
- cat_state = "modified"
- cat_list.append({"cat_label": category.label,
- "cat_description": category.description,
- "cat_id": category.cat_id,
- "cat_properties": category.properties,
- "state": cat_state})
-
- # logger.debug(c.properties)
- cat_list = sorted(cat_list, key=lambda cat: cat['cat_id'])
- return render_template('catrecap.html',
- 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},
- methods=['POST'])
-@app.route('/<string:repository>/catmodifs/delete-<deleted_cat_id>',
- defaults={'deleted_modifs_id': None},
- methods=['POST'])
-@app.route('/<string:repository>/catmodifs',
- defaults={'deleted_cat_id': None, 'deleted_modifs_id': None},
- methods=['GET', 'POST'])
-def cat_modifs(repository, deleted_cat_id, deleted_modifs_id):
- """
- View that handles all the visualisation of changes for a user's
- session, links to the editor forms, allows the users to cancel their
- own changes and submits all their changes.
- """
- if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
- abort(404)
-
- logger.debug(repository)
-
- changes_list = []
-
- existing_cat_list = []
- modified_cat_list = []
- created_cat_list = []
- total_cat_list = []
-
- deleted_cat_namelist = []
- modified_cat_namelist = []
-
- cat_api_instance = CategoryAPI()
- cat_changes_api_instance = CategoryChangesAPI()
-
- if deleted_cat_id is None and deleted_modifs_id is None:
-
- commit_form = CommitForm(request.form)
-
- # if it's a GET with no delete_cat_id or deleted_modifs_id, then we'll
- # display the changes
- if request.method == "GET":
- 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]
-
- cat_data = dict({})
-
- # Creating existing cat list
- 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)
-
- cat_data = {
- "cat_label": cat.label,
- "cat_description": cat.description,
- "cat_id": cat.cat_id,
- "cat_properties": cat.properties
- }
-
- existing_cat_list.append(cat_data)
- total_cat_list.append(cat_data)
- cat_data = dict({})
-
- # Creating modified and created cat lists
- for modified_cat_name in \
- changes_list.get("modified_categories", {}).keys():
- modified_cat_rdf_graph = Graph()
- modified_cat_rdf_graph.parse(
- source=StringIO(
- changes_list["modified_categories"]
- [modified_cat_name]
- ),
- format='turtle'
- )
- modified_cat = Category(graph=modified_cat_rdf_graph)
-
- cat_data = {
- "cat_label": modified_cat.label,
- "cat_description": modified_cat.description,
- "cat_id": modified_cat.cat_id,
- "cat_properties": modified_cat.properties
- }
- if modified_cat.cat_id in \
- [existing_cat["cat_id"]
- for existing_cat in existing_cat_list]:
- modified_cat_list.append(cat_data)
- else:
- created_cat_list.append(cat_data)
- total_cat_list.append(cat_data)
- cat_data = dict({})
-
- # Creating deleted cat list
- deleted_cat_namelist = list(
- changes_list["deleted_categories"].keys()
- )
- modified_cat_namelist = list(
- changes_list["modified_categories"].keys()
- )
- logger.debug("total:"+str(total_cat_list))
- logger.debug("existing:"+str(existing_cat_list))
- logger.debug("created:"+str(created_cat_list))
- logger.debug("modified:"+str(modified_cat_list))
- logger.debug("modified names:"+str(modified_cat_namelist))
- logger.debug("deleted names:"+str(deleted_cat_namelist))
-
- return render_template('catmodifs.html',
- total_cat_list=total_cat_list,
- existing_cat_list=existing_cat_list,
- created_cat_list=created_cat_list,
- modified_cat_list=modified_cat_list,
- modified_cat_namelist=modified_cat_namelist,
- deleted_cat_namelist=deleted_cat_namelist,
- commit_form=commit_form,
- current_repository=repository)
- # If it's a POST with no delete_cat_id and delete_modifs_id, then we
- # will submit all the stored changes
- elif request.method == "POST":
- if commit_form.validate_on_submit():
- cat_api_instance.put(repository=repository)
- cat_changes_api_instance.delete(repository=repository)
- return redirect(url_for('cat_recap', repository=repository))
-
- # One of the ids is not None (either deleting a category or a modification)
- else:
- # We only do that if we have a POST
- if request.method == "POST":
- # We identify if we want to delete a change
- if (session.get("user_logged", None) is not None) \
- and \
- (session.get("user_can_edit", False) is not False):
- if deleted_modifs_id is not None:
- cat_changes_api_instance.delete(
- repository=repository,
- modified_cat_id=deleted_modifs_id
- )
- # We identify if we want to delete a category
- elif deleted_cat_id is not None:
- cat_api_instance.delete(
- repository=repository,
- deleted_cat_id=deleted_cat_id
- )
- 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>',
- methods=['GET', 'POST'])
-def cat_editor(repository, cat_id=None):
- """
- View that handles creation and edition of categories. Template is
- cateditor.html, located in src/templates
- """
- if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
- abort(404)
-
- cat_api_instance = CategoryAPI()
- cat_changes_api_instance = CategoryChangesAPI()
-
- specific_serialized_cat = ""
- # Serialization of the category of id cat_id, either from
- # CategoryChangesAPI (if it was modified) or from CategoryAPI (if it
- # was not)
-
- changes_response = None
- # Changes on the category cat_id we get from CategoryChangesAPI
-
- current_cat = None
- current_cat_id = None
- 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(
- repository=repository,
- modified_cat_id=cat_id
- )
- if changes_response[0]["type"] is "deleted":
- abort(404)
- elif changes_response[0]["type"] is "modified":
- specific_serialized_cat = changes_response[0] \
- ["category"] \
- [cat_id]
- elif changes_response[0]["type"] is "untouched":
- specific_serialized_cat = cat_api_instance.get(
- repository=repository,
- cat_id=cat_id
- )[0]
- logger.debug(specific_serialized_cat)
-
- cat_rdf_graph = Graph()
- cat_rdf_graph.parse(source=StringIO(specific_serialized_cat),
- format='turtle')
-
- current_cat = Category(graph=cat_rdf_graph)
- current_cat_id = current_cat.cat_id
- 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":
- """
- GET Method means we will display the editor
- """
- if current_cat is not None:
- 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": object_id
- })
- # 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}
- 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=list(deleted_cat_dict.keys()),
- current_repository=repository)
- elif request.method == "POST":
- """
- POST Method means we will compute the form data, call the relevant
- "POST" and "PUT" method on CategoryAPI, and return to a display
- page (catrecap for now, ideally the one from whence we came)
- """
- if (session.get("user_logged", None) is not None and
- session.get("user_can_edit", False) is not False):
-
- if cat_form.validate_on_submit():
- cat_data = {}
- cat_data["label"] = cat_form.label.data
- cat_data["description"] = cat_form.description.data
- cat_data["properties"] = [
- (cat_property["property_predicate"],
- cat_property["property_object"])
- for cat_property in cat_form.properties.data
- ]
-
- if cat_id is not None:
- logger.debug(str(cat_data))
- cat_api_instance.put(
- repository=repository,
- cat_id=cat_id,
- cat_data=cat_data
- )
- else:
- logger.debug(str(cat_data))
- cat_api_instance.post(
- repository=repository,
- cat_data=cat_data
- )
- return redirect(url_for('cat_recap', repository=repository))
- else:
- # if form doesn't validate we don't want to delete whatever
- # changes the user did
- return render_template('cateditor.html',
- cat_id=cat_id,
- cat_properties=current_cat_properties,
- form=cat_form,
- cat_list=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
- else:
- return redirect(url_for('cat_index'))
-
-
-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
-
- Note: If Persistence is set to PersistenceToFile (categories stored
- in local files, used for debugging), creates a mock user named
- "FileEditUser"
- """
- if not session.get("user_logged", False):
- session["modified_categories"] = {
- repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_LIST"]
- }
- session["deleted_categories"] = {
- repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_LIST"]
- }
- 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
-def github_callback(oauth_code):
- """
- Function that handles callback from Github after succesful login
- """
- session.permanent = False
- 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"]
- except GitHubError as ghe:
- logger.error(
- "GitHubError trying to get the user login"
- )
- 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"])
- user_repos_name = [repo["name"] for repo in repo_list]
- logger.debug(
- str(user_repos_name) + " "
- + str(app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_LIST"])
- )
- 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.error(
- "GitHubError trying to get the list of repository for user "
- + session["user_login"]
- )
- logger.error(ghe.response.text)
- return redirect(url_for('cat_index'))
-
-
-@github.access_token_getter
-def token_getter():
- """
- Utility function for github-flask module to get user token when
- making authenticated requests
- """
- if session.get("user_logged", None):
- # logger.debug("I made an authentified request")
- return session["user_code"]
-
-
-@app.route('/catedit-logout')
-def logout():
- """
- Function that manages authentication (Github), logout
-
- Note: if you want to switch github users, you will have to logout of
- 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_login"] = None
- session["user_can_edit"] = None
- session["modified_categories"] = {
- repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_LIST"]
- }
- session["deleted_categories"] = {
- repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
- ["REPOSITORY_LIST"]
- }
- return redirect(url_for('cat_index'))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/views/__init__.py Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,1 @@
+__all__ = ["home", "categories", "social"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/views/categories.py Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,494 @@
+"""
+categories.py:
+The views functions that handle category management and editing
+"""
+
+from catedit import app
+from catedit.models import Category
+from views.utils import check_user_status
+from flask import render_template, request, redirect, url_for, session, \
+ abort, Blueprint
+from flask_wtf import Form
+from catedit.resources import CategoryAPI, CategoryChangesAPI
+from wtforms import StringField, TextAreaField, FormField, \
+ FieldList, HiddenField
+from wtforms.validators import DataRequired, Optional, AnyOf
+from rdflib import Graph
+from io import StringIO
+
+module = Blueprint('categories', __name__)
+logger = app.logger
+
+def make_category_list(repository, without_changes=False):
+ """
+ Shortcut function that generates the list of category to use for
+ view templates.
+ Each category is a dict with the following format:
+ {
+ "cat_label": category label,
+ "cat_description": category description,
+ "cat_id": category id,
+ "cat_properties": category properties,
+ "state": category state (one of {"untouched", "created",
+ "edited", "deleted"})
+ }
+ """
+ cat_api_instance = CategoryAPI()
+ cat_changes_api_instance = CategoryChangesAPI()
+
+ deleted_cat_dict = {}
+ modified_cat_dict = {}
+ serialized_cat_list = []
+ if session.get("user_logged", None) is not None:
+ 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 = []
+ for serialized_cat in serialized_cat_list:
+ cat_rdf_graph = Graph()
+ cat_rdf_graph.parse(source=StringIO(serialized_cat),
+ format='turtle')
+ original_cat_list.append(Category(graph=cat_rdf_graph))
+
+ if without_changes:
+ for category in original_cat_list:
+ cat_list.append(
+ {
+ "cat_label": category.label,
+ "cat_description": category.description,
+ "cat_id": category.cat_id,
+ "cat_properties": category.properties,
+ "state": "original"
+ }
+ )
+ else:
+ edited_cat_list = []
+ for modified_cat_name in modified_cat_dict.keys():
+ new_cat_rdf_graph = Graph()
+ new_cat_rdf_graph.parse(
+ source=StringIO(
+ modified_cat_dict[modified_cat_name]
+ ),
+ format='turtle'
+ )
+ edited_cat_list.append(Category(graph=new_cat_rdf_graph))
+ # first we find the untouched, edited and deleted categories
+ cat_state = ""
+ for category in original_cat_list:
+ if category.cat_id not in modified_cat_dict.keys():
+ if category.cat_id in deleted_cat_dict.keys():
+ cat_state = "deleted"
+ else:
+ cat_state = "untouched"
+
+ cat_list.append(
+ {
+ "cat_label": category.label,
+ "cat_description": category.description,
+ "cat_id": category.cat_id,
+ "cat_properties": category.properties,
+ "state": cat_state
+ }
+ )
+
+ # now we must find the not yet submitted categories that were created
+ cat_state = ""
+ logger.debug("Edited cat list: "
+ + str([cat.label for cat in edited_cat_list])
+ + " - 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]:
+ cat_state = "created"
+ else:
+ cat_state = "modified"
+ cat_list.append({"cat_label": category.label,
+ "cat_description": category.description,
+ "cat_id": category.cat_id,
+ "cat_properties": category.properties,
+ "state": cat_state})
+ return cat_list
+
+@module.route('/<string:repository>/workshop',
+ defaults={'deleted_cat_id': None, 'deleted_changes_id': None},
+ methods=['GET', 'POST'])
+@module.route('/<string:repository>/workshop/delete-modifs-<deleted_changes_id>',
+ defaults={'deleted_cat_id': None},
+ methods=['POST'])
+@module.route('/<string:repository>/workshop/delete-<deleted_cat_id>',
+ defaults={'deleted_changes_id': None},
+ methods=['POST'])
+def workshop(repository, deleted_cat_id, deleted_changes_id):
+ """
+ View that has a list of all categories available. Template is
+ categories/workshop.html, located in src/templates/
+
+ Note: it also handles category deletion from the same page.
+ """
+ if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
+ abort(404)
+
+ check_user_status(repository)
+
+ cat_api_instance = CategoryAPI()
+ cat_changes_api_instance = CategoryChangesAPI()
+
+ if request.method == "POST":
+ if deleted_changes_id is None and deleted_cat_id is None:
+ cat_changes_api_instance.delete(repository=repository)
+ elif deleted_changes_id is not None:
+ logger.debug("Changes for category "
+ + deleted_changes_id
+ + " will be deleted.")
+ cat_changes_api_instance.delete(
+ repository=repository,
+ modified_cat_id=deleted_changes_id
+ )
+ # We identify if we want to delete a category
+ elif deleted_cat_id is not None:
+ logger.debug("Category "
+ + deleted_cat_id
+ + " will be deleted.")
+ cat_api_instance.delete(
+ repository=repository,
+ deleted_cat_id=deleted_cat_id
+ )
+ return redirect(url_for('categories.workshop', repository=repository))
+ elif request.method == "GET":
+
+ cat_list = make_category_list(repository=repository)
+ # logger.debug(c.properties)
+ cat_list = sorted(cat_list, key=lambda cat: cat['cat_id'])
+ return render_template('categories/workshop.html',
+ 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()]
+ )
+
+
+@module.route('/<string:repository>/'
+ + 'submit/'
+ + 'delete-changes-<deleted_changes_id>',
+ defaults={'deleted_cat_id': None},
+ methods=['POST'])
+@module.route('/<string:repository>/submit/delete-<deleted_cat_id>',
+ defaults={'deleted_changes_id': None},
+ methods=['POST'])
+@module.route('/<string:repository>/submit',
+ defaults={'deleted_cat_id': None, 'deleted_changes_id': None},
+ methods=['GET', 'POST'])
+def submit(repository, deleted_cat_id, deleted_changes_id):
+ """
+ View that handles all the visualisation of changes for a user's
+ session, links to the editor forms, allows the users to cancel their
+ own changes and submits all their changes.
+
+ Template is categories/submit.html, located in src/templates/
+ """
+ if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
+ abort(404)
+
+ check_user_status(repository)
+
+ cat_api_instance = CategoryAPI()
+ cat_changes_api_instance = CategoryChangesAPI()
+
+ logger.debug(
+ "submit page for " + repository + ", method is " + request.method
+ )
+
+ if deleted_cat_id is None and deleted_changes_id is None:
+
+ commit_form = CommitForm(request.form)
+
+ # If it's a POST with no delete_cat_id and delete_modifs_id, then we
+ # will submit all the stored changes
+ if request.method == "POST":
+ if commit_form.validate_on_submit():
+ cat_api_instance.put(repository=repository)
+ cat_changes_api_instance.delete(repository=repository)
+ return redirect(url_for('categories.workshop', repository=repository))
+
+ # if it's a GET with no delete_cat_id or deleted_changes_id, then we'll
+ # display the changes
+ elif request.method == "GET":
+ cat_list = make_category_list(repository=repository)
+ original_cat_list = make_category_list(
+ repository=repository,
+ without_changes=True
+ )
+
+ created_cat_count = len([
+ created_cat for created_cat in cat_list
+ if created_cat["state"] == "created"
+ ])
+ edited_cat_count = len([
+ edited_cat for edited_cat in cat_list
+ if edited_cat["state"] == "modified"
+ ])
+ deleted_cat_count = len([
+ deleted_cat for deleted_cat in cat_list
+ if deleted_cat["state"] == "deleted"
+ ])
+
+ return render_template('categories/submit.html',
+ cat_list=cat_list,
+ original_cat_list=original_cat_list,
+ original_cat_count=len(original_cat_list),
+ created_cat_count=created_cat_count,
+ edited_cat_count=edited_cat_count,
+ deleted_cat_count=deleted_cat_count,
+ commit_form=commit_form,
+ current_repository=repository)
+
+ # One of the ids is not None (either deleting a category or a modification)
+ else:
+ # We only do that if we have a POST
+ if (request.method == "POST"):
+ # We identify if we want to delete a change
+ if deleted_changes_id is not None:
+ cat_changes_api_instance.delete(
+ repository=repository,
+ modified_cat_id=deleted_changes_id
+ )
+ # We identify if we want to delete a category
+ elif deleted_cat_id is not None:
+ cat_api_instance.delete(
+ repository=repository,
+ deleted_cat_id=deleted_cat_id
+ )
+ return redirect(
+ url_for('categories.submit', repository=repository)
+ )
+ else:
+ return redirect(
+ url_for('home.index')
+ )
+
+
+
+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()])
+
+
+@module.route('/<string:repository>/editor',
+ defaults={'cat_id': None},
+ methods=['GET', 'POST'])
+@module.route('/<string:repository>/editor/<string:cat_id>',
+ methods=['GET', 'POST'])
+def editor(repository, cat_id):
+ """
+ View that handles creation and edition of categories. Template is
+ categories/editor.html, located in src/templates
+ """
+ if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
+ abort(404)
+
+ check_user_status(repository)
+
+ cat_api_instance = CategoryAPI()
+ cat_changes_api_instance = CategoryChangesAPI()
+
+ specific_serialized_cat = ""
+ # Serialization of the category of id cat_id, either from
+ # CategoryChangesAPI (if it was modified) or from CategoryAPI (if it
+ # was not)
+
+ changes_response = None
+ # Changes on the category cat_id we get from CategoryChangesAPI
+
+ current_cat = None
+ current_cat_id = None
+ 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:
+ changes_response = cat_changes_api_instance.get(
+ repository=repository,
+ modified_cat_id=cat_id
+ )
+ if changes_response[0]["type"] is "deleted":
+ abort(404)
+ elif changes_response[0]["type"] is "modified":
+ specific_serialized_cat = changes_response[0] \
+ ["category"] \
+ [cat_id]
+ elif changes_response[0]["type"] is "untouched":
+ category_api_response = cat_api_instance.get(
+ repository=repository,
+ cat_id=cat_id
+ )
+ if category_api_response == 404:
+ abort(404)
+ else:
+ specific_serialized_cat = cat_api_response[0]
+ logger.debug(specific_serialized_cat)
+
+ cat_rdf_graph = Graph()
+ cat_rdf_graph.parse(source=StringIO(specific_serialized_cat),
+ format='turtle')
+
+ current_cat = Category(graph=cat_rdf_graph)
+ current_cat_id = current_cat.cat_id
+ 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":
+ """
+ GET Method means we will display the editor
+ """
+ if current_cat is not None:
+ 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": object_id
+ })
+ # 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}
+ 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('categories/editor.html',
+ cat_id=current_cat_id,
+ cat_properties=current_cat_properties,
+ form=cat_form,
+ cat_list=cat_list,
+ deleted_cat_list=list(deleted_cat_dict.keys()),
+ current_repository=repository)
+ elif request.method == "POST":
+ """
+ POST Method means we will compute the form data, call the relevant
+ "POST" and "PUT" method on CategoryAPI, and return to a display
+ page (workshop for now, ideally the one from whence we came)
+ """
+ if cat_form.validate_on_submit():
+ cat_data = {}
+ cat_data["label"] = cat_form.label.data
+ cat_data["description"] = cat_form.description.data
+ cat_data["properties"] = [
+ (cat_property["property_predicate"],
+ cat_property["property_object"])
+ for cat_property in cat_form.properties.data
+ ]
+
+ if cat_id is not None:
+ logger.debug(str(cat_data))
+ cat_api_instance.put(
+ repository=repository,
+ cat_id=cat_id,
+ cat_data=cat_data
+ )
+ else:
+ logger.debug(str(cat_data))
+ cat_api_instance.post(
+ repository=repository,
+ cat_data=cat_data
+ )
+ return redirect(url_for('categories.workshop', repository=repository))
+ else:
+ # if form doesn't validate we don't want to delete whatever
+ # changes the user did
+ return render_template('categories/editor.html',
+ cat_id=cat_id,
+ cat_properties=current_cat_properties,
+ form=cat_form,
+ cat_list=cat_list,
+ deleted_cat_list=list(
+ deleted_cat_dict.keys()
+ ),
+ current_repository=repository)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/views/home.py Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,214 @@
+"""
+home.py:
+The views functions that handle authentication and index pages
+"""
+
+from catedit import app, github
+from views.utils import check_user_status
+from requests import get
+from requests.auth import HTTPBasicAuth
+from flask import render_template, request, redirect, url_for, \
+ session, Blueprint
+from flask.ext.github import GitHubError
+from flask_wtf import Form
+from wtforms import StringField, PasswordField
+from wtforms.validators import DataRequired
+
+module = Blueprint('home', __name__)
+logger = app.logger
+
+
+@module.route('/', methods=['GET'])
+@module.route('/index', methods=['GET'])
+def index():
+ """
+ View for index page (for now it's only a readme text so no computing
+ is required)
+ """
+ return render_template("home/index.html")
+
+
+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()]
+ )
+
+
+@module.route('/catedit-login', methods=["GET", "POST"])
+def login():
+ """
+ Function that manages authentication (Github), login
+
+ Note: If Persistence is set to PersistenceToFile (categories stored
+ in local files, used for debugging), creates a mock user named
+ "FileEditUser"
+ """
+ if not session.get("user_logged", False):
+ session["modified_categories"] = {
+ repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
+ ["REPOSITORY_LIST"]
+ }
+ session["deleted_categories"] = {
+ repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
+ ["REPOSITORY_LIST"]
+ }
+ if app.config["PERSISTENCE_CONFIG"]["METHOD"] == "PersistenceToGithub":
+ login_form = LoginForm(request.form)
+ if request.method == "GET":
+ # We'll render the login form
+ return render_template(
+ "home/login.html",
+ form=login_form,
+ )
+ elif request.method == "POST":
+ if login_form.validate_on_submit():
+ # We'll try to get the auth token for given username
+ 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(str(github.get("rate_limit")["resources"]))
+ 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(
+ "home/login.html",
+ form=login_form
+ )
+ else:
+ # we did get it, so we redirect to callback function
+ # to wrap up user auth
+ return redirect(url_for('home.login_callback'))
+ else:
+ # form didn't validate, so we send it back to user
+ return render_template(
+ "home/login.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('home.index'))
+ else:
+ return redirect(url_for('home.index'))
+
+@module.route('/catedit-login-confirm', methods=["GET", "POST"])
+def 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('home.login_callback', _external=True)
+ )
+ else:
+ return redirect(url_for('home.index'))
+
+@module.route('/catedit-callback')
+@github.authorized_handler
+def login_callback(oauth_code):
+ """
+ Function that handles callback from Github after succesful login
+ """
+ session.permanent = False
+ 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"]
+ except GitHubError as ghe:
+ logger.error(
+ "GitHubError trying to get the user login"
+ )
+ 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"])
+ user_repos_name = [repo["name"] for repo in repo_list]
+ logger.debug(
+ str(user_repos_name) + " "
+ + str(app.config["PERSISTENCE_CONFIG"]
+ ["REPOSITORY_LIST"])
+ )
+ 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.error(
+ "GitHubError trying to get the list of repository for user "
+ + session["user_login"]
+ )
+ logger.error(ghe.response.text)
+ return redirect(url_for('home.index'))
+
+@module.route('/catedit-logout')
+def logout():
+ """
+ Function that manages authentication (Github), logout
+
+ Note: if you want to switch github users, you will have to logout of
+ 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_login"] = None
+ session["user_can_edit"] = None
+ session["modified_categories"] = {
+ repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
+ ["REPOSITORY_LIST"]
+ }
+ session["deleted_categories"] = {
+ repo: {} for repo in app.config["PERSISTENCE_CONFIG"]
+ ["REPOSITORY_LIST"]
+ }
+ return redirect(url_for('home.index'))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/views/social.py Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,184 @@
+"""
+social.py:
+The views functions that handle comment display and posting
+"""
+
+from catedit import app, github, cache
+from views.utils import check_user_status, get_comments, \
+ post_comment, get_commits, get_issues, \
+ convert_github_date, get_category_list
+from flask import render_template, request, redirect, url_for, \
+ session, Blueprint
+from flask.ext.github import GitHubError
+from flask_wtf import Form
+from wtforms import TextAreaField, StringField
+from wtforms.validators import DataRequired
+
+module = Blueprint('social', __name__)
+logger = app.logger
+
+@module.route("/<string:repository>/social",
+ methods=["GET"])
+def index(repository):
+ """
+ View that displays every changeset and thread in general discussion
+ and links to every thread
+ """
+ if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
+ abort(404)
+
+ check_user_status(repository)
+
+ issues_data = []
+ commits_data = []
+
+ changeset_list = get_commits(repository)
+ discussion_list = get_issues(repository)
+
+ return render_template(
+ "social/index.html",
+ current_repository=repository,
+ discussion_list=discussion_list,
+ changeset_list=changeset_list
+ )
+
+class CommentForm(Form):
+ """
+ Custom form class for commiting changes
+ """
+ comment_field = TextAreaField(
+ "Poster un commentaire: ",
+ validators=[DataRequired()]
+ )
+
+
+@module.route("/<string:repository>/changesets/<string:changeset_id>",
+ methods=["GET", "POST"])
+def changeset(repository, changeset_id):
+ """
+ View that displays a snapshot of the repository as it was for a given
+ changeset, and the related discussion to this changeset. Allows
+ users to comment on it.
+
+ For re-building the old category set we use the tree and blob data:
+ 1) first we get the commit of id changeset_id
+ 2) we get the tree associated with this commit
+ 3) we get all the blobs referenced by this tree
+ 4) we build categories from the blobs contents
+ --> Will probably be incorporated into a function later on
+
+ """
+ if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
+ abort(404)
+
+ check_user_status(repository)
+
+ comment_form = CommentForm()
+ comments_list = get_comments({
+ "repository": repository,
+ "type": "commits",
+ "id": changeset_id
+ })
+ cat_list = get_category_list(repository, changeset_id)
+
+ if request.method == "GET":
+ return render_template(
+ "social/changeset.html",
+ cat_list=cat_list,
+ comments=comments_list,
+ changeset_id = changeset_id,
+ comment_form = comment_form,
+ current_repository=repository
+ )
+ elif request.method == "POST":
+ if comment_form.validate_on_submit():
+ comment_data = {
+ "repository": repository,
+ "type": "commits",
+ "thread_id": changeset_id,
+ "comment_body": comment_form.comment_field.data
+ }
+ return_id = post_comment(comment_data)
+ return redirect(url_for(
+ "social.changeset",
+ repository=repository,
+ changeset_id = return_id
+ ))
+ else :
+ return render_template(
+ "social/changeset.html",
+ cat_list=cat_list,
+ comments=comments_list,
+ changeset_id = changeset_id,
+ comment_form = comment_form,
+ current_repository=repository
+ )
+
+class NewDiscussionForm(Form):
+ """
+ Custom form class for commiting changes
+ """
+ discussion_title = StringField(
+ "Titre de la discussion: ",
+ validators=[DataRequired()]
+ )
+ comment_field = TextAreaField(
+ "Commentaire d'ouverture (facultatif): "
+ )
+
+@module.route("/<string:repository>/discussions/<string:discussion_id>",
+ methods=["GET", "POST"])
+def discussion(repository, discussion_id):
+ """
+ View that displays the discussion of a given id and allows users
+ to post comments on it.
+ """
+ if repository not in app.config["PERSISTENCE_CONFIG"]["REPOSITORY_LIST"]:
+ abort(404)
+
+ check_user_status(repository)
+
+ comment_form = None
+ if discussion_id == "new":
+ comment_form = NewDiscussionForm()
+ comments_list = []
+ else:
+ comment_form = CommentForm()
+ comments_list = get_comments({
+ "repository": repository,
+ "type": "issues",
+ "id": discussion_id
+ })
+
+ if request.method == "GET":
+ return render_template(
+ "social/discussion.html",
+ comments=comments_list,
+ comment_form=comment_form,
+ current_repository=repository,
+ discussion_id=discussion_id
+ )
+ elif request.method == "POST":
+ if comment_form.validate_on_submit():
+ comment_data = {
+ "repository": repository,
+ "type": "issues",
+ "thread_id": discussion_id,
+ "comment_body": comment_form.comment_field.data
+ }
+ if discussion_id == "new":
+ comment_data["thread_title"] = comment_form.discussion_title.data
+ return_id = post_comment(comment_data)
+ return redirect(url_for(
+ "social.discussion",
+ repository=repository,
+ discussion_id=return_id
+ ))
+ else:
+ return render_template(
+ "social/discussion.html",
+ comments=comments_list,
+ comment_form=comment_form,
+ current_repository=repository,
+ discussion_id=discussion_id
+ )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/catedit/views/utils.py Fri Feb 13 17:22:21 2015 +0100
@@ -0,0 +1,365 @@
+"""
+utils.py:
+Module that groups utility functions that are used by views, partly because
+most of them do requests to the Github API and as such must be cached
+"""
+
+from catedit import app, github, cache
+from catedit.models import Category
+from flask import redirect, url_for, session, abort
+from flask.ext.github import GitHubError
+from datetime import datetime
+from rdflib import Graph
+from base64 import b64decode
+from io import StringIO
+
+logger = app.logger
+
+def check_user_status(repository):
+ """
+ Function to call at the beginning of every view that would require
+ authentication and editing privilege to work properly (basically
+ everything save login and index page)
+ """
+ if not session.get("user_logged", None):
+ return redirect(url_for("home.index"))
+ if not session.get("user_can_edit", {}).get(repository, False):
+ return redirect(url_for("home.index"))
+
+@cache.memoize(timeout=3600)
+def get_comments(request_data):
+ """
+ Function that takes a dict with the following two values:
+ * type: either "issues" or "commits"
+ * id: the id of the issue of commit to get comments from
+ then builds a comment list with each comment being a dict with the
+ following format:
+ {
+ "author": author of the comment
+ "body": body of the comment
+ "date": date of the comment format dd/mm/yy hh:mm
+ }
+ """
+ github_comments_data = []
+
+ if request_data["type"] == "commits":
+ commits_list = get_commits(request_data["repository"])
+ if request_data["id"] not in [commit["id"] for commit in commits_list]:
+ abort(404)
+ elif request_data["type"] == "issues":
+ issues_list = get_issues(request_data["repository"])
+ if request_data["id"] not in [issue["id"] for issue in issues_list]:
+ abort(404)
+
+ try:
+ github_comments_data = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + request_data["repository"] + "/"
+ + request_data["type"] + "/"
+ + request_data["id"] + "/comments?per_page=100"
+ )
+
+ except GitHubError as ghe:
+ logger.error(
+ "Error trying to get comments with following data: "
+ + str(request_data)
+ )
+ logger.error(ghe.response.text)
+
+ comment_list = []
+ for comment in github_comments_data:
+ comment_dict = {
+ "author": comment["user"]["login"],
+ "body": comment["body"],
+ "date": convert_github_date(
+ comment["created_at"]
+ )
+ }
+ comment_list.append(comment_dict)
+
+ discussion_data = {}
+ try:
+ discussion_data = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + request_data["repository"] + "/"
+ + request_data["type"] + "/"
+ + request_data["id"]
+ )
+ except GitHubError as ghe:
+ logger.error(
+ "Error trying to get the or issue of id " + request_data["id"]
+ )
+ logger.error(
+ "endpoint: " + "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + request_data["repository"] + "/"
+ + request_data["type"] + "/"
+ + request_data["id"]
+ )
+ logger.error(ghe.response.text)
+
+ thread_author = ""
+ thread_opening_date = ""
+ thread_title = ""
+ thread_opening_post = ""
+
+ if request_data["type"] == "commits":
+ thread_author = discussion_data.get("author",{}).get("login", "")
+ thread_opening_date = convert_github_date(
+ discussion_data.get(
+ "commit",
+ {}
+ ).get(
+ "author",
+ {}
+ ).get("date","")
+ )
+ thread_title = discussion_data.get("commit",{}).get("message","")
+ elif request_data["type"] == "issues":
+ thread_author = discussion_data.get("user",{}).get("login", "")
+ thread_opening_date = convert_github_date(
+ discussion_data.get("created_at", "0001-01-01T00:00:00Z")
+ )
+ thread_title = discussion_data.get("title", "")
+ thread_opening_post = discussion_data.get("body", "")
+
+ logger.debug(str(github.get("rate_limit")["resources"]))
+ thread_dict = {
+ "author": thread_author,
+ "title": thread_title,
+ "opening_date": thread_opening_date,
+ "comment_list": comment_list,
+ "opening_post": thread_opening_post
+ }
+ return thread_dict
+
+
+def post_comment(request_data):
+ """
+ Function that posts a given comment to github.
+
+ * repository is the repository to post in
+ * type is either "issues" or "commits"
+ * thread_id is the id of the issue or the commit to comment
+ * thread_title is the title of the new issue, if we're posting a new
+ issue
+ * comment_body is the content of the comment
+ """
+ comment_data = {
+ "body": request_data["comment_body"]
+ }
+ return_id = ""
+ if request_data["thread_id"] != "new":
+ try:
+ github_response = github.post(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + request_data["repository"] + "/"
+ + request_data["type"] + "/"
+ + request_data["thread_id"]
+ + "/comments",
+ data=comment_data
+ )
+ return_id = request_data["thread_id"]
+ except GitHubError as ghe:
+ logger.error(
+ "Error posting comment with following data: "
+ + str(request_data)
+ )
+ logger.error(ghe.response.text)
+ else:
+ # We're posting a new issue
+ comment_data["title"] = request_data["thread_title"]
+ try:
+ github_response = github.post(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + request_data["repository"] + "/"
+ + request_data["type"],
+ data=comment_data
+ )
+ return_id = str(github_response["number"])
+ except GitHubError as ghe:
+ logger.error(
+ "Error posting new issue with following data: "
+ + str(request_data)
+ )
+ logger.error(ghe.response.text)
+ cache.clear()
+ return return_id
+
+@cache.memoize(timeout=3600)
+def get_commits(repository):
+ """
+ Fuction that get the list of commits for a given repository. Returns a
+ list of dict with the format:
+ {
+ id : commit id
+ title : commit message
+ author : commit author
+ comment_count : commit comments count
+ }
+ """
+ commits_data = []
+ try:
+ commits_data = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + repository
+ + "/commits?per_page=100"
+ )
+ except GitHubError as ghe:
+ logger.error("Error getting commits for repo " + repository)
+ logger.error(ghe.response.text)
+ changeset_list = [
+ {
+ "id": commit["sha"],
+ "title": commit["commit"]["message"],
+ "date": convert_github_date(
+ commit["commit"]["committer"]["date"],
+ ),
+ "author": commit["commit"]["committer"]["name"],
+ "comment_count": commit["commit"]["comment_count"],
+ }
+ for commit in commits_data
+ ]
+
+ logger.debug(str(github.get("rate_limit")["resources"]))
+ return changeset_list
+
+@cache.memoize(timeout=3600)
+def get_issues(repository):
+ """
+ Fuction that get the list of issues for a given repository. Returns a
+ list of dict with the format:
+ {
+ id: issue id
+ title: issue title
+ author: issue author
+ opening_date: issue opening date
+ last_updated: last update date
+ comment_count: comments count
+ }
+ """
+ issues_data = []
+ try:
+ issues_data = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + repository
+ + "/issues?per_page=100")
+ except GitHubError as ghe:
+ logger.error("Error getting issues for repo " + repository)
+ logger.error(ghe.response.text)
+
+ discussion_list = [
+ {
+ "id": str(issue["number"]),
+ "title": issue["title"],
+ "author": issue["user"]["login"],
+ "opening_date": convert_github_date(issue["created_at"]),
+ "last_updated": convert_github_date(issue["updated_at"]),
+ "comment_count": issue["comments"],
+ }
+ for issue in issues_data
+ ]
+
+ logger.debug(str(github.get("rate_limit")["resources"]))
+ return discussion_list
+
+@cache.memoize(timeout=3600)
+def get_category_list(repository, changeset_id):
+ """
+ Get the category list as it was following the changeset of
+ id changeset_id
+ """
+ commits_list = get_commits(repository)
+ if changeset_id not in [commit["id"] for commit in commits_list]:
+ abort(404)
+
+ # First step
+ commit_data = {}
+ try:
+ commit_data = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + repository + "/commits/"
+ + changeset_id
+ )
+ except GitHubError as ghe:
+ logger.error("Error trying to get the commit of id " + changeset_id)
+ logger.error(ghe.response.text)
+
+ logger.debug(str(github.get("rate_limit")["resources"]))
+
+ tree_sha = commit_data.get("commit", {}).get("tree", {}).get("sha", "")
+
+ # Second step
+ tree_data = {}
+ try:
+ tree_data = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"] + "/"
+ + repository + "/git/trees/"
+ + commit_data["commit"]["tree"]["sha"]
+ + "?recursive=1"
+ )
+ except GitHubError as ghe:
+ logger.error("Error trying to get the tree of sha " + tree_sha)
+ logger.error(ghe.response.text)
+
+ logger.debug(str(github.get("rate_limit")["resources"]))
+ logger.debug(tree_data)
+
+ # Third step and fourth step
+ cat_list = []
+ for blob in tree_data.get("tree", []):
+ if app.config["PERSISTENCE_CONFIG"]["CATEGORIES_PATH"] in blob["path"]:
+ blob_data = {}
+ try:
+ blob_data = github.get(
+ "repos/"
+ + app.config["PERSISTENCE_CONFIG"]["REPOSITORY_OWNER"]
+ + "/" + repository + "/git/blobs/"
+ + blob["sha"]
+ )
+ except GitHubError as ghe:
+ logger.error(
+ "Error trying to get the blob of sha " + blob["sha"]
+ )
+ logger.error(ghe.response.text)
+
+ cat_graph = Graph()
+ cat_graph.parse(
+ source=StringIO(
+ str(b64decode(blob_data["content"]), "utf-8")
+ ),
+ format="turtle"
+ )
+ category = Category(graph=cat_graph)
+ cat_list.append(
+ {
+ "cat_label": category.label,
+ "cat_description": category.description,
+ "cat_id": category.cat_id,
+ "cat_properties": category.properties,
+ "state": "original"
+ }
+ )
+
+ logger.debug(str(github.get("rate_limit")["resources"]))
+ return cat_list
+
+def convert_github_date(date):
+ """
+ Function that converts github date format to a
+ "dd/mm/yyyy à hh:mm" format
+ """
+ return datetime.strptime(
+ date,
+ "%Y-%m-%dT%H:%M:%SZ"
+ ).strftime(
+ "%d/%m/%Y à %H:%M"
+ )
--- a/virtualenv/requirements.txt Fri Jan 30 12:41:40 2015 +0100
+++ b/virtualenv/requirements.txt Fri Feb 13 17:22:21 2015 +0100
@@ -12,6 +12,7 @@
isodate==0.5.1
itsdangerous==0.24
pep8==1.5.7
+pylint
pyparsing==2.0.3
python-slugify==0.1.0
pytz==2014.10