├── CNAME ├── generic ├── models.py ├── __init__.py ├── static │ ├── sass │ │ ├── _mixins.scss │ │ ├── _reset.scss │ │ ├── print.scss │ │ ├── ie.scss │ │ ├── _global.scss │ │ ├── _variables.scss │ │ ├── _form.scss │ │ ├── _grid.scss │ │ ├── _layout.scss │ │ ├── _typo.scss │ │ └── styles.scss │ ├── img │ │ ├── logo.png │ │ ├── expli1.png │ │ ├── expli2.png │ │ ├── expli3.png │ │ ├── expli4.png │ │ ├── favicon.ico │ │ ├── favicon.png │ │ ├── home_1.jpg │ │ ├── home_2.jpg │ │ ├── home_3.jpg │ │ ├── bg-footer.png │ │ ├── error404.png │ │ ├── error500.png │ │ ├── logo-grey.png │ │ ├── home-illustration.jpg │ │ └── bg-home-illustration.jpg │ ├── fonts │ │ ├── museo300-regular-webfont.eot │ │ ├── museo300-regular-webfont.ttf │ │ ├── museo300-regular-webfont.woff │ │ ├── museo700-regular-webfont.eot │ │ ├── museo700-regular-webfont.ttf │ │ ├── museo700-regular-webfont.woff │ │ └── museo700-regular-webfont.svg │ ├── css │ │ ├── print.css │ │ ├── ie.css │ │ ├── styles.css │ │ └── normalize.css │ └── config.rb ├── templates │ ├── registration │ │ ├── activation_email_subject.txt │ │ ├── logout.html │ │ ├── password_change_done.html │ │ ├── registration_complete.html │ │ ├── password_reset_done.html │ │ ├── password_reset_email.html │ │ ├── password_reset_complete.html │ │ ├── password_change_form.html │ │ ├── password_reset_form.html │ │ ├── activation_complete.html │ │ ├── activate.html │ │ ├── password_reset_confirm.html │ │ ├── activation_email.txt │ │ ├── registration_form.html │ │ └── login.html │ ├── 404.html │ ├── 500.html │ ├── dashboard.html │ └── layout.html ├── locale │ └── fr │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── views.py └── tests.py ├── pages ├── __init__.py ├── models.py ├── markdown │ ├── .gitkeep │ ├── en │ │ └── mentions-legales.md │ └── fr │ │ └── mentions-legales.md ├── locale │ └── fr │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── templates │ ├── mails │ │ ├── admin_suggest.txt │ │ ├── admin_contact.txt │ │ ├── en │ │ │ ├── contact.txt │ │ │ └── suggest.txt │ │ └── fr │ │ │ ├── contact.txt │ │ │ └── suggest.txt │ └── pages │ │ ├── markdown.html │ │ ├── confirm_contact.html │ │ └── contact.html ├── utils.py ├── forms.py ├── views.py └── tests.py ├── uxperiment ├── __init__.py ├── static │ └── .gitkeep ├── settings │ ├── local.py.sample │ ├── test.py │ ├── staging.py │ ├── __init__.py │ ├── production.py │ ├── dev.py │ └── base.py ├── config.rb ├── wsgi.py ├── utils.py └── urls.py ├── websites ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0001_initial.py │ └── 0002_auto__add_field_website_submitter.py ├── fixtures │ └── green-nature.jpg ├── locale │ └── fr │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── admin.py ├── forms.py ├── models.py ├── utils.py ├── templates │ └── websites │ │ ├── suggest.html │ │ └── index.html ├── views.py └── tests.py ├── Procfile ├── .gitignore ├── .travis.yml ├── manage.py ├── requirements.txt ├── requirements-dev.txt └── README.md /CNAME: -------------------------------------------------------------------------------- 1 | prswb.fr 2 | -------------------------------------------------------------------------------- /generic/models.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/models.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/markdown/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uxperiment/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /websites/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uxperiment/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generic/static/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /websites/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uxperiment/settings/local.py.sample: -------------------------------------------------------------------------------- 1 | # Feel free to override the base settings from here 2 | -------------------------------------------------------------------------------- /generic/static/sass/_reset.scss: -------------------------------------------------------------------------------- 1 | @import "reset/utilities"; 2 | 3 | @include global-reset; 4 | -------------------------------------------------------------------------------- /generic/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/logo.png -------------------------------------------------------------------------------- /generic/static/img/expli1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/expli1.png -------------------------------------------------------------------------------- /generic/static/img/expli2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/expli2.png -------------------------------------------------------------------------------- /generic/static/img/expli3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/expli3.png -------------------------------------------------------------------------------- /generic/static/img/expli4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/expli4.png -------------------------------------------------------------------------------- /generic/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/favicon.ico -------------------------------------------------------------------------------- /generic/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/favicon.png -------------------------------------------------------------------------------- /generic/static/img/home_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/home_1.jpg -------------------------------------------------------------------------------- /generic/static/img/home_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/home_2.jpg -------------------------------------------------------------------------------- /generic/static/img/home_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/home_3.jpg -------------------------------------------------------------------------------- /generic/static/img/bg-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/bg-footer.png -------------------------------------------------------------------------------- /generic/static/img/error404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/error404.png -------------------------------------------------------------------------------- /generic/static/img/error500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/error500.png -------------------------------------------------------------------------------- /generic/static/img/logo-grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/logo-grey.png -------------------------------------------------------------------------------- /pages/markdown/en/mentions-legales.md: -------------------------------------------------------------------------------- 1 | Legal terms 2 | 3 | Legal terms 4 | =========== 5 | 6 | To be contributed. 7 | -------------------------------------------------------------------------------- /websites/fixtures/green-nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/websites/fixtures/green-nature.jpg -------------------------------------------------------------------------------- /generic/templates/registration/activation_email_subject.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% trans "Your UXperiment membership" %} 2 | -------------------------------------------------------------------------------- /pages/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/pages/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /pages/templates/mails/admin_suggest.txt: -------------------------------------------------------------------------------- 1 | L'utilisateur : {{ data.username }}, vient de proposer le site {{ data.website }} -------------------------------------------------------------------------------- /generic/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /generic/static/img/home-illustration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/home-illustration.jpg -------------------------------------------------------------------------------- /websites/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/websites/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: python manage.py migrate;python manage.py collectstatic --noinput;python manage.py run_gunicorn -b 0.0.0.0:$PORT 2 | -------------------------------------------------------------------------------- /pages/templates/mails/admin_contact.txt: -------------------------------------------------------------------------------- 1 | Email : {{ data.sender }} 2 | Sujet : {{ data.subject }} 3 | Message : 4 | {{ data.message }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.sass-cache 3 | /test.db 4 | /dummy.db 5 | /uxperiment/media/* 6 | /uxperiment/settings/local.py 7 | .env 8 | 9 | -------------------------------------------------------------------------------- /generic/static/img/bg-home-illustration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/img/bg-home-illustration.jpg -------------------------------------------------------------------------------- /generic/static/fonts/museo300-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/fonts/museo300-regular-webfont.eot -------------------------------------------------------------------------------- /generic/static/fonts/museo300-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/fonts/museo300-regular-webfont.ttf -------------------------------------------------------------------------------- /generic/static/fonts/museo300-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/fonts/museo300-regular-webfont.woff -------------------------------------------------------------------------------- /generic/static/fonts/museo700-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/fonts/museo700-regular-webfont.eot -------------------------------------------------------------------------------- /generic/static/fonts/museo700-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/fonts/museo700-regular-webfont.ttf -------------------------------------------------------------------------------- /generic/static/fonts/museo700-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k0/prswb/master/generic/static/fonts/museo700-regular-webfont.woff -------------------------------------------------------------------------------- /pages/templates/mails/en/contact.txt: -------------------------------------------------------------------------------- 1 | Hello, 2 | 3 | Your comment on www.uxperiment.fr was recieved. 4 | 5 | Thank you for your participation. 6 | 7 | Regards, 8 | 9 | The UXperiment team. -------------------------------------------------------------------------------- /pages/templates/mails/en/suggest.txt: -------------------------------------------------------------------------------- 1 | Hello, 2 | 3 | Your suggested website on www.experiment.fr was recieved. 4 | 5 | Thank you for your participation. 6 | 7 | Regards, 8 | 9 | The UXperiment team -------------------------------------------------------------------------------- /generic/templates/registration/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "You've been succesfully logged out." %}

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /pages/templates/pages/markdown.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block meta_title %}{{ title }}{% endblock %} 5 | 6 | {% block content %} 7 | {{ html|safe }} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /pages/templates/mails/fr/contact.txt: -------------------------------------------------------------------------------- 1 | Bonjour, 2 | 3 | Votre commentaire sur le site www.uxperiment.fr a bien été pris en compte. 4 | 5 | Nous vous remercions de votre participation. 6 | 7 | A bientôt, 8 | 9 | L'équipe UXperiment -------------------------------------------------------------------------------- /pages/templates/mails/fr/suggest.txt: -------------------------------------------------------------------------------- 1 | Bonjour, 2 | 3 | Votre proposition de site internet sur le site www.uxperiment.fr a bien été pris en compte. 4 | 5 | Nous vous remercions de votre participation. 6 | 7 | A bientôt, 8 | 9 | L'équipe UXperiment -------------------------------------------------------------------------------- /generic/static/css/print.css: -------------------------------------------------------------------------------- 1 | /* Welcome to Compass. Use this file to define print styles. 2 | * Import this file using the following HTML or equivalent: 3 | * */ 4 | -------------------------------------------------------------------------------- /generic/templates/registration/password_change_done.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Password change" %}

6 |

{% trans "Password changed" %}

7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /websites/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from models import Website 4 | 5 | 6 | class WebsiteAdmin(admin.ModelAdmin): 7 | class Meta: 8 | model = Website 9 | 10 | 11 | admin.site.register(Website, WebsiteAdmin) -------------------------------------------------------------------------------- /generic/static/sass/print.scss: -------------------------------------------------------------------------------- 1 | /* Welcome to Compass. Use this file to define print styles. 2 | * Import this file using the following HTML or equivalent: 3 | * */ 4 | -------------------------------------------------------------------------------- /uxperiment/settings/test.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | DATABASES = { 4 | 'default': { 5 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 6 | 'NAME': 'test.db', 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 2.7 3 | 4 | env: DJANGO_SETTINGS_MODULE=uxperiment.settings.test 5 | 6 | # command to install depencies 7 | install: pip install -r requirements-dev.txt --use-mirrors 8 | script: python manage.py test generic websites pages 9 | -------------------------------------------------------------------------------- /generic/templates/registration/registration_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Registration" %}

6 |

{% trans "You are now registered. Activation email sent." %}

7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /generic/templates/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Password reset" %}

6 |

{% trans "Email with password reset instructions has been sent." %}

7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /generic/templates/registration/password_reset_email.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% blocktrans %}Reset password at {{ site_name }}{% endblocktrans %}: 3 | {% block reset_link %} 4 | {{ protocol }}://{{ domain }}{% url auth_password_reset_confirm uidb36=uid, token=token %} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "uxperiment.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /generic/static/css/ie.css: -------------------------------------------------------------------------------- 1 | /* Welcome to Compass. Use this file to write IE specific override styles. 2 | * Import this file using the following HTML or equivalent: 3 | * */ 6 | -------------------------------------------------------------------------------- /generic/static/sass/ie.scss: -------------------------------------------------------------------------------- 1 | /* Welcome to Compass. Use this file to write IE specific override styles. 2 | * Import this file using the following HTML or equivalent: 3 | * */ 6 | -------------------------------------------------------------------------------- /generic/templates/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Password reset" %}

6 |

{% trans "Password reset successfully" %}

7 |

{% trans "Log in" %}

8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /generic/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block body_class %}error error404{% endblock %} 5 | 6 | {% block content %} 7 |

{% trans "Error 404" %}

8 |

{% trans "Prophet lost." %}

9 | Back home 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /generic/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block body_class %}error error500{% endblock %} 5 | 6 | {% block content %} 7 |

{% trans "Error 500" %}

8 |

{% trans "Internal server error." %}

9 | Back home 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /websites/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from websites.models import Website 3 | 4 | 5 | class SuggestForm(forms.ModelForm): 6 | """ Picture field is excluded on purpose, as Heroku provided an ephemeral 7 | filesystem. 8 | """ 9 | class Meta: 10 | model = Website 11 | exclude = ('submitter','picture',) 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.4.2 2 | South==0.7.6 3 | Markdown==2.2.0 4 | -e git+git://github.com/vinyll/django-imagefit.git#egg=django-imagefit 5 | beautifulsoup4==4.1.3 6 | dj-database-url==0.2.1 7 | django-gravatar==0.1.0 8 | django-registration==0.8 9 | gunicorn==0.14.6 10 | psycopg2==2.4.5 11 | requests==0.14.0 12 | PIL==1.1.7 13 | wsgiref==0.1.2 14 | -------------------------------------------------------------------------------- /generic/templates/registration/password_change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Password change" %}

6 |
7 | {% csrf_token %} 8 | {{ form.as_p }} 9 | 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /generic/templates/registration/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Password reset" %}

6 |
7 | {% csrf_token %} 8 | {{ form.as_p }} 9 | 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /generic/templates/registration/activation_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% load i18n %} 4 | {% load url from future %} 5 | 6 | {% block content %} 7 |

{% trans "Congratulations!" %}

8 |

9 | {% trans "Your registration being complete, you can now" %} 10 | {% trans "log in" %}. 11 |

12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | Django==1.4.2 2 | South==0.7.6 3 | Markdown==2.2.0 4 | -e git+git://github.com/vinyll/django-imagefit.git#egg=django-imagefit 5 | beautifulsoup4==4.1.3 6 | django-gravatar==0.1.0 7 | django-registration==0.8 8 | django-debug-toolbar==0.9.4 9 | wsgiref==0.1.2 10 | PIL==1.1.7 11 | requests==0.14.0 12 | #heroku requirements to not add 13 | #dj-database-url==0.2.1 14 | #gunicorn==0.14.6 15 | #psycopg2==2.4.5 16 | -------------------------------------------------------------------------------- /uxperiment/settings/staging.py: -------------------------------------------------------------------------------- 1 | import os 2 | import dj_database_url 3 | 4 | from .base import * 5 | 6 | 7 | INSTALLED_APPS += ( 8 | 'gunicorn', 9 | ) 10 | 11 | DATABASES = {'default': dj_database_url.config(default='postgres://localhost')} 12 | 13 | EMAIL_RECIPIENT = os.environ.get('EMAIL_RECIPIENT') 14 | EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER') 15 | EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD') 16 | -------------------------------------------------------------------------------- /generic/templates/registration/activate.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | {% load url from future %} 4 | 5 | {% block content %} 6 | {% if account %} 7 |

{% trans "Account successfully activated" %}

8 |

{% trans "Log in" %}

9 | {% else %} 10 |

{% trans "Account activation failed" %}

11 | {% endif %} 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /pages/templates/pages/confirm_contact.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load url from future %} 3 | {% load i18n %} 4 | 5 | {% block content %} 6 |

{% trans "Thank you!" %}

7 |

{% trans "Your comment on UXperiment has been received." %}

8 | 9 |

{% trans "Thanks for your participation." %}

10 | 11 |

{% trans "Cheers," %}

12 | 13 | {% trans "The UXperiment team" %} 14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /generic/templates/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Password reset" %}

6 | {% if validlink %} 7 |
8 | {% csrf_token %} 9 | {{ form.as_p }} 10 | 11 |
12 | {% else %} 13 |

{% trans "Password reset failed" %}

14 | {% endif %} 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /generic/static/sass/_global.scss: -------------------------------------------------------------------------------- 1 | /*Floats*/ 2 | .fl { 3 | float: left; 4 | } 5 | .fr { 6 | float: right; 7 | } 8 | 9 | /*Clearing floats*/ 10 | .clear { 11 | clear: both; 12 | display: block; 13 | overflow: hidden; 14 | visibility: hidden; 15 | width: 0; 16 | height: 0; 17 | } 18 | .clearfix:before, 19 | .clearfix:after { 20 | content: ""; 21 | display: table; 22 | } 23 | .clearfix:after { clear: both; } 24 | .clearfix { *zoom:1 } 25 | 26 | ul { 27 | margin: 0 28 | } -------------------------------------------------------------------------------- /uxperiment/settings/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | UXPERIMENT_ENV = os.environ.get('UXPERIMENT_ENV') 4 | 5 | if UXPERIMENT_ENV == 'dev': 6 | from .dev import * 7 | elif UXPERIMENT_ENV == 'test': 8 | from .test import * 9 | elif UXPERIMENT_ENV == 'staging': 10 | from .staging import * 11 | elif UXPERIMENT_ENV == 'production': 12 | from .production import * 13 | else: 14 | from .base import * 15 | 16 | # local settings 17 | try: 18 | from .local import * 19 | except ImportError: 20 | pass 21 | -------------------------------------------------------------------------------- /generic/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf import settings 4 | from django.http import HttpResponseRedirect 5 | from django.utils.translation import check_for_language 6 | 7 | 8 | def change_language(request, lang): 9 | response = HttpResponseRedirect('/') 10 | if lang and check_for_language(lang): 11 | if hasattr(request, 'session'): 12 | request.session['django_language'] = lang 13 | else: 14 | response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang) 15 | return response 16 | -------------------------------------------------------------------------------- /generic/templates/registration/activation_email.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load url from future %} 3 | 4 | {% trans "Hey," %} 5 | 6 | {% trans "Thanks for registering with UXperiment." %} 7 | 8 | {% trans "Please click the link below to activate your account:" %} 9 | http://{{ site }}{% url 'registration_activate' activation_key=activation_key %} 10 | 11 | {% blocktrans %}Note: this link will expires in {{ expiration_days }} days.{% endblocktrans %} 12 | 13 | {% trans "Thank you," %} 14 | 15 | -- 16 | {% trans "The UXperiment Team" %} 17 | http://{{ site }} 18 | -------------------------------------------------------------------------------- /pages/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.conf import settings 4 | 5 | 6 | def resolve_markdown_path(slug, lang): 7 | """ Retrieves an existing markdown file path from slug and a language code. 8 | Returns None in case no matching file was found. 9 | """ 10 | i18n_filename = os.path.join(settings.MARKDOWN_DIR, lang, '%s.md' % slug) 11 | filename = os.path.join(settings.MARKDOWN_DIR, '%s.md' % slug) 12 | if os.path.isfile(i18n_filename): 13 | return i18n_filename 14 | elif os.path.isfile(filename): 15 | return filename 16 | -------------------------------------------------------------------------------- /uxperiment/settings/production.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .base import * 4 | 5 | 6 | DATABASES = { 7 | 'default': { 8 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 9 | 'USER': os.environ.get('UXPERIMENT_POSTGRES_USERNAME', 'postgres'), 10 | 'PASSWORD': os.environ.get('UXPERIMENT_POSTGRES_PASSWORD'), 11 | 'HOST': os.environ.get('UXPERIMENT_POSTGRES_HOST', 'localhost'), 12 | 'PORT': os.environ.get('UXPERIMENT_POSTGRES_PORT', 5432), 13 | } 14 | } 15 | 16 | EMAIL_RECIPIENT = os.environ.get('EMAIL_RECIPIENT') 17 | EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER') 18 | EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD') 19 | -------------------------------------------------------------------------------- /generic/static/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // === FONTS === 3 | $default-font-stack: "Open Sans", Arial, sans-serif; //main font 4 | $special-font-stack: "museo_700regular", Arial, sans-serif; //font-face 5 | $special-font-stack-light: "museo_300regular", Arial, sans-serif; //font-face 6 | 7 | // === COLORS === 8 | $secondary-color: #29abe2; //in this case, blue 9 | $links: $secondary-color; //links color 10 | $links-hover : #007; //hover links color 11 | $links-with-bg : #fff; //links with background 12 | $body-color: #4d4d4d; //text color 13 | $title-color: #29abe2; //titles color 14 | $color-for-bg: #fff; //in this case, white 15 | $border: #e6e6e6; 16 | $error: #c1272d; 17 | // === cols === 18 | -------------------------------------------------------------------------------- /generic/templates/registration/registration_form.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | {% block meta_title %}S'inscrire{% endblock %} 4 | {% block meta_description %}Inscrivez-vous sur le site afin de participer à l'expérience d'UXperiment. Contribuez à l'évolution des sites en partageant votre expérience.{% endblock %} 5 | 6 | {% block content %} 7 |

{% trans "Registration" %}

8 |

Inscrivez-vous sur le site afin de participer à UXperiment

9 |

Contribuez à l'évolution des sites en partageant votre expérience

10 |
11 | {% csrf_token %} 12 | {{ form.as_p }} 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /generic/templates/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Log in" %}

6 |

Bonjour / Hello / Ayo / Ola !

7 |

Authentifiez vous et venez nous rejoindre !

8 |
9 | {% csrf_token %} 10 | {{ form.as_p }} 11 | 12 | 13 |
14 | 15 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /generic/static/sass/_form.scss: -------------------------------------------------------------------------------- 1 | input, textarea { 2 | padding: 4px; 3 | display: inline-block; 4 | width: 560px; 5 | -webkit-border-radius: 0; 6 | -moz-border-radius: 0; 7 | border-radius: 0; 8 | border: 1px solid $border; 9 | vertical-align: top; 10 | &:invalid { 11 | border: 2px solid #ff0000; 12 | } 13 | } 14 | input:focus:required, 15 | textarea:focus:required:invalid { 16 | border: 1px solid #f00; 17 | } 18 | label { 19 | display: inline-block; 20 | width: 150px; 21 | } 22 | input[type="submit"] { 23 | padding: 5px 10px; 24 | margin: 0 0 0 153px; 25 | background: $secondary-color; 26 | color: #fff; 27 | border: none; 28 | width: auto; 29 | } 30 | input[type="file"] { 31 | width: 430px; 32 | } 33 | .errorlist { 34 | margin: 0 0 0 153px; 35 | padding: 0; 36 | li { 37 | list-style-type: none; 38 | color: $error; 39 | } 40 | } 41 | form { 42 | p { 43 | margin: 0 0 10px; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pages/forms.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from django import forms 4 | from django.utils.translation import ugettext as _ 5 | 6 | 7 | class ContactForm(forms.Form): 8 | subject = forms.CharField(min_length=3, error_messages={ 9 | 'min_length': _('3 characters minimum required.'), 10 | 'required': _('A subject is required.') 11 | }, widget=forms.TextInput(attrs={'placeholder': _('Subject') + '*'})) 12 | sender = forms.EmailField(error_messages={ 13 | 'invalid': _('A valid email address is required'), 14 | 'required': _('An email is required.') 15 | }, widget=forms.TextInput(attrs={'placeholder': _('Email address' + '*')})) 16 | message = forms.CharField(min_length=8, error_messages={ 17 | 'min_length': _('8 characters minimum required.'), 18 | 'required': _('A message is required.') 19 | }, widget=forms.Textarea(attrs={'placeholder': _('Message') + '*'})) 20 | -------------------------------------------------------------------------------- /generic/static/config.rb: -------------------------------------------------------------------------------- 1 | # Require any additional compass plugins here. 2 | 3 | # Set this to the root of your project when deployed: 4 | http_path = "/" 5 | css_dir = "css" 6 | sass_dir = "sass" 7 | images_dir = "images" 8 | javascripts_dir = "js" 9 | 10 | # You can select your preferred output style here (can be overridden via the command line): 11 | # output_style = :expanded or :nested or :compact or :compressed 12 | 13 | # To enable relative paths to assets via compass helper functions. Uncomment: 14 | # relative_assets = true 15 | 16 | # To disable debugging comments that display the original location of your selectors. Uncomment: 17 | # line_comments = false 18 | 19 | 20 | # If you prefer the indented syntax, you might want to regenerate this 21 | # project again passing --syntax sass, or you can uncomment this: 22 | # preferred_syntax = :sass 23 | # and then run: 24 | # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass 25 | -------------------------------------------------------------------------------- /uxperiment/config.rb: -------------------------------------------------------------------------------- 1 | # Require any additional compass plugins here. 2 | 3 | # Set this to the root of your project when deployed: 4 | http_path = "/" 5 | css_dir = "stylesheets" 6 | sass_dir = "sass" 7 | images_dir = "images" 8 | javascripts_dir = "javascripts" 9 | 10 | # You can select your preferred output style here (can be overridden via the command line): 11 | # output_style = :expanded or :nested or :compact or :compressed 12 | 13 | # To enable relative paths to assets via compass helper functions. Uncomment: 14 | # relative_assets = true 15 | 16 | # To disable debugging comments that display the original location of your selectors. Uncomment: 17 | # line_comments = false 18 | 19 | 20 | # If you prefer the indented syntax, you might want to regenerate this 21 | # project again passing --syntax sass, or you can uncomment this: 22 | # preferred_syntax = :sass 23 | # and then run: 24 | # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass 25 | -------------------------------------------------------------------------------- /generic/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.core.urlresolvers import reverse 4 | from django.test import TransactionTestCase 5 | 6 | from django.contrib.auth.models import User 7 | 8 | 9 | class RegistrationTest(TransactionTestCase): 10 | "Registration tests." 11 | 12 | def test_register(self): 13 | self.assertEquals(User.objects.filter(username='johndoe').count(), 0) 14 | response = self.client.get(reverse('registration_register')) 15 | self.assertEquals(response.status_code, 200) 16 | self.assertTemplateUsed(response, 'registration/registration_form.html') 17 | self.client.post(reverse('registration_register'), { 18 | 'username': 'johndoe', 19 | 'email': 'john@doe.org', 20 | 'password1': 'doepsswd', 21 | 'password2': 'doepsswd', 22 | }, follow=True) 23 | self.assertEquals(response.status_code, 200) 24 | self.assertEquals(User.objects.filter(username='johndoe').count(), 1) 25 | -------------------------------------------------------------------------------- /uxperiment/settings/dev.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | # Debug 4 | DEBUG = True 5 | TEMPLATE_DEBUG = DEBUG 6 | 7 | # Installed apps for dev only 8 | INSTALLED_APPS += ( 9 | 'debug_toolbar', 10 | ) 11 | 12 | # Dev only middlewares 13 | MIDDLEWARE_CLASSES += ( 14 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 15 | ) 16 | 17 | # Email 18 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 19 | 20 | # DJT 21 | INTERNAL_IPS = ('127.0.0.1',) 22 | DEBUG_TOOLBAR_CONFIG = {'INTERCEPT_REDIRECTS': False} 23 | DEBUG_TOOLBAR_PANELS = ( 24 | 'debug_toolbar.panels.version.VersionDebugPanel', 25 | 'debug_toolbar.panels.timer.TimerDebugPanel', 26 | 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 27 | 'debug_toolbar.panels.headers.HeaderDebugPanel', 28 | 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 29 | 'debug_toolbar.panels.template.TemplateDebugPanel', 30 | 'debug_toolbar.panels.sql.SQLDebugPanel', 31 | 'debug_toolbar.panels.signals.SignalDebugPanel', 32 | 'debug_toolbar.panels.logger.LoggingPanel', 33 | ) 34 | -------------------------------------------------------------------------------- /uxperiment/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for uxperiment project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "uxperiment.settings.production") 19 | 20 | # This application object is used by any WSGI server configured to use this 21 | # file. This includes Django's development server, if the WSGI_APPLICATION 22 | # setting points here. 23 | from django.core.wsgi import get_wsgi_application 24 | application = get_wsgi_application() 25 | 26 | # Apply WSGI middleware here. 27 | # from helloworld.wsgi import HelloWorldApplication 28 | # application = HelloWorldApplication(application) 29 | -------------------------------------------------------------------------------- /generic/static/sass/_grid.scss: -------------------------------------------------------------------------------- 1 | /* Choose a grid width, the number of columns and the margin between columns. The result of the following equation has to be an integer, not a fraction: (width - (columns - 1) * col_margin) / columns = N */ 2 | $width: 960px; /* total with of page */ 3 | // $columns: 24; /* number of columns */ 4 | // $col_margin: 0; /* margin between columns */ /* math magic */ 5 | // $col_width: ($width - ($col_margin * ($columns - 1))) / $columns; 6 | // $col_total_width: $col_width + $col_margin; /* create row div */ 7 | // @mixin row() { 8 | // float: left; 9 | // clear: both; 10 | // width: $width; 11 | // } /* create a column div */ 12 | // @mixin col($n: 1) { 13 | // float: left; 14 | // @include span($n); 15 | // } /* make an element span n columns */ 16 | // @mixin span($n: 1) { 17 | // width: ($n * $col_width) + (($n - 1) * $col_margin); 18 | // @if $n == $columns { margin-right: 0; } 19 | // @else { margin-right: $col_margin; } 20 | // } /* the last column in a row needs this */ 21 | // @mixin last() { margin-right: 0; } /* prepend n blank columns */ 22 | // @mixin prepend($n: 1) { margin-left: $col_total_width * $n } /* append n blank columns */ 23 | // @mixin append($n: 1) { margin-right: $col_total_width * $n + $col_margin } -------------------------------------------------------------------------------- /websites/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import hashlib 4 | import os 5 | 6 | from django.db import models 7 | from django.utils.translation import ugettext as _ 8 | from django.contrib.auth.models import User 9 | 10 | 11 | def website_upload_to(instance, filename): 12 | return os.path.join("websites", "%s.jpg" % hashlib.sha1(instance.url).hexdigest()) 13 | 14 | class Website(models.Model): 15 | 16 | REQUEST_COMMENT = 'comment' 17 | REQUEST_IMPROVE = 'improve' 18 | REQUEST_DEBUG = 'debug' 19 | 20 | REQUEST_CHOICES = ( 21 | (REQUEST_COMMENT, _(u"Comment")), 22 | (REQUEST_IMPROVE, _(u"Enhancement suggestion")), 23 | (REQUEST_DEBUG, _(u"Problem or bug")), 24 | ) 25 | 26 | submitter = models.ForeignKey(User, null=True) 27 | url = models.URLField(max_length=255, unique=True) 28 | title = models.CharField(max_length=255) 29 | description = models.CharField(max_length=255, null=True) 30 | picture = models.ImageField(upload_to=website_upload_to, null=True, blank=True) 31 | request_type = models.CharField(max_length=30, choices=REQUEST_CHOICES) 32 | date = models.DateTimeField(auto_now_add=True) 33 | 34 | def __unicode__(self): 35 | return unicode(self.title) 36 | 37 | class Meta: 38 | ordering = ('-date',) 39 | -------------------------------------------------------------------------------- /generic/static/sass/_layout.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 0 auto; 3 | width: $width; 4 | } 5 | header { 6 | margin: 0 0 20px; 7 | border-top: 3px solid $secondary-color; 8 | .header-block { 9 | position: relative; 10 | } 11 | nav { 12 | margin: 40px 0 0; 13 | } 14 | } 15 | .main-container { 16 | margin-bottom: 70px; 17 | } 18 | footer { 19 | background: #f2f2f2; 20 | .footer-block { 21 | padding: 30px 0 20px; 22 | border-top: 1px solid #e6e6e6; 23 | background: url(../img/bg-footer.png) right 23px no-repeat; 24 | nav { 25 | margin: 5px 40px 0 0; 26 | } 27 | } 28 | } 29 | 30 | .col3 { 31 | margin: 0 50px 70px 0; 32 | width: 270px; 33 | img { 34 | padding: 10px; 35 | margin: 0 0 10px; 36 | border: 1px solid #e6e6e6; 37 | } 38 | &:last-child { 39 | margin: 0; 40 | } 41 | h2 { 42 | text-align: center; 43 | font-size: 18px; 44 | } 45 | } 46 | .col4 { 47 | margin: 0 20px 0 0; 48 | width: 220px; 49 | text-align: center; 50 | &:last-child { 51 | margin: 0; 52 | } 53 | img { 54 | margin: 0 0 25px; 55 | } 56 | h3 { 57 | margin: 0; 58 | font-family: $special-font-stack; 59 | font-size: 17px; 60 | line-height: 20px; 61 | span { 62 | display: block; 63 | font-family: $default-font-stack; 64 | font-size: 14px; 65 | color: #999; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /uxperiment/utils.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from django.core.mail import send_mass_mail 3 | from django.conf import settings 4 | from django.template.loader import get_template 5 | from django.utils.translation import get_language, ugettext as _ 6 | 7 | 8 | from django.template import Context 9 | 10 | 11 | def send_message(confirm, data): 12 | send_mass_mail((build_user_email(confirm, data['sender']), 13 | build_admin_email(confirm, data))) 14 | 15 | 16 | def build_user_email(confirm, recipient): 17 | """ Build user message in form of send_mail """ 18 | subjects = ( 19 | ('suggest', _("Confirmation de votre proposition sur UXperiment")), 20 | ('contact', _("Commentaire sur UXperiment")), 21 | ('signin', _("Confirmation d'inscription sur UXperiment")), 22 | ) 23 | subject = dict(subjects).get(confirm) 24 | lang = get_language()[0:2] 25 | message = get_template('mails/%s/%s.txt' % (lang, confirm))\ 26 | .render(Context()) 27 | 28 | return subject, message, settings.EMAIL_HOST_USER, [recipient] 29 | 30 | 31 | def build_admin_email(confirm, data): 32 | """ Build admin message in form of send_mail """ 33 | subjects = ( 34 | ('suggest', _("Nouvelle proposition de site sur UXperiment")), 35 | ('contact', _("Nouveau contact sur UXperiment")), 36 | ) 37 | subject = dict(subjects).get(confirm) 38 | message = get_template('mails/admin_%s.txt' % confirm).render(Context(dict( 39 | data=data 40 | ))) 41 | return subject, message, settings.EMAIL_HOST_USER, [settings.EMAIL_RECIPIENT] 42 | -------------------------------------------------------------------------------- /pages/templates/pages/contact.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | {% load url from future %} 4 | {% block meta_title %}{% trans "Contact" %}{% endblock %} 5 | {% block meta_description %} 6 | {% trans "UXperiment is a collaborative platform. We're listening your remerks to make to service better. Don't hesitate to contact us, event you just want to say hello ;)" %} 7 | {% endblock %} 8 | 9 | {% block content %} 10 |

{% trans "Contact" %}

11 |

12 | {% trans "UXperiment is a mariage between websites & their user experience. Here, you are the user." %} 13 |

14 |

15 | {% trans "We're open to any remark, suggestion, contact enquiry, collaboration & beer offering." %} 16 | {% trans "For the last kind of request, note we're always ready to sacrifice for the common good." %} 17 |

18 |

19 | {% trans "For other cases, we'll be happy to provide you with an appropriate answer anyway." %} 20 |

21 |
22 | {% csrf_token %} 23 | {{ form.subject.errors }} 24 |

25 | {{ form.subject }}

26 | 27 | {{ form.sender.errors }} 28 |

29 | {{ form.sender }}

30 | 31 | {{ form.message.errors }} 32 |

33 | {{ form.message }}

34 | 35 |
36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /websites/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.forms import URLField 4 | from django.utils.translation import ugettext as _ 5 | 6 | from models import Website 7 | 8 | from bs4 import BeautifulSoup 9 | import requests 10 | 11 | import re 12 | 13 | 14 | def get_url_informations(url): 15 | """Get informations about a url""" 16 | 17 | error_invalid = False, {'error': _(u'Invalid URL')} 18 | error_exist = False, {'error': _(u'This URL has already been submitted')} 19 | error_connect = False, {'error': _(u'Failed at opening the URL')} 20 | 21 | # Check url is valid 22 | try: 23 | url_field = URLField() 24 | clean_url = url_field.clean(url) 25 | except: 26 | return error_invalid 27 | 28 | # Check url exist 29 | if exist_url(clean_url): 30 | return error_exist 31 | 32 | # Get informations 33 | req = requests.get(clean_url) 34 | if req.status_code == 200: 35 | final_url = req.url 36 | soup = BeautifulSoup(req.text) 37 | title = soup.title.string 38 | description = soup.findAll('meta', 39 | attrs={'name': re.compile("^description$", re.I)})[0].get('content') 40 | else: 41 | return error_connect 42 | 43 | # Check final url exist if different from clean url 44 | if final_url != clean_url: 45 | if exist_url(final_url): 46 | return error_exist 47 | 48 | return True, { 49 | 'url': final_url, 50 | 'title': title, 51 | 'description': description, 52 | } 53 | 54 | 55 | def exist_url(url): 56 | """Check url exist""" 57 | return Website.objects.filter(url__exact=url).exists() 58 | -------------------------------------------------------------------------------- /websites/templates/websites/suggest.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | {% block meta_title %}{% trans "Suggest a website" %}{% endblock %} 4 | {% block meta_description %}{% trans "Suggest a site to the community to share your experience on this website." %} {% trans "How? Leave a comment to tell us what you think, point out issues you would met on the website and propose new features or enhancements to improve user experience on it." %}{% endblock %} 5 | 6 | {% block content %} 7 |

{% trans "Suggest a website" %}

8 |

{% trans "Suggest a site to the community to share your experience on this website." %}

9 |

{% trans "How? Leave a comment to tell us what you think, point out issues you would met on the website and propose new features or enhancements to improve user experience on it." %}

10 | {% if errors %} 11 |

{% trans "Please correct the errors highlighted below." %}

12 | {% endif %} 13 |
14 | {% csrf_token %} 15 | {{ form.url.errors }} 16 |

17 | {{ form.url }}

18 | 19 | {{ form.title.errors }} 20 |

21 | {{ form.title }}

22 | 23 | {{ form.description.errors }} 24 |

25 | {{ form.description }}

26 | 27 | {{ form.request_type.errors }} 28 |

29 | {{ form.request_type }}

30 | 31 | 32 |
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /generic/static/sass/_typo.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'museo_300regular'; 3 | src: url('../fonts/museo300-regular-webfont.eot'); 4 | src: url('../fonts/museo300-regular-webfont.eot?#iefix') format('embedded-opentype'), 5 | url('../fonts/museo300-regular-webfont.woff') format('woff'), 6 | url('../fonts/museo300-regular-webfont.ttf') format('truetype'), 7 | url('../fonts/museo300-regular-webfont.svg#museo_300regular') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | @font-face { 13 | font-family: 'museo_700regular'; 14 | src: url('../fonts/museo700-regular-webfont.eot'); 15 | src: url('../fonts/museo700-regular-webfont.eot?#iefix') format('embedded-opentype'), 16 | url('../fonts/museo700-regular-webfont.woff') format('woff'), 17 | url('../fonts/museo700-regular-webfont.ttf') format('truetype'), 18 | url('../fonts/museo700-regular-webfont.svg#museo_700regular') format('svg'); 19 | font-weight: normal; 20 | font-style: normal; 21 | } 22 | 23 | html { 24 | font-size: 100%; 25 | } 26 | body { 27 | font-family: $default-font-stack; 28 | font-size: 0.813em; 29 | font-weight: 400; 30 | // line-height: 1.06em; 31 | color: $body-color; 32 | } 33 | 34 | h1 { 35 | margin: 0 0 0.972em; 36 | font-weight: 300; 37 | font-size: 2.250em ; 38 | span { 39 | font-weight: 800; 40 | } 41 | } 42 | h2 { 43 | margin: 0 0 0.682em; 44 | font-weight: 600; 45 | font-size: 1.375em; 46 | } 47 | h3 { 48 | margin: 0 0 1.250em; 49 | font-weight: 600; 50 | font-size: 1em; 51 | } 52 | a { 53 | color: $body-color; 54 | text-decoration: underline; 55 | &:hover { color: $links;} 56 | } 57 | 58 | nav { 59 | font-size: 1em; 60 | } 61 | input { 62 | font-size: 0.938em; 63 | } 64 | .chapo { 65 | font-style: italic; 66 | } -------------------------------------------------------------------------------- /websites/templates/websites/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load gravatar i18n %} 3 | {% load imagefit %} 4 | {% block meta_title %}{% trans "Suggested websites" %}{% endblock %} 5 | {% block meta_description %}{% trans "Discover suggested websites and contribute to tests 6 | on these sites by sharing your experience." %}{% endblock %} 7 | 8 | {% block content %} 9 |

{% trans "Suggested websites" %}

10 |

{% trans "Suggest a site to the community to share your experience on this website." %}

11 |

{% trans "How? Leave a comment to tell us what you think, point out issues you would met on the website and propose new features or enhancements to improve user experience on it." %}

12 | {% if websites %} 13 | 30 | {% else %} 31 |

{% trans "No suggested website yet." %}

32 |

{% trans "Suggest a website" %}

33 | {% endif %} 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /pages/views.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import codecs 4 | import markdown 5 | 6 | from django.contrib import messages 7 | from django.http import Http404 8 | from django.shortcuts import render, redirect 9 | from django.utils.translation import get_language, ugettext as _ 10 | from django.views.decorators.csrf import csrf_protect 11 | 12 | from uxperiment.utils import send_message 13 | 14 | from pages.forms import ContactForm 15 | from utils import resolve_markdown_path 16 | 17 | 18 | def dashboard(request): 19 | return render(request, 'dashboard.html') 20 | 21 | 22 | @csrf_protect 23 | def contact(request): 24 | """ Contact form 25 | Display and proceed contact form submission 26 | """ 27 | form = ContactForm(request.POST or None) 28 | if form.is_valid(): 29 | send_message('contact', form.cleaned_data) 30 | return redirect('confirm_contact') 31 | 32 | return render(request, 'pages/contact.html', {'form': form}) 33 | 34 | 35 | def confirm_contact(request): 36 | """ Contact form confirmation """ 37 | return render(request, 'pages/confirm_contact.html') 38 | 39 | 40 | def markdown_page(request, slug): 41 | """ 42 | Computes markdown file path, converts its contents to html and renders it. 43 | Otherwise, if the file doesn't exist, the view returns a 404. 44 | """ 45 | filename = resolve_markdown_path(slug, get_language()) 46 | if not filename: 47 | raise Http404 48 | try: 49 | input_file = codecs.open(filename, mode="r", encoding="utf-8") 50 | content = input_file.readlines() 51 | except IOError: 52 | # TODO: Shouldn't we log these kinds of errors? 53 | messages.error(request, _(u"Unable to render page, sorry.")) 54 | finally: 55 | input_file.close() 56 | return render(request, 'pages/markdown.html', { 57 | 'html': markdown.markdown("\n".join(content[2:])), 58 | 'title': content[0].replace("\n", ''), 59 | }) 60 | -------------------------------------------------------------------------------- /websites/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import json 4 | 5 | from django.contrib import messages 6 | from django.contrib.auth.decorators import login_required 7 | from django.http import HttpResponse, Http404 8 | from django.shortcuts import render, redirect 9 | from django.utils.translation import ugettext as _ 10 | 11 | from websites.utils import get_url_informations 12 | from websites.forms import SuggestForm 13 | from models import Website 14 | from uxperiment.utils import send_message 15 | 16 | 17 | def index(request): 18 | """ Websites index. 19 | """ 20 | return render(request, 'websites/index.html', { 21 | 'websites': Website.objects.all(), 22 | }) 23 | 24 | 25 | @login_required 26 | def suggest(request): 27 | """ Suggest form 28 | Display and proceed suggest a website form submission 29 | """ 30 | form = SuggestForm( 31 | request.POST or None, 32 | request.FILES or None, 33 | initial=dict(url='http://') 34 | ) 35 | 36 | if form.is_valid(): 37 | website = form.save(commit=False) 38 | website.submitter = request.user 39 | website.save() 40 | send_message('suggest', { 41 | 'website': website.url, 42 | 'username': request.user.username, 43 | 'sender': request.user.email 44 | }) 45 | messages.success(request, _('Thanks for your proposition.')) 46 | return redirect('websites_index') 47 | 48 | return render(request, 'websites/suggest.html', {'form': form}) 49 | 50 | 51 | def informations(request): 52 | """ Get informations about a website. 53 | """ 54 | if not request.is_ajax(): 55 | raise Http404("This view should be called in ajax") 56 | success = False 57 | status = 400 58 | infos = {'error': _(u'Invalid request')} 59 | url = request.GET.get('url', False) 60 | if url: 61 | success, infos = get_url_informations(url) 62 | status = 200 if success else 409 63 | return HttpResponse(json.dumps(infos), 64 | content_type="application/json", status=status) 65 | -------------------------------------------------------------------------------- /websites/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding model 'Website' 12 | db.create_table('websites_website', ( 13 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 14 | ('url', self.gf('django.db.models.fields.URLField')(unique=True, max_length=255)), 15 | ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), 16 | ('description', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)), 17 | ('picture', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True)), 18 | ('request_type', self.gf('django.db.models.fields.CharField')(max_length=30)), 19 | ('date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), 20 | )) 21 | db.send_create_signal('websites', ['Website']) 22 | 23 | 24 | def backwards(self, orm): 25 | # Deleting model 'Website' 26 | db.delete_table('websites_website') 27 | 28 | 29 | models = { 30 | 'websites.website': { 31 | 'Meta': {'object_name': 'Website'}, 32 | 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 33 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), 34 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 35 | 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True'}), 36 | 'request_type': ('django.db.models.fields.CharField', [], {'max_length': '30'}), 37 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 38 | 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '255'}) 39 | } 40 | } 41 | 42 | complete_apps = ['websites'] -------------------------------------------------------------------------------- /uxperiment/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf.urls import patterns, include, url 4 | from django.conf import settings 5 | from django.conf.urls.i18n import i18n_patterns 6 | from django.contrib import admin 7 | from django import views as django_views 8 | 9 | from generic import views as generic_views 10 | from pages import views as pages_views 11 | from websites import views as websites_views 12 | from registration.backends.default import urls as registration_urls 13 | from imagefit import urls as imagefit_urls 14 | 15 | admin.autodiscover() 16 | 17 | urlpatterns = i18n_patterns('', 18 | url(r'^$', pages_views.dashboard, 19 | name='homepage'), 20 | url(r'^i18n/(?P[a-z]{2,})/$', generic_views.change_language, 21 | name='change_language'), 22 | 23 | # contact 24 | url(r'^contact/$', pages_views.contact, 25 | name='contact'), 26 | url(r'^contact/merci/$', pages_views.confirm_contact, 27 | name='confirm_contact'), 28 | 29 | # website 30 | url(r'^sites-proposes/$', websites_views.index, 31 | name='websites_index'), 32 | url(r'^proposer-un-site/$', websites_views.suggest, 33 | name='suggest_website'), 34 | url(r'^informations-sur-un-site/$', websites_views.informations, 35 | name='informations_website'), 36 | 37 | # registration, accounts 38 | url(r'^compte/', include(registration_urls)), 39 | 40 | # pages 41 | url(r'^pages/(?P[-\w\d]+)/$', pages_views.markdown_page, 42 | name='markdown_page'), 43 | ) 44 | 45 | # non i18 urls 46 | urlpatterns += patterns('', 47 | # errors 48 | url(r'^404/', django_views.generic.simple.direct_to_template, {'template': '404.html'}), 49 | url(r'^500/', django_views.generic.simple.direct_to_template, {'template': '500.html'}), 50 | # imagefit 51 | url(r'^imagefit/', include(imagefit_urls)), 52 | # admin 53 | url(r'^admin/', include(admin.site.urls)), 54 | # static files 55 | url(r'^static/(?P.*)$', django_views.static.serve, { 56 | 'document_root': settings.STATIC_ROOT, 57 | }), 58 | # media files 59 | url(r'^media/(?P.*)$', django_views.static.serve, { 60 | 'document_root': settings.MEDIA_ROOT, 61 | }), 62 | ) 63 | -------------------------------------------------------------------------------- /websites/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from django.core.urlresolvers import reverse 6 | from django.test import TestCase, TransactionTestCase 7 | 8 | from django.contrib.auth.models import User 9 | 10 | from models import Website 11 | 12 | 13 | class WebsiteFixtures(object): 14 | def get_config(self): 15 | return dict( 16 | title="Prswb", 17 | url="http://prswb.fr", 18 | description="Scrum vu des petites tranchees.", 19 | request_type=Website.REQUEST_COMMENT) 20 | 21 | def generate_websites(self): 22 | return Website.objects.create(**self.get_config()) 23 | 24 | 25 | class WebsiteModelTest(TestCase, WebsiteFixtures): 26 | pass 27 | 28 | 29 | class ViewListTest(TestCase, WebsiteFixtures): 30 | def setUp(self): 31 | self.website = self.generate_websites() 32 | 33 | def test_displays_website(self): 34 | response = self.client.get('/fr/sites-proposes/') 35 | self.assertEqual(response.status_code, 200) 36 | self.assertIn(self.website.url, response.content) 37 | self.assertIn(self.website.title, response.content) 38 | 39 | 40 | class SubmitWebsiteTest(TransactionTestCase, WebsiteFixtures): 41 | def setUp(self): 42 | self.user = User.objects.create(username='jdoe') 43 | self.user.set_password('jdoepswd') 44 | self.user.save() 45 | 46 | def test_suggest_website_unauthenticated(self): 47 | response = self.client.get(reverse('suggest_website'), follow=True) 48 | self.assertEqual(response.status_code, 200) 49 | self.assertTemplateUsed(response, 'registration/login.html') 50 | 51 | def test_suggest_website_authenticated(self): 52 | self.client.login(username='jdoe', password='jdoepswd') 53 | response = self.client.get(reverse('suggest_website'), follow=True) 54 | self.assertEqual(response.status_code, 200) 55 | self.assertTemplateUsed(response, 'websites/suggest.html') 56 | with open(os.path.join(os.path.dirname(__file__), 'fixtures', 'green-nature.jpg')) as fp: 57 | website_data = self.get_config() 58 | website_data.update(picture=fp) 59 | response = self.client.post(reverse('suggest_website'), website_data, follow=True) 60 | self.assertEqual(response.status_code, 200) 61 | self.assertTemplateUsed(response, 'websites/index.html') 62 | self.assertIn("http://prswb.fr", response.content) 63 | 64 | 65 | class UtilsTest(TestCase): 66 | def test_url_informations(self): 67 | pass 68 | -------------------------------------------------------------------------------- /generic/templates/dashboard.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load i18n %} 3 | {% block meta_title %}Accueil{% endblock %} 4 | {% block meta_description %}Contribuer à l'évolution des sites{% endblock %} 5 | 6 | {% block body_class %}home{% endblock %} 7 | 8 | {% block content %} 9 |
10 |
11 |

12 | {% blocktrans %}Contribute to website evolution{% endblocktrans %} 13 |

14 |
15 |
16 |
17 |

UXperiment, {% trans "the Web, better." %}

18 |
19 | illu c'est quoi? 20 |

{% trans "UXperiment, le wat?" %}

21 |

{% trans "This original project intends to let users discuss and share their experience about websites" %}

22 |
23 |
24 | illu proposer site 25 |

{% trans "Suggest a website" %}

26 |

27 | {% trans "Once registered, submit a website and tell the world how you've experienced it, and how it could be enhanced." %} 28 | {% trans "Other users then may engage a discussion about it." %} 29 |

30 |
31 |
32 | illu contribuer evolution 33 |

{% trans "Help to make it better!" %}

34 |

{% trans "Search for already reviewed websites, add your comments & experience, suggest enhancements and report problems." %}

35 |
36 |
37 |
38 |
39 |

{% trans "Ok." %} {% trans "How does it work?" %}

40 |
41 | 42 |

43 | {% trans "Submit your site." %} 44 | ({% trans "We'll take care of it" %}) 45 |

46 |
47 |
48 | 49 |

50 | {% trans "Relax." %} 51 | ({% trans "zombo.com" %}) 52 |

53 |
54 |
55 | 56 |

57 | {% trans "Get feedback." %} 58 | ({% trans "honest, always" %}) 59 |

60 |
61 |
62 | 63 |

64 | {% trans "Become powerful" %} 65 | ({% trans "world is yours" %}) 66 |

67 |
68 |
69 |
70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /pages/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-10-18 13:49+0200\n" 11 | "PO-Revision-Date: 2012-10-18 13:50+0100\n" 12 | "Last-Translator: Nicolas Perriault \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 18 | "X-Generator: Poedit 1.5.4\n" 19 | 20 | msgid "3 characters minimum required." 21 | msgstr "3 caractères minimum sont requis." 22 | 23 | msgid "A subject is required." 24 | msgstr "Un sujet doit être renseigné." 25 | 26 | msgid "Subject" 27 | msgstr "Sujet" 28 | 29 | msgid "A valid email address is required" 30 | msgstr "Une adresse email valide doit être renseignée." 31 | 32 | msgid "An email is required." 33 | msgstr "Une adresse email est demandée." 34 | 35 | msgid "Email address" 36 | msgstr "Adresse email" 37 | 38 | msgid "8 characters minimum required." 39 | msgstr "8 caractères minimum sont requis." 40 | 41 | msgid "A message is required." 42 | msgstr "Un message est demandé." 43 | 44 | msgid "Message" 45 | msgstr "Message" 46 | 47 | msgid "Unable to render page, sorry." 48 | msgstr "Impossible d'afficher la page, désolé." 49 | 50 | msgid "Thank you!" 51 | msgstr "Merci !" 52 | 53 | msgid "Your comment on UXperiment has been received." 54 | msgstr "Votre commentaire sur UXperiment a bien été reçu." 55 | 56 | msgid "Thanks for your participation." 57 | msgstr "Merci de votre participation." 58 | 59 | msgid "Cheers," 60 | msgstr "À bientôt," 61 | 62 | msgid "The UXperiment team" 63 | msgstr "L'équipe UXperiment" 64 | 65 | msgid "Contact" 66 | msgstr "Contact" 67 | 68 | msgid "" 69 | "UXperiment is a collaborative platform. We're listening your remerks to make " 70 | "to service better. Don't hesitate to contact us, event you just want to say " 71 | "hello ;)" 72 | msgstr "" 73 | "UXperiment est un site qui se veut collaboratif. Nous sommes à l'écoute de " 74 | "toutes remarques qui puissent nous faire avancer. N'hésitez pas à nous " 75 | "contacter, même pour nous dire 3 mots ;)" 76 | 77 | msgid "" 78 | "UXperiment is a mariage between websites & their user experience. Here, you " 79 | "are the user." 80 | msgstr "" 81 | "UXperiment, c’est le mariage de l’experience utilisateur ET de " 82 | "l’utilisateur. Et ici, l'utilisateur, c’est vous." 83 | 84 | msgid "" 85 | "We're open to any remark, suggestion, contact enquiry, collaboration & beer " 86 | "offering." 87 | msgstr "" 88 | "Nous sommes ouvert à toute remarque, prise de contact, collaboration, " 89 | "invitation à des banquets et divers apéro." 90 | 91 | msgid "" 92 | "For the last kind of request, note we're always ready to sacrifice for the " 93 | "common good." 94 | msgstr "" 95 | "S'il s’agit de ces 2 derniers, sachez que nous sommes toujours prêts à nous " 96 | "sacrifier pour le bien commun, donc n’ayez aucune hésitations." 97 | 98 | msgid "" 99 | "For other cases, we'll be happy to provide you with an appropriate answer " 100 | "anyway." 101 | msgstr "Dans les autres cas, nous serons aussi très heureux de vous répondre." 102 | 103 | msgid "Email" 104 | msgstr "Email" 105 | 106 | msgid "Send" 107 | msgstr "Envoyer" 108 | -------------------------------------------------------------------------------- /generic/templates/layout.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load url from future %} 3 | {% get_current_language as LANGUAGE_CODE %} 4 | 5 | 6 | 7 | 8 | 9 | UXperiment - {% block meta_title %}{% endblock %} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | 26 | 27 |
28 | {% if user.is_authenticated %} 29 | {% trans "Hello," %} {{ user.username }} / 30 | {% trans "Log out" %} 31 | {% else %} 32 | > {% trans "Register" %} / 33 | {% trans "Log in" %} 34 | {% endif %} 35 |
36 | 37 | 44 |
45 |
46 | 47 |
48 |
49 | {% if messages %} 50 |
    51 | {% for message in messages %} 52 | {{ message }} 53 | {% endfor %} 54 |
55 | {% endif %} 56 | {% block content %}{% trans "No content yet." %}{% endblock %} 57 |
58 |
59 | 60 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /websites/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-10-18 13:56+0200\n" 11 | "PO-Revision-Date: 2012-10-18 14:21+0100\n" 12 | "Last-Translator: Nicolas Perriault \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 18 | "X-Generator: Poedit 1.5.4\n" 19 | 20 | msgid "Comment" 21 | msgstr "Commentaire" 22 | 23 | msgid "Enhancement suggestion" 24 | msgstr "Suggestion d'amélioration" 25 | 26 | msgid "Problem or bug" 27 | msgstr "Problème/bug" 28 | 29 | msgid "Invalid URL" 30 | msgstr "Adresse invalide" 31 | 32 | msgid "This URL has already been submitted" 33 | msgstr "Cette URL a déjà été suggérée" 34 | 35 | msgid "Failed at opening the URL" 36 | msgstr "Échec d'ouverture de l'URL" 37 | 38 | msgid "Thanks for your proposition." 39 | msgstr "Merci pour cette proposition." 40 | 41 | msgid "Invalid request" 42 | msgstr "Requête invalide" 43 | 44 | msgid "Suggested websites" 45 | msgstr "Sites proposés" 46 | 47 | msgid "" 48 | "Suggest a site to the community to share your experience on this website." 49 | msgstr "" 50 | "Proposez un site à la communauté afin de partager votre expérience sur ce " 51 | "site." 52 | 53 | msgid "" 54 | "How? Leave a comment to tell us what you think, point out issues you would " 55 | "met on the website and propose new features or enhancements to improve user " 56 | "experience on it." 57 | msgstr "" 58 | "Comment ? Il vous suffit de nous indiquer ce que vous en pensez à l'aide " 59 | "d'un commentaire, de signaler les problèmes que vous avez soulevés sur le " 60 | "site et/ ou d'émettre des propositions de fonctionnalités ou d'améliorations " 61 | "que vous suggérez pour améliorer l'expérience utilisateur du site." 62 | 63 | msgid "submitted on" 64 | msgstr "proposé le" 65 | 66 | msgid "by" 67 | msgstr "par" 68 | 69 | msgid "No suggested website yet." 70 | msgstr "Aucun site proposé actuellement." 71 | 72 | msgid "Suggest a website" 73 | msgstr "Proposer un site" 74 | 75 | msgid "Suggest a website to share your experience of it as a user." 76 | msgstr "" 77 | "Proposez un site à la communauté afin de partager votre expérience sur ce " 78 | "site." 79 | 80 | msgid "Suggest a website to be reviewed by the UXperiment community." 81 | msgstr "" 82 | "Proposez un site à la communauté afin de partager votre expérience sur ce " 83 | "site." 84 | 85 | msgid "" 86 | "How? Just tell about what you're thinking of the website with a comment, " 87 | "raise any problem you encountered with it or suggest how it could be " 88 | "improved for a better user experience." 89 | msgstr "" 90 | "Comment? Il suffit de nous indiquer ce que vous en pensez à l'aide d'un " 91 | "commentaire, de signaler les problèmes que vous avez soulevez et/ ou " 92 | "d'émettre des propositions de fonctionnalités ou d'amélioration que vous " 93 | "suggérez pour améliorer l'expérience utilisateur du site." 94 | 95 | msgid "Please correct the errors highlighted below." 96 | msgstr "Veuillez corriger les erreurs pointées ci-dessous." 97 | 98 | msgid "Website address (URL)" 99 | msgstr "Adresse du site (URL)" 100 | 101 | msgid "Site name" 102 | msgstr "Nom du site" 103 | 104 | msgid "Site description" 105 | msgstr "Description du site" 106 | 107 | msgid "Request type" 108 | msgstr "Type de demande" 109 | 110 | msgid "Suggest this website" 111 | msgstr "Proposer ce site" 112 | 113 | #~ msgid "Thank you" 114 | #~ msgstr "Merci" 115 | 116 | #~ msgid "Submit" 117 | #~ msgstr "Envoyer" 118 | -------------------------------------------------------------------------------- /generic/static/sass/styles.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "typo"; 3 | @import "mixins"; 4 | @import "grid"; 5 | @import "layout"; 6 | @import "form"; 7 | @import "global"; 8 | 9 | #logo { 10 | margin: 35px 0 0 11 | } 12 | .btn-subscribe { 13 | position: absolute; 14 | top:0; 15 | right: 0; 16 | margin: 0 0 15px; 17 | padding: 5px 15px; 18 | background: $secondary-color; 19 | color: $color-for-bg; 20 | font-weight: 600; 21 | a { 22 | color: $links-with-bg; 23 | text-decoration: none; 24 | &:first-child { 25 | padding: 0 10px 0 0; 26 | } 27 | &:last-child { 28 | padding: 0 0 0 10px; 29 | } 30 | } 31 | } 32 | nav { 33 | li { 34 | margin: 0 15px 0 0; 35 | &:last-child { 36 | margin: 0; 37 | &:after { 38 | content: ""; 39 | } 40 | a { 41 | padding: 0; 42 | } 43 | } 44 | &:after { 45 | content:"|"; 46 | } 47 | } 48 | a{ 49 | padding: 0 15px 0 0; 50 | color: $body-color; 51 | text-transform: uppercase; 52 | text-decoration: none; 53 | font-weight: 600; 54 | &:hover { 55 | color: $secondary-color; 56 | } 57 | } 58 | } 59 | .home-illustration { 60 | height: 430px; 61 | background: url(../img/home-illustration.jpg) center bottom no-repeat, 62 | url(../img/bg-home-illustration.jpg) 0 0 repeat-x; 63 | } 64 | .illustration-title { 65 | margin: 0; 66 | padding: 70px 0 0; 67 | text-transform: uppercase; 68 | font-size: 30px; 69 | span { 70 | display: block; 71 | font-weight: 700; 72 | font-size: 36px; 73 | } 74 | } 75 | .home-title { 76 | margin: 70px 0 60px; 77 | text-align: center; 78 | font-size: 36px; 79 | } 80 | .home-with-bg { 81 | padding: 0 0 75px; 82 | background: #f2f2f2; 83 | border-top: 1px solid #e6e6e6; 84 | } 85 | .link-form { 86 | margin: 20px 0 0 153px; 87 | & + .link-form{ 88 | margin-top: 0px; 89 | } 90 | } 91 | 92 | ul.websites { 93 | padding: 0; 94 | margin: 40px 0px; 95 | > li { 96 | display: inline-block; 97 | width: 255px; 98 | height: 260px; 99 | margin: 0 40px 25px 0; 100 | padding: 10px; 101 | -webkit-box-shadow: 0px 0px 5px #333333; 102 | -moz-box-shadow: 0px 0px 5px #333333; 103 | box-shadow: 0px 0px 5px #333333; 104 | > h3 { 105 | margin: 0; 106 | > a { 107 | color: $secondary-color; 108 | font-size: 1.5em; 109 | font-weight: 400; 110 | text-decoration: none; 111 | font-family: $default-font-stack; 112 | } 113 | } 114 | > .info { 115 | margin: 0; 116 | color: #999; 117 | font-style: italic; 118 | height: 40px; 119 | } 120 | > img { 121 | margin: 5px 0; 122 | } 123 | > a{ 124 | text-decoration: none; 125 | } 126 | } 127 | } 128 | 129 | ul.messages { 130 | padding: 0 !important; 131 | margin: 0 !important; 132 | list-style-type: none; 133 | .info, .error, .success, .warning { 134 | padding: 8px 15px; 135 | } 136 | .info { 137 | background: #F2F7FF; 138 | color: #84B0F6; 139 | } 140 | .error { 141 | background: #FCE2E2; 142 | color: #F66; 143 | } 144 | .success { 145 | background: #F2FEF2; 146 | color: #7FBA7F; 147 | } 148 | .warning { 149 | background: #FFF0D2; 150 | color: #F93; 151 | } 152 | } 153 | 154 | body.error { 155 | > #content { 156 | background: none no-repeat center 100px; 157 | min-height: 400px; 158 | padding-bottom: 100px; 159 | margin-bottom: 30px; 160 | 161 | h1 { 162 | font-size: 3em; 163 | text-transform: uppercase; 164 | margin: 0; 165 | } 166 | h2 { 167 | font-size: 3em; 168 | text-transform: uppercase; 169 | } 170 | a { 171 | background: $secondary-color; 172 | color: $color-for-bg; 173 | text-decoration: none; 174 | padding: 10px 20px; 175 | font-size: 1.3em; 176 | } 177 | a:before { 178 | content: "> "; 179 | } 180 | } 181 | } 182 | body.error404 > #content { 183 | background-image: url('../img/error404.png'); 184 | } 185 | body.error500 > #content { 186 | background-image: url('../img/error500.png'); 187 | } 188 | -------------------------------------------------------------------------------- /websites/migrations/0002_auto__add_field_website_submitter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | 8 | class Migration(SchemaMigration): 9 | 10 | def forwards(self, orm): 11 | # Adding field 'Website.submitter' 12 | db.add_column('websites_website', 'submitter', 13 | self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True), 14 | keep_default=False) 15 | 16 | 17 | def backwards(self, orm): 18 | # Deleting field 'Website.submitter' 19 | db.delete_column('websites_website', 'submitter_id') 20 | 21 | 22 | models = { 23 | 'auth.group': { 24 | 'Meta': {'object_name': 'Group'}, 25 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 26 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 27 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 28 | }, 29 | 'auth.permission': { 30 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 31 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 32 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 33 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 34 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 35 | }, 36 | 'auth.user': { 37 | 'Meta': {'object_name': 'User'}, 38 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 39 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 40 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 41 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 42 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 43 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 44 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 45 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 46 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 47 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 48 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 49 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 50 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 51 | }, 52 | 'contenttypes.contenttype': { 53 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 54 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 55 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 56 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 57 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 58 | }, 59 | 'websites.website': { 60 | 'Meta': {'ordering': "('-date',)", 'object_name': 'Website'}, 61 | 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 62 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), 63 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 64 | 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True'}), 65 | 'request_type': ('django.db.models.fields.CharField', [], {'max_length': '30'}), 66 | 'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), 67 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 68 | 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '255'}) 69 | } 70 | } 71 | 72 | complete_apps = ['websites'] -------------------------------------------------------------------------------- /pages/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from django.test import TestCase 6 | from django.conf import settings 7 | from django.core import mail 8 | 9 | 10 | class MarkdownPageTest(TestCase): 11 | 12 | def test_page_existence(self): 13 | """ 14 | Tests that you can reach a page if the markdown file exists. 15 | """ 16 | # if the page doesn't exist, we should have a 404 17 | response = self.client.get('/fr/pages/this-page-doesnt-exist/') 18 | self.assertEqual(response.status_code, 404) 19 | 20 | # if the page exists, we should have rendered HTML 21 | response = self.client.get('/fr/pages/mentions-legales/') 22 | self.assertTemplateUsed(response, 'pages/markdown.html') 23 | self.assertContains(response, "

Mentions légales

") 24 | 25 | def test_page_life(self): 26 | """ 27 | Tests that you can create a page via a newly created markdown 28 | file and still raise a 404 on deletion. 29 | """ 30 | # root pages 31 | test_filepath = os.path.join(settings.MARKDOWN_DIR, 'test-page.md') 32 | if os.path.isfile(test_filepath): 33 | os.remove(test_filepath) 34 | # if the page doesn't exist, we should have a 404 35 | response = self.client.get('/fr/pages/test-page/') 36 | self.assertEqual(response.status_code, 404) 37 | 38 | # i18n mardown file 39 | with open(test_filepath, 'w') as markdown_file: 40 | markdown_file.write("Test page title\n\n# This is a test for all languages") 41 | 42 | # check the HTML dynamic rendering 43 | response = self.client.get('/fr/pages/test-page/') 44 | self.assertContains(response, "UXperiment - Test page title") 45 | self.assertContains(response, "

This is a test for all languages

") 46 | 47 | os.remove(test_filepath) 48 | 49 | # i18n pages 50 | for lang in [lng[0] for lng in settings.LANGUAGES]: 51 | # preliminary checks 52 | test_filepath = os.path.join(settings.MARKDOWN_DIR, lang, 53 | 'test-page-%s.md' % lang) 54 | if os.path.isfile(test_filepath): 55 | os.remove(test_filepath) 56 | 57 | # if the page doesn't exist, we should have a 404 58 | response = self.client.get('/%s/pages/test-page-%s/' % (lang, lang,)) 59 | self.assertEqual(response.status_code, 404) 60 | 61 | # i18n mardown file 62 | with open(test_filepath, 'w') as markdown_file: 63 | markdown_file.write("Title in %(lang)s\n\n# This is a test in %(lang)s" 64 | % dict(lang=lang)) 65 | 66 | # check the HTML dynamic rendering 67 | response = self.client.get('/%s/pages/test-page-%s/' % (lang, lang,)) 68 | self.assertContains(response, "

This is a test in %s

" % lang) 69 | 70 | # remove the test file 71 | os.remove(test_filepath) 72 | 73 | # if the file has been removed, we should get back to a 404 74 | response = self.client.get('/%s/pages/test-page-%s/' % (lang, lang,)) 75 | self.assertEqual(response.status_code, 404) 76 | 77 | 78 | class ContactPageTest(TestCase): 79 | 80 | def test_contact_submission(self): 81 | # first, we verify that the page exist 82 | response = self.client.get('/fr/contact/') 83 | self.assertEqual(response.status_code, 200) 84 | self.assertContains(response, "Contact") 85 | 86 | # then we submit the form and verify the redirection 87 | response = self.client.post('/fr/contact/', { 88 | "subject": "A test", 89 | "sender": "test@example.org", 90 | "message": "This website is wonderful!" 91 | }) 92 | self.assertRedirects(response, '/fr/contact/merci/') 93 | 94 | # we verify that emails has been sent to the admin and the user 95 | self.assertEqual(len(mail.outbox), 2) 96 | self.assertEqual(mail.outbox[0].subject, 'Commentaire sur UXperiment') 97 | self.assertEqual(mail.outbox[1].subject, 'Nouveau contact sur UXperiment') 98 | self.assertEqual(mail.outbox[1].body, 99 | u'Email : test@example.org\nSujet : A test\nMessage :\nThis website is wonderful!') 100 | 101 | def test_bad_submission(self): 102 | # invalid subject 103 | response = self.client.post('/fr/contact/', { 104 | "subject": "", 105 | "sender": "test@example.org", 106 | "message": "This website is wonderful!" 107 | }) 108 | self.assertFormError(response, 'form', 'subject', 109 | [u'Un sujet doit \xeatre renseign\xe9.']) 110 | 111 | response = self.client.post('/fr/contact/', { 112 | "subject": "fo", 113 | "sender": "test@example.org", 114 | "message": "This website is wonderful!" 115 | }) 116 | self.assertFormError(response, 'form', 'subject', 117 | [u'3 caract\xe8res minimum sont requis.']) 118 | 119 | # invalid sender 120 | response = self.client.post('/fr/contact/', { 121 | "subject": "A test", 122 | "sender": "", 123 | "message": "This website is wonderful!" 124 | }) 125 | self.assertFormError(response, 'form', 'sender', 126 | [u'Une adresse email est demand\xe9e.']) 127 | 128 | response = self.client.post('/fr/contact/', { 129 | "subject": "A test", 130 | "sender": "foo", 131 | "message": "This website is wonderful!" 132 | }) 133 | self.assertFormError(response, 'form', 'sender', 134 | [u'Une adresse email valide doit \xeatre renseign\xe9e.']) 135 | 136 | # invalid message 137 | response = self.client.post('/fr/contact/', { 138 | "subject": "A test", 139 | "sender": "test@example.org", 140 | "message": "" 141 | }) 142 | self.assertFormError(response, 'form', 'message', 143 | [u'Un message est demand\xe9.']) 144 | 145 | response = self.client.post('/fr/contact/', { 146 | "subject": "A test", 147 | "sender": "test@example.org", 148 | "message": "foobar" 149 | }) 150 | self.assertFormError(response, 'form', 'message', 151 | [u'8 caract\xe8res minimum sont requis.']) 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UXperiment [![Build Status](https://secure.travis-ci.org/prswb/prswb.png?branch=master)](http://travis-ci.org/prswb/prswb) 2 | 3 | **Code related to the «[Scrum vu des petites tranchées](http://www.paris-web.fr/2012/conferences/scrum-vue-des-petites-tranchees.php)» talk to be given at [Paris-Web 2012](http://www.paris-web.fr/2012/).** 4 | 5 | Installation 6 | ------------ 7 | 8 | Clone the project: 9 | 10 | ``` 11 | $ git clone https://github.com/prswb/prswb.git 12 | $ cd prswb 13 | ``` 14 | 15 | Create a new [virtualenv](http://pypi.python.org/pypi/virtualenv) in `./.env` and enable it: 16 | 17 | ``` 18 | $ virtualenv --no-site-packages `pwd`/.env 19 | $ source .env/bin/activate 20 | ``` 21 | 22 | Install dependencies: 23 | 24 | ``` 25 | $ pip install -r requirements-dev.txt 26 | ``` 27 | 28 | To launch a local dev webserver instance: 29 | 30 | ``` 31 | $ export UXPERIMENT_ENV=dev 32 | $ python manage.py runserver 33 | Validating models... 34 | 35 | 0 errors found 36 | Django version 1.4.1, using settings 'uxperiment.settings' 37 | Development server is running at http://127.0.0.1:8000/ 38 | Quit the server with CONTROL-C. 39 | ``` 40 | 41 | Head to `http://127.0.0.1:8000/`, profit. 42 | 43 | ### Updating the codebase 44 | 45 | After each `git pull`, you have to run the following commands: 46 | 47 | ``` 48 | $ pip install -r requirements-dev.txt 49 | $ python manage.py syncdb 50 | $ python manage.py migrate 51 | $ python manage.py collectstatic 52 | ``` 53 | 54 | Working with Sass & Compass 55 | --------------------------- 56 | 57 | Stylesheets are handled through [Compass](http://compass-style.org/). 58 | 59 | To get started: 60 | 61 | ``` 62 | $ sudo gem install compass 63 | $ cd generic/static 64 | $ compass watch 65 | >>> Compass is polling for changes. Press Ctrl-C to Stop. 66 | ``` 67 | 68 | Now you can edit the stylesheets in the `sass/` directory, related css files will 69 | be compiled in the background. 70 | 71 | Settings management 72 | ------------------- 73 | 74 | This django project has a `settings/` directory having a setting module per environment: 75 | 76 | settings/ 77 | |- base.py # common shared settings 78 | |- dev.py # dev settings 79 | |- production.py # production settings (hosting platform yet to be determined) 80 | |- staging.py # staging (hosted on heroku) 81 | |- test.py # travis settings 82 | 83 | Eventually, custom settings may be stored in a `settings/local.py` module. 84 | 85 | The `UXPERIMENT_ENV` environment variable will set the specific settings module 86 | to load. To run the local webserver against a given environment: 87 | 88 | ``` 89 | $ UXPERIMENT_ENV=test python manage.py runserver 90 | ``` 91 | 92 | If you intend to work always with a given environment within the project virtualenv: 93 | 94 | ``` 95 | $ echo 'export UXPERIMENT_ENV=dev' >> .env/bin/postactivate 96 | $ echo 'export unset UXPERIMENT_ENV' >> .env/bin/postdeactivate 97 | ``` 98 | 99 | Deploying on Heroku 100 | ------------------- 101 | 102 | The staging is hosted on [Heroku](http://heroku.com/) and reachable at 103 | [http://aqueous-mountain-3105.herokuapp.com/](http://aqueous-mountain-3105.herokuapp.com/). 104 | 105 | **Note:** You'll have to download and install the [Heroku Toolbelt](https://toolbelt.heroku.com/) 106 | in order to manage some of the remote deployment procedures detailed below. 107 | 108 | ### Deploying with a push 109 | 110 | Your git user must have `push` privileges on the heroku repository. You must also 111 | provide your SSH public key to the admin account of the `uxperiment` project. 112 | 113 | Once done, add this section to the `.git/config` of your local clone of the repo: 114 | 115 | ``` 116 | [remote "heroku"] 117 | url = git@heroku.com:aqueous-mountain-3105.git 118 | fetch = +refs/heads/*:refs/remotes/heroku/* 119 | ``` 120 | 121 | To push and deploy to heroku: 122 | 123 | ``` 124 | $ git push heroku master 125 | ``` 126 | 127 | ### Post push deployment 128 | 129 | After a push, Heroku will load the packages defined in the `requirements.txt` 130 | file. At the end of the process, the `Procfile` will be used by Heroku to start 131 | the server. 132 | 133 | Basically the `Procfile` will run these commands: 134 | 135 | ``` 136 | $ python manage.py migrate 137 | $ python manage.py collectstatic --noinput 138 | $ python manage.py run_gunicorn -b 0.0.0.0:$PORT 139 | ``` 140 | 141 | ### Environment & settings 142 | 143 | To tell Heroku to use the `staging` environment: 144 | 145 | ``` 146 | $ heroku config:set UXPERIMENT_ENV=staging 147 | ``` 148 | 149 | The `staging` settings module uses environment variables to configure the platform; 150 | to set them you have to use the `heroku config:add` command: 151 | 152 | ``` 153 | $ heroku config:add VARIABLE=VALUE 154 | ``` 155 | 156 | Here are some of the settings required for the staging to work properly: 157 | 158 | * `EMAIL_RECIPIENT`: The email address to receive notification emails sent from the platform 159 | * `EMAIL_HOST_USER`: The email user account name to send the email from 160 | * `EMAIL_HOST_PASSWORD`: The email user account password 161 | 162 | ### Heroku commands 163 | 164 | You can always run django commands using the `heroku run` command, eg: 165 | 166 | ``` 167 | $ heroku run 'python manage.py migrate' 168 | ``` 169 | 170 | To tail the server logs: 171 | 172 | ``` 173 | $ heroku logs --tail 174 | ``` 175 | 176 | Travis-CI Test Environment 177 | -------------------------- 178 | 179 | The `.travis.yml` file at the root of the repository contains the required configuration. 180 | 181 | Code review 182 | ----------- 183 | 184 | Somebody submitted a new pull-request, let's say: https://github.com/prswb/prswb/pull/24 185 | 186 | This is a pull-request from `n1k0` on his `i18n-pages` branch. To retrieve the branch in order to test it: 187 | 188 | ``` 189 | $ git remote add n1k0 https://github.com/n1k0/prswb.git 190 | $ git fetch n1k0 i18n-pages 191 | From https://github.com/n1k0/prswb 192 | * branch i18n-pages -> FETCH_HEAD 193 | $ git checkout -b pr-24 194 | Switched to a new branch 'pr-24' 195 | $ git pull --rebase n1k0 i18n-pages 196 | From https://github.com/n1k0/prswb 197 | * branch i18n-pages -> FETCH_HEAD 198 | ``` 199 | 200 | Now you can launch tests, verify scenario and so on. To get back on master: 201 | 202 | ``` 203 | $ git checkout master 204 | Switched to branch 'master' 205 | Your branch is ahead of 'origin/master' by 47 commits. 206 | ``` 207 | 208 | To sync from central repository: 209 | 210 | ``` 211 | $ git remote add upstream git://github.com/prswb/prswb.git 212 | $ git pull upstream master 213 | From git://github.com/prswb/prswb 214 | * branch master -> FETCH_HEAD 215 | ``` 216 | -------------------------------------------------------------------------------- /uxperiment/settings/base.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 4 | 5 | DEBUG = False 6 | TEMPLATE_DEBUG = DEBUG 7 | 8 | ADMINS = ( 9 | ('Mathieu Tricoire', 'mathieu.tricoire@gmail.com'), 10 | ('David Larlet', 'david.larlet@scopyleft.fr'), 11 | ('Nicolas Perriault', 'nicolas.perriault@scopyleft.fr'), 12 | ('Vincent Agnano', 'vincent.agnano@scopyleft.fr'), 13 | ) 14 | 15 | MANAGERS = ADMINS 16 | 17 | DATABASES = { 18 | 'default': { 19 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 20 | 'NAME': 'dummy.db', # Or path to database file if using sqlite3. 21 | } 22 | } 23 | 24 | # Local time zone for this installation. Choices can be found here: 25 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 26 | # although not all choices may be available on all operating systems. 27 | # In a Windows environment this must be set to your system time zone. 28 | TIME_ZONE = 'Europe/Paris' 29 | 30 | # Language code for this installation. All choices can be found here: 31 | # http://www.i18nguy.com/unicode/language-identifiers.html 32 | LANGUAGE_CODE = 'fr-fr' 33 | 34 | gettext = lambda s: s 35 | LANGUAGES = ( 36 | #('en', gettext('English')), # to be reintroduced when we "feel" it 37 | ('fr', gettext('French')), 38 | ) 39 | 40 | SITE_ID = 1 41 | 42 | # If you set this to False, Django will make some optimizations so as not 43 | # to load the internationalization machinery. 44 | USE_I18N = True 45 | 46 | # If you set this to False, Django will not format dates, numbers and 47 | # calendars according to the current locale. 48 | USE_L10N = True 49 | 50 | # If you set this to False, Django will not use timezone-aware datetimes. 51 | USE_TZ = True 52 | 53 | # Absolute filesystem path to the directory that will hold user-uploaded files. 54 | # Example: "/home/media/media.lawrence.com/media/" 55 | MEDIA_ROOT = os.path.join(ROOT_DIR, 'media') 56 | 57 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 58 | # trailing slash. 59 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 60 | MEDIA_URL = '/media/' 61 | 62 | # Absolute path to the directory static files should be collected to. 63 | # Don't put anything in this directory yourself; store your static files 64 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 65 | # Example: "/home/media/media.lawrence.com/static/" 66 | STATIC_ROOT = os.path.join(ROOT_DIR, 'static') 67 | 68 | # URL prefix for static files. 69 | # Example: "http://media.lawrence.com/static/" 70 | STATIC_URL = '/static/' 71 | 72 | # Additional locations of static files 73 | STATICFILES_DIRS = ( 74 | ) 75 | 76 | # List of finder classes that know how to find static files in 77 | # various locations. 78 | STATICFILES_FINDERS = ( 79 | 'django.contrib.staticfiles.finders.FileSystemFinder', 80 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 81 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 82 | ) 83 | 84 | # Make this unique, and don't share it with anybody. 85 | SECRET_KEY = '*fr+l+y)z_91w6mt9fzdk!0ao9v91l0wjf!rgt%mo&&9s0l6pd' 86 | 87 | # List of callables that know how to import templates from various sources. 88 | TEMPLATE_LOADERS = ( 89 | 'django.template.loaders.filesystem.Loader', 90 | 'django.template.loaders.app_directories.Loader', 91 | # 'django.template.loaders.eggs.Loader', 92 | ) 93 | 94 | MIDDLEWARE_CLASSES = ( 95 | 'django.middleware.common.CommonMiddleware', 96 | 'django.contrib.sessions.middleware.SessionMiddleware', 97 | 'django.middleware.csrf.CsrfViewMiddleware', 98 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 99 | 'django.contrib.messages.middleware.MessageMiddleware', 100 | 'django.middleware.locale.LocaleMiddleware', 101 | # Uncomment the next line for simple clickjacking protection: 102 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 103 | ) 104 | 105 | TEMPLATE_CONTEXT_PROCESSORS = ( 106 | 'django.contrib.auth.context_processors.auth', 107 | 'django.core.context_processors.debug', 108 | 'django.core.context_processors.i18n', 109 | 'django.core.context_processors.media', 110 | 'django.core.context_processors.static', 111 | 'django.core.context_processors.tz', 112 | 'django.contrib.messages.context_processors.messages', 113 | ) 114 | 115 | ROOT_URLCONF = 'uxperiment.urls' 116 | 117 | # Python dotted path to the WSGI application used by Django's runserver. 118 | WSGI_APPLICATION = 'uxperiment.wsgi.application' 119 | 120 | TEMPLATE_DIRS = ( 121 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 122 | # Always use forward slashes, even on Windows. 123 | # Don't forget to use absolute paths, not relative paths. 124 | os.path.join(ROOT_DIR, 'templates') 125 | ) 126 | 127 | # django contrib apps 128 | DJANGO_APPS = ( 129 | 'django.contrib.admin', 130 | 'django.contrib.auth', 131 | 'django.contrib.contenttypes', 132 | 'django.contrib.sessions', 133 | # 'django.contrib.sites', 134 | 'django.contrib.messages', 135 | 'django.contrib.staticfiles', 136 | ) 137 | 138 | # third party packages 139 | VENDOR_APPS = ( 140 | 'gravatar', 141 | 'south', 142 | 'registration', 143 | 'imagefit', 144 | ) 145 | 146 | # local apps 147 | LOCAL_APPS = ( 148 | 'generic', 149 | 'pages', 150 | 'websites', 151 | ) 152 | 153 | INSTALLED_APPS = DJANGO_APPS + VENDOR_APPS + LOCAL_APPS 154 | 155 | # A sample logging configuration. The only tangible logging 156 | # performed by this configuration is to send an email to 157 | # the site admins on every HTTP 500 error when DEBUG=False. 158 | # See http://docs.djangoproject.com/en/dev/topics/logging for 159 | # more details on how to customize your logging configuration. 160 | LOGGING = { 161 | 'version': 1, 162 | 'disable_existing_loggers': False, 163 | 'filters': { 164 | 'require_debug_false': { 165 | '()': 'django.utils.log.RequireDebugFalse' 166 | } 167 | }, 168 | 'handlers': { 169 | 'mail_admins': { 170 | 'level': 'ERROR', 171 | 'filters': ['require_debug_false'], 172 | 'class': 'django.utils.log.AdminEmailHandler' 173 | } 174 | }, 175 | 'loggers': { 176 | 'django.request': { 177 | 'handlers': ['mail_admins'], 178 | 'level': 'ERROR', 179 | 'propagate': True, 180 | }, 181 | } 182 | } 183 | 184 | MARKDOWN_DIR = os.path.abspath(os.path.join(ROOT_DIR, '..', 'pages', 'markdown')) 185 | 186 | PASSWORD_HASHERS = ( 187 | 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 188 | 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 189 | 'django.contrib.auth.hashers.BCryptPasswordHasher', 190 | 'django.contrib.auth.hashers.SHA1PasswordHasher', 191 | 'django.contrib.auth.hashers.MD5PasswordHasher', 192 | 'django.contrib.auth.hashers.CryptPasswordHasher', 193 | ) 194 | 195 | # Email settings 196 | EMAIL_HOST = 'smtp.gmail.com' 197 | EMAIL_PORT = 587 198 | EMAIL_USE_TLS = True 199 | EMAIL_HOST_USER = "" 200 | EMAIL_RECIPIENT = "contact@uxperiment.fr" 201 | 202 | # Profiles 203 | LOGIN_URL = '/compte/login/' 204 | LOGIN_REDIRECT_URL = '/' # once profiles will exist, '/compte/profil/' 205 | LOGOUT_URL = '/compte/logout/' 206 | 207 | # Registration settings 208 | ACCOUNT_ACTIVATION_DAYS = 7 209 | 210 | IMAGEFIT_ROOT = ROOT_DIR 211 | -------------------------------------------------------------------------------- /pages/markdown/fr/mentions-legales.md: -------------------------------------------------------------------------------- 1 | Mentions légales 2 | 3 |

Mentions légales

4 |

Hébergeur du site

5 | 6 |

Les serveurs du site www.uxperiment.fr sont hébergés en France par OVH. 7 | OVH, SAS au capital de 500 000€ 8 | RCS Roubaix – Tourcoing 424 761 419 00011 9 | Code APE 6202A 10 | N° TVA : FR 22 424 761 419 11 | Siège social : 2 rue Kellermann - 59100 Roubaix - France.

12 | 13 |

Informatiques et libertés

14 | 15 |

Le site web www.uxperiment.fr fait l'objet d'une déclaration à la CNIL (Commission Nationale de l'Informatique et des Libertés) sous le numéro 1619285.

16 | 17 |

Conformément à la loi n° 78-17 du 6 janvier 1978 modifiée, relative à l'informatique, aux fichiers et aux libertés, vous disposez d'un droit d'accès, de modification, de rectification et de suppression, pour toute information vous concernant en vous adressant à :


18 | 19 |

Martinez Amanda 20 | 41 rue du chasselas 21 | 34980 St Gély du fesc 22 | Email : contact@uxperiment.fr

23 | 24 |

Afin de faciliter l’utilisation des informations, l’utilisateur est informé que UXperiment utilise des cookies. Tout utilisateur a la faculté de s’y opposer en désactivant l’option dans les paramètres de son terminal prévu à cet effet.

25 | 26 |

Cookies

27 | 28 |

UXperiment peut être amené à implanter un cookie dans l’ordinateur de l’utilisateur. Un cookie ne permet pas à UXperiment de l’identifier ; en revanche, il enregistre des informations relatives à la navigation de l’ordinateur de l’utilisateur sur le site www.uxperiment.fr (les pages que l’utilisateur a consultées, la date et l'heure de la consultation, etc.) que UXperiment pourra lire lors des visites ultérieures de l’utilisateur. La durée de conservation de ces informations dans l’ordinateur de l’utilisateur est de 1(un) an. 29 | UXperiment informe l’utilisateur qu’il peut s’opposer à l'enregistrement de "cookies" en configurant son navigateur de la manière suivante :


30 | 31 |

Pour Mozilla firefox : 32 | Choisissez le menu "outil " puis "Options" 33 | Cliquez sur l'icône "vie privée" 34 | Repérez le menu "cookie" et sélectionnez les options qui vous conviennent. 35 | Pour Microsoft Internet Explorer 6.0 : 36 | Choisissez le menu "Outils" (ou "Tools"), puis "Options Internet" (ou "Internet Options"). 37 | Cliquez sur l'onglet "Confidentialité" (ou "Confidentiality") 38 | Sélectionnez le niveau souhaité à l'aide du curseur. 39 | Pour Microsoft Internet Explorer 5 : 40 | Choisissez le menu "Outils" (ou "Tools"), puis "Options Internet" (ou "Internet Options"). 41 | Cliquez sur l'onglet "Sécurité" (ou "Security") 42 | Sélectionnez "Internet" puis "Personnaliser le niveau" (ou "CustomLevel") 43 | Repérez la rubrique "cookies" et choisissez l'option qui vous convient. 44 | Pour Netscape 6.X et 7. X : 45 | Choisissez le menu "Edition">"Préférences" 46 | Confidentialité et Sécurité 47 | Cookies. 48 | Pour Opéra 6.0 et au-delà : 49 | Choisissez le menu "Fichier">"Préférences" 50 | Vie Privée.

51 | 52 |

Informations générales sur les services

53 | 54 |

Les informations, fournies par UXperiment sur le site www.uxperiment.fr, le sont à titre indicatif. UXperiment ne saurait garantir l'exactitude, la complétude, l'actualité des informations diffusées sur son site. 55 | En conséquence, l'utilisateur reconnaît utiliser ces informations sous sa responsabilité exclusive. UXperiment met tout en œuvre pour offrir aux utilisateurs des informations et/ou des outils disponibles et vérifiés, mais ne saurait être tenu pour responsable des erreurs, d'une absence de disponibilité des informations et/ou de la présence d'un virus sur son site.

56 | 57 |

Pré-requis de participation

58 | 59 |

L'accès à la contribution nécessite la création d'un compte, accessible au moyen d'un login et d'un mot de passe. Afin de créer ce compte, l’internaute doit remplir un formulaire en renseignant un certain nombre de champs obligatoires (mot de passe, adresse e‐mail valide, ...). L'accès est subordonné à la transmission de l'ensemble des informations demandées, notamment une adresse email valide, qui permet l'échange d'informations liées au site.

60 | 61 |

Identification du client

62 | 63 |

Le client est seul responsable de la confidentialité et de l'utilisation de ses identifiants (login et mot de passe de son compte). Il est également seul responsable de toute connexion au site ou transmission de données effectuées en utilisant son compte et qui seront réputées avoir été effectuées par le client lui‐même ou sous sa responsabilité, notamment si le client prend le risque de pré‐enregistrer ses identifiants sur son ordinateur, permettant ainsi la connexion automatique au site via son compte. Le compte du client peut être résilié en cas d’utilisation non conforme aux présentes et ce, quelque soit la personne l’ayant utilisé. Le client s'engage à ne pas révéler ses identifiants à un tiers.

64 | 65 |

Droits d'auteurs - version 1

66 | 67 |

Nous autorisons l'audience du site l'utilisation de l'ensemble des informations présentes sur ce site au téléchargement, reproduction, impression sous réserve de : 68 | 69 | - n’utiliser de telles informations qu’à des fins personnelles et en aucune manière à des fins commerciales 70 | 71 | - ne pas modifier de telles informations

72 | 73 |

Principes de modération

74 | 75 |

L'administrateur se réserve le droit de supprimer tout contenu illicite, de suspendre l’accès aux outils communautaires de tout client agissant en violation des présentes. En sa qualité d’auteur potentiel, le client est informé qu’il est susceptible de voir sa responsabilité tant civile que pénale engagée pour les contenus publiés sous sa responsabilité sur le site. 76 | 77 | En sa qualité d’hébergeur de données, l'administrateur fera ses meilleurs efforts pour retirer, dès la connaissance de son caractère manifestement illicite (texte diffamatoire, fichier contrefaisant…), toute donnée présente sur le site et conserve, conformément aux obligations légales, toute information de nature à permettre l’identification de l’auteur incriminé. 78 | 79 | En tout état de cause, les clients sont invités à signaler tout contenu ou fichier susceptible de présenter un caractère illicite au support client du site à l'adresse mail suivante : contact@uxperiment.fr.

80 | 81 |

Propriété des contributions

82 | 83 |

Le client choisit librement de procéder à une publication de ses contributions sur le site. Il déclare disposer de tous les droits afférents à ces contributions et garantit à ce titre l'administrateur contre tout recours éventuel de tiers. 84 | 85 | En choisissant de procéder à une publication, le client permet de mettre gracieusement la contribution concernée à disposition des internautes via le site et cède expressément à www.uxperiment.fr tous droits de propriété intellectuelle nécessaires à une telle publication, notamment le droit de reproduction, de représentation, pour le monde entier et pour la durée des droits attachés aux contributions publiées.

86 | 87 |

Propriété intellectuelle

88 | 89 |

L’ensemble des informations accessibles via ce site sont fournies en l’état. 90 | UXperiment ne donne aucune garantie, explicite ou implicite, et n’assume aucune responsabilité relative à l’utilisation de ces informations. 91 | 92 | UXperiment n’est responsable ni de l’exactitude, ni des erreurs, ni des omissions contenues sur ce site. 93 | 94 | L’utilisateur est seul responsable de l’utilisation de telles informations. 95 | 96 | UXperiment se réserve le droit de modifier à tout moment les présentes notamment en actualisant ce site. 97 | 98 | UXperiment ne pourra être responsable pour quel que dommage que ce soit tant direct qu’indirect, résultant d’une information contenue sur ce site. 99 | 100 | UXperiment ne peut être tenu responsable des problèmes liés à la bonne consultation de ce site ainsi que les dégâts causés par virus, hacking, piratage ou toute utilisation malveillante du réseau Internet. 101 | 102 | Pour toute remarque sur le fonctionnement du site, vous pouvez envoyer un mail à contact@uxperiment.fr.

103 | 104 |

Liens hypertextes

105 | 106 |

Les liens hypertextes mis en place dans le cadre du site www.uxperiment.fr en direction d'autres ressources présentes sur le réseau Internet ont fait l'objet d'une autorisation préalable, expresse et écrite. 107 | En activant un lien hypertexte, l'internaute reconnaît qu'il change de site et que des lors, l'ensemble des informations disponible sur le site auxquels il a accès ne relève plus des conditions générales d’utilisation.

108 | -------------------------------------------------------------------------------- /generic/static/css/styles.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'museo_300regular'; 3 | src: url("../fonts/museo300-regular-webfont.eot"); 4 | src: url("../fonts/museo300-regular-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/museo300-regular-webfont.woff") format("woff"), url("../fonts/museo300-regular-webfont.ttf") format("truetype"), url("../fonts/museo300-regular-webfont.svg#museo_300regular") format("svg"); 5 | font-weight: normal; 6 | font-style: normal; } 7 | 8 | @font-face { 9 | font-family: 'museo_700regular'; 10 | src: url("../fonts/museo700-regular-webfont.eot"); 11 | src: url("../fonts/museo700-regular-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/museo700-regular-webfont.woff") format("woff"), url("../fonts/museo700-regular-webfont.ttf") format("truetype"), url("../fonts/museo700-regular-webfont.svg#museo_700regular") format("svg"); 12 | font-weight: normal; 13 | font-style: normal; } 14 | 15 | html { 16 | font-size: 100%; } 17 | 18 | body { 19 | font-family: "Open Sans", Arial, sans-serif; 20 | font-size: 0.813em; 21 | font-weight: 400; 22 | color: #4d4d4d; } 23 | 24 | h1 { 25 | margin: 0 0 0.972em; 26 | font-weight: 300; 27 | font-size: 2.250em; } 28 | h1 span { 29 | font-weight: 800; } 30 | 31 | h2 { 32 | margin: 0 0 0.682em; 33 | font-weight: 600; 34 | font-size: 1.375em; } 35 | 36 | h3 { 37 | margin: 0 0 1.250em; 38 | font-weight: 600; 39 | font-size: 1em; } 40 | 41 | a { 42 | color: #4d4d4d; 43 | text-decoration: underline; } 44 | a:hover { 45 | color: #29abe2; } 46 | 47 | nav { 48 | font-size: 1em; } 49 | 50 | input { 51 | font-size: 0.938em; } 52 | 53 | .chapo { 54 | font-style: italic; } 55 | 56 | /* Choose a grid width, the number of columns and the margin between columns. The result of the following equation has to be an integer, not a fraction: (width - (columns - 1) * col_margin) / columns = N */ 57 | /* total with of page */ 58 | .container { 59 | margin: 0 auto; 60 | width: 960px; } 61 | 62 | header { 63 | margin: 0 0 20px; 64 | border-top: 3px solid #29abe2; } 65 | header .header-block { 66 | position: relative; } 67 | header nav { 68 | margin: 40px 0 0; } 69 | 70 | .main-container { 71 | margin-bottom: 70px; } 72 | 73 | footer { 74 | background: #f2f2f2; } 75 | footer .footer-block { 76 | padding: 30px 0 20px; 77 | border-top: 1px solid #e6e6e6; 78 | background: url(../img/bg-footer.png) right 23px no-repeat; } 79 | footer .footer-block nav { 80 | margin: 5px 40px 0 0; } 81 | 82 | .col3 { 83 | margin: 0 50px 70px 0; 84 | width: 270px; } 85 | .col3 img { 86 | padding: 10px; 87 | margin: 0 0 10px; 88 | border: 1px solid #e6e6e6; } 89 | .col3:last-child { 90 | margin: 0; } 91 | .col3 h2 { 92 | text-align: center; 93 | font-size: 18px; } 94 | 95 | .col4 { 96 | margin: 0 20px 0 0; 97 | width: 220px; 98 | text-align: center; } 99 | .col4:last-child { 100 | margin: 0; } 101 | .col4 img { 102 | margin: 0 0 25px; } 103 | .col4 h3 { 104 | margin: 0; 105 | font-family: "museo_700regular", Arial, sans-serif; 106 | font-size: 17px; 107 | line-height: 20px; } 108 | .col4 h3 span { 109 | display: block; 110 | font-family: "Open Sans", Arial, sans-serif; 111 | font-size: 14px; 112 | color: #999; } 113 | 114 | input, textarea { 115 | padding: 4px; 116 | display: inline-block; 117 | width: 560px; 118 | -webkit-border-radius: 0; 119 | -moz-border-radius: 0; 120 | border-radius: 0; 121 | border: 1px solid #e6e6e6; 122 | vertical-align: top; } 123 | input:invalid, textarea:invalid { 124 | border: 2px solid #ff0000; } 125 | 126 | input:focus:required, 127 | textarea:focus:required:invalid { 128 | border: 1px solid #f00; } 129 | 130 | label { 131 | display: inline-block; 132 | width: 150px; } 133 | 134 | input[type="submit"] { 135 | padding: 5px 10px; 136 | margin: 0 0 0 153px; 137 | background: #29abe2; 138 | color: #fff; 139 | border: none; 140 | width: auto; } 141 | 142 | input[type="file"] { 143 | width: 430px; } 144 | 145 | .errorlist { 146 | margin: 0 0 0 153px; 147 | padding: 0; } 148 | .errorlist li { 149 | list-style-type: none; 150 | color: #c1272d; } 151 | 152 | form p { 153 | margin: 0 0 10px; } 154 | 155 | /*Floats*/ 156 | .fl { 157 | float: left; } 158 | 159 | .fr { 160 | float: right; } 161 | 162 | /*Clearing floats*/ 163 | .clear { 164 | clear: both; 165 | display: block; 166 | overflow: hidden; 167 | visibility: hidden; 168 | width: 0; 169 | height: 0; } 170 | 171 | .clearfix:before, 172 | .clearfix:after { 173 | content: ""; 174 | display: table; } 175 | 176 | .clearfix:after { 177 | clear: both; } 178 | 179 | .clearfix { 180 | *zoom: 1; } 181 | 182 | ul { 183 | margin: 0; } 184 | 185 | #logo { 186 | margin: 35px 0 0; } 187 | 188 | .btn-subscribe { 189 | position: absolute; 190 | top: 0; 191 | right: 0; 192 | margin: 0 0 15px; 193 | padding: 5px 15px; 194 | background: #29abe2; 195 | color: white; 196 | font-weight: 600; } 197 | .btn-subscribe a { 198 | color: white; 199 | text-decoration: none; } 200 | .btn-subscribe a:first-child { 201 | padding: 0 10px 0 0; } 202 | .btn-subscribe a:last-child { 203 | padding: 0 0 0 10px; } 204 | 205 | nav li { 206 | margin: 0 15px 0 0; } 207 | nav li:last-child { 208 | margin: 0; } 209 | nav li:last-child:after { 210 | content: ""; } 211 | nav li:last-child a { 212 | padding: 0; } 213 | nav li:after { 214 | content: "|"; } 215 | nav a { 216 | padding: 0 15px 0 0; 217 | color: #4d4d4d; 218 | text-transform: uppercase; 219 | text-decoration: none; 220 | font-weight: 600; } 221 | nav a:hover { 222 | color: #29abe2; } 223 | 224 | .home-illustration { 225 | height: 430px; 226 | background: url(../img/home-illustration.jpg) center bottom no-repeat, url(../img/bg-home-illustration.jpg) 0 0 repeat-x; } 227 | 228 | .illustration-title { 229 | margin: 0; 230 | padding: 70px 0 0; 231 | text-transform: uppercase; 232 | font-size: 30px; } 233 | .illustration-title span { 234 | display: block; 235 | font-weight: 700; 236 | font-size: 36px; } 237 | 238 | .home-title { 239 | margin: 70px 0 60px; 240 | text-align: center; 241 | font-size: 36px; } 242 | 243 | .home-with-bg { 244 | padding: 0 0 75px; 245 | background: #f2f2f2; 246 | border-top: 1px solid #e6e6e6; } 247 | 248 | .link-form { 249 | margin: 20px 0 0 153px; } 250 | .link-form + .link-form { 251 | margin-top: 0px; } 252 | 253 | ul.websites { 254 | padding: 0; 255 | margin: 40px 0px; } 256 | ul.websites > li { 257 | display: inline-block; 258 | width: 255px; 259 | height: 260px; 260 | margin: 0 40px 25px 0; 261 | padding: 10px; 262 | -webkit-box-shadow: 0px 0px 5px #333333; 263 | -moz-box-shadow: 0px 0px 5px #333333; 264 | box-shadow: 0px 0px 5px #333333; } 265 | ul.websites > li > h3 { 266 | margin: 0; } 267 | ul.websites > li > h3 > a { 268 | color: #29abe2; 269 | font-size: 1.5em; 270 | font-weight: 400; 271 | text-decoration: none; 272 | font-family: "Open Sans", Arial, sans-serif; } 273 | ul.websites > li > .info { 274 | margin: 0; 275 | color: #999; 276 | font-style: italic; 277 | height: 40px; } 278 | ul.websites > li > img { 279 | margin: 5px 0; } 280 | ul.websites > li > a { 281 | text-decoration: none; } 282 | 283 | ul.messages { 284 | padding: 0 !important; 285 | margin: 0 !important; 286 | list-style-type: none; } 287 | ul.messages .info, ul.messages .error, ul.messages .success, ul.messages .warning { 288 | padding: 8px 15px; } 289 | ul.messages .info { 290 | background: #F2F7FF; 291 | color: #84B0F6; } 292 | ul.messages .error { 293 | background: #FCE2E2; 294 | color: #F66; } 295 | ul.messages .success { 296 | background: #F2FEF2; 297 | color: #7FBA7F; } 298 | ul.messages .warning { 299 | background: #FFF0D2; 300 | color: #F93; } 301 | 302 | body.error > #content { 303 | background: none no-repeat center 100px; 304 | min-height: 400px; 305 | padding-bottom: 100px; 306 | margin-bottom: 30px; } 307 | body.error > #content h1 { 308 | font-size: 3em; 309 | text-transform: uppercase; 310 | margin: 0; } 311 | body.error > #content h2 { 312 | font-size: 3em; 313 | text-transform: uppercase; } 314 | body.error > #content a { 315 | background: #29abe2; 316 | color: white; 317 | text-decoration: none; 318 | padding: 10px 20px; 319 | font-size: 1.3em; } 320 | body.error > #content a:before { 321 | content: "> "; } 322 | 323 | body.error404 > #content { 324 | background-image: url("../img/error404.png"); } 325 | 326 | body.error500 > #content { 327 | background-image: url("../img/error500.png"); } 328 | -------------------------------------------------------------------------------- /generic/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-10-18 10:24+0200\n" 11 | "PO-Revision-Date: 2012-10-14 23:52+0100\n" 12 | "Last-Translator: Nicolas Perriault \n" 13 | "Language-Team: LANGUAGE \n" 14 | "Language: \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 19 | "X-Generator: Poedit 1.5.4\n" 20 | 21 | #: templates/404.html:7 22 | msgid "Error 404" 23 | msgstr "Erreur 404" 24 | 25 | #: templates/404.html:8 26 | msgid "Prophet lost." 27 | msgstr "Prophet lost." 28 | 29 | #: templates/500.html:7 30 | msgid "Error 500" 31 | msgstr "Erreur 500" 32 | 33 | #: templates/500.html:8 34 | msgid "Internal server error." 35 | msgstr "Erreur interne." 36 | 37 | #: templates/dashboard.html:12 38 | msgid "Contribute to website evolution" 39 | msgstr "Contribuer à l'évolution des sites" 40 | 41 | #: templates/dashboard.html:17 42 | msgid "the Web, better." 43 | msgstr "le Web, en mieux." 44 | 45 | #: templates/dashboard.html:20 46 | msgid "UXperiment, le wat?" 47 | msgstr "UXperiment, c'est quoi ?" 48 | 49 | #: templates/dashboard.html:21 50 | msgid "" 51 | "This original project intends to let users discuss and share their " 52 | "experience about websites" 53 | msgstr "" 54 | "Ce projet original a pour vocation de laisser la parole aux utilisateurs " 55 | "afin d'échanger autour de l'expérience utilisateur des sites que vous " 56 | "utilisez" 57 | 58 | #: templates/dashboard.html:25 templates/layout.html:40 59 | #: templates/layout.html.py:66 60 | msgid "Suggest a website" 61 | msgstr "Proposer un site" 62 | 63 | #: templates/dashboard.html:27 64 | msgid "" 65 | "Once registered, submit a website and tell the world how you've experienced " 66 | "it, and how it could be enhanced." 67 | msgstr "" 68 | "Une fois inscrit, indiquez le site que vous souhaitez proposer et ce que " 69 | "vous en pensez." 70 | 71 | #: templates/dashboard.html:28 72 | msgid "Other users then may engage a discussion about it." 73 | msgstr "" 74 | "Il sera ensuite directement publié et les autres utilisateurs pourront " 75 | "donner leur avis." 76 | 77 | #: templates/dashboard.html:33 78 | msgid "Help to make it better!" 79 | msgstr "Contribuer à son évolution" 80 | 81 | #: templates/dashboard.html:34 82 | msgid "" 83 | "Search for already reviewed websites, add your comments & experience, " 84 | "suggest enhancements and report problems." 85 | msgstr "" 86 | "Une fois inscrit, il vous suffit de sélectionner le site sur lequel vous " 87 | "souhaitez faire part d’un avis, d’une suggestion d'amélioration ou d’un " 88 | "problème." 89 | 90 | #: templates/dashboard.html:39 91 | msgid "Ok." 92 | msgstr "Ok." 93 | 94 | #: templates/dashboard.html:39 95 | msgid "How does it work?" 96 | msgstr "Comment ça marche ?" 97 | 98 | #: templates/dashboard.html:43 99 | msgid "Submit your site." 100 | msgstr "Proposez votre site." 101 | 102 | #: templates/dashboard.html:44 103 | msgid "We'll take care of it" 104 | msgstr "Nous serons doux" 105 | 106 | #: templates/dashboard.html:50 107 | msgid "Relax." 108 | msgstr "Détendez-vous." 109 | 110 | #: templates/dashboard.html:51 111 | msgid "zombo.com" 112 | msgstr "mangerbouger.fr" 113 | 114 | #: templates/dashboard.html:57 115 | msgid "Get feedback." 116 | msgstr "Écoutez les remarques." 117 | 118 | #: templates/dashboard.html:58 119 | msgid "honest, always" 120 | msgstr "pertinentes, toujours" 121 | 122 | #: templates/dashboard.html:64 123 | msgid "Become powerful" 124 | msgstr "Devenez Powerful" 125 | 126 | #: templates/dashboard.html:65 127 | msgid "world is yours" 128 | msgstr "le monde est à vous" 129 | 130 | #: templates/layout.html:29 131 | msgid "Hello," 132 | msgstr "Bonjour," 133 | 134 | #: templates/layout.html:30 135 | msgid "Log out" 136 | msgstr "Déconnexion" 137 | 138 | #: templates/layout.html:32 templates/registration/login.html:15 139 | msgid "Register" 140 | msgstr "Inscription" 141 | 142 | #: templates/layout.html:33 templates/registration/activate.html:8 143 | #: templates/registration/login.html:5 templates/registration/login.html:11 144 | #: templates/registration/password_reset_complete.html:7 145 | msgid "Log in" 146 | msgstr "Connexion" 147 | 148 | #: templates/layout.html:39 templates/layout.html.py:65 149 | #, fuzzy 150 | msgid "Suggested websites" 151 | msgstr "Sites proposés" 152 | 153 | #: templates/layout.html:41 templates/layout.html.py:67 154 | msgid "Contact" 155 | msgstr "Contact" 156 | 157 | #: templates/layout.html:56 158 | msgid "No content yet." 159 | msgstr "Aucun contenu (pour le moment)." 160 | 161 | #: templates/layout.html:68 162 | msgid "Legal" 163 | msgstr "Mentions légales" 164 | 165 | #: templates/registration/activate.html:7 166 | msgid "Account successfully activated" 167 | msgstr "Votre compte a été activé" 168 | 169 | #: templates/registration/activate.html:10 170 | msgid "Account activation failed" 171 | msgstr "L'activation du compte a échoué" 172 | 173 | #: templates/registration/activation_complete.html:7 174 | msgid "Congratulations!" 175 | msgstr "Félicitations !" 176 | 177 | #: templates/registration/activation_complete.html:9 178 | msgid "Your registration being complete, you can now" 179 | msgstr "Votre inscription étant finalisée, vous pouvez maintenant" 180 | 181 | #: templates/registration/activation_complete.html:10 182 | msgid "log in" 183 | msgstr "vous connecter" 184 | 185 | #: templates/registration/activation_email.txt:4 186 | msgid "Hey," 187 | msgstr "Bonjour," 188 | 189 | #: templates/registration/activation_email.txt:6 190 | msgid "Thanks for registering with UXperiment." 191 | msgstr "Merci de votre inscription sur UXperiment." 192 | 193 | #: templates/registration/activation_email.txt:8 194 | msgid "Please click the link below to activate your account:" 195 | msgstr "Veuillez cliquer sur le lien ci-dessous pour activer votre compte :" 196 | 197 | #: templates/registration/activation_email.txt:11 198 | #, python-format 199 | msgid "Note: this link will expires in %(expiration_days)s days." 200 | msgstr "Note : ce lien expirera dans %(expiration_days)s jours." 201 | 202 | #: templates/registration/activation_email.txt:13 203 | msgid "Thank you," 204 | msgstr "Merci," 205 | 206 | #: templates/registration/activation_email.txt:16 207 | msgid "The UXperiment Team" 208 | msgstr "L'équipe UXperiment" 209 | 210 | #: templates/registration/activation_email_subject.txt:1 211 | msgid "Your UXperiment membership" 212 | msgstr "Votre inscription sur UXperiment" 213 | 214 | #: templates/registration/login.html:14 215 | msgid "Forgot password" 216 | msgstr "J'ai oublié mon mot de passe" 217 | 218 | #: templates/registration/login.html:14 219 | msgid "Reset it" 220 | msgstr "Réinitialisez-le" 221 | 222 | #: templates/registration/login.html:15 223 | msgid "Not member" 224 | msgstr "Non-membre" 225 | 226 | #: templates/registration/logout.html:5 227 | msgid "You've been succesfully logged out." 228 | msgstr "Vous avez été déconnecté." 229 | 230 | #: templates/registration/password_change_done.html:5 231 | #: templates/registration/password_change_form.html:5 232 | msgid "Password change" 233 | msgstr "Changement de mot de passe" 234 | 235 | #: templates/registration/password_change_done.html:6 236 | msgid "Password changed" 237 | msgstr "Le mot de passe a été mis à jour" 238 | 239 | #: templates/registration/password_change_form.html:9 240 | #: templates/registration/password_reset_confirm.html:10 241 | #: templates/registration/password_reset_form.html:9 242 | #: templates/registration/registration_form.html:13 243 | msgid "Submit" 244 | msgstr "Envoyer" 245 | 246 | #: templates/registration/password_reset_complete.html:5 247 | #: templates/registration/password_reset_confirm.html:5 248 | #: templates/registration/password_reset_done.html:5 249 | #: templates/registration/password_reset_form.html:5 250 | msgid "Password reset" 251 | msgstr "Réinitialisation du mot de passe" 252 | 253 | #: templates/registration/password_reset_complete.html:6 254 | msgid "Password reset successfully" 255 | msgstr "Le mot de passe a été réinitialisé" 256 | 257 | #: templates/registration/password_reset_confirm.html:13 258 | msgid "Password reset failed" 259 | msgstr "La réinitialisation du mot de passe a échoué" 260 | 261 | #: templates/registration/password_reset_done.html:6 262 | msgid "Email with password reset instructions has been sent." 263 | msgstr "" 264 | "Un email contenant les instructions de réinitialisation vous a été envoyé." 265 | 266 | #: templates/registration/password_reset_email.html:2 267 | #, python-format 268 | msgid "Reset password at %(site_name)s" 269 | msgstr "Réinitialisation de votre mot de passe %(site_name)s" 270 | 271 | #: templates/registration/registration_complete.html:5 272 | #: templates/registration/registration_form.html:7 273 | msgid "Registration" 274 | msgstr "Inscription" 275 | 276 | #: templates/registration/registration_complete.html:6 277 | msgid "You are now registered. Activation email sent." 278 | msgstr "" 279 | "Nous avons bien reçu votre demande d'inscription. Une email d'activation " 280 | "vient de vous être envoyé." 281 | -------------------------------------------------------------------------------- /generic/static/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.0.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /* 8 | * Corrects `block` display not defined in IE 6/7/8/9 and Firefox 3. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | nav, 20 | section, 21 | summary { 22 | display: block; 23 | } 24 | 25 | /* 26 | * Corrects `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 27 | */ 28 | 29 | audio, 30 | canvas, 31 | video { 32 | display: inline-block; 33 | *display: inline; 34 | *zoom: 1; 35 | } 36 | 37 | /* 38 | * Prevents modern browsers from displaying `audio` without controls. 39 | * Remove excess height in iOS 5 devices. 40 | */ 41 | 42 | audio:not([controls]) { 43 | display: none; 44 | height: 0; 45 | } 46 | 47 | /* 48 | * Addresses styling for `hidden` attribute not present in IE 7/8/9, Firefox 3, 49 | * and Safari 4. 50 | * Known issue: no IE 6 support. 51 | */ 52 | 53 | [hidden] { 54 | display: none; 55 | } 56 | 57 | /* ========================================================================== 58 | Base 59 | ========================================================================== */ 60 | 61 | /* 62 | * 1. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using 63 | * `em` units. 64 | * 2. Prevents iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | font-size: 100%; /* 1 */ 70 | -webkit-text-size-adjust: 100%; /* 2 */ 71 | -ms-text-size-adjust: 100%; /* 2 */ 72 | } 73 | 74 | /* 75 | * Addresses `font-family` inconsistency between `textarea` and other form 76 | * elements. 77 | */ 78 | 79 | html, 80 | button, 81 | input, 82 | select, 83 | textarea { 84 | font-family: sans-serif; 85 | } 86 | 87 | /* 88 | * Addresses margins handled incorrectly in IE 6/7. 89 | */ 90 | 91 | body { 92 | margin: 0; 93 | } 94 | 95 | /* ========================================================================== 96 | Links 97 | ========================================================================== */ 98 | 99 | /* 100 | * Addresses `outline` inconsistency between Chrome and other browsers. 101 | */ 102 | 103 | a:focus { 104 | outline: thin dotted; 105 | } 106 | 107 | /* 108 | * Improves readability when focused and also mouse hovered in all browsers. 109 | */ 110 | 111 | a:active, 112 | a:hover { 113 | outline: 0; 114 | } 115 | 116 | /* ========================================================================== 117 | Typography 118 | ========================================================================== */ 119 | 120 | /* 121 | * Addresses font sizes and margins set differently in IE 6/7. 122 | * Addresses font sizes within `section` and `article` in Firefox 4+, Safari 5, 123 | * and Chrome. 124 | */ 125 | 126 | h1 { 127 | font-size: 2em; 128 | margin: 0.67em 0; 129 | } 130 | 131 | h2 { 132 | font-size: 1.5em; 133 | margin: 0.83em 0; 134 | } 135 | 136 | h3 { 137 | font-size: 1.17em; 138 | margin: 1em 0; 139 | } 140 | 141 | h4 { 142 | font-size: 1em; 143 | margin: 1.33em 0; 144 | } 145 | 146 | h5 { 147 | font-size: 0.83em; 148 | margin: 1.67em 0; 149 | } 150 | 151 | h6 { 152 | font-size: 0.75em; 153 | margin: 2.33em 0; 154 | } 155 | 156 | /* 157 | * Addresses styling not present in IE 7/8/9, Safari 5, and Chrome. 158 | */ 159 | 160 | abbr[title] { 161 | border-bottom: 1px dotted; 162 | } 163 | 164 | /* 165 | * Addresses style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. 166 | */ 167 | 168 | b, 169 | strong { 170 | font-weight: bold; 171 | } 172 | 173 | blockquote { 174 | margin: 1em 40px; 175 | } 176 | 177 | /* 178 | * Addresses styling not present in Safari 5 and Chrome. 179 | */ 180 | 181 | dfn { 182 | font-style: italic; 183 | } 184 | 185 | /* 186 | * Addresses styling not present in IE 6/7/8/9. 187 | */ 188 | 189 | mark { 190 | background: #ff0; 191 | color: #000; 192 | } 193 | 194 | /* 195 | * Addresses margins set differently in IE 6/7. 196 | */ 197 | 198 | p, 199 | pre { 200 | margin: 1em 0; 201 | } 202 | 203 | /* 204 | * Corrects font family set oddly in IE 6, Safari 4/5, and Chrome. 205 | */ 206 | 207 | code, 208 | kbd, 209 | pre, 210 | samp { 211 | font-family: monospace, serif; 212 | _font-family: 'courier new', monospace; 213 | font-size: 1em; 214 | } 215 | 216 | /* 217 | * Improves readability of pre-formatted text in all browsers. 218 | */ 219 | 220 | pre { 221 | white-space: pre; 222 | white-space: pre-wrap; 223 | word-wrap: break-word; 224 | } 225 | 226 | /* 227 | * Addresses CSS quotes not supported in IE 6/7. 228 | */ 229 | 230 | q { 231 | quotes: none; 232 | } 233 | 234 | /* 235 | * Addresses `quotes` property not supported in Safari 4. 236 | */ 237 | 238 | q:before, 239 | q:after { 240 | content: ''; 241 | content: none; 242 | } 243 | 244 | /* 245 | * Addresses inconsistent and variable font size in all browsers. 246 | */ 247 | 248 | small { 249 | font-size: 80%; 250 | } 251 | 252 | /* 253 | * Prevents `sub` and `sup` affecting `line-height` in all browsers. 254 | */ 255 | 256 | sub, 257 | sup { 258 | font-size: 75%; 259 | line-height: 0; 260 | position: relative; 261 | vertical-align: baseline; 262 | } 263 | 264 | sup { 265 | top: -0.5em; 266 | } 267 | 268 | sub { 269 | bottom: -0.25em; 270 | } 271 | 272 | /* ========================================================================== 273 | Lists 274 | ========================================================================== */ 275 | 276 | /* 277 | * Addresses margins set differently in IE 6/7. 278 | */ 279 | 280 | dl, 281 | menu, 282 | ol, 283 | ul { 284 | margin: 1em 0; 285 | } 286 | 287 | dd { 288 | margin: 0 0 0 40px; 289 | } 290 | 291 | /* 292 | * Addresses paddings set differently in IE 6/7. 293 | */ 294 | 295 | menu, 296 | ol, 297 | ul { 298 | padding: 0 0 0 40px; 299 | } 300 | 301 | /* 302 | * Corrects list images handled incorrectly in IE 7. 303 | */ 304 | 305 | nav ul, 306 | nav ol { 307 | list-style: none; 308 | list-style-image: none; 309 | } 310 | 311 | /* ========================================================================== 312 | Embedded content 313 | ========================================================================== */ 314 | 315 | /* 316 | * 1. Removes border when inside `a` element in IE 6/7/8/9 and Firefox 3. 317 | * 2. Improves image quality when scaled in IE 7. 318 | */ 319 | 320 | img { 321 | border: 0; /* 1 */ 322 | -ms-interpolation-mode: bicubic; /* 2 */ 323 | } 324 | 325 | /* 326 | * Corrects overflow displayed oddly in IE 9. 327 | */ 328 | 329 | svg:not(:root) { 330 | overflow: hidden; 331 | } 332 | 333 | /* ========================================================================== 334 | Figures 335 | ========================================================================== */ 336 | 337 | /* 338 | * Addresses margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 339 | */ 340 | 341 | figure { 342 | margin: 0; 343 | } 344 | 345 | /* ========================================================================== 346 | Forms 347 | ========================================================================== */ 348 | 349 | /* 350 | * Corrects margin displayed oddly in IE 6/7. 351 | */ 352 | 353 | form { 354 | margin: 0; 355 | } 356 | 357 | /* 358 | * Define consistent border, margin, and padding. 359 | */ 360 | 361 | fieldset { 362 | border: 1px solid #c0c0c0; 363 | margin: 0 2px; 364 | padding: 0.35em 0.625em 0.75em; 365 | } 366 | 367 | /* 368 | * 1. Corrects color not being inherited in IE 6/7/8/9. 369 | * 2. Corrects text not wrapping in Firefox 3. 370 | * 3. Corrects alignment displayed oddly in IE 6/7. 371 | */ 372 | 373 | legend { 374 | border: 0; /* 1 */ 375 | padding: 0; 376 | white-space: normal; /* 2 */ 377 | *margin-left: -7px; /* 3 */ 378 | } 379 | 380 | /* 381 | * 1. Corrects font size not being inherited in all browsers. 382 | * 2. Addresses margins set differently in IE 6/7, Firefox 3+, Safari 5, 383 | * and Chrome. 384 | * 3. Improves appearance and consistency in all browsers. 385 | */ 386 | 387 | button, 388 | input, 389 | select, 390 | textarea { 391 | font-size: 100%; /* 1 */ 392 | margin: 0; /* 2 */ 393 | vertical-align: baseline; /* 3 */ 394 | *vertical-align: middle; /* 3 */ 395 | } 396 | 397 | /* 398 | * Addresses Firefox 3+ setting `line-height` on `input` using `!important` in 399 | * the UA stylesheet. 400 | */ 401 | 402 | button, 403 | input { 404 | line-height: normal; 405 | } 406 | 407 | /* 408 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 409 | * and `video` controls. 410 | * 2. Corrects inability to style clickable `input` types in iOS. 411 | * 3. Improves usability and consistency of cursor style between image-type 412 | * `input` and others. 413 | * 4. Removes inner spacing in IE 7 without affecting normal text inputs. 414 | * Known issue: inner spacing remains in IE 6. 415 | */ 416 | 417 | button, 418 | html input[type="button"], /* 1 */ 419 | input[type="reset"], 420 | input[type="submit"] { 421 | -webkit-appearance: button; /* 2 */ 422 | cursor: pointer; /* 3 */ 423 | *overflow: visible; /* 4 */ 424 | } 425 | 426 | /* 427 | * Re-set default cursor for disabled elements. 428 | */ 429 | 430 | button[disabled], 431 | input[disabled] { 432 | cursor: default; 433 | } 434 | 435 | /* 436 | * 1. Addresses box sizing set to content-box in IE 8/9. 437 | * 2. Removes excess padding in IE 8/9. 438 | * 3. Removes excess padding in IE 7. 439 | * Known issue: excess padding remains in IE 6. 440 | */ 441 | 442 | input[type="checkbox"], 443 | input[type="radio"] { 444 | box-sizing: border-box; /* 1 */ 445 | padding: 0; /* 2 */ 446 | *height: 13px; /* 3 */ 447 | *width: 13px; /* 3 */ 448 | } 449 | 450 | /* 451 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. 452 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome 453 | * (include `-moz` to future-proof). 454 | */ 455 | 456 | input[type="search"] { 457 | -webkit-appearance: textfield; /* 1 */ 458 | -moz-box-sizing: content-box; 459 | -webkit-box-sizing: content-box; /* 2 */ 460 | box-sizing: content-box; 461 | } 462 | 463 | /* 464 | * Removes inner padding and search cancel button in Safari 5 and Chrome 465 | * on OS X. 466 | */ 467 | 468 | input[type="search"]::-webkit-search-cancel-button, 469 | input[type="search"]::-webkit-search-decoration { 470 | -webkit-appearance: none; 471 | } 472 | 473 | /* 474 | * Removes inner padding and border in Firefox 3+. 475 | */ 476 | 477 | button::-moz-focus-inner, 478 | input::-moz-focus-inner { 479 | border: 0; 480 | padding: 0; 481 | } 482 | 483 | /* 484 | * 1. Removes default vertical scrollbar in IE 6/7/8/9. 485 | * 2. Improves readability and alignment in all browsers. 486 | */ 487 | 488 | textarea { 489 | overflow: auto; /* 1 */ 490 | vertical-align: top; /* 2 */ 491 | } 492 | 493 | /* ========================================================================== 494 | Tables 495 | ========================================================================== */ 496 | 497 | /* 498 | * Remove most spacing between table cells. 499 | */ 500 | 501 | table { 502 | border-collapse: collapse; 503 | border-spacing: 0; 504 | } 505 | -------------------------------------------------------------------------------- /generic/static/fonts/museo700-regular-webfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | --------------------------------------------------------------------------------