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,99 @@
1
+ <div id="project-sidebar" class="project-sidebar">
2
+
3
+ <a href="javascript:void(0)" data-test-id="close-sidebar-button" title="{% trans %}Close{% endtrans %}" class="close-project-navbar" onclick="closeSideBar()">&times;</a>
4
+
5
+ <div class="project-sidebar-content">
6
+
7
+ <!-- Tab selector -->
8
+ <ul class="nav nav-tabs mx-3" id="myTab" role="tablist">
9
+ <li class="nav-item">
10
+ <a class="nav-link active" id="project-tab" data-test-id="project-tab" data-bs-toggle="tab" href="#project" role="tab" aria-controls="project" aria-selected="true">{# TRANSLATORS: Left pane tab, keep short #}{% trans "tab" %}Project info{% endtrans %}</a>
11
+ </li>
12
+ </ul>
13
+
14
+ <div class="tab-content" id="myTabContent">
15
+ <div class="tab-pane fade show active" id="project" role="tabpanel" aria-labelledby="project-tab">
16
+ <div class="px-5">
17
+ <form id="project-metadata-form" >
18
+ <div class="form-group">
19
+ <label for="project-name" class="col-sm-2 col-form-label">{% trans "project metadata form label" %}Name{% endtrans %}</label>
20
+ <input type="text" class="form-control" id="project-name" name="project-name" value="{{ project.name }}" required />
21
+ </div>
22
+ <div class="form-group">
23
+ <label for="project-description" class="col-sm-2 col-form-label">{% trans "project metadata form label" %}Description{% endtrans %}</label>
24
+ <textarea class="form-control" id="project-description" name="project-description">{{ project.description }}</textarea>
25
+ </div>
26
+ </form>
27
+
28
+ {% if multiuser %}
29
+ <div class="accordion py-5" id="accordionCollaborators">
30
+ <div class="accordion-item">
31
+ <h2 class="accordion-header" id="headingCollaborators">
32
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCollaborators" aria-expanded="false" aria-controls="collapseCollaborators">
33
+ {% trans "modal window title" %}Add Collaborators{% endtrans %}
34
+ </button>
35
+ </h2>
36
+ <div id="collapseCollaborators" class="accordion-collapse collapse" aria-labelledby="headingCollaborators" data-bs-parent="#accordionCollaborators">
37
+ <div class="accordion-body mt-2">
38
+ <form id="members-add">
39
+ <fieldset id="members-add-fields">
40
+ <div class="row">
41
+ <div class="col-md-4 form-group">
42
+ <input type="text" class="form-control" id="member-add-name" placeholder="{% trans "project members form placeholder" %}Username{% endtrans %}" required />
43
+ </div>
44
+ <div class="col-md-4 form-group">
45
+ <select id="member-add-privileges" class="form-control">
46
+ <option value="ADMIN">{% trans "permission level" %}Full permissions{% endtrans %}</option>
47
+ <option value="MANAGE_DOCS">{% trans "permission level" %}Can't change collaborators / delete project{% endtrans %}</option>
48
+ <option value="TAG" selected>{% trans "permission level" %}View & make changes{% endtrans %}</option>
49
+ <option value="READ">{% trans "permission level" %}View only{% endtrans %}</option>
50
+ </select>
51
+ </div>
52
+ <button type="submit" class="btn btn-info col-md-4 form-group">{% trans "form submit button" %}Add to project{% endtrans %}</button>
53
+ </div>
54
+ </fieldset>
55
+ </form>
56
+ <h5 class="pb-2">{% trans %}Current Collaborators{% endtrans %}</h5>
57
+ <form id="members-current">
58
+ <!-- Existing collaborators -->
59
+ </form>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ {% endif %}
65
+
66
+ <p><a href="{{ reverse_url('export_project_sqlite', project.id) }}" class="w-100 btn btn-outline-primary" role="button">{% trans %}Export project{% endtrans %}</a></p>
67
+ <div class="dropdown mb-3">
68
+ <button class="w-100 btn btn-outline-primary dropdown-toggle" type="button" id="dropdown-codebook" data-test-id="dropdown-codebook" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
69
+ {% trans %}Export codebook{% endtrans %}
70
+ </button>
71
+ <ul class="dropdown-menu" aria-labelledby="dropdown-codebook">
72
+ <button type="button" class="dropdown-item" id="codebook-viz-trigger">{% trans %}Image{% endtrans %}</button>
73
+ <li><hr class="dropdown-divider"></li>
74
+ <!-- these need to be fixed after changing to a hierarchical tag logic
75
+ <a href="{{ reverse_url('export_codebook_qdc', project.id) }}" class="dropdown-item">{% trans %}QDC (XML){% endtrans %}</a>
76
+ -->
77
+ <a href="{{ reverse_url('export_codebook_xlsx', project.id) }}" class="dropdown-item">{% trans %}Excel{% endtrans %}</a>
78
+ <a href="{{ reverse_url('export_codebook_csv', project.id) }}" class="dropdown-item">{% trans %}CSV{% endtrans %}</a>
79
+ <a href="{{ reverse_url('export_codebook_doc', project.id, 'html') }}" class="dropdown-item">{% trans %}HTML{% endtrans %}</a>
80
+ <a href="{{ reverse_url('export_codebook_doc', project.id, 'docx') }}" class="dropdown-item">{% trans %}DOCX{% endtrans %}</a>
81
+ <a href="{{ reverse_url('export_codebook_doc', project.id, 'pdf') }}" class="dropdown-item">{% trans %}PDF{% endtrans %}</a>
82
+ </ul>
83
+ </div>
84
+
85
+ {% if can_import_codebook %}
86
+ <p><a href="{{ reverse_url('import_codebook', project.id) }}" class="w-100 btn btn-outline-primary" type="button">{% trans %}Import codebook{% endtrans %}</a></p>
87
+ {% endif %}
88
+
89
+ {% if can_delete_project %}
90
+ <p><a href="{{ reverse_url('delete_project', project.id) }}" class="w-100 btn btn-outline-danger">{% trans %}Delete project{% endtrans %}</a></p>
91
+ {% endif %}
92
+ </div>
93
+ </div>
94
+
95
+ </div>
96
+
97
+ </div>
98
+
99
+ </div>
@@ -0,0 +1,11 @@
1
+ <div id="spinner-modal" class="modal bd-example-modal-lg" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
2
+ <div class="modal-dialog modal-sm">
3
+ <div class="modal-content">
4
+ <div id="spinner"></div>
5
+ <div id="progress" class="progress">
6
+ <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
7
+ </div>
8
+ <button class="cancel btn btn-secondary">{% trans %}Cancel{% endtrans %}</button>
9
+ </div>
10
+ </div>
11
+ </div>
@@ -0,0 +1,54 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{% trans %}Grant permissions{% endtrans %} | LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ <p>{% trans %}To use this instance of LibreQDA, users outside the <a href="https://www.canarie.ca/identity/caf/">Canadian Access Federation (CAF)</a> must be sponsored{% endtrans %}.</p>
7
+
8
+ <h3>{% trans %}Grant permissions{% endtrans %}:</h3>
9
+ <form id="members-add">
10
+ <fieldset id="members-add-fields">
11
+ <div class="row">
12
+ <div class="col-md-4 form-group">
13
+ {% if is_external_login %}
14
+ <input type="text" class="form-control" id="member-add-name" placeholder="{% trans "form label for email address" %}Email{% endtrans %}" required />
15
+ {% else %}
16
+ <input type="text" class="form-control" id="member-add-name" placeholder="{% trans "project members form placeholder" %}Username{% endtrans %}" required />
17
+ {% endif %}
18
+ </div>
19
+ <div class="col-md-4 form-group">
20
+ <select id="member-add-privileges" class="form-control">
21
+ <option value="PROJECT_ADMIN">{% trans "permission level" %}Can create projects{% endtrans %}</option>
22
+ </select>
23
+ </div>
24
+ <button type="submit" class="btn btn-info col-md-4 form-group">{% trans "form submit button" %}Update role{% endtrans %}</button>
25
+ </div>
26
+ </fieldset>
27
+ </form>
28
+ <h4>{% trans %}Users that you approved{% endtrans %}</h4>
29
+ <form id="members-current">
30
+ </form>
31
+
32
+ <button type="button" id="members-submit" class="btn btn-primary">{% trans %}Save{% endtrans %}</button>
33
+ <a href="{{ reverse_url('index') }}" class="btn btn-secondary">{% trans %}Cancel{% endtrans %}</a>
34
+
35
+ <!-- Spinner modal -->
36
+ {% include 'partials/spinner.html' %}
37
+
38
+ <!-- Alert modal -->
39
+ {% include 'partials/alert.html' %}
40
+ {% endblock %}
41
+
42
+
43
+ {% block morejs %}
44
+ <script type="text/javascript" src="{{ reverse_url('trans.js') }}?lang={{ user.language }}"></script>
45
+ <!-- Utilities -->
46
+ <script type="text/javascript" src="{{ static_url('js/utilities.js') }}"></script>
47
+ <script type="text/javascript" src="{{ static_url('js/permissions.js') }}"></script>
48
+
49
+ <script type="text/javascript">
50
+ const user_login = {{ user_login }};
51
+ let members = {{ members }};
52
+ showMembers();
53
+ </script>
54
+ {% endblock %}
@@ -0,0 +1,288 @@
1
+ {% extends "base.html" %}
2
+ {% set active_page = "project" %}
3
+
4
+ {% block styles %}
5
+ <link rel="stylesheet" href="{{ static_url('css/vendor/hierarchy-select.min.css') }}">
6
+ <link rel="stylesheet" href="{{ static_url('css/vendor/bootstrap-treeview.min.css') }}">
7
+ <link rel="stylesheet" href="{{ static_url('css/vendor/jquery.contextMenu.min.css') }}">
8
+ {% endblock %}
9
+
10
+ {% block title %}{{ project.name }} | LibreQDA{% endblock %}
11
+
12
+ {% block containerclass %}container-fluid{% endblock %}
13
+
14
+ {% block contents %}
15
+ <div class="row flex-column flex-md-row">
16
+ <!-- Left panel -->
17
+ <div class="col-12 col-md-2-5 order-2 order-md-1" id="left-panel">
18
+ {% include 'partials/project_sidebar.html' %}
19
+ <div class="bordered-container sticky-top">
20
+ <div class="codebook-header-wrap px-2 pt-2 pb-1 d-flex align-items-start gap-2">
21
+ <div class="panel-side-controls d-flex flex-column align-items-center gap-1 flex-shrink-0">
22
+ <button id="toggle-left-panel" class="btn btn-sm btn-outline-secondary" title="{% trans %}Toggle panel{% endtrans %}">
23
+ <i class="fas fa-chevron-left"></i>
24
+ </button>
25
+ <button type="button" class="btn border-0 btn-sm" id="tag-treeview-help-icon"
26
+ title="{% trans %}Tips{% endtrans %}">
27
+ <i class="fas fa-question-circle"></i>
28
+ </button>
29
+ </div>
30
+ <div class="codebook-toolbar panel-content flex-grow-1 min-width-0">
31
+ <div class="compact-toggle-wrap form-check form-switch mb-0 d-flex align-items-center">
32
+ <input class="form-check-input me-1" type="checkbox" role="switch"
33
+ id="compact-layout-toggle" onchange="toggleCompactLayout()"
34
+ {% if project.compact_layout %}checked{% endif %}>
35
+ <label class="form-check-label small" for="compact-layout-toggle">
36
+ {% trans %}Compact{% endtrans %}
37
+ </label>
38
+ </div>
39
+ <a id="create-tag-link" class="btn btn-sm btn-primary rounded-4 codebook-toolbar-btn"
40
+ href="javascript:createTag()" data-test-id="create-tag-link"
41
+ title="{% trans %}Create a code{% endtrans %}">
42
+ <i class="fas fa-plus"></i><span class="btn-label"> {% trans %}Create a code{% endtrans %}</span>
43
+ </a>
44
+ <a id="explore-codes-link" class="btn btn-sm btn-outline-primary rounded-4 codebook-toolbar-btn"
45
+ href="javascript:openExploreCodes()" data-test-id="explore-codes-link"
46
+ title="{% trans %}Explore codes{% endtrans %}">
47
+ <i class="fas fa-search"></i><span class="btn-label"> {% trans %}Explore codes{% endtrans %}</span>
48
+ </a>
49
+ <div class="dropdown">
50
+ <button class="btn btn-sm rounded-4 btn-outline-primary" type="button"
51
+ id="tag-more-action-button" data-bs-toggle="dropdown" data-bs-auto-close="outside"
52
+ aria-haspopup="true" aria-expanded="false"
53
+ title="{% trans %}More actions{% endtrans %}">
54
+ <i class="fas fa-bars"></i><span class="btn-label"> {% trans %}More actions{% endtrans %}</span>
55
+ </button>
56
+ <div class="dropdown-menu" id="tag-menu-more-actions"
57
+ aria-labelledby="tag-more-action-button">
58
+ <h6 class="dropdown-header">{% trans %}Sort by{% endtrans %}</h6>
59
+ <a class="dropdown-item sort-option active" data-sort-field="path" data-sort-dir="asc" href="javascript:sortTagsBy('path', 'asc')">{% trans
60
+ %}Text [a&rarr;z]{% endtrans %}</a>
61
+ <a class="dropdown-item sort-option" data-sort-field="path" data-sort-dir="desc" href="javascript:sortTagsBy('path', 'desc')">{% trans %}Text
62
+ [z&rarr;a]{% endtrans %}</a>
63
+ {% if not user.hide_code_counts %}
64
+ <a class="dropdown-item sort-option" data-sort-field="count" data-sort-dir="asc" href="javascript:sortTagsBy('count', 'asc')">{% trans %}Count
65
+ [0&rarr;9]{% endtrans %}</a>
66
+ <a class="dropdown-item sort-option" data-sort-field="count" data-sort-dir="desc" href="javascript:sortTagsBy('count', 'desc')">{% trans %}Count
67
+ [9&rarr;0]{% endtrans %}</a>
68
+ {% endif %}
69
+ <div class="dropdown-divider"></div>
70
+ <a id="load-all-tags" class="dropdown-item" href="/project/{{ project.id }}/highlights/"
71
+ data-test-id="see-all-highlights-button">{% trans %}See all highlights{% endtrans
72
+ %}</a>
73
+ <a class="dropdown-item" href="javascript:expandAllNodes(treeview_tags)">{% trans
74
+ %}Expand all{% endtrans %}</a>
75
+ <a class="dropdown-item" href="javascript:collapseAllNodes(treeview_tags)">{% trans
76
+ %}Collapse all{% endtrans %}</a>
77
+ <div class="dropdown-divider"></div>
78
+ <button type="button" class="dropdown-item" id="dropdown-tips-btn">
79
+ <i class="fas fa-question-circle me-1"></i> {% trans %}Tips{% endtrans %}
80
+ </button>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ <div class="mb-2 panel-content">
86
+ <div class="form-group">
87
+ <input type="text" class="form-control" id="input-search-tags" autocomplete="off" placeholder="{% trans " tag change
88
+ form placeholder" %}Search for a code{% endtrans %}">
89
+ </div>
90
+ <div id="tag-selection-criteria-area" class="tag-selection-box mt-3 mb-3">
91
+ </div>
92
+ <p id="tag-selection-return"><a href="">{% trans %}Back to highlights{% endtrans %}</a></p>
93
+ <div id="treeview-tags"></div>
94
+ <div class="alert alert-info mt-2 d-none" role="alert" id="alert-tag-treeview">
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+
100
+ <!-- Right panel -->
101
+ <div class="col-12 col-md-7-5 order-3 order-md-2" id="middle-panel">
102
+
103
+ {% include 'partials/document_toolbar.html' %}
104
+
105
+ <div id="document-view" class="document-view">
106
+ <div id="document-contents">
107
+ <p style="font-style: oblique; text-align: center;">{% trans %}Load a document from your bookmarks or on
108
+ the right{% endtrans %}</p>
109
+ </div>
110
+ </div>
111
+
112
+ <button id="scroll-to-top-button" class="btn btn-primary rounded-circle scroll-btn d-none"
113
+ title="Scroll to top">
114
+ <i class="fas fa-arrow-up"></i>
115
+ </button>
116
+
117
+ </div>
118
+
119
+ <!-- List of documents -->
120
+ <div class="col-12 col-md-2-5 order-1 order-md-3" id="right-panel">
121
+ <div class="bordered-container sticky-top">
122
+ <div class="documents-header-wrap px-2 pt-2 pb-1 d-flex align-items-start gap-2">
123
+ <div class="document-toolbar panel-content flex-grow-1 min-width-0">
124
+ <a class="btn btn-sm btn-outline-primary rounded-4 codebook-toolbar-btn"
125
+ href="javascript:createCollection()" data-test-id="create-collection-link"
126
+ title="{% trans %}Add a collection{% endtrans %}">
127
+ <i class="fas fa-folder-plus"></i> {% trans %}Collection{% endtrans %}
128
+ </a>
129
+ <a class="btn btn-sm btn-primary rounded-4 codebook-toolbar-btn" href="javascript:createDocument()"
130
+ data-test-id="create-document-link"
131
+ title="{% trans %}Add a document{% endtrans %}">
132
+ <i class="fas fa-plus"></i> {% trans %}Documents{% endtrans %}
133
+ </a>
134
+ </div>
135
+ <div class="panel-side-controls d-flex flex-column align-items-center gap-1 flex-shrink-0">
136
+ <button id="toggle-right-panel" class="btn btn-sm btn-outline-secondary" title="{% trans %}Toggle panel{% endtrans %}">
137
+ <i class="fas fa-chevron-right"></i>
138
+ </button>
139
+ <button type="button" class="btn border-0 btn-sm" id="treeview-help-icon"
140
+ title="{% trans %}Tips{% endtrans %}">
141
+ <i class="fas fa-question-circle"></i>
142
+ </button>
143
+ </div>
144
+ </div>
145
+ <div class="panel-content px-2 pb-1">
146
+ <input type="text" class="form-control" id="input-search-titles" autocomplete="off"
147
+ placeholder="{% trans %}Search titles{% endtrans %}">
148
+ </div>
149
+ <!-- Documents treeview -->
150
+ <div class="panel-content" id="treeview-documents">
151
+ <div class="list-group-item disabled">{% trans %}Loading...{% endtrans %}</div>
152
+ </div>
153
+ <div class="alert alert-info mt-2 d-none" role="alert" id="alert-title-search"></div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+
158
+ {% if is_demo == 'true' %}
159
+ <button id="start-tutorial" class="btn btn-primary rounded-4">{% trans %}Resume tutorial{% endtrans %}</button>
160
+ {% endif %}
161
+
162
+ <!-- Manage collaborators modal -->
163
+ {% include 'modal/manage_collaborators.html' %}
164
+
165
+ <!-- Add document modal -->
166
+ {% include 'modal/create_document.html' %}
167
+
168
+ <!-- Add collection modal -->
169
+ {% include 'modal/create_collection.html' %}
170
+
171
+ <!-- Change document modal -->
172
+ {% include 'modal/change_document.html' %}
173
+
174
+ <!-- Add/change highlight modal -->
175
+ {% include 'modal/create_highlight.html' %}
176
+
177
+ <!-- Explore codes modal -->
178
+ {% include 'modal/explore_codes.html' %}
179
+
180
+ <!-- Merge tags modal -->
181
+ {% include 'modal/create_tag.html' %}
182
+
183
+ <!-- Add/change tag modal -->
184
+ {% include 'modal/create_tag.html' %}
185
+
186
+ <!-- Merge tags modal -->
187
+ {% include 'modal/merge_tag.html' %}
188
+
189
+ <!-- Spinner modal -->
190
+ {% include 'partials/spinner.html' %}
191
+
192
+ <!-- Alert modal -->
193
+ {% include 'partials/alert.html' %}
194
+
195
+ <!-- Confirm modal -->
196
+ {% include 'partials/confirm.html' %}
197
+
198
+ <!-- Codebook visualization modal -->
199
+ {% include 'partials/codebook_viz_modal.html' %}
200
+
201
+ <!-- Initial state -->
202
+ <script type="text/javascript">
203
+ const user_login = {{ user_login }};
204
+ const project_id = {{ project.id }};
205
+ const hideCodeCounts = {{ 'true' if user.hide_code_counts else 'false' }};
206
+ const projectCompactLayout = {{ 'true' if project.compact_layout else 'false' }};
207
+ const version = '{{ version }}';
208
+ let documents = {{ documents }};
209
+ let tags = {{ tags }};
210
+ let members = {{ members }};
211
+ let highlights = {};
212
+ let last_event = {{ last_event }};
213
+ let is_demo = {{ is_demo }};
214
+ let demo_highlights_location = {{ demo_highlights_location }};
215
+ let collections = {{ collections }};
216
+ </script>
217
+
218
+ <script type="text/javascript" src="{{ reverse_url('trans.js') }}?lang={{ user.language }}"></script>
219
+
220
+ <!-- "New highlight" button shown after selecting text -->
221
+ {% include 'partials/hlinfo.html' %}
222
+
223
+ {% endblock %}
224
+
225
+ {% block morejs %}
226
+
227
+ <!-- Should minify this but I hate NPM -->
228
+
229
+ <!-- Utilities -->
230
+ <script type="text/javascript" src="{{ static_url('js/utilities.js') }}"></script>
231
+
232
+ <!-- libraries -->
233
+ <script type="text/javascript" src="{{ static_url('js/vendor/hierarchy-select.min.js') }}"></script>
234
+ <script type="text/javascript" src="{{ static_url('js/vendor/bootstrap-treeview-custum.min.js') }}"></script>
235
+ <script type="text/javascript" src="{{ static_url('js/vendor/jquery.contextMenu.min.js') }}"></script>
236
+
237
+ <!-- tag -->
238
+ <script type="text/javascript" src="{{ static_url('js/tag/manager.js') }}"></script>
239
+ <script type="text/javascript" src="{{ static_url('js/tag/hierarchy-select.js') }}"></script>
240
+ <script type="text/javascript" src="{{ static_url('js/tag/treeview.js') }}"></script>
241
+ <script type="text/javascript" src="{{ static_url('js/tag/context-menu.js') }}"></script>
242
+ <script type="text/javascript" src="{{ static_url('js/tag/explore-codes.js') }}"></script>
243
+
244
+ <!-- collection -->
245
+ <script type="text/javascript" src="{{ static_url('js/collection/manager.js') }}"></script>
246
+ <script type="text/javascript" src="{{ static_url('js/collection/hierarchy-select.js') }}"></script>
247
+ <script type="text/javascript" src="{{ static_url('js/collection/context-menu.js') }}"></script>
248
+
249
+ <!-- highlight system -->
250
+ <script type="text/javascript" src="{{ static_url('js/highlight/renderer.js') }}"></script>
251
+ <script type="text/javascript" src="{{ static_url('js/highlight/loader.js') }}"></script>
252
+ <script type="text/javascript" src="{{ static_url('js/highlight/crud.js') }}"></script>
253
+ <script type="text/javascript" src="{{ static_url('js/highlight/extracts.js') }}"></script>
254
+
255
+ <!-- search documents -->
256
+ <script type="text/javascript" src="{{ static_url('js/project/search.js') }}"></script>
257
+
258
+ <!-- core -->
259
+ <script type="text/javascript" src="{{ static_url('js/taguette.js') }}"></script>
260
+
261
+ <!-- document -->
262
+ <script type="text/javascript" src="{{ static_url('js/document/manager.js') }}"></script>
263
+ <script type="text/javascript" src="{{ static_url('js/document/custom-select.js') }}"></script>
264
+ <script type="text/javascript" src="{{ static_url('js/document/treeview.js') }}"></script>
265
+ <script type="text/javascript" src="{{ static_url('js/document/context-menu.js') }}"></script>
266
+
267
+ <!-- codebook visualization -->
268
+ <script type="text/javascript" src="{{ static_url('js/codebook-viz.js') }}"></script>
269
+
270
+ <!-- others -->
271
+ <script type="text/javascript" src="{{ static_url('js/polling-event.js') }}"></script>
272
+
273
+ <script type="text/javascript">
274
+ $(document).ready(function () {
275
+
276
+ sortTagsBy('path', 'asc');
277
+ addContextMenu();
278
+
279
+ // Initialize document treeview
280
+ initDocumentsTreeview();
281
+
282
+ });
283
+
284
+ </script>
285
+
286
+ <script src="{{ static_url('js/project/intro.js') }}"></script>
287
+
288
+ {% endblock %}
@@ -0,0 +1,17 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{% trans "page title" %}Delete project{% endtrans %} | LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ <h1>{% trans "title" %}Delete project: confirm?{% endtrans %}</h1>
7
+ {% set project_name %}<span style="font-family: monospace, monospace;">{{ project.name }}</span>{% endset %}
8
+ <p class="lead">{% trans "project delete page" %}Name: {{ project_name }}{% endtrans %}</p>
9
+ <p>{% trans %}Are you sure you want to delete this project? It currently has <strong>{{ documents }} documents and {{ highlights }} highlights in {{ tags }} tags</strong>. There is no way to undo.{% endtrans %}</p>
10
+ <form action="{{ reverse_url('delete_project', project.id) }}" method="post">
11
+ {{ xsrf_form_html() }}
12
+ <div class="form-group">
13
+ <button type="submit" class="btn btn-danger">{% trans "submit button" %}Delete forever{% endtrans %}</button>
14
+ <a href="{{ reverse_url('project', project.id) }}" class="btn btn-secondary">{% trans "cancel button" %}Go back{% endtrans %}</a>
15
+ </div>
16
+ </form>
17
+ {% endblock %}
@@ -0,0 +1,122 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{% trans "page title" %}Import project{% endtrans %} | LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ <h1>{% trans "title" %}Import a project{% endtrans %}</h1>
7
+
8
+ <form class="mt-3" onsubmit="javascript:importProject();return false;">
9
+ {{ xsrf_form_html() }}
10
+ <div class="form-group row">
11
+ <label for="project-file" class="col-sm-2 col-form-label">{% trans "form label for project file" %}File:{% endtrans %}</label>
12
+ <div class="col-sm-10">
13
+ <input type="file" class="form-control" name="project-file" id="project-file" accept="application/x-sqlite3,.sqlite3" />
14
+ </div>
15
+ </div>
16
+ <div class="form-group row">
17
+ <label for="project" class="col-sm-2 col-form-label">{% trans "form label for project dropdown" %}Project:{% endtrans %}</label>
18
+ <div class="col-sm-10">
19
+ <select class="form-control" name="project" id="project" disabled>
20
+ <option>{% trans %}No file selected...{% endtrans %}</option>
21
+ </select>
22
+ </div>
23
+ </div>
24
+ <p>
25
+ <input type="submit" class="btn btn-primary" id="submit" value="{% trans "form submit button" %}Import project{% endtrans %}" disabled>
26
+ </p>
27
+ </form>
28
+
29
+ <script type="text/javascript" src="{{ reverse_url('trans.js') }}?lang={{ user.language }}"></script>
30
+
31
+ <script type="text/javascript">
32
+ var projectFile = document.getElementById('project-file');
33
+ projectFile.addEventListener('change', function() {
34
+ if(!projectFile.files || !projectFile.files.length) {
35
+ return;
36
+ }
37
+ var file = projectFile.files[0];
38
+ var form_data = new FormData();
39
+ form_data.append('file', file);
40
+ var xsrf = document.cookie.match('\\b' + '_xsrf' + '=([^;]*)\\b');
41
+ if(xsrf) {
42
+ form_data.append('_xsrf', xsrf[1]);
43
+ }
44
+
45
+ var xhr = new XMLHttpRequest();
46
+ xhr.responseType = 'json';
47
+ xhr.open('POST', base_path + '/api/import');
48
+ xhr.onload = function() {
49
+ if(xhr.status === 200) {
50
+ var projectList = document.getElementById('project');
51
+ projectList.disabled = false;
52
+ projectList.innerHTML = '';
53
+ for(var i = 0; i < xhr.response.projects.length; ++i) {
54
+ var project = xhr.response.projects[i];
55
+ var elem = document.createElement('option');
56
+ elem.setAttribute('value', project.id);
57
+ elem.innerText = '' + project.id + ' - ' + project.name;
58
+ projectList.appendChild(elem);
59
+ }
60
+ document.getElementById('submit').disabled = false;
61
+ } else {
62
+ console.error("Project upload failed:", xhr.status);
63
+ var error = null;
64
+ try {
65
+ error = xhr.response.error;
66
+ } catch(e) {
67
+ }
68
+ if(!error) {
69
+ error = "Status " + xhr.status;
70
+ }
71
+ alert(gettext("Project upload failed!") + "\n\n" + error);
72
+ }
73
+ };
74
+ xhr.onerror = function(e) {
75
+ console.log("Project upload failed:", e);
76
+ alert(gettext("Project upload failed!"));
77
+ };
78
+ xhr.send(form_data);
79
+ });
80
+
81
+ function importProject() {
82
+ if(!projectFile.files || !projectFile.files.length) {
83
+ return;
84
+ }
85
+ var file = projectFile.files[0];
86
+ var form_data = new FormData();
87
+ form_data.append('project_id', parseInt(document.getElementById('project').value, 10));
88
+ form_data.append('file', file);
89
+ var xsrf = document.cookie.match('\\b' + '_xsrf' + '=([^;]*)\\b');
90
+ if(xsrf) {
91
+ form_data.append('_xsrf', xsrf[1]);
92
+ }
93
+
94
+ var xhr = new XMLHttpRequest();
95
+ xhr.responseType = 'json';
96
+ xhr.open('POST', base_path + '/api/import');
97
+ xhr.onload = function() {
98
+ if(xhr.status === 200) {
99
+ var new_project_id = xhr.response.project_id;
100
+ window.location.href = base_path + '/project/' + new_project_id;
101
+ } else {
102
+ console.error("Project upload failed:", xhr.status);
103
+ var error = null;
104
+ try {
105
+ error = xhr.response.error;
106
+ } catch(e) {
107
+ }
108
+ if(!error) {
109
+ error = "Status " + xhr.status;
110
+ }
111
+ alert(gettext("Project upload failed!") + "\n\n" + error);
112
+ }
113
+ };
114
+ xhr.onerror = function(e) {
115
+ console.log("Project upload failed:", e);
116
+ alert(gettext("Project upload failed!"));
117
+ };
118
+ document.getElementById('submit').disabled = true;
119
+ xhr.send(form_data);
120
+ }
121
+ </script>
122
+ {% endblock %}
@@ -0,0 +1,34 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{% trans "page title" %}Import codebook{% endtrans %} | LibreQDA{% endblock %}
4
+
5
+ {% block contents %}
6
+ <h1>{% trans "title" %}Import a codebook{% endtrans %}</h1>
7
+ <p>{% trans %}Use this form to import an existing codebook into your project. Existing tags will not be deleted, new tags will be added alongside them.{% endtrans %}</p>
8
+ <p>{% trans %}The file should have a column for the tag names, called 'name' or 'tag' or 'path'. It can also optionally have a column called 'description'. Other columns are ignored.{% endtrans %}</p>
9
+ {% if error %}
10
+ <div class="alert alert-danger">{{ error }}</div>
11
+ {% endif %}
12
+ <form action="{{ reverse_url('import_codebook', project.id) }}" method="post" enctype='multipart/form-data'>
13
+ {{ xsrf_form_html() }}
14
+ <div class="form-group row">
15
+ <label class="col-sm-2 col-form-label">{% trans "form label for project name (readonly)" %}Project:{% endtrans %}</label>
16
+ <div class="col-sm-10">
17
+ <span class="form-control" style="font-family: monospace, monospace;">{{ project.name }}</span>
18
+ </div>
19
+ </div>
20
+ <div class="form-group row">
21
+ <label for="file" class="col-sm-2 col-form-label">{% trans "form label for codebook file" %}File:{% endtrans %}</label>
22
+ <div class="col-sm-10">
23
+ <input type="file" class="form-control" name="file" id="file" accept="text/csv,.csv" />
24
+ <small>{% trans %}Accepted format: CSV{% endtrans %}</small>
25
+ </div>
26
+ </div>
27
+ <div class="form-group row">
28
+ <div class="col-sm-10 offset-sm-2">
29
+ <input type="submit" class="btn btn-primary" value="{% trans "form submit button" %}Review{% endtrans %}" />
30
+ <a href="{{ reverse_url('project', project.id) }}" class="btn btn-secondary">{% trans "cancel button" %}Go back{% endtrans %}</a>
31
+ </div>
32
+ </div>
33
+ </form>
34
+ {% endblock %}