Skip to content
Commits on Source (24)
......@@ -6,6 +6,20 @@ All notable changes to this project will be documented in this file.
The format is based on `Keep a Changelog`_,
and this project adheres to `Semantic Versioning`_.
`2.0rc3`_ - 2021-07-26
----------------------
Added
~~~~~
* Support PDF generation without available request object (started completely from background).
* Display a loading animation while fetching search results in the sidebar.
Fixed
~~~~~
* Make search suggestions selectable using the arrow keys.
* Use correct HTML 5 elements for the search frontend and fix CSS accordingly.
`2.0rc2`_ - 2021-06-24
---------------------
......@@ -284,3 +298,4 @@ Fixed
.. _2.0b2: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.0b2
.. _2.0rc1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.0rc1
.. _2.0rc2: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.0rc2
.. _2.0rc3: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.0rc3
# Generated by Django 3.2.4 on 2021-07-24 13:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0019_fix_uniqueness_per_site'),
]
operations = [
migrations.AlterField(
model_name='pdffile',
name='person',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pdf_files', to='core.person', verbose_name='Owner'),
),
]
......@@ -1037,7 +1037,12 @@ class PDFFile(ExtensibleModel):
return timezone.now() + timedelta(minutes=get_site_preferences()["general__pdf_expiration"])
person = models.ForeignKey(
to=Person, on_delete=models.CASCADE, verbose_name=_("Owner"), related_name="pdf_files"
to=Person,
on_delete=models.CASCADE,
blank=True,
null=True,
verbose_name=_("Owner"),
related_name="pdf_files",
)
expires_at = models.DateTimeField(
verbose_name=_("File expires at"), default=_get_default_expiration
......
import os
from glob import glob
from socket import getfqdn
from django.utils.translation import gettext_lazy as _
......@@ -66,7 +67,10 @@ UWSGI = {
UWSGI_SERVE_STATIC = True
UWSGI_SERVE_MEDIA = False
ALLOWED_HOSTS = _settings.get("http.allowed_hosts", [])
ALLOWED_HOSTS = _settings.get("http.allowed_hosts", [getfqdn(), "localhost", "127.0.0.1", "[::1]"])
BASE_URL = _settings.get(
"http.base_url", "http://localhost:8000" if DEBUG else f"https://{ALLOWED_HOSTS[0]}"
)
# Application definition
INSTALLED_APPS = [
......
......@@ -32,10 +32,11 @@ Autocomplete.prototype.setup = function () {
// Trigger the "keyup" event if input gets focused
this.query_box.focus(function () {
self.query_box.trigger("keydown");
self.query_box.trigger("input");
});
this.query_box.keyup(function () {
this.query_box.on("input", () => {
console.log("Input changed, fetching again...")
var query = self.query_box.val();
if (query.length < self.minimum_length) {
......@@ -97,11 +98,16 @@ Autocomplete.prototype.fetch = function (query) {
var self = this;
$.ajax({
url: this.url
, data: {
url: this.url,
data: {
'q': query
}
, success: function (data) {
},
beforeSend: (request, settings) => {
$('#search-results').remove();
self.setLoader(true);
},
success: function (data) {
self.setLoader(false);
self.show_results(data);
}
})
......@@ -122,3 +128,7 @@ Autocomplete.prototype.setSelectedResult = function (element) {
this.selected_element = element;
console.log("New element: ", element);
};
Autocomplete.prototype.setLoader = function (value) {
$("#search-loader").css("display", (value === true ? "block" : "none"))
}
......@@ -179,17 +179,25 @@ ul.sidenav li.logo > a:hover {
box-shadow: none;
}
.sidenav li.search .search-wrapper > i.material-icons {
.sidenav li.search .search-wrapper > button.search-button {
position: absolute;
top: 21px;
top: calc(50% - 18px);
right: 10px;
cursor: pointer;
}
button.btn-flat.search-button:hover {
background-color: $button-disabled-background;
}
a.collection-item.search-item {
padding: 20px 10px;
}
div#search-loader {
margin: 0.5rem 0 0 0;
display: none;
}
div#search-results {
position: absolute;
margin-top: -10px;
......
......@@ -80,8 +80,11 @@
<li class="search">
<form method="get" action="{% url "haystack_search" %}" id="search-form" class="autocomplete">
<div class="search-wrapper">
<input id="search" name="q" placeholder="{% trans "Search" %}">
<i class="material-icons">search</i>
<input id="search" name="q" type="search" enterkeyhint="search" placeholder="{% trans "Search" %}">
<button class="btn btn-flat search-button" type="submit" aria-label="{% trans "Search" %}">
<i class="material-icons">search</i>
</button>
<div class="progress" id="search-loader"><div class="indeterminate"></div></div>
</div>
</form>
</li>
......
import os
import subprocess # noqa
from tempfile import TemporaryDirectory
from typing import Optional
from typing import Optional, Tuple
from urllib.parse import urljoin
from django.conf import settings
from django.core.files import File
from django.core.files.base import ContentFile
from django.http.request import HttpRequest
......@@ -14,6 +16,7 @@ from django.utils import timezone
from django.utils.translation import get_language
from django.utils.translation import gettext as _
from celery.result import AsyncResult
from celery_progress.backend import ProgressRecorder
from aleksis.core.celery import app
......@@ -64,6 +67,26 @@ def generate_pdf(
recorder.set_progress(1, 1)
def generate_pdf_from_template(
template_name: str, context: Optional[dict] = None, request: Optional[HttpRequest] = None
) -> Tuple[PDFFile, AsyncResult]:
"""Start a PDF generation task and return the matching file object and Celery result."""
html_template = render_to_string(template_name, context, request)
file_object = PDFFile.objects.create(html_file=ContentFile(html_template, name="source.html"))
# As this method may be run in background and there is no request available,
# we have to use a predefined URL from settings then
if request:
html_url = request.build_absolute_uri(file_object.html_file.url)
else:
html_url = urljoin(settings.BASE_URL, file_object.html_file.url)
result = generate_pdf.delay(file_object.pk, html_url, lang=get_language())
return file_object, result
def render_pdf(request: HttpRequest, template_name: str, context: dict = None) -> HttpResponse:
"""Start PDF generation and show progress page.
......@@ -72,14 +95,7 @@ def render_pdf(request: HttpRequest, template_name: str, context: dict = None) -
if not context:
context = {}
html_template = render_to_string(template_name, context, request)
file_object = PDFFile.objects.create(
person=request.user.person, html_file=ContentFile(html_template, name="source.html")
)
html_url = request.build_absolute_uri(file_object.html_file.url)
result = generate_pdf.delay(file_object.pk, html_url, lang=get_language())
file_object, result = generate_pdf_from_template(template_name, context, request)
redirect_url = reverse("redirect_to_pdf_file", args=[file_object.pk])
......
......@@ -47,6 +47,7 @@ Install some packages from the Debian package system.
python3 \
python3-dev \
libldap2-dev \
libpq-dev \
libsasl2-dev \
yarnpkg \
python3-virtualenv \
......
......@@ -31,7 +31,7 @@ author = "AlekSIS team"
# The short X.Y version
version = "2.0"
# The full version, including alpha/beta/rc tags
release = "2.0rc1"
release = "2.0rc3"
# -- General configuration ---------------------------------------------------
......
This diff is collapsed.
[tool.poetry]
name = "AlekSIS-Core"
version = "2.0rc2"
version = "2.0rc3"
packages = [
{ include = "aleksis" }
]
......@@ -82,7 +82,7 @@ django-celery-email = "^3.0.0"
django-jsonstore = "^0.5.0"
django-polymorphic = "^3.0.0"
django-colorfield = "^0.4.0"
django-bleach = "^0.6.1"
django-bleach = "^0.7.0"
django-guardian = "^2.2.0"
rules = "^2.2"
django-cache-memoize = "^0.1.6"
......@@ -91,7 +91,7 @@ celery-haystack-ng = "^0.20"
django-dbbackup = "^3.3.0"
spdx-license-list = "^0.5.0"
license-expression = "^1.2"
django-reversion = "^3.0.7"
django-reversion = "^4.0.0"
django-favicon-plus-reloaded = "^1.1.2"
django-health-check = "^3.12.1"
psutil = "^5.7.0"
......@@ -100,7 +100,7 @@ django-cachalot = "^2.3.2"
django-prometheus = "^2.1.0"
django-model-utils = "^4.0.0"
bs4 = "^0.0.1"
django-allauth = "^0.44.0"
django-allauth = "^0.45.0"
django-uwsgi-ng = "^1.1.0"
django-extensions = "^3.1.1"
ipython = "^7.20.0"
......