Project

General

Profile

Revision 27140946

ID27140946a1ee5a4006fe10e7765e92b81451b59a
Parent 03059edd
Child 558ccefb

Added by Francois POIROTTE over 13 years ago

Grosse simplification de la gestion des plugins

Ces modifications permettent d'isoler un peu plus les plugins
du code principal de VigiBoard (de sorte qu'il serait possible
de fournir les plugins dans des paquets RPM séparés).

Ajout d'un validateur type !FormEncode pour la conversion d'une date
selon un format (obtenu dans les traductions).

Ajout d'un mécanisme permettant aux plugins d'ajouter dynamiquement
des lignes dans le formulaire de recherche.

Ajout de nouveaux plugins pour coller au mécanisme décrit ci-dessus.

Transformation des plugins existants pour profiter de ce modèle
dynamique pour la recherche. En conséquence, le code nécessaire
pour gérer la recherche dans le contrôleur Root a été grandement
simplifié.

Ajout de la possibilité de filtrer en fonction de la priorité des
événements affichés (cf. #572).

Légère correction dans les tests (pour ne pas dépendre de l'i18n).

git-svn-id: https://vigilo-dev.si.c-s.fr/svn@6559 b22e2e97-25c9-44ff-b637-2e5ceca36478

View differences:

vigiboard/config/app_cfg.py
65 65
                plugin_class = ep.load(require=True)
66 66
                if issubclass(plugin_class, VigiboardRequestPlugin):
67 67
                    plugins.append((unicode(ep.name), plugin_class()))
68
            except Exception, e:
69
                LOGGER.error('Unable to import plugin %s : %s' % (plugin_name, e))
68
            except:
69
                LOGGER.exception(u'Unable to import plugin %s', plugin_name)
70 70

  
71 71
        config['columns_plugins'] = plugins
72 72

  
......
115 115
base_config['vigiboard_plugins'] = (
116 116
#    'id',
117 117
    'details',
118
    'groups',
118 119
    'date',
119 120
    'priority',
120 121
    'occurrences',
121 122
    'hostname',
122 123
    'servicename',
123 124
    'output',
125
    'masked_events',
124 126
    'hls',
125 127
    'status',
126 128
#    'test',
vigiboard/controllers/plugins/__init__.py
86 86
        @rtype: C{int}
87 87
        """
88 88
        return 1
89

  
90
    def get_search_fields(self):
91
        return []
92

  
93
    def handle_search_fields(self, query, search):
94
        pass
vigiboard/controllers/plugins/date.py
22 22
Un plugin pour VigiBoard qui ajoute une colonne avec la date à laquelle
23 23
est survenu un événement et la durée depuis laquelle l'événement est actif.
24 24
"""
25
import tw.forms as twf
26
from pylons.i18n import ugettext as _, lazy_ugettext as l_
27
from tg.i18n import get_lang
28
import tg
29

  
30
from vigilo.models import tables
25 31

  
26 32
from vigiboard.controllers.plugins import VigiboardRequestPlugin
33
from vigiboard.lib.dateformat import DateFormatConverter
34

  
35
def get_calendar_lang():
36
    # TODO: Utiliser le champ "language" du modèle pour cet utilisateur ?
37
    # On récupère la langue du navigateur de l'utilisateur
38
    lang = get_lang()
39
    if not lang:
40
        lang = tg.config['lang']
41
    else:
42
        lang = lang[0]
43

  
44
    # TODO: Il faudrait gérer les cas où tout nous intéresse dans "lang".
45
    # Si l'identifiant de langage est composé (ex: "fr_FR"),
46
    # on ne récupère que la 1ère partie.
47
    lang = lang.replace('_', '-')
48
    lang = lang.split('-')[0]
49
    return lang
50

  
51
def get_date_format():
52
    # @HACK: nécessaire car l_() retourne un object LazyString
53
    # qui n'est pas sérialisable en JSON.
54
    return _('%Y-%m-%d %I:%M:%S %p').encode('utf-8')
27 55

  
28 56
class PluginDate(VigiboardRequestPlugin):
29 57
    """Plugin pour l'ajout d'une colonne Date."""
30
    pass
58
    def get_search_fields(self):
59
        return [
60
            twf.CalendarDateTimePicker(
61
                'from_date',
62
                label_text=l_('From'),
63
                button_text=l_("Choose"),
64
                not_empty=False,
65
                validator=DateFormatConverter(if_missing=None),
66
                date_format=get_date_format,
67
                calendar_lang=get_calendar_lang,
68
            ),
69
            twf.CalendarDateTimePicker(
70
                'to_date',
71
                label_text=l_('To'),
72
                button_text=l_("Choose"),
73
                not_empty=False,
74
                validator=DateFormatConverter(if_missing=None),
75
                date_format=get_date_format,
76
                calendar_lang=get_calendar_lang,
77
            ),
78
        ]
79

  
80
    def handle_search_fields(self, query, search):
81
        if search.get('from_date'):
82
            query.add_filter(tables.CorrEvent.timestamp_active >=
83
                search['from_date'])
84
        if search.get('to_date'):
85
            query.add_filter(tables.CorrEvent.timestamp_active <=
86
                search['to_date'])
vigiboard/controllers/plugins/groups.py
1
# -*- coding: utf-8 -*-
2
# vim:set expandtab tabstop=4 shiftwidth=4:
3
################################################################################
4
#
5
# Copyright (C) 2007-2011 CS-SI
6
#
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License version 2 as
9
# published by the Free Software Foundation.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
################################################################################
20

  
21
"""
22
Un plugin pour VigiBoard qui ajoute une colonne avec les groupes
23
d'éléments supervisés auxquels appartient l'objet associé
24
à l'événement corrélé.
25
"""
26
import tw.forms as twf
27
from pylons.i18n import lazy_ugettext as l_
28

  
29
from vigiboard.controllers.plugins import VigiboardRequestPlugin
30
from vigilo.models.session import DBSession
31
from vigilo.models.tables.group import Group
32
from vigilo.models.tables.grouphierarchy import GroupHierarchy
33

  
34
class GroupSelector(twf.InputField):
35
    params = ["choose_text", "text_value", "clear_text"]
36
    choose_text = l_('Choose')
37
    clear_text = l_('Clear')
38
    text_value = ''
39

  
40
    template = """
41
<div xmlns="http://www.w3.org/1999/xhtml"
42
   xmlns:py="http://genshi.edgewall.org/" py:strip="">
43
<input type="hidden" name="${name}" class="${css_class}"
44
    id="${id}.value" value="${value}" py:attrs="attrs" />
45
<input type="text" class="${css_class}" id="${id}.ui"
46
    value="${text_value}" readonly="readonly" py:attrs="attrs" />
47
<input type="button" class="${css_class}" id="${id}"
48
    value="${choose_text}" py:attrs="attrs" />
49
<input type="button" class="${css_class}" id="${id}.clear"
50
    value="${clear_text}" py:attrs="attrs" />
51
</div>
52
"""
53

  
54
    def update_params(self, d):
55
        super(GroupSelector, self).update_params(d)
56
        text_value = DBSession.query(Group.name).filter(
57
                        Group.idgroup == d.value).scalar()
58
        if not text_value:
59
            d.value = ''
60
        else:
61
            d.text_value = text_value
62

  
63

  
64
class PluginGroups(VigiboardRequestPlugin):
65
    """
66
    Affiche les groupes d'éléments supervisés auxquels
67
    appartient l'événement corrélé.
68
    """
69
    def get_search_fields(self):
70
        return [
71
            GroupSelector(
72
                'supitemgroup',
73
                label_text=l_('Group'),
74
                validator=twf.validators.Int(if_invalid=None, if_missing=None),
75
            )
76
        ]
77

  
78
    def handle_search_fields(self, query, search):
79
        if search.get('supitemgroup'):
80
            query.add_join((GroupHierarchy, GroupHierarchy.idchild ==
81
                query.items.c.idsupitemgroup))
82
            query.add_filter(GroupHierarchy.idparent ==
83
                search['supitemgroup'])
vigiboard/controllers/plugins/hostname.py
17 17
# along with this program; if not, write to the Free Software
18 18
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 19
################################################################################
20

  
21 20
"""
22 21
Un plugin pour VigiBoard qui ajoute une colonne avec le nom de l'hôte
23 22
sur lequel porte l'événement corrélé.
24 23
"""
24
import tw.forms as twf
25
from pylons.i18n import lazy_ugettext as l_
26

  
27
from vigilo.models.functions import sql_escape_like
25 28
from vigiboard.controllers.plugins import VigiboardRequestPlugin
26 29

  
27 30
class PluginHostname(VigiboardRequestPlugin):
28 31
    """
29 32
    Ajoute une colonne avec le nom de l'hôte impacté par un événement corrélé.
30 33
    """
34
    def get_search_fields(self):
35
        return [
36
            twf.TextField(
37
                'host',
38
                label_text=l_('Host'),
39
                validator=twf.validators.String(if_missing=None),
40
            )
41
        ]
42

  
43
    def handle_search_fields(self, query, search):
44
        if search.get('host'):
45
            host = sql_escape_like(search['host'])
46
            query.add_filter(query.items.c.hostname.ilike(host))
vigiboard/controllers/plugins/masked_events.py
1
# -*- coding: utf-8 -*-
2
# vim:set expandtab tabstop=4 shiftwidth=4:
3
################################################################################
4
#
5
# Copyright (C) 2007-2011 CS-SI
6
#
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License version 2 as
9
# published by the Free Software Foundation.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
################################################################################
20

  
21
"""
22
Un plugin pour VigiBoard qui ajoute une colonne avec le nombre
23
d'événements masqués d'un événement corrélé.
24
"""
25
from vigiboard.controllers.plugins import VigiboardRequestPlugin
26

  
27
class PluginMaskedEvents(VigiboardRequestPlugin):
28
    """
29
    Affiche le nombre d'événements masqués par l'événement corrélé.
30
    """
vigiboard/controllers/plugins/output.py
22 22
Un plugin pour VigiBoard qui ajoute une colonne avec la sortie
23 23
de la commande de test exécutée par Nagios sur cet hôte/service.
24 24
"""
25
import tw.forms as twf
26
from pylons.i18n import lazy_ugettext as l_
27

  
28
from vigilo.models.tables import Event
29
from vigilo.models.functions import sql_escape_like
25 30
from vigiboard.controllers.plugins import VigiboardRequestPlugin
26 31

  
27 32
class PluginOutput(VigiboardRequestPlugin):
28 33
    """Ajoute une colonne avec le message de Nagios."""
34
    def get_search_fields(self):
35
        return [
36
            twf.TextField(
37
                'output',
38
                label_text=l_('Output'),
39
                validator=twf.validators.String(if_missing=None),
40
            )
41
        ]
42

  
43
    def handle_search_fields(self, query, search):
44
        if search.get('output'):
45
            output = sql_escape_like(search['output'])
46
            query.add_filter(Event.message.ilike(output))
vigiboard/controllers/plugins/priority.py
22 22
Un plugin pour VigiBoard qui ajoute une colonne avec la priorité
23 23
ITIL de l'événement corrélé.
24 24
"""
25
import tw.forms as twf
26
from pylons.i18n import lazy_ugettext as l_
27
from formencode import schema, validators
28

  
29
from vigilo.models.tables import CorrEvent
25 30
from vigiboard.controllers.plugins import VigiboardRequestPlugin
26 31

  
32
from tw.forms.fields import ContainerMixin, FormField
33
from tw.core.base import WidgetsList
34

  
35
class HorizontalBox(ContainerMixin, FormField):
36
    """
37
    Container de widgets, qui se contente de les placer
38
    côte-à-côte horizontalement.
39
    """
40

  
41
    template = """<div
42
    xmlns="http://www.w3.org/1999/xhtml"
43
    xmlns:py="http://genshi.edgewall.org/"
44
    id="${id}"
45
    name="${name}"
46
    class="${css_class}"
47
    py:attrs="attrs">
48
    <py:for each="field in fields">
49
        ${field.display(value_for(field), **args_for(field))}
50
    </py:for>
51
</div>
52
"""
53

  
54
    def generate_schema(self):
55
        """
56
        Fait en sorte que l'absence de saisie dans les sous-champs
57
        du container ne génère pas une erreur (Valeur manquante)
58
        sur le container lui-même.
59
        """
60
        super(HorizontalBox, self).generate_schema()
61
        self.validator.if_missing = None
62

  
63

  
27 64
class PluginPriority(VigiboardRequestPlugin):
28 65
    """
29 66
    Ce plugin affiche la priorité ITIL des événements corrélés.
......
36 73
    (ordre opposé). L'ordre utilisé par VigiBoard pour le tri est
37 74
    défini dans la variable de configuration C{vigiboard_priority_order}.
38 75
    """
76

  
77
    def get_search_fields(self):
78
        options = [
79
            ('eq',  u'='),
80
            ('neq', u'≠'),
81
            ('gt',  u'>'),
82
            ('gte', u'≥'),
83
            ('lt',  u'<'),
84
            ('lte', u'≤'),
85
        ]
86

  
87
        return [
88
            HorizontalBox(
89
                'priority',
90
                label_text=l_('Priority'),
91
                fields=[
92
                    twf.SingleSelectField(
93
                        'op',
94
                        options=options,
95
                        validator=twf.validators.OneOf(
96
                            dict(options).keys(),
97
                            if_invalid=None,
98
                            if_missing=None,
99
                        ),
100
                    ),
101
                    twf.TextField(
102
                        'value',
103
                        validator=twf.validators.Int(
104
                            if_invalid=None,
105
                            if_missing=None,
106
                        ),
107
                    ),
108
                ],
109
            )
110
        ]
111

  
112
    def handle_search_fields(self, query, search):
113
        if (not search.get('priority')):
114
            return
115

  
116
        op = search['priority']['op']
117
        value = search['priority']['value']
118

  
119
        if (not op) or (not value):
120
            search['priority'] = None
121
            return
122

  
123
        if op == 'eq':
124
            query.add_filter(CorrEvent.priority == value)
125
        elif op == 'neq':
126
            query.add_filter(CorrEvent.priority != value)
127
        elif op == 'gt':
128
            query.add_filter(CorrEvent.priority > value)
129
        elif op == 'gte':
130
            query.add_filter(CorrEvent.priority >= value)
131
        elif op == 'lt':
132
            query.add_filter(CorrEvent.priority < value)
133
        elif op == 'lte':
134
            query.add_filter(CorrEvent.priority <= value)
vigiboard/controllers/plugins/servicename.py
22 22
Un plugin pour VigiBoard qui ajoute une colonne avec le nom du service
23 23
à l'origine de l'événement corrélé.
24 24
"""
25
import tw.forms as twf
26
from pylons.i18n import lazy_ugettext as l_
27

  
28
from vigilo.models.functions import sql_escape_like
25 29
from vigiboard.controllers.plugins import VigiboardRequestPlugin
26 30

  
27 31
class PluginServicename(VigiboardRequestPlugin):
......
30 34
    Si l'événement corrélé porte directement sur un hôte,
31 35
    alors le nom de service vaut None.
32 36
    """
37
    def get_search_fields(self):
38
        return [
39
            twf.TextField(
40
                'service',
41
                label_text=l_('Service'),
42
                validator=twf.validators.String(if_missing=None),
43
            )
44
        ]
45

  
46
    def handle_search_fields(self, query, search):
47
        if search.get('service'):
48
            service = sql_escape_like(search['service'])
49
            query.add_filter(query.items.c.servicename.ilike(service))
vigiboard/controllers/plugins/status.py
26 26
    -   la dernière colonne permet de (dé)sélectionner l'événement pour
27 27
        effectuer un traitement par lot.
28 28
"""
29
import tw.forms as twf
30
from pylons.i18n import lazy_ugettext as l_
31

  
32
from vigilo.models.tables import CorrEvent
33
from vigilo.models.functions import sql_escape_like
29 34
from vigiboard.controllers.plugins import VigiboardRequestPlugin
30 35

  
31 36
class PluginStatus(VigiboardRequestPlugin):
......
40 45
        Ce plugin en ajoute 4, au lieu de 1 comme la plupart des plugins.
41 46
        """
42 47
        return 4
48

  
49
    def get_search_fields(self):
50
        return [
51
            twf.TextField(
52
                'trouble_ticket',
53
                label_text=l_('Trouble Ticket'),
54
                validator=twf.validators.String(if_missing=None),
55
            )
56
        ]
57

  
58
    def handle_search_fields(self, query, search):
59
        if search.get('trouble_ticket'):
60
            tt = sql_escape_like(search['trouble_ticket'])
61
            query.add_filter(CorrEvent.trouble_ticket.ilike(tt))
vigiboard/controllers/root.py
22 22

  
23 23
from datetime import datetime
24 24
from time import mktime
25
import math
26 25

  
27
from tg.exceptions import HTTPNotFound, HTTPInternalServerError
26
from tg.exceptions import HTTPNotFound
28 27
from tg import expose, validate, require, flash, url, \
29 28
    tmpl_context, request, config, session, redirect
30 29
from webhelpers import paginate
......
33 32
from sqlalchemy import asc
34 33
from sqlalchemy.sql import func
35 34
from sqlalchemy.orm import aliased
36
from sqlalchemy.orm import contains_eager
37 35
from sqlalchemy.sql.expression import or_
38 36
from repoze.what.predicates import Any, All, in_group, \
39 37
                                    has_permission, not_anonymous, \
40 38
                                    NotAuthorizedError
41 39
from formencode import schema
42
from pkg_resources import working_set
43 40

  
44 41
from vigilo.models.session import DBSession
45 42
from vigilo.models.tables import Event, EventHistory, CorrEvent, Host, \
46 43
                                    SupItem, SupItemGroup, LowLevelService, \
47 44
                                    StateName, State, DataPermission
48 45
from vigilo.models.tables.grouphierarchy import GroupHierarchy
49
from vigilo.models.functions import sql_escape_like
50 46
from vigilo.models.tables.secondary_tables import EVENTSAGGREGATE_TABLE, \
51 47
        USER_GROUP_TABLE, SUPITEM_GROUP_TABLE
52 48

  
......
60 56

  
61 57
from vigiboard.widgets.edit_event import edit_event_status_options, \
62 58
                                            EditEventForm
63
from vigiboard.widgets.search_form import create_search_form, get_calendar_lang
59
from vigiboard.widgets.search_form import create_search_form
64 60

  
65 61
__all__ = ('RootController', 'get_last_modification_timestamp',
66 62
           'date_to_timestamp')
......
88 84

  
89 85
    def process_form_errors(self, *argv, **kwargv):
90 86
        """
91
        Gestion des erreurs de validation : On affiche les erreurs
87
        Gestion des erreurs de validation : on affiche les erreurs
92 88
        puis on redirige vers la dernière page accédée.
93 89
        """
94 90
        for k in tmpl_context.form_errors:
......
103 99
    class DefaultSchema(schema.Schema):
104 100
        """Schéma de validation de la méthode default."""
105 101
        page = validators.Int(min=1, if_missing=1, if_invalid=1)
106
        supitemgroup = validators.Int(if_missing=None, if_invalid=None)
107
        host = validators.String(if_missing=None)
108
        service = validators.String(if_missing=None)
109
        output = validators.String(if_missing=None)
110
        trouble_ticket = validators.String(if_missing=None)
111
        from_date = validators.String(if_missing=None)
112
        to_date = validators.String(if_missing=None)
102

  
103
        # Nécessaire pour que les critères de recherche soient conservés.
104
        allow_extra_fields = True
105

  
106
        # 2ème validation, cette fois avec les champs
107
        # du formulaire de recherche.
108
        chained_validators = [create_search_form.validator]
113 109

  
114 110
    @validate(
115 111
        validators=DefaultSchema(),
116 112
        error_handler = process_form_errors)
117 113
    @expose('events_table.html')
118 114
    @require(access_restriction)
119
    def default(self, page, supitemgroup, host, service,
120
                output, trouble_ticket, from_date, to_date):
115
    def default(self, page, **search):
121 116
        """
122 117
        Page d'accueil de Vigiboard. Elle affiche, suivant la page demandée
123 118
        (page 1 par defaut), la liste des événements, rangés par ordre de prise
......
125 120
        Pour accéder à cette page, l'utilisateur doit être authentifié.
126 121

  
127 122
        @param page: Numéro de la page souhaitée, commence à 1
128
        @param host: Si l'utilisateur souhaite sélectionner seulement certains
129
                     événements suivant leur hôte, il peut placer une expression
130
                     ici en suivant la structure du LIKE en SQL
131
        @param service: Idem que host mais sur les services
132
        @param output: Idem que host mais sur le text explicatif
133
        @param trouble_ticket: Idem que host mais sur les tickets attribués
123
        @type page: C{int}
124
        @param search: Dictionnaire contenant les critères de recherche.
125
        @type search: C{dict}
134 126

  
135 127
        Cette méthode permet de satisfaire les exigences suivantes :
136 128
            - VIGILO_EXIG_VIGILO_BAC_0040,
......
152 144
            Event.idsupitem == aggregates.items.c.idsupitem))
153 145
        aggregates.add_order_by(asc(aggregates.items.c.hostname))
154 146

  
155
        search = {}
156

  
157
        # Application des filtres si nécessaire
158
        if supitemgroup:
159
            search['supitemgroup'] = supitemgroup
160
            aggregates.add_join((GroupHierarchy, GroupHierarchy.idchild ==
161
                aggregates.items.c.idsupitemgroup))
162
            aggregates.add_filter(GroupHierarchy.idparent == supitemgroup)
163

  
164
        if host:
165
            search['host_'] = host
166
            host = sql_escape_like(host)
167
            aggregates.add_filter(aggregates.items.c.hostname.ilike(
168
                '%s' % host))
169

  
170
        if service:
171
            search['service'] = service
172
            service = sql_escape_like(service)
173
            aggregates.add_filter(aggregates.items.c.servicename.ilike(
174
                '%s' % service))
175

  
176
        if output:
177
            search['output'] = output
178
            output = sql_escape_like(output)
179
            aggregates.add_filter(Event.message.ilike('%s' % output))
180

  
181
        if trouble_ticket:
182
            search['tt'] = trouble_ticket
183
            trouble_ticket = sql_escape_like(trouble_ticket)
184
            aggregates.add_filter(CorrEvent.trouble_ticket.ilike(
185
                '%s' % trouble_ticket))
186

  
187
        if from_date:
188
            search['from_date'] = from_date.lower()
189
            try:
190
                # TRANSLATORS: Format de date et heure Python/JavaScript.
191
                # TRANSLATORS: http://www.dynarch.com/static/jscalendar-1.0/doc/html/reference.html#node_sec_5.3.5
192
                # TRANSLATORS: http://docs.python.org/release/2.5/lib/module-time.html
193
                from_date = datetime.strptime(
194
                    from_date.encode('utf8'),
195
                    _('%Y-%m-%d %I:%M:%S %p').encode('utf8'))
196
            except ValueError:
197
                # On ignore silencieusement la date invalide reçue.
198
                pass
199
            else:
200
                aggregates.add_filter(CorrEvent.timestamp_active >= from_date)
201

  
202
        if to_date:
203
            search['to_date'] = to_date.lower()
204
            try:
205
                # TRANSLATORS: Format de date et heure Python/JavaScript.
206
                # TRANSLATORS: http://www.dynarch.com/static/jscalendar-1.0/doc/html/reference.html#node_sec_5.3.5
207
                # TRANSLATORS: http://docs.python.org/release/2.5/lib/module-time.html
208
                to_date = datetime.strptime(
209
                    to_date.encode('utf8'),
210
                    _('%Y-%m-%d %I:%M:%S %p').encode('utf8'))
211
            except ValueError:
212
                # On ignore silencieusement la date invalide reçue.
213
                pass
214
            else:
215
                aggregates.add_filter(CorrEvent.timestamp_active <= to_date)
147
        # Application des filtres des plugins si nécessaire.
148
        for plugin, instance in config.get('columns_plugins', []):
149
            instance.handle_search_fields(aggregates, search)
150

  
151
        # Certains arguments sont réservés dans url_for().
152
        # On effectue les substitutions adéquates.
153
        # Par exemple: "host" devient "host_".
154
        reserved = ('host', )
155
        copy = search.copy()
156
        for column in copy:
157
            if column in reserved:
158
                search[column + '_'] = search[column]
159
                del search[column]
216 160

  
217 161
        # Pagination des résultats
218 162
        aggregates.generate_request()
219 163
        items_per_page = int(config['vigiboard_items_per_page'])
220
        page = paginate.Page(aggregates.req, page=page, items_per_page=items_per_page)
164
        page = paginate.Page(aggregates.req, page=page,
165
            items_per_page=items_per_page)
221 166

  
222 167
        # Récupération des données des plugins
223 168
        plugins_data = {}
......
246 191
            event_edit_status_options = edit_event_status_options,
247 192
            search_form = create_search_form,
248 193
            search = search,
249
            get_calendar_lang = get_calendar_lang,
250 194
        )
251 195

  
252 196

  
......
310 254
        # Pagination des résultats
311 255
        events.generate_request()
312 256
        items_per_page = int(config['vigiboard_items_per_page'])
313
        page = paginate.Page(events.req, page=page, items_per_page=items_per_page)
257
        page = paginate.Page(events.req, page=page,
258
            items_per_page=items_per_page)
314 259

  
315 260
        # Vérification que l'événement existe
316 261
        if not page.item_count:
......
325 270
            page = page,
326 271
            search_form = create_search_form,
327 272
            search = {},
328
            get_calendar_lang = get_calendar_lang,
329 273
        )
330 274

  
331 275

  
......
388 332
            page = page,
389 333
            search_form = create_search_form,
390 334
            search = {},
391
            get_calendar_lang = get_calendar_lang,
392 335
        )
393 336

  
394 337

  
......
436 379
        # Pagination des résultats
437 380
        aggregates.generate_request()
438 381
        items_per_page = int(config['vigiboard_items_per_page'])
439
        page = paginate.Page(aggregates.req, page=page, items_per_page=items_per_page)
382
        page = paginate.Page(aggregates.req, page=page,
383
            items_per_page=items_per_page)
440 384

  
441 385
        # Vérification qu'il y a au moins 1 événement qui correspond
442 386
        if not page.item_count:
......
460 404
            event_edit_status_options = edit_event_status_options,
461 405
            search_form = create_search_form,
462 406
            search = {},
463
            get_calendar_lang = get_calendar_lang,
464 407
        )
465 408

  
466 409

  
......
554 497
        for event in events.req:
555 498
            if trouble_ticket and trouble_ticket != event.trouble_ticket:
556 499
                history = EventHistory(
557
                        type_action="Ticket change",
500
                        type_action=u"Ticket change",
558 501
                        idevent=event.idcause,
559 502
                        value=unicode(trouble_ticket),
560 503
                        text="Changed trouble ticket from '%(from)s' "
vigiboard/lib/dateformat.py
1
# -*- coding: utf-8 -*-
2
"""
3
Validateur et convertisseur de dates selon un format.
4
"""
5
from formencode.api import FancyValidator, Invalid
6
from datetime import datetime
7

  
8
from pylons.i18n import ugettext as _, lazy_ugettext as l_
9

  
10
class DateFormatConverter(FancyValidator):
11
    """
12
    Valide une date selon un format identique à ceux
13
    acceptés par la fonction strptime().
14
    """
15
    messages = {
16
        'invalid': 'Invalid value',
17
    }
18

  
19
    def _to_python(self, value, state):
20
        if not isinstance(value, basestring):
21
            raise Invalid(self.message('invalid', state), value, state)
22

  
23
        str_date = value.lower()
24
        if isinstance(str_date, unicode):
25
            str_date = str_date.encode('utf-8')
26

  
27
        try:
28
            # TRANSLATORS: Format de date et heure Python/JavaScript.
29
            # TRANSLATORS: http://www.dynarch.com/static/jscalendar-1.0/doc/html/reference.html#node_sec_5.3.5
30
            # TRANSLATORS: http://docs.python.org/release/2.5/lib/module-time.html
31
            date = datetime.strptime(str_date, _('%Y-%m-%d %I:%M:%S %p').encode('utf8'))
32
        except ValueError, e:
33
            raise Invalid(self.message('invalid', state), value, state)
34
        return date
35

  
36
    def _from_python(self, value, state):
37
        if not isinstance(value, datetime):
38
            raise Invalid(self.message('invalid', state), value, state)
39

  
40
        # Même format que pour _to_python.
41
        return datetime.strftime(
42
                    value,
43
                    _('%Y-%m-%d %I:%M:%S %p').encode('utf8')
44
                ).decode('utf-8')
vigiboard/tests/functional/test_search_form_misc.py
133 133
        transaction.commit()
134 134

  
135 135
        # Préparation des dates/heures.
136
        # On réutilise le formattage attendu par le contrôleur
137
        # (donc dépendant de la locale des tests, "fr" par défaut).
138
        # TRANSLATORS: Format de date et heure.
139
        from_date = timestamp.strftime(str(_("%Y-%m-%d %I:%M:%S %p")))
140
        # TRANSLATORS: Format de date et heure.
141
        to_date = datetime.max.strftime(str(_("%Y-%m-%d %I:%M:%S %p")))
136
        from_date = timestamp.strftime("%Y-%m-%d %I:%M:%S %p")
137
        to_date = datetime.max.strftime("%Y-%m-%d %I:%M:%S %p")
142 138

  
143 139
        # Permet également de vérifier que la recherche
144 140
        # par date est inclusive.
......
148 144
                'to_date': to_date,
149 145
            },
150 146
            extra_environ={'REMOTE_USER': 'user'})
151
        transaction.commit()
152 147

  
153 148
        # Il doit y avoir 1 seule ligne de résultats.
154 149
        rows = response.lxml.xpath('//table[@class="vigitable"]/tbody/tr')
vigiboard/widgets/search_form.py
23 23
from pylons.i18n import lazy_ugettext as l_
24 24
from tw.api import WidgetsList
25 25
import tw.forms as twf
26
from tg.i18n import get_lang
27 26
import tg
28 27

  
29
from vigilo.models.session import DBSession
30
from vigilo.models.tables.group import Group
31

  
32 28
__all__ = (
33 29
    'SearchForm',
34 30
    'create_search_form',
35 31
)
36 32

  
37
class GroupSelector(twf.InputField):
38
    params = ["choose_text", "text_value", "clear_text"]
39
    choose_text = l_('Choose')
40
    clear_text = l_('Clear')
41
    text_value = ''
42

  
43
    template = """
44
<div xmlns="http://www.w3.org/1999/xhtml"
45
   xmlns:py="http://genshi.edgewall.org/" py:strip="">
46
<input type="hidden" name="${name}" class="${css_class}"
47
    id="${id}.value" value="${value}" py:attrs="attrs" />
48
<input type="text" class="${css_class}" id="${id}.ui"
49
    value="${text_value}" readonly="readonly" py:attrs="attrs" />
50
<input type="button" class="${css_class}" id="${id}"
51
    value="${choose_text}" py:attrs="attrs" />
52
<input type="button" class="${css_class}" id="${id}.clear"
53
    value="${clear_text}" py:attrs="attrs" />
54
</div>
55
"""
56

  
57
    def update_params(self, d):
58
        super(GroupSelector, self).update_params(d)
59
        text_value = DBSession.query(Group.name).filter(
60
                        Group.idgroup == d.value).scalar()
61
        if not text_value:
62
            d.value = ''
63
        else:
64
            d.text_value = text_value
65

  
66 33
class SearchForm(twf.TableForm):
67 34
    """
68 35
    Formulaire de recherche dans les événements
......
78 45
    method = 'GET'
79 46
    style = 'display: none'
80 47

  
81
    class fields(WidgetsList):
82
        supitemgroup = GroupSelector(label_text=l_('Group'))
83
        host = twf.TextField(label_text=l_('Host'))
84
        service = twf.TextField(label_text=l_('Service'))
85
        output = twf.TextField(label_text=l_('Output'))
86
        trouble_ticket = twf.TextField(label_text=l_('Trouble Ticket'))
87
        from_date = twf.CalendarDateTimePicker(
88
            label_text = l_('From'),
89
            button_text = l_("Choose"),
90
            not_empty = False)
91
        to_date = twf.CalendarDateTimePicker(
92
            label_text = l_('To'),
93
            button_text = l_("Choose"),
94
            not_empty = False)
95

  
96
def get_calendar_lang():
97
    # TODO: Utiliser le champ "language" du modèle pour cet utilisateur ?
98
    # On récupère la langue du navigateur de l'utilisateur
99
    lang = get_lang()
100
    if not lang:
101
        lang = tg.config['lang']
102
    else:
103
        lang = lang[0]
104

  
105
    # TODO: Il faudrait gérer les cas où tout nous intéresse dans "lang".
106
    # Si l'identifiant de langage est composé (ex: "fr_FR"),
107
    # on ne récupère que la 1ère partie.
108
    lang = lang.replace('_', '-')
109
    lang = lang.split('-')[0]
110
    return lang
48
    fields = [
49
        twf.HiddenField('page')
50
    ]
51
    for plugin, instance in tg.config.get('columns_plugins', []):
52
        fields.extend(instance.get_search_fields())
111 53

  
112 54
create_search_form = SearchForm("search_form",
113 55
    submit_text=l_('Search'), action=tg.url('/'),

Also available in: Unified diff