libreqda 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. CHANGELOG.md +481 -0
  2. libreqda-1.2.0.dist-info/LICENSE.txt +28 -0
  3. libreqda-1.2.0.dist-info/METADATA +131 -0
  4. libreqda-1.2.0.dist-info/RECORD +255 -0
  5. libreqda-1.2.0.dist-info/WHEEL +4 -0
  6. libreqda-1.2.0.dist-info/entry_points.txt +4 -0
  7. scripts/babelrc +8 -0
  8. scripts/dist/LICENSE.txt +33 -0
  9. scripts/dist/RELEASE.txt +23 -0
  10. scripts/dist/macos/libreqda.icns +0 -0
  11. scripts/dist/macos/macos.spec +43 -0
  12. scripts/dist/macos/plist.patch +10 -0
  13. scripts/dist/macos/taguette.icns +0 -0
  14. scripts/dist/macos/taguette_console_wrapper +4 -0
  15. scripts/dist/macos.sh +32 -0
  16. scripts/dist/pyinstaller_entrypoint.py +19 -0
  17. scripts/dist/windows/windows-installer.iss +51 -0
  18. scripts/dist/windows/windows.spec +39 -0
  19. scripts/edugain/worker_update_edugain_idps.py +89 -0
  20. scripts/edugain/worker_update_edugain_idps.sh +9 -0
  21. scripts/make_password_reset_link.py +56 -0
  22. scripts/make_theme.py +99 -0
  23. scripts/new_db_revision.sh +8 -0
  24. scripts/register_user.py +76 -0
  25. scripts/update_pot.sh +33 -0
  26. scripts/update_translations.sh +17 -0
  27. scripts/validate.py +140 -0
  28. scripts/wipe_and_disable_user.py +90 -0
  29. taguette/__init__.py +13 -0
  30. taguette/access_control.py +145 -0
  31. taguette/convert.py +623 -0
  32. taguette/database/__init__.py +177 -0
  33. taguette/database/base.py +9 -0
  34. taguette/database/copy.py +433 -0
  35. taguette/database/models.py +920 -0
  36. taguette/demodata.py +688 -0
  37. taguette/export.py +500 -0
  38. taguette/extract.py +287 -0
  39. taguette/import_codebook.py +110 -0
  40. taguette/l10n/de_DE/LC_MESSAGES/taguette_javascript.mo +0 -0
  41. taguette/l10n/de_DE/LC_MESSAGES/taguette_main.mo +0 -0
  42. taguette/l10n/el_EL/LC_MESSAGES/taguette_javascript.mo +0 -0
  43. taguette/l10n/el_GR/LC_MESSAGES/taguette_javascript.mo +0 -0
  44. taguette/l10n/el_GR/LC_MESSAGES/taguette_main.mo +0 -0
  45. taguette/l10n/en_US/LC_MESSAGES/taguette_javascript.mo +0 -0
  46. taguette/l10n/en_US/LC_MESSAGES/taguette_main.mo +0 -0
  47. taguette/l10n/es_ES/LC_MESSAGES/taguette_javascript.mo +0 -0
  48. taguette/l10n/es_ES/LC_MESSAGES/taguette_main.mo +0 -0
  49. taguette/l10n/fr_FR/LC_MESSAGES/taguette_javascript.mo +0 -0
  50. taguette/l10n/fr_FR/LC_MESSAGES/taguette_main.mo +0 -0
  51. taguette/l10n/id_ID/LC_MESSAGES/taguette_javascript.mo +0 -0
  52. taguette/l10n/id_ID/LC_MESSAGES/taguette_main.mo +0 -0
  53. taguette/l10n/it_IT/LC_MESSAGES/taguette_javascript.mo +0 -0
  54. taguette/l10n/it_IT/LC_MESSAGES/taguette_main.mo +0 -0
  55. taguette/l10n/nl_NL/LC_MESSAGES/taguette_javascript.mo +0 -0
  56. taguette/l10n/nl_NL/LC_MESSAGES/taguette_main.mo +0 -0
  57. taguette/l10n/pt_PT/LC_MESSAGES/taguette_javascript.mo +0 -0
  58. taguette/l10n/pt_PT/LC_MESSAGES/taguette_main.mo +0 -0
  59. taguette/l10n/sv_SE/LC_MESSAGES/taguette_javascript.mo +0 -0
  60. taguette/l10n/sv_SE/LC_MESSAGES/taguette_main.mo +0 -0
  61. taguette/loki_handler.py +107 -0
  62. taguette/main.py +649 -0
  63. taguette/migrations/env.py +72 -0
  64. taguette/migrations/script.py.mako +24 -0
  65. taguette/migrations/versions/09c662cd9483_make_documents_filename_not_null.py +39 -0
  66. taguette/migrations/versions/1b29e68900c1_add_user_roles.py +55 -0
  67. taguette/migrations/versions/1b96a606551d_update_user_to_have_a_display_name.py +34 -0
  68. taguette/migrations/versions/2467bab602b7_add_color_column_to_tags_table.py +33 -0
  69. taguette/migrations/versions/382db36f20f4_add_user_disabled.py +66 -0
  70. taguette/migrations/versions/43d6c240309d_remove_document_groups.py +81 -0
  71. taguette/migrations/versions/447d636f72c5_cascades.py +40 -0
  72. taguette/migrations/versions/491de2dc7cd7_add_indexes.py +51 -0
  73. taguette/migrations/versions/4c55b3b3ff30_user_login_length.py +58 -0
  74. taguette/migrations/versions/61a4abcc8769_user_with_parent_for_permissions.py +40 -0
  75. taguette/migrations/versions/6489b5f9cfb5_increase_password_hash_size.py +34 -0
  76. taguette/migrations/versions/679f625e6e6a_fix_unique_tags.py +59 -0
  77. taguette/migrations/versions/77c4543a5b9b_add_parent_id_column_to_collection_table.py +41 -0
  78. taguette/migrations/versions/7c94b7a31e69_add_is_archived_column_to_projets.py +39 -0
  79. taguette/migrations/versions/7f56de97e3c8_change_edugain_displayname_length.py +42 -0
  80. taguette/migrations/versions/807c0cc0ecf6_remove_invalid_hltags.py +43 -0
  81. taguette/migrations/versions/80b1cc9d4c22_add_user_email.py +35 -0
  82. taguette/migrations/versions/91ade71ccf4d_add_user_language.py +28 -0
  83. taguette/migrations/versions/955db2e523d1_add_collections_tables_and_associations_.py +60 -0
  84. taguette/migrations/versions/a1b2c3d4e5f6_add_user_hide_code_counts.py +28 -0
  85. taguette/migrations/versions/b23f3b7a638e_add_user_password_change_date.py +36 -0
  86. taguette/migrations/versions/b7c1e2d3f4a5_add_project_compact_layout.py +28 -0
  87. taguette/migrations/versions/bc8e0e0677e9_rename_command_fields.py +126 -0
  88. taguette/migrations/versions/bce44849c2f2_lowercase_logins.py +96 -0
  89. taguette/migrations/versions/ca076140922f_update_user_for_external_logins.py +32 -0
  90. taguette/migrations/versions/d86c45d7aa8a_add_document_direction.py +127 -0
  91. taguette/migrations/versions/db5e31a0233d_remove_blank_highlights.py +88 -0
  92. taguette/migrations/versions/dd37d9027a24_unique_tags.py +68 -0
  93. taguette/migrations/versions/de678cef2a94_add_project_id_to_collections_and_.py +41 -0
  94. taguette/migrations/versions/e459991d960e_add_table_edugainidps.py +39 -0
  95. taguette/migrations/versions/e4cf92942271_add_index.py +29 -0
  96. taguette/migrations/versions/e4e090a5b511_remove_commands_document_id_fk.py +38 -0
  97. taguette/migrations/versions/e9a9f1c77971_add_favorite_column_to_document.py +32 -0
  98. taguette/migrations/versions/ecb4065de575_don_t_use_server_default_now_timezone_.py +44 -0
  99. taguette/migrations/versions/fbdc29566f62_edugain_fix_field_type.py +42 -0
  100. taguette/migrations/versions/fdc6eb54b034_add_parent_id_column_to_tags_table.py +31 -0
  101. taguette/static/README.md +34 -0
  102. taguette/static/alt_templates/.gitkeep +0 -0
  103. taguette/static/assets/test/logos/monsters-university-logo.png +0 -0
  104. taguette/static/css/custom.css +1696 -0
  105. taguette/static/css/fa-all.min.css +5 -0
  106. taguette/static/css/themed.css +3 -0
  107. taguette/static/css/themed.css.map +25 -0
  108. taguette/static/css/vendor/bootstrap-treeview.min.css +1 -0
  109. taguette/static/css/vendor/bootstrap.min.css +6 -0
  110. taguette/static/css/vendor/font/context-menu-icons.eot +0 -0
  111. taguette/static/css/vendor/font/context-menu-icons.ttf +0 -0
  112. taguette/static/css/vendor/font/context-menu-icons.woff +0 -0
  113. taguette/static/css/vendor/font/context-menu-icons.woff2 +0 -0
  114. taguette/static/css/vendor/hierarchy-select.min.css +1 -0
  115. taguette/static/css/vendor/introjs.min.css +2 -0
  116. taguette/static/css/vendor/jquery.contextMenu.min.css +16 -0
  117. taguette/static/favicon.ico +0 -0
  118. taguette/static/js/codebook-viz.js +428 -0
  119. taguette/static/js/collection/context-menu.js +180 -0
  120. taguette/static/js/collection/hierarchy-select.js +71 -0
  121. taguette/static/js/collection/manager.js +217 -0
  122. taguette/static/js/document/context-menu.js +92 -0
  123. taguette/static/js/document/custom-select.js +82 -0
  124. taguette/static/js/document/manager.js +943 -0
  125. taguette/static/js/document/treeview.js +593 -0
  126. taguette/static/js/highlight/README.md +18 -0
  127. taguette/static/js/highlight/crud.js +119 -0
  128. taguette/static/js/highlight/extracts.js +497 -0
  129. taguette/static/js/highlight/loader.js +109 -0
  130. taguette/static/js/highlight/renderer.js +405 -0
  131. taguette/static/js/login.js +15 -0
  132. taguette/static/js/permissions.js +153 -0
  133. taguette/static/js/polling-event.js +145 -0
  134. taguette/static/js/project/intro.js +524 -0
  135. taguette/static/js/project/manager.js +0 -0
  136. taguette/static/js/project/search.js +124 -0
  137. taguette/static/js/tag/context-menu.js +133 -0
  138. taguette/static/js/tag/explore-codes.js +400 -0
  139. taguette/static/js/tag/hierarchy-select.js +69 -0
  140. taguette/static/js/tag/manager.js +545 -0
  141. taguette/static/js/tag/treeview.js +613 -0
  142. taguette/static/js/taguette.js +1790 -0
  143. taguette/static/js/utilities.js +714 -0
  144. taguette/static/js/vendor/bootstrap-treeview-custum.js +421 -0
  145. taguette/static/js/vendor/bootstrap-treeview-custum.min.js +1 -0
  146. taguette/static/js/vendor/bootstrap-treeview-default.min.js +1 -0
  147. taguette/static/js/vendor/bootstrap.bundle.min.js +7 -0
  148. taguette/static/js/vendor/bootstrap.bundle.min.js.map +1 -0
  149. taguette/static/js/vendor/hierarchy-select.min.js +1 -0
  150. taguette/static/js/vendor/introjs.min.js +17 -0
  151. taguette/static/js/vendor/jquery-3.7.1.js +2 -0
  152. taguette/static/js/vendor/jquery.contextMenu.min.js +2 -0
  153. taguette/static/libreqda.ico +0 -0
  154. taguette/static/logo-h.png +0 -0
  155. taguette/static/logo.png +0 -0
  156. taguette/static/logo_blanc.png +0 -0
  157. taguette/static/missing.png +0 -0
  158. taguette/static/robots.txt +2 -0
  159. taguette/static/scss/components/_badge.scss +42 -0
  160. taguette/static/scss/components/_button.scss +169 -0
  161. taguette/static/scss/components/_checkbox.scss +40 -0
  162. taguette/static/scss/components/_context_menu.scss +28 -0
  163. taguette/static/scss/components/_custom_select.scss +150 -0
  164. taguette/static/scss/components/_dropdown.scss +43 -0
  165. taguette/static/scss/components/_form.scss +54 -0
  166. taguette/static/scss/components/_modal.scss +57 -0
  167. taguette/static/scss/components/_project_card.scss +163 -0
  168. taguette/static/scss/components/_tag.scss +308 -0
  169. taguette/static/scss/components/_treeview.scss +76 -0
  170. taguette/static/scss/custom.scss +809 -0
  171. taguette/static/scss/layout/_credits.scss +62 -0
  172. taguette/static/scss/layout/_footer.scss +6 -0
  173. taguette/static/scss/layout/_navigation.scss +165 -0
  174. taguette/static/scss/layout/_sidebar.scss +37 -0
  175. taguette/static/scss/themes/default/_variables.scss +103 -0
  176. taguette/static/scss/themes/default/theme.css +3 -0
  177. taguette/static/scss/themes/default/theme.css.map +26 -0
  178. taguette/static/scss/themes/default/theme.scss +4 -0
  179. taguette/static/webfonts/BadScript-OFL.txt +94 -0
  180. taguette/static/webfonts/BadScript.ttf +0 -0
  181. taguette/static/webfonts/Montserrat-Light.ttf +0 -0
  182. taguette/static/webfonts/Montserrat-OFL.txt +93 -0
  183. taguette/static/webfonts/fa-brands-400.eot +0 -0
  184. taguette/static/webfonts/fa-brands-400.svg +1184 -0
  185. taguette/static/webfonts/fa-brands-400.ttf +0 -0
  186. taguette/static/webfonts/fa-brands-400.woff +0 -0
  187. taguette/static/webfonts/fa-brands-400.woff2 +0 -0
  188. taguette/static/webfonts/fa-regular-400.eot +0 -0
  189. taguette/static/webfonts/fa-regular-400.svg +467 -0
  190. taguette/static/webfonts/fa-regular-400.ttf +0 -0
  191. taguette/static/webfonts/fa-regular-400.woff +0 -0
  192. taguette/static/webfonts/fa-regular-400.woff2 +0 -0
  193. taguette/static/webfonts/fa-solid-900.eot +0 -0
  194. taguette/static/webfonts/fa-solid-900.svg +2618 -0
  195. taguette/static/webfonts/fa-solid-900.ttf +0 -0
  196. taguette/static/webfonts/fa-solid-900.woff +0 -0
  197. taguette/static/webfonts/fa-solid-900.woff2 +0 -0
  198. taguette/templates/alt_templates/.gitkeep +0 -0
  199. taguette/templates/archived_projects.html +50 -0
  200. taguette/templates/auth/account.html +74 -0
  201. taguette/templates/auth/cilogon.login.html +89 -0
  202. taguette/templates/auth/email_reset_password.html +5 -0
  203. taguette/templates/auth/external_account.html +60 -0
  204. taguette/templates/auth/login.html +144 -0
  205. taguette/templates/auth/reset_password.html +27 -0
  206. taguette/templates/auth/token_needed.html +7 -0
  207. taguette/templates/base.html +209 -0
  208. taguette/templates/bug_report.html +158 -0
  209. taguette/templates/cookies_prompt.html +15 -0
  210. taguette/templates/downloads.html +127 -0
  211. taguette/templates/email_bug_report.txt +9 -0
  212. taguette/templates/email_reset_password.txt +5 -0
  213. taguette/templates/error.html +8 -0
  214. taguette/templates/export_codebook.html +20 -0
  215. taguette/templates/export_document.html +20 -0
  216. taguette/templates/export_highlights.html +47 -0
  217. taguette/templates/index.html +347 -0
  218. taguette/templates/maintenance.html +8 -0
  219. taguette/templates/messages.js +74 -0
  220. taguette/templates/modal/change_document.html +68 -0
  221. taguette/templates/modal/create_collection.html +100 -0
  222. taguette/templates/modal/create_document.html +84 -0
  223. taguette/templates/modal/create_highlight.html +38 -0
  224. taguette/templates/modal/create_tag.html +73 -0
  225. taguette/templates/modal/explore_codes.html +58 -0
  226. taguette/templates/modal/manage_collaborators.html +44 -0
  227. taguette/templates/modal/merge_tag.html +49 -0
  228. taguette/templates/new_password.html +32 -0
  229. taguette/templates/partials/alert.html +16 -0
  230. taguette/templates/partials/codebook_viz_modal.html +34 -0
  231. taguette/templates/partials/confirm.html +17 -0
  232. taguette/templates/partials/document_toolbar.html +37 -0
  233. taguette/templates/partials/hlinfo.html +13 -0
  234. taguette/templates/partials/project_card.html +44 -0
  235. taguette/templates/partials/project_card_archived.html +41 -0
  236. taguette/templates/partials/project_sidebar.html +99 -0
  237. taguette/templates/partials/spinner.html +11 -0
  238. taguette/templates/permissions.html +54 -0
  239. taguette/templates/project.html +288 -0
  240. taguette/templates/project_delete.html +17 -0
  241. taguette/templates/project_import.html +122 -0
  242. taguette/templates/project_import_codebook.html +34 -0
  243. taguette/templates/project_import_codebook_confirm.html +49 -0
  244. taguette/templates/project_new.html +31 -0
  245. taguette/templates/tos.html +8 -0
  246. taguette/templates/trans.js +7 -0
  247. taguette/templates/welcome.html +15 -0
  248. taguette/utils.py +177 -0
  249. taguette/validate.py +141 -0
  250. taguette/version_check.py +102 -0
  251. taguette/web/__init__.py +252 -0
  252. taguette/web/api.py +1835 -0
  253. taguette/web/base.py +864 -0
  254. taguette/web/export.py +338 -0
  255. taguette/web/views.py +1618 -0
@@ -0,0 +1,209 @@
1
+ <!DOCTYPE html>
2
+ {% set fluid = False %}
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <meta name="Description" content="Free and open source qualitative research tool">
8
+ <link rel="stylesheet" href="{{ static_url('css/fa-all.min.css') }}">
9
+ <link rel="stylesheet" href="{{ static_url('css/vendor/bootstrap.min.css') }}">
10
+ <link rel="stylesheet" href="{{ static_url('css/vendor/introjs.min.css') }}">
11
+ {% if theme %}
12
+ <link rel="stylesheet" href="{{ static_url('scss/themes/' + theme + '/theme.css') }}">
13
+ {% endif %}
14
+
15
+ {% block styles %}
16
+ {% endblock %}
17
+ <title>{% block title %}LibreQDA{% endblock %}</title>
18
+ <script type="text/javascript">
19
+ var base_path = '{{ base_path }}';
20
+ </script>
21
+ </head>
22
+ <body class="page-{{ active_page }}">
23
+ {% if outdated_version %}
24
+ <div id="version-outdated-banner" class="alert alert-warning alert-dismissible mb-0 rounded-0 border-0 text-center d-none" role="alert" style="position: fixed; top: 6.5em; left: 0; right: 0; z-index: 101;">
25
+ {% set downloads_url = reverse_url('downloads') %}
26
+ {% trans latest_version=outdated_version, current_version=version, url=downloads_url %}A newer version of LibreQDA ({{ latest_version }}) is available. You are running {{ current_version }}. <a href="{{ url }}">Download the latest installer</a>.{% endtrans %}
27
+ <button type="button" class="btn-close" id="version-outdated-dismiss" aria-label="{% trans %}Close{% endtrans %}"></button>
28
+ </div>
29
+ <script>
30
+ (function() {
31
+ var latest = {{ outdated_version|tojson }};
32
+ var banner = document.getElementById('version-outdated-banner');
33
+ if (!banner) return;
34
+
35
+ function positionBanner() {
36
+ var navbar = document.querySelector('nav.navbar');
37
+ var brand = document.querySelector('.navbar-brand');
38
+ var navBottom = navbar ? navbar.getBoundingClientRect().bottom : 0;
39
+ var brandBottom = brand ? brand.getBoundingClientRect().bottom : 0;
40
+ var top = Math.max(navBottom, brandBottom);
41
+ banner.style.top = top + 'px';
42
+ var bannerH = banner.offsetHeight;
43
+ document.querySelectorAll('.container-body').forEach(function(el) {
44
+ el.style.marginTop = bannerH + 'px';
45
+ });
46
+ }
47
+ function resetLayout() {
48
+ document.querySelectorAll('.container-body').forEach(function(el) {
49
+ el.style.marginTop = '';
50
+ });
51
+ }
52
+
53
+ function init() {
54
+ try {
55
+ if (localStorage.getItem('libreqda-outdated-dismissed') !== latest) {
56
+ banner.classList.remove('d-none');
57
+ }
58
+ } catch (e) {
59
+ banner.classList.remove('d-none');
60
+ }
61
+
62
+ if (!banner.classList.contains('d-none')) {
63
+ positionBanner();
64
+ window.addEventListener('resize', positionBanner);
65
+ }
66
+
67
+ document.getElementById('version-outdated-dismiss').addEventListener('click', function() {
68
+ try { localStorage.setItem('libreqda-outdated-dismissed', latest); } catch (e) {}
69
+ banner.classList.add('d-none');
70
+ resetLayout();
71
+ window.removeEventListener('resize', positionBanner);
72
+ });
73
+ }
74
+
75
+ if (document.readyState === 'loading') {
76
+ document.addEventListener('DOMContentLoaded', init);
77
+ } else {
78
+ init();
79
+ }
80
+ })();
81
+ </script>
82
+ {% endif %}
83
+ <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
84
+ <div class="container">
85
+ {% if active_page != "welcome" %}
86
+ <a class="navbar-brand special-brand" href="{{ reverse_url('index') }}">
87
+ <div class="logo not-scrolled" title="LibreQDA"></div>
88
+ <div class="logo scrolled" title="LibreQDA"></div>
89
+ </a>
90
+ {% endif %}
91
+ {% if not disable_navbar_toggle %}
92
+ <div class="d-flex ms-auto">
93
+ <button class="navbar-toggler"
94
+ type="button"
95
+ data-bs-toggle="collapse"
96
+ data-bs-target="#navbar"
97
+ aria-controls="navbar"
98
+ aria-expanded="false"
99
+ aria-label="Toggle navigation">
100
+ <span class="navbar-toggler-icon"></span>
101
+ </button>
102
+ </div>
103
+ {% endif %}
104
+ <div class="collapse navbar-collapse" id="navbar">
105
+ <ul class="navbar-nav mr-auto">
106
+ {% if project %}
107
+ <li class="nav-item d-flex align-items-center">
108
+ <a class="nav-link {{ 'active' if active_page == 'project' else '' }} project-name" href="{{ reverse_url('project', project.id) }}">
109
+ {{ project.name }}
110
+ </a>
111
+ <i id="project-sidebar-open" title="{% trans %}Edit project{% endtrans %}" class="fa fa-briefcase"></i>
112
+ </li>
113
+ {% endif %}
114
+ </ul>
115
+ {% if project %}
116
+ <form class="navbar-nav search mr-auto" id="form-search-documents">
117
+ <div class="input-group" data-bs-toggle="dropdown" id="dropdown-trigger-search">
118
+ <input type="text" class="form-control" id="input-search-documents" autocomplete="off" placeholder="{% trans "search form placeholder" %}Search in documents{% endtrans %}">
119
+ <button class="btn btn-outline-primary page-selector" type="button" id="btn-search-previous-page">&lt;</button>
120
+ <button class="btn btn-outline-primary page-selector" type="button" id="btn-search-next-page">&gt;</button>
121
+ <button class="btn btn-outline-primary" type="submit"><i class="fas fa-search search-icon"></i><i class="fas fa-spinner searching"></i></button>
122
+ </div>
123
+ <ul id="ul-search-results" class="dropdown-menu" role="menu" aria-labelledby="dropdown-trigger-search"></ul>
124
+ </form>
125
+ {% endif %}
126
+ <ul class="navbar-nav">
127
+ {% if active_page != "welcome" or show_language_selector %}
128
+ {% if not user %}
129
+ <li class="nav-item">
130
+ <form action="{{ reverse_url('index') }}" method="post">
131
+ <select id="language" name="language" class="form-control" onchange="this.form.submit()">
132
+ {% for lang_code, lang_name in languages %}
133
+ <option value="{{ lang_code }}"{% if lang_code == current_language %} selected{% endif %}>{{ lang_name }}</option>
134
+ {% endfor %}
135
+ </select>
136
+ {% if active_page and active_page != "welcome" %}
137
+ <input type="hidden" name="url" value="{{ reverse_url(active_page) }}" />
138
+ {% else %}
139
+ <input type="hidden" name="url" value="{{ reverse_url("index") }}" />
140
+ {% endif %}
141
+ </form>
142
+ </li>
143
+ {% endif %}
144
+ {% endif %}
145
+ {% if active_page != "welcome" %}
146
+ <li class="nav-item">
147
+ {% set external_icon %}<i class="fa fa-book-open"></i>{% endset %}
148
+ <a id="intro-documentation" class="nav-link" href="https://aide.libreqda.org" target="_blank" rel="noopener">{% trans "navigation item" %}LibreQDA Guide {{ external_icon }}{% endtrans %}</a>
149
+ </li>
150
+ {% endif %}
151
+ {% if not multiuser %}
152
+ <li class="nav-item">
153
+ <a class="nav-link disabled">{% trans %}Single-user mode{% endtrans %}</a>
154
+ </li>
155
+ {% elif current_user %}
156
+ <li class="nav-item dropdown">
157
+ <a class="nav-link dropdown-toggle {{ 'active' if active_page == 'account' else '' }}" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{% trans "navigation dropdown menu" %}Account{% endtrans %}</a>
158
+ <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
159
+ <a class="dropdown-item {{ 'active' if active_page == 'account' else '' }}" href="{{ reverse_url('account') }}">{% trans "navigation item" %}Settings{% endtrans %}</a>
160
+ {% if tos %}
161
+ <a class="dropdown-item {{ 'active' if active_page == 'tos' else '' }}" href="{{ reverse_url('tos') }}">{% trans "navigation item" %}Terms of Service{% endtrans %}</a>
162
+ {% endif %}
163
+ <a class="dropdown-item" href="{{ reverse_url('logout') }}">{% trans "navigation item" %}Log out{% endtrans %}</a>
164
+ </ul>
165
+ </li>
166
+ {% else %}
167
+ {% if active_page != "welcome" %}
168
+ <li class="nav-item {{ 'active' if active_page == 'login' else '' }}">
169
+ <a class="nav-link" href="{{ reverse_url('login') }}">{% trans "navigation item" %}Log in{% endtrans %}</a>
170
+ </li>
171
+ {% endif %}
172
+ {% endif %}
173
+ {% if (not multiuser or current_user) and bug_report_url %}
174
+ <li class="nav-item">
175
+ <a class="nav-link" href="{{ bug_report_url }}" title="{% trans "navigation item" %}Bug report{% endtrans %}" target="_blank"><i class="fas fa-bug"></i></a>
176
+ </li>
177
+ {% endif %}
178
+ </ul>
179
+ </div>
180
+ </div>
181
+ </nav>
182
+
183
+ <div class="{% block containerclass %}container{% endblock %} container-body">
184
+ {% if show_messages %}
185
+ <div id="messages-div" class="mb-3"></div>
186
+ <script src="{{ reverse_url('messages.js') }}" async></script>
187
+ {% endif %}
188
+ {% block contents %}{% endblock %}
189
+
190
+ <footer class="footer">
191
+ <hr/>
192
+ <div class="container">
193
+ <p>
194
+ {% trans %}LibreQDA {{ version }} is released under a <a href="https://gitlab.com/bin-cirst/libreqda/libre-qda/-/blob/main/LICENSE.txt?ref_type=heads">BSD-3-Clause license</a>, &#169; Plateforme en humanités numériques (Université de Sherbrooke) and contributors
195
+ | &#169; Remi Rampin and Taguette contributors | <a href="https://help.libreqda.org/">User Documentation</a>{% endtrans %}
196
+ {{ extra_footer |safe }}
197
+ </p>
198
+ </div>
199
+ </footer>
200
+ </div>
201
+
202
+ <script src="{{ static_url('js/vendor/jquery-3.7.1.js') }}"></script>
203
+ <script src="{{ static_url('js/vendor/bootstrap.bundle.min.js') }}"></script>
204
+ <script src="{{ static_url('js/vendor/introjs.min.js') }}"></script>
205
+
206
+ {% block morejs %}
207
+ {% endblock %}
208
+ </body>
209
+ </html>
@@ -0,0 +1,158 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{% trans "page title" %}Report a bug{% endtrans %} | LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ <div class="container py-4" style="max-width: 720px;">
7
+ <div class="card shadow-sm">
8
+ <div class="card-body">
9
+ <h1 class="h4 mb-3">{% trans "bug report page heading" %}Report a bug{% endtrans %}</h1>
10
+
11
+ {% if message %}
12
+ <div class="alert alert-success">{{ message }}</div>
13
+ {% endif %}
14
+ {% if error %}
15
+ <div class="alert alert-danger">{{ error }}</div>
16
+ {% endif %}
17
+
18
+ <p class="text-muted">{% trans %}Found a bug? Describe what happened below and it will be emailed to the LibreQDA team.{% endtrans %}</p>
19
+ <form action="{{ reverse_url('bug_report') }}" method="post" enctype="multipart/form-data">
20
+ {{ xsrf_form_html() }}
21
+ <div class="form-group mb-3">
22
+ <label for="description">{% trans "bug report form label" %}What happened?{% endtrans %}</label>
23
+ <textarea class="form-control" id="description" name="description" rows="6" required placeholder="{% trans "bug report description placeholder" %}Example: When I delete a colored coding stripe, it doesn't disappear from my screen. Refreshing the page doesn't help. I'm using LibreQDA on Firefox, and I can reproduce this in one specific document (my_document.pdf).{% endtrans %}">{{ description }}</textarea>
24
+ </div>
25
+ <div class="form-group mb-3">
26
+ <label for="project_id">{% trans "bug report form label for project" %}Project{% endtrans %}</label>
27
+ {% if projects %}
28
+ <select class="form-select" id="project_id" name="project_id" required>
29
+ <option value="" {% if not project_id %}selected{% endif %} disabled>{% trans "bug report project select placeholder" %}Select a project…{% endtrans %}</option>
30
+ <option value="all" {% if project_id == "all" %}selected{% endif %}>{% trans "bug report all projects option" %}All projects (general issue){% endtrans %}</option>
31
+ {% for p in projects %}
32
+ <option value="{{ p.id }}" {% if project_id == p.id|string %}selected{% endif %}>{{ p.name }} (#{{ p.id }})</option>
33
+ {% endfor %}
34
+ </select>
35
+ {% else %}
36
+ <input type="text" class="form-control" id="project_id" name="project_id" value="{{ project_id }}" required placeholder="{% trans "bug report project id placeholder" %}Find it in your browser's address bar: https://www.libreqda.org/project/...{% endtrans %}" />
37
+ {% endif %}
38
+ </div>
39
+ <div class="form-group mb-3">
40
+ <label for="email">{% trans "bug report form label for email address" %}Your email (optional, in case we need to follow up){% endtrans %}</label>
41
+ <input type="text" class="form-control" id="email" name="email" value="{{ email }}" />
42
+ </div>
43
+ <div class="form-group mb-3">
44
+ <label>{% trans "bug report form label for screenshot" %}Screenshots (optional, up to 5, images only, max 15 MB each){% endtrans %}</label>
45
+ <div id="screenshot-dropzone" class="text-center p-4" style="border: 2px dashed #aaa; border-radius: 4px; cursor: pointer;">
46
+ <p id="screenshot-dropzone-text" class="mb-0 text-muted">{% trans %}Drag & drop images here, or click to choose files{% endtrans %}</p>
47
+ <input type="file" id="screenshot" name="screenshot" accept="image/*" multiple style="display: none;" />
48
+ </div>
49
+ <ul id="screenshot-list" class="list-unstyled mt-2 mb-0 d-none"></ul>
50
+ </div>
51
+ <div class="d-flex justify-content-between align-items-center mt-4">
52
+ <a href="{{ reverse_url('index') }}" class="btn btn-outline-secondary">{% trans "bug report form cancel button" %}Cancel{% endtrans %}</a>
53
+ <button type="submit" class="btn btn-primary">{% trans "bug report form submit button" %}Send{% endtrans %}</button>
54
+ </div>
55
+ </form>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <script type="text/javascript">
61
+ (function() {
62
+ var select = document.getElementById('project_id');
63
+ if (!select || select.tagName !== 'SELECT') { return; }
64
+ if (select.value) { return; }
65
+ var m = (document.referrer || '').match(/\/project\/(\d+)(?:[\/?#]|$)/);
66
+ if (!m) { return; }
67
+ var wanted = m[1];
68
+ for (var i = 0; i < select.options.length; ++i) {
69
+ if (select.options[i].value === wanted) {
70
+ select.value = wanted;
71
+ return;
72
+ }
73
+ }
74
+ })();
75
+ </script>
76
+
77
+ <script type="text/javascript">
78
+ (function() {
79
+ var dropzone = document.getElementById('screenshot-dropzone');
80
+ var text = document.getElementById('screenshot-dropzone-text');
81
+ var input = document.getElementById('screenshot');
82
+ var list = document.getElementById('screenshot-list');
83
+
84
+ var MAX_FILES = 5;
85
+ var files = [];
86
+
87
+ function fileKey(f) { return f.name + '\0' + f.size + '\0' + f.lastModified; }
88
+
89
+ function sync() {
90
+ var dt = new DataTransfer();
91
+ files.forEach(function(f) { dt.items.add(f); });
92
+ input.files = dt.files;
93
+ render();
94
+ }
95
+
96
+ function render() {
97
+ list.innerHTML = '';
98
+ if (!files.length) {
99
+ list.classList.add('d-none');
100
+ return;
101
+ }
102
+ list.classList.remove('d-none');
103
+ files.forEach(function(f, idx) {
104
+ var li = document.createElement('li');
105
+ li.className = 'd-flex align-items-center justify-content-between border rounded px-2 py-1 mb-1';
106
+ var name = document.createElement('span');
107
+ name.className = 'text-truncate me-2';
108
+ name.textContent = f.name;
109
+ var btn = document.createElement('button');
110
+ btn.type = 'button';
111
+ btn.className = 'btn-close';
112
+ btn.setAttribute('aria-label', '{% trans %}Remove{% endtrans %}');
113
+ btn.addEventListener('click', function() {
114
+ files.splice(idx, 1);
115
+ sync();
116
+ });
117
+ li.appendChild(name);
118
+ li.appendChild(btn);
119
+ list.appendChild(li);
120
+ });
121
+ }
122
+
123
+ function addFiles(added) {
124
+ for (var i = 0; i < added.length && files.length < MAX_FILES; ++i) {
125
+ var f = added[i];
126
+ var key = fileKey(f);
127
+ if (files.some(function(existing) { return fileKey(existing) === key; })) {
128
+ continue;
129
+ }
130
+ files.push(f);
131
+ }
132
+ sync();
133
+ }
134
+
135
+ dropzone.addEventListener('click', function(event) {
136
+ if (event.target.closest('#screenshot-list')) return;
137
+ input.click();
138
+ });
139
+ input.addEventListener('change', function() {
140
+ addFiles(input.files);
141
+ });
142
+ dropzone.addEventListener('dragover', function(event) {
143
+ event.preventDefault();
144
+ dropzone.style.background = '#f0f0f0';
145
+ });
146
+ dropzone.addEventListener('dragleave', function() {
147
+ dropzone.style.background = '';
148
+ });
149
+ dropzone.addEventListener('drop', function(event) {
150
+ event.preventDefault();
151
+ dropzone.style.background = '';
152
+ if (event.dataTransfer.files && event.dataTransfer.files.length) {
153
+ addFiles(event.dataTransfer.files);
154
+ }
155
+ });
156
+ })();
157
+ </script>
158
+ {% endblock %}
@@ -0,0 +1,15 @@
1
+ {% extends "base.html" %}
2
+ {% set active_page = "cookies_prompt" %}
3
+
4
+ {% block title %}{% trans "page title" %}Cookie warning{% endtrans %} | LibreQDA{% endblock %}
5
+
6
+ {% block contents %}
7
+ <h1>{% trans "title" %}Cookie warning{% endtrans %}</h1>
8
+ <p>{% trans %}This website uses cookies to store your identity (if you choose to log in) and as a protection against <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">cross-site request forgery (CSRF)</a>. Before continuing, you must accept their use by clicking the button below. Those <strong>are not</strong> "third-party" or "tracking" cookies.{% endtrans %}</p>
9
+
10
+ <form action="{{ reverse_url('cookies_prompt') }}" method="post">
11
+ <input type="hidden" name="accepted" value="yes" />
12
+ <input type="hidden" name="next" value="{{ next }}" />
13
+ <button type="submit" class="btn btn-primary">{% trans "button on cookie warning page" %}Accept cookies{% endtrans %}</button>
14
+ </form>
15
+ {% endblock %}
@@ -0,0 +1,127 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{% trans %}Downloads{% endtrans %} - LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ <div class="py-5">
7
+ <div class="text-center mb-4">
8
+ <h1>{% trans %}Download LibreQDA{% endtrans %}</h1>
9
+ {% if latest_version %}
10
+ <p class="lead text-muted">
11
+ {% trans latest_version=latest_version %}Latest version: {{ latest_version }}{% endtrans %}
12
+ </p>
13
+ {% endif %}
14
+ <p class="text-muted">
15
+ {% trans %}Free and open source qualitative research tool.{% endtrans %}
16
+ </p>
17
+ </div>
18
+
19
+ <div class="bg-light rounded p-4 mb-5">
20
+ <h4 class="text-center mb-3">{% trans %}Why install LibreQDA on your computer?{% endtrans %}</h4>
21
+ <div class="row">
22
+ <div class="col-md-6">
23
+ <ul class="mb-0">
24
+ <li>
25
+ <strong>{% trans %}Your data stays on your machine.{% endtrans %}</strong>
26
+ {% trans %}Interviews, transcripts, and sensitive documents never leave your computer.{% endtrans %}
27
+ </li>
28
+ <li>
29
+ <strong>{% trans %}Works offline.{% endtrans %}</strong>
30
+ {% trans %}Code your documents during fieldwork, on planes, or in areas without internet.{% endtrans %}
31
+ </li>
32
+ </ul>
33
+ </div>
34
+ <div class="col-md-6">
35
+ <ul class="mb-0">
36
+ <li>
37
+ <strong>{% trans %}No account, no login.{% endtrans %}</strong>
38
+ {% trans %}Launch the app, start coding. Your project is a local file you fully control and back up as you wish.{% endtrans %}
39
+ </li>
40
+ </ul>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="row g-4">
46
+
47
+ <div class="col-md-4">
48
+ <div class="card h-100 shadow-sm">
49
+ <div class="card-body text-center d-flex flex-column">
50
+ <div class="mb-3" style="font-size: 3rem;"><i class="fab fa-windows"></i></div>
51
+ <h3 class="card-title">Windows</h3>
52
+ <p class="text-muted small">{% trans %}Windows 10 or newer{% endtrans %}</p>
53
+ <div class="mt-auto">
54
+ {% if windows_url %}
55
+ <a href="{{ reverse_url('download_track', 'windows') }}" class="btn btn-primary btn-lg w-100">
56
+ <i class="fas fa-download"></i>
57
+ {% trans %}Download installer{% endtrans %}
58
+ </a>
59
+ <p class="small text-muted mt-2">{% trans %}.exe installer{% endtrans %}</p>
60
+ {% else %}
61
+ <button class="btn btn-outline-secondary btn-lg w-100" disabled>
62
+ {% trans %}Coming soon{% endtrans %}
63
+ </button>
64
+ <p class="small text-muted mt-2">
65
+ {% trans %}Installer not yet published. Use the web version at libreqda.org.{% endtrans %}
66
+ </p>
67
+ {% endif %}
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="col-md-4">
74
+ <div class="card h-100 shadow-sm">
75
+ <div class="card-body text-center d-flex flex-column">
76
+ <div class="mb-3" style="font-size: 3rem;"><i class="fab fa-apple"></i></div>
77
+ <h3 class="card-title">macOS</h3>
78
+ <p class="text-muted small">{% trans %}macOS 10.14 or newer{% endtrans %}</p>
79
+ <div class="mt-auto">
80
+ {% if macos_url %}
81
+ <a href="{{ reverse_url('download_track', 'macos') }}" class="btn btn-primary btn-lg w-100">
82
+ <i class="fas fa-download"></i>
83
+ {% trans %}Download installer{% endtrans %}
84
+ </a>
85
+ <p class="small text-muted mt-2">{% trans %}.dmg installer{% endtrans %}</p>
86
+ {% else %}
87
+ <button class="btn btn-outline-secondary btn-lg w-100" disabled>
88
+ {% trans %}Coming soon{% endtrans %}
89
+ </button>
90
+ <p class="small text-muted mt-2">
91
+ {% trans %}Installer not yet published. Use the web version at libreqda.org.{% endtrans %}
92
+ </p>
93
+ {% endif %}
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="col-md-4">
100
+ <div class="card h-100 shadow-sm">
101
+ <div class="card-body text-center d-flex flex-column">
102
+ <div class="mb-3" style="font-size: 3rem;"><i class="fab fa-linux"></i></div>
103
+ <h3 class="card-title">Linux / {% trans %}Server{% endtrans %}</h3>
104
+ <p class="text-muted small">{% trans %}Install via pip or Docker{% endtrans %}</p>
105
+ <div class="mt-auto text-start">
106
+ <p class="mb-1 fw-bold small">pip</p>
107
+ <pre class="bg-light p-2 rounded small mb-3"><code>pip install libreqda</code></pre>
108
+ <p class="mb-1 fw-bold small">Docker</p>
109
+ <pre class="bg-light p-2 rounded small mb-0"><code>docker pull phunudes/libreqda</code></pre>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ </div>
116
+
117
+ <div class="mt-5 text-center">
118
+ <h4>{% trans %}Source code{% endtrans %}</h4>
119
+ <p class="text-muted">
120
+ {% trans %}LibreQDA is open source under a BSD-3-Clause license.{% endtrans %}
121
+ </p>
122
+ <a href="https://gitlab.com/bin-cirst/libreqda/libre-qda" target="_blank" rel="noopener" class="btn btn-outline-primary">
123
+ <i class="fab fa-gitlab"></i> {% trans %}View on GitLab{% endtrans %}
124
+ </a>
125
+ </div>
126
+ </div>
127
+ {% endblock %}
@@ -0,0 +1,9 @@
1
+ {% trans %}A bug report was submitted on LibreQDA.{% endtrans %}
2
+
3
+ {% trans %}User: {{ login }}{% endtrans %}
4
+ {% trans %}Project ID: {{ project_id }}{% endtrans %}
5
+ {% if email %}{% trans %}Contact email: {{ email }}{% endtrans %}{% endif %}
6
+
7
+ ______
8
+
9
+ {{ description }}
@@ -0,0 +1,5 @@
1
+ {% trans %}Someone has requested to reset the password on the Taguette account associated with this email. If this was you, please follow this link: {{ link }}{% endtrans %}
2
+
3
+ {% trans %}Your user name is: {{ login }}{% endtrans %}
4
+
5
+ {% trans %}This link will expire in 1 day.{% endtrans %}
@@ -0,0 +1,8 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ error_title }} | LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ <h1>{{ error_title }}</h1>
7
+ <p>{{ error_message }} Use the back button of your browser or <a href="/">click here to return to the index</a>.</p>
8
+ {% endblock %}
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <style>
6
+ .highlight {
7
+ background-color: #ff0;
8
+ }
9
+ </style>
10
+ <title>{% trans "exported codebook document title" %}LibreQDA Codebook{% endtrans %}</title>
11
+ </head>
12
+ <body>
13
+ <h1>{% trans "exported codebook document title" %}LibreQDA Codebook{% endtrans %}</h1>
14
+ {% for tag in tags %}
15
+ <h2>{{ tag.path }}</h2>
16
+ <p class="number">{{ ngettext("%(number)d highlight", "%(number)d highlights", tag.highlights_count, number=tag.highlights_count) }}</p>
17
+ <p>{{ tag.description }}</p>
18
+ {% endfor %}
19
+ </body>
20
+ </html>
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <style>
6
+ .highlight {
7
+ background-color: #ff0;
8
+ }
9
+ .taglist {
10
+ font-style: oblique;
11
+ background: #fbb !important;
12
+ }
13
+ </style>
14
+ <title>{{ name }}</title>
15
+ </head>
16
+ <body>
17
+ <h1>{{ name }}</h1>
18
+ {{ contents }}
19
+ </body>
20
+ </html>
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ {%- if path %}
7
+ <title>{% trans "exported highlights document title" %}LibreQDA highlights: {{ path }}{% endtrans %}</title>
8
+ {%- else %}
9
+ <title>{% trans "exported highlights document title" %}LibreQDA highlights{% endtrans %}</title>
10
+ {%- endif %}
11
+ <style>
12
+ h1 {
13
+ margin-bottom: 1em;
14
+ }
15
+ </style>
16
+ </head>
17
+
18
+ <body>
19
+ {%- if path %}
20
+ <h1>{% trans "exported highlights document title" %}LibreQDA highlights: {{ path }}{% endtrans %}</h1>
21
+ {%- else %}
22
+ <h1>{% trans "exported highlights document title" %}LibreQDA highlights{% endtrans %}</h1>
23
+ {%- endif %}
24
+ {% for id, snippet, document, tags, collection in highlights %}
25
+ {{ snippet |safe }}
26
+ <p>
27
+ {% trans "exported highlight details" doc=document %}<strong>Document:</strong> {{ doc }}{% endtrans %}
28
+ </p>
29
+ {%- if collection %}
30
+ <p>
31
+ <strong>{% trans "exported highlight details" %}Collection:{% endtrans %}</strong> {{ collection }}
32
+ </p>
33
+ {%- endif %}
34
+ <p>
35
+ <strong>{% trans "exported highlight details" %}Codes:{% endtrans %}</strong>
36
+ {%- for tag in tags %}
37
+ {{ tag }}
38
+ {%- if not loop.last %},{% endif %}
39
+ {%- endfor %}
40
+ </p>
41
+ {%- if not loop.last %}
42
+ <hr>
43
+ {%- endif %}
44
+ {% endfor %}
45
+ </body>
46
+
47
+ </html>