diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 5e6624f9..6f95c4d7 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -32,27 +32,3 @@ $(document).on 'page:receive', -> $(document).on 'page:load', -> # Reload polyfill when turbolinks loads a new page $(this).updatePolyfill() - -$(document).on 'page:receive', -> - # Delete existing tinymce editors, very important in the turbolinks context! - tinymce.remove() - -$(document).ready -> - tinyMCE.init - selector: 'textarea', - menubar : false, - schema: 'html5', - add_unload_trigger: true, - browser_spellcheck: true, - toolbar: [ - ' bold italic strikethrough - | bullist numlist outdent indent - | alignleft aligncenter alignright alignjustify - | link media insertdatetime charmap table - | undo redo - | searchreplace - | code visualblocks preview fullscreen' - ], - plugins: 'lists, advlist, autolink, link, charmap, paste, print, preview, - table, fullscreen, searchreplace, media, insertdatetime, visualblocks, - visualchars, wordcount, contextmenu, code' diff --git a/app/assets/javascripts/events.js.coffee b/app/assets/javascripts/events.js.coffee index 728976d4..3b39edd6 100644 --- a/app/assets/javascripts/events.js.coffee +++ b/app/assets/javascripts/events.js.coffee @@ -1,3 +1,26 @@ $(document).ready -> $('form.region_selector select').change -> this.form.submit() + + tinyMCE.init + selector: 'textarea#event_description', + menubar : false, + schema: 'html5', + add_unload_trigger: true, + browser_spellcheck: true, + toolbar: [ + ' bold italic strikethrough + | bullist numlist outdent indent + | alignleft aligncenter alignright alignjustify + | link media insertdatetime charmap table + | undo redo + | searchreplace + | code visualblocks preview fullscreen' + ], + plugins: 'lists, advlist, autolink, link, charmap, paste, print, preview, + table, fullscreen, searchreplace, media, insertdatetime, visualblocks, + visualchars, wordcount, contextmenu, code' + +$(document).on 'page:receive', -> + # Delete existing tinymce editors, very important in the turbolinks context! + tinymce.remove() diff --git a/public/adl-submit-latest-version b/public/adl-submit-latest-version new file mode 100644 index 00000000..9f55b2cc --- /dev/null +++ b/public/adl-submit-latest-version @@ -0,0 +1 @@ +3.0 diff --git a/public/adl-submit.py b/public/adl-submit.py new file mode 100755 index 00000000..839bbbcb --- /dev/null +++ b/public/adl-submit.py @@ -0,0 +1,387 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (C) 2005 Thomas Petazzoni +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import xml.dom.minidom +import getopt +import sys +import pycurl +import StringIO +import re +import time +import locale + +locale.setlocale(locale.LC_ALL, ('fr_FR', 'utf-8')) + +eventFields = [ "title", "start-date", "end-date", "start-hour", + "end-hour", "description", "city", "region", + "url", "contact", "submitter", "tags" ] + +regions = { + u'Alsace' : 1, + u'Aquitaine' : 2, + u'Auvergne' : 3, + u'Basse-Normandie' : 4, + u'Bourgogne' : 5, + u'Bretagne' : 6, + u'Centre' : 7, + u'Champagne-Ardenne' : 8, + u'Corse' : 9, + u'Franche-Comté' : 10, + u'Haute-Normandie' : 11, + u'Île-de-France' : 12, + u'Languedoc-Roussillon' : 13, + u'Limousin' : 14, + u'Lorraine' : 15, + u'Midi-Pyrénées' : 16, + u'Nord-Pas-de-Calais' : 17, + u'Pays' : 18, + u'Picardie' : 19, + u'Poitou-Charentes' : 20, + u'Provence-Alpes-Côte-d\'Azur' : 21, + u'Rhône-Alpes' : 22, + u'Guadeloupe' : 23, + u'Guyane' : 24, + u'Martinique' : 25, + u'Réunion' : 26, + u'Autre' : 27 +} + +baseUrl = "http://www.agendadulibre.org" +#baseUrl = "http://localhost:3000/" + +def Usage(): + print u"""Soumettre un évènement dans l'Agenda du Libre + +Options: + --file event.xml Fichier XML décrivant l'évènement. + --test-output test.html Fichier de sortie HTML de test + --start-date YYYY-MM-DD Date de début de l'évènement. + --end-date YYYY-MM-DD Date de fin de l'évènement. + --start-hour HH:MM Heure de début de l'évènement. + --end-hour HH:MM Heure de fin de l'évènement. + --title chaine Titre de l'évènement. + --description chaine-html Description de l'évènement. + --city chaine Ville de l'évènement. + --region entier Région de l'évènement. + --url chaine URL décrivant l'évènement. + --contact chaine E-mail de contact. + --tags chaine Liste des tags. + +Exemple de fichier XML: + + + Permanence Logiciels Libres + 18:00 + 21:00 + Gulliver organise chaque + semaine une permanence Logiciels Libres ouverte à tous, + membre de l'association ou non.

+ +

Durant cette permanence, vous pourrez trouver des réponses aux + questions que vous vous posez au sujet du Logiciel Libre, ainsi que + de l'aide pour résoudre vos problèmes d'installation, de + configuration et d'utilisation de Logiciels Libres. N'hésitez pas + à apporter votre ordinateur, afin que les autres participants + puissent vous aider.

+ +

Une connexion Internet est disponible sur place, ainsi que les + mises à jour pour les distributions GNU/Linux les plus + courantes.

+ +

Cette permanence a lieu à la MJC du Grand Cordel, 18 + rue des Plantes à Rennes.

+ + ]]>
+ Rennes + Bretagne + http://www.gulliver.eu.org + contact@gulliver.eu.org + gulliver permanence +
+ +Valeurs des champs: + Le fichier XML peut contenir des champs dont le nom est semblable + à celui des options, à savoir start-date, end-date, + start-hour, end-hour, title, description, city, region, url et + contact. Si un champ est défini à la fois dans le fichier XML + et sur la ligne de commande, alors c'est la valeur donnée sur la + ligne de commande qui l'emporte. Entre le fichier XML et la ligne de + commande, tous les champs doivent être définis, sinon l'ajout + de l'évènement sera refusé. Le seul champ qui peut être + vide est end-date, auquel cas il sera positionné à la même + valeur que start-date. + +Remplacements: + Si la chaîne $month est trouvée dans la description, elle sera + automatiquement remplacée par le nom du mois de la date de début + de l'évènement. + Si la chaîne $date est trouvée dans la description, elle sera + automatiquement remplacée par la date de début de l'évènement + (numéro du jour dans le mois puis nom du mois). + +Exemple d'utilisation: + ./adl-submit.py --file event.xml --start-date 2005-12-10 + + Ajoutera l'évènement décrit dans le fichier event.xml + (donné ci-dessous) pour la date du 10 décembre 2005. Puisque + le champ end-date n'est pas spécifié, alors il vaudra la + même chose que start-date, c'est à dire le 10 décembre + 2005. + + Pour vérifier que le formatage est correct avant l'envoi, + on pourra utiliser: + + ./adl-submit.py --file event.xml --start-date 2005-12-10 + --test-output test.html + + et regarder le fichier test.html avec un navigateur Web. +""" + sys.exit (1) + +def HandleXmlFile(file, values): + dom = xml.dom.minidom.parse(file) + for node in dom.getElementsByTagName("event")[0].childNodes: + if node.nodeType == node.ELEMENT_NODE: + val = node.childNodes[0] + for field in eventFields: + if node.nodeName == field: + values[field] = val.data + +def HandleParamValue(param, val, values): + for field in eventFields: + if param == "--" + field: + values[field] = val + +def ParseOptions(options): + getoptOptions = map (lambda elt: elt + "=", eventFields) + getoptOptions.append ("file=") + getoptOptions.append ("help") + getoptOptions.append("test-output=") + eventFieldValues = {} + + testOutputFile = "" + + try: + opts, args = getopt.getopt(options, "", getoptOptions) + except getopt.GetoptError: + print u"Option inconnue." + Usage() + + if opts == []: + Usage() + + for param, val in opts: + if param == "--help": + Usage() + + if param == "--file": + HandleXmlFile(val, eventFieldValues) + + if param == "--test-output": + testOutputFile = val + + for param, val in opts: + HandleParamValue (param, val, eventFieldValues) + + return (eventFieldValues, testOutputFile) + +def getAuthToken(baseUrl): + + curl = pycurl.Curl() + + contents = StringIO.StringIO() + curl.setopt(curl.WRITEFUNCTION, contents.write) + + curl.setopt(curl.URL, baseUrl) + curl.setopt(pycurl.COOKIEJAR, '/tmp/cookie.txt') + curl.setopt(pycurl.COOKIEFILE, '/tmp/cookie.txt') + + curl.perform() + + m = re.search(r'()', contents.getvalue()) + m = re.search(r'"(.*?)"', m.group()) + return m.group().strip('"') + +def SubmitEvent(event, testOutputFile): + + if not event.has_key ("end-date") and event.has_key('start-date'): + event ["end-date"] = event ["start-date"] + + if not event.has_key("submitter") and event.has_key('contact'): + event ['submitter'] = event['contact'] + + for field in eventFields: + if not event.has_key(field): + print u"Le champ '%s' n'est pas renseigné" % field + return + + if re.compile(r'^[^\<\>]*$').search (event['title']) is None: + print u"Problème de formatage dans le titre: '%s'. Les tags HTML ne sont pas autorisés." % event['title'] + return + + try: + startDate = time.strptime(event['start-date'], "%Y-%m-%d") + except ValueError: + print u"Problème de formatage dans la date de début: '%s'. Elle doit être de la forme AAAA-MM-JJ" % event['start-date'] + return + + try: + endDate = time.strptime(event['end-date'], "%Y-%m-%d") + except ValueError: + print u"Problème de formatage dans la date de fin: '%s'. Elle doit être de la forme AAAA-MM-JJ" % event['end-date'] + return + + try: + startHour = time.strptime(event['start-hour'], "%H:%M") + except ValueError: + print u"Problème de formatage dans l'heure de début: '%s'. Elle doit être de la forme: HH:MM" % event['start-hour'] + return + + try: + endHour = time.strptime(event['end-hour'], "%H:%M") + except ValueError: + print u"Problème de formatage dans l'heure de fin: '%s'. Elle doit être de la forme HH:MM" % event['start-hour'] + return + + for tag in event['tags'].split(' '): + if len(tag) < 3: + print u"Le tag '%s' est trop petit, minimum de 3 caractères" % tag + return + + startDate = (startDate[0], startDate[1], startDate[2], startHour[3], + startHour[4], startDate[5], startDate[6], startDate[7], startDate[8]) + endDate = (endDate[0], endDate[1], endDate[2], endHour[3], + endHour[4], endDate[5], endDate[6], endDate[7], endDate[8]) + + if time.mktime(startDate) <= time.time(): + print u"ERREUR: La date de début de l'évènement est dans le passé." + return + + if time.mktime(endDate) <= time.time(): + print u"ERREUR: La date de fin de l'évènement est dans le passé." + return + + if time.mktime(endDate) < time.mktime(startDate): + print u"ERREUR: La date de fin de l'évènement est avant la date de début." + return + + if re.compile(r'^[^\<\>]*$').search (event['city']) is None: + print u"ERREUR: Problème de formatage dans le nom de la ville: '%s'. Les tags HTML sont interdits." % event['city'] + return + + if regions.has_key(event['region']) is False: + print u"ERREUR: La région '%s' n'existe pas." % event['region'] + print u"Les régions existantes sont:" + for name in regions: + print u" - " + name + return + + if re.compile(r'^http://.*$').search (event['url']) is None and re.compile(r'^https://.*$').search (event['url']) is None: + print u"ERREUR: Problème de formatage dans l'URL: '%s'. Elle doit commencer par http:// ou https://." % event['url'] + return + + if re.compile(r'^([A-Za-z0-9_\.\-]*)@([A-Za-z0-9_\-]*)\.([A-Za-z0-9_\.\-]*)$').search (event['contact']) is None: + print u"ERREUR: Problème de formatage dans l'adresse e-mail." % event ['contact'] + return + + if re.compile(r'^([A-Za-z0-9_\.\-]*)@([A-Za-z0-9_\-]*)\.([A-Za-z0-9_\.\-]*)$').search (event['submitter']) is None: + print u"ERREUR: Problème de formatage dans l'adresse e-mail." % event ['submitter'] + return + + monthstr = unicode(time.strftime("%B", startDate), 'utf-8') + datestr = unicode(time.strftime("%d %B", startDate), 'utf-8') + + event['description'] = event['description'].replace("$month", monthstr) + event['description'] = event['description'].replace("$date", datestr) + + curl = pycurl.Curl() + + contents = StringIO.StringIO() + curl.setopt(curl.WRITEFUNCTION, contents.write) + + if testOutputFile: + curl.setopt (curl.URL, baseUrl + 'events?visu=true') + else: + curl.setopt (curl.URL, baseUrl + 'events') + + curl.setopt(curl.HTTPPOST, [('authenticity_token', str(getAuthToken(baseUrl+'events/new'))), + ('event[title]', event['title'].encode('utf-8')), + ('event[start_time(3i)]', str(startDate[2])), + ('event[start_time(2i)]', str(startDate[1])), + ('event[start_time(1i)]', str(startDate[0])), + ('event[start_time(4i)]', str(startDate[3])), + ('event[start_time(5i)]', str(startDate[4])), + ('event[end_time(3i)]', str(endDate[2])), + ('event[end_time(2i)]', str(endDate[1])), + ('event[end_time(1i)]', str(endDate[0])), + ('event[end_time(4i)]', str(endHour[3])), + ('event[end_time(5i)]', str(endHour[4])), + ('event[description]', event['description'].encode('utf-8')), + ('event[city]', event['city'].encode('utf-8')), + ('event[region]', str(regions[event['region']])), + ('event[locality]', str(0)), + ('event[url]', event['url'].encode('utf-8')), + ('event[contact]', event['contact'].encode('utf-8')), + ('event[submitter]', event['submitter'].encode('utf-8')), + ('event[tags]', event['tags'].encode('utf-8'))]) + + curl.setopt(pycurl.COOKIEJAR, '/tmp/cookie.txt') + curl.setopt(pycurl.COOKIEFILE, '/tmp/cookie.txt') + curl.perform() + + if testOutputFile: + if curl.getinfo(curl.HTTP_CODE) != 200: + print u"Erreur lors de la récupération de la sortie HTML" + sys.exit(0) + fp = open(testOutputFile, "wb") + s = contents.getvalue() + s = re.sub(r'href="([A-Za-z0-9]*).css"', r'href="'+baseUrl+'\1.css"', s) + fp.write(s) + fp.close() + + else: + if curl.getinfo(curl.HTTP_CODE) != 302: + print u"Erreur lors de la soumission de l'évènement" + sys.exit(0) + else: + print u"Évènement soumis avec succès. Il sera prochainement validé par un modérateur." + + +if (len(sys.argv) == 1) and sys.argv[1] == "--help": + Usage() + +(event, testOutputFile) = ParseOptions(sys.argv[1:]) + +# Check that we are running the latest version of the adl-submit +# script +if not testOutputFile: + contents = StringIO.StringIO() + curl = pycurl.Curl() + curl.setopt(curl.WRITEFUNCTION, contents.write) + curl.setopt (curl.URL, baseUrl + './adl-submit-latest-version') + curl.perform() + if curl.getinfo(curl.HTTP_CODE) == 200: + if float(contents.getvalue()) != float('3.0'): + print u"Votre script n'est plus à jour, merci de télécharger la nouvelle version à l'adresse" + print u"%sadl-submit.py" % baseUrl + sys.exit(1) + +SubmitEvent(event, testOutputFile)