tg-prepare 2.2.2__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 (82) hide show
  1. tg_prepare-2.2.2.dist-info/METADATA +20 -0
  2. tg_prepare-2.2.2.dist-info/RECORD +82 -0
  3. tg_prepare-2.2.2.dist-info/WHEEL +5 -0
  4. tg_prepare-2.2.2.dist-info/entry_points.txt +4 -0
  5. tg_prepare-2.2.2.dist-info/licenses/LICENSE +202 -0
  6. tg_prepare-2.2.2.dist-info/projects/.secret_key +1 -0
  7. tg_prepare-2.2.2.dist-info/top_level.txt +2 -0
  8. tgp_backend/__init__.py +17 -0
  9. tgp_backend/auth.py +71 -0
  10. tgp_backend/cli.py +95 -0
  11. tgp_backend/config.py +35 -0
  12. tgp_backend/directories.py +79 -0
  13. tgp_backend/interfaces.py +3 -0
  14. tgp_backend/nextcloud.py +146 -0
  15. tgp_backend/project.py +468 -0
  16. tgp_backend/session_manager.py +47 -0
  17. tgp_backend/tgclient.py +187 -0
  18. tgp_backend/user.py +137 -0
  19. tgp_backend/util.py +131 -0
  20. tgp_ui/__init__.py +0 -0
  21. tgp_ui/app.py +150 -0
  22. tgp_ui/routes/__init__.py +0 -0
  23. tgp_ui/routes/auth.py +72 -0
  24. tgp_ui/routes/collection.py +319 -0
  25. tgp_ui/routes/data.py +229 -0
  26. tgp_ui/routes/project.py +103 -0
  27. tgp_ui/routes/publication.py +229 -0
  28. tgp_ui/routes/tabs.py +34 -0
  29. tgp_ui/routes/views.py +66 -0
  30. tgp_ui/static/css/bootstrap-icons.min.css +5 -0
  31. tgp_ui/static/css/bootstrap.min.css +6 -0
  32. tgp_ui/static/css/bootstrap.min.css.map +1 -0
  33. tgp_ui/static/css/main.css +141 -0
  34. tgp_ui/static/css/navbar.css +92 -0
  35. tgp_ui/static/css/simpleXML.css +67 -0
  36. tgp_ui/static/img/favicon.ico +0 -0
  37. tgp_ui/static/img/textgrid-logo.svg +1 -0
  38. tgp_ui/static/js/bootstrap.bundle.min.js +7 -0
  39. tgp_ui/static/js/collectionManager.js +60 -0
  40. tgp_ui/static/js/fileManager.js +153 -0
  41. tgp_ui/static/js/jquery.min.js +2 -0
  42. tgp_ui/static/js/main.js +36 -0
  43. tgp_ui/static/js/modalManager.js +105 -0
  44. tgp_ui/static/js/navbarManager.js +151 -0
  45. tgp_ui/static/js/projectManager.js +60 -0
  46. tgp_ui/static/js/require.js +5 -0
  47. tgp_ui/static/js/sidebarManager.js +44 -0
  48. tgp_ui/static/js/simpleXML.js +193 -0
  49. tgp_ui/static/js/tabManager.js +83 -0
  50. tgp_ui/templates/auth/login.html +42 -0
  51. tgp_ui/templates/details/empty_container.html +16 -0
  52. tgp_ui/templates/details/manage_collection.html +209 -0
  53. tgp_ui/templates/file_tree.html +39 -0
  54. tgp_ui/templates/includes/get_sessionid.html +29 -0
  55. tgp_ui/templates/includes/publish_form.html +55 -0
  56. tgp_ui/templates/includes/set_sessionid.html +26 -0
  57. tgp_ui/templates/includes/upload_form.html +258 -0
  58. tgp_ui/templates/layout.html +74 -0
  59. tgp_ui/templates/macros.html +101 -0
  60. tgp_ui/templates/modal/delete_project.html +25 -0
  61. tgp_ui/templates/modal/empty_container.html +9 -0
  62. tgp_ui/templates/modal/file_explorer_content.html +34 -0
  63. tgp_ui/templates/modal/file_explorer_main.html +22 -0
  64. tgp_ui/templates/modal/file_explorer_nextcloud.html +27 -0
  65. tgp_ui/templates/modal/github_modal.html +29 -0
  66. tgp_ui/templates/modal/nextcloud_login.html +48 -0
  67. tgp_ui/templates/modal/tei_explorer.html +58 -0
  68. tgp_ui/templates/modal/xpath_parser.html +52 -0
  69. tgp_ui/templates/nxc_login.html +25 -0
  70. tgp_ui/templates/nxc_tab.html +15 -0
  71. tgp_ui/templates/project_main.html +36 -0
  72. tgp_ui/templates/project_navbar.html +81 -0
  73. tgp_ui/templates/projects_main.html +58 -0
  74. tgp_ui/templates/tabs/check_upload.html +87 -0
  75. tgp_ui/templates/tabs/edit_project.html +113 -0
  76. tgp_ui/templates/tabs/empty_container.html +12 -0
  77. tgp_ui/templates/tabs/import_data.html +122 -0
  78. tgp_ui/templates/tabs/manage_collections.html +107 -0
  79. tgp_ui/templates/tabs/publication.html +42 -0
  80. tgp_ui/templates/tabs/select_directories.html +68 -0
  81. tgp_ui/templates/tabs/upload.html +42 -0
  82. tgp_ui/templates/tabs/validate_metadata.html +227 -0
@@ -0,0 +1,81 @@
1
+ <!DOCTYPE html>
2
+ <div class="row align-items-center justify-content-between position-relative">
3
+ <!-- Line -->
4
+ <div class="line blank"></div>
5
+ <div class="line blue"></div>
6
+
7
+ <!-- Main Button 1: Manage Project -->
8
+ <div class="col-auto expanding-button text-center">
9
+ <button type="button" class="btn btn-light btn-circle btn-xl" id="tab-group-project" title="Manage Project">
10
+ <i class="bi bi-archive main-icon"></i>
11
+ <div class="row sub-buttons d-none nav">
12
+ <!-- Sub-Button: Project Settings -->
13
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary last-opened " data-bs-toggle="tab"
14
+ data-bs-target="#tab-edit-project"
15
+ data-url="{{ url_for('project.tab_edit_project', projectname=project.name) }}">
16
+ <i class="bi bi-pen fs-4"></i>
17
+ </div>
18
+ <!-- Sub-Button: Upload Data -->
19
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary" data-bs-toggle="tab"
20
+ data-bs-target="#tab-import-data"
21
+ data-url="{{ url_for('data.tab_import_data', projectname=project.name)}}">
22
+ <i class="bi bi-archive fs-4"></i>
23
+ </div>
24
+ </div>
25
+ </button>
26
+ </div>
27
+
28
+ <!-- Main Button 2: Manage Collections -->
29
+ <div class="col-auto text-center">
30
+ <button type="button" class="btn btn-light btn-circle btn-xl" id="tab-group-collections"
31
+ title="Manage Collections">
32
+ <i class="bi bi-pencil"></i>
33
+ <div class="row sub-buttons d-none nav">
34
+ <!-- Sub-Button: Select Data -->
35
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary" data-bs-toggle="tab"
36
+ data-bs-target="#tab-select-directories"
37
+ data-url="{{ url_for('collection.tab_select_directories', projectname=project.name)}}">
38
+ <i class="bi bi-list-nested fs-4"></i>
39
+ </div>
40
+ <!-- Sub-Button: Manage Metadata -->
41
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary" data-bs-toggle="tab"
42
+ data-bs-target="#tab-set-collections"
43
+ data-url="{{ url_for('collection.tab_manage_collections', projectname=project.name)}}">
44
+ <i class="bi bi-pen fs-4"></i>
45
+ </div>
46
+ <!-- Sub-Button: Check Settings & Metadata -->
47
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary" data-bs-toggle="tab"
48
+ data-bs-target="#tab-check-metadata"
49
+ data-url="{{ url_for('collection.tab_validate_metadata', projectname=project.name)}}">
50
+ <i class="bi bi-list-check fs-4"></i>
51
+ </div>
52
+ </div>
53
+ </button>
54
+ </div>
55
+ <!-- Main Button 3: Upload & Publish to TextGrid -->
56
+ <div class="col-auto text-center">
57
+ <button type="button" class="btn btn-light btn-circle btn-xl" id="tab-group-upload">
58
+ <i class="bi bi-cloud"></i>
59
+ <div class="row sub-buttons d-none nav">
60
+ <!-- Sub-Button: TG-Sesssion-ID, -Project-ID & -Upload -->
61
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary" data-bs-toggle="tab"
62
+ data-bs-target="#tab-prepare-upload"
63
+ data-url="{{ url_for('publication.tab_upload', projectname=project.name)}}">
64
+ <i class="bi bi-key fs-4"></i>
65
+ </div>
66
+ <!-- Sub-Button: Check TG-Project -->
67
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary" data-bs-toggle="tab"
68
+ data-bs-target="#tab-check-results"
69
+ data-url="{{ url_for('publication.tab_check_result', projectname=project.name) }}">
70
+ <i class="bi bi-arrow-repeat fs-4"></i>
71
+ </div>
72
+ <!-- Sub-Button: TG-Publish -->
73
+ <div class="col-4 mx-3 btn-circle btn-sm btn-outline-secondary" data-bs-toggle="tab"
74
+ data-bs-target="#tab-publication"
75
+ data-url="{{ url_for('publication.tab_publication', projectname=project.name)}}">
76
+ <i class="bi bi-cloud-upload fs-4"></i>
77
+ </div>
78
+ </div>
79
+ </button>
80
+ </div>
81
+ </div>
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ {% extends 'layout.html' %}
3
+ {% block content %}
4
+ <div class="row">
5
+ <div class="col-12">
6
+ <h1 class="text-center">Projects</h1>
7
+ <p class="text-center text-muted">Manage your projects here. You can create, edit, and delete projects.</p>
8
+ </div>
9
+ </div>
10
+ <div class="row row-cols-xxl-6 row-cols-lg-4 row-cols-md-2">
11
+ {% for project in get_projects() %}
12
+ <div class="card text-center m-1">
13
+ <div class="card-body">
14
+ <h5 class="card-title">{{ project.name }}</h5>
15
+ <a class="btn btn-secondary btn-forward" href="project/{{ project.path }}" title="Edit project">
16
+ <span class="bi bi-pencil-square"></span>
17
+ </a>
18
+ <a class="btn btn-danger load-modal" title="Delete project"
19
+ data-url="{{ url_for('project.modal_delete_project', projectname=project.path) }}">
20
+ <span class="bi bi-trash"></span>
21
+ </a>
22
+ </div>
23
+ </div>
24
+ {% endfor %}
25
+ <div class="card text-center mx-1">
26
+ <div class="card-body">
27
+ <h5 class="card-title">New project</h5>
28
+ <a class="btn btn-secondary" id="createNewProject" title="Add a new project" data-bs-toggle="modal"
29
+ data-bs-target="#newProject">
30
+ <span class="bi-plus-circle"></span>
31
+ </a>
32
+ </div>
33
+ </div>
34
+ </div>
35
+
36
+ <div class="modal fade" id="newProject" tabindex="-1" aria-labelledby="newProjectLabel" aria-hidden="true">
37
+ <div class="modal-dialog">
38
+ <div class="modal-content">
39
+ <form action="{{ url_for('project.new_project') }}" method="POST">
40
+ <div class="modal-header">
41
+ <h5 class="modal-title" id="newProjectLabel">Create new Project</h5>
42
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
43
+ </div>
44
+ <div class="modal-body">
45
+ <div class="mb-3">
46
+ <input type="text" id="projectNameInput" class="form-control" name="projectname"
47
+ placeholder="Projectname" value="{{ projectname }}">
48
+ </div>
49
+ </div>
50
+ <div class="modal-footer">
51
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
52
+ <button type="submit" class="btn btn-primary">Create</button>
53
+ </div>
54
+ </form>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ {% endblock %}
@@ -0,0 +1,87 @@
1
+ <div class="container py-4">
2
+ <div class="row">
3
+ <!-- Form Title -->
4
+ <div class="col-12 text-center mb-4">
5
+ <h1 class="fs-3 fw-bold">Check Upload Results</h1>
6
+ <p class="text-muted">
7
+ Please switch to the <b>TextGrid Repository website</b> to check whether your upload was successful and
8
+ everything is displayed as you expect.
9
+ <br />
10
+ Below you will find helpful explanations and exemplary
11
+ screenshots to guide your review.
12
+ </p>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="row border-bottom mb-4 pb-4">
17
+ <div class="col-6 text-center">
18
+ <a href="https://test.textgridrep.org/projects" target="_blank" class="btn btn-lg btn-success px-5 py-3">
19
+ <i class="bi bi-eye"></i> Open Test-Instance
20
+ </a>
21
+ </div>
22
+ <div class="col-6 text-center">
23
+ <a href="https://textgridrep.org/projects" target="_blank" class="btn btn-lg btn-danger px-5 py-3">
24
+ <i class="bi bi-eye"></i> Open Live-Instance
25
+ </a>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="row">
30
+ <div class="col-md-6 d-flex align-items-center">
31
+ <div id="carouselIndicators" class="carousel carousel-dark slide w-100" data-bs-ride="carousel">
32
+ <div class="carousel-indicators">
33
+ {% for img in images %}
34
+ <button type="button" data-bs-target="#carouselIndicators" data-bs-slide-to="{{ loop.index0 }}" {%
35
+ if loop.first %}class="active" aria-current="true" {% endif %}
36
+ aria-label="Slide {{ loop.index }}">
37
+ </button>
38
+ {% endfor %}
39
+ </div>
40
+ <div class="carousel-inner">
41
+ {% for img in images %}
42
+ <div class="carousel-item {% if loop.first %}active{% endif %}">
43
+ <div class="w-100 p-3 mb-2 bg-dark-subtle text-secondary rounded" style="min-height: 90px;">
44
+ <h5 class="fw-bold text-center">{{img.caption}}</h5>
45
+ </div>
46
+ <img src="{{ url_for('static', filename='img/publication/%s' % img.filename) }}"
47
+ class="d-block w-100 rounded" alt="{{img.alt}}" style="max-width:100%;">
48
+ </div>
49
+ {% endfor %}
50
+ </div>
51
+ <button class="carousel-control-prev" type="button" data-bs-target="#carouselIndicators"
52
+ data-bs-slide="prev">
53
+ <span class="carousel-control-prev-icon" aria-hidden="true"></span>
54
+ <span class="visually-hidden">Previous</span>
55
+ </button>
56
+ <button class="carousel-control-next" type="button" data-bs-target="#carouselIndicators"
57
+ data-bs-slide="next">
58
+ <span class="carousel-control-next-icon" aria-hidden="true"></span>
59
+ <span class="visually-hidden">Next</span>
60
+ </button>
61
+ </div>
62
+ </div>
63
+ <div class="col-md-6 mb-4 mb-md-0">
64
+ <div class="bg-light rounded p-4 h-100">
65
+ <h2 class="fs-4 mb-3">What to check in the preview?</h2>
66
+ <ul class="list-group mb-3">
67
+ {% for img in images %}
68
+ <li class="list-group-item p-0 border-0 bg-transparent">
69
+ <button type="button"
70
+ class="list-group-item list-group-item-action d-flex align-items-center border-0 bg-transparent px-3 py-2"
71
+ data-bs-target="#carouselIndicators" data-bs-slide-to="{{ loop.index0 }}">
72
+ {% if img.icon %}
73
+ <i class="bi {{ img.icon }} me-2"></i>
74
+ {% endif %}
75
+ <span>{{ img.caption }}</span>
76
+ </button>
77
+ </li>
78
+ {% endfor %}
79
+ </ul>
80
+ <div class="alert alert-info mt-3" role="alert">
81
+ <b>Tip:</b> You can repeat this process as often as needed!
82
+ </div>
83
+ </div>
84
+ </div>
85
+
86
+ </div>
87
+ </div>
@@ -0,0 +1,113 @@
1
+ <!DOCTYPE html>
2
+ {% from "macros.html" import multi_input with context %}
3
+ <div class="container py-4">
4
+ <div class="row">
5
+ <!-- Form Title -->
6
+ <div class="col-12 text-center mb-4">
7
+ <h1 class="fs-3 fw-bold">Project Settings</h1>
8
+ <p class="text-muted">Define some basic settings of your project.</p>
9
+ </div>
10
+ </div>
11
+ <div class="row justify-content-center">
12
+ <div class="col-xl-8 col-lg-12">
13
+ <div class="bg-white border rounded-3 shadow-sm p-4">
14
+ <form method="POST" enctype="multipart/form-data"
15
+ action="{{ url_for('project.tab_edit_project', projectname=project.path) }}" id="editProjectForm"
16
+ class="js-tab-submit" data-next-target="#tab-import-data">
17
+ <div class="row">
18
+ <!-- Linke Seite: Name & Bild -->
19
+ <div class="col-md-5 d-flex flex-column align-items-center justify-content-start">
20
+ <div class="mb-3 w-100">
21
+ <label for="inlineFormInputName" class="form-label small">Name</label>
22
+ <input type="text" class="form-control" name="title" id="inlineFormInputName"
23
+ value="{{ project.title }}" placeholder="Name of the project">
24
+ </div>
25
+ <label for="inlineImage"
26
+ class="ratio ratio-1x1 d-block rounded overflow-hidden bg-light border mb-3"
27
+ style="cursor: pointer; height: 18rem; width: 100%;">
28
+ {% if project.avatar %}
29
+ <!-- Display existing project image -->
30
+ <div class="w-100 h-100" style="background-image: url({{ url_for('data.serve_image', filepath=project.avatar) }});
31
+ background-size: cover;
32
+ background-position: center;">
33
+ </div>
34
+ {% else %}
35
+ <div class="d-flex align-items-center justify-content-center bg-light h-100">
36
+ <div class="text-center">
37
+ <i class="bi bi-cloud-upload text-secondary d-block mb-2"
38
+ style="font-size: 2.5rem;"></i>
39
+ <span class="text-secondary">Upload image</span>
40
+ </div>
41
+ </div>
42
+ {% endif %}
43
+ </label>
44
+ <input type="file" class="showPreviewOfAvatar d-none" id="inlineImage" name="avatar"
45
+ accept="image/*">
46
+ </div>
47
+ <!-- Rechte Seite: Rest -->
48
+ <div class="col-md-7 d-flex flex-column">
49
+ <div class="">
50
+ <label for="xsltFileUpload" class="form-label small">Upload XSLT File</label>
51
+ <div class="d-flex align-items-center">
52
+ <!-- Custom File Input -->
53
+ <input class="form-control me-2" type="file" id="xsltFileUpload" name="xslt"
54
+ accept=".xslt,.xsl" title="Upload XSLT">
55
+
56
+ <!-- Löschen-Button -->
57
+ <button {{ "disabled" if not project.xslt else "" }} type="button"
58
+ data-projectname="{{project.name}}" id="deleteXsltButton"
59
+ class="btn btn-outline-danger me-2" title="Delete XSLT">
60
+ <i class="bi bi-trash"></i>
61
+ </button>
62
+
63
+ <!-- Anzeigen-Button -->
64
+ {% set path = url_for('data.serve_file', filepath=project.xslt or "") %}
65
+ <button {{ "disabled" if not project.xslt else "" }} type="button"
66
+ id="viewXsltButton" class="btn btn-outline-primary" title="View XSLT"
67
+ onclick="window.open('{{ path }}', '_blank' )">
68
+ <i class="bi bi-eye"></i>
69
+ </button>
70
+ </div>
71
+ </div>
72
+ <label for="formFile" class="form-label mb-2">
73
+ <div id="uploadStatus" class="mt-2 d-flex align-items-center gap-2">
74
+ {% if project.xslt %}
75
+ <span class="text-success d-flex align-items-center xslt-upload">
76
+ <i class="bi bi-check-circle me-2"></i>
77
+ {{ project.xslt.split('/')[-1] }}
78
+ </span>
79
+ {% else %}
80
+ <span class="text-danger d-flex align-items-center xslt-upload">
81
+ <i class="bi bi-exclamation-circle me-2"></i>
82
+ No file uploaded
83
+ </span>
84
+ {% endif %}
85
+ </div>
86
+ </label>
87
+ <div class="mb-3 flex-grow-1 d-flex flex-column">
88
+ <label for="inlineFormInputDescription" class="form-label small">Description</label>
89
+ <textarea class="form-control flex-grow-1" name="description"
90
+ id="inlineFormInputDescription">{{ project.description or '' }}</textarea>
91
+ </div>
92
+ </div>
93
+ <div class="col-12">
94
+ <div class="mb-3">
95
+ <label for="inlineFormInputCollector" class="form-label small">Collector/s</label>
96
+ {{ multi_input(project.collectors, 'collector') }}
97
+ </div>
98
+ </div>
99
+ <div class="col-12">
100
+ <div class="text-center mt-1">
101
+ <button type="submit" class="btn btn-primary px-4 rounded-3">
102
+ <i class="bi bi-chevron-right"></i> Next
103
+ </button>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </form>
108
+
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <div class="container py-5">
3
+ <div class="row justify-content-center">
4
+ <div class="col-md-6 text-center">
5
+ <div class="spinner-container my-5">
6
+ <div class="spinner-border text-secondary" style="width: 4rem; height: 4rem;" role="status">
7
+ <span class="visually-hidden">Loading...</span>
8
+ </div>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ </div>
@@ -0,0 +1,122 @@
1
+ <!DOCTYPE html>
2
+ {% macro file_upload_form(button_size='btn-sm') %}
3
+ <form action="{{ url_for('data.upload_files', projectname=project.name) }}" method="POST" enctype="multipart/form-data"
4
+ class="d-inline js-tab-submit" id="fileUploadLocal">
5
+ <input type="file" id="fileInput" name="files" multiple webkitdirectory class="d-none">
6
+ <button type="button" class="btn {{ button_size }} btn-outline-primary" id="triggerFileInput">
7
+ <i class="bi bi-folder-plus"></i> Local Files
8
+ </button>
9
+
10
+ </form>
11
+ {% endmacro %}
12
+
13
+ {% block content %}
14
+ {% set has_files = project.list_files_and_folders()|length > 0 %}
15
+ <div class="container py-4">
16
+ <div class="row">
17
+ <!-- Form Title -->
18
+ <div class="col-12 text-center mb-4">
19
+ <h1 class="fs-3 fw-bold">Upload Data</h1>
20
+ <p class="text-muted">Import directories containing your TEI files.</p>
21
+ {% if error_message %}
22
+ <div class="alert alert-danger" role="alert">
23
+ {{ error_message }}
24
+ </div>
25
+ {% endif %}
26
+ </div>
27
+ <div class="col-12">
28
+ {% if has_files %}
29
+ <!-- File display card when files exist -->
30
+ <div class="card">
31
+ <!-- Card header with title and upload buttons -->
32
+ <div class="card-header d-flex justify-content-between align-items-center">
33
+ <div class="btn-group">
34
+ <!-- Upload option buttons -->
35
+ {{ file_upload_form('btn-sm') }}
36
+ <button class="load-modal btn btn-sm btn-outline-dark"
37
+ data-url="{{ url_for('data.modal_github', projectname=project.name) }}">
38
+ <i class=" bi bi-github"></i> GitHub
39
+ </button>
40
+ <button class="load-modal btn btn-sm btn-outline-dark"
41
+ data-url="{{ url_for('data.modal_nextcloud', projectname=project.name) }}">
42
+ <i class="bi bi-cloud-plus"></i> Nextcloud
43
+ </button>
44
+ </div>
45
+ </div>
46
+ <!-- List of files and folders -->
47
+ <div class="list-group list-group-flush">
48
+ {% for item in project.list_files_and_folders() %}
49
+ <div class="list-group-item list-group-item-action d-flex align-items-center">
50
+ <!-- Different display for folders and files -->
51
+ {% if item.type == 'folder' %}
52
+ <button
53
+ class="btn btn-link text-decoration-none text-dark p-0 d-flex align-items-center flex-grow-1"
54
+ data-bs-toggle="modal" data-bs-target="#folderModal{{ loop.index }}">
55
+ <i class="bi bi-folder-fill text-warning me-2"></i>
56
+ <span>{{ item.name }}</span>
57
+ </button>
58
+ {% else %}
59
+ <i class="bi bi-file-text me-2"></i>
60
+ <span class="flex-grow-1">{{ item.name }}</span>
61
+ {% endif %}
62
+
63
+ <!-- Action buttons for each item -->
64
+ <div class="btn-group">
65
+ <button class="btn btn-sm btn-outline-danger delete-folder" data-item-path="{{ item.path }}"
66
+ data-item-name="{{ item.name }}" data-projectname="{{ project.name }}"
67
+ data-item-type="{{ item.type }}">
68
+ <i class="bi bi-trash"></i>
69
+ </button>
70
+ </div>
71
+ </div>
72
+ {% endfor %}
73
+ </div>
74
+
75
+ <!-- Folder modals - rendered at the end of file -->
76
+ {% for item in project.list_files_and_folders() %}
77
+ {% if item.type == 'folder' %}
78
+ {% with folder=item, index=loop.index %}
79
+ {% include 'modal/file_explorer_main.html' %}
80
+ {% endwith %}
81
+ {% endif %}
82
+ {% endfor %}
83
+
84
+ </div>
85
+ {% else %}
86
+ <!-- Welcome screen shown when no files exist -->
87
+ <div class="row min-vh-50 align-items-center justify-content-center">
88
+ <div class="col-12 text-center">
89
+ <div class="card border-0 bg-transparent">
90
+ <div class="card-body">
91
+ <div class="btn-group">
92
+ {{ file_upload_form('btn-lg') }}
93
+ <button class="load-modal btn btn-lg btn-outline-dark"
94
+ data-url="{{ url_for('data.modal_github', projectname=project.name) }}">
95
+ <i class="bi bi-github"></i> GitHub
96
+ </button>
97
+ <button class="load-modal btn btn-lg btn-outline-dark"
98
+ data-url="{{ url_for('data.modal_nextcloud', projectname=project.name) }}">
99
+ <i class="bi bi-cloud-plus"></i> Nextcloud
100
+ </button>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ {% endif %}
107
+ </div>
108
+ {% if has_files %}
109
+ <div class=" row mt-4">
110
+ <div class="col-12 text-end">
111
+ <button type="button" class="btn btn-primary btn-next" data-next-target="#tab-select-directories"
112
+ data-additional-button="#tab-group-collections">
113
+ <i class="bi bi-chevron-right"></i> Next
114
+ </button>
115
+ </div>
116
+ </div>
117
+ {% endif %}
118
+ </div>
119
+
120
+ <!-- Modal definitions -->
121
+
122
+ {% endblock %}
@@ -0,0 +1,107 @@
1
+ <!-- Main container with padding -->
2
+ <div class="container py-4">
3
+ <div class="row">
4
+ <!-- Page Title -->
5
+ <div class="col-12 text-center mb-4">
6
+ <h1 class="fs-3 fw-bold">Manage Collections & Metadata</h1>
7
+ </div>
8
+
9
+ <!-- Main Content Area -->
10
+ <div class="row">
11
+ <!-- Left Column - Collections List -->
12
+ <div class="col-md-3">
13
+ <div class="card border-0 shadow-sm">
14
+ <div class="card-header bg-light">
15
+ <h5 class="card-title mb-0">Collections</h5>
16
+ </div>
17
+ <div class="card-body pt-0">
18
+ <!-- Dynamic Collections List -->
19
+ {% if project.collections %}
20
+ {% for _collection in project.collections %}
21
+ <div class="row py-3 border-bottom border-2">
22
+ <!-- Loop through available collections -->
23
+ {% set collection = project.collections[_collection] %}
24
+ <!-- Collection Row -->
25
+ <div class="col-12">
26
+ <button
27
+ class="btn btn-secondary btn-outline-secondary text-white w-100 text-start edit-collection-button text-truncate"
28
+ style="height: 50px;"
29
+ data-url="{{ url_for('collection.load_collection', projectname=project.path, collectionname=_collection) }}">
30
+ <span class="overflow-hidden">
31
+ {{ collection.config.title.short }}
32
+ </span>
33
+ </button>
34
+ </div>
35
+ <div class="col-12">
36
+ <div class="row g-2 mt-1 border-bottom border-1 pb-2">
37
+ <div class="col-auto">
38
+ <button class="btn btn-sm btn-outline-secondary load-modal"
39
+ title="Open XPath Parser"
40
+ data-url="{{ url_for('collection.modal_xpath_parser', projectname=project.path, collectionname=_collection) }}">
41
+ XPath Parser
42
+ </button>
43
+ </div>
44
+ <div class="col-auto">
45
+ <button class="btn btn-sm btn-outline-secondary load-modal"
46
+ title="Open TEI Explorer"
47
+ data-url="{{ url_for('collection.modal_tei_explorer', projectname=project.path, collectionname=_collection) }}">
48
+ TEI Explorer
49
+ </button>
50
+ </div>
51
+ </div>
52
+ <div class="row g-2 mt-1">
53
+ <div class="col-auto">
54
+ <a class="btn btn-sm btn-outline-secondary" title="Download Collection Config"
55
+ href="{{ url_for('collection.download_collection_yaml', projectname=project.path, collectionname=_collection) }}"
56
+ download>
57
+ <i class="bi bi-download"></i> Download
58
+ </a>
59
+ </div>
60
+ <div class="col-auto">
61
+ <form
62
+ action="{{ url_for('collection.upload_collection_yaml', projectname=project.path, collectionname=_collection) }}"
63
+ method="POST" enctype="multipart/form-data"
64
+ class="d-inline js-tab-submit config-upload"
65
+ data-reload-to-target="#detailsContent">
66
+ <input type="file" name="config" accept=".yaml,.yml"
67
+ class="d-none config-upload">
68
+ <button type="button"
69
+ class="btn btn-sm btn-outline-secondary trigger-config-upload">
70
+ <i class="bi bi-filetype-yml"></i> Upload
71
+ </button>
72
+ </form>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ {% endfor %}
78
+ {% else %}
79
+ <!-- Message for no collections -->
80
+ <div class="col-12">
81
+ <p class="text-center text-muted">No collections available.</p>
82
+ </div>
83
+ {% endif %}
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ <!-- Right Column - Collection Details -->
89
+ <div class="col-md-9">
90
+ <div class="card border-0 shadow-sm">
91
+ <div class="card-header bg-light">
92
+ <h5 class="card-title mb-0">Details</h5>
93
+ </div>
94
+ <!-- Details Content Area -->
95
+ {% include 'details/empty_container.html' %}
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ <div class="row mt-4">
101
+ <div class="col-12 text-end">
102
+ <button type="button" class="btn btn-primary btn-next" data-next-target="#tab-check-metadata">
103
+ <i class="bi bi-chevron-right"></i> Next
104
+ </button>
105
+ </div>
106
+ </div>
107
+ </div>
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <div class="container py-4">
3
+ <div class="row">
4
+ <!-- Page Title -->
5
+ <div class="col-12 text-center mb-4">
6
+ <h1 class="fs-3 fw-bold">Publish Project</h1>
7
+ </div>
8
+ <!-- Instanzauswahl -->
9
+ <div class="col-12 mb-4">
10
+ <h4 class="text-center mb-3">Choose the instance:</h4>
11
+ <ul class="nav nav-tabs justify-content-center" id="instanceTabs" role="tablist">
12
+ <li class="nav-item" role="presentation">
13
+ <button class="nav-link active" id="test-tab" data-bs-toggle="tab" data-bs-target="#test-pane"
14
+ type="button" role="tab" aria-controls="test-pane" aria-selected="true">
15
+ Test
16
+ </button>
17
+ </li>
18
+ <li class="nav-item" role="presentation">
19
+ <button class="nav-link" id="prod-tab" data-bs-toggle="tab" data-bs-target="#prod-pane"
20
+ type="button" role="tab" aria-controls="prod-pane" aria-selected="false">
21
+ Live
22
+ </button>
23
+ </li>
24
+ </ul>
25
+ </div>
26
+
27
+ <!-- Tab-Inhalte -->
28
+ <div class="tab-content">
29
+ <!-- Testinstanz -->
30
+ <div class="tab-pane fade show active" id="test-pane" role="tabpanel" aria-labelledby="test-tab">
31
+ {% set instance='test' %}
32
+ {% include 'includes/publish_form.html' %}
33
+ </div>
34
+
35
+ <!-- Produktionsinstanz -->
36
+ <div class="tab-pane fade" id="prod-pane" role="tabpanel" aria-labelledby="prod-tab">
37
+ {% set instance='live' %}
38
+ {% include 'includes/publish_form.html' %}
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </div>