doc/tutorials/advanced/part04_ui-base.rst
branch3.26
changeset 12468 3c66259723d9
parent 12459 dc793c6ebc48
child 12405 99e150b6a876
equal deleted inserted replaced
12467:7b887f7d711f 12468:3c66259723d9
    26     from cubicweb import _
    26     from cubicweb import _
    27     from cubicweb.web import component
    27     from cubicweb.web import component
    28     from cubicweb.web.views import error
    28     from cubicweb.web.views import error
    29     from cubicweb.predicates import anonymous_user
    29     from cubicweb.predicates import anonymous_user
    30 
    30 
       
    31 
    31     class FourOhFour(error.FourOhFour):
    32     class FourOhFour(error.FourOhFour):
    32 	__select__ = error.FourOhFour.__select__ & anonymous_user()
    33         __select__ = error.FourOhFour.__select__ & anonymous_user()
    33 
    34 
    34 	def call(self):
    35         def call(self):
    35 	    self.w(u"<h1>%s</h1>" % self._cw._('this resource does not exist'))
    36             self.w(u"<h1>%s</h1>" % self._cw._('this resource does not exist'))
    36 	    self.w(u"<p>%s</p>" % self._cw._('have you tried to login?'))
    37             self.w(u"<p>%s</p>" % self._cw._('have you tried to login?'))
    37 
    38 
    38 
    39 
    39     class LoginBox(component.CtxComponent):
    40     class LoginBox(component.CtxComponent):
    40 	"""display a box containing links to all startup views"""
    41         """display a box containing links to all startup views"""
    41 	__regid__ = 'sytweb.loginbox'
    42         __regid__ = 'sytweb.loginbox'
    42 	__select__ = component.CtxComponent.__select__ & anonymous_user()
    43         __select__ = component.CtxComponent.__select__ & anonymous_user()
    43 
    44 
    44 	title = _('Authenticate yourself')
    45         title = _('Authenticate yourself')
    45 	order = 70
    46         order = 70
    46 
    47 
    47 	def render_body(self, w):
    48         def render_body(self, w):
    48 	    cw = self._cw
    49             cw = self._cw
    49 	    form = cw.vreg['forms'].select('logform', cw)
    50             form = cw.vreg['forms'].select('logform', cw)
    50 	    form.render(w=w, table_class='', display_progress_div=False)
    51             form.render(w=w, table_class='', display_progress_div=False)
    51 
    52 
    52 The first class provides a new specific implementation of the default page you
    53 The first class provides a new specific implementation of the default page you
    53 get on 404 error, to display an adapted message to anonymous user.
    54 get on 404 error, to display an adapted message to anonymous user.
    54 
    55 
    55 .. Note::
    56 .. Note::
    84 
    85 
    85 .. sourcecode:: python
    86 .. sourcecode:: python
    86 
    87 
    87     from cubicweb.web.views import startup
    88     from cubicweb.web.views import startup
    88 
    89 
       
    90 
    89     class IndexView(startup.IndexView):
    91     class IndexView(startup.IndexView):
    90 	def call(self, **kwargs):
    92         def call(self, **kwargs):
    91 	    self.w(u'<div>\n')
    93             self.w(u'<div>\n')
    92 	    if self._cw.cnx.session.anonymous_session:
    94             if self._cw.cnx.session.anonymous_session:
    93 		self.w(u'<h4>%s</h4>\n' % self._cw._('Public Albums'))
    95                 self.w(u'<h4>%s</h4>\n' % self._cw._('Public Albums'))
    94 	    else:
    96             else:
    95 		self.w(u'<h4>%s</h4>\n' % self._cw._('Albums for %s') % self._cw.user.login)
    97                 self.w(u'<h4>%s</h4>\n' % self._cw._('Albums for %s') % self._cw.user.login)
    96 	    self._cw.vreg['views'].select('tree', self._cw).render(w=self.w)
    98             self._cw.vreg['views'].select('tree', self._cw).render(w=self.w)
    97 	    self.w(u'</div>\n')
    99             self.w(u'</div>\n')
       
   100 
    98 
   101 
    99     def registration_callback(vreg):
   102     def registration_callback(vreg):
   100 	vreg.register_all(globals().values(), __name__, (IndexView,))
   103         vreg.register_all(globals().values(), __name__, (IndexView,))
   101 	vreg.register_and_replace(IndexView, startup.IndexView)
   104         vreg.register_and_replace(IndexView, startup.IndexView)
   102 
   105 
   103 As you can see, we override the default index view found in
   106 As you can see, we override the default index view found in
   104 `cubicweb.web.views.startup`, geting back nothing but its identifier and selector
   107 `cubicweb.web.views.startup`, geting back nothing but its identifier and selector
   105 since we override the top level view's `call` method.
   108 since we override the top level view's `call` method.
   106 
   109 
   147 we can see:
   150 we can see:
   148 
   151 
   149 .. sourcecode:: python
   152 .. sourcecode:: python
   150 
   153 
   151     class File(AnyEntity):
   154     class File(AnyEntity):
   152 	"""customized class for File entities"""
   155         """customized class for File entities"""
   153 	__regid__ = 'File'
   156         __regid__ = 'File'
   154 	fetch_attrs, cw_fetch_order = fetch_config(['data_name', 'title'])
   157         fetch_attrs, cw_fetch_order = fetch_config(['data_name', 'title'])
   155 
   158 
   156 
   159 
   157 By default, `fetch_config` will return a `cw_fetch_order` method that will order
   160 By default, `fetch_config` will return a `cw_fetch_order` method that will order
   158 on the first attribute in the list. So, we could expect to get files ordered by
   161 on the first attribute in the list. So, we could expect to get files ordered by
   159 their name. But we don't.  What's up doc ?
   162 their name. But we don't.  What's up doc ?
   173 
   176 
   174     from cubes.folder import entities as folder
   177     from cubes.folder import entities as folder
   175 
   178 
   176     class FolderITreeAdapter(folder.FolderITreeAdapter):
   179     class FolderITreeAdapter(folder.FolderITreeAdapter):
   177 
   180 
   178 	def different_type_children(self, entities=True):
   181         def different_type_children(self, entities=True):
   179 	    rql = self.entity.cw_related_rql(self.tree_relation,
   182             rql = self.entity.cw_related_rql(self.tree_relation,
   180 					     self.parent_role, ('File',))
   183                                              self.parent_role, ('File',))
   181 	    rset = self._cw.execute(rql, {'x': self.entity.eid})
   184             rset = self._cw.execute(rql, {'x': self.entity.eid})
   182 	    if entities:
   185             if entities:
   183 		return list(rset.entities())
   186                 return list(rset.entities())
   184 	    return rset
   187             return rset
   185 
   188 
   186     def registration_callback(vreg):
   189     def registration_callback(vreg):
   187 	vreg.register_and_replace(FolderITreeAdapter, folder.FolderITreeAdapter)
   190         vreg.register_and_replace(FolderITreeAdapter, folder.FolderITreeAdapter)
   188 
   191 
   189 As you can see, we simple inherit from the adapter defined in the `folder` cube,
   192 As you can see, we simple inherit from the adapter defined in the `folder` cube,
   190 then we override the `different_type_children` method to give a clue to the ORM's
   193 then we override the `different_type_children` method to give a clue to the ORM's
   191 `cw_related_rql` method, that is responsible to generate the rql to get entities
   194 `cw_related_rql` method, that is responsible to generate the rql to get entities
   192 related to the folder by the `filed_under` relation (the value of the
   195 related to the folder by the `filed_under` relation (the value of the
   214     from cubicweb.predicates import is_instance
   217     from cubicweb.predicates import is_instance
   215     from cubicweb.web.views import navigation
   218     from cubicweb.web.views import navigation
   216 
   219 
   217 
   220 
   218     class FileIPrevNextAdapter(navigation.IPrevNextAdapter):
   221     class FileIPrevNextAdapter(navigation.IPrevNextAdapter):
   219 	__select__ = is_instance('File')
   222         __select__ = is_instance('File')
   220 
   223 
   221 	def previous_entity(self):
   224         def previous_entity(self):
   222 	    rset = self._cw.execute('File F ORDERBY FDN DESC LIMIT 1 WHERE '
   225             rset = self._cw.execute('File F ORDERBY FDN DESC LIMIT 1 WHERE '
   223 				    'X filed_under FOLDER, F filed_under FOLDER, '
   226                                     'X filed_under FOLDER, F filed_under FOLDER, '
   224 				    'F data_name FDN, X data_name > FDN, X eid %(x)s',
   227                                     'F data_name FDN, X data_name > FDN, X eid %(x)s',
   225 				    {'x': self.entity.eid})
   228                                     {'x': self.entity.eid})
   226 	    if rset:
   229             if rset:
   227 		return rset.get_entity(0, 0)
   230                 return rset.get_entity(0, 0)
   228 
   231 
   229 	def next_entity(self):
   232         def next_entity(self):
   230 	    rset = self._cw.execute('File F ORDERBY FDN ASC LIMIT 1 WHERE '
   233             rset = self._cw.execute('File F ORDERBY FDN ASC LIMIT 1 WHERE '
   231 				    'X filed_under FOLDER, F filed_under FOLDER, '
   234                                     'X filed_under FOLDER, F filed_under FOLDER, '
   232 				    'F data_name FDN, X data_name < FDN, X eid %(x)s',
   235                                     'F data_name FDN, X data_name < FDN, X eid %(x)s',
   233 				    {'x': self.entity.eid})
   236                                     {'x': self.entity.eid})
   234 	    if rset:
   237             if rset:
   235 		return rset.get_entity(0, 0)
   238                 return rset.get_entity(0, 0)
   236 
   239 
   237 
   240 
   238 The `IPrevNext` interface implemented by the adapter simply consist in the
   241 The `IPrevNext` interface implemented by the adapter simply consist in the
   239 `previous_entity` / `next_entity` methods, that should respectivly return the
   242 `previous_entity` / `next_entity` methods, that should respectivly return the
   240 previous / next entity or `None`. We make an RQL query to get files in the same
   243 previous / next entity or `None`. We make an RQL query to get files in the same
   277 .. sourcecode:: python
   280 .. sourcecode:: python
   278 
   281 
   279     from cubicweb.web.views import ibreadcrumbs
   282     from cubicweb.web.views import ibreadcrumbs
   280 
   283 
   281     class FileIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):
   284     class FileIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):
   282 	__select__ = is_instance('File')
   285         __select__ = is_instance('File')
   283 
   286 
   284 	def parent_entity(self):
   287         def parent_entity(self):
   285 	    if self.entity.filed_under:
   288             if self.entity.filed_under:
   286 		return self.entity.filed_under[0]
   289                 return self.entity.filed_under[0]
   287 
   290 
   288 In that case, we simply use attribute notation provided by the ORM to get the
   291 In that case, we simply use attribute notation provided by the ORM to get the
   289 folder in which the current file (e.g. `self.entity`) is located.
   292 folder in which the current file (e.g. `self.entity`) is located.
   290 
   293 
   291 .. Note::
   294 .. Note::