base for static pages #4
authordurandn
Fri, 16 Sep 2016 12:07:53 +0200
changeset 183 b57504a2983f
parent 182 7581a9f3b2c9
child 184 1c96a8098ab9
base for static pages #4
src/iconolab/static/iconolab/css/iconolab.css
src/iconolab/templates/iconolab/glossary.html
src/iconolab/templates/iconolab/misc/charter.html
src/iconolab/templates/iconolab/misc/credits.html
src/iconolab/templates/iconolab/misc/glossary.html
src/iconolab/templates/iconolab/misc/legalmentions.html
src/iconolab/templates/iconolab_base.html
src/iconolab/urls.py
src/iconolab/views/__init__.py
src/iconolab/views/iconolab.py
src/iconolab/views/iconolab_misc.py
src/iconolab/views/iconolab_objects.py
--- a/src/iconolab/static/iconolab/css/iconolab.css	Thu Sep 15 15:27:11 2016 +0200
+++ b/src/iconolab/static/iconolab/css/iconolab.css	Fri Sep 16 12:07:53 2016 +0200
@@ -154,4 +154,19 @@
 .diff-panel .close-btn {
 	cursor: pointer;
 	margin-right: 5px;
+}
+
+footer div{
+}
+
+
+.partners-icons{
+	margin-left: 20px;
+}
+.footer-link{
+	margin-left: 20px;
+}
+
+.footer-info{
+	margin-left:15px;
 }
\ No newline at end of file
--- a/src/iconolab/templates/iconolab/glossary.html	Thu Sep 15 15:27:11 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-{% extends 'iconolab_base.html' %}
-
-{% load staticfiles %}
-
-{% load thumbnail %}
-
-{% load iconolab_tags %}
-
-{% block content %}
-  <div class="row" style="border: 1px solid gray;padding: 15px;">
-    <div class="col-md-9">
-    <h1>Le projet</h1>
-    <p class="text-justify">Iconolab est une plateforme contributive grâce à laquelle les utilisateurs peuvent explorer et annoter des collections (fonds) d'images fournies par plusieurs musées et référencées dans la base de données Joconde. 
-    <br><br>
-    Sur cette plateforme, il est possible de découper des fragments d'une image et de lui assigner des mots-clés pour en faire une annotation. Les autres utilisateurs de la plateforme peuvent ensuite échanger, débattre et faire évoluer ces annotations. 
-    <br><br>
-    Cette page décrit le vocabulaire employé sur la plateforme afin de vous aider à en comprendre le fonctionnement.
-    </p>
-    </div>
-    <div class="col-md-3 text-center">
-         <img src="{% static 'iconolab/img/glossary_example.png' %}" width="200" height="200">
-         <br><h4 class="text-center"><small>Exemple de fragment</small></h6>
-    </div>
-    <div class="col-md-12">
-    <h2>Glossaire <small>Iconolab</small></h2>
-      <div class="row">
-        <div class="col-md-6">
-          <dl class="dl-horizontal">
-            <dt>Fonds</dt>
-            <dd class="text-justify">Un "fonds" dans Iconolab représente une collection d'objets, chacun présentant une ou plusieurs images sur laquelle les utilisateurs vont pouvoir créer des annotations et commenter sur les annotations</dd>
-            <br>
-            
-            <dt>Image</dt>           
-            <dd class="text-justify">Une "image" est liée à un objet. C'est l'élément sur lequel les utilisateurs vont pouvoir créer des annotations.</dd>
-            <br>
-            
-            <dt>Révision</dt>
-            <dd class="text-justify">Une "révision" est un état de l'annotation à un instant T. Une révision est créée à chaque fois qu'un utilisateur édite une annotation. <br><br> Les utilisateurs peuvent également proposer des révisions sur les annotations des autres utilisateurs. <br><br> La révision acceptée la plus récente est dite "Révision courante" et représente l'état officiel/publié de l'annotation.</dd>
-            <br>
-            <dt>Pertinence et fiabilité <br>des mot-clés</dt>
-            <dd class="text-justify">
-              Chaque mot-clé est accompagné de deux notes qui représentent la pertinence et la fiabilité de celui-ci. C'est à l'utilisateur qui pose les mot-clés d'estimer ces deux notions.<br><br>
-              <ul>
-              <li><b>La pertinence:</b> Indique le niveau de précision du mot-clé, càd si le mot-clé est plus ou moins généraliste dans le contexte où il est posé (Par exemple: Napoléon 3 est un mot-clé plus pertinent que Napoléon)</li>
-              <li><b>La fiabilité:</b> Indique à quel point l'utilisateur est certain que le mot-clé correspond à l'annotation sur laquelle il est posée (par exemple, si un utilisateur pose le mot-clé "église" sur un bâtiment en ruine mais n'est pas certain que ça soit bel et bien une église, il se notera une faible fiabilité)</li>
-              </ul>
-            </dd>
-          </dl>
-        </div>
-        <div class="col-md-6">
-          <dl class="dl-horizontal">
-            <dt>Objet</dt>
-            <dd class="text-justify">Un "objet" représente le sujet d'une notice Joconde, dans le contexte d'un fonds Iconolab. Il lui est associé une partie des métadonnées de la notice Joconde correspondante (à des fins d'affichage), et une ou plusieurs images.</dd>
-            <br>
-            <dt>Annotation</dt>
-            <dd class="text-justify">
-            Une "annotation" est créée par un utilisateur sur une image. Chaque annotation regroupe les éléments suivants:<br><br>
-              <ul>
-              <li><b>Titre</b></li>
-              <li><b>Description</b></li>
-              <li><b>Mot-clés:</b> un mot-clé est un mot-clé qui sera associé à l'annotation. En plus de cela, chaque mot-clé doit être associé d'une évaluation de "pertinence" (voir plus bas) et de "fiabilité" (voir plus bas)</li>
-              <li><b>Fragments:</b> le fragment est la partie de l'image (découpée à l'aide d'un éditeur) sur laquelle porte l'annotation. S'il n'y a pas de fragment, l'annotation porte sur l'image entière.</li>
-              <li><b>Commentaires:</b> chaque Annotation ouvre un fil de commentaires sur lesquels les utilisateurs vont pouvoir intervenir. Quand on créé une annotation, on doit également indiquer dans un commentaire la raison pour laquelle on a créé cette annotation.</li>
-              </ul>
-            </dd>
-          </dl>
-        </div>
-      </div>
-    </div>
-  </div>
-{% endblock %}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/misc/charter.html	Fri Sep 16 12:07:53 2016 +0200
@@ -0,0 +1,15 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}
+  <div class="row" style="border: 1px solid gray;padding: 15px;">
+    <div class="col-md-12">
+      <h4>Charte de contribution</h4>
+    </div>
+  </div>
+{% endblock %}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/misc/credits.html	Fri Sep 16 12:07:53 2016 +0200
@@ -0,0 +1,15 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}
+  <div class="row" style="border: 1px solid gray;padding: 15px;">
+    <div class="col-md-12">
+      <h4>Crédits</h4>
+    </div>
+  </div>
+{% endblock %}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/misc/glossary.html	Fri Sep 16 12:07:53 2016 +0200
@@ -0,0 +1,71 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}
+  <div class="row" style="border: 1px solid gray;padding: 15px;">
+    <div class="col-md-9">
+    <h1>Le projet</h1>
+    <p class="text-justify">Iconolab est une plateforme contributive grâce à laquelle les utilisateurs peuvent explorer et annoter des collections (fonds) d'images fournies par plusieurs musées et référencées dans la base de données Joconde. 
+    <br><br>
+    Sur cette plateforme, il est possible de découper des fragments d'une image et de lui assigner des mots-clés pour en faire une annotation. Les autres utilisateurs de la plateforme peuvent ensuite échanger, débattre et faire évoluer ces annotations. 
+    <br><br>
+    Cette page décrit le vocabulaire employé sur la plateforme afin de vous aider à en comprendre le fonctionnement.
+    </p>
+    </div>
+    <div class="col-md-3 text-center">
+         <img src="{% static 'iconolab/img/glossary_example.png' %}" width="200" height="200">
+         <br><h4 class="text-center"><small>Exemple de fragment</small></h6>
+    </div>
+    <div class="col-md-12">
+    <h2>Glossaire <small>Iconolab</small></h2>
+      <div class="row">
+        <div class="col-md-6">
+          <dl class="dl-horizontal">
+            <dt>Fonds</dt>
+            <dd class="text-justify">Un "fonds" dans Iconolab représente une collection d'objets, chacun présentant une ou plusieurs images sur laquelle les utilisateurs vont pouvoir créer des annotations et commenter sur les annotations</dd>
+            <br>
+            
+            <dt>Image</dt>           
+            <dd class="text-justify">Une "image" est liée à un objet. C'est l'élément sur lequel les utilisateurs vont pouvoir créer des annotations.</dd>
+            <br>
+            
+            <dt>Révision</dt>
+            <dd class="text-justify">Une "révision" est un état de l'annotation à un instant T. Une révision est créée à chaque fois qu'un utilisateur édite une annotation. <br><br> Les utilisateurs peuvent également proposer des révisions sur les annotations des autres utilisateurs. <br><br> La révision acceptée la plus récente est dite "Révision courante" et représente l'état officiel/publié de l'annotation.</dd>
+            <br>
+            <dt>Pertinence et fiabilité <br>des mot-clés</dt>
+            <dd class="text-justify">
+              Chaque mot-clé est accompagné de deux notes qui représentent la pertinence et la fiabilité de celui-ci. C'est à l'utilisateur qui pose les mot-clés d'estimer ces deux notions.<br><br>
+              <ul>
+              <li><b>La pertinence:</b> Indique le niveau de précision du mot-clé, càd si le mot-clé est plus ou moins généraliste dans le contexte où il est posé (Par exemple: Napoléon 3 est un mot-clé plus pertinent que Napoléon)</li>
+              <li><b>La fiabilité:</b> Indique à quel point l'utilisateur est certain que le mot-clé correspond à l'annotation sur laquelle il est posée (par exemple, si un utilisateur pose le mot-clé "église" sur un bâtiment en ruine mais n'est pas certain que ça soit bel et bien une église, il se notera une faible fiabilité)</li>
+              </ul>
+            </dd>
+          </dl>
+        </div>
+        <div class="col-md-6">
+          <dl class="dl-horizontal">
+            <dt>Objet</dt>
+            <dd class="text-justify">Un "objet" représente le sujet d'une notice Joconde, dans le contexte d'un fonds Iconolab. Il lui est associé une partie des métadonnées de la notice Joconde correspondante (à des fins d'affichage), et une ou plusieurs images.</dd>
+            <br>
+            <dt>Annotation</dt>
+            <dd class="text-justify">
+            Une "annotation" est créée par un utilisateur sur une image. Chaque annotation regroupe les éléments suivants:<br><br>
+              <ul>
+              <li><b>Titre</b></li>
+              <li><b>Description</b></li>
+              <li><b>Mot-clés:</b> un mot-clé est un mot-clé qui sera associé à l'annotation. En plus de cela, chaque mot-clé doit être associé d'une évaluation de "pertinence" (voir plus bas) et de "fiabilité" (voir plus bas)</li>
+              <li><b>Fragments:</b> le fragment est la partie de l'image (découpée à l'aide d'un éditeur) sur laquelle porte l'annotation. S'il n'y a pas de fragment, l'annotation porte sur l'image entière.</li>
+              <li><b>Commentaires:</b> chaque Annotation ouvre un fil de commentaires sur lesquels les utilisateurs vont pouvoir intervenir. Quand on créé une annotation, on doit également indiquer dans un commentaire la raison pour laquelle on a créé cette annotation.</li>
+              </ul>
+            </dd>
+          </dl>
+        </div>
+      </div>
+    </div>
+  </div>
+{% endblock %}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/templates/iconolab/misc/legalmentions.html	Fri Sep 16 12:07:53 2016 +0200
@@ -0,0 +1,15 @@
+{% extends 'iconolab_base.html' %}
+
+{% load staticfiles %}
+
+{% load thumbnail %}
+
+{% load iconolab_tags %}
+
+{% block content %}
+  <div class="row" style="border: 1px solid gray;padding: 15px;">
+    <div class="col-md-12">
+      <h4>Mentions légales</h4>
+    </div>
+  </div>
+{% endblock %}
\ No newline at end of file
--- a/src/iconolab/templates/iconolab_base.html	Thu Sep 15 15:27:11 2016 +0200
+++ b/src/iconolab/templates/iconolab_base.html	Fri Sep 16 12:07:53 2016 +0200
@@ -44,14 +44,23 @@
     <footer>
       <div class="container">
       <hr>
-      <div class="pull-left" style="margin-left:15px;">
-        <h4><small>Version {% version %}</small></h4>
-      </div>
-      <div class="pull-right partners-icons">
-        <a href="http://www.iri.centrepompidou.fr"><img src="{% static 'iconolab/img/logo_IRI_footer.png' %}"></a>
-        &nbsp;&nbsp;
-        <a href="http://www.culturecommunication.gouv.fr/"><img src="{% static 'iconolab/img/logo_mcc_footer.png' %}"></a>
-      </div>
+        <div class="pull-left footer-info">
+          <h4><small>Version {% version %}</small></h4>
+        </div>
+        <div class="pull-right partners-icons">
+          <a href="http://www.iri.centrepompidou.fr"><img src="{% static 'iconolab/img/logo_IRI_footer.png' %}"></a>
+          &nbsp;&nbsp;
+          <a href="http://www.culturecommunication.gouv.fr/"><img src="{% static 'iconolab/img/logo_mcc_footer.png' %}"></a>
+        </div>
+        <div class="pull-right footer-link">
+          <h4><small><a href="{% url 'iconolab_legals' %}">Mentions légales</a></small></h4>
+        </div>
+        <div class="pull-right footer-link">
+          <h4><small><a href="{% url 'iconolab_credits' %}">Crédits</a></small></h4>
+        </div>
+        <div class="pull-right footer-link">
+          <h4><small><a href="{% url 'iconolab_charter' %}">Chartre de contribution</a></small></h4>
+        </div>
       </div>
     </footer>
 </html>
\ No newline at end of file
--- a/src/iconolab/urls.py	Thu Sep 15 15:27:11 2016 +0200
+++ b/src/iconolab/urls.py	Fri Sep 16 12:07:53 2016 +0200
@@ -30,23 +30,27 @@
 urlpatterns = [
     url(r'^$', django_views.generic.RedirectView.as_view(url=reverse_lazy("home"))),
     url(r'^admin/', admin.site.urls),
-    url(r'^home$', views.iconolab.GlobalHomepageView.as_view(), name="home"),
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)$', views.iconolab.CollectionHomepageView.as_view(), name='collection_home'), # Home fond
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/items/(?P<item_guid>[^/]+)$', views.iconolab.ShowItemView.as_view(), name='item_detail'),
+    url(r'^home$', views.iconolab_objects.GlobalHomepageView.as_view(), name="home"),
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)$', views.iconolab_objects.CollectionHomepageView.as_view(), name='collection_home'), # Home fond
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/items/(?P<item_guid>[^/]+)$', views.iconolab_objects.ShowItemView.as_view(), name='item_detail'),
     url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/items/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")),
     url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")),
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)$', views.iconolab.ShowImageView.as_view(), name='image_detail'),
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)$', views.iconolab_objects.ShowImageView.as_view(), name='image_detail'),
     url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/?$', django_views.generic.RedirectView.as_view(pattern_name="image_detail")),
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/create$', login_required(views.iconolab.CreateAnnotationView.as_view()), name='annotation_create'),
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/detail$', views.iconolab.ShowAnnotationView.as_view(), name='annotation_detail'),
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/edit$', login_required(views.iconolab.EditAnnotationView.as_view()), name='annotation_edit'),
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/create$', login_required(views.iconolab_objects.CreateAnnotationView.as_view()), name='annotation_create'),
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/detail$', views.iconolab_objects.ShowAnnotationView.as_view(), name='annotation_detail'),
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/edit$', login_required(views.iconolab_objects.EditAnnotationView.as_view()), name='annotation_edit'),
     url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/?$', django_views.generic.RedirectView.as_view(pattern_name="annotation_detail")),
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/detail', views.iconolab.ShowRevisionView.as_view(), name='revision_detail'),
-    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/merge$', login_required(views.iconolab.MergeProposalView.as_view()), name='annotation_merge'),
-    url(r'^user/(?P<slug>[a-z0-9\-]+)/home/?$', views.iconolab.UserHomeView.as_view(), name="user_home"),
-    url(r'^user/notifications/all/?$', login_required(views.iconolab.UserNotificationsView.as_view()), name="user_notifications"),
-    url(r'^errors/404', views.iconolab.NotFoundErrorView.as_view(), name="404error"),
-    url(r'^help/', views.iconolab.HelpView.as_view(), name="iconolab_help"),
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/detail', views.iconolab_objects.ShowRevisionView.as_view(), name='revision_detail'),
+    url(r'^collections/(?P<collection_name>[a-z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/merge$', login_required(views.iconolab_objects.MergeProposalView.as_view()), name='annotation_merge'),
+    url(r'^user/(?P<slug>[a-z0-9\-]+)/home/?$', views.iconolab_objects.UserHomeView.as_view(), name="user_home"),
+    url(r'^user/notifications/all/?$', login_required(views.iconolab_objects.UserNotificationsView.as_view()), name="user_notifications"),
+    url(r'^errors/404', views.iconolab_misc.NotFoundErrorView.as_view(), name="404error"),
+    url(r'^help/', views.iconolab_misc.HelpView.as_view(), name="iconolab_help"),
+    url(r'^credits/', views.iconolab_misc.CreditsView.as_view(), name="iconolab_credits"),
+    url(r'^contributioncharter/', views.iconolab_misc.ContributionCharterView.as_view(), name="iconolab_charter"),
+    url(r'^contributioncharter/', views.iconolab_misc.LegalMentionsView.as_view(), name="iconolab_legals"),
+    
     url(r'^account/', include('iconolab.auth.urls', namespace='account')),
     url(r'^comments/', include('django_comments_xtd.urls')),
     url(r'^comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment"),
@@ -58,7 +62,7 @@
 
     url(r'collections/(?P<collection_name>[a-z0-9\-]+)/search', IconolabSearchView.as_view(), name="collection_haystack_search"),
     url(r'^search/$', IconolabSearchView.as_view(), name="haystack_search"),
-    url(r'^compare/$', views.iconolab.TestView.as_view(), name="compare_view")
+    url(r'^compare/$', views.iconolab_objects.TestView.as_view(), name="compare_view")
     #url(r'^search/', include('haystack.urls'), name="search_iconolab"),
 ]
 
--- a/src/iconolab/views/__init__.py	Thu Sep 15 15:27:11 2016 +0200
+++ b/src/iconolab/views/__init__.py	Fri Sep 16 12:07:53 2016 +0200
@@ -1,2 +1,3 @@
 from . import comments
-from . import iconolab
\ No newline at end of file
+from . import iconolab_objects
+from . import iconolab_misc
\ No newline at end of file
--- a/src/iconolab/views/iconolab.py	Thu Sep 15 15:27:11 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,547 +0,0 @@
-from django.shortcuts import HttpResponse, get_object_or_404, render
-from django.http import Http404
-from django.db.models import Count
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth.models import User
-from django.views.generic import View, DetailView, RedirectView, TemplateView
-from django.views.generic.base import ContextMixin
-from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
-from django.core.urlresolvers import reverse
-from django.core.exceptions import ObjectDoesNotExist
-from django.contrib.contenttypes.models import ContentType
-from django.contrib.sites.models import Site
-from django.conf import settings
-from notifications.models import Notification
-from iconolab.models import Annotation, AnnotationRevision, Collection, Item, Image, IconolabComment, MetaCategory, MetaCategoryInfo
-from iconolab.forms.annotations import AnnotationRevisionForm
-import logging
-
-logger = logging.getLogger(__name__)
-
-class GlobalHomepageView(View):
-    def get(self, request, *args, **kwargs):
-        context = {}
-        context['collections'] = Collection.objects
-        return render(request, 'iconolab/home.html', context)
-
-class TestView(View):
-    template_name = "iconolab/compare.html"
-
-    def get(self, request, *args, **kwargs):
-        return render(request, self.template_name)
-
-
-
-class UserHomeView(DetailView):
-    model = User
-    slug_field = 'id'
-
-    def get_context_data(self, **kwargs):
-        context = super(UserHomeView, self).get_context_data(**kwargs)
-        return context
-
-    def get(self, request, *args, **kwargs):
-        self.object = self.get_object()
-        context = self.get_context_data()
-        profile_user = self.object
-        context['profile_user'] = profile_user
-        context['user_annotations'] = Annotation.objects.filter(author=profile_user).prefetch_related(
-            'current_revision',
-            'revisions',
-            'image',
-            'image__item',
-            'image__item__collection'
-        )
-        context['user_revisions_annotations'] = Annotation.objects.filter(revisions__author=profile_user).exclude(author=profile_user).prefetch_related(
-            'current_revision',
-            'revisions',
-            'image',
-            'image__item',
-            'image__item__collection'
-        ).distinct()
-        comments_annotations_str_id = IconolabComment.objects.filter(user=profile_user, content_type__app_label='iconolab', content_type__model='annotation').values_list('object_pk', flat=True)
-        comments_annotations_id = [int(str_id) for str_id in comments_annotations_str_id]
-        context['user_comments_annotations'] = Annotation.objects.filter(id__in=comments_annotations_id).exclude(author=profile_user).exclude(annotation_guid__in=context['user_revisions_annotations'].values_list('annotation_guid', flat=True)).prefetch_related(
-            'current_revision',
-            'revisions',
-            'image',
-            'image__item',
-            'image__item__collection'
-        ).distinct()
-        if request.user.is_authenticated() and self.object == request.user:
-            if request.GET.get('clear_notifications', False):
-                Notification.objects.filter(recipient=request.user).mark_all_as_read()
-            context['notifications'] = Notification.objects.filter(recipient=request.user)
-        return render(request, 'iconolab/user_home.html', context)
-
-class UserNotificationsView(View):
-
-    def get(self, request, *args, **kwargs):
-        context = {}
-        notifications = Notification.objects.filter(recipient=request.user)
-        context['notifications_unread_ids'] = notifications.unread().values_list('id', flat=True)
-        page = request.GET.get('page', 1)
-        paginator = Paginator(notifications, 50)
-        try:
-            notifications_list = paginator.page(page)
-        except PageNotAnInteger:
-            notifications_list = paginator.page(1)
-        except EmptyPage:
-            notifications_list = paginator.page(paginator.num_pages)
-        context['notifications'] = notifications_list
-        return render(request, 'iconolab/user_notifications.html', context)
-
-# Class with check_kwargs method to fetch objects from database depending on what level in the app we're currently at
-class IconolabObjectView(object):
-    def check_kwargs(self, kwargs):
-        '''
-            Returns a boolean depending on wether (True) or not (False) the objects were found and a tuple containing the objects, with a select_related/prefetch_related on relevant related objects
-            following this ordering: (collection, item, image, annotation, revision)
-        '''
-
-        objects_tuple = ()
-        if 'collection_name' in kwargs.keys():
-            try:
-                objects_tuple += (Collection.objects.prefetch_related('items', 'items__images').get(name=kwargs.get('collection_name')),)
-            except (ValueError, Collection.DoesNotExist):
-                return False, RedirectView.as_view(url=reverse('404error'))
-        if 'item_guid' in kwargs.keys():
-            try:
-                objects_tuple += (Item.objects.prefetch_related('images', 'metadatas', 'images__stats').get(item_guid=kwargs.get('item_guid')),)
-            except (ValueError, Item.DoesNotExist):
-                return False, RedirectView.as_view(url=reverse('404error'))
-        if 'image_guid' in kwargs.keys():
-            try:
-                objects_tuple += (Image.objects.prefetch_related('annotations', 'item', 'stats').get(image_guid=kwargs.get('image_guid')),)
-            except (ValueError, Image.DoesNotExist):
-                return False, RedirectView.as_view(url=reverse('404error'))
-        if 'annotation_guid' in kwargs.keys():
-            try:
-                objects_tuple += (Annotation.objects.select_related('current_revision', 'stats', 'image').get(annotation_guid=kwargs.get('annotation_guid')),)
-            except (ValueError, Annotation.DoesNotExist):
-                return False, RedirectView.as_view(url=reverse('404error'))
-        if 'revision_guid' in kwargs.keys():
-            try:
-                objects_tuple += (AnnotationRevision.objects.select_related('parent_revision').get(revision_guid=kwargs.get('revision_guid')),)
-            except (ValueError, AnnotationRevision.DoesNotExist):
-                return False, RedirectView.as_view(url=reverse('404error'))
-        return True, objects_tuple
-
-class CollectionHomepageView(View, ContextMixin, IconolabObjectView):
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection,) = result
-        else:
-            return result(request)
-        context = super(CollectionHomepageView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['collection'] = collection
-
-        # Recent annotations
-        context['recent_annotations'] = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related(
-            'current_revision',
-            'stats'
-        ).order_by('-current_revision__created')
-
-        # Recent annotations
-        context['revised_annotations'] = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related(
-            'current_revision',
-            'stats'
-        ).annotate(revision_count=Count("revisions")).order_by('-revision_count')
-
-        contrib_calls_annotations_ids = list(set(MetaCategoryInfo.objects.filter(
-            metacategory__collection__name=collection.name,
-            metacategory__triggers_notifications=MetaCategory.CONTRIBUTORS
-        ).order_by("comment__submit_date").values_list("comment__object_pk", flat=True)))
-
-        collection_annotations = Annotation.objects.filter(id__in=contrib_calls_annotations_ids).all()
-        collection_ann_dict = dict([(str(annotation.id), annotation) for annotation in collection_annotations])
-        context["contribution_calls_annotations_list"] = [collection_ann_dict[id] for id in contrib_calls_annotations_ids]
-
-        return render(request, 'iconolab/collection_home.html', context)
-
-
-
-class ShowItemView(View, ContextMixin, IconolabObjectView):
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, item) = result
-        else:
-            return result(request)
-        context = super(ShowItemView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['item_guid'] = self.kwargs.get('image_guid', '')
-        context['collection'] = collection
-        context['item'] = item
-        for image in item.images.all():
-            image.stats.views_count += 1
-            image.stats.save()
-        return render(request, 'iconolab/detail_item.html', context);
-
-class ShowImageView(View, ContextMixin, IconolabObjectView):
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image) = result
-        else:
-            return result(request)
-        context = super(ShowImageView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['image_guid'] = self.kwargs.get('image_guid', '')
-        context['collection'] = collection
-        context['image'] = image
-        return render(request, 'iconolab/detail_image.html', context)
-
-class CreateAnnotationView(View, ContextMixin, IconolabObjectView):
-
-    def get_context_data(self, **kwargs):
-        context = super(CreateAnnotationView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['image_guid'] = self.kwargs.get('image_guid', '')
-        return context
-
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image,) = result
-        else:
-            return result(request)
-        annotation_form = AnnotationRevisionForm()
-        context = self.get_context_data(**kwargs)
-        context['image'] = image
-        context['form'] = annotation_form
-        context['tags_data'] = '[]'
-        return render(request, 'iconolab/change_annotation.html', context)
-
-    def post(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image) = result
-        else:
-            return result(request)
-        collection_name = kwargs['collection_name']
-        image_guid = kwargs['image_guid']
-        annotation_form = AnnotationRevisionForm(request.POST)
-        if annotation_form.is_valid():
-            author = request.user
-            title = annotation_form.cleaned_data['title']
-            description = annotation_form.cleaned_data['description']
-            fragment = annotation_form.cleaned_data['fragment']
-            tags_json = annotation_form.cleaned_data['tags']
-            new_annotation = Annotation.objects.create_annotation(author, image, title=title, description=description, fragment=fragment, tags_json=tags_json)
-            revision_comment = annotation_form.cleaned_data['comment']
-            IconolabComment.objects.create(
-                comment = revision_comment,
-                revision = new_annotation.current_revision,
-                content_type = ContentType.objects.get(app_label='iconolab', model='annotation'),
-                content_object = new_annotation,
-                site = Site.objects.get(id=settings.SITE_ID),
-                object_pk = new_annotation.id,
-                user = request.user,
-                user_name = request.user.username
-            )
-            return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': new_annotation.annotation_guid}))(request)
-        context = self.get_context_data(**kwargs)
-        context['image'] = image
-        context['form'] = annotation_form
-        context['tags_data'] = '[]'
-        return render(request, 'iconolab/change_annotation.html', context)
-
-class ShowAnnotationView(View, ContextMixin, IconolabObjectView):
-
-    def get_context_data(self, **kwargs):
-        context = super(ShowAnnotationView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['image_guid'] = self.kwargs.get('image_guid', '')
-        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
-        return context
-
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image, annotation,) = result
-        else:
-            return result(request)
-        context = self.get_context_data(**kwargs)
-        context['collection'] = collection
-        context['image'] = image
-        context['annotation'] = annotation
-        context['tags_data'] = annotation.current_revision.get_tags_json()
-
-        page = request.GET.get('page', 1)
-        per_page = request.GET.get('perpage', 10)
-        full_comments_list = IconolabComment.objects.for_app_models('iconolab.annotation').filter(object_pk = annotation.pk).order_by('thread_id', '-order')
-        paginator = Paginator(full_comments_list, per_page)
-        try:
-            comments_list = paginator.page(page)
-        except PageNotAnInteger:
-            comments_list = paginator.page(1)
-        except EmptyPage:
-            comments_list = paginator.page(paginator.num_pages)
-        context['comments'] = comments_list
-
-        if request.user.is_authenticated():
-            user_comment_notifications = Notification.objects.filter(
-                recipient=request.user,
-                action_object_content_type__app_label='iconolab',
-                action_object_content_type__model='iconolabcomment',
-                target_content_type__app_label='iconolab',
-                target_content_type__model='annotation',
-                target_object_id=annotation.id
-            ).unread()
-            context['notifications_comments_ids'] = [int(val) for val in user_comment_notifications.values_list('action_object_object_id', flat=True)]
-            comment_list_ids = [comment.id for comment in context['comments'] ]
-            for notification in user_comment_notifications.all():
-                if int(notification.action_object_object_id) in comment_list_ids:
-                    notification.mark_as_read()
-
-        image.stats.views_count += 1
-        image.stats.save()
-        annotation.stats.views_count += 1
-        annotation.stats.save()
-        return render(request, 'iconolab/detail_annotation.html', context)
-
-
-class EditAnnotationView(View, ContextMixin, IconolabObjectView):
-
-    def get_context_data(self, **kwargs):
-        context = super(EditAnnotationView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['image_guid'] = self.kwargs.get('image_guid', '')
-        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
-        return context
-
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image, annotation,) = result
-        else:
-            return result(request)
-        annotation_form = AnnotationRevisionForm(instance=annotation.current_revision)
-        context = self.get_context_data(**kwargs)
-        context['image'] = image
-        context['annotation'] = annotation
-        context['form'] = annotation_form
-        context['tags_data'] = annotation.current_revision.get_tags_json()
-        return render(request, 'iconolab/change_annotation.html', context)
-
-    def post(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image, annotation) = result
-        else:
-            return result(request)
-        collection_name = kwargs['collection_name']
-        image_guid = kwargs['image_guid']
-        annotation_guid = kwargs['annotation_guid']
-        annotation_form = AnnotationRevisionForm(request.POST)
-        if annotation_form.is_valid():
-            revision_author = request.user
-            revision_title = annotation_form.cleaned_data['title']
-            revision_description = annotation_form.cleaned_data['description']
-            revision_fragment = annotation_form.cleaned_data['fragment']
-            revision_tags_json = annotation_form.cleaned_data['tags']
-            new_revision = annotation.make_new_revision(revision_author, revision_title, revision_description, revision_fragment, revision_tags_json)
-            revision_comment = annotation_form.cleaned_data['comment']
-            comment = IconolabComment.objects.create(
-                comment = revision_comment,
-                revision = new_revision,
-                content_type = ContentType.objects.get(app_label='iconolab', model='annotation'),
-                content_object = annotation,
-                site = Site.objects.get(id=settings.SITE_ID),
-                object_pk = annotation.id,
-                user = request.user,
-                user_name = request.user.username
-            )
-            return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request)
-        context = self.get_context_data(**kwargs)
-        context['image'] = image
-        context['form'] = annotation_form
-        context['annotation'] = annotation
-        context['tags_data'] = annotation.current_revision.get_tags_json()
-        return render(request, 'iconolab/change_annotation.html', context)
-
-
-class ShowRevisionView(View, ContextMixin, IconolabObjectView):
-
-    def get_context_data(self, **kwargs):
-        context = super(ShowRevisionView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['image_guid'] = self.kwargs.get('image_guid', '')
-        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
-        context['revision_guid'] = self.kwargs.get('revision_guid', '')
-        return context
-
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image, annotation, revision,) = result
-        else:
-            return result(request)
-        context = self.get_context_data(**kwargs)
-        context['collection'] = collection
-        context['image'] = image
-        context['annotation'] = annotation
-        context['revision'] = revision
-        context['tags_data'] = revision.get_tags_json()
-        context['comment'] = revision.creation_comment.first()
-        if request.user.is_authenticated() and annotation.author == request.user:
-            ann_author_notified = Notification.objects.filter(
-                    recipient=request.user,
-                    action_object_content_type__app_label='iconolab',
-                    action_object_content_type__model='annotationrevision',
-                    action_object_object_id=revision.id,
-                    target_content_type__app_label='iconolab',
-                    target_content_type__model='annotation',
-                    target_object_id=annotation.id
-                ).unread()
-            if ann_author_notified:
-                ann_author_notified.first().mark_as_read()
-                context['notified_revision'] = True
-        if request.user.is_authenticated() and revision.author == request.user:
-            rev_author_notified = Notification.objects.filter(
-                    recipient=request.user,
-                    action_object_content_type__app_label='iconolab',
-                    action_object_content_type__model='annotationrevision',
-                    action_object_object_id=revision.id,
-                    target_content_type__app_label='iconolab',
-                    target_content_type__model='annotation',
-                    target_object_id=annotation.id
-                ).unread()
-            if rev_author_notified:
-                rev_author_notified.first().mark_as_read()
-                context['notified_revision'] = True
-        return render(request, 'iconolab/detail_revision.html', context)
-
-
-class MergeProposalView(View, ContextMixin, IconolabObjectView):
-
-    def get_context_data(self, **kwargs):
-        context = super(MergeProposalView, self).get_context_data(**kwargs)
-        context['collection_name'] = self.kwargs.get('collection_name', '')
-        context['image_guid'] = self.kwargs.get('image_guid', '')
-        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
-        context['revision_guid'] = self.kwargs.get('revision_guid', '')
-        return context
-
-    def get(self, request, *args, **kwargs):
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image, annotation, revision,) = result
-        else:
-            return result(request)
-        # Only show merge form if there is a revision to merge AND the current user is the annotation author
-        if revision.state != AnnotationRevision.AWAITING or request.user != annotation.author:
-            return RedirectView.as_view(
-                url=reverse('revision_detail',
-                    kwargs={
-                        'collection_name': collection.name,
-                        'image_guid': image.image_guid,
-                        'annotation_guid': annotation.annotation_guid,
-                        'revision_guid': revision.revision_guid
-                    }
-                )
-            )(request)
-        # Auto-accepts the revision only if the proper query arg is set and only if the revision parent is the current revision
-        if 'auto_accept' in request.GET and request.GET['auto_accept'] in ['True', 'true', '1', 'yes'] and revision.parent_revision == annotation.current_revision:
-            annotation.validate_existing_revision(revision)
-            return RedirectView.as_view(
-                url=reverse('annotation_detail',
-                    kwargs={
-                        'collection_name': collection.name,
-                        'image_guid': image.image_guid,
-                        'annotation_guid': annotation.annotation_guid
-                    }
-                )
-            )(request)
-        # Auto-reject the revision only if the proper query arg is set
-        if 'auto_reject' in request.GET and request.GET['auto_reject'] in ['True', 'true', '1', 'yes']:
-            annotation.reject_existing_revision(revision)
-            return RedirectView.as_view(
-                url=reverse('annotation_detail',
-                    kwargs={
-                        'collection_name': collection.name,
-                        'image_guid': image.image_guid,
-                        'annotation_guid': annotation.annotation_guid
-                    }
-                )
-            )(request)
-
-        context = self.get_context_data(**kwargs)
-        context['collection'] = collection
-        context['image'] = image
-        context['annotation'] = annotation
-        # Proposal data
-        context['proposal_revision'] = revision
-        context['proposal_tags_data'] = revision.get_tags_json()
-        context['proposal_comment'] = revision.creation_comment.first()
-        # Parent data
-        context['parent_revision'] = revision.parent_revision
-        context['parent_tags_data'] = revision.parent_revision.get_tags_json()
-        context['parent_comment'] = revision.parent_revision.creation_comment.first()
-        # Current data
-        context['current_revision'] = annotation.current_revision
-        context['current_tags_data'] = annotation.current_revision.get_tags_json()
-        context['current_comment'] = annotation.current_revision.creation_comment.first()
-
-        merge_form = AnnotationRevisionForm(instance=revision)
-        context['merge_form'] = merge_form
-        return render(request, 'iconolab/merge_revision.html', context)
-
-    def post(self, request, *args, **kwargs):
-        # Handle merge form submit here
-        success, result = self.check_kwargs(kwargs)
-        if success:
-            (collection, image, annotation, revision) = result
-        else:
-            return result(request)
-        collection_name = kwargs['collection_name']
-        image_guid = kwargs['image_guid']
-        annotation_guid = kwargs['annotation_guid']
-        revision_guid = kwargs['revision_guid']
-
-        merge_revision_form = AnnotationRevisionForm(request.POST)
-        if merge_revision_form.is_valid():
-            revision_title = merge_revision_form.cleaned_data['title']
-            revision_description = merge_revision_form.cleaned_data['description']
-            revision_fragment = merge_revision_form.cleaned_data['fragment']
-            revision_tags_json = merge_revision_form.cleaned_data['tags']
-            new_revision = annotation.merge_existing_revision(revision_title, revision_description, revision_fragment, revision_tags_json, revision)
-            revision_comment = merge_revision_form.cleaned_data['comment']
-            comment = IconolabComment.objects.create(
-                comment = revision_comment,
-                revision = new_revision,
-                content_type = ContentType.objects.get(app_label='iconolab', model='annotation'),
-                content_object = annotation,
-                site = Site.objects.get(id=settings.SITE_ID),
-                object_pk = annotation.id,
-                user = request.user,
-                user_name = request.user.username
-            )
-            return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request)
-        context = self.get_context_data(**kwargs)
-        context['image'] = image
-        context['merge_form'] = merge_revision_form
-        context['annotation'] = annotation
-        # Proposal data
-        context['proposal_revision'] = revision
-        context['proposal_tags_data'] = revision.get_tags_json()
-        context['proposal_comment'] = revision.creation_comment.first()
-        # Parent data
-        context['parent_revision'] = revision.parent_revision
-        context['parent_tags_data'] = revision.parent_revision.get_tags_json()
-        context['parent_comment'] = revision.parent_revision.creation_comment.first()
-        # Current data
-        context['current_revision'] = annotation.current_revision
-        context['current_tags_data'] = annotation.current_revision.get_tags_json()
-        context['current_comment'] = annotation.current_revision.creation_comment.first()
-        return render(request, 'iconolab/merge_revision.html', context)
-
-
-class NotFoundErrorView(TemplateView):
-    template_name='errors/404error.html'
-
-class HelpView(TemplateView):
-    template_name='iconolab/glossary.html'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/views/iconolab_misc.py	Fri Sep 16 12:07:53 2016 +0200
@@ -0,0 +1,17 @@
+from django.views.generic import TemplateView
+
+class NotFoundErrorView(TemplateView):
+    template_name='errors/404error.html'
+
+class HelpView(TemplateView):
+    template_name='iconolab/misc/glossary.html'
+    
+class CreditsView(TemplateView):
+    template_name='iconolab/misc/credits.html'
+
+class LegalMentionsView(TemplateView):
+    template_name='iconolab/misc/legalmentions.html'
+    
+class ContributionCharterView(TemplateView):
+    template_name='iconolab/misc/charter.html'
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/views/iconolab_objects.py	Fri Sep 16 12:07:53 2016 +0200
@@ -0,0 +1,557 @@
+from django.shortcuts import HttpResponse, get_object_or_404, render
+from django.http import Http404
+from django.db.models import Count
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
+from django.views.generic import View, DetailView, RedirectView, TemplateView
+from django.views.generic.base import ContextMixin
+from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
+from django.core.urlresolvers import reverse
+from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.sites.models import Site
+from django.conf import settings
+from notifications.models import Notification
+from iconolab.models import Annotation, AnnotationRevision, Collection, Item, Image, IconolabComment, MetaCategory, MetaCategoryInfo
+from iconolab.forms.annotations import AnnotationRevisionForm
+import logging
+
+logger = logging.getLogger(__name__)
+
+class GlobalHomepageView(View):
+    def get(self, request, *args, **kwargs):
+        context = {}
+        context['collections'] = Collection.objects
+        return render(request, 'iconolab/home.html', context)
+
+class TestView(View):
+    template_name = "iconolab/compare.html"
+
+    def get(self, request, *args, **kwargs):
+        return render(request, self.template_name)
+
+
+
+class UserHomeView(DetailView):
+    model = User
+    slug_field = 'id'
+
+    def get_context_data(self, **kwargs):
+        context = super(UserHomeView, self).get_context_data(**kwargs)
+        return context
+
+    def get(self, request, *args, **kwargs):
+        self.object = self.get_object()
+        context = self.get_context_data()
+        profile_user = self.object
+        context['profile_user'] = profile_user
+        context['user_annotations'] = Annotation.objects.filter(author=profile_user).prefetch_related(
+            'current_revision',
+            'revisions',
+            'image',
+            'image__item',
+            'image__item__collection'
+        )
+        context['user_revisions_annotations'] = Annotation.objects.filter(revisions__author=profile_user).exclude(author=profile_user).prefetch_related(
+            'current_revision',
+            'revisions',
+            'image',
+            'image__item',
+            'image__item__collection'
+        ).distinct()
+        comments_annotations_str_id = IconolabComment.objects.filter(user=profile_user, content_type__app_label='iconolab', content_type__model='annotation').values_list('object_pk', flat=True)
+        comments_annotations_id = [int(str_id) for str_id in comments_annotations_str_id]
+        context['user_comments_annotations'] = Annotation.objects.filter(id__in=comments_annotations_id).exclude(author=profile_user).exclude(annotation_guid__in=context['user_revisions_annotations'].values_list('annotation_guid', flat=True)).prefetch_related(
+            'current_revision',
+            'revisions',
+            'image',
+            'image__item',
+            'image__item__collection'
+        ).distinct()
+        if request.user.is_authenticated() and self.object == request.user:
+            if request.GET.get('clear_notifications', False):
+                Notification.objects.filter(recipient=request.user).mark_all_as_read()
+            context['notifications'] = Notification.objects.filter(recipient=request.user)
+        return render(request, 'iconolab/user_home.html', context)
+
+class UserNotificationsView(View):
+
+    def get(self, request, *args, **kwargs):
+        context = {}
+        notifications = Notification.objects.filter(recipient=request.user)
+        context['notifications_unread_ids'] = notifications.unread().values_list('id', flat=True)
+        page = request.GET.get('page', 1)
+        paginator = Paginator(notifications, 50)
+        try:
+            notifications_list = paginator.page(page)
+        except PageNotAnInteger:
+            notifications_list = paginator.page(1)
+        except EmptyPage:
+            notifications_list = paginator.page(paginator.num_pages)
+        context['notifications'] = notifications_list
+        return render(request, 'iconolab/user_notifications.html', context)
+
+# Class with check_kwargs method to fetch objects from database depending on what level in the app we're currently at
+class IconolabObjectView(object):
+    def check_kwargs(self, kwargs):
+        '''
+            Returns a boolean depending on wether (True) or not (False) the objects were found and a tuple containing the objects, with a select_related/prefetch_related on relevant related objects
+            following this ordering: (collection, item, image, annotation, revision)
+        '''
+
+        objects_tuple = ()
+        if 'collection_name' in kwargs.keys():
+            try:
+                objects_tuple += (Collection.objects.prefetch_related('items', 'items__images').get(name=kwargs.get('collection_name')),)
+            except (ValueError, Collection.DoesNotExist):
+                return False, RedirectView.as_view(url=reverse('404error'))
+        if 'item_guid' in kwargs.keys():
+            try:
+                objects_tuple += (Item.objects.prefetch_related('images', 'metadatas', 'images__stats').get(item_guid=kwargs.get('item_guid')),)
+            except (ValueError, Item.DoesNotExist):
+                return False, RedirectView.as_view(url=reverse('404error'))
+        if 'image_guid' in kwargs.keys():
+            try:
+                objects_tuple += (Image.objects.prefetch_related('annotations', 'item', 'stats').get(image_guid=kwargs.get('image_guid')),)
+            except (ValueError, Image.DoesNotExist):
+                return False, RedirectView.as_view(url=reverse('404error'))
+        if 'annotation_guid' in kwargs.keys():
+            try:
+                objects_tuple += (Annotation.objects.select_related('current_revision', 'stats', 'image').get(annotation_guid=kwargs.get('annotation_guid')),)
+            except (ValueError, Annotation.DoesNotExist):
+                return False, RedirectView.as_view(url=reverse('404error'))
+        if 'revision_guid' in kwargs.keys():
+            try:
+                objects_tuple += (AnnotationRevision.objects.select_related('parent_revision').get(revision_guid=kwargs.get('revision_guid')),)
+            except (ValueError, AnnotationRevision.DoesNotExist):
+                return False, RedirectView.as_view(url=reverse('404error'))
+        return True, objects_tuple
+
+class CollectionHomepageView(View, ContextMixin, IconolabObjectView):
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection,) = result
+        else:
+            return result(request)
+        context = super(CollectionHomepageView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['collection'] = collection
+
+        # Recent annotations
+        context['recent_annotations'] = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related(
+            'current_revision',
+            'stats'
+        ).order_by('-current_revision__created')
+
+        # Recent annotations
+        context['revised_annotations'] = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related(
+            'current_revision',
+            'stats'
+        ).annotate(revision_count=Count("revisions")).order_by('-revision_count')
+
+        contrib_calls_annotations_ids = list(set(MetaCategoryInfo.objects.filter(
+            metacategory__collection__name=collection.name,
+            metacategory__triggers_notifications=MetaCategory.CONTRIBUTORS
+        ).order_by("comment__submit_date").values_list("comment__object_pk", flat=True)))
+
+        collection_annotations = Annotation.objects.filter(id__in=contrib_calls_annotations_ids).all()
+        collection_ann_dict = dict([(str(annotation.id), annotation) for annotation in collection_annotations])
+        context["contribution_calls_annotations_list"] = [collection_ann_dict[id] for id in contrib_calls_annotations_ids]
+
+        return render(request, 'iconolab/collection_home.html', context)
+
+
+
+class ShowItemView(View, ContextMixin, IconolabObjectView):
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, item) = result
+        else:
+            return result(request)
+        context = super(ShowItemView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['item_guid'] = self.kwargs.get('image_guid', '')
+        context['collection'] = collection
+        context['item'] = item
+        for image in item.images.all():
+            image.stats.views_count += 1
+            image.stats.save()
+        return render(request, 'iconolab/detail_item.html', context);
+
+class ShowImageView(View, ContextMixin, IconolabObjectView):
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image) = result
+        else:
+            return result(request)
+        context = super(ShowImageView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['image_guid'] = self.kwargs.get('image_guid', '')
+        context['collection'] = collection
+        context['image'] = image
+        return render(request, 'iconolab/detail_image.html', context)
+
+class CreateAnnotationView(View, ContextMixin, IconolabObjectView):
+
+    def get_context_data(self, **kwargs):
+        context = super(CreateAnnotationView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['image_guid'] = self.kwargs.get('image_guid', '')
+        return context
+
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image,) = result
+        else:
+            return result(request)
+        annotation_form = AnnotationRevisionForm()
+        context = self.get_context_data(**kwargs)
+        context['image'] = image
+        context['form'] = annotation_form
+        context['tags_data'] = '[]'
+        return render(request, 'iconolab/change_annotation.html', context)
+
+    def post(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image) = result
+        else:
+            return result(request)
+        collection_name = kwargs['collection_name']
+        image_guid = kwargs['image_guid']
+        annotation_form = AnnotationRevisionForm(request.POST)
+        if annotation_form.is_valid():
+            author = request.user
+            title = annotation_form.cleaned_data['title']
+            description = annotation_form.cleaned_data['description']
+            fragment = annotation_form.cleaned_data['fragment']
+            tags_json = annotation_form.cleaned_data['tags']
+            new_annotation = Annotation.objects.create_annotation(author, image, title=title, description=description, fragment=fragment, tags_json=tags_json)
+            revision_comment = annotation_form.cleaned_data['comment']
+            IconolabComment.objects.create(
+                comment = revision_comment,
+                revision = new_annotation.current_revision,
+                content_type = ContentType.objects.get(app_label='iconolab', model='annotation'),
+                content_object = new_annotation,
+                site = Site.objects.get(id=settings.SITE_ID),
+                object_pk = new_annotation.id,
+                user = request.user,
+                user_name = request.user.username
+            )
+            return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': new_annotation.annotation_guid}))(request)
+        context = self.get_context_data(**kwargs)
+        context['image'] = image
+        context['form'] = annotation_form
+        context['tags_data'] = '[]'
+        return render(request, 'iconolab/change_annotation.html', context)
+
+class ShowAnnotationView(View, ContextMixin, IconolabObjectView):
+
+    def get_context_data(self, **kwargs):
+        context = super(ShowAnnotationView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['image_guid'] = self.kwargs.get('image_guid', '')
+        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
+        return context
+
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image, annotation,) = result
+        else:
+            return result(request)
+        context = self.get_context_data(**kwargs)
+        context['collection'] = collection
+        context['image'] = image
+        context['annotation'] = annotation
+        context['tags_data'] = annotation.current_revision.get_tags_json()
+
+        page = request.GET.get('page', 1)
+        per_page = request.GET.get('perpage', 10)
+        full_comments_list = IconolabComment.objects.for_app_models('iconolab.annotation').filter(object_pk = annotation.pk).order_by('thread_id', '-order')
+        paginator = Paginator(full_comments_list, per_page)
+        try:
+            comments_list = paginator.page(page)
+        except PageNotAnInteger:
+            comments_list = paginator.page(1)
+        except EmptyPage:
+            comments_list = paginator.page(paginator.num_pages)
+        context['comments'] = comments_list
+
+        if request.user.is_authenticated():
+            user_comment_notifications = Notification.objects.filter(
+                recipient=request.user,
+                action_object_content_type__app_label='iconolab',
+                action_object_content_type__model='iconolabcomment',
+                target_content_type__app_label='iconolab',
+                target_content_type__model='annotation',
+                target_object_id=annotation.id
+            ).unread()
+            context['notifications_comments_ids'] = [int(val) for val in user_comment_notifications.values_list('action_object_object_id', flat=True)]
+            comment_list_ids = [comment.id for comment in context['comments'] ]
+            for notification in user_comment_notifications.all():
+                if int(notification.action_object_object_id) in comment_list_ids:
+                    notification.mark_as_read()
+
+        image.stats.views_count += 1
+        image.stats.save()
+        annotation.stats.views_count += 1
+        annotation.stats.save()
+        return render(request, 'iconolab/detail_annotation.html', context)
+
+
+class EditAnnotationView(View, ContextMixin, IconolabObjectView):
+
+    def get_context_data(self, **kwargs):
+        context = super(EditAnnotationView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['image_guid'] = self.kwargs.get('image_guid', '')
+        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
+        return context
+
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image, annotation,) = result
+        else:
+            return result(request)
+        annotation_form = AnnotationRevisionForm(instance=annotation.current_revision)
+        context = self.get_context_data(**kwargs)
+        context['image'] = image
+        context['annotation'] = annotation
+        context['form'] = annotation_form
+        context['tags_data'] = annotation.current_revision.get_tags_json()
+        return render(request, 'iconolab/change_annotation.html', context)
+
+    def post(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image, annotation) = result
+        else:
+            return result(request)
+        collection_name = kwargs['collection_name']
+        image_guid = kwargs['image_guid']
+        annotation_guid = kwargs['annotation_guid']
+        annotation_form = AnnotationRevisionForm(request.POST)
+        if annotation_form.is_valid():
+            revision_author = request.user
+            revision_title = annotation_form.cleaned_data['title']
+            revision_description = annotation_form.cleaned_data['description']
+            revision_fragment = annotation_form.cleaned_data['fragment']
+            revision_tags_json = annotation_form.cleaned_data['tags']
+            new_revision = annotation.make_new_revision(revision_author, revision_title, revision_description, revision_fragment, revision_tags_json)
+            revision_comment = annotation_form.cleaned_data['comment']
+            comment = IconolabComment.objects.create(
+                comment = revision_comment,
+                revision = new_revision,
+                content_type = ContentType.objects.get(app_label='iconolab', model='annotation'),
+                content_object = annotation,
+                site = Site.objects.get(id=settings.SITE_ID),
+                object_pk = annotation.id,
+                user = request.user,
+                user_name = request.user.username
+            )
+            return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request)
+        context = self.get_context_data(**kwargs)
+        context['image'] = image
+        context['form'] = annotation_form
+        context['annotation'] = annotation
+        context['tags_data'] = annotation.current_revision.get_tags_json()
+        return render(request, 'iconolab/change_annotation.html', context)
+
+
+class ShowRevisionView(View, ContextMixin, IconolabObjectView):
+
+    def get_context_data(self, **kwargs):
+        context = super(ShowRevisionView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['image_guid'] = self.kwargs.get('image_guid', '')
+        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
+        context['revision_guid'] = self.kwargs.get('revision_guid', '')
+        return context
+
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image, annotation, revision,) = result
+        else:
+            return result(request)
+        context = self.get_context_data(**kwargs)
+        context['collection'] = collection
+        context['image'] = image
+        context['annotation'] = annotation
+        context['revision'] = revision
+        context['tags_data'] = revision.get_tags_json()
+        context['comment'] = revision.creation_comment.first()
+        if request.user.is_authenticated() and annotation.author == request.user:
+            ann_author_notified = Notification.objects.filter(
+                    recipient=request.user,
+                    action_object_content_type__app_label='iconolab',
+                    action_object_content_type__model='annotationrevision',
+                    action_object_object_id=revision.id,
+                    target_content_type__app_label='iconolab',
+                    target_content_type__model='annotation',
+                    target_object_id=annotation.id
+                ).unread()
+            if ann_author_notified:
+                ann_author_notified.first().mark_as_read()
+                context['notified_revision'] = True
+        if request.user.is_authenticated() and revision.author == request.user:
+            rev_author_notified = Notification.objects.filter(
+                    recipient=request.user,
+                    action_object_content_type__app_label='iconolab',
+                    action_object_content_type__model='annotationrevision',
+                    action_object_object_id=revision.id,
+                    target_content_type__app_label='iconolab',
+                    target_content_type__model='annotation',
+                    target_object_id=annotation.id
+                ).unread()
+            if rev_author_notified:
+                rev_author_notified.first().mark_as_read()
+                context['notified_revision'] = True
+        return render(request, 'iconolab/detail_revision.html', context)
+
+
+class MergeProposalView(View, ContextMixin, IconolabObjectView):
+
+    def get_context_data(self, **kwargs):
+        context = super(MergeProposalView, self).get_context_data(**kwargs)
+        context['collection_name'] = self.kwargs.get('collection_name', '')
+        context['image_guid'] = self.kwargs.get('image_guid', '')
+        context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
+        context['revision_guid'] = self.kwargs.get('revision_guid', '')
+        return context
+
+    def get(self, request, *args, **kwargs):
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image, annotation, revision,) = result
+        else:
+            return result(request)
+        # Only show merge form if there is a revision to merge AND the current user is the annotation author
+        if revision.state != AnnotationRevision.AWAITING or request.user != annotation.author:
+            return RedirectView.as_view(
+                url=reverse('revision_detail',
+                    kwargs={
+                        'collection_name': collection.name,
+                        'image_guid': image.image_guid,
+                        'annotation_guid': annotation.annotation_guid,
+                        'revision_guid': revision.revision_guid
+                    }
+                )
+            )(request)
+        # Auto-accepts the revision only if the proper query arg is set and only if the revision parent is the current revision
+        if 'auto_accept' in request.GET and request.GET['auto_accept'] in ['True', 'true', '1', 'yes'] and revision.parent_revision == annotation.current_revision:
+            annotation.validate_existing_revision(revision)
+            return RedirectView.as_view(
+                url=reverse('annotation_detail',
+                    kwargs={
+                        'collection_name': collection.name,
+                        'image_guid': image.image_guid,
+                        'annotation_guid': annotation.annotation_guid
+                    }
+                )
+            )(request)
+        # Auto-reject the revision only if the proper query arg is set
+        if 'auto_reject' in request.GET and request.GET['auto_reject'] in ['True', 'true', '1', 'yes']:
+            annotation.reject_existing_revision(revision)
+            return RedirectView.as_view(
+                url=reverse('annotation_detail',
+                    kwargs={
+                        'collection_name': collection.name,
+                        'image_guid': image.image_guid,
+                        'annotation_guid': annotation.annotation_guid
+                    }
+                )
+            )(request)
+
+        context = self.get_context_data(**kwargs)
+        context['collection'] = collection
+        context['image'] = image
+        context['annotation'] = annotation
+        # Proposal data
+        context['proposal_revision'] = revision
+        context['proposal_tags_data'] = revision.get_tags_json()
+        context['proposal_comment'] = revision.creation_comment.first()
+        # Parent data
+        context['parent_revision'] = revision.parent_revision
+        context['parent_tags_data'] = revision.parent_revision.get_tags_json()
+        context['parent_comment'] = revision.parent_revision.creation_comment.first()
+        # Current data
+        context['current_revision'] = annotation.current_revision
+        context['current_tags_data'] = annotation.current_revision.get_tags_json()
+        context['current_comment'] = annotation.current_revision.creation_comment.first()
+
+        merge_form = AnnotationRevisionForm(instance=revision)
+        context['merge_form'] = merge_form
+        return render(request, 'iconolab/merge_revision.html', context)
+
+    def post(self, request, *args, **kwargs):
+        # Handle merge form submit here
+        success, result = self.check_kwargs(kwargs)
+        if success:
+            (collection, image, annotation, revision) = result
+        else:
+            return result(request)
+        collection_name = kwargs['collection_name']
+        image_guid = kwargs['image_guid']
+        annotation_guid = kwargs['annotation_guid']
+        revision_guid = kwargs['revision_guid']
+
+        merge_revision_form = AnnotationRevisionForm(request.POST)
+        if merge_revision_form.is_valid():
+            revision_title = merge_revision_form.cleaned_data['title']
+            revision_description = merge_revision_form.cleaned_data['description']
+            revision_fragment = merge_revision_form.cleaned_data['fragment']
+            revision_tags_json = merge_revision_form.cleaned_data['tags']
+            new_revision = annotation.merge_existing_revision(revision_title, revision_description, revision_fragment, revision_tags_json, revision)
+            revision_comment = merge_revision_form.cleaned_data['comment']
+            comment = IconolabComment.objects.create(
+                comment = revision_comment,
+                revision = new_revision,
+                content_type = ContentType.objects.get(app_label='iconolab', model='annotation'),
+                content_object = annotation,
+                site = Site.objects.get(id=settings.SITE_ID),
+                object_pk = annotation.id,
+                user = request.user,
+                user_name = request.user.username
+            )
+            return RedirectView.as_view(url=reverse('annotation_detail', kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': annotation_guid}))(request)
+        context = self.get_context_data(**kwargs)
+        context['image'] = image
+        context['merge_form'] = merge_revision_form
+        context['annotation'] = annotation
+        # Proposal data
+        context['proposal_revision'] = revision
+        context['proposal_tags_data'] = revision.get_tags_json()
+        context['proposal_comment'] = revision.creation_comment.first()
+        # Parent data
+        context['parent_revision'] = revision.parent_revision
+        context['parent_tags_data'] = revision.parent_revision.get_tags_json()
+        context['parent_comment'] = revision.parent_revision.creation_comment.first()
+        # Current data
+        context['current_revision'] = annotation.current_revision
+        context['current_tags_data'] = annotation.current_revision.get_tags_json()
+        context['current_comment'] = annotation.current_revision.creation_comment.first()
+        return render(request, 'iconolab/merge_revision.html', context)
+
+
+class NotFoundErrorView(TemplateView):
+    template_name='errors/404error.html'
+
+class HelpView(TemplateView):
+    template_name='iconolab/misc/glossary.html'
+    
+class CreditsView(TemplateView):
+    template_name='errors/misc/credits.html'
+
+class LegalMentionsView(TemplateView):
+    template_name='iconolab/misc/legalmentions.html'
+    
+class ContributionCharterView(TemplateView):
+    template_name='iconolab/misc/charter.html'
+