cubicweb/web/views/undohistory.py
changeset 11057 0b59724cb3f2
parent 10666 7f6b5f023884
child 11767 432f87a63057
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 # copyright 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 
       
    19 __docformat__ = "restructuredtext en"
       
    20 from cubicweb import _
       
    21 
       
    22 
       
    23 from logilab.common.registry import Predicate
       
    24 
       
    25 from cubicweb import UnknownEid, tags, transaction as tx
       
    26 from cubicweb.view import View, StartupView
       
    27 from cubicweb.predicates import match_kwargs, ExpectedValuePredicate
       
    28 from cubicweb.schema import display_name
       
    29 
       
    30 
       
    31 class undoable_action(Predicate):
       
    32     """Select only undoable actions depending on filters provided. Undo Action
       
    33     is expected to be specified by the `tx_action` argument.
       
    34 
       
    35     Currently the only implemented filter is:
       
    36 
       
    37     :param action_type: chars among CUDAR (standing for Create, Update, Delete,
       
    38                         Add, Remove)
       
    39     """
       
    40 
       
    41     # XXX FIXME : this selector should be completed to allow selection on the
       
    42     # entity or relation types and public / private.
       
    43     def __init__(self, action_type='CUDAR'):
       
    44         assert not set(action_type) - set('CUDAR')
       
    45         self.action_type = action_type
       
    46 
       
    47     def __str__(self):
       
    48         return '%s(%s)' % (self.__class__.__name__, ', '.join(
       
    49             "%s=%v" % (str(k), str(v)) for k, v in kwargs.items() ))
       
    50 
       
    51     def __call__(self, cls, req, tx_action=None, **kwargs):
       
    52         # tx_action is expected to be a transaction.AbstractAction
       
    53         if not isinstance(tx_action, tx.AbstractAction):
       
    54             return 0
       
    55         # Filter according to action type
       
    56         return int(tx_action.action in self.action_type)
       
    57 
       
    58 
       
    59 class UndoHistoryView(StartupView):
       
    60     __regid__ = 'undohistory'
       
    61     title = _('Undoing')
       
    62     item_vid = 'undoable-transaction-view'
       
    63     cache_max_age = 0
       
    64 
       
    65     redirect_path = 'view' #TODO
       
    66     redirect_params = dict(vid='undohistory') #TODO
       
    67     public_actions_only = True
       
    68 
       
    69     # TODO Allow to choose if if want all actions or only the public ones
       
    70     # (default)
       
    71 
       
    72     def call(self, **kwargs):
       
    73         txs = self._cw.cnx.undoable_transactions()
       
    74         if txs :
       
    75             self.w(u"<ul class='undo-transactions'>")
       
    76             for tx in txs:
       
    77                 self.cell_call(tx)
       
    78             self.w(u"</ul>")
       
    79 
       
    80     def cell_call(self, tx):
       
    81         self.w(u'<li>')
       
    82         self.wview(self.item_vid, None, txuuid=tx.uuid,
       
    83                    public=self.public_actions_only,
       
    84                    redirect_path=self.redirect_path,
       
    85                    redirect_params=self.redirect_params)
       
    86         self.w(u'</li>\n')
       
    87 
       
    88 
       
    89 class UndoableTransactionView(View):
       
    90     __regid__ = 'undoable-transaction-view'
       
    91     __select__ = View.__select__ & match_kwargs('txuuid')
       
    92 
       
    93     item_vid = 'undoable-action-list-view'
       
    94     cache_max_age = 0
       
    95 
       
    96     def build_undo_link(self, txuuid,
       
    97                         redirect_path=None, redirect_params=None):
       
    98         """ the kwargs are passed to build_url"""
       
    99         _ = self._cw._
       
   100         redirect = {}
       
   101         if redirect_path:
       
   102             redirect['__redirectpath'] = redirect_path
       
   103         if redirect_params:
       
   104             if isinstance(redirect_params, dict):
       
   105                 redirect['__redirectparams'] = self._cw.build_url_params(**redirect_params)
       
   106             else:
       
   107                 redirect['__redirectparams'] = redirect_params
       
   108         link_url = self._cw.build_url('undo', txuuid=txuuid, **redirect)
       
   109         msg = u"<span class='undo'>%s</span>" % tags.a( _('undo'), href=link_url)
       
   110         return msg
       
   111 
       
   112     def call(self, txuuid, public=True,
       
   113              redirect_path=None, redirect_params=None):
       
   114         _ = self._cw._
       
   115         txinfo = self._cw.cnx.transaction_info(txuuid)
       
   116         try:
       
   117             #XXX Under some unknown circumstances txinfo.user_eid=-1
       
   118             user = self._cw.entity_from_eid(txinfo.user_eid)
       
   119         except UnknownEid:
       
   120             user = None
       
   121         undo_url = self.build_undo_link(txuuid,
       
   122                                         redirect_path=redirect_path,
       
   123                                         redirect_params=redirect_params)
       
   124         txinfo_dict = dict( dt = self._cw.format_date(txinfo.datetime, time=True),
       
   125                             user_eid = txinfo.user_eid,
       
   126                             user = user and user.view('outofcontext') or _("undefined user"),
       
   127                             txuuid = txuuid,
       
   128                             undo_link = undo_url)
       
   129         self.w( _("By %(user)s on %(dt)s [%(undo_link)s]") % txinfo_dict)
       
   130 
       
   131         tx_actions = txinfo.actions_list(public=public)
       
   132         if tx_actions :
       
   133             self.wview(self.item_vid, None, tx_actions=tx_actions)
       
   134 
       
   135 
       
   136 class UndoableActionListView(View):
       
   137     __regid__ = 'undoable-action-list-view'
       
   138     __select__ = View.__select__ & match_kwargs('tx_actions')
       
   139     title = _('Undoable actions')
       
   140     item_vid = 'undoable-action-view'
       
   141     cache_max_age = 0
       
   142 
       
   143     def call(self, tx_actions):
       
   144         if tx_actions :
       
   145             self.w(u"<ol class='undo-actions'>")
       
   146             for action in tx_actions:
       
   147                 self.cell_call(action)
       
   148             self.w(u"</ol>")
       
   149 
       
   150     def cell_call(self, action):
       
   151         self.w(u'<li>')
       
   152         self.wview(self.item_vid, None, tx_action=action)
       
   153         self.w(u'</li>\n')
       
   154 
       
   155 
       
   156 class UndoableActionBaseView(View):
       
   157     __regid__ = 'undoable-action-view'
       
   158     __abstract__ = True
       
   159 
       
   160     def call(self, tx_action):
       
   161         raise NotImplementedError(self)
       
   162 
       
   163     def _build_entity_link(self, eid):
       
   164         try:
       
   165             entity = self._cw.entity_from_eid(eid)
       
   166             return entity.view('outofcontext')
       
   167         except UnknownEid:
       
   168             return _("(suppressed) entity #%d") % eid
       
   169 
       
   170     def _build_relation_info(self, rtype, eid_from,  eid_to):
       
   171         return dict( rtype=display_name(self._cw, rtype),
       
   172                      entity_from=self._build_entity_link(eid_from),
       
   173                      entity_to=self._build_entity_link(eid_to) )
       
   174 
       
   175     def _build_entity_info(self, etype, eid, changes):
       
   176         return dict( etype=display_name(self._cw, etype),
       
   177                      entity=self._build_entity_link(eid),
       
   178                      eid=eid,
       
   179                      changes=changes)
       
   180 
       
   181 
       
   182 class UndoableAddActionView(UndoableActionBaseView):
       
   183     __select__ = UndoableActionBaseView.__select__ & undoable_action(action_type='A')
       
   184 
       
   185     def call(self, tx_action):
       
   186         _ = self._cw._
       
   187         self.w(_("Added relation : %(entity_from)s %(rtype)s %(entity_to)s") %
       
   188                self._build_relation_info(tx_action.rtype, tx_action.eid_from, tx_action.eid_to))
       
   189 
       
   190 
       
   191 class UndoableRemoveActionView(UndoableActionBaseView):
       
   192     __select__ = UndoableActionBaseView.__select__ & undoable_action(action_type='R')
       
   193 
       
   194     def call(self, tx_action):
       
   195         _ = self._cw._
       
   196         self.w(_("Delete relation : %(entity_from)s %(rtype)s %(entity_to)s") %
       
   197                self._build_relation_info(tx_action.rtype, tx_action.eid_from, tx_action.eid_to))
       
   198 
       
   199 
       
   200 class UndoableCreateActionView(UndoableActionBaseView):
       
   201     __select__ = UndoableActionBaseView.__select__ & undoable_action(action_type='C')
       
   202 
       
   203     def call(self, tx_action):
       
   204         _ = self._cw._
       
   205         self.w(_("Created %(etype)s : %(entity)s") % #  : %(changes)s
       
   206                self._build_entity_info( tx_action.etype, tx_action.eid, tx_action.changes) )
       
   207 
       
   208 
       
   209 class UndoableDeleteActionView(UndoableActionBaseView):
       
   210     __select__ = UndoableActionBaseView.__select__ & undoable_action(action_type='D')
       
   211 
       
   212     def call(self, tx_action):
       
   213         _ = self._cw._
       
   214         self.w(_("Deleted %(etype)s : %(entity)s") %
       
   215                self._build_entity_info( tx_action.etype, tx_action.eid, tx_action.changes))
       
   216 
       
   217 
       
   218 class UndoableUpdateActionView(UndoableActionBaseView):
       
   219     __select__ = UndoableActionBaseView.__select__ & undoable_action(action_type='U')
       
   220 
       
   221     def call(self, tx_action):
       
   222         _ = self._cw._
       
   223         self.w(_("Updated %(etype)s : %(entity)s") %
       
   224                self._build_entity_info( tx_action.etype, tx_action.eid, tx_action.changes))