Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
urls: Generic object path parsing
Browse files Browse the repository at this point in the history
Avoid using fixed structure for object URLs. All objects are now
referected as path with possibly variable number of elements. This
allows to introduce component categorization in the future, see #263.

This change also forced change of some URLs which would no longer be
distinguishable (anything under component can no longer have variable
suffix).

Changed widgets URLs to better fit in the new structure. Redirects for
old ones are in place.
nijel committed Aug 9, 2023

Verified

This commit was signed with the committer’s verified signature. The key has expired.
coreyja Corey Alexander
1 parent 2a1e562 commit 4cbc140
Showing 106 changed files with 1,384 additions and 1,915 deletions.
8 changes: 5 additions & 3 deletions weblate/accounts/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -479,7 +479,7 @@ def test_watch(self):
self.assertEqual(self.user.subscription_set.count(), 9)

# Watch project
self.client.post(reverse("watch", kwargs=self.kw_project))
self.client.post(reverse("watch", kwargs={"path": self.project.get_url_path()}))
self.assertEqual(self.user.profile.watched.count(), 1)
self.assertEqual(
self.user.subscription_set.filter(project=self.project).count(), 0
@@ -492,13 +492,15 @@ def test_watch(self):
)

# Mute notifications for project
self.client.post(reverse("mute", kwargs=self.kw_project))
self.client.post(reverse("mute", kwargs={"path": self.project.get_url_path()}))
self.assertEqual(
self.user.subscription_set.filter(project=self.project).count(), 18
)

# Unwatch project
self.client.post(reverse("unwatch", kwargs=self.kw_project))
self.client.post(
reverse("unwatch", kwargs={"path": self.project.get_url_path()})
)
self.assertEqual(self.user.profile.watched.count(), 0)
self.assertEqual(
self.user.subscription_set.filter(project=self.project).count(), 0
16 changes: 3 additions & 13 deletions weblate/accounts/urls.py
Original file line number Diff line number Diff line change
@@ -55,19 +55,9 @@
path("userdata/", weblate.accounts.views.userdata, name="userdata"),
path("unsubscribe/", weblate.accounts.views.unsubscribe, name="unsubscribe"),
path("subscribe/", weblate.accounts.views.subscribe, name="subscribe"),
path("watch/<name:project>/", weblate.accounts.views.watch, name="watch"),
path(
"watch/<name:project>/<name:component>/",
weblate.accounts.views.watch,
name="watch",
),
path("unwatch/<name:project>/", weblate.accounts.views.unwatch, name="unwatch"),
path(
"mute/<name:project>/<name:component>/",
weblate.accounts.views.mute_component,
name="mute",
),
path("mute/<name:project>/", weblate.accounts.views.mute_project, name="mute"),
path("watch/<object_path:path>/", weblate.accounts.views.watch, name="watch"),
path("unwatch/<object_path:path>/", weblate.accounts.views.unwatch, name="unwatch"),
path("mute/<object_path:path>/", weblate.accounts.views.mute, name="mute"),
path("remove/", weblate.accounts.views.user_remove, name="remove"),
path("confirm/", weblate.accounts.views.confirm, name="confirm"),
path("login/", weblate.accounts.views.WeblateLoginView.as_view(), name="login"),
45 changes: 21 additions & 24 deletions weblate/accounts/views.py
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@
from weblate.auth.models import Invitation, User, get_auth_keys
from weblate.auth.utils import format_address
from weblate.logger import LOGGER
from weblate.trans.models import Change, Component, Suggestion, Translation
from weblate.trans.models import Change, Component, Project, Suggestion, Translation
from weblate.trans.models.component import translation_prefetch_tasks
from weblate.trans.models.project import prefetch_project_flags
from weblate.utils import messages
@@ -108,7 +108,7 @@
from weblate.utils.request import get_ip_address, get_user_agent
from weblate.utils.stats import prefetch_stats
from weblate.utils.token import get_token
from weblate.utils.views import get_component, get_paginator, get_project
from weblate.utils.views import get_paginator, parse_path

CONTACT_TEMPLATE = """
Message from %(name)s <%(email)s>:
@@ -1022,35 +1022,37 @@ def userdata(request):

@require_POST
@login_required
def watch(request, project, component=None):
def watch(request, path):
user = request.user
if component:
redirect_obj = component_obj = get_component(request, project, component)
obj = component_obj.project
redirect_obj = obj = parse_path(request, path, (Component, Project))
if isinstance(obj, Component):
project = obj.project

# Mute project level subscriptions
mute_real(user, scope=SCOPE_PROJECT, component=None, project=obj)
mute_real(user, scope=SCOPE_PROJECT, component=None, project=project)
# Manually enable component level subscriptions
for default_subscription in user.subscription_set.filter(scope=SCOPE_WATCHED):
subscription, created = user.subscription_set.get_or_create(
notification=default_subscription.notification,
scope=SCOPE_COMPONENT,
component=component_obj,
component=obj,
project=None,
defaults={"frequency": default_subscription.frequency},
)
if not created and subscription.frequency != default_subscription.frequency:
subscription.frequency = default_subscription.frequency
subscription.save(update_fields=["frequency"])
else:
redirect_obj = obj = get_project(request, project)

# Watch project
obj = project
user.profile.watched.add(obj)
return redirect(redirect_obj)


@require_POST
@login_required
def unwatch(request, project):
obj = get_project(request, project)
def unwatch(request, path):
obj = parse_path(request, path, (Project,))
request.user.profile.watched.remove(obj)
request.user.subscription_set.filter(
Q(project=obj) | Q(component__project=obj)
@@ -1074,18 +1076,13 @@ def mute_real(user, **kwargs):

@require_POST
@login_required
def mute_component(request, project, component):
obj = get_component(request, project, component)
mute_real(request.user, scope=SCOPE_COMPONENT, component=obj, project=None)
return redirect(
"{}?notify_component={}#notifications".format(reverse("profile"), obj.pk)
)


@require_POST
@login_required
def mute_project(request, project):
obj = get_project(request, project)
def mute(request, path):
obj = parse_path(request, path, (Component, Project))
if isinstance(obj, Component):
mute_real(request.user, scope=SCOPE_COMPONENT, component=obj, project=None)
return redirect(
"{}?notify_component={}#notifications".format(reverse("profile"), obj.pk)
)
mute_real(request.user, scope=SCOPE_PROJECT, component=None, project=obj)
return redirect(
"{}?notify_project={}#notifications".format(reverse("profile"), obj.pk)
9 changes: 1 addition & 8 deletions weblate/addons/models.py
Original file line number Diff line number Diff line change
@@ -107,14 +107,7 @@ def save(
)

def get_absolute_url(self):
return reverse(
"addon-detail",
kwargs={
"project": self.component.project.slug,
"component": self.component.slug,
"pk": self.pk,
},
)
return reverse("addon-detail", kwargs={"pk": self.pk})

def store_change(self, action):
Change.objects.create(
50 changes: 30 additions & 20 deletions weblate/addons/views.py
Original file line number Diff line number Diff line change
@@ -9,35 +9,30 @@
from django.views.generic import ListView, UpdateView

from weblate.addons.models import ADDONS, Addon
from weblate.trans.models import Component
from weblate.utils import messages
from weblate.utils.views import ComponentViewMixin
from weblate.utils.views import PathViewMixin


class AddonViewMixin(ComponentViewMixin):
class AddonList(PathViewMixin, ListView):
paginate_by = None
model = Addon
supported_path_types = (Component,)

def get_queryset(self):
component = self.get_component()
if not self.request.user.has_perm("component.edit", component):
if not self.request.user.has_perm("component.edit", self.component):
raise PermissionDenied("Can not edit component")
self.kwargs["component_obj"] = component
return Addon.objects.filter_component(component)
self.kwargs["component_obj"] = self.component
return Addon.objects.filter_component(self.component)

def get_success_url(self):
component = self.get_component()
return reverse(
"addons",
kwargs={"project": component.project.slug, "component": component.slug},
)
return reverse("addons", kwargs={"path": self.component.get_url_path()})

def redirect_list(self, message=None):
if message:
messages.error(self.request, message)
return redirect(self.get_success_url())


class AddonList(AddonViewMixin, ListView):
paginate_by = None
model = Addon

def get_context_data(self, **kwargs):
result = super().get_context_data(**kwargs)
component = self.kwargs["component_obj"]
@@ -54,8 +49,12 @@ def get_context_data(self, **kwargs):
)
return result

def setup(self, request, **kwargs):
super().setup(request, **kwargs)
self.component = self.get_path_object()

def post(self, request, **kwargs):
component = self.get_component()
component = self.component
component.acting_user = request.user
name = request.POST.get("name")
addon = ADDONS.get(name)
@@ -99,7 +98,7 @@ def post(self, request, **kwargs):
)


class AddonDetail(AddonViewMixin, UpdateView):
class AddonDetail(UpdateView):
model = Addon
template_name_suffix = "_detail"

@@ -115,10 +114,21 @@ def get_context_data(self, **kwargs):
result["addon"] = self.object.addon
return result

def get_success_url(self):
return reverse("addons", kwargs={"path": self.object.component.get_url_path()})

def get_object(self):
obj = super().get_object()
if not self.request.user.has_perm("component.edit", obj.component):
raise PermissionDenied("Can not edit component")
return obj

def post(self, request, *args, **kwargs):
obj = self.get_object()
obj.component.acting_user = request.user
if "delete" in request.POST:
obj.component.acting_user = request.user
obj.delete()
return self.redirect_list()
return redirect(
reverse("addons", kwargs={"path": obj.component.get_url_path()})
)
return super().post(request, *args, **kwargs)
41 changes: 16 additions & 25 deletions weblate/checks/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -48,8 +48,8 @@ def test_check(self):
self.assertRedirects(
response,
reverse(
"show_check_project",
kwargs={"name": "same", "project": self.project.slug},
"show_check_path",
kwargs={"name": "same", "path": self.project.get_url_path()},
),
)
response = self.client.get(
@@ -60,70 +60,61 @@ def test_check(self):
def test_project(self):
response = self.client.get(
reverse(
"show_check_project",
kwargs={"name": "same", "project": self.project.slug},
"show_check_path",
kwargs={"name": "same", "path": self.project.get_url_path()},
)
)
self.assertContains(response, "/same/")

response = self.client.get(
reverse(
"show_check_project",
kwargs={"name": "same", "project": self.project.slug},
"show_check_path",
kwargs={"name": "same", "path": self.project.get_url_path()},
),
{"lang": "cs"},
)
self.assertContains(response, "/same/")

response = self.client.get(
reverse(
"show_check_project",
kwargs={"name": "ellipsis", "project": self.project.slug},
"show_check_path",
kwargs={"name": "ellipsis", "path": self.project.get_url_path()},
)
)
self.assertContains(response, "…")

response = self.client.get(
reverse(
"show_check_project",
kwargs={"name": "non-existing", "project": self.project.slug},
"show_check_path",
kwargs={"name": "non-existing", "path": self.project.get_url_path()},
)
)
self.assertEqual(response.status_code, 404)

def test_component(self):
response = self.client.get(
reverse(
"show_check_component",
kwargs={
"name": "same",
"project": self.project.slug,
"component": self.component.slug,
},
"show_check_path",
kwargs={"name": "same", "path": self.component.get_url_path()},
)
)
self.assertContains(response, "/same/")

response = self.client.get(
reverse(
"show_check_component",
"show_check_path",
kwargs={
"name": "multiple_failures",
"project": self.project.slug,
"component": self.component.slug,
"path": self.component.get_url_path(),
},
)
)
self.assertContains(response, "/multiple_failures/")

response = self.client.get(
reverse(
"show_check_component",
kwargs={
"name": "non-existing",
"project": self.project.slug,
"component": self.component.slug,
},
"show_check_path",
kwargs={"name": "non-existing", "path": self.component.get_url_path()},
)
)
self.assertEqual(response.status_code, 404)
Loading

0 comments on commit 4cbc140

Please sign in to comment.