Mercurial > p > roundup > code
comparison doc/admin_guide.txt @ 8416:370689471a08 issue2550923_computed_property
merge from default branch accumulated changes since Nov 2023
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sun, 17 Aug 2025 16:12:25 -0400 |
| parents | 0663a7bcef6c |
| children | 94eed885e958 |
comparison
equal
deleted
inserted
replaced
| 7693:78585199552a | 8416:370689471a08 |
|---|---|
| 221 ``-----END PRIVATE KEY-----`` and | 221 ``-----END PRIVATE KEY-----`` and |
| 222 ``-----BEGIN CERTIFICATE-----``, | 222 ``-----BEGIN CERTIFICATE-----``, |
| 223 ``-----END CERTIFICATE-----``. | 223 ``-----END CERTIFICATE-----``. |
| 224 If not specified, roundup will generate a temporary, self-signed certificate | 224 If not specified, roundup will generate a temporary, self-signed certificate |
| 225 for use. | 225 for use. |
| 226 **loghttpvialogger** section | |
| 227 If you: | |
| 228 | |
| 229 * have **loghttpvialogger** enabled | |
| 230 * use **pidfile** | |
| 231 * use a logging config file in the tracker's config.ini | |
| 232 | |
| 233 it is essential to specify absolute paths for log files in the | |
| 234 tracker's logging.config file. The use of pidfile causes the | |
| 235 server to switch to the root directory ('/'). As a result | |
| 236 relative paths in the logging ini configuration file (as | |
| 237 opposed to the tracker's config.ini) will be written to the | |
| 238 system's root directory. The access error will cause the server | |
| 239 to exit. | |
| 226 **trackers** section | 240 **trackers** section |
| 227 Each line denotes a mapping from a URL component to a tracker home. | 241 Each line denotes a mapping from a URL component to a tracker home. |
| 228 Make sure the name part doesn't include any url-unsafe characters like | 242 Make sure the name part doesn't include any url-unsafe characters like |
| 229 spaces. Stick to alphanumeric characters and you'll be ok. | 243 spaces. Stick to alphanumeric characters and you'll be ok. |
| 230 | 244 |
| 241 existing config.ini. Running this in a tracker home directory will | 255 existing config.ini. Running this in a tracker home directory will |
| 242 move the exsiting config.ini to config.bak and replace it with the | 256 move the exsiting config.ini to config.bak and replace it with the |
| 243 roundup-server's config.ini. This will make the tracker in the | 257 roundup-server's config.ini. This will make the tracker in the |
| 244 directory fail to start util the original config.ini is restored. | 258 directory fail to start util the original config.ini is restored. |
| 245 | 259 |
| 260 .. _configuring-compression: | |
| 261 | |
| 246 Configuring Compression | 262 Configuring Compression |
| 247 ======================= | 263 ======================= |
| 248 | 264 |
| 249 Roundup will compress HTTP responses to clients on the fly. Dynamic, | 265 Roundup will compress HTTP responses to clients on the fly. Dynamic, |
| 250 on the fly, compression is enabled by default, to disable it set:: | 266 on the fly, compression is enabled by default, to disable it set:: |
| 256 in the tracker's ``config.ini``. You should disable compression if | 272 in the tracker's ``config.ini``. You should disable compression if |
| 257 your proxy (e.g. nginx or apache) or wsgi server (uwsgi) is configured | 273 your proxy (e.g. nginx or apache) or wsgi server (uwsgi) is configured |
| 258 to compress responses on the fly. The python standard library includes | 274 to compress responses on the fly. The python standard library includes |
| 259 gzip support. For brotli or zstd you will need to install packages. See | 275 gzip support. For brotli or zstd you will need to install packages. See |
| 260 the `installation documentation`_ for details. | 276 the `installation documentation`_ for details. |
| 277 | |
| 278 .. index:: single: interfaces.py; configuring http compression | |
| 261 | 279 |
| 262 Some assets will not be compressed on the fly. Assets with mime types | 280 Some assets will not be compressed on the fly. Assets with mime types |
| 263 of "image/png" or "image/jpeg" will not be compressed. You | 281 of "image/png" or "image/jpeg" will not be compressed. You |
| 264 can add mime types to the list by using ``interfaces.py`` as discussed | 282 can add mime types to the list by using ``interfaces.py`` as discussed |
| 265 in the `customisation documentation`_. As an example adding:: | 283 in the `customisation documentation`_. As an example adding:: |
| 317 files, the data will be compressed dynamically (on the fly) using | 335 files, the data will be compressed dynamically (on the fly) using |
| 318 brotli. If there is a precompressed gzip file present the client will | 336 brotli. If there is a precompressed gzip file present the client will |
| 319 get the gzip version and not a brotli compressed version. This | 337 get the gzip version and not a brotli compressed version. This |
| 320 mechanism allows the admin to allow use of brotli and zstd for | 338 mechanism allows the admin to allow use of brotli and zstd for |
| 321 dynamic content, but not for static content. | 339 dynamic content, but not for static content. |
| 340 | |
| 341 .. _browser_handling_attached_files: | |
| 342 | |
| 343 .. index:: single: interfaces.py; Controlling browser handling of attached files | |
| 344 | |
| 345 Controlling Browser Handling of Attached Files | |
| 346 ============================================== | |
| 347 | |
| 348 You may be aware of the ``allow_html_file`` `config.ini setting | |
| 349 <reference.html#config-ini-section-web>`_. When set to yes, it permits | |
| 350 html files to be attached and displayed in the browser as html | |
| 351 files. The underlying mechanism used to enable/disable attaching HTML | |
| 352 is exposed using ``interfaces.py``. | |
| 353 | |
| 354 Similar to ``Client.precompressed_mime_types`` above, there is a | |
| 355 ``Client.mime_type_allowlist``. If a mime type is present in this | |
| 356 list, an attachment with this mime type is served to the browser. If | |
| 357 the mime type is not present, the mime type is set to | |
| 358 ``application/octet-stream`` which causes the browser to download the | |
| 359 attachment to a file. | |
| 360 | |
| 361 In release 2.4.0, the mime type ``application/pdf`` was removed from | |
| 362 the precompressed_mime_types list. This prevents the browser from | |
| 363 executing scripts that may be included in the PDF file. If you trust | |
| 364 the individuals uploading PDF files to your tracker and wish to allow | |
| 365 viewing PDF files from your tracker, you can do so by editing your | |
| 366 tracker's "interfaces.py" file. Adding:: | |
| 367 | |
| 368 from roundup.cgi.client import Client | |
| 369 Client.mime_type_allowlist.append('application/pdf') | |
| 370 | |
| 371 will permit the PDF files to be viewed in the browser rather than | |
| 372 downloaded to a file. | |
| 373 | |
| 374 Similarly, you can remove a mime type (e.g. audio/oog) using:: | |
| 375 | |
| 376 from roundup.cgi.client import Client | |
| 377 Client.mime_type_allowlist.remove('audio/oog') | |
| 378 | |
| 379 which will force the browser to save the attachment to a file rather | |
| 380 than playing the audio file. | |
| 381 | |
| 382 .. index:: single: interfaces.py; setting REST maximum result limit | |
| 383 | |
| 384 Configuring REST Maximum Result Limit | |
| 385 ===================================== | |
| 386 | |
| 387 To prevent denial of service (DOS) and limit user wait time for an | |
| 388 unbounded request, the REST endpoint has a maximum limit on the number | |
| 389 of rows that can be returned. By default, this is set to 10 million. | |
| 390 This setting applies to all users of the REST interface. If you want | |
| 391 to change this limit, you can add the following code to the | |
| 392 ``interfaces.py`` file in your tracker:: | |
| 393 | |
| 394 # change max response rows | |
| 395 from roundup.rest import RestfulInstance | |
| 396 RestfulInstance.max_response_row_size = 26 | |
| 397 | |
| 398 This code will set the maximum number of rows to 25 (one less than the | |
| 399 value). Note that this setting is rarely used and is not available in | |
| 400 the tracker's ``config.ini`` file. Setting it through this mechanism | |
| 401 allows you to enter a string or number that may break Roundup, such as | |
| 402 "asdf" or 0. In general, it is recommended to keep the limit at its | |
| 403 default value. However, this option is available for cases when a | |
| 404 request requires more than 10 million rows and pagination using | |
| 405 ``@page_index`` and ``@page_size=9999999`` is not possible. | |
| 322 | 406 |
| 323 Adding a Web Content Security Policy (CSP) | 407 Adding a Web Content Security Policy (CSP) |
| 324 ========================================== | 408 ========================================== |
| 325 | 409 |
| 326 A Content Security Policy (`CSP`_) adds a layer of security to | 410 A Content Security Policy (`CSP`_) adds a layer of security to |
| 370 interface. These changes are also trusted code that will be run | 454 interface. These changes are also trusted code that will be run |
| 371 when invoked. | 455 when invoked. |
| 372 | 456 |
| 373 More secure CSPs can also be created. However because of the ability | 457 More secure CSPs can also be created. However because of the ability |
| 374 to customise the web interface, it is difficult to provide guidance. | 458 to customise the web interface, it is difficult to provide guidance. |
| 459 | |
| 460 .. _dynamic_csp: | |
| 375 | 461 |
| 376 Dynamic CSP | 462 Dynamic CSP |
| 377 ----------- | 463 ----------- |
| 378 | 464 |
| 379 Roundup creates a cryptographic nonce for every client request. The | 465 Roundup creates a cryptographic nonce for every client request. The |
| 397 "object-src 'self' 'nonce-{nonce}'; " | 483 "object-src 'self' 'nonce-{nonce}'; " |
| 398 ), | 484 ), |
| 399 } | 485 } |
| 400 | 486 |
| 401 | 487 |
| 402 def AddHtmlHeaders(client, header_dict=None): | 488 def AddHtmlHeaders(self, header_dict=None): |
| 403 ''' Generate https headers from dict use default security headers | 489 ''' Generate https headers from dict use default security headers |
| 404 | 490 |
| 405 Setting the header with a value of None will not inject the | 491 Setting the header with a value of None will not inject the |
| 406 header and can override the default set. | 492 header and can override the default set. |
| 407 | 493 |
| 408 Header values will be formatted with a dictionary including a | 494 Header values will be formatted with a dictionary including a |
| 409 nonce. Use to set a nonce for inline scripts. | 495 nonce. Use to set a nonce for inline scripts. |
| 496 | |
| 497 self is an instance of the TemplatingUtilities class, so | |
| 498 you have access to self.client as well as any functions added | |
| 499 using registerUtil. | |
| 410 ''' | 500 ''' |
| 411 try: | 501 try: |
| 412 if client.client_nonce is None: | 502 if self.client.client_nonce is None: |
| 413 # logger.warning("client_nonce is None") | 503 # logger.warning("client_nonce is None") |
| 414 client.client_nonce = client.session_api._gen_sid() | 504 self.client.client_nonce = self.client.session_api._gen_sid() |
| 415 except AttributeError: | 505 except AttributeError: |
| 416 # client.client_nonce doesn't exist, create it | 506 # self.client.client_nonce doesn't exist, create it |
| 417 # logger.warning("client_nonce does not exist, creating") | 507 # logger.warning("client_nonce does not exist, creating") |
| 418 client.client_nonce = client.session_api._gen_sid() | 508 self.client.client_nonce = client.session_api._gen_sid() |
| 419 | 509 |
| 420 headers = default_security_headers.copy() | 510 headers = default_security_headers.copy() |
| 421 if isinstance(header_dict, dict): | 511 if isinstance(header_dict, dict): |
| 422 headers.update(header_dict) | 512 headers.update(header_dict) |
| 423 | 513 |
| 424 client_headers = client.additional_headers | 514 client_headers = self.client.additional_headers |
| 425 | 515 |
| 426 for header, value in list(headers.items()): | 516 for header, value in list(headers.items()): |
| 427 if value is None: | 517 if value is None: |
| 428 continue | 518 continue |
| 429 client_headers[header] = value.format( | 519 client_headers[header] = value.format( |
| 430 nonce=client.client_nonce) | 520 nonce=self.client.client_nonce) |
| 431 | 521 |
| 432 def init(instance): | 522 def init(instance): |
| 433 instance.registerUtil('AddHtmlHeaders', AddHtmlHeaders) | 523 # Note the use of the new (in version 2.5) registerUtilMethod |
| 524 instance.registerUtilMethod('AddHtmlHeaders', AddHtmlHeaders) | |
| 434 | 525 |
| 435 | 526 |
| 436 Adding the following to ``page.html`` right after the opening | 527 Adding the following to ``page.html`` right after the opening |
| 437 ``<html....`>`` tag:: | 528 ``<html....`>`` tag:: |
| 438 | 529 |
| 439 <tal:code tal:content="python:utils.AddHtmlHeaders(request.client)" /> | 530 <tal:code tal:content="python:utils.AddHtmlHeaders()" /> |
| 440 | 531 |
| 441 will invoke ``AddHtmlHeaders()`` to add the CSP header with the nonce. | 532 will invoke ``AddHtmlHeaders()`` to add the CSP header with the nonce. |
| 442 | 533 |
| 443 With this set of CSP headers, all style, script and object tags will | 534 With this set of CSP headers, all style, script and object tags will |
| 444 need a ``nonce`` attribute. This can be added by changing:: | 535 need a ``nonce`` attribute. This can be added by changing:: |
| 450 <script | 541 <script |
| 451 tal:attributes="nonce request/client/client_nonce" | 542 tal:attributes="nonce request/client/client_nonce" |
| 452 src="javascript.js"></script> | 543 src="javascript.js"></script> |
| 453 | 544 |
| 454 for each script, object or style tag. | 545 for each script, object or style tag. |
| 546 | |
| 547 If you are using a version of Roundup before version 2.5, you need to | |
| 548 replace ``instance.registerUtilMethod`` with | |
| 549 ``instance.registerUtil``. For example:: | |
| 550 | |
| 551 def init(instance): | |
| 552 instance.registerUtil('AddHtmlHeaders', AddHtmlHeaders) | |
| 553 | |
| 554 The AddHtmlHeaders function needs to be changed so that ``self.client`` | |
| 555 is replaced by ``client``:: | |
| 556 | |
| 557 # replace self parameter with client | |
| 558 def AddHtmlHeaders(client, header_dict=None): | |
| 559 ''' Generate https headers from dict use default security headers | |
| 560 | |
| 561 Setting the header with a value of None will not inject the | |
| 562 header and can override the default set. | |
| 563 | |
| 564 Header values will be formatted with a dictionary including a | |
| 565 nonce. Use to set a nonce for inline scripts. | |
| 566 ''' | |
| 567 | |
| 568 ### Then change all references to self.client to client | |
| 569 | |
| 570 try: | |
| 571 if client.client_nonce is None: # note self.client -> client | |
| 572 # logger.warning("client_nonce is None") | |
| 573 client.client_nonce = self.client.session_api._gen_sid() | |
| 574 | |
| 575 ... | |
| 576 | |
| 577 Lastly the client must be passed explicitly when calling | |
| 578 AddHtmlHeaders. The call looks like:: | |
| 579 | |
| 580 <tal:code tal:content="python:utils.AddHtmlHeaders(request.client)" /> | |
| 581 | |
| 455 | 582 |
| 456 Remediating ``unsafe-inline`` | 583 Remediating ``unsafe-inline`` |
| 457 ----------------------------- | 584 ----------------------------- |
| 458 .. _remediating unsafe-inline: | 585 .. _remediating unsafe-inline: |
| 459 | 586 |
| 485 the trusted function in base_javascript to replace the ignored ``onX`` | 612 the trusted function in base_javascript to replace the ignored ``onX`` |
| 486 handlers. | 613 handlers. |
| 487 | 614 |
| 488 .. _CSP: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP | 615 .. _CSP: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP |
| 489 | 616 |
| 617 Classhelper Web Component | |
| 618 ========================= | |
| 619 | |
| 620 Version 2.4.0 provides a new classhelper popup written as a web | |
| 621 component. By installing 3 files and editing the tracker's templates | |
| 622 you can enable the new component. | |
| 623 | |
| 624 The `development of this component was done by team-03 | |
| 625 <https://github.com/UMB-CS-682-Team-03/tracker>`_ of the | |
| 626 Spring 2024 CS682 graduate software engineering SDL capstone | |
| 627 class at the University of Massachusetts - Boston. Their | |
| 628 documentation is copied/adapted below. | |
| 629 | |
| 630 File Installation | |
| 631 ----------------- | |
| 632 | |
| 633 There are three files to install in your tracker. You can | |
| 634 copy them from the template directory for the classic | |
| 635 tracker. The location of the template file can be obtained | |
| 636 by running: ``roundup-admin templates`` and looking for the | |
| 637 path value of the classic template. If the path value is | |
| 638 ``/path/to/template``, copy:: | |
| 639 | |
| 640 /path/to/template/html/classhelper.js | |
| 641 /path/to/template/html/classhelper.css | |
| 642 /path/to/template/html/_generic.translation | |
| 643 | |
| 644 to your tracker's html directory. | |
| 645 | |
| 646 Wrapping the Classic Classhelper | |
| 647 -------------------------------- | |
| 648 | |
| 649 To allow your users to select items in the web interface | |
| 650 using the new classhelper, you should edit the template files | |
| 651 in the ``html/`` subdirectory of your tracker. Where you see | |
| 652 code like:: | |
| 653 | |
| 654 <th i18n:translate="">Superseder</th> | |
| 655 <td> | |
| 656 <span tal:replace="structure | |
| 657 python:context.superseder.field(showid=1,size=20)" /> | |
| 658 <span tal:condition="context/is_edit_ok" | |
| 659 tal:replace="structure | |
| 660 python:db.issue.classhelp('id,title', | |
| 661 property='superseder', pagesize=100)" /> | |
| 662 [...] | |
| 663 </td> | |
| 664 | |
| 665 change it to wrap the classhelp span like this:: | |
| 666 | |
| 667 <th i18n:translate="">Superseder</th> | |
| 668 <td> | |
| 669 <span tal:replace="structure | |
| 670 python:context.superseder.field(showid=1,size=20)" /> | |
| 671 <roundup-classhelper | |
| 672 data-popup-title="Superseder Classhelper - {itemDesignator}" | |
| 673 data-search-with="title,status,keyword[]-name"> | |
| 674 <span tal:condition="context/is_edit_ok" | |
| 675 tal:replace="structure | |
| 676 python:db.issue.classhelp('id,title', | |
| 677 property='superseder', pagesize=100)" /> | |
| 678 </roundup-classhelper> | |
| 679 [...] | |
| 680 </td> | |
| 681 | |
| 682 which displays a three part classhelper. | |
| 683 | |
| 684 1. the search pane includes a text search box for the `title` | |
| 685 and `status` properties and a dropdown for the keyword property, | |
| 686 sorted by name in descending order. | |
| 687 2. the selection pane will show 100 search results per page. | |
| 688 It also allows the user to move to the next or previous result | |
| 689 page. | |
| 690 3. the accumulator at the bottom shows all the selected items. It | |
| 691 also has buttons to accept the items or cancel the | |
| 692 classhelper, leaving the original page unchanged. | |
| 693 | |
| 694 Note that the user class is a little different because users without | |
| 695 an Admin role can't search for a user by Role. So we hide the Role | |
| 696 search element for non admin users. Starting with:: | |
| 697 | |
| 698 <th i18n:translate="">Nosy List</th> | |
| 699 <td> | |
| 700 <span tal:replace="structure context/nosy/field" /> | |
| 701 <span tal:condition="context/is_edit_ok" tal:replace="structure | |
| 702 python:db.user.classhelp('username,realname,address', | |
| 703 property='nosy', width='600'" /> | |
| 704 </td> | |
| 705 | |
| 706 wrap the classhelp span with ``<roundup-classhelper>`` like:: | |
| 707 | |
| 708 <th i18n:translate="">Nosy List</th> | |
| 709 <td> | |
| 710 <span tal:replace="structure context/nosy/field" /> | |
| 711 <roundup-classhelper tal:define="search string:name,phone,roles[]" | |
| 712 tal:attributes="data-search-with python:search | |
| 713 if request.user.hasRole('Admin') else | |
| 714 ','.join(search.split(',')[:-1])"> | |
| 715 <span tal:condition="context/is_edit_ok" tal:replace="structure | |
| 716 python:db.user.classhelp('username,realname,address', | |
| 717 property='nosy', width='600'" /> | |
| 718 </roundup-classhelper> | |
| 719 </td> | |
| 720 | |
| 721 The ``','.join(search.split(',')[:-1])`` removes the last element of | |
| 722 the search string (``roles[]``) if the user does not have the Admin | |
| 723 role. | |
| 724 | |
| 725 Loading the <roundup-classhelper> Script | |
| 726 ---------------------------------------- | |
| 727 | |
| 728 To make the ``<roundup-classhelper>`` wrappers work, you | |
| 729 have to load the classhelper.js script. Add the following | |
| 730 html script tag in the head section of page.html:: | |
| 731 | |
| 732 <script type="text/javascript" src="@@file/classhelper.js"> | |
| 733 </script> | |
| 734 | |
| 735 You can place it anywhere before ``</head>``. This will make | |
| 736 it available on all pages. | |
| 737 | |
| 738 The script will also work if it is loaded at the end of the | |
| 739 body. It can also be added to the ``more-javascript`` slot | |
| 740 in one of the templates (see ``user.item.html`` for an | |
| 741 example) if you don't want the overhead of loading the script | |
| 742 on every page. | |
| 743 | |
| 744 You may want to minimize and precompress the file. Using | |
| 745 dynamic compression, the file is 10k when compressed with | |
| 746 gzip (8k with brotli). If you minimize the file first with | |
| 747 `rjsmin <https://pypi.org/project/rjsmin/>`_ and then | |
| 748 compress it, it's about 6k. See the information about using | |
| 749 precompressed files in the :ref:`section on compression | |
| 750 <configuring-compression>`. | |
| 751 | |
| 752 <roundup-classhelper> configuration | |
| 753 ----------------------------------- | |
| 754 | |
| 755 There are two attributes used to configure the classhelper. | |
| 756 | |
| 757 data-popup-title: | |
| 758 * this attribute is optional. A reasonable default is | |
| 759 provided if it is missing. | |
| 760 * Adding ``data-popup-title`` changes the title of the popup | |
| 761 window with the value of the attribute. | |
| 762 * ``{itemDesignator}`` can be used inside the attribute value | |
| 763 to replace it with the current classhelper usage context. | |
| 764 E.G. ``data-popup-title="Nosy List Classhelper - {itemDesignator}"`` | |
| 765 will display the popup window title as ``Nosy List Classhelper - issue24`` | |
| 766 | |
| 767 data-search-with: | |
| 768 * this attribute is optional. If it is not set, a search | |
| 769 panel is not created to allow the user to search within | |
| 770 the class. | |
| 771 * Adding ``data-search-with`` specifies the fields that can | |
| 772 be used for searching. For example when invoking the | |
| 773 classhelper for the issue class, using | |
| 774 ``data-search-with="title,status,keyword"`` wil enable | |
| 775 three search fields. | |
| 776 * The search can be customized using the following syntax: | |
| 777 | |
| 778 * Adding ``[]`` at then end of a field (``"status[]"``) | |
| 779 will displays a dropdown for the "status" field | |
| 780 listing all the values the user can access. E.G.:: | |
| 781 | |
| 782 <roundup-classhelper | |
| 783 data-search-with="title,status[],keyword[]"> | |
| 784 <span tal:condition="context/is_edit_ok" | |
| 785 tal:replace="structure | |
| 786 python:db.issue.classhelp('id,title', | |
| 787 property='superseder', pagesize=100)" /> | |
| 788 </roundup-classhelper> | |
| 789 | |
| 790 will create a search pane with a text search for title | |
| 791 and dropdowns for status and keyword. | |
| 792 | |
| 793 * Adding a sort key after the ``[]`` allows you to | |
| 794 select the order of the elements in the dropdown. For | |
| 795 example ``keyword[]+name`` sorts the keyword | |
| 796 dropdown in ascending order by name. While | |
| 797 ``keyword[]-name`` sorts the keyword dropdown in | |
| 798 descending order by name. If the sort order is not | |
| 799 specified, the default order for the class is used. | |
| 800 | |
| 801 <roundup-classhelper> styling | |
| 802 ----------------------------- | |
| 803 | |
| 804 The roundup-classhelper component uses minimal styling so it | |
| 805 can blend in with most trackers. If you want to change the | |
| 806 styling, you can modify the classhelper.css file in the html | |
| 807 directory. Even though roundup-classhelper is a web | |
| 808 component, it doesn't use the shadow DOM. If you don't know | |
| 809 what this means, it just means that it's easy to style. | |
| 810 | |
| 811 Getting the web component to load changes to the css file is | |
| 812 a bit tricky. The browser caches the old file and you have | |
| 813 to resort to tricks to make it get a new copy of the file. | |
| 814 | |
| 815 One way to do this is to open to the ``classhelper.css`` | |
| 816 file in your browser and force refresh it. To do this: | |
| 817 | |
| 818 1. Open the home page for your Roundup issue tracker in a | |
| 819 web browser. | |
| 820 | |
| 821 2. In the address bar, append ``@@file/classhelper.css`` | |
| 822 to the end of your Roundup URL. For example, if your | |
| 823 Roundup URL is ``https://example.com/tracker/``, the | |
| 824 URL you should visit would be | |
| 825 ``https://example.com/tracker/@@file/classhelper.css``. | |
| 826 | |
| 827 3. This will open the ``classhelper.css`` file in your browser. | |
| 828 | |
| 829 4. Press ``Ctrl+Shift+R`` (on Windows and Linux) or | |
| 830 ``Cmd+Shift+R`` (on macOS). This triggers a hard | |
| 831 refresh of the page, which forces the browser to | |
| 832 reload the file and associated resources from the | |
| 833 server. | |
| 834 | |
| 835 This should resolve any issues caused by cached or outdated | |
| 836 files. It is possible that you have to open devtools and set | |
| 837 the disable cache option in the network panel in extreme | |
| 838 cases. | |
| 839 | |
| 840 Also during development, you might want to `set a very low | |
| 841 cache time | |
| 842 <customizing.html#changing-cache-control-headers>`_ for | |
| 843 classhelper.css using something like:: | |
| 844 | |
| 845 Client.Cache_Control['classhelper.css'] = "public, max-age=10" | |
| 846 | |
| 847 | |
| 848 Translations | |
| 849 ------------ | |
| 850 | |
| 851 To set up translations for the <roundup-classhelper> | |
| 852 component, follow these steps. | |
| 853 | |
| 854 1. Create a ``messages.pot`` file by running | |
| 855 ``roundup-gettext <tracker_home_directory>``. This | |
| 856 creates ``locale/messages.pot`` in your tracker's home | |
| 857 directory. It extracts all translatable strings from | |
| 858 your tracker. We will use it as a base template for the | |
| 859 new strings you want to translate. | |
| 860 2. See if you already have a ``.po`` translation file for | |
| 861 your language in the tracker's locale/ directory. If you | |
| 862 don't, copy ``messages.pot`` to a .po file for the | |
| 863 language you want to translate. For example German | |
| 864 would be at ``de.po`` English would be at ``en.po`` | |
| 865 (for example if you want to change the ``apply`` button | |
| 866 to say ``Do It``. | |
| 867 | |
| 868 3. Edit the new .po file. After the header, add the | |
| 869 translation entries for the <roundup-classhelper> | |
| 870 component. For example `next` and `submit` are | |
| 871 displayed in English when the rest of the interface is | |
| 872 in German. Add:: | |
| 873 | |
| 874 msgid "submit" | |
| 875 msgstr "gehen" | |
| 876 | |
| 877 msgid "next" | |
| 878 msgstr "nächste" | |
| 879 | |
| 880 msgid "name" | |
| 881 msgstr "name" | |
| 882 | |
| 883 Note: the value for `msgid` is case sensitive. You can | |
| 884 see the msgid for static strings by looking for | |
| 885 ``CLASSHELPER_TRANSLATION_KEYWORDS`` in classhelper.js. | |
| 886 | |
| 887 4. Save the .po file. | |
| 888 | |
| 889 5. Restart your Roundup instance. | |
| 890 | |
| 891 This should display the missing translations, for more | |
| 892 details refer to the `translation (i18n) section of the | |
| 893 developers documentation | |
| 894 <developers.html#extracting-translatable-messages>`_. | |
| 895 | |
| 896 The default title used for read only popups can be changed by using | |
| 897 the translation mechanism. Use the following:: | |
| 898 | |
| 899 msgid "Info on {className} - {itemDesignator} - Classhelper" | |
| 900 msgstr "{itemDesignator} - info on {className}" | |
| 901 | |
| 902 in ``en.po`` to reformat the title for the English language. Note that | |
| 903 ``{classname}`` is only supported in the default title. | |
| 904 | |
| 905 In addition to the default template you can translate a title set | |
| 906 using ``data-popup-title`` by matching the template as the msgid. | |
| 907 Using an example above:: | |
| 908 | |
| 909 msgid "Nosy List Classhelper - {itemDesignator}" | |
| 910 msgstr "Nosy List Klassenhelfer - {itemDesignator}" | |
| 911 | |
| 912 placed in ``de.po`` will translate the title into German. | |
| 913 | |
| 914 Troubleshooting | |
| 915 --------------- | |
| 916 | |
| 917 The roundup-classhelper will fallback to using the classic | |
| 918 classhelper if: | |
| 919 | |
| 920 * the user doesn't have REST access | |
| 921 * the browser doesn't support web components | |
| 922 | |
| 923 It will display an alert modal dialog to the user before triggering | |
| 924 the classic classhelper as a fallback. A detailed error will be | |
| 925 printed to the browser console. The console is visible in devtools and | |
| 926 can be opened by pressing the ``F12`` key. | |
| 927 | |
| 928 You can disable the classhelper on a per URL basis by adding | |
| 929 ``#classhelper-wc-toggle`` to the end of the URL. This will prevent | |
| 930 the web component from starting up. | |
| 931 | |
| 932 Also you can set ``DISABLE_CLASSHELP = true`` at the top of | |
| 933 classhelper.js to disable the classhelper without having to make any | |
| 934 changes to your templates. | |
| 935 | |
| 936 Advanced Configuration | |
| 937 ---------------------- | |
| 938 | |
| 939 The classhelper.js file has a few tweakable options for use | |
| 940 by advanced users. The endpoint for the roles list requires | |
| 941 the user to have Admin rights. You can add your own roles | |
| 942 endpoint with a different authorization mechanism. The | |
| 943 following code can be added to your tracker's interfaces.py. | |
| 944 You can create this file if it doesn't exist. This code | |
| 945 creates a new REST endpoint at ``/rest/roles``:: | |
| 946 | |
| 947 from roundup.rest import Routing, RestfulInstance, _data_decorator | |
| 948 | |
| 949 class RestfulInstance: | |
| 950 | |
| 951 @Routing.route("/roles", 'GET') | |
| 952 @_data_decorator | |
| 953 def get_roles(self, input): | |
| 954 """Return all defined roles. The User class property | |
| 955 roles is a string but simulate it as a MultiLink | |
| 956 to an actual Roles class. | |
| 957 """ | |
| 958 return 200, {"collection": | |
| 959 [{"id": rolename,"name": rolename} | |
| 960 for rolename in list(self.db.security.role.keys())]} | |
| 961 | |
| 962 | |
| 963 See the `REST documentation <rest.html>`_ for details on | |
| 964 using ``interfaces.py`` to add new REST endpoints. | |
| 965 | |
| 966 The code above allows any user with REST access to see all | |
| 967 the roles defined in the tracker. | |
| 968 | |
| 969 To make classhelper.js use this new endpoint, look for:: | |
| 970 | |
| 971 | |
| 972 const ALTERNATIVE_DROPDOWN_PATHNAMES = { | |
| 973 "roles": "/rest/data/user/roles" | |
| 974 } | |
| 975 | |
| 976 and change it to:: | |
| 977 | |
| 978 const ALTERNATIVE_DROPDOWN_PATHNAMES = { | |
| 979 "roles": "/rest/roles" | |
| 980 } | |
| 981 | |
| 982 Users may have to perform a hard reload to cache this change | |
| 983 on their system. | |
| 984 | |
| 490 Configuring native-fts Full Text Search | 985 Configuring native-fts Full Text Search |
| 491 ======================================= | 986 ======================================= |
| 492 | 987 |
| 493 Roundup release 2.2.0 supports database-native full text search. | 988 Roundup release 2.2.0 supports database-native full text search. |
| 494 SQLite (minimum version 3.9.0) with FTS5 and PostgreSQL (minimum | 989 SQLite (minimum version 3.9.0) with FTS5 and PostgreSQL (minimum |
| 553 `Parsing Queries`_ under websearch_to_tsquery. This is the default. | 1048 `Parsing Queries`_ under websearch_to_tsquery. This is the default. |
| 554 | 1049 |
| 555 2. tsquery - described at the beginning of `Parsing Queries`_ with | 1050 2. tsquery - described at the beginning of `Parsing Queries`_ with |
| 556 to_tsquery. It is enabled by starting the search phrase with ``ts:``. | 1051 to_tsquery. It is enabled by starting the search phrase with ``ts:``. |
| 557 | 1052 |
| 558 .. _Parsing Queries: \ | 1053 .. _Parsing Queries: |
| 559 https://www.postgresql.org/docs/14/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES | 1054 https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES |
| 560 | 1055 |
| 561 Websearch provides a more natural style of search and supports: | 1056 Websearch provides a more natural style of search and supports: |
| 562 | 1057 |
| 563 * plain word search (stemmed in most cases) | 1058 * plain word search (stemmed in most cases) |
| 564 * phrase search with terms enclosed in quotes (``"``) | 1059 * phrase search with terms enclosed in quotes (``"``) |
| 600 requires reindexing. | 1095 requires reindexing. |
| 601 | 1096 |
| 602 The `configuration list can be obtained using using psql's`_ | 1097 The `configuration list can be obtained using using psql's`_ |
| 603 ``\dF`` command. | 1098 ``\dF`` command. |
| 604 | 1099 |
| 605 .. _configuration list can be obtained using using psql's: \ | 1100 .. _configuration list can be obtained using using psql's: |
| 606 https://www.postgresql.org/docs/current/textsearch-psql.html | 1101 https://www.postgresql.org/docs/current/textsearch-psql.html |
| 607 | 1102 |
| 608 Roundup includes a hardcoded list for all languages supported by | 1103 Roundup includes a hardcoded list for all languages supported by |
| 609 PostgreSQL 14.1. The list includes 5 custom "languages" | 1104 PostgreSQL 14.1. The list includes 5 custom "languages" |
| 610 ``custom1`` ... ``custom5`` to allow you to set up your `own textsearch | 1105 ``custom1`` ... ``custom5`` to allow you to set up your `own textsearch |
| 611 configuration`_ using one of the custom names. Depending on your | 1106 configuration`_ using one of the custom names. Depending on your |
| 612 PostgreSQL version, we may allow an invalid language to be configured. | 1107 PostgreSQL version, we may allow an invalid language to be configured. |
| 613 You will see an error about ``text search configuration ... does not | 1108 You will see an error about ``text search configuration ... does not |
| 614 exist``. | 1109 exist``. |
| 615 | 1110 |
| 616 .. _own textsearch configuration: \ | 1111 .. _own textsearch configuration: |
| 617 https://www.postgresql.org/docs/14/textsearch-configuration.html | 1112 https://www.postgresql.org/docs/current/textsearch-configuration.html |
| 618 | 1113 |
| 619 It may be possible to append to this list using the tracker's | 1114 It may be possible to append to this list using the tracker's |
| 620 interfaces.py. For details, see ``test/test_indexer.py`` in the | 1115 interfaces.py. For details, see ``test/test_indexer.py`` in the |
| 621 roundup distribution and search for ``valid_langs``. If you succeed | 1116 roundup distribution and search for ``valid_langs``. If you succeed |
| 622 please email roundup-users AT lists.sourceforge.net with a description | 1117 please email roundup-users AT lists.sourceforge.net with a description |
| 688 sqlite databases for storing the session and otk data. | 1183 sqlite databases for storing the session and otk data. |
| 689 | 1184 |
| 690 The following table shows which primary databases support | 1185 The following table shows which primary databases support |
| 691 different session database backends: | 1186 different session database backends: |
| 692 | 1187 |
| 693 .. table:: D - default if unconfigured, X - compatible choice | 1188 .. table:: D - default if unconfigured, + - compatible choice |
| 694 :class: captionbelow | 1189 :class: captionbelow |
| 695 | 1190 |
| 696 +---------------+--------+--------+-------+-------+------------+ | 1191 +---------------+--------+--------+-------+-------+------------+ |
| 697 | | session db | | 1192 | | session db | |
| 698 +---------------+--------+--------+-------+-------+------------+ | 1193 +---------------+--------+--------+-------+-------+------------+ |
| 699 |primary db | anydbm | sqlite | redis | mysql | postgresql | | 1194 |primary db | anydbm | sqlite | redis | mysql | postgresql | |
| 700 +===============+========+========+=======+=======+============+ | 1195 +===============+========+========+=======+=======+============+ |
| 701 |anydbm | D | | X | | | | 1196 |anydbm | D | | \+ | | | |
| 702 +---------------+--------+--------+-------+-------+------------+ | 1197 +---------------+--------+--------+-------+-------+------------+ |
| 703 |sqlite | X | D | X | | | | 1198 |sqlite | \+ | D | \+ | | | |
| 704 +---------------+--------+--------+-------+-------+------------+ | 1199 +---------------+--------+--------+-------+-------+------------+ |
| 705 |mysql | | | | D | | | 1200 |mysql | | | | D | | |
| 706 +---------------+--------+--------+-------+-------+------------+ | 1201 +---------------+--------+--------+-------+-------+------------+ |
| 707 |postgresql | | | | | D | | 1202 |postgresql | | | | | D | |
| 708 +---------------+--------+--------+-------+-------+------------+ | 1203 +---------------+--------+--------+-------+-------+------------+ |
| 749 where you specify the file that can be used to validate the | 1244 where you specify the file that can be used to validate the |
| 750 SSL certificate. `Securing Redis`_ has more details. | 1245 SSL certificate. `Securing Redis`_ has more details. |
| 751 | 1246 |
| 752 .. _Redis: https://redis.io | 1247 .. _Redis: https://redis.io |
| 753 .. _redis-py: https://pypi.org/project/redis/ | 1248 .. _redis-py: https://pypi.org/project/redis/ |
| 754 .. _Securing Redis: https://redis.io/docs/manual/security/ | 1249 .. _Securing Redis: https://redis.io/docs/latest/operate/oss_and_stack/management/security/ |
| 755 | 1250 |
| 756 | 1251 |
| 757 Users and Security | 1252 Users and Security |
| 758 ================== | 1253 ================== |
| 759 | 1254 |
| 912 pip3 install pytest | 1407 pip3 install pytest |
| 913 python3 -m pytest test/ | 1408 python3 -m pytest test/ |
| 914 | 1409 |
| 915 2. If you're using an RDBMS backend, make a backup of its contents now. | 1410 2. If you're using an RDBMS backend, make a backup of its contents now. |
| 916 3. Make a backup of the tracker home itself. | 1411 3. Make a backup of the tracker home itself. |
| 917 4. Stop the tracker web and email frontends. | 1412 4. Stop the tracker web, email frontends and any scheduled |
| 918 5. Install the new version of the software:: | 1413 (cron) jobs that access the tracker. |
| 919 | 1414 5. Install the new version of the software in a new virtual |
| 920 python setup.py install | 1415 environment:: |
| 1416 | |
| 1417 python3 -m venv /path/to/env | |
| 1418 . /path/to/env/bin/activate | |
| 1419 python3 pip install roundup | |
| 921 | 1420 |
| 922 6. Follow the steps in the `upgrading documentation`_ for all the | 1421 6. Follow the steps in the `upgrading documentation`_ for all the |
| 923 versions between your original version and the new version. | 1422 versions between your original version and the new version. |
| 924 | 1423 |
| 925 Usually you should run `roundup_admin -i <tracker_home> migrate` | 1424 Usually you should run `roundup_admin -i <tracker_home> migrate` |
| 1228 single: roundup-admin; usage | 1727 single: roundup-admin; usage |
| 1229 single: roundup-admin; data formats | 1728 single: roundup-admin; data formats |
| 1230 single: roundup-admin; man page reference | 1729 single: roundup-admin; man page reference |
| 1231 pair: roundup-admin; designator | 1730 pair: roundup-admin; designator |
| 1232 | 1731 |
| 1732 .. _`roundup-admin templates`: | |
| 1733 | |
| 1233 Using roundup-admin | 1734 Using roundup-admin |
| 1234 =================== | 1735 =================== |
| 1235 | 1736 |
| 1236 Part of the installation includes a man page for roundup-admin. Ypu | 1737 Part of the installation includes a man page for roundup-admin. Ypu |
| 1237 should be able to read it using ``man roundup-admin``. As shown above, | 1738 should be able to read it using ``man roundup-admin``. As shown above, |
| 1243 * install and initialize a new tracker | 1744 * install and initialize a new tracker |
| 1244 * export/import tracker data for migrating between backends | 1745 * export/import tracker data for migrating between backends |
| 1245 * creating a new user from the command line | 1746 * creating a new user from the command line |
| 1246 * list/find users in the tracker | 1747 * list/find users in the tracker |
| 1247 | 1748 |
| 1248 The basic usage is:: | 1749 The basic usage is: |
| 1750 | |
| 1751 .. code-block:: text | |
| 1249 | 1752 |
| 1250 Usage: roundup-admin [options] [<command> <arguments>] | 1753 Usage: roundup-admin [options] [<command> <arguments>] |
| 1251 | 1754 |
| 1252 Options: | 1755 Options: |
| 1253 -i instance home -- specify the issue tracker "home directory" to administer | 1756 -i instance home -- specify the issue tracker "home directory" to administer |
| 1280 filter classname propname=value ... | 1783 filter classname propname=value ... |
| 1281 find classname propname=value ... | 1784 find classname propname=value ... |
| 1282 genconfig <filename> | 1785 genconfig <filename> |
| 1283 get property designator[,designator]* | 1786 get property designator[,designator]* |
| 1284 help topic | 1787 help topic |
| 1285 history designator [skipquiet] | 1788 history designator [skipquiet] [raw] |
| 1286 import import_dir | 1789 import import_dir |
| 1287 importtables export_dir | 1790 importtables export_dir |
| 1288 initialise [adminpw] | 1791 initialise [adminpw] |
| 1289 install [template [backend [key=val[,key=val]]]] | 1792 install [template [backend [key=val[,key=val]]]] |
| 1290 list classname [property] | 1793 list classname [property] |
| 1405 | 1908 |
| 1406 (for more details see https://issues.roundup-tracker.org/issue2550789.) | 1909 (for more details see https://issues.roundup-tracker.org/issue2550789.) |
| 1407 | 1910 |
| 1408 .. index:: ! roundup-admin; usage in scripts | 1911 .. index:: ! roundup-admin; usage in scripts |
| 1409 | 1912 |
| 1913 Using Interactively | |
| 1914 ------------------- | |
| 1915 | |
| 1916 You can edit the command line using left/right arrow keys to move the | |
| 1917 cursor. Using the up/down arrow keys moves among commands. It will | |
| 1918 save your commands between roundup-admin sessions. This is supported | |
| 1919 on Unix/Linux and Mac's. On windows you should install the pyreadline3 | |
| 1920 package (see `installation documentation`_). | |
| 1921 | |
| 1922 Using the ``history_length`` pragma you can set the saved history size | |
| 1923 for just one session. | |
| 1924 | |
| 1925 You can add initialization commands to ``~/.roundup_admin_rlrc``. It | |
| 1926 will be loaded when roundup-admin starts. This is the mechanism to | |
| 1927 persistently set the number of history commands, change editing modes | |
| 1928 (Vi vs. Emacs). Note that redefining the history file will not work. | |
| 1929 | |
| 1930 If you are using GNU readline, ``set history-size 10``. If your | |
| 1931 installation uses libedit (macs), it should be possible to | |
| 1932 persistently set the history size using ``history size | |
| 1933 10``. Pyreadline3 can set history length using | |
| 1934 ``history_length(10)``. See the documentation for example syntax: | |
| 1935 https://pythonhosted.org/pyreadline/usage.html#configuration-file. | |
| 1936 | |
| 1937 History is saved to the file ``.roundup_admin_history`` in your home | |
| 1938 directory (for windows usually ``\Users\<username>``. | |
| 1939 | |
| 1410 Using with the shell | 1940 Using with the shell |
| 1411 -------------------- | 1941 -------------------- |
| 1412 | 1942 |
| 1413 With version 0.6.0 or newer of roundup (which introduced support for | 1943 With version 0.6.0 or newer of roundup (which introduced support for |
| 1414 multiple designators to display and the -d, -S and -s flags): | 1944 multiple designators to display and the -d, -S and -s flags): |
| 1550 However under certain circumstances, Roundup can skip an id | 2080 However under certain circumstances, Roundup can skip an id |
| 1551 number. This can lead to a difference of more than 1 between the | 2081 number. This can lead to a difference of more than 1 between the |
| 1552 Importing and setting numbers. It's not a problem. However setting | 2082 Importing and setting numbers. It's not a problem. However setting |
| 1553 can (and must) always be higher than the Importing number. | 2083 can (and must) always be higher than the Importing number. |
| 1554 | 2084 |
| 2085 Interactive Help | |
| 2086 ---------------- | |
| 2087 | |
| 2088 The help command produces the following output for all the commands. | |
| 2089 | |
| 2090 | |
| 2091 .. raw:: html | |
| 2092 :file: admin_help.html | |
| 1555 | 2093 |
| 1556 .. _`customisation documentation`: customizing.html | 2094 .. _`customisation documentation`: customizing.html |
| 1557 .. _`reference documentation`: reference.html | 2095 .. _`reference documentation`: reference.html |
| 1558 .. _`upgrading documentation`: upgrading.html | 2096 .. _`upgrading documentation`: upgrading.html |
| 1559 .. _`installation documentation`: installation.html | 2097 .. _`installation documentation`: installation.html |
