Add workspace due indicators and todo assignment
This commit is contained in:
+34
-4
@@ -7,7 +7,7 @@ from html import unescape
|
||||
from urllib.request import Request, urlopen
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth import get_user_model, login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import transaction
|
||||
from django.db.models import Case, Count, IntegerField, ProtectedError, Q, Value, When
|
||||
@@ -32,6 +32,7 @@ FIRST_LINE_URL_RE = re.compile(r'^(https?://\S+)[ \t]*(?:\r?\n|$)')
|
||||
TITLE_RE = re.compile(r'<title[^>]*>(.*?)</title>', re.I | re.S)
|
||||
DATE_COMMAND_RE = re.compile(r'/datum\s+(\d{1,2}\.\d{1,2}\.(?:\d{2}|\d{4}))(?:\s+(\d{1,2}:\d{2}))?\b', re.I)
|
||||
RELATIVE_DATE_COMMAND_RE = re.compile(r'/(morgen|übermorgen|uebermorgen|nächstewoche|naechstewoche)\b', re.I)
|
||||
MENTION_RE = re.compile(r'(?<!\w)@([\w.@+-]+)')
|
||||
CODE_FENCE_RE = re.compile(r'^\s*(`{3,}|~{3,})')
|
||||
INLINE_CODE_RE = re.compile(r'(`+).*?\1', re.S)
|
||||
|
||||
@@ -228,7 +229,7 @@ def user_team_ids(user, workspace=None):
|
||||
|
||||
def visible_items(user, api_key=None, workspace=None):
|
||||
workspace = current_workspace(user, api_key, workspace)
|
||||
qs = Item.objects.select_related('owner', 'team', 'workspace').prefetch_related('tags', 'attachments', 'linked_items')
|
||||
qs = Item.objects.select_related('owner', 'assignee', 'team', 'workspace').prefetch_related('tags', 'attachments', 'linked_items')
|
||||
if workspace:
|
||||
qs = qs.filter(workspace=workspace)
|
||||
elif not user.is_authenticated:
|
||||
@@ -240,12 +241,12 @@ def visible_items(user, api_key=None, workspace=None):
|
||||
qs = qs.filter(tags__in=allowed_tags).distinct()
|
||||
return qs
|
||||
if user.is_authenticated:
|
||||
return qs.filter(Q(owner=user) | Q(team_id__in=user_team_ids(user, workspace)) | Q(visibility=Item.Visibility.PUBLIC)).distinct()
|
||||
return qs.filter(Q(owner=user) | Q(assignee=user) | Q(team_id__in=user_team_ids(user, workspace)) | Q(visibility=Item.Visibility.PUBLIC)).distinct()
|
||||
return qs.none()
|
||||
|
||||
|
||||
def user_can_edit_item(user, item):
|
||||
return item.owner_id == user.id or (item.team_id and TeamMembership.objects.filter(team=item.team, user=user).exists())
|
||||
return item.owner_id == user.id or item.assignee_id == user.id or (item.team_id and TeamMembership.objects.filter(team=item.team, user=user).exists())
|
||||
|
||||
|
||||
def visible_tags(user, workspace=None):
|
||||
@@ -282,6 +283,26 @@ def item_scope_fits_collections(item, visibility, team):
|
||||
) for section in sections)
|
||||
|
||||
|
||||
def assign_item_from_mentions(item):
|
||||
if item.kind != Item.Kind.TODO:
|
||||
if item.assignee_id:
|
||||
item.assignee = None
|
||||
item.save(update_fields=['assignee', 'updated_at'])
|
||||
return item
|
||||
usernames = [match.group(1).lower() for match in MENTION_RE.finditer(without_markdown_code(item.content))]
|
||||
assignee = None
|
||||
if usernames:
|
||||
members = WorkspaceMembership.objects.filter(workspace=item.workspace, user__username__in=usernames).select_related('user')
|
||||
if item.team_id:
|
||||
members = members.filter(user__team_memberships__team=item.team)
|
||||
by_username = {membership.user.username.lower(): membership.user for membership in members}
|
||||
assignee = next((by_username[name] for name in usernames if name in by_username), None)
|
||||
if item.assignee_id != (assignee.id if assignee else None):
|
||||
item.assignee = assignee
|
||||
item.save(update_fields=['assignee', 'updated_at'])
|
||||
return item
|
||||
|
||||
|
||||
def apply_team_from_tags(item, user, force_private=False):
|
||||
if force_private:
|
||||
item.team = None
|
||||
@@ -517,6 +538,7 @@ def index(request):
|
||||
attach_files_and_replace_tokens(item, request.FILES.getlist('files'))
|
||||
item.sync_metadata()
|
||||
apply_team_from_tags(item, request.user, force_private=bool(re.search(r'/private\b', raw, re.I)))
|
||||
assign_item_from_mentions(item)
|
||||
if request.headers.get('HX-Request'):
|
||||
active_tags = [t.strip().lower() for t in request.GET.getlist('tag') if t.strip()]
|
||||
response = render(request, 'core/_create_response.html', {'item': item, 'tags': index_filter_tags(request, request.user, prefs, active_tags, workspace=active_workspace(request)), 'active_tag': active_tags[0] if active_tags else None, 'active_tags': active_tags})
|
||||
@@ -612,8 +634,10 @@ def new_item(request):
|
||||
attach_files_and_replace_tokens(item, request.FILES.getlist('files'))
|
||||
item.sync_metadata()
|
||||
apply_team_from_tags(item, request.user, force_private=bool(re.search(r'/private\b', raw, re.I)))
|
||||
assign_item_from_mentions(item)
|
||||
if collection:
|
||||
item = adapt_item_for_collection(item, collection)
|
||||
assign_item_from_mentions(item)
|
||||
add_collection_section(collection, item)
|
||||
return redirect(next_url)
|
||||
return render(request, 'core/new_item.html', {
|
||||
@@ -635,6 +659,7 @@ def journal(request):
|
||||
attach_files_and_replace_tokens(item, request.FILES.getlist('files'))
|
||||
item.sync_metadata()
|
||||
apply_team_from_tags(item, request.user, force_private=bool(re.search(r'/private\b', raw, re.I)))
|
||||
assign_item_from_mentions(item)
|
||||
if request.headers.get('HX-Request'):
|
||||
return render(request, 'core/_item.html', {'item': item, 'overview_lines': request.user.preferences.overview_lines if hasattr(request.user, 'preferences') else 4})
|
||||
return redirect('journal')
|
||||
@@ -1290,6 +1315,7 @@ def edit_item(request, pk):
|
||||
attach_files_and_replace_tokens(item, request.FILES.getlist('files'))
|
||||
item.sync_metadata()
|
||||
apply_team_from_tags(item, request.user, force_private=item.visibility == Item.Visibility.PRIVATE)
|
||||
assign_item_from_mentions(item)
|
||||
response = render(request, 'core/_item.html', {'item': item, 'overview_lines': request.user.preferences.overview_lines if hasattr(request.user, 'preferences') else 4})
|
||||
response['HX-Trigger'] = 'tagsChanged'
|
||||
return response
|
||||
@@ -1320,6 +1346,7 @@ def item_editor(request, pk):
|
||||
attach_files_and_replace_tokens(item, request.FILES.getlist('files'))
|
||||
item.sync_metadata()
|
||||
apply_team_from_tags(item, request.user, force_private=item.visibility == Item.Visibility.PRIVATE)
|
||||
assign_item_from_mentions(item)
|
||||
return redirect(next_url)
|
||||
return render(request, 'core/item_editor.html', {'item': item, 'tags': visible_tags(request.user, workspace=active_workspace(request)), 'next_url': next_url})
|
||||
|
||||
@@ -1464,6 +1491,7 @@ def serialize_item(item):
|
||||
'comment': item.comment,
|
||||
'visibility': item.visibility,
|
||||
'team': item.team.slug if item.team else None,
|
||||
'assignee': item.assignee.username if item.assignee else None,
|
||||
'is_done': item.is_done,
|
||||
'completed_at': item.completed_at.isoformat() if item.completed_at else None,
|
||||
'due_at': item.due_at.isoformat() if item.due_at else None,
|
||||
@@ -1493,6 +1521,7 @@ def api_items(request):
|
||||
item = Item.objects.create(workspace=current_workspace(user, request.api_key), owner=user, kind=data.get('kind') or kind, visibility=data.get('visibility') or visibility, content=content, url=data.get('url') or url, due_at=due_at)
|
||||
item.sync_metadata()
|
||||
apply_team_from_tags(item, user, force_private=bool(re.search(r'/private\b', raw, re.I)))
|
||||
assign_item_from_mentions(item)
|
||||
return JsonResponse(serialize_item(item), status=201)
|
||||
|
||||
|
||||
@@ -1532,4 +1561,5 @@ def api_item_detail(request, pk):
|
||||
item.save()
|
||||
item.sync_metadata()
|
||||
apply_team_from_tags(item, user, force_private=item.visibility == Item.Visibility.PRIVATE)
|
||||
assign_item_from_mentions(item)
|
||||
return JsonResponse(serialize_item(item))
|
||||
|
||||
Reference in New Issue
Block a user