Skip to content

Commit

Permalink
YDA-5720: configure publication terms on admin page
Browse files Browse the repository at this point in the history
  • Loading branch information
FuHsinyu authored Aug 6, 2024
1 parent 58fcfd0 commit f93b893
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 11 deletions.
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"
19 changes: 19 additions & 0 deletions admin/static/admin/js/create_preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* global bootstrap, DOMPurify */
'use strict'

$(document).ready(function () {
// Preview publication terms in a modal.
$('#create-preview').on('click', function () {
// 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()
})
})
61 changes: 59 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>
{% 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,64 @@ <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 -->
<button type="button" id="create-preview" class="btn btn-secondary">Preview Terms</button>
</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" data-bs-dismiss="modal">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

0 comments on commit f93b893

Please sign in to comment.