from django.shortcuts import render, redirect, get_object_or_404 from django.views import View from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse_lazy from django.db.models import Q from django.http import JsonResponse from .models import Bookmark, Team, Extraction, BookmarkShare from .tasks import process_bookmark class BookmarkListView(LoginRequiredMixin, ListView): model = Bookmark template_name = 'core/index.html' context_object_name = 'bookmarks' paginate_by = 20 def get_queryset(self): queryset = Bookmark.objects.filter(user=self.request.user).order_by('-created_at') # Search filter query = self.request.GET.get('q') if query: queryset = queryset.filter( Q(title__icontains=query) | Q(url__icontains=query) | Q(notes__icontains=query) | Q(extraction__content_text__icontains=query) ).distinct() # Tag filter tag = self.request.GET.get('tag') if tag: queryset = queryset.filter(tags__name__in=[tag]) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Add all tags used by the user for a sidebar or filter list from taggit.models import Tag context['all_tags'] = Tag.objects.filter(bookmark__user=self.request.user).distinct() context['teams'] = self.request.user.teams.all() return context class BookmarkCreateView(LoginRequiredMixin, CreateView): model = Bookmark fields = ['url', 'title', 'notes', 'is_favorite'] template_name = 'core/bookmark_form.html' success_url = reverse_lazy('home') def form_valid(self, form): form.instance.user = self.request.user response = super().form_valid(form) # Handle tags if provided in a separate field or as a comma-separated string # For simplicity, we'll assume the model's TaggableManager handles it if added to fields, # but here we might need to handle it manually if we use a custom field. # Let's add 'tags' to fields in the actual form. tags = self.request.POST.get('tags_input') if tags: form.instance.tags.add(*[t.strip() for t in tags.split(',')]) process_bookmark.delay(self.object.id) return response class BookmarkUpdateView(LoginRequiredMixin, UpdateView): model = Bookmark fields = ['url', 'title', 'notes', 'is_favorite'] template_name = 'core/bookmark_form.html' success_url = reverse_lazy('home') def get_queryset(self): return Bookmark.objects.filter(user=self.request.user) class BookmarkDeleteView(LoginRequiredMixin, DeleteView): model = Bookmark success_url = reverse_lazy('home') def get_queryset(self): return Bookmark.objects.filter(user=self.request.user) class BookmarkDetailView(LoginRequiredMixin, DetailView): model = Bookmark template_name = 'core/bookmark_detail.html' context_object_name = 'bookmark' def get_queryset(self): # Allow viewing if it's the user's bookmark OR shared with one of their teams user_teams = self.request.user.teams.all() return Bookmark.objects.filter( Q(user=self.request.user) | Q(shares__team__in=user_teams) ).distinct() class TeamListView(LoginRequiredMixin, ListView): model = Team template_name = 'core/team_list.html' context_object_name = 'teams' def get_queryset(self): return self.request.user.teams.all() class TeamDetailView(LoginRequiredMixin, DetailView): model = Team template_name = 'core/team_detail.html' context_object_name = 'team' def get_queryset(self): return self.request.user.teams.all() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Get bookmarks shared with this team context['shared_bookmarks'] = Bookmark.objects.filter(shares__team=self.object).order_by('-shares__shared_at') return context class BookmarkShareToggleView(LoginRequiredMixin, View): def post(self, request, pk, team_id): bookmark = get_object_or_404(Bookmark, pk=pk, user=request.user) team = get_object_or_404(Team, pk=team_id, members=request.user) share, created = BookmarkShare.objects.get_or_create( bookmark=bookmark, team=team, defaults={'shared_by': request.user} ) if not created: share.delete() shared = False else: shared = True return JsonResponse({'shared': shared})