source: main/waeup.sirp/trunk/src/waeup/sirp/browser/viewlets.py @ 7205

Last change on this file since 7205 was 7205, checked in by Henrik Bettermann, 13 years ago

academics: Show students in departments.

students: Search for students in department.

  • Property svn:keywords set to Id
File size: 22.9 KB
Line 
1## $Id: viewlets.py 7205 2011-11-26 06:49:16Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import grok
19from zope.component import getMultiAdapter, queryAdapter
20from zope.interface import Interface
21from zope.location.interfaces import ISite
22from zope.traversing.browser import absoluteURL
23from waeup.sirp.browser.pages import (
24    UniversityPage, FacultyContainerPage, DatacenterPage, FacultyPage,
25    DepartmentPage, CoursePage, CertificatePage, CertificateCoursePage, UsersContainerPage)
26from waeup.sirp.browser.interfaces import (
27    IFacultyContainer, IFaculty, IDepartment, ICourse, ICertificate,
28    ICertificateCourse, IBreadcrumbContainer, IUniversity, IUsersContainer)
29from waeup.sirp.interfaces import (IWAeUPObject, IWAeUPXMLExporter,
30                                   IWAeUPXMLImporter, IDataCenter)
31from waeup.sirp.browser.layout import WAeUPPage
32from waeup.sirp.utils.helpers import get_user_account
33
34grok.templatedir('templates')
35grok.context(IWAeUPObject) # Make IWAeUPObject the default context
36
37class LeftSidebar(grok.ViewletManager):
38    grok.name('left')
39
40class BreadCrumbManager(grok.ViewletManager):
41    grok.name('breadcrumbs')
42
43class ActionBar(grok.ViewletManager):
44    grok.name('actionbar')
45
46class AdministrationTasks(grok.ViewletManager):
47    grok.name('admintasks')
48
49
50#
51# Baseclasses that give some defaults for really used viewlets.
52#
53class ActionButton(grok.Viewlet):
54    """A base for action buttons.
55
56    An action button provides an icon, some text and links to a
57    target.  If you want to set a different text, icon or target name
58    for some active button below, just override the approriate
59    attribute in the concerned viewlet.
60
61    Action buttons provide by default dynamic attributes
62
63     * ``alt``
64          An alternative text for the icon. By default the same as
65          the text.
66
67     * ``icon_url``
68          The URL of the icon.
69
70     * ``target_url``
71          The URL of the link target.
72
73    """
74    grok.baseclass()
75    grok.context(IWAeUPObject)
76    grok.viewletmanager(ActionBar)
77    icon = 'actionicon_modify.png' # File must exist in static/
78    target = '@@manage' # link to this viewname.
79    text = 'Edit' # Text to display on the button
80
81    # We set the template file explicitly (instead of using
82    # ``grok.template('actionbutton')``) to stick with this template
83    # also in derived classes in other packages. If we didn't, those
84    # derived ActionButton viewlets had to provide an own template,
85    # which would not be updated automatically, when the local
86    # template ``templates/actionbutton.pt`` changes.
87    #
88    # Inheriting viewlets that wish to use their own template anyway
89    # can do so by setting their local ``grok.template(<mytemplate>)``
90    # and setting ``template`` to ``None`` for the class::
91    #
92    # class DerivedActionButton(ActionButton):
93    #   ...
94    #   grok.template('overriding_template')
95    #   template = None
96    #   ...
97    #
98    template = grok.PageTemplateFile('templates/actionbutton.pt')
99
100    @property
101    def alt(self):
102        """Alternative text for icon.
103        """
104        return self.text
105
106    @property
107    def icon_url(self):
108        """Get the icon URL.
109        """
110        static = self.view.static
111        if static is None or static.get(self.icon, None) is None:
112            # In derived classes defined in other modules/packages
113            # than w.s.browser, ``static`` might refer to a static dir
114            # local to the derived class' module. As we often like to
115            # get the icons from here
116            # (i.e. waeup.sirp.browser/static), we set the directory
117            # resource appropiately.
118            #
119            # XXX: The hardcoding of 'w.s.browser' should be replaced
120            #      by something smarter.
121            #
122            # TODO: notes in here should go to general documentation.
123            static = queryAdapter(
124                self.request, Interface, name='waeup.sirp.browser')
125        return static[self.icon]()
126
127    @property
128    def target_url(self):
129        """Get a URL to the target...
130        """
131        return self.view.url(self.view.context, self.target)
132
133class PlainActionButton(ActionButton):
134    """A base for action buttons without image
135    """
136    grok.baseclass()
137    template = grok.PageTemplateFile('templates/plainactionbutton.pt')
138
139   
140class ManageActionButton(ActionButton):
141    """A base for 'edit' buttons
142    """
143    grok.baseclass()
144    grok.order(2)
145    grok.require('waeup.manageUniversity')
146    icon = 'actionicon_modify.png'
147    target = '@@manage'
148    text = 'Edit'
149
150class AddActionButton(ActionButton):
151    """A base for 'add' buttons.
152    """
153    grok.baseclass()
154    grok.order(4)
155    grok.require('waeup.manageUniversity')
156    icon = 'actionicon_add.png'
157    target = 'add'
158    text = 'Add'
159   
160class RemoveActionButton(ActionButton):
161    """A base for 'remove' buttons.
162    """
163    grok.baseclass()
164    grok.order(4)
165    grok.require('waeup.manageUniversity')
166    icon = 'actionicon_delete.png'
167    target = 'remove'
168    text = 'Remove'   
169
170class SearchActionButton(ActionButton):
171    """A base for 'search' buttons.
172    """
173    grok.baseclass()
174    grok.order(5)
175    grok.require('waeup.manageUniversity')
176    icon = 'actionicon_search.png'
177    target = 'search'
178    text = 'Search'
179
180
181#
182# General viewlets (for more than one page/context)
183#
184
185class BreadCrumbs(grok.Viewlet):
186    grok.context(IWAeUPObject)
187    grok.viewletmanager(BreadCrumbManager)
188    grok.order(1)
189
190    def getEntries(self):
191        result = []
192        site = grok.getSite()
193        context = self.context
194        breadcrumbs = IBreadcrumbContainer(self.view)
195        for breadcrumb in breadcrumbs:
196            yield dict(
197                title = breadcrumb.title,
198                url = self.view.url(breadcrumb.context, breadcrumb.target)
199                )
200
201
202# Problem with circular references. Disabled for now...
203# class ExportXMLAction(grok.Viewlet):
204#    grok.viewletmanager(ActionBar)
205#     #grok.view(Index)
206#     grok.order(98)
207#     grok.require('waeup.manageUniversity')
208
209#class ImportXMLAction(grok.Viewlet):
210#    grok.viewletmanager(ActionBar)
211#    #grok.view(Index)
212#    grok.order(99)
213#    grok.require('waeup.manageUniversity')
214#
215#    def update(self):
216#        # We cannot simply replace local sites.
217#        self.can_import = not ISite.providedBy(self.context)
218
219class LeftSidebarLink(grok.Viewlet):
220    """ An entry on left sidebar.
221
222    This is only a baseclass that won't be rendered actually. Deriving
223    viewlets can override certain values and will be rendered with the
224    values set here as default.
225    """
226    grok.baseclass()
227    grok.viewletmanager(LeftSidebar)
228    grok.context(IWAeUPObject)
229    grok.order(5)
230    grok.require('waeup.manageUniversity')
231    icon = 'actionicon_modify.png' # File must exist in static/
232    title = 'Text of link'
233
234    @property
235    def url(self):
236        return '@@index'
237
238    @property
239    def icon_url(self):
240        """Get the icon URL.
241        """
242        if self.icon:
243            static = self.view.static
244            if static is None or static.get(self.icon, None) is None:
245                # In derived classes defined in other modules/packages
246                # than w.s.browser, ``static`` might refer to a static dir
247                # local to the derived class' module. As we often like to
248                # get the icons from here
249                # (i.e. waeup.sirp.browser/static), we set the directory
250                # resource appropiately.
251                #
252                # XXX: The hardcoding of 'w.s.browser' should be replaced
253                #      by something smarter.
254                #
255                # TODO: notes in here should go to general documentation.
256                static = queryAdapter(
257                    self.request, Interface, name='waeup.sirp.browser')
258            return static[self.icon]()
259        return
260   
261    # Render link only if url is provided.
262    def render(self):
263        if self.url:
264            if self.icon_url:
265                return u'<div class="portlet"><a href="%s"><img src="%s" /> %s </a></div>' % (
266                    self.url, self.icon_url, self.title)
267            else:
268                return u'<div class="portlet"><a href="%s">%s </a></div>' % (
269                    self.url, self.title)
270        else:
271            return ''
272           
273#
274# waeup.sirp.app.University viewlets...
275#
276class Login(grok.Viewlet):
277    """This viewlet allows to login in the sidebar.
278    """
279    grok.viewletmanager(LeftSidebar)
280    grok.context(IWAeUPObject)
281    grok.view(Interface)
282    grok.order(2)
283    grok.require('waeup.Anonymous')
284    text = 'Login'
285    link = 'login'
286
287    def render(self):
288        if self.request.principal.id != 'zope.anybody':
289            return ''
290        url = self.view.url(grok.getSite(), self.link)
291        return u'<div class="portlet"><a href="%s">%s</a></div>' % (
292                url, self.text)
293
294
295class ManageLink(grok.Viewlet):
296    """A link displayed in the upper left box.
297
298    This viewlet renders a link to the application object's settings
299    form (the 'manage' view).
300
301    In derived classes you can create different links by setting a
302    different link and text attribute. The `link` parameter is
303    understood relative to the respective application object, so that
304    ``@@manage`` will create a link to
305    ``localhost:8080/app/@@manage``.
306
307    Links defined by descendants from this viewlet are displayed on
308    every page the user is allowed to go to, if the user has also the
309    permissions set by `grok.require()`. By default only users with
310    ``waeup.manageUniversity`` permission will see links defined by
311    this or derivated classes.
312    """
313    grok.viewletmanager(LeftSidebar)
314    grok.context(IWAeUPObject)
315    grok.view(Interface)
316    grok.order(5)
317    # This link is only displayed, if the user is
318    # allowed to use it!
319    grok.require('waeup.managePortalConfiguration')
320
321    link = 'configuration'
322    text = u'Portal Configuration'
323   
324    def render(self):
325        url = self.view.url(grok.getSite(), self.link)
326        return u'<div class="portlet"><a href="%s">%s</a></div>' % (
327                url, self.text)
328
329class ManageUsersLink(ManageLink):
330    """A link to users management, placed in upper left box.
331    """
332    grok.order(6)
333    grok.require('waeup.manageUsers')
334
335    link = u'users'
336    text = u'Portal Users'
337
338class ManageDataCenter(ManageLink):
339    """A link to datacenter, placed in upper left box.
340    """
341    grok.order(6)
342    grok.require('waeup.manageDataCenter')
343
344    link = u'datacenter'
345    text = u'Data Center'
346
347class MyPreferences(LeftSidebarLink):
348    """A link to personal preferences, placed in upper left box.
349    """
350    grok.order(7)
351    grok.require('waeup.Public')
352    title = u'My Preferences'
353    icon = ''
354
355    @property
356    def url(self):
357        account_object = get_user_account(self.request)
358        if account_object:
359            return self.view.url(account_object)
360        return
361
362class MyRoles(LeftSidebarLink):
363    """A link to display site and local roles.
364    """
365    grok.order(8)
366    grok.require('waeup.Public')
367    title = u'My Roles'
368    icon = ''
369
370    @property
371    def url(self):
372        account_object = get_user_account(self.request)
373        if account_object:
374            return self.view.url(account_object) + '/my_roles'
375        return
376
377#
378# Manage ("Edit settings") actions...
379#
380
381class ManageDataCenterActionButton(ManageActionButton):
382    """ 'Edit settings' button for datacenter.
383    """
384    grok.context(IDataCenter)
385    grok.view(DatacenterPage)
386    text = 'Edit settings'
387
388class ManageFacultyContainerActionButton(ManageActionButton):
389    """ 'Manage settings' button for faculties.
390    """
391    grok.context(IFacultyContainer)
392    grok.view(FacultyContainerPage)
393    text = 'Manage academic section'
394
395class SearchFacultyContainerActionButton(ManageActionButton):
396    """ 'Manage settings' button for faculties.
397    """
398    grok.context(IFacultyContainer)
399    grok.view(FacultyContainerPage)
400    text = 'Search academic section'
401    icon = 'actionicon_search.png'
402    target = '@@search'
403   
404class ManageFacultyActionButton(ManageActionButton):
405    """ 'Manage settings' button for faculties.
406    """
407    grok.context(IFaculty)
408    grok.view(FacultyPage)
409    text = 'Manage faculty'
410
411class ManageDepartmentActionButton(ManageActionButton):
412    """ 'Manage settings' button for departments.
413    """
414    grok.context(IDepartment)
415    grok.view(DepartmentPage)
416    text = 'Manage department'
417
418class ShowDepartmentStudentsActionButton(ManageActionButton):
419    """ 'Show students' button for departments.
420    """
421    grok.context(IDepartment)
422    grok.view(DepartmentPage)
423    grok.require('waeup.showStudents')
424    icon = 'actionicon_student.png'
425    text = 'Show students'
426    target = 'showstudents'
427
428class ManageCourseActionButton(ManageActionButton):
429    """ 'Edit settings' button for courses.
430    """
431    grok.context(ICourse)
432    grok.view(CoursePage)
433    text = 'Edit course'
434   
435class ManageCertificateActionButton(ManageActionButton):
436    """ 'Manage settings' button for certificates.
437    """
438    grok.context(ICertificate)
439    grok.view(CertificatePage)
440    text = 'Manage certificate'
441
442class ManageCertificateCourseActionButton(ManageActionButton):
443    """ 'Manage settings' button for certificate courses.
444    """
445    grok.context(ICertificateCourse)
446    grok.view(CertificateCoursePage)
447    text = 'Edit course referrer'
448
449#
450# Add actions...
451#
452
453class AddUserActionButton(AddActionButton):
454    grok.require('waeup.manageUsers')
455    grok.context(IUsersContainer)
456    grok.view(UsersContainerPage)
457    text = 'Add user'
458
459#class AddFacultyActionButton(AddActionButton):
460#    grok.context(IFacultyContainer)
461#    grok.view(FacultyContainerPage)
462#    text = 'Add faculty'
463   
464#class AddDepartmentActionButton(AddActionButton):
465#    grok.context(IFaculty)
466#    grok.view(FacultyPage)
467#    text = 'Add department'
468
469#class AddCertificateActionButton(AddActionButton):
470#    grok.context(IDepartment)
471#    grok.view(DepartmentPage)
472#    grok.order(3)
473#    text = 'Add certificate'
474#    target = 'addcertificate'
475   
476#class AddCourseActionButton(AddActionButton):
477#    grok.context(IDepartment)
478#    grok.view(DepartmentPage)
479#    grok.order(4)
480#    text = 'Add course'
481#    target = 'addcourse'
482
483#class AddCertificateCourseActionButton(AddActionButton):
484#    grok.context(ICertificate)
485#    grok.view(CertificatePage)
486#    grok.order(4)
487#    text = 'Add course referrer'
488#    target = 'addcertificatecourse'
489
490#
491# Remove actions...
492#
493
494
495#class RemoveFacultyActionButton(RemoveActionButton):
496#    grok.context(IFacultyContainer)
497#    grok.view(FacultyContainerPage)
498#    text = 'Remove faculty'
499   
500#class RemoveDepartmentActionButton(RemoveActionButton):
501#    grok.context(IFaculty)
502#    grok.view(FacultyPage)
503#    text = 'Remove department'
504   
505
506#
507# Actions with a 'browse' icon...
508#
509class BrowseActionButton(ActionButton):
510    grok.baseclass()
511    grok.context(IWAeUPObject)
512    grok.template('actionbutton')
513    grok.viewletmanager(ActionBar)
514    grok.require('waeup.manageUniversity')
515    icon = 'actionicon_manage.png' # File must exist in static/
516    target = '@@show' # link to this viewname.
517    text = 'Show batch logs' # Text to display on the button
518
519class BrowseDatacenterLogs(BrowseActionButton):
520    grok.context(IDataCenter)
521    grok.view(DatacenterPage)
522    grok.order(4)
523    icon = 'documentinfo_templet.png'
524    target = '@@logs'
525    text = 'Show batch logs'
526
527#
528# Misc. buttons...
529#
530class BatchOpButton(ActionButton):
531    grok.context(IDataCenter)
532    grok.view(DatacenterPage)
533    grok.require('waeup.manageUniversity')
534    grok.order(6)
535    icon = 'actionbox_templet.png'
536    target = '@@import1'
537    text = 'Batch processing'
538
539class UploadCSVButton(ActionButton):
540    grok.context(IDataCenter)
541    grok.view(DatacenterPage)
542    grok.require('waeup.manageUniversity')
543    grok.order(5)
544    icon = 'go-up-16x16.png'
545    target = '@@upload'
546    text = 'Upload CSV file'
547
548#
549# Primary navigation tabs (in upper left navigation bar)...
550#
551class PrimaryNavManager(grok.ViewletManager):
552    """Viewlet manager for the primary navigation tab.
553    """
554    grok.name('primary_nav')
555
556class PrimaryNavTab(grok.Viewlet):
557    """Base for primary nav tabs.
558    """
559    grok.baseclass()
560    grok.viewletmanager(PrimaryNavManager)
561    grok.order(1)
562    grok.require('waeup.Public')
563
564    pnav = 0 # This is a kind of id of a tab. If some page provides
565             # also a 'pnav' attribute with the same value (here: 0),
566             # then the tab will be rendered as 'active' when the page
567             # gets rendered.
568             #
569             # This way you can assign certain pages to certain
570             # primary nav tabs. Each primary tab should therefore set
571             # the 'pnav' attribute to a different value (or several
572             # tabs might be rendered as active simultanously when the
573             # page gets rendered.
574    tab_title = u'Some Text'
575   
576    @property
577    def link_target(self):
578        return self.view.application_url()
579
580    @property
581    def active(self):
582        view_pnav = getattr(self.view, 'pnav', 0)
583        if view_pnav == self.pnav:
584            return 'active'
585        return ''
586
587class HomeTab(PrimaryNavTab):
588    """Home-tab in primary navigation.
589    """
590    grok.order(1)
591    grok.require('waeup.Public')
592    grok.template('primarynavtab')
593
594    pnav = 0
595    tab_title = u'Home'
596
597class FacultiesTab(PrimaryNavTab):
598    """Faculties-tab in primary navigation.
599    """
600    grok.order(2)
601    grok.require('waeup.viewAcademics')
602    grok.template('primarynavtab')
603
604    pnav = 1
605    tab_title = u'Academics'
606
607    @property
608    def link_target(self):
609        return self.view.application_url('faculties')
610
611   
612class ContactTab(PrimaryNavTab):
613    """Contact tab in primary navigation.
614
615    Display tab only for anonymous. Authenticated users can call the
616    form from the user navigation bar.
617    """
618    grok.order(6)
619    grok.require('waeup.Anonymous')
620    grok.template('primarynavtab')
621    tab_title = u'Enquiries'
622    pnav = 2
623
624    # Also zope.manager has role Anonymous.
625    # To avoid displaying this tab, uncomment the following.
626    #def tab_title(self):
627    #    userid = self.request.principal.id
628    #    if userid != 'zope.anybody':
629    #        tt = u''
630    #    else:
631    #        tt = u'Enquiries'
632    #    return tt
633
634    #@property
635    #def active(self):
636    #    view_pnav = getattr(self.view, 'pnav', 0)
637    #    userid = self.request.principal.id
638    #    if view_pnav == self.pnav and userid == 'zope.anybody':
639    #        return 'active'
640    #    return ''
641
642    @property
643    def link_target(self):
644        return self.view.application_url('contactadmin')       
645
646
647
648#
649# Administration tasks
650#
651class AdminTask(grok.Viewlet):
652    """The base for task entries on administration page.
653    """
654    grok.baseclass()
655    grok.order(1)
656    grok.viewletmanager(AdministrationTasks)
657    grok.require('waeup.manageUniversity')
658    grok.template('admintask')
659
660    link_title = 'Manage users' # How the link to the target will be titled.
661    target_viewname = 'users'   # The name of the target view.
662   
663    @property
664    def link_target(self):
665        return self.view.url(self.context[self.target_viewname])
666
667class AdminTaskPortalConfiguration(AdminTask):
668    """Entry on administration page that link to portal settings.
669    """
670    grok.order(1)
671    grok.require('waeup.managePortalConfiguration')
672
673    link_title = 'Portal Configuration'
674    def link_target(self):
675        return self.view.url(self.view.context, 'configuration')
676
677class AdminTaskUsers(AdminTask):
678    """Entry on administration page that link to user folder.
679    """
680    grok.order(2)
681    grok.require('waeup.manageUsers')
682
683    link_title = 'Portal Users'
684    target_viewname = 'users'
685   
686class AdminTaskDatacenter(AdminTask):
687    """Entry on administration page that link to datacenter.
688    """
689    grok.order(3)
690    grok.require('waeup.manageDataCenter')
691
692    link_title = 'Data Center'
693    target_viewname = 'datacenter'
694
695# The SubobjectLister and its viewlets below are not used in SIRP.
696
697class SubobjectLister(grok.ViewletManager):
698    """Very special viewlet manager that displays lists of subobjects.
699    """
700    grok.name('subobjectlist')
701    grok.template('subobjectlist')
702
703    def update(self):
704        # The default implementation of update() sets self.viewlets to
705        # a list of viewlets for the current context
706        # (self.context). We make use of that fact by retrieving all
707        # viewlets for all items in our context container by simply
708        # setting these items as context while we call the default
709        # update() method. So we get a list of lists of viewlets for
710        # each item in a 'row' (where a single item is a row).
711        rows = []
712        orig_context = self.context
713        for name, value in self.context.items():
714            # Retrieve all viewlets for the current item (not the context)
715            self.context = value
716            super(SubobjectLister, self).update() # sets self.viewlets
717            rows.append(self.viewlets)
718            self.context = orig_context
719        self.rows = rows
720        # Finally, set the viewlets we would retrieve normally...
721        super(SubobjectLister, self).update()
722        return
723
724
725class FacultyContainerListHead(grok.Viewlet):
726    """The header line of faculty container subobject lists.
727    """
728    grok.order(1)
729    grok.viewletmanager(SubobjectLister)
730    grok.context(IFacultyContainer)
731    grok.require('waeup.viewAcademics')
732
733    def render(self):
734        return u'<th>Code</th><th>Title</th><th></th>'
735
736class FacultyListName(grok.Viewlet):
737    """Display a the title of a faculty as link in a list.
738    """
739    grok.order(1)
740    grok.viewletmanager(SubobjectLister)
741    grok.context(IFaculty)
742    grok.require('waeup.viewAcademics')
743
744    def render(self):
745        return u'<a href="%s">%s</a>' % (
746            self.view.url(self.context), self.context.__name__)
747
748class FacultyListTitle(grok.Viewlet):
749    """Display the title of a faculty in a list.
750    """
751    grok.order(2)
752    grok.viewletmanager(SubobjectLister)
753    grok.context(IFaculty)
754    grok.require('waeup.viewAcademics')
755
756    def render(self):
757        return self.context.title
758
759class FacultyRemoveButton(grok.Viewlet):
760    """Render a remove button for faculty lists.
761    """
762    grok.order(3)
763    grok.viewletmanager(SubobjectLister)
764    grok.context(IFaculty)
765    grok.require('waeup.manageUniversity')
766
767    def render(self):
768        return u'<div class="text-right"><form method="POST"><input class="text-right" type="submit" value="remove" /></form></div>'
Note: See TracBrowser for help on using the repository browser.