Skip to content
Snippets Groups Projects
Commit 0a1e10f4 authored by dcz's avatar dcz
Browse files

Merge branch 'master' into unified-frame-edit-view

# Conflicts:
#	unifier/models.py
parents 4885e545 58676fc0
No related branches found
No related tags found
No related merge requests found
Showing with 236 additions and 16 deletions
function update_entries() {
const can_see_assignees = has_permission("auth.view_user");
const can_see_assignees = has_permission("users.view_assignment");
function is_assigned_to_user_renderer (data) {
return (
......@@ -14,6 +14,7 @@ function update_entries() {
columns: [
{ data: 'lemma' },
{ data: 'POS' },
{ render: data => (data && data.lexical_units && data.lexical_units.some(lu => lu.assignee_username !== null)) ? gettext("nie") : gettext("tak") },
can_see_assignees ? { data: 'assignee_username' } : { render: is_assigned_to_user_renderer },
]
});
......@@ -51,7 +52,7 @@ function setup_lexical_units_table(drilldown, lexical_units, can_see_assignees)
</tr>
`).click(function () {
$(this).addClass('table-primary').siblings().removeClass("table-primary");
get_entry($(drilldown.data("row")).data("entry"), false); // TODO replace with loading LU details view
get_lexical_unit(lexical_unit.pk, $(drilldown.data("row")).data("entry"));
});
}
var table = $(`
......@@ -75,3 +76,42 @@ function setup_lexical_units_table(drilldown, lexical_units, can_see_assignees)
$("tbody", table).append(get_lexical_unit_row(lexical_unit));
});
}
function setup_notes($container, $template, lexical_unit_pk, entry) {
$container.html($template.children().clone());
$('.show-note-form', $container).click(function () {
$('.note-form', $container).html($('#note-form-template > div', $container).clone());
$('.hide-note-form', $container).click(function () { $('.note-form', $container).html(''); });
$('.add-note', $container).click(function () {
console.log($('textarea[name=note]', $container).val());
$.ajax({
type : 'post',
url : $('#note-form-template').data('url').replace('MODEL', 'meanings.LexicalUnit').replace('PK', lexical_unit_pk),
dataType : 'json',
data : { note: $('.note-form textarea[name=note]').val() },
timeout : 5000,
success : function (response) {
get_lexical_unit(lexical_unit_pk, entry);
},
error : function () {
alert(gettext('Nie udało się dodać notatki.'));
}
});
});
return false
});
$.ajax({
type: 'get',
url: $('.notes-table').data('url').replace('MODEL', 'meanings.LexicalUnit').replace('PK', lexical_unit_pk),
success: function (data) {
data.notes.map(function (note) {
$('.notes-table tbody', $container).append(`<tr><td>${note.note}</td><td>${note.owner_label}</td></tr>`);
});
}
})
}
function get_lexical_unit(pk, entry) {
get_entry(entry, false); // TODO replace with loading LU details view
setup_notes($('#lexical-unit-notes'), $('#lexical-unit-notes-template'), pk, entry);
}
function update_entries() {
const can_see_assignees = has_permission("auth.view_user");
const can_see_assignees = has_permission("users.view_assignment");
function is_assigned_to_user_renderer (data) {
return (
......
......@@ -12,4 +12,4 @@
{% block left_pane %}{% include "entries_list.html" %}{% endblock %}
{% block right_pane %}{% include "entry_display.html" with show_tabs=True %}{% endblock %}
{% block right_pane %}{% include "entry_display.html" %}{% endblock %}
{% load i18n %}
{% if show_tabs %}
<ul class="nav nav-pills nav-justified p-1" id="entryTabs" role="tablist">
<li class="nav-item mr-1">
<a class="btn btn-sm btn-outline-dark nav-link active" id="semantics-tab" data-toggle="tab" href="#semantics" role="tab" aria-controls="semantics" aria-selected="true">
......@@ -18,7 +17,6 @@
</a>
</li>
</ul>
{% endif %}
<div class="tab-content h-100 w-100 p-0" id="entryTabsContent">
<div class="col h-100 w-100 p-0 tab-pane show active" id="semantics" role="tabpanel" aria-labelledby="semantics-tab">
......
......@@ -12,4 +12,4 @@
{% block left_pane %}{% include "unification_entries_list.html" %}{% endblock %}
{% block right_pane %}{% include "entry_display.html" %}{% endblock %}
{% block right_pane %}{% include "unification_lexical_unit_display.html" %}{% endblock %}
......@@ -5,7 +5,8 @@
<tr>
<th class="p-1">{% trans "Lemat" %}</th>
<th class="p-1">{% trans "Część mowy" %}</th>
{% if perms.users.view_user %}
<th class="p-1">{% trans "Do pobrania" %}</th>
{% if perms.users.view_assignment %}
<th class="p-1">{% trans "Semantyk" %}</th>
{% else %}
<th class="p-1">{% trans "Moje (w opracowaniu)" %}</th>
......
{% load i18n %}
<div class="tab-content h-100 w-100 p-0" id="entryTabsContent">
<div class="col h-100 w-100 p-0 tab-pane show active" id="semantics" role="tabpanel" aria-labelledby="semantics-tab">
<div class="row m-0 p-0" id="semantics-top-pane">
<div class="col h-100 px-1 pt-0 pb-0 overflow-auto" id="semantics-frames-pane">
<div id="semantics-frames"></div>
<div id="lexical-unit-notes-template" class="d-none">{% include 'notes.html' %}</div>
<div id="lexical-unit-notes"></div>
</div>
<div class="col h-100 px-1 pt-0 pb-0 overflow-auto" id="semantics-schemata-pane">
<div id="semantics-schemata"></div>
</div>
</div>
<div class="row m-0 p-0 overflow-auto" id="semantics-examples-pane">
<table id="semantics-examples" class="table table-sm table-hover">
<thead>
<tr>
<th scope="col">{% trans "Przykład" %}<i id="examples-argument"></i><i id="examples-lu"></i><i id="examples-schema"></i></th>
<th scope="col">{% trans "Źródło" %}</th>
<th scope="col">{% trans "Opinia" %}</th>
</tr>
</thead>
<tbody id="semantics-examples-list">
</tbody>
</table>
<p class="mx-1 my-1"id="semantics-no-examples">{% trans "Brak przykładów" %}</p>
</div>
</div>
<div class="col h-100 w-100 p-0 tab-pane" id="syntax" role="tabpanel" aria-labelledby="syntax-tab">
<div class="col w-100 px-1 pt-0 pb-0 overflow-auto" id="syntax-schemata-pane">
<div id="syntax-schemata"></div>
</div>
<div class="col w-100 p-0 overflow-auto" id="syntax-examples-pane">
<table id="syntax-examples" class="table table-sm table-hover">
<thead>
<tr>
<th scope="col">{% trans "Przykład" %}</th>
<th scope="col">{% trans "Źródło" %}</th>
<th scope="col">{% trans "Opinia" %}</th>
</tr>
</thead>
<tbody id="syntax-examples-list">
</tbody>
</table>
<p class="mx-1 my-1"id="syntax-no-examples">{% trans "Brak przykładów" %}</p>
</div>
</div>
<div class="col h-100 w-100 p-0 tab-pane" id="examples" role="tabpanel" aria-labelledby="examples-tab">
<table id="unmatched-examples" class="table table-sm table-hover">
<thead>
<tr>
<th scope="col">{% trans "Przykład" %}</th>
<th scope="col">{% trans "Źródło" %}</th>
<th scope="col">{% trans "Opinia" %}</th>
</tr>
</thead>
<tbody id="unmatched-examples-list">
</tbody>
</table>
<p class="mx-1 my-1"id="unmatched-no-examples">{% trans "Brak przykładów" %}</p>
</div>
</div>
......@@ -412,6 +412,7 @@ def get_entries(request):
{
'lexical_units': [
{
'pk': lu.pk,
'display': str(lu),
'assignee_username': (
assignment.user.username if (assignment := lu.assignments.first()) else None
......
......@@ -174,3 +174,5 @@ EMAIL_HOST_USER = get_environment('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = get_environment('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = get_environment('EMAIL_USE_TLS', mapper=boolean_mapper)
EMAIL_USE_SSL = get_environment('EMAIL_USE_SSL', mapper=boolean_mapper)
SUPER_LEXICOGRAPHS_GROUP_NAME = 'Super Leksykografowie'
......@@ -7,6 +7,8 @@ from django.utils.translation import gettext_lazy as _
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Layout, Fieldset, ButtonHolder, Submit
from users.models import Note
class UserForm(forms.ModelForm):
group = forms.ModelChoiceField(queryset=Group.objects.all(), label=_('Grupa'))
......@@ -102,3 +104,9 @@ password_reset_set_password_form_helper.layout = Layout(
HTML(format_lazy('<a class="btn btn-sm btn-light" href="{}">{}</a>', reverse_lazy('dash'), _("Wróć"))),
)
)
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ["note"]
from django.conf import settings
from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
from users.models import Assignment, Note
class Command(BaseCommand):
def handle(self, **options):
Permission.objects.update_or_create(
content_type=ContentType.objects.get_for_model(Note),
codename="view_all_notes",
defaults={"name": "View all notes"}
)
admins, __ = Group.objects.get_or_create(name='Admini')
admins.permissions.add(
Permission.objects.get(codename='view_user', content_type=ContentType.objects.get_for_model(User)),
Permission.objects.get(codename='add_user', content_type=ContentType.objects.get_for_model(User)),
Permission.objects.get(codename='change_user', content_type=ContentType.objects.get_for_model(User)),
Permission.objects.get(codename='delete_user', content_type=ContentType.objects.get_for_model(User)),
self._get_permission(User, 'view_user'),
self._get_permission(User, 'add_user'),
self._get_permission(User, 'change_user'),
self._get_permission(User, 'delete_user'),
self._get_permission(Assignment, 'view_assignment'),
self._get_permission(Note, 'view_all_notes'),
)
lexicographs, __ = Group.objects.get_or_create(name='Leksykografowie')
lexicographs.permissions.add(
# TODO
)
super_lexicographs, __ = Group.objects.get_or_create(name='Super Leksykografowie')
super_lexicographs, __ = Group.objects.get_or_create(name=settings.SUPER_LEXICOGRAPHS_GROUP_NAME)
super_lexicographs.permissions.add(
# TODO
self._get_permission(Assignment, 'view_assignment'),
self._get_permission(Note, 'view_all_notes'),
)
def _get_permission(self, model, codename) -> Permission:
return Permission.objects.get(codename=codename, content_type=ContentType.objects.get_for_model(model))
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import gettext_lazy as _
class Assignment(models.Model):
......@@ -13,3 +15,31 @@ class Assignment(models.Model):
unique_together = [
("subject_ct", "subject_id"),
]
class NoteQuerySet(models.QuerySet):
def for_user(self, user):
notes = (
self.filter(author=user).annotate(owner_label=models.Value(_('własne'), output_field=models.CharField()))
)
if user.has_perm('user.view_all_notes'):
notes |= (
self.exclude(author=user).annotate(owner_label=models.F('author__username'))
)
else:
notes |= (
self.exclude(author=user).filter(author__groups__name=settings.SUPER_LEXICOGRAPHS_GROUP_NAME)
.annotate(owner_label=models.Value(_('Super'), output_field=models.CharField()))
)
return notes
class Note(models.Model):
author = models.ForeignKey("auth.User", on_delete=models.PROTECT)
subject_ct = models.ForeignKey(ContentType, on_delete=models.PROTECT)
subject_id = models.PositiveIntegerField()
subject = GenericForeignKey('subject_ct', 'subject_id')
note = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
objects = models.Manager.from_queryset(NoteQuerySet)()
{% load i18n %}
<div class="border p-2">
<div id="note-form-template" class="d-none" data-url="{% url 'users:add_note' model='MODEL' pk='PK' %}">
<div class="mb-2 border-bottom pb-2">
<div class="clearfix">
<h6 class="float-left">{% trans 'Dodaj notatkę' %}</h6>
<a href="#" class="btn btn-xs btn-outline-dark float-right hide-note-form">&times;</a>
</div>
<div class="d-flex">
<textarea name="note" class="form-control mr-3"></textarea>
<a href="#" class="btn btn-sm btn-outline-dark ml-auto align-self-end add-note">{% trans 'Dodaj' %}</a>
</div>
</div>
</div>
<div class="note-form"></div>
<div>
<div class="mb-2">
<h6 class="mt-4 d-inline">{% trans 'Notatki' %}</h6>
<a href="#" class="show-note-form btn btn-xs btn-outline-dark ml-2">{% trans 'Dodaj' %}</a>
</div>
<table class="table table-sm table-striped border notes-table" data-url="{% url 'users:get_notes' model='MODEL' pk='PK' %}">
<tbody></tbody>
</table>
</div>
</div>
from django.contrib.auth import views as auth_views
from django.urls import include, path, reverse_lazy
from django.urls import include, path, re_path, reverse_lazy
from django.views.generic import TemplateView
from . import views
......@@ -54,4 +54,6 @@ urlpatterns = [
),
name='password_reset_confirm'
),
re_path(r'^notes/(?P<model>\w+.\w+)/(?P<pk>\w+)/$', views.get_notes, name='get_notes'),
re_path(r'^notes/(?P<model>\w+.\w+)/(?P<pk>\w+)/add/$', views.add_note, name='add_note'),
]
from django.apps import apps
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.shortcuts import get_current_site
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, render, redirect
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from users.forms import UserForm, UserProfileForm
from common.decorators import ajax
from users.forms import UserForm, UserProfileForm, NoteForm
from users.models import Note
from users.utils import send_new_user_email
......@@ -49,3 +55,32 @@ def user_edit(request, pk):
else:
form = UserForm(instance=user)
return render(request, 'user_form.html', {'form': form, 'title': _('Edytuj użytkownika')})
@login_required
def get_notes(request, model, pk):
model = apps.get_model(*model.split('.'))
subject = get_object_or_404(model, pk=pk)
ct = ContentType.objects.get_for_model(model)
notes = Note.objects.filter(subject_ct=ct, subject_id=subject.pk).for_user(request.user).order_by('created_at')
return JsonResponse({
"notes": [{
"pk": note.pk,
"owner_label": note.owner_label,
"created_at": note.created_at,
"note": note.note,
} for note in notes],
})
@require_http_methods(["POST"])
@login_required
def add_note(request, model, pk):
model = apps.get_model(*model.split('.'))
subject = get_object_or_404(model, pk=pk)
note = Note(author=request.user, subject=subject)
form = NoteForm(instance=note, data=request.POST)
if form.is_valid():
form.save()
return JsonResponse({})
return JsonResponse(form.errors.get_json_data(), status=400)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment