diff --git a/apiserver/plane/app/serializers/__init__.py b/apiserver/plane/app/serializers/__init__.py index d8364f9314..0511f315ec 100644 --- a/apiserver/plane/app/serializers/__init__.py +++ b/apiserver/plane/app/serializers/__init__.py @@ -37,7 +37,6 @@ from .project import ( ) from .state import StateSerializer, StateLiteSerializer from .view import ( - GlobalViewSerializer, IssueViewSerializer, ) from .cycle import ( diff --git a/apiserver/plane/app/serializers/view.py b/apiserver/plane/app/serializers/view.py index c46a545d05..32fba7d0c9 100644 --- a/apiserver/plane/app/serializers/view.py +++ b/apiserver/plane/app/serializers/view.py @@ -2,44 +2,13 @@ from rest_framework import serializers # Module imports -from .base import BaseSerializer, DynamicBaseSerializer +from .base import DynamicBaseSerializer from .workspace import WorkspaceLiteSerializer from .project import ProjectLiteSerializer -from plane.db.models import GlobalView, IssueView +from plane.db.models import IssueView from plane.utils.issue_filters import issue_filters -class GlobalViewSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer( - source="workspace", read_only=True - ) - - class Meta: - model = GlobalView - fields = "__all__" - read_only_fields = [ - "workspace", - "query", - ] - - def create(self, validated_data): - query_params = validated_data.get("query_data", {}) - if bool(query_params): - validated_data["query"] = issue_filters(query_params, "POST") - else: - validated_data["query"] = dict() - return GlobalView.objects.create(**validated_data) - - def update(self, instance, validated_data): - query_params = validated_data.get("query_data", {}) - if bool(query_params): - validated_data["query"] = issue_filters(query_params, "POST") - else: - validated_data["query"] = dict() - validated_data["query"] = issue_filters(query_params, "PATCH") - return super().update(instance, validated_data) - - class IssueViewSerializer(DynamicBaseSerializer): is_favorite = serializers.BooleanField(read_only=True) project_detail = ProjectLiteSerializer(source="project", read_only=True) @@ -54,6 +23,7 @@ class IssueViewSerializer(DynamicBaseSerializer): "workspace", "project", "query", + "owned_by", ] def create(self, validated_data): diff --git a/apiserver/plane/app/urls/views.py b/apiserver/plane/app/urls/views.py index 36372c03ad..a2f8e2ac88 100644 --- a/apiserver/plane/app/urls/views.py +++ b/apiserver/plane/app/urls/views.py @@ -3,8 +3,8 @@ from django.urls import path from plane.app.views import ( IssueViewViewSet, - GlobalViewViewSet, - GlobalViewIssuesViewSet, + WorkspaceViewViewSet, + WorkspaceViewIssuesViewSet, IssueViewFavoriteViewSet, ) @@ -34,7 +34,7 @@ urlpatterns = [ ), path( "workspaces//views/", - GlobalViewViewSet.as_view( + WorkspaceViewViewSet.as_view( { "get": "list", "post": "create", @@ -44,7 +44,7 @@ urlpatterns = [ ), path( "workspaces//views//", - GlobalViewViewSet.as_view( + WorkspaceViewViewSet.as_view( { "get": "retrieve", "put": "update", @@ -56,7 +56,7 @@ urlpatterns = [ ), path( "workspaces//issues/", - GlobalViewIssuesViewSet.as_view( + WorkspaceViewIssuesViewSet.as_view( { "get": "list", } diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 0f2104187e..913385fa60 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -80,8 +80,8 @@ from .workspace.cycle import ( from .state.base import StateViewSet from .view.base import ( - GlobalViewViewSet, - GlobalViewIssuesViewSet, + WorkspaceViewViewSet, + WorkspaceViewIssuesViewSet, IssueViewViewSet, IssueViewFavoriteViewSet, ) diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index d4bf258a5c..8e9f5b284a 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -52,7 +52,7 @@ from plane.db.models import ( ) -class GlobalViewViewSet(BaseViewSet): +class WorkspaceViewViewSet(BaseViewSet): serializer_class = IssueViewSerializer model = IssueView permission_classes = [ @@ -61,7 +61,7 @@ class GlobalViewViewSet(BaseViewSet): def perform_create(self, serializer): workspace = Workspace.objects.get(slug=self.kwargs.get("slug")) - serializer.save(workspace_id=workspace.id) + serializer.save(workspace_id=workspace.id, owned_by=self.request.user) def get_queryset(self): return self.filter_queryset( @@ -75,7 +75,7 @@ class GlobalViewViewSet(BaseViewSet): ) -class GlobalViewIssuesViewSet(BaseViewSet): +class WorkspaceViewIssuesViewSet(BaseViewSet): permission_classes = [ WorkspaceEntityPermission, ] @@ -271,7 +271,10 @@ class IssueViewViewSet(BaseViewSet): ] def perform_create(self, serializer): - serializer.save(project_id=self.kwargs.get("project_id")) + serializer.save( + project_id=self.kwargs.get("project_id"), + owned_by=self.request.user, + ) def get_queryset(self): subquery = UserFavorite.objects.filter( diff --git a/apiserver/plane/db/migrations/0069_alter_account_provider_and_more.py b/apiserver/plane/db/migrations/0069_alter_account_provider_and_more.py index 309f73d04a..8c806ababc 100644 --- a/apiserver/plane/db/migrations/0069_alter_account_provider_and_more.py +++ b/apiserver/plane/db/migrations/0069_alter_account_provider_and_more.py @@ -1,6 +1,16 @@ # Generated by Django 4.2.11 on 2024-06-03 17:16 from django.db import migrations, models +from django.conf import settings +from django.db.models import F +import django.db.models.deletion + + +def populate_views_owned_by(apps, schema_editor): + IssueView = apps.get_model("db", "IssueView") + + # update all existing views to be owned by the user who created them + IssueView.objects.update(owned_by_id=F("created_by_id")) class Migration(migrations.Migration): @@ -11,13 +21,53 @@ class Migration(migrations.Migration): operations = [ migrations.AlterField( - model_name='account', - name='provider', - field=models.CharField(choices=[('google', 'Google'), ('github', 'Github'), ('gitlab', 'GitLab')]), + model_name="account", + name="provider", + field=models.CharField( + choices=[ + ("google", "Google"), + ("github", "Github"), + ("gitlab", "GitLab"), + ] + ), ), migrations.AlterField( - model_name='socialloginconnection', - name='medium', - field=models.CharField(choices=[('Google', 'google'), ('Github', 'github'), ('GitLab', 'gitlab'), ('Jira', 'jira')], default=None, max_length=20), + model_name="socialloginconnection", + name="medium", + field=models.CharField( + choices=[ + ("Google", "google"), + ("Github", "github"), + ("GitLab", "gitlab"), + ("Jira", "jira"), + ], + default=None, + max_length=20, + ), + ), + migrations.AddField( + model_name="issueview", + name="is_locked", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="issueview", + name="owned_by", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="views", + to=settings.AUTH_USER_MODEL, + null=True, + ), + ), + migrations.RunPython(populate_views_owned_by), + migrations.AlterField( + model_name="issueview", + name="owned_by", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="views", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index a1c2b5ecfe..38586791dc 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -65,7 +65,7 @@ from .session import Session from .social_connection import SocialLoginConnection from .state import State from .user import Account, Profile, User -from .view import GlobalView, IssueView, IssueViewFavorite +from .view import IssueView, IssueViewFavorite from .webhook import Webhook, WebhookLog from .workspace import ( Team, diff --git a/apiserver/plane/db/models/view.py b/apiserver/plane/db/models/view.py index 8916bd4063..3822b531ec 100644 --- a/apiserver/plane/db/models/view.py +++ b/apiserver/plane/db/models/view.py @@ -102,6 +102,13 @@ class IssueView(WorkspaceBaseModel): ) sort_order = models.FloatField(default=65535) logo_props = models.JSONField(default=dict) + owned_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="views", + ) + is_locked = models.BooleanField(default=False) + class Meta: verbose_name = "Issue View"