Skip to content
Snippets Groups Projects
Commit e53b830e authored by Tomáš Valenta's avatar Tomáš Valenta
Browse files

handle permissions for files, UI changes

parent 63159cfd
Branches
No related tags found
No related merge requests found
Pipeline #11922 passed
......@@ -26,6 +26,7 @@ RUN DATABASE_URL=postgres://x/x \
DEFAULT_CONTRACTEE_ZIP=x \
DEFAULT_CONTRACTEE_DISTRICT=x \
DEFAULT_CONTRACTEE_ICO_NUMBER=x \
DOWNLOADVIEW_BACKEND=x \
python manage.py collectstatic --noinput --settings=registry.settings.production
RUN bash -c "adduser --disabled-login --quiet --gecos app app && \
......
# Generated by Django 4.1.4 on 2023-03-20 16:11
from django.db import migrations, models
import markdownx.models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0004_alter_contract_options_and_more'),
]
operations = [
migrations.AlterField(
model_name='contract',
name='notes',
field=markdownx.models.MarkdownxField(blank=True, help_text='Poznámky jsou viditelné pro všechny, kteří mohou smlouvu spravovat a pro tajné čtenáře.', null=True, verbose_name='Poznámky'),
),
migrations.AlterField(
model_name='contractee',
name='address_country',
field=models.CharField(default='Česká Republika', max_length=256, verbose_name='Země'),
),
migrations.AlterField(
model_name='signee',
name='address_country',
field=models.CharField(default='Česká Republika', max_length=256, verbose_name='Země'),
),
]
import typing
from django.conf import settings
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse
from markdownx.models import MarkdownxField
from shared.models import NameStrMixin
......@@ -433,6 +436,16 @@ class Contract(NameStrMixin, models.Model):
("view_confidential", "Zobrazit tajné informace"),
)
@property
def primary_contract_url(self) -> typing.Union[None, str]:
if self.primary_contract is None:
return
return reverse(
"contracts:view_contract",
args=(self.primary_contract.id,),
)
def get_public_files(self):
return ContractFile.objects.filter(
contract=self,
......@@ -569,6 +582,13 @@ class ContractFile(NameStrMixin, models.Model):
verbose_name="Soubory",
)
@property
def protected_url(self) -> str:
return reverse(
"contracts:download_contract_file",
args=(self.id,),
)
class Meta:
app_label = "contracts"
......
<a
class="p-1.5 rounded-sm text-ellipsis whitespace-nowrap bg-gray-200 duration-100 hover:bg-gray-300 hover:no-underline"
class="flex gap-2 items-baseline w-min p-1.5 rounded-sm text-ellipsis whitespace-nowrap bg-gray-200 duration-100 hover:bg-gray-300 hover:no-underline"
href="{{ url }}"
>{% if icon %}<i class="{{ icon }} mr-2"></i>{% endif %}{{ content }}</a>
>{% if icon %}<i class="{{ icon }}"></i>{% endif %}{{ content }}</a>
......@@ -2,7 +2,9 @@
{% load subtract markdownify %}
{% block content %}
<h1 class="head-alt-lg mb-10"><i class="ico--file-blank mr-4"></i>{{ contract.name }}</h1>
<h1 class="head-alt-lg mb-10">
<i class="ico--file-blank mr-4"></i>{{ contract.name }}
</h1>
<table class="table table-auto w-full table--striped table--bordered mb-7">
<tbody>
......@@ -34,9 +36,7 @@
<tr>
<td class="w-1/5 !p-2.5">Primární smlouva</td>
<td class="w-4/5 !p-2.5">
<a
href="{% url "contracts:view_contract" contract.primary_contract.id %}"
>{{ contract.primary_contract.name }}</a>
{% include "contracts/includes/tag.html" with url=contract.primary_contract_url icon="ico--file-blank" content=contract.primary_contract.name %}
</td>
</tr>
{% endif %}
......@@ -70,7 +70,7 @@
<td class="w-4/5 !p-2.5">
{{ contract.cost_amount }} Kč
{% if contract.cost_unit != contract.CostUnits.TOTAL %}
/ {{ contract.get_cost_unit_display }}
/ {{ contract.get_cost_unit_display.lower }}
{% else %}
celkem
{% endif %}
......@@ -84,8 +84,8 @@
{% if files|length != 0 %}
<ul class="flex gap-2">
{% for file in files %}
<li class="flex">
{% include "contracts/includes/tag.html" with url=file.file.url icon="ico--attachment" content=file.name %}
<li>
{% include "contracts/includes/tag.html" with url=file.protected_url icon="ico--attachment" content=file.name %}
</li>
{% endfor %}
</ul>
......@@ -112,8 +112,8 @@
{% if files|length != 0 %}
<ul class="flex gap-2">
{% for file in files %}
<li class="flex">
{% include "contracts/includes/tag.html" with url=file.file.url icon="ico--attachment" content=file.name %}
<li>
{% include "contracts/includes/tag.html" with url=file.protected_url icon="ico--attachment" content=file.name %}
</li>
{% endfor %}
</ul>
......@@ -218,7 +218,7 @@
{% if intents|length != 0 %}
<ul class="flex gap-2">
{% for intent in intents %}
<li class="flex">
<li>
{% include "contracts/includes/tag.html" with url=intent.url icon="ico--link" content=intent.name %}
</li>
{% endfor %}
......@@ -247,7 +247,7 @@
<td class="w-4/5 !pl-2.5 !py-1 !pr-1">
<ul class="flex gap-2">
{% for issue in issues %}
<li class="flex">
<li>
{% include "contracts/includes/tag.html" with url="#TODO" icon="ico--warning" content=issue.name %}
</li>
{% endfor %}
......
......@@ -8,6 +8,11 @@ app_name = "contracts"
urlpatterns = [
path("", views.index, name="index"),
path("contracts/<int:id>", views.view_contract, name="view_contract"),
path(
"contracts/files/<str:pk>",
views.ContractFileDownloadView.as_view(),
name="download_contract_file",
),
path(
"contracts/autocomplete",
dal.autocomplete.Select2QuerySetView.as_view(model=models.Contract),
......
from django.conf import settings
from django.core.paginator import Paginator
from django.shortcuts import render
from django_downloadview import ObjectDownloadView
from guardian.shortcuts import get_objects_for_user
from .models import Contract
from .models import Contract, ContractFile
# Create your views here.
class ContractFileDownloadView(ObjectDownloadView):
model = ContractFile
file_field = "file"
attachment = False
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
if not self.current_user.has_perm("contracts.view_confidential"):
queryset = queryset.filter(is_public=True)
return queryset
def get(self, request, *args, **kwargs):
self.current_user = request.user
return super().get(request, *args, **kwargs)
def index(request):
......
......@@ -15,3 +15,5 @@ DEFAULT_CONTRACTEE_STREET="Na Moráni 360/3"
DEFAULT_CONTRACTEE_ZIP="128 00"
DEFAULT_CONTRACTEE_DISTRICT="Praha 2"
DEFAULT_CONTRACTEE_ICO_NUMBER="71339698"
DOWNLOADVIEW_BACKEND=django_downloadview.nginx.XAccelRedirectMiddleware
......@@ -79,6 +79,7 @@ MIDDLEWARE = [
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_http_exceptions.middleware.ExceptionHandlerMiddleware",
"django_http_exceptions.middleware.ThreadLocalRequestMiddleware",
#"django_downloadview.SmartDownloadMiddleware",
]
ROOT_URLCONF = "registry.urls"
......@@ -173,7 +174,7 @@ USE_TZ = True
## Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = "static/"
STATIC_URL = "/static/"
WEBPACK_LOADER = {
"DEFAULT": {
......@@ -188,8 +189,17 @@ WEBPACK_LOADER = {
## Media files
MEDIA_URL = "media/"
MEDIA_URL = "/media/"
DEFAULT_FILE_STORAGE = "django_downloadview.storage.SignedFileSystemStorage"
#DOWNLOADVIEW_BACKEND = env.str("DOWNLOADVIEW_BACKEND")
#DOWNLOADVIEW_RULES = [
#{
#"source_url": "/media/",
#"destination_url": "/media-nginx-optimized/",
#},
#]
## Server
......
......@@ -15,6 +15,7 @@ Including another URLconf
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path, re_path
from django.views.static import serve
......@@ -29,12 +30,3 @@ urlpatterns = [
path("oidc/", include("oidc.urls")),
path("admin/", admin.site.urls),
] + pirates_urlpatterns
if settings.DEBUG:
urlpatterns.append(
re_path(
r"^media/(?P<path>.*)$",
serve,
{"document_root": settings.MEDIA_ROOT}
),
)
......@@ -5,7 +5,8 @@ django-admin-interface==0.24.2
django-admin-rangefilter==0.9.0
django-autocomplete-light==3.9.4
django-database-url==1.0.3
django-fieldsets-with-inlines==0.6
django-downloadview==2.3.0
django-fieldsets-with-inlines==0.6
django-import-export==3.1.0
djhacker==0.2.3
django-ordered-model==3.7.1
......
......@@ -36,7 +36,7 @@ class User(pirates_models.AbstractUser):
from contracts.models import Contract
return Contract.objects.filter(
approval_state=Contract.ApprovalStates.NO
is_approved=False
).count()
class Meta:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment