Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d79b0cc
WIP
sangeethailango Jan 2, 2025
4edfa55
WIP
sangeethailango Jan 3, 2025
abb68ff
WIP
sangeethailango Jan 3, 2025
f4a1b0f
WIP
sangeethailango Jan 3, 2025
1d147b0
Create home preference if not exist
sangeethailango Jan 3, 2025
de5643c
chore: handled the unique state name validation (#6299)
NarayanBavisetti Jan 2, 2025
2eb0aae
fix: changed the response structure (#6301)
NarayanBavisetti Jan 2, 2025
97b569f
[WEB-1964]chore: cycles actions restructuring (#6298)
vamsikrishnamathala Jan 2, 2025
e22f5ce
fix: active cycle graph tooltip and endpoint validation (#6306)
anmolsinghbhatia Jan 3, 2025
269ac69
[WEB-2870]feat: language support (#6215)
vamsikrishnamathala Jan 3, 2025
59700c5
feat: introduced stacked bar chart and tree map chart. (#6305)
prateekshourya29 Jan 3, 2025
225d131
feat: add issue attachment external endpoint (#6307)
pablohashescobar Jan 3, 2025
73f3151
[PE-97] chore: re-order pages options (#6303)
aaryan610 Jan 3, 2025
8d27e54
fix: remove localdb tracing
sriramveeraghanta Jan 3, 2025
1a3479c
[WEB-2937] feat: home recent activies list endpoint (#6295)
sangeethailango Jan 3, 2025
69daabb
fix: remove emoji edit for uneditable pages (#6304)
aaryan610 Jan 3, 2025
1b4575e
Merge branch 'preview' of github.com:makeplane/plane into feat-home-w…
sangeethailango Jan 3, 2025
77a2206
Removed duplicate imports
sangeethailango Jan 3, 2025
30434b4
feat: patch api
sangeethailango Jan 3, 2025
3d66618
Merge branch 'preview' into feat-home-widgets
sangeethailango Jan 3, 2025
1d60d14
Enable sort order to be updatable
sangeethailango Jan 6, 2025
739e07a
Merge branch 'preview' into feat-home-widgets
sangeethailango Jan 6, 2025
901164b
Merge branch 'preview' into feat-home-widgets
sangeethailango Jan 6, 2025
6997528
Return key name
sangeethailango Jan 6, 2025
ac7c604
Merge branch 'feat-home-widgets' of github.com:makeplane/plane into f…
sriramveeraghanta Jan 6, 2025
b9b33ed
Merge branch 'preview' of github.com:makeplane/plane into feat-home-w…
sriramveeraghanta Jan 6, 2025
d9c7249
Remove random generation of sort_order
sangeethailango Jan 6, 2025
e766e3d
Merge branch 'feat-home-widgets' of github.com:makeplane/plane into f…
sangeethailango Jan 6, 2025
607f2df
Remove name field
sangeethailango Jan 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apiserver/plane/app/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
WorkspaceMemberMeSerializer,
WorkspaceUserPropertiesSerializer,
WorkspaceUserLinkSerializer,
WorkspaceRecentVisitSerializer
WorkspaceRecentVisitSerializer,
WorkspaceHomePreferenceSerializer,
)
from .project import (
ProjectSerializer,
Expand Down
72 changes: 52 additions & 20 deletions apiserver/plane/app/serializers/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@
WorkspaceUserProperties,
WorkspaceUserLink,
UserRecentVisit,
Issue,
Page,
Issue,
Page,
Project,
ProjectMember
ProjectMember,
WorkspaceHomePreference,
)
from plane.utils.constants import RESTRICTED_WORKSPACE_SLUGS

# Django imports
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError


class WorkSpaceSerializer(DynamicBaseSerializer):
owner = UserLiteSerializer(read_only=True)
total_members = serializers.IntegerField(read_only=True)
Expand Down Expand Up @@ -119,6 +121,7 @@ class Meta:
fields = "__all__"
read_only_fields = ["workspace", "user"]


class WorkspaceUserLinkSerializer(BaseSerializer):
class Meta:
model = WorkspaceUserLink
Expand All @@ -129,7 +132,7 @@ def to_internal_value(self, data):
url = data.get("url", "")
if url and not url.startswith(("http://", "https://")):
data["url"] = "http://" + url

return super().to_internal_value(data)

def validate_url(self, value):
Expand All @@ -141,74 +144,96 @@ def validate_url(self, value):

return value


class IssueRecentVisitSerializer(serializers.ModelSerializer):
project_identifier = serializers.SerializerMethodField()

class Meta:
model = Issue
fields = ["name", "state", "priority", "assignees", "type", "sequence_id", "project_id", "project_identifier"]
fields = [
"name",
"state",
"priority",
"assignees",
"type",
"sequence_id",
"project_id",
"project_identifier",
]

def get_project_identifier(self, obj):
project = obj.project

return project.identifier if project else None


class ProjectMemberSerializer(BaseSerializer):
member = UserLiteSerializer(read_only=True)

class Meta:
model = ProjectMember
fields = ["member"]


class ProjectRecentVisitSerializer(serializers.ModelSerializer):
project_members = serializers.SerializerMethodField()
project_members = serializers.SerializerMethodField()

class Meta:
model = Project
fields = ["id", "name", "logo_props", "project_members", "identifier"]

def get_project_members(self, obj):
members = ProjectMember.objects.filter(project_id=obj.id).select_related('member')
members = ProjectMember.objects.filter(project_id=obj.id).select_related(
"member"
)

serializer = ProjectMemberSerializer(members, many=True)
return serializer.data



class PageRecentVisitSerializer(serializers.ModelSerializer):
project_id = serializers.SerializerMethodField()
project_identifier = serializers.SerializerMethodField()

class Meta:
model = Page
fields = ["id", "name", "logo_props", "project_id", "owned_by", "project_identifier"]
fields = [
"id",
"name",
"logo_props",
"project_id",
"owned_by",
"project_identifier",
]

def get_project_id(self, obj):
return obj.project_id if hasattr(obj, 'project_id') else obj.projects.values_list('id', flat=True).first()

return (
obj.project_id
if hasattr(obj, "project_id")
else obj.projects.values_list("id", flat=True).first()
)

def get_project_identifier(self, obj):
project = obj.projects.first()

return project.identifier if project else None


def get_entity_model_and_serializer(entity_type):
entity_map = {
"issue": (Issue, IssueRecentVisitSerializer),
"page": (Page, PageRecentVisitSerializer),
"project": (Project, ProjectRecentVisitSerializer)
"project": (Project, ProjectRecentVisitSerializer),
}
return entity_map.get(entity_type, (None, None))


class WorkspaceRecentVisitSerializer(BaseSerializer):
entity_data = serializers.SerializerMethodField()

class Meta:
model = UserRecentVisit
fields = [
"id",
"entity_name",
"entity_identifier",
"entity_data",
"visited_at"
]
fields = ["id", "entity_name", "entity_identifier", "entity_data", "visited_at"]
read_only_fields = ["workspace", "owner", "created_by", "updated_by"]

def get_entity_data(self, obj):
Expand All @@ -225,3 +250,10 @@ def get_entity_data(self, obj):
except entity_model.DoesNotExist:
return None
return None


class WorkspaceHomePreferenceSerializer(BaseSerializer):
class Meta:
model = WorkspaceHomePreference
fields = ["key", "is_enabled", "sort_order"]
read_only_fields = ["worspace", "created_by", "update_by"]
33 changes: 21 additions & 12 deletions apiserver/plane/app/urls/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
WorkspaceFavoriteGroupEndpoint,
WorkspaceDraftIssueViewSet,
QuickLinkViewSet,
UserRecentVisitViewSet
UserRecentVisitViewSet,
WorkspacePreferenceViewSet,
)


Expand Down Expand Up @@ -215,25 +216,33 @@
WorkspaceDraftIssueViewSet.as_view({"post": "create_draft_to_issue"}),
name="workspace-drafts-issues",
),

# quick link
path(
"workspaces/<str:slug>/quick-links/",
QuickLinkViewSet.as_view({"get": "list", "post": "create"}),
name="workspace-quick-links"
name="workspace-quick-links",
),
path(
"workspaces/<str:slug>/quick-links/<uuid:pk>/",
QuickLinkViewSet.as_view(
{"get": "retrieve", "patch": "partial_update", "delete": "destroy"}
),
name="workspace-quick-links",
),
# Widgets
path(
"workspaces/<str:slug>/home-preferences/",
WorkspacePreferenceViewSet.as_view(),
name="workspace-home-preference",
),
path(
"workspaces/<str:slug>/quick-links/<uuid:pk>/",
QuickLinkViewSet.as_view({
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy"
}),
name="workspace-quick-links"
"workspaces/<str:slug>/home-preferences/<str:key>/",
WorkspacePreferenceViewSet.as_view(),
name="workspace-home-preference",
),
path(
"workspaces/<str:slug>/recent-visits/",
UserRecentVisitViewSet.as_view({"get": "list"}),
name="workspace-recent-visits"
)
name="workspace-recent-visits",
),
]
1 change: 1 addition & 0 deletions apiserver/plane/app/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

from .workspace.draft import WorkspaceDraftIssueViewSet

from .workspace.preference import WorkspacePreferenceViewSet
from .workspace.favorite import (
WorkspaceFavoriteEndpoint,
WorkspaceFavoriteGroupEndpoint,
Expand Down
72 changes: 72 additions & 0 deletions apiserver/plane/app/views/workspace/preference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Module imports
from ..base import BaseAPIView
from plane.db.models.workspace import WorkspaceHomePreference
from plane.app.permissions import allow_permission, ROLE
from plane.db.models import Workspace
from plane.app.serializers.workspace import WorkspaceHomePreferenceSerializer

# Third party imports
from rest_framework.response import Response
from rest_framework import status


class WorkspacePreferenceViewSet(BaseAPIView):
model = WorkspaceHomePreference

def get_serializer_class(self):
return WorkspaceHomePreferenceSerializer

@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
def get(self, request, slug):
workspace = Workspace.objects.get(slug=slug)

get_preference = WorkspaceHomePreference.objects.filter(
user=request.user, workspace_id=workspace.id
)

create_preference_keys = []

keys = [key for key, _ in WorkspaceHomePreference.HomeWidgetKeys.choices]

for preference in keys:
if preference not in get_preference.values_list("key", flat=True):
create_preference_keys.append(preference)

preference = WorkspaceHomePreference.objects.bulk_create(
[
WorkspaceHomePreference(
key=key, user=request.user, workspace=workspace
)
for key in create_preference_keys
],
batch_size=10,
ignore_conflicts=True,
)
preference = WorkspaceHomePreference.objects.filter(
user=request.user, workspace_id=workspace.id
)

return Response(
preference.values("key", "is_enabled", "config", "sort_order"),
status=status.HTTP_200_OK,
)

@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
def patch(self, request, slug, key):
preference = WorkspaceHomePreference.objects.filter(
key=key, workspace__slug=slug
).first()

if preference:
serializer = WorkspaceHomePreferenceSerializer(
preference, data=request.data, partial=True
)

if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

return Response(
{"detail": "Preference not found"}, status=status.HTTP_400_BAD_REQUEST
)
1 change: 1 addition & 0 deletions apiserver/plane/db/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
WorkspaceTheme,
WorkspaceUserProperties,
WorkspaceUserLink,
WorkspaceHomePreference
)

from .favorite import UserFavorite
Expand Down
Loading