@@ -514,6 +514,323 @@ handlers.
514514
515515.. _CSP: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
516516
517+ Classhelper Web Component
518+ =========================
519+
520+ Version 2.4.0 provides a new classhelper popup written as a
521+ web component. By installing 3 files and adding a stanza to
522+ ``interfaces.py`` you can enable the new component.
523+
524+ The `development of this component was done by team-03
525+ <https://github.com/UMB-CS-682-Team-03/tracker>`_ of the
526+ Spring 2024 CS682 graduate software engineering SDL capstone
527+ class at the University of Massachusetts - Boston. Their
528+ documentation is copied/adapted below.
529+
530+ File Installation
531+ -----------------
532+
533+ There are three files to install in your tracker. You can
534+ copy them from the template directory for the classic
535+ tracker. The location of the template file can be obtained
536+ by running: ``roundup-admin templates`` and looking for the
537+ path value of the classic template. If the path value is
538+ ``/path/to/template``, copy::
539+
540+ /path/to/template/html/classhelper.js
541+ /path/to/template/html/classhelper.css
542+ /path/to/template/html/_generic.translation
543+
544+ to your tracker's html directory.
545+
546+ .. :
547+ remove in 2.5 release if /data/user/roles endpoint
548+ in rest.py is successful.
549+
550+ If you wish to search for user's by role, you have to do one more
551+ step. Because roles are not an actual class (e.g. like status or
552+ keyword), you have to set up a new REST endpoint for them. To do this,
553+ copy::
554+
555+
556+ from roundup.rest import Routing, RestfulInstance, _data_decorator
557+
558+ class RestfulInstance:
559+
560+ @Routing.route("/roles", 'GET')
561+ @_data_decorator
562+ def get_roles(self, input):
563+ """Return all defined roles. The User class property
564+ roles is a string but simulate it as a MultiLink
565+ to an actual Roles class.
566+ """
567+ return 200, {"collection":
568+ [{"id": rolename,"name": rolename}
569+ for rolename in list(self.db.security.role.keys())]}
570+
571+ into the file ``interfaces.py`` in your tracker's home
572+ directory. You can create this file if it doesn't exist.
573+ See the `REST documentation <rest.html>`_ for details on
574+ ``interfaces.py``.
575+
576+ The classic tracker does not have the ``/roles`` REST endpoint
577+ configured. If you are creating a new tracker from the classic
578+ template, it will show a text based search not a select/dropdown based
579+ search. By modifying interfaces.py you will enable a dropdown search.
580+
581+ Wrapping the Classic Classhelper
582+ --------------------------------
583+
584+ To allow your users to select items in the web interface
585+ using the new classhelper, you should edit the template files
586+ in the ``html/`` subdirectory of your tracker. Where you see
587+ code like::
588+
589+ <th i18n:translate="">Superseder</th>
590+ <td>
591+ <span tal:replace="structure
592+ python:context.superseder.field(showid=1,size=20)" />
593+ <span tal:condition="context/is_edit_ok"
594+ tal:replace="structure
595+ python:db.issue.classhelp('id,title',
596+ property='superseder', pagesize=100)" />
597+ [...]
598+ </td>
599+
600+ change it to wrap the classhelp span like this::
601+
602+ <th i18n:translate="">Superseder</th>
603+ <td>
604+ <span tal:replace="structure
605+ python:context.superseder.field(showid=1,size=20)" />
606+ <roundup-classhelper
607+ data-popup-title="Superseder Classhelper - {itemDesignator}"
608+ data-search-with="title,status,keyword[]-name">
609+ <span tal:condition="context/is_edit_ok"
610+ tal:replace="structure
611+ python:db.issue.classhelp('id,title',
612+ property='superseder', pagesize=100)" />
613+ </roundup-classhelper>
614+ [...]
615+ </td>
616+
617+ which displays a three part classhelper.
618+
619+ 1. the search pane includes a text search box for the `title`
620+ and `status` properties and a dropdown for the keyword property,
621+ sorted by name in descending order.
622+ 2. the selection pane will show 100 search results per page.
623+ It also allows the user to move to the next or previous result
624+ page.
625+ 3. the accumulator at the bottom shows all the selected items. It
626+ also has buttons to accept the items or cancel the
627+ classhelper, leaving the original page unchanged.
628+
629+ Note that the user class is a little different because users without
630+ an Admin role can't search for a user by Role. So we hide the Role
631+ search element for non admin users. Starting with::
632+
633+ <th i18n:translate="">Nosy List</th>
634+ <td>
635+ <span tal:replace="structure context/nosy/field" />
636+ <span tal:condition="context/is_edit_ok" tal:replace="structure
637+ python:db.user.classhelp('username,realname,address',
638+ property='nosy', width='600'" />
639+ </td>
640+
641+ wrap the classhelp span with ``<roundup-classhelper>`` like::
642+
643+ <th i18n:translate="">Nosy List</th>
644+ <td>
645+ <span tal:replace="structure context/nosy/field" />
646+ <roundup-classhelper tal:define="search string:name,phone,roles[]"
647+ tal:attributes="data-search-with python:search
648+ if request.user.hasRole('Admin') else
649+ ','.join(search.split(',')[:-1])">
650+ <span tal:condition="context/is_edit_ok" tal:replace="structure
651+ python:db.user.classhelp('username,realname,address',
652+ property='nosy', width='600'" />
653+ </roundup-classhelper>
654+ </td>
655+
656+ The ``','.join(search.split(',')[:-1])`` removes the last element of
657+ the search string (``roles[]``) if the user does not have the Admin
658+ role.
659+
660+ <roundup-classhelper> configuration
661+ -----------------------------------
662+
663+ There are two attributes used to configure the classhelper.
664+
665+ data-popup-title:
666+ * this attribute is optional. A reasonable default is
667+ provided if it is missing.
668+ * Adding ``data-popup-title`` changes the title of the popup
669+ window with the value of the attribute.
670+ * ``{itemDesignator}`` can be used inside the attribute value
671+ to replace it with the current classhelper usage context.
672+ E.G. ``data-popup-title="Nosy List Classhelper - {itemDesignator}"``
673+ will display the popup window title as ``Nosy List Classhelper - issue24``
674+
675+ data-search-with:
676+ * this attribute is optional. If it is not set, a search
677+ panel is not created provided to allow the users search
678+ within the class.
679+ * Adding ``data-search-with`` specifies the fields that can
680+ be used for searching.
681+ E.G. ``data-search-with="title,status,keyword"``
682+ * The search can be customized using the following syntax:
683+
684+ * Adding ``[]`` at then end of a field (``"status[]"``)
685+ will displays a dropdown for the "status" field
686+ listing all the values the user can access. E.G.::
687+
688+ <roundup-classhelper
689+ data-search-with="title,status[],keyword[]">
690+ <span tal:condition="context/is_edit_ok"
691+ tal:replace="structure
692+ python:db.issue.classhelp('id,title',
693+ property='superseder', pagesize=100)" />
694+ </roundup-classhelper>
695+
696+ will create a search pane with a text search for title
697+ and dropdowns for status and keyword.
698+
699+ * Adding a sort key after the ``[]`` allows you to
700+ select the order of the elements in the dropdown. For
701+ example ``keyword[]+name`` sorts the keyword
702+ dropdown in ascending order by name. While
703+ ``keyword[]-name`` sorts the keyword dropdown in
704+ descending order by name. If the sort order is not
705+ specified, the default order for the class is used.
706+
707+ .. :
708+ remove in 2.5 release if /data/user/roles endpoint
709+ in rest.py is successful.
710+
711+ * Note that the ``roles`` field for the user class is
712+ special. If you have not modified ``interfaces.py``,
713+ roles can not be displayed as a dropdown. It will be
714+ displayed as a text search field instead.
715+
716+ <roundup-classhelper> styling
717+ -----------------------------
718+
719+ The roundup-classhelper component uses minimal styling so it
720+ can blend in with most trackers. If you want to change the
721+ styling, you can modify the classhelper.css file in the html
722+ directory. Even though roundup-classhelper is a web
723+ component, it doesn't use the shadow DOM. If you don't know
724+ what this means, it just means that it's easy to style.
725+
726+ There is on trick however. Getting the web component to load
727+ changes to the css file is a bit tricky. Basically the
728+ browser caches the old file and you have to resort to tricks
729+ to make it get a new copy of the file.
730+
731+ One way to do this is to open to the ``classhelper.css``
732+ file in your browser and force refresh it. To do this:
733+
734+ 1. Open the home page for your Roundup issue tracker in a
735+ web browser.
736+
737+ 2. In the address bar, append ``@@file/classhelper.css``
738+ to the end of your Roundup URL. For example, if your
739+ Roundup URL is ``https://example.com/tracker/``, the
740+ URL you should visit would be
741+ ``https://example.com/tracker/@@file/classhelper.css``.
742+
743+ 3. This will open the `classhelper.css` file in your browser.
744+
745+ 4. Press ``Ctrl+Shift+R`` (on Windows and Linux) or
746+ ``Cmd+Shift+R`` (on macOS). This triggers a hard
747+ refresh of the page, which forces the browser to
748+ reload the file and associated resources from the
749+ server.
750+
751+ This should resolve any issues caused by cached or outdated
752+ files. It is possible that you have to open devtools and set
753+ the disable cache option in the network panel in extreme
754+ cases.
755+
756+ Also during development, you might want to `set a very low
757+ cache time
758+ <customizing.html#changing-cache-control-headers>`_ for
759+ classhelper.css using something like::
760+
761+ Client.Cache_Control['classhelper.css'] = "public, max-age=10"
762+
763+
764+ Translations
765+ ------------
766+
767+ To set up translations for the <roundup-classhelper>
768+ component, follow these steps.
769+
770+ 1. Create a ``messages.pot`` file by running
771+ ``roundup-gettext <tracker_home_directory>``. This
772+ creates ``locale/messages.pot`` in your tracker's home
773+ directory. It extracts all translatable strings from
774+ your tracker. We will use it as a base template for the
775+ new strings you want to translate.
776+ 2. See if you already have a ``.po`` translation file for
777+ your language in the tracker's locale/ directory. If you
778+ don't, copy ``messages.pot`` to a .po file for the
779+ language you want to translate. For example German
780+ would be at ``de.po`` English would be at ``en.po``
781+ (for example if you want to change the ``apply`` button
782+ to say ``Do It``.
783+
784+ 3. Edit the new .po file. After the header, add the
785+ translation entries for the <roundup-classhelper>
786+ component. For example `next` and `submit` are
787+ displayed in English when the rest of the interface is
788+ in German. Add::
789+
790+ msgid "submit"
791+ msgstr "gehen"
792+
793+ msgid "next"
794+ msgstr "nächste"
795+
796+ msgid "name"
797+ msgstr "name"
798+
799+ Note: the value for `msgid` is case sensitive. You can
800+ see the msgid for static strings by looking for
801+ ``CLASSHELPER_TRANSLATION_KEYWORDS`` in classhelper.js.
802+
803+ 4. Save the .po file.
804+
805+ 5. Restart your Roundup instance.
806+
807+ This should display the missing translations, for more
808+ details refer to the `translation (i18n) section of the
809+ developers documentation
810+ <developers.html#extracting-translatable-messages>`_.
811+
812+ Troubleshooting
813+ ---------------
814+
815+ The roundup-classhelper will fallback to using the classic
816+ classhelper if:
817+
818+ * the user doesn't have REST access
819+ * the browser doesn't support web components
820+
821+ It will display an alert modal dialog to the user before triggering
822+ the classic classhelper as a fallback. A detailed error will be
823+ printed to the browser console. The console is visible in devtools and
824+ can be opened by pressing the ``F12`` key.
825+
826+ You can disable the classhelper on a per URL basis by adding
827+ ``#classhelper-wc-toggle`` to the end of the URL. This will prevent
828+ the web component from starting up.
829+
830+ Also you can set ``DISABLE_CLASSHELP = true`` at the top of
831+ classhelper.js to disable the classhelper without having to make any
832+ changes to your templates.
833+
517834Configuring native-fts Full Text Search
518835=======================================
519836
0 commit comments