Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

YDA-5720 config publication terms #341

Merged
merged 25 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a9059eb
add basic ui for publication terms
FuHsinyu Aug 1, 2024
914eda2
add endpoint to retrive local terms and add preview window for terms …
FuHsinyu Aug 1, 2024
e62bb72
add reading default terms from irods, if current terms not exist in /…
FuHsinyu Aug 1, 2024
38f7510
add endpoint to update terms displayed when user click publication
FuHsinyu Aug 1, 2024
0a9f535
add saving terms to /config if there is no such in the /config directory
FuHsinyu Aug 1, 2024
ef1f03c
add the modal for preview publication terms
FuHsinyu Aug 2, 2024
d47d2c2
let preiview function retrive current terms from endpoints instead of…
FuHsinyu Aug 2, 2024
a8cdfeb
Rearrange UI components to make admin page slightly consistent in vis…
FuHsinyu Aug 2, 2024
7cda848
Move create_preview js script out of the html to a separate .js file …
FuHsinyu Aug 5, 2024
034198d
refacorized codes
FuHsinyu Aug 5, 2024
94c7668
Add type hint and doc strings
FuHsinyu Aug 5, 2024
1aaa405
Remove unused codes
FuHsinyu Aug 5, 2024
a9f5492
Clean codes
FuHsinyu Aug 5, 2024
247ea0f
Formatted codes
FuHsinyu Aug 5, 2024
ff15b0b
Fix the vault js to fetch terms from flask endpoint
FuHsinyu Aug 5, 2024
f106c69
Let the preview function preview the terms in textarea
FuHsinyu Aug 6, 2024
d6afcd7
Add DOMpurify to santinize html content
FuHsinyu Aug 6, 2024
ddb9671
move purify to /static/lib/ and add purify map
FuHsinyu Aug 6, 2024
a778e35
Merge branch 'development' into YDA-5720-config-publication-terms
lwesterhof Aug 6, 2024
6af3638
Fix javascript problems
lwesterhof Aug 6, 2024
054fb80
add comment to global createPreview for JS lint
FuHsinyu Aug 6, 2024
6c15755
disable no-unused-vars for createPreview
FuHsinyu Aug 6, 2024
7c954c2
Refactor to remove eslint ignore
lwesterhof Aug 6, 2024
7a47233
Add missing document ready
lwesterhof Aug 6, 2024
1d20bcf
Fix id name
lwesterhof Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion admin/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
__copyright__ = 'Copyright (c) 2024, Utrecht University'
__license__ = 'GPLv3, see LICENSE'

import html
import json
from functools import wraps
from os import path
Expand All @@ -14,6 +15,7 @@
flash,
Flask,
g,
jsonify,
redirect,
render_template,
request,
Expand Down Expand Up @@ -46,7 +48,9 @@ def index() -> Response:
if session['admin']:
# reload theme options
theme_directories = get_theme_directories(app.config.get('YODA_THEME_PATH'))
return render_template("admin.html", theme_directories=theme_directories)
publication_terms_text = get_publication_terms()
return render_template("admin.html", theme_directories=theme_directories, publication_terms=publication_terms_text)

else:
return abort(403)

Expand Down Expand Up @@ -198,3 +202,72 @@ def set_theme_loader(app: Flask, remove_cache: Optional[bool] = False) -> None:
# Remove template cache
if remove_cache and hasattr(app.jinja_env, 'cache'):
app.jinja_env.cache = {}


@admin_bp.route('/set_publication_terms', methods=['POST'])
@admin_required
def set_publication_terms() -> Response:
"""Receives new publication terms from a POST request, sanitizes, and saves them.

:return: A redirection response to the admin index page.
"""
# Retrives new terms from the POST request
new_terms = request.form['publicationTerms']
sanitized_terms = html.escape(new_terms)

# Save new terms to local file
try:
publication_terms_path = path.join(app.config['YODA_CONFIG_PATH'], 'publication_terms.html')
with open(publication_terms_path, 'w') as file:
file.write(sanitized_terms)
flash("Publication terms updated successfully.", "success")
return redirect(url_for("admin_bp.index"))
except Exception:
flash("Failed to update publication terms", "error")

return redirect(url_for("admin_bp.index"))


@admin_bp.route("/get_publication_terms")
def publication_terms() -> Response:
"""Retrieve and return the current publication terms as JSON.

:return: JSON object containing the current publication terms.
"""
terms = get_publication_terms()
return jsonify({'terms': terms})


def get_publication_terms() -> Optional[str]:
"""Retrieve publication terms from a local file or from an iRODS API fallback.

:return: A string containing the html-like publication terms.
"""
publication_terms_path = path.join(app.config['YODA_CONFIG_PATH'], 'publication_terms.html')

# Attempt to read from local file
if path.exists(publication_terms_path):
try:
with open(publication_terms_path, 'r') as file:
publication_terms_html = file.read()
return html.unescape(publication_terms_html) # Convert escaped terms to html
except Exception:
flash("Failed to load publication terms from file.", "error")

# Fallback to API if the file does not exist or an error occurred
try:
response = api.call('vault_get_publication_terms', {})
publication_terms_html = response["data"]

# Save the data to a local file if it was fetched from the API
try:
with open(publication_terms_path, 'w') as file:
file.write(html.escape(publication_terms_html))
except Exception:
flash("Failed to save publication terms to file", "error")

return publication_terms_html
except Exception:
flash("Failed to load publication terms from API", "error")

return "Error: failed to read publication terms"
17 changes: 17 additions & 0 deletions admin/static/admin/js/create_preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* global bootstrap, DOMPurify */
'use strict'

// Function to preview publication terms in a specific modal
function createPreview () {
lwesterhof marked this conversation as resolved.
Show resolved Hide resolved
// Get the content of the textarea and sanitize it
const termsText = document.getElementById('publicationTerms').value
const sanitizedContent = DOMPurify.sanitize(termsText)

// Set the content in the modal body for preview
const modalBody = document.querySelector('#confirmAgreementConditions .modal-body')
modalBody.innerHTML = sanitizedContent

// Show the modal
const myModal = new bootstrap.Modal(document.getElementById('confirmAgreementConditions'))
myModal.show()
}
65 changes: 63 additions & 2 deletions admin/templates/admin/admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

{% block title %}{{ super() }} ‐ Administration{% endblock title %}

{% block scripts %}
<script src="{{ url_for('static', filename='lib/purify-3.1.6/js/purify.min.js') }}"></script>
<script src="{{ url_for('admin_bp.static', filename='js/create_preview.js') }}"></script>
lwesterhof marked this conversation as resolved.
Show resolved Hide resolved
{% endblock scripts %}

{% block content %}
<div class="container mt-4">
<header>
Expand All @@ -26,7 +31,8 @@ <h2 class="card-title">Set Maintenance Banner</h2>
</div>
<!-- Checkbox to mark the banner as important -->
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="importance" name="importance" {{ 'checked' if config.get('banner', {}).get('importance', False) else '' }}>
<input type="checkbox" class="form-check-input" id="importance" name="importance"
{{ 'checked' if config.get('banner', {}).get('importance', False) else '' }}>
<label class="form-check-label" for="importance">Mark as Important</label>
</div>
<button type="submit" class="btn btn-primary" name="Set Banner">Set Banner</button>
Expand All @@ -39,6 +45,7 @@ <h2 class="card-title">Set Maintenance Banner</h2>
</div>
</div>
</section>

<!-- Theme Change section-->
<section class="card my-3">
<div class="card-body">
Expand All @@ -51,14 +58,68 @@ <h2 class="card-title">Change Portal Theme</h2>
<!-- Add default theme option -->
{% set current_theme = config.get('YODA_THEME', 'uu') %}
{% for folder in theme_directories %}
<option value="{{ folder }}" {% if folder == current_theme %}selected{% endif %}> {{ config.get('YODA_THEME_MAPPING').get(folder, folder) }}</option>
<option value="{{ folder }}" {% if folder == current_theme %}selected{% endif %}> {{ config.get('YODA_THEME_MAPPING').get(folder, folder) }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary" name="Change Theme">Change Theme</button>
</form>
</div>
</section>

<!-- Update Publication Terms -->
<section class="card my-3">
<div class="card-body">
<h2 class="card-title">Update Publication Terms</h2>
<div class="d-flex justify-content-start align-items-end" style="width: 100%;">
<!-- Form to update publication terms -->
<form action="{{ url_for('admin_bp.set_publication_terms') }}" method="POST"
class="flex-fill me-2 needs-validation" novalidate>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="mb-3">
<label for="publicationTerms">Current Publication Terms:</label>
<textarea class="form-control" id="publicationTerms" name="publicationTerms" rows="10"
style="width: 110%;" required>{{ publication_terms|safe }}</textarea>
</div>
<button type="submit" class="btn btn-primary">Update Terms</button>
</form>
<!-- Form to preview updated terms -->
<form action="javascript:void(0);" method="POST" class="flex-shrink-1">
lwesterhof marked this conversation as resolved.
Show resolved Hide resolved
<button type="button" class="btn btn-secondary" onclick="createPreview()">Preview Terms</button>
</form>
</div>
</div>
</section>

<!-- Confirmation Agreement Conditions Modal, copied from vault/browse.html -->
<div class="modal" tabindex="-1" role="dialog" id="confirmAgreementConditions">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<!-- Updated content will be displayed here -->
</div>
<div class="modal-footer">
<div class="row">
<div class="col-sm-12">
<fieldset>
<input type="checkbox" class="confirm-conditions" id="checkbox-confirm-conditions">
<label for="checkbox-confirm-conditions">Please confirm that you agree with the
above</label>
</fieldset>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button"
class="btn btn-primary ms-2 action-confirm-submit-for-publication">Confirm
agreement</button>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
{% endblock content %}
3 changes: 3 additions & 0 deletions static/lib/purify-3.1.6/js/purify.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions static/lib/purify-3.1.6/js/purify.min.js.map

Large diffs are not rendered by default.

26 changes: 18 additions & 8 deletions vault/static/vault/js/vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,24 @@ $(function () {

$('#confirmAgreementConditions .modal-body').text('') // clear it first

Yoda.call('vault_get_publication_terms', {}).then((data) => {
$('#confirmAgreementConditions .modal-body').html(data)

// Set default status and show dialog.
$('.action-confirm-submit-for-publication').prop('disabled', true)
$('#confirmAgreementConditions .confirm-conditions').prop('checked', false)

$('#confirmAgreementConditions').modal('show')
// Fetch publication terms from the Flask endpoint
$.ajax({
url: '/admin/get_publication_terms',
type: 'GET',
success: function (response) {
$('#confirmAgreementConditions .modal-body').html(response.terms)

// Set default status and show dialog.
$('.action-confirm-submit-for-publication').prop('disabled', true)
$('#confirmAgreementConditions .confirm-conditions').prop('checked', false)

$('#confirmAgreementConditions').modal('show')
},
error: function () {
console.error('Failed to load publication terms.')
// Handle error case
$('#confirmAgreementConditions .modal-body').html('Failed to load publication terms.')
}
})
})

Expand Down
Loading