Exemplo n.º 1
0
class RDEFPermTab(SecurityViewMixIn, EntityView):
    __regid__ = 'rdef-permissions'
    __select__ = is_instance('CWRelation',
                             'CWAttribute') & authenticated_user()

    def cell_call(self, row, col):
        self.permissions_table(self.cw_rset.get_entity(row, col).yams_schema())
Exemplo n.º 2
0
class TimeLeft(HeaderComponent):
    """ Build a time left before session expiration display in the header.
    """
    __regid__ = "time-left"
    __select__ = authenticated_user()
    context = u"header-right"
    order = 3

    def render(self, w):

        # Get the expiration delay
        expiration_delay = self._cw.vreg.config.get(
            "apache-cleanup-session-time")
        if expiration_delay is None:
            expiration_delay = self._cw.vreg.config.get("cleanup-session-time")
        if expiration_delay is None:
            return

        # Define the expiration delay div
        w(u'<script>var cookie_name = "{0}clock";</script>'.format(
            self._cw.session.sessionid))
        w(u'<script>var expiration_time = {0};</script>'.format(
            expiration_delay * 1000))
        w(u'Auto logout in: <b id="timeh"></b>:<b id="timem"></b>:'
          '<b id="times"></b>')
Exemplo n.º 3
0
class CWRTypePermTab(SecurityViewMixIn, EntityView):
    __regid__ = 'cwrtype-permissions'
    __select__ = is_instance('CWRType') & authenticated_user()

    def cell_call(self, row, col):
        entity = self.cw_rset.get_entity(row, col)
        rschema = self._cw.vreg.schema.rschema(entity.name)
        self.grouped_permissions_table(rschema)
Exemplo n.º 4
0
class SecurityManagementView(SecurityViewMixIn, EntityView):
    """display security information for a given entity"""
    __regid__ = 'security'
    __select__ = EntityView.__select__ & authenticated_user()

    title = _('security')

    def call(self):
        self.w(u'<div id="progress">%s</div>' % self._cw._('validating...'))
        super(SecurityManagementView, self).call()

    def entity_call(self, entity):
        self._cw.add_js('cubicweb.edition.js')
        self._cw.add_css('cubicweb.acl.css')
        w = self.w
        _ = self._cw._
        w(u'<h1><span class="etype">%s</span> <a href="%s">%s</a></h1>' %
          (entity.dc_type().capitalize(), xml_escape(
              entity.absolute_url()), xml_escape(entity.dc_title())))
        # first show permissions defined by the schema
        self.w('<h2>%s</h2>' % _('Schema\'s permissions definitions'))
        self.permissions_table(entity.e_schema)
        self.w('<h2>%s</h2>' % _('Manage security'))
        # ownership information
        if self._cw.vreg.schema.rschema('owned_by').has_perm(
                self._cw, 'add', fromeid=entity.eid):
            self.owned_by_edit_form(entity)
        else:
            self.owned_by_information(entity)

    def owned_by_edit_form(self, entity):
        self.w('<h3>%s</h3>' % self._cw._('Ownership'))
        msg = self._cw._('ownerships have been changed')
        form = self._cw.vreg['forms'].select(
            'base',
            self._cw,
            entity=entity,
            form_renderer_id='onerowtable',
            submitmsg=msg,
            form_buttons=[wdgs.SubmitButton()],
            domid='ownership%s' % entity.eid,
            __redirectvid='security',
            __redirectpath=entity.rest_path())
        field = guess_field(entity.e_schema,
                            self._cw.vreg.schema['owned_by'],
                            req=self._cw)
        form.append_field(field)
        form.render(w=self.w, display_progress_div=False)

    def owned_by_information(self, entity):
        ownersrset = entity.related('owned_by')
        if ownersrset:
            self.w('<h3>%s</h3>' % self._cw._('Ownership'))
            self.w(u'<div class="ownerInfo">')
            self.w(self._cw._('this entity is currently owned by') + ' ')
            self.wview('csv', entity.related('owned_by'), 'null')
            self.w(u'</div>')
Exemplo n.º 5
0
class LogOutButton(AuthenticatedUserStatus):
    """ Close the current session.
    """
    __regid__ = "logout"
    __select__ = authenticated_user()
    order = 6

    def attributes(self):
        return (self._cw.build_url("logout"), "Sign-out", "fa-sign-out")
Exemplo n.º 6
0
class LogoutAction(action.Action):
    __regid__ = 'logout'
    __select__ = authenticated_user()

    title = _('logout')
    category = 'useractions'
    order = 30

    def url(self):
        return self._cw.build_url(self.__regid__)
Exemplo n.º 7
0
class HomeButton(HeaderComponent):
    """ Build a home button displayed in the header.
    """
    __regid__ = "home-snapview"
    __select__ = authenticated_user()
    order = 0
    context = u"header-right"

    def attributes(self):
        return self._cw.build_url("view", vid="index"), "Home", "fa-home"
Exemplo n.º 8
0
class UserPreferencesAction(action.Action):
    __regid__ = 'myprefs'
    __select__ = authenticated_user()

    title = _('user preferences')
    category = 'useractions'
    order = 10

    def url(self):
        return self._cw.build_url(self.__regid__)
Exemplo n.º 9
0
class UserInfoAction(action.Action):
    __regid__ = 'myinfos'
    __select__ = authenticated_user()

    title = _('profile')
    category = 'useractions'
    order = 20

    def url(self):
        return self._cw.build_url('cwuser/%s' % self._cw.user.login,
                                  vid='edition')
Exemplo n.º 10
0
class AdminGroupButton(HeaderComponent):
    """ Build a create group button displayed in the header.
    Only the managers have accessed to this functionality.
    """
    __regid__ = "admin-status"
    __select__ = match_user_groups("managers") & authenticated_user()
    context = "header-right"
    order = 4

    def attributes(self):
        return (self._cw.build_url("view", vid="shiirdor.groups-management"),
                "Create groups", "fa-plus-square")
Exemplo n.º 11
0
class AdminImportButton(HeaderComponent):
    """ Build an importation button displayed in the header.
    Only the managers have accessed to this functionality.
    """
    __regid__ = "admin-import"
    __select__ = match_user_groups("managers") & authenticated_user()
    context = "header-right"
    order = 5

    def attributes(self):
        return (self._cw.build_url("view", vid="shiirdor.users-groups-import"),
                "Import users & groups", "fa-cloud-download")
Exemplo n.º 12
0
class ManagerSyncButton(HeaderComponent):
    """ Build a synchronisation button displayed in the header.
    Only administrators and moderators will see this button.
    """
    __regid__ = "manager-sync"
    __select__ = match_user_groups("managers",
                                   "moderators") & authenticated_user()
    context = "header-right"
    order = 3

    def attributes(self):
        return (self._cw.build_url("view", vid="schiirdor.sync-management"),
                "Sync", "fa-exchange")
Exemplo n.º 13
0
class ManagerManageButton(HeaderComponent):
    """ Build a manage button displayed in the header.
    Only administrators and moderators will see this button.
    """
    __regid__ = "manager-manage"
    __select__ = match_user_groups("managers",
                                   "moderators") & authenticated_user()
    context = "header-right"
    order = 2

    def attributes(self):
        return (self._cw.build_url("view", vid="schiirdor.users-management"),
                "Users & groups", "fa-users")
Exemplo n.º 14
0
class ExpenseLineFilesComponent(component.EntityCtxComponent):
    """display the list of files attached to the expenselines"""
    __regid__ = 'expenseline.attachments'
    __select__ = (component.EntityCtxComponent.__select__
                  & authenticated_user() & is_instance('Expense')
                  & rql_condition('EXISTS(X has_lines Y, Y has_attachment F) '
                                  'OR EXISTS(X has_attachment F)'))
    title = _('has_attachment')

    def render_body(self, w):
        rset = self._cw.execute(
            '(Any F WHERE X has_attachment F, X eid %(x)s)'
            ' UNION '
            '(Any F WHERE X has_lines Y, Y has_attachment F, X eid %(x)s)',
            dict(x=self.entity.eid))
        self._cw.view('incontext', rset, 'null', w=w)
Exemplo n.º 15
0
class History(NullView):
    """ Create a view to display the history.
    """
    __regid__ = "piws-history"
    __select__ = authenticated_user()
    templatable = False
    div_id = "piws-history"
    default_message = "Hostory has not been provided yet."

    def __init__(self, *args, **kwargs):
        """ Initialize the History class.
        """
        super(History, self).__init__(*args, **kwargs)

    def call(self, **kwargs):
        """ Create the history page.
        """

        # Get additional resources links
        resources = {
            "euaims-image": self._cw.data_url("startup/images/euaims.png"),
            "pi-image": self._cw.data_url("startup/images/pi.png"),
            "imagen-image": self._cw.data_url("startup/images/imagen.png"),
            "fmri-image": self._cw.data_url("startup/images/fmri.png"),
            "piws-image": self._cw.data_url("startup/images/piws-small.png"),
            "nsap-image": self._cw.data_url("startup/images/nsap.png")
        }

        # Get local html startup
        startup_html = os.path.join(os.path.dirname(__file__), "startup.html")
        if os.path.isfile(startup_html):
            links = (
                u'<link rel="stylesheet" type="text/css" href="{0}"/>'.format(
                    self._cw.data_url("startup/css/style.css")))
            for path in ("startup/js/jquery.js",
                         "startup/js/jquery-migrate.js", "startup/js/mezr.js",
                         "startup/js/public.js"):
                links += (u'<script type="text/javascript" src="{0}"></script>'
                          .format(self._cw.data_url(path)))
            with open(startup_html, "rt") as open_file:
                html = open_file.readlines()
            html = "\n".join(html[:8] + [links] + html[8:])
            for key, value in resources.items():
                html = html.replace("%({0})s".format(key), value)
            self.w(unicode(html))
        else:
            self.w(unicode(self.default_message))
Exemplo n.º 16
0
class AuthenticatedUserStatus(AnonUserStatusLink):
    __select__ = authenticated_user()

    def render(self, w):
        # display useractions and siteactions
        self._cw.add_css('cubicweb.pictograms.css')
        actions = self._cw.vreg['actions'].possible_actions(self._cw, rset=self.cw_rset,
                                                            view=self.cw_extra_kwargs['view'])
        box = MenuWidget('', 'userActionsBox', _class='', islist=False)
        menu = PopupBoxMenu(self._cw.user.login, isitem=False, link_class='icon-user')
        box.append(menu)
        for action in actions.get('useractions', ()):
            menu.append(self.action_link(action))
        if actions.get('useractions') and actions.get('siteactions'):
            menu.append(self.separator())
        for action in actions.get('siteactions', ()):
            menu.append(self.action_link(action))
        box.render(w=w)
Exemplo n.º 17
0
class SCHIIRDORModeratorIndexView(IndexView):
    """ Class that defines the index view.
    """
    __regid__ = "index"
    __select__ = authenticated_user() & match_user_groups(
        "managers", "moderators")
    title = _("Index")

    def call(self, **kwargs):
        """ Create the loggedin 'index' page of our site.
        """
        # Format template
        template = self._cw.vreg.template_env.get_template(
            "startup.logged.jinja2")
        html = template.render(
            header_url=self._cw.data_url("creative/img/neurospin.jpg"),
            moderator=True)
        self.w(html)
Exemplo n.º 18
0
class CWETypePermTab(SecurityViewMixIn, EntityView):
    __regid__ = 'cwetype-permissions'
    __select__ = is_instance('CWEType') & authenticated_user()

    def cell_call(self, row, col):
        entity = self.cw_rset.get_entity(row, col)
        eschema = self._cw.vreg.schema.eschema(entity.name)
        self.w(u'<h4>%s</h4>' % self._cw._('This entity type permissions:'))
        self.permissions_table(eschema)
        self.w(u'<div style="margin: 0px 1.5em">')
        self.w(u'<h4>%s</h4>' % self._cw._('Attributes permissions:'))
        for attr, etype in eschema.attribute_definitions():
            if attr not in META_RTYPES:
                rdef = eschema.rdef(attr)
                attrtype = str(rdef.rtype)
                self.w(u'<h4 class="schema">%s (%s)</h4> ' %
                       (attrtype, self._cw._(attrtype)))
                self.permissions_table(rdef)
        self.w(u'</div>')
Exemplo n.º 19
0
class UndoController(Controller):
    __regid__ = 'undo'
    __select__ = authenticated_user() & match_form_params('txuuid')

    def publish(self, rset=None):
        txuuid = self._cw.form['txuuid']
        try:
            self._cw.cnx.undo_transaction(txuuid)
        except UndoTransactionException as exc:
            errors = exc.errors
            #This will cause a rollback in main_publish
            raise ValidationError(None, {None: '\n'.join(errors)})
        else:
            self.redirect()  # Will raise Redirect

    def redirect(self, msg=None):
        req = self._cw
        msg = msg or req._("transaction undone")
        self._redirect({'_cwmsgid': req.set_redirect_message(msg)})
Exemplo n.º 20
0
class SCHIIRDORIndexView(IndexView):
    """ Class that defines the index view.
    """
    __regid__ = "index"
    __select__ = ~authenticated_user()
    title = _("Index")
    templatable = False

    def call(self, **kwargs):
        """ Create the anonymous 'index' page of our site.
        """
        # Get additional resources links
        css = []
        for path in ("creative/vendor/bootstrap/css/bootstrap.min.css",
                     "creative/vendor/font-awesome/css/font-awesome.min.css",
                     "creative/vendor/magnific-popup/magnific-popup.css",
                     "creative/css/creative.css"):
            css.append(self._cw.data_url(path))
        js = []
        for path in (
                "creative/vendor/jquery/jquery.min.js",
                "creative/vendor/bootstrap/js/bootstrap.min.js",
                "creative/vendor/scrollreveal/scrollreveal.min.js",
                "creative/vendor/magnific-popup/jquery.magnific-popup.min.js",
                "creative/js/creative.js"):
            js.append(self._cw.data_url(path))

        # Format template
        template = self._cw.vreg.template_env.get_template("startup.jinja2")
        html = template.render(
            header_url=self._cw.data_url("creative/img/neurospin.jpg"),
            login_url=self._cw.build_url(
                "login", __message=u"Please login with your account."),
            contact_email=self._cw.vreg.config.get("administrator-emails",
                                                   "*****@*****.**"),
            css_url=css,
            js_url=js)
        self.w(html)
Exemplo n.º 21
0
class MetaGenSearchAutoView(View):
    """ Create a view to filter the PLINK genomic data from a metagen
    reference.
    """
    __regid__ = "metagen-search-auto"
    __select__ = authenticated_user()
    title = _("MetaGen Search")
    paginable = False
    div_id = "metagen-search-auto"

    def call(self, measure=None, **kwargs):
        """ Method that will create a filtered genomic measures table.

        If no resultset are passed to this method, the current resultset is
        used.

        Parameters
        ----------
        rset: resultset (optional, default None)
            a  cw resultset
        patient_id: string (optional, default None)
            the patient identifier.
        """
        # Get the 'measure' parameter: if we use 'build_url' method, the data
        # are in the form dictionary
        measure = measure or self._cw.form.get("measure", None)

        # Add css resources
        self._cw.add_css(
            "DataTables-1.10.10/media/css/jquery.dataTables.min.css")
        self._cw.add_css("DataTables-1.10.10/extensions/FixedColumns/css/"
                         "fixedColumns.dataTables.min.css")
        self._cw.add_css("DataTables-1.10.10/extensions/Scroller/css/"
                         "scroller.dataTables.min.css")
        self._cw.add_css(
            ("https://code.jquery.com/ui/1.11.4/themes/smoothness/"
             "jquery-ui.css"),
            localfile=False)

        # Add js resources
        self._cw.add_js("DataTables-1.10.10/media/js/jquery.dataTables.min.js")
        self._cw.add_js("DataTables-1.10.10/extensions/FixedColumns/js/"
                        "dataTables.fixedColumns.js")
        self._cw.add_js("DataTables-1.10.10/extensions/fnSetFilteringDelay.js")

        # Create a gene picker
        genes = metagen_get_genes(
            metagen_url=self._cw.vreg.config["metagen_url"])
        genes_struct = {}
        for gene in genes:
            genes_struct.setdefault(gene.chromosome, []).append(gene.hgnc_name)
        html = "<h1>PLINK Genomic Measures Search</h1>"
        html += "<hr>"
        html += ("<h2>Please select a gene of interest:</h2>")
        html += "<select class='selectpicker' data-live-search='true'>"
        html += "<option></option>"
        for chromosome in sorted(genes_struct.keys()):
            html += "<optgroup label='{0}' data-icon='glyphicon-heart'>".format(
                chromosome)
            for name in sorted(genes_struct[chromosome]):
                html += "<option value='{0}'>{0}</option>".format(name)
            html += "</optgroup>"
        html += "</select>"

        # Ceate a div to display the plots
        html += "<div id='metagen-search-disp'></div>"

        # Add an event when the selection change
        search_url = self._cw.build_url(vid="metagen-search", measure=measure)
        html += "<script type='text/javascript'>"
        html += "$(function() {"
        html += "$('.selectpicker').on('change', function(){"
        html += "var selected = $(this).find('option:selected').val();"
        html += "if (selected != ''){"

        # > execute the ajax callback
        html += "var request = $.ajax({"
        html += "url: 'ajax?fname=get_metagen_search_body',"
        html += "method: 'POST',"
        html += "data: {{'measure': '{0}', 'gene': selected}},".format(measure)
        html += "dataType: 'html'"
        html += "}).done(function(html){$('#metagen-search-disp').html(html)});"

        html += "}"
        html += "});"
        html += "});"
        html += "</script>"

        # Display the page content
        self.w(unicode(html))
Exemplo n.º 22
0
class ImageViewer(View):
    """ Create an image viewer.
    """
    __regid__ = "brainbrowser-image-viewer"
    __select__ = authenticated_user()
    title = _("Brainbrowser")
    paginable = False
    div_id = "brainbrowser-simple"

    def __init__(self, *args, **kwargs):
        """ Initialize the ImageViewer class.

        If you want to construct the viewer manually in your view pass the
        parent view in the 'parent_view' attribute.
        """
        super(ImageViewer, self).__init__(*args, **kwargs)
        if "parent_view" in kwargs:
            self._cw = kwargs["parent_view"]._cw
            self.w = kwargs["parent_view"].w

    def call(self, imagefiles=None, **kwargs):
        """ Method that will create a simple BrainBrowser image viewer.

        Parameters
        ----------
        imagefiles: list of str (mandatory)
            the path to the paths that will be rendered.
        """
        # Get the parameters
        imagefiles = imagefiles or self._cw.form.get("imagefiles", "")

        # Guarantee that we have a list of paths
        if not isinstance(imagefiles, list):
            imagefiles = [imagefiles]

        # Get the path to the in progress resource
        wait_image_url = self._cw.data_url("images/please_wait.gif")

        # Add css resources
        self._cw.add_css("brainbrowser-2.3.0/css/ui-darkness/"
                         "jquery-ui-1.8.10.custom.css")
        self._cw.add_css("brainbrowser-2.3.0/css/common.css")
        self._cw.add_css("volume-viewer.css")

        # Add js resources
        self._cw.add_js("brainbrowser-2.3.0/src/jquery-1.6.4.min.js")
        self._cw.add_js("brainbrowser-2.3.0/src/"
                        "jquery-ui-1.8.10.custom.min.js")
        self._cw.add_js("brainbrowser-2.3.0/src/ui.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "brainbrowser.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/core/"
                        "tree-store.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/lib/" "config.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/lib/" "utils.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/lib/" "events.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/lib/" "loader.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/lib/"
                        "color-map.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer/lib/display.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer/lib/panel.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer/lib/utils.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer/modules/loading.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer/modules/rendering.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer/volume-loaders/overlay.js")
        self._cw.add_js("brainbrowser-2.3.0/src/brainbrowser/"
                        "volume-viewer/volume-loaders/minc.js")
        self._cw.add_js("image_viewer.js")

        # Set the brainbrowser viewer navigation tools
        html = self.build_brainbrowser_tools()

        # Create a div for the in progress resource
        html += ("<div id='loading' style='display:none' align='center'>"
                 "<img src='{0}'/></div>".format(wait_image_url))

        # Build a brainborwser banner with tools
        html += self.build_brainbrowser_banner(imagefiles)

        # Set brainbrowser colormaps
        html += self.build_color_maps()

        # Define global javascript variables
        html += "<script type='text/javascript'>"
        html += "var quality = 50;"
        html += "var ajaxcallback = 'get_brainbrowser_image';"
        html += "</script>"

        # Set brainbrowser nifti image loader
        html += self.build_nifti_image_loader()

        # Set a callback to select the qulity of the rendering
        html += self.build_quality_callback(imagefiles)

        # Set a callback to select an image to render
        html += self.build_image_callback(imagefiles)

        # Set a callback to select the rendering
        html += self.build_rendering_callback(imagefiles)

        # Set the cw brainbrowser image loader
        html += self.build_cw_loader(imagefiles, 0, True)

        html += '<strong>BrainBrowser&#169; visualisation tool :</strong>'
        html += '<dl class="dl-horizontal">'
        html += '<dt>Lead Developer</dt><dd>Tarek Sherif</dd>'
        html += '<dt>Full credits</dt><dd><a href="https://brainbrowser.cbrain.mcgill.ca/">BrainBrowser Website</a></dd>'
        html += '</dl>'

        # Creat the corresponding html page
        self.w(unicode(html))

    def build_nifti_image_loader(self):
        """ Define a nifti image loader for BrainBrowser based on a CubicWeb
        ajax callback.

        Returns
        -------
        html: str
            the nifti loader definition.
        """
        # Add javascript
        html = "<script type='text/javascript'>"

        # And create the appropriate viewer
        html += "var VolumeViewer = BrainBrowser.VolumeViewer;"
        html += ("VolumeViewer.volume_loaders.nifti = "
                 "function(description, callback) {")

        # Display wait message
        html += "$('#loading').show();"

        # Execute the ajax callback
        html += "var postData = {};"
        html += "postData.imagefile = description.data_file;"
        html += "postData.dquality = quality;"
        html += "var post = $.ajax({"
        html += "url: '{0}ajax?fname=' + ajaxcallback,".format(
            self._cw.base_url())
        html += "type: 'POST',"
        html += "data: postData"
        html += "});"

        # The ajax callback is done, get the result set
        html += "post.done(function(p){"
        html += "$('#loading').hide();"
        html += "var data = p.data;"
        html += "var header_text = p.header;"

        # Decode and display data: expect uint8 buffer
        html += "if (ajaxcallback == 'get_encoded_brainbrowser_image') {"

        # Create the flatten image array data
        html += "var slice_cnt = 0;"
        html += "var the_array = [];"
        html += "data.forEach(function(slicedata, slice_cnt) {"

        # Decode the image in a function where a callback is called and
        # and executed when the buffer is properly filled
        html += "var effect = function(slicedata, length, callback) {"
        html += "var img = new Image();"
        html += "img.onload = function() {"
        html += "var canvas = document.createElement('canvas');"
        html += "canvas.width  = img.width;"
        html += "canvas.height = img.height;"
        html += "var ctx = canvas.getContext('2d');"
        html += "ctx.drawImage(img, 0, 0);"
        html += "var imageData = ctx.getImageData(0,0,canvas.width, canvas.height);"
        html += "var slicedata = imageData.data;"

        # Fill the buffer: use scale factor to match uint16 dynamic
        html += "for (var j = 0; j < slicedata.length; j += 4) {"
        html += "the_array.push(slicedata[j] * 257);"
        html += "}"

        # Call the callback that will render the image when the buffer is
        # completed
        html += "callback(the_array, (slice_cnt + 1) == length);"

        # Close onload
        html += "};"

        # Set the current slice base64 source
        html += "img.src = 'data:image/jpg;base64,' + slicedata;"

        # Close effect
        html += "};"

        # Load the image and define the callback
        html += "effect(slicedata, data.length, function(data, is_loaded) {"

        # Test if all the slice have been loaded in the buffer
        html += "if (is_loaded) {"

        # Load the image in BrainBrowsers
        html += "var error_message;"
        html += "if (description.data_file) {"
        html += "BrainBrowser.parseHeader(header_text, function(header) {"
        html += "BrainBrowser.createMincVolume(header, data, callback);"
        html += "});"
        html += "error_message = header.xspace.name;"
        html += ("BrainBrowser.events.triggerEvent('error', "
                 "{ message: error_message });")
        html += "throw new Error(error_message);"
        html += "} else {"
        html += "error_message = 'Error';"
        html += ("BrainBrowser.events.triggerEvent('error', "
                 "{ message: error_message });")
        html += "throw new Error(error_message);"
        html += "}"

        # Close callback
        html += "};"
        html += "});"

        # Close foreach
        html += "});"

        # Close if encoded
        html += "}"

        # Display data: expect uint16 buffer
        html += "else {"

        # Load the image in BrainBrowsers
        html += "var error_message;"
        html += "if (description.data_file) {"
        html += "BrainBrowser.parseHeader(header_text, function(header) {"
        html += "BrainBrowser.createMincVolume(header, data, callback);"
        html += "});"
        html += "error_message = header.xspace.name;"
        html += ("BrainBrowser.events.triggerEvent('error', "
                 "{ message: error_message });")
        html += "throw new Error(error_message);"
        html += "} else {"
        html += "error_message = 'Error';"
        html += ("BrainBrowser.events.triggerEvent('error', "
                 "{ message: error_message });")
        html += "throw new Error(error_message);"
        html += "}"

        # Close else
        html += "}"

        # Close post
        html += "});"

        # Error when the loading failed
        html += "post.fail(function(){"
        html += "$('#loading').hide();"
        html += " alert('Error : Image buffering failed!');"
        html += "});"

        html += "};"

        # Close javascript
        html += "</script>"

        return html

    def build_cw_loader(self,
                        imagefiles,
                        index=0,
                        decorated=False,
                        multiview=False):
        """ Define the script that will load the image in BrainBrowser.

        Parameters
        ----------
        imagefiles: list of str (mandatory)
            the path to the paths that will be rendered.
        index: int (optional, default 0)
            the index of the image to render.
        decorated: bool (optional, default False)
            add the javascript tag.
        multiview: bool (optional, default False)
            display all the images at the same time.

        Returns
        -------
        html: str
            the loader definition.
        """
        # Add javascript
        html = ""
        if decorated:
            html += "<script type='text/javascript'>"
        html += "$(document).ready(function() {"

        # Load the volume
        html += "var viewer = window.viewer;"
        if multiview:
            html += "viewer.loadVolumes({"
            html += "volumes: ["
            for path in imagefiles:
                html += "{"
                html += "type: 'nifti',"
                html += "data_file: '{0}',".format(path)
                html += "template: {"
                html += "element_id: 'volume-ui-template',"
                html += "viewer_insert_class: 'volume-viewer-display'"
                html += "}"
                html += "},"
            html += "],"
            html += "complete: function() {"
            html += "$('#volume-type').hide();"
            html += "$('.slice-display').css('display', 'inline');"
            html += "$('.volume-controls').css('width', 'auto');"
            html += "$('#loading').hide();"
            html += "$('#brainbrowser-wrapper').slideDown({duration: 600});"
            html += "}"
            html += "});"
        else:
            html += "viewer.loadVolume({"
            html += "type: 'nifti',"
            html += "data_file: '{0}',".format(imagefiles[index])
            html += "template: {"
            html += "element_id: 'volume-ui-template',"
            html += "viewer_insert_class: 'volume-viewer-display'"
            html += "}"
            html += "}, function() {"
            html += "$('#volume-type').show();"
            html += "$('.slice-display').css('display', 'inline');"
            html += "$('.volume-controls').css('width', 'auto');"
            html += "$('#loading').hide();"
            html += "$('#brainbrowser-wrapper').slideDown({duration: 600});"
            html += "});"

        # Close function
        html += "});"

        # Close javascript
        if decorated:
            html += "</script>"

        return html

    def build_quality_callback(self, imagefiles):
        """ Define the rendering quality callback.

        Parameters
        ----------
        imagefiles: list of str (mandatory)
            the path to the images that will be rendered.

        Returns
        -------
        html: str
            the quality definition.
        """
        # Add javascript
        html = "<script type='text/javascript'>"

        # Define the callback
        html += "$('#volume-quality').change(function() {"

        # Raw data are requested
        html += "if ($('#volume-quality').val() === 'RAW') {"
        html += "ajaxcallback = 'get_brainbrowser_image';"
        html += "}"

        # Low quality encoded data are requested
        html += "else if ($('#volume-quality').val() === 'LOW JPEG') {"
        html += "ajaxcallback = 'get_encoded_brainbrowser_image';"
        html += "quality = 50;"
        html += "}"

        # Add out of range event
        html += "else {"
        html += "ajaxcallback = 'get_brainbrowser_image';"
        html += "}"

        # Show the new image representation
        html += self.build_image_callback(imagefiles, False)

        # Close callback function
        html += "});"

        # Close javascript
        html += "</script>"

        return html

    def build_rendering_callback(self, imagefiles):
        """ Define the rendering callback.

        Parameters
        ----------
        imagefiles: list of str (mandatory)
            the path to the images that will be rendered.

        Returns
        -------
        html: str
            the rendering definition.
        """
        # Add javascript
        html = "<script type='text/javascript'>"

        # Define the callback
        html += "$('#volume-rendering').change(function() {"

        # Add an event when an event occur
        html += self.build_image_callback(imagefiles, False)

        # Close callback function
        html += "});"

        # Close javascript
        html += "</script>"

        return html

    def build_image_callback(self, imagefiles, decorated=True):
        """ Define the on image change callback.

        Parameters
        ----------
        imagefiles: list of str (mandatory)
            the path to the paths that will be rendered.
        decorated: bool (optional, default True)
            add the javascript tag.

        Returns
        -------
        html: str
            the callback definition.
        """
        # Add javascript
        html = ""
        if decorated:
            html += "<script type='text/javascript'>"

            # Define the callback
            html += "$('#volume-type').change(function() {"

        # Add an event for each image
        for cnt in range(len(imagefiles)):
            if cnt == 0:
                html += "if ($('#volume-type').val() === '{0}') {{".format(cnt)
            else:
                html += "else if ($('#volume-type').val() === '{0}') {{".format(
                    cnt)
            html += "viewer.clearVolumes();"
            html += "if ($('#volume-rendering').val() == 'multi') {"
            html += self.build_cw_loader(imagefiles, cnt, multiview=True)
            html += "}"
            html += "else {"
            html += self.build_cw_loader(imagefiles, cnt, multiview=False)
            html += "}"
            html += "}"

        # Add out of range event
        html += "else {"
        html += "viewer.clearVolumes();"
        html += "}"

        if decorated:
            # Close callback function
            html += "});"

            # Close javascript
            html += "</script>"

        return html

    def build_color_maps(self):
        """ Define the BrainBrowser color-maps.

        Returns
        -------
        html: str
            the color-maps definition.
        """
        # Go through colormaps
        html = "<script>"
        html += "BrainBrowser.config.set('color_maps', ["
        baseurl = "brainbrowser-2.3.0/color-maps/"
        for name, color in [("Gray", "#FF0000"), ("Spectral", "#FFFFFF"),
                            ("Thermal", "#FFFFFF"), ("Blue", "#FFFFFF"),
                            ("Green", "#FF0000")]:
            resource = self._cw.data_url(
                os.path.join(baseurl, "{0}.txt".format(name.lower())))

            html += "{"
            html += "name: '{0}',".format(name)
            html += "url: '{0}',".format(resource)
            html += "cursor_color: '{0}'".format(color)
            html += "},"
        html += "]);"
        html += "</script>"

        return html

    def build_brainbrowser_tools(self,
                                 time=True,
                                 contrast=False,
                                 brightness=False):
        """ Define the default BrainBrowser tools.

        Parameters
        ----------
        time: bool (optional, default False)
            add control to display time serie images.
        contrast, brightness: bool (optional, default False)
            add extra controls (not recommended).

        Returns
        -------
        html: str
            the tools definition.
        """
        # Start javascript
        html = "<script id='volume-ui-template' type='x-volume-ui-template'>"

        # Define the image rendering location
        html += "<div class='volume-viewer-display'>"
        html += "</div>\n"

        # Define control tools
        html += "<div class='volume-viewer-controls volume-controls'>"

        # Define a tool to display the voxel and world coordinates
        html += "<div class='coords'>"
        html += "<div class='control-container'>"
        html += ("<div class='control-heading' "
                 "id='voxel-coordinates-heading-{{VOLID}}'>")
        html += "Voxel Coordinates:"
        html += "</div>"
        html += "<div class='voxel-coords' data-volume-id='{{VOLID}}'>"
        html += "I:<input id='voxel-i-{{VOLID}}' class='control-inputs'>"
        html += "J:<input id='voxel-j-{{VOLID}}' class='control-inputs'>"
        html += "K:<input id='voxel-k-{{VOLID}}' class='control-inputs'>"
        html += "</div>"
        html += ("<div class='control-heading' "
                 "id='world-coordinates-heading-{{VOLID}}'>")
        html += "World Coordinates:"
        html += "</div>"
        html += "<div class='world-coords' data-volume-id='{{VOLID}}'>"
        html += "X:<input id='world-x-{{VOLID}}' class='control-inputs'>"
        html += "Y:<input id='world-y-{{VOLID}}' class='control-inputs'>"
        html += "Z:<input id='world-z-{{VOLID}}' class='control-inputs'>"
        html += "</div>"

        # Define a tool to control different images in the volume
        if time:
            html += "<div id='time-{{VOLID}}' class='time-div' data-volume-id='{{VOLID}}' style='display:none'>"
            html += "<div class='control-heading'>"
            html += "<span>Time:</span>"
            html += "</div>"
            html += "<input class='control-inputs' value='0' id='time-val-{{VOLID}}'/>"
            html += "<input type='checkbox' class='button' id='play-{{VOLID}}'><label for='play-{{VOLID}}'>Play</label>"
            html += "<div class='slider volume-viewer-threshold' id='time-slider-{{VOLID}}'></div>"
            html += "</div>"
            html += "</div>"
        else:
            html += "</div>"

        # Define a tool to change the colormap
        html += "<div class='control-container'>"
        html += "<div id='color-map-{{VOLID}}'>"
        html += "<div class='control-heading'>"
        html += "<span id='color-map-heading-{{VOLID}}'>"
        html += "Color Map:"
        html += "</span>"
        html += "</div>"
        html += "</div>"

        # Define a tool to display the selected voxel intensity
        html += "<div id='intensity-value-div-{{VOLID}}'>"
        html += "<div class='control-heading'>"
        html += "<span data-volume-id='{{VOLID}}'>"
        html += "Value:"
        html += "</span>"
        html += "</div>"
        html += ("<span id='intensity-value-{{VOLID}}' "
                 "class='intensity-value'></span>")
        html += "</div>"
        html += "</div>"

        # Define a tool to threshold the image
        html += "<div class='control-container'>"
        html += "<div class='threshold-div' data-volume-id='{{VOLID}}'>"
        html += "<div class='control-heading'>"
        html += "Brightness/Contrast:"
        html += "</div>"
        html += "<div class='thresh-inputs'>"
        html += ("<input id='min-threshold-{{VOLID}}' "
                 "class='control-inputs thresh-input-left' value='0'/>")
        html += ("<input id='max-threshold-{{VOLID}}' "
                 "class='control-inputs thresh-input-right' value='65535'/>")
        html += "</div>"
        html += ("<div class='slider volume-viewer-threshold' "
                 "id='threshold-slider-{{VOLID}}'></div>")
        html += "</div>"

        # Define a complete slicer tool
        html += ("<div id='slice-series-{{VOLID}}' "
                 "class='slice-series-div' data-volume-id='{{VOLID}}'>")
        html += ("<div class='control-heading' "
                 "id='slice-series-heading-{{VOLID}}'>All slices: </div>")
        html += ("<span class='slice-series-button button' "
                 "data-axis='xspace'>Sagittal</span>")
        html += ("<span class='slice-series-button button' "
                 "data-axis='yspace'>Coronal</span>")
        html += ("<span class='slice-series-button button' "
                 "data-axis='zspace'>Transverse</span>")
        html += "</div>"
        html += "</div>"

        # Define a tool to control the image contrast
        if contrast:
            html += "<div class='control-container'>"
            html += "<div class='contrast-div' data-volume-id='{{VOLID}}'>"
            html += ("<span class='control-heading' "
                     "id='contrast-heading{{VOLID}}'>Contrast (0.0 to 2.0):"
                     "</span>")
            html += ("<input class='control-inputs' value='1.0' "
                     "id='contrast-val'/>")
            html += ("<div id='contrast-slider' "
                     "class='slider volume-viewer-contrast'></div>")
            html += "</div>"
            html += "</div>"

        # Define a tool to control the image brightness
        if brightness:
            html += "<div class='control-container'>"
            html += "<div class='brightness-div' data-volume-id='{{VOLID}}'>"
            html += ("<span class='control-heading' "
                     "id='brightness-heading{{VOLID}}'>Brightness (-1 to 1):"
                     "</span>")
            html += "<input class='control-inputs' value='0' id='brightness-val'/>"
            html += ("<div id='brightness-slider' "
                     "class='slider volume-viewer-brightness'></div>")
            html += "</div>"
            html += "</div>"

        # End controls
        html += "</div>"

        # End javascript
        html += "</script>"

        return html

    def build_brainbrowser_banner(self, imagefiles):
        """ Define the default BrainBrowser banner.

        Parameters
        ----------
        imagefiles: list of str (mandatory)
            the path to the paths that will be rendered.

        Returns
        -------
        html: str
            the banner definition.
        """
        # Define a banner divs
        html = "<div id='brainbrowser-wrapper' style='display:none'>"
        html += "<div id='volume-viewer'>"
        html += "<div id='global-controls' class='volume-viewer-controls'>"

        # Define item to select the image rendering
        html += "<span class='control-heading'>Select rendering:</span>"
        html += "<select id='volume-rendering'>"
        html += "<option value='single'>Single</option>"
        html += "<option value='multi'>Multi</option>"
        html += "</select>"

        # Define item to select the image to be displayed
        html += "<select id='volume-type'>"
        for cnt, path in enumerate(imagefiles):
            html += "<option value='{0}'>{1}</option>".format(
                cnt, os.path.basename(path))
        html += "</select>"

        # Define item to change the panle size
        html += "<select id='volume-quality'>"
        html += "<option value='RAW' SELECTED>RAW</option>"
        # html += "<option value='LOW JPEG' SELECTED>LOW JPEG</option>"
        html += "</select>"

        # Define item to change the panel size
        html += "<span class='control-heading'>Panel size:</span>"
        html += "<select id='panel-size'>"
        html += "<option value='128'>128</option>"
        html += "<option value='256' SELECTED>256</option>"
        html += "<option value='512'>512</option>"
        html += "</select>"

        # Define item to reset displayed views
        # html += "<span id='sync-volumes-wrapper'>"
        # html += ("<input type='checkbox' class='button' id='reset-volumes'>"
        #          "<label for='reset-volumes'>Reset</label>")
        # html += "</span>"

        # Define item to create a screenshot
        html += "<span id='screenshot' class='button'>Screenshot</span>"
        html += ("<div class='instructions'>Shift-click to drag. Hold ctrl "
                 "to measure distance.</div>")

        # End divs
        html += "</div>"
        html += "<div id='brainbrowser'></div>"
        html += "</div>"
        html += "</div>"

        return html
Exemplo n.º 23
0
class MetaGenSearchView(View):
    """ View to display the genomic measures store in PLINK format: bef/bim/fam
    files.

    The view id is 'metagen-search': .../view?vid=metagen-search&... . This
    view accpets four parameters:
    - measure (mandatory): specify the GenomicMeasure entity 'label' that
      contains the PLINK file to be analysed: ...&measure=Chip1&...
    - gene (manadatory): in order to filter the genomic dataset specify at
      least one gene 'hgnc_name': ...&gene=CAMTA1&gene=EVI5...
    - subject (optional, default all subjects): used to acces the data of
      specific subjects only: ....&subject=iid1&subject=iid2...
    - export (optional, default 'data'): the data export type: 'data' will
      export the measured genomic data from the selected filtering options,
      'metagen' will export the genomic metadata reference from the selected
      genes only, 'ref'  will export the genomic metadata reference from the
      selected filtering options: ...&export=ref&...
    """
    __regid__ = "metagen-search"
    __select__ = authenticated_user()
    title = _("MetaGen Search")
    div_id = "metagen-search"
    _display = True

    def call(self, gene=None, measure=None, subjects=None, export_type=None):
        """ Generate/display the genomic dataset of insterest.
        """
        # Display header
        if self._display:
            self.w(u"<h1>MetaGen Search</h1>")
            self.w(u"<hr>")

        # Retrieve form parameters from the url
        genes = gene or self._cw.form.get("gene", None)
        if genes is not None and not isinstance(genes, list):
            genes = [genes]
        measure = measure or self._cw.form.get("measure", None)
        subjects = subjects or self._cw.form.get("subject", None)
        if subjects == "all":
            subjects = None
        if subjects is not None and not isinstance(subjects, list):
            subjects = [subjects]
        export_type = export_type or self._cw.form.get("export", "data")

        # Check input parameters
        if genes is None or measure is None:
            msg = ("Need a gene name to perform a search and a valid "
                   "genomic measure name.")
            if self._display:
                self.error(msg)
            else:
                self.w(unicode(json.dumps({"error": msg})))
            return
        if export_type not in ("data", "metagen", "ref"):
            msg = ("'{0}' export type not recognize. Supported format are "
                   "'data', 'metagen' and 'ref'.".format(export_type))
            if self._display:
                self.error(msg)
            else:
                self.w(unicode(json.dumps({"error": msg})))
            return

        # Display search parameters
        if self._display:
            self.w(u"<b>Gene Names</b>: {0}<br/>".format("; ".join(genes)))
            self.w(u"<b>Genomic Measure</b>: {0}<br/>".format(measure))
            self.w(u"<b>Subject</b>: {0}<br/>".format(
                "; ".join(subjects) if subjects is not None else "all"))
            self.w(u"<b>Export Type</b>: {0}<br/>".format(export_type))

        # Get the genomic measure associated plink files
        rset = self._cw.execute(
            "Any G Where G is GenomicMeasure, G label '{0}'".format(measure))
        if rset.rowcount != 1:
            msg = u"'{0}' genomic measure(s) detected, one expected.".format(
                rset.rowcount)
            if self._display:
                self.error(msg)
            else:
                self.w(unicode(json.dumps({"error": msg})))
            return
        egmeasure = rset.get_entity(0, 0)
        plinkfiles = []
        for efset in egmeasure.filesets:
            for efile in efset.external_files:
                plinkfiles.append(efile.filepath)
        if self._display:
            self.w(u"<b>Plink Files</b>: {0}<br/>".format(
                "; ".join(plinkfiles)))
            self.w(u"<br/>")
        roots = []
        for path in plinkfiles:
            root, ext = os.path.splitext(path)
            if ext not in [".bed", ".bim", ".fam"]:
                msg = u"'{0}' plink extension not supported.".format(ext)
                if self._display:
                    self.error(msg)
                else:
                    self.w(unicode(json.dumps({"error": msg})))
                return
            roots.append(root)
        if len(roots) != 3 or len(set(roots)) != 1:
            msg = u"Three plink bed/bim/fam files expected in the same folder."
            if self._display:
                self.error(msg)
            else:
                self.w(unicode(json.dumps({"error": msg})))
            return

        # Load the plink and the
        try:
            snp_data, metagen_snps_of_gene = genotype_measure(
                path_dataset=root,
                snp_ids=None,
                gene_names=genes,
                subject_ids=subjects,
                count_A1=True,
                path_log=None,
                timeout=10,
                nb_tries=3,
                metagen_url=self._cw.vreg.config["metagen_url"])
        except Exception as e:
            msg = u"Can't acces the required genotype measure: {0}".format(e)
            if self._display:
                self.error(msg)
            else:
                self.w(unicode(json.dumps({"error": msg})))
            return

        # Display result in a table view

        # > export the measured genomic data from the selected filtering
        # options
        if export_type == "data":
            labels = ["family_id"] + snp_data.sid.tolist()
            records = numpy.concatenate((snp_data.iid, snp_data.val),
                                        axis=1).tolist()
            if self._display:
                self.wview("jtable-hugetable-clientside",
                           None,
                           "null",
                           labels=labels,
                           records=records,
                           csv_export=True,
                           title="Genotypes",
                           timepoint="",
                           elts_to_sort=["ID", "family_id"],
                           tooltip_name=None,
                           use_scroller=False,
                           index=0)
            else:
                labels = ["subject_id"] + labels
                self.w(
                    unicode(json.dumps({
                        "labels": labels,
                        "records": records
                    })))

        # > export the genomic metadata reference from the selected filtering
        # options
        elif export_type == "ref":
            labels = ["chromosome", "cM_position", "bp_position"]
            snp_data.sid.shape += (1, )
            records = numpy.concatenate((snp_data.sid, snp_data.pos),
                                        axis=1).tolist()
            if self._display:
                self.wview("jtable-hugetable-clientside",
                           None,
                           "null",
                           labels=labels,
                           records=records,
                           csv_export=True,
                           title="Genotypes",
                           timepoint="",
                           elts_to_sort=[],
                           tooltip_name=None,
                           use_scroller=False,
                           index=1)
            else:
                labels = ["rs_id"] + labels
                self.w(
                    unicode(json.dumps({
                        "labels": labels,
                        "records": records
                    })))

        # > export the genomic metadata reference from the selected genes
        # only
        elif export_type == "metagen":
            if metagen_snps_of_gene is not None:
                labels = ["rs_id", "chromosome", "bp_position"]
                records = []
                for gname, snps in metagen_snps_of_gene.items():
                    for snp in snps:
                        records.append(
                            [gname, snp.rs_id, snp.chromosome, snp.bp_pos])
                if self._display:
                    self.wview("jtable-hugetable-clientside",
                               None,
                               "null",
                               labels=labels,
                               records=records,
                               csv_export=True,
                               title="Genotypes",
                               timepoint="",
                               elts_to_sort=["ID", "rs_id", "chromosome"],
                               tooltip_name=None,
                               use_scroller=False,
                               index=2)
                else:
                    labels = ["hgnc_name"] + labels
                    self.w(
                        unicode(
                            json.dumps({
                                "labels": labels,
                                "records": records
                            })))
            else:
                msg = (u"No genomic metadata found, please contact the "
                       "service administrator.")
                if self._display:
                    self.error(msg)
                else:
                    self.w(unicode(json.dumps({"error": msg})))
                return

    def error(self, msg):
        """ Display an error message.
        """
        self.w(u"<div class='panel panel-danger'>")
        self.w(u"<div class='panel-heading'>")
        self.w(u"<h2 class='panel-title'>ERROR</h2>")
        self.w(u"</div>")
        self.w(u"<div class='panel-body'>")
        self.w(u"<h3>{0}</h3>".format(msg))
        self.w(u"</div>")
        self.w(u"</div>")
Exemplo n.º 24
0
class DisplayDocumentation(NullView):
    """ Create a view to display the documentation.
    """
    __regid__ = "piws-documentation"
    __select__ = authenticated_user()
    templatable = False
    div_id = "piws-documentation"
    default_message = "Documentation has not been provided yet."

    def __init__(self, *args, **kwargs):
        """ Initialize the DisplayDocumentation class.
        """
        super(DisplayDocumentation, self).__init__(*args, **kwargs)

    def call(self, tooltip=None, tooltip_name=None, **kwargs):
        """ Create the documentation page.

        Parameters
        ----------
        tooltip: str
            a html formated string.
        tooltip_name: str
            the key of the item containing the html formated documentation.
        """
        # Get the parameters
        tooltip_name = tooltip_name or self._cw.form.get("tooltip_name", None)
        if tooltip_name is not None:
            tooltip = self._cw.vreg.docmap.get(tooltip_name, "")
            page_title = tooltip_name
        else:
            tooltip = tooltip or self._cw.form.get("tooltip", "")
            page_title = "Documentation"

        # Set a default message if documentation is empty
        if tooltip:
            tooltip = tooltip
            add_css = '<link rel="stylesheet" type="text/css" href="{0}"/>'\
                .format(self._cw.data_url("doc.css"))
        else:
            image_url = self._cw.data_url("images/error.png")
            error_html = '<div style="align: left; text-align:center;">'
            error_html += '<img src="{0}"/>'.format(image_url)
            error_html += '<div class="caption"><font size="7">{0}</font></div>'\
                .format(self.default_message)
            error_html += '</div>'
            tooltip = "<div class='body'>{0}</div>".format(error_html)
            add_css = ''

        # Display documentation
        self.w(u"<!DOCTYPE html>")
        self.w(u"<html xmlns:cubicweb='http://www.cubicweb.org' lang='en'>")
        self.w(u"<head>")
        self.w(u"<meta http-equiv='content-type' content='text/html; charset=UTF-8'/>")
        self.w(u"<meta http-equiv='X-UA-Compatible' content='IE=8' />")
        self.w(u'{0}'.format(add_css))
        self.w(u'<title>{0}</title>'.format(page_title))
        self.w(u'<link rel="icon" href="{0}" />'.format(
            self._cw.data_url("favicon.ico")))
        self.w(u"</head>")
        self.w(u"<body>")
        tooltip = tooltip.replace('class="document"', 'class="body"')
        self.w(unicode(tooltip))
        self.w(u"</body>")
        self.w(u"</html>")