Add organization private pages
This commit is contained in:
parent
4c3441b197
commit
4502dbf6b9
14 changed files with 369 additions and 156 deletions
|
@ -49,6 +49,8 @@ class BlogPostForm(ModelForm):
|
|||
class Meta:
|
||||
widgets = {
|
||||
'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}),
|
||||
'organizations': AdminHeavySelect2MultipleWidget(data_view='organization_select2',
|
||||
attrs={'style': 'width: 100%'}),
|
||||
}
|
||||
|
||||
if HeavyPreviewAdminPageDownWidget is not None:
|
||||
|
@ -58,7 +60,8 @@ class BlogPostForm(ModelForm):
|
|||
|
||||
class BlogPostAdmin(VersionAdmin):
|
||||
fieldsets = (
|
||||
(None, {'fields': ('title', 'slug', 'authors', 'visible', 'sticky', 'publish_on')}),
|
||||
(None, {'fields': ('title', 'slug', 'authors', 'visible', 'sticky', 'publish_on',
|
||||
'is_organization_private', 'organizations')}),
|
||||
(_('Content'), {'fields': ('content', 'og_image')}),
|
||||
(_('Summary'), {'classes': ('collapse',), 'fields': ('summary',)}),
|
||||
)
|
||||
|
|
|
@ -15,7 +15,8 @@ class ProblemFeed(Feed):
|
|||
description = 'The latest problems added on the %s website' % settings.SITE_LONG_NAME
|
||||
|
||||
def items(self):
|
||||
return Problem.objects.filter(is_public=True, is_organization_private=False).order_by('-date', '-id')[:25]
|
||||
return BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now(), is_organization_private=False) \
|
||||
.order_by('-sticky', '-publish_on')
|
||||
|
||||
def item_title(self, problem):
|
||||
return problem.name
|
||||
|
|
23
judge/migrations/0114_auto_20201228_1041.py
Normal file
23
judge/migrations/0114_auto_20201228_1041.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.2.17 on 2020-12-28 03:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('judge', '0113_auto_20201228_0911'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='is_organization_private',
|
||||
field=models.BooleanField(default=False, verbose_name='private to organizations'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='blogpost',
|
||||
name='organizations',
|
||||
field=models.ManyToManyField(blank=True, help_text='If private, only these organizations may see the blog post.', to='judge.Organization', verbose_name='organizations'),
|
||||
),
|
||||
]
|
|
@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from mptt.fields import TreeForeignKey
|
||||
from mptt.models import MPTTModel
|
||||
|
||||
from judge.models.profile import Profile
|
||||
from judge.models.profile import Organization, Profile
|
||||
|
||||
__all__ = ['MiscConfig', 'validate_regex', 'NavigationBar', 'BlogPost']
|
||||
|
||||
|
@ -72,6 +72,9 @@ class BlogPost(models.Model):
|
|||
content = models.TextField(verbose_name=_('post content'))
|
||||
summary = models.TextField(verbose_name=_('post summary'), blank=True)
|
||||
og_image = models.CharField(verbose_name=_('openGraph image'), default='', max_length=150, blank=True)
|
||||
organizations = models.ManyToManyField(Organization, blank=True, verbose_name=_('organizations'),
|
||||
help_text=_('If private, only these organizations may see the blog post.'))
|
||||
is_organization_private = models.BooleanField(verbose_name=_('private to organizations'), default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
@ -81,11 +84,22 @@ class BlogPost(models.Model):
|
|||
|
||||
def can_see(self, user):
|
||||
if self.visible and self.publish_on <= timezone.now():
|
||||
return True
|
||||
if not self.is_organization_private:
|
||||
return True
|
||||
if user.is_authenticated and \
|
||||
self.organizations.filter(id__in=user.profile.organizations.all()).exists():
|
||||
return True
|
||||
if user.has_perm('judge.edit_all_post'):
|
||||
return True
|
||||
return user.is_authenticated and self.authors.filter(id=user.profile.id).exists()
|
||||
|
||||
def is_editable_by(self, user):
|
||||
if not user.is_authenticated:
|
||||
return False
|
||||
if user.has_perm('judge.edit_all_post'):
|
||||
return True
|
||||
return user.has_perm('judge.change_blogpost') and self.authors.filter(id=user.profile.id).exists()
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('edit_all_post', _('Edit all posts')),
|
||||
|
|
|
@ -56,8 +56,9 @@ class BlogPostSitemap(Sitemap):
|
|||
priority = 0.7
|
||||
|
||||
def items(self):
|
||||
return BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()).values_list('id', 'slug')
|
||||
|
||||
return (BlogPost.objects.filter(visible=True, is_organization_private=False, publish_on__lte=timezone.now())
|
||||
.values_list('id', 'slug'))
|
||||
|
||||
def location(self, obj):
|
||||
return reverse('blog_post', args=obj)
|
||||
|
||||
|
|
|
@ -30,8 +30,15 @@ class PostList(ListView):
|
|||
orphans=orphans, allow_empty_first_page=allow_empty_first_page, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return (BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()).order_by('-sticky', '-publish_on')
|
||||
.prefetch_related('authors__user'))
|
||||
queryset = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()) \
|
||||
.order_by('-sticky', '-publish_on') \
|
||||
.prefetch_related('authors__user', 'organizations')
|
||||
if not self.request.user.has_perm('judge.edit_all_post'):
|
||||
filter = Q(is_organization_private=False)
|
||||
if self.request.user.is_authenticated:
|
||||
filter |= Q(organizations__in=self.request.profile.organizations.all())
|
||||
queryset = queryset.filter(filter)
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(PostList, self).get_context_data(**kwargs)
|
||||
|
@ -107,7 +114,7 @@ class PostView(TitleMixin, CommentedDetailView):
|
|||
model = BlogPost
|
||||
pk_url_kwarg = 'id'
|
||||
context_object_name = 'post'
|
||||
template_name = 'blog/content.html'
|
||||
template_name = 'blog/blog.html'
|
||||
|
||||
def get_title(self):
|
||||
return self.object.title
|
||||
|
|
|
@ -6,17 +6,18 @@ from django.core.cache import cache
|
|||
from django.core.cache.utils import make_template_fragment_key
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import transaction
|
||||
from django.db.models import Count, Q
|
||||
from django.db.models import Count, Q, Value, BooleanField
|
||||
from django.forms import Form, modelformset_factory
|
||||
from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext as _, gettext_lazy, ungettext
|
||||
from django.views.generic import DetailView, FormView, ListView, UpdateView, View
|
||||
from django.views.generic.detail import SingleObjectMixin, SingleObjectTemplateResponseMixin
|
||||
from reversion import revisions
|
||||
|
||||
from judge.forms import EditOrganizationForm
|
||||
from judge.models import Organization, OrganizationRequest, Profile
|
||||
from judge.models import BlogPost, Comment, Organization, OrganizationRequest, Problem, Profile, Contest
|
||||
from judge.utils.ranker import ranker
|
||||
from judge.utils.views import TitleMixin, generic_message
|
||||
|
||||
|
@ -25,8 +26,21 @@ __all__ = ['OrganizationList', 'OrganizationHome', 'OrganizationUsers', 'Organiz
|
|||
'OrganizationRequestDetail', 'OrganizationRequestView', 'OrganizationRequestLog',
|
||||
'KickUserWidgetView']
|
||||
|
||||
class OrganizationBase(object):
|
||||
def can_edit_organization(self, org=None):
|
||||
if org is None:
|
||||
org = self.object
|
||||
if not self.request.user.is_authenticated:
|
||||
return False
|
||||
profile_id = self.request.profile.id
|
||||
return org.admins.filter(id=profile_id).exists() or org.registrant_id == profile_id
|
||||
|
||||
class OrganizationMixin(object):
|
||||
def is_member(self, org=None):
|
||||
if org is None:
|
||||
org = self.object
|
||||
return self.request.profile in org if self.request.user.is_authenticated else False
|
||||
|
||||
class OrganizationMixin(OrganizationBase):
|
||||
context_object_name = 'organization'
|
||||
model = Organization
|
||||
|
||||
|
@ -46,15 +60,7 @@ class OrganizationMixin(object):
|
|||
else:
|
||||
return generic_message(request, _('No such organization'),
|
||||
_('Could not find such organization.'))
|
||||
|
||||
def can_edit_organization(self, org=None):
|
||||
if org is None:
|
||||
org = self.object
|
||||
if not self.request.user.is_authenticated:
|
||||
return False
|
||||
profile_id = self.request.profile.id
|
||||
return org.admins.filter(id=profile_id).exists() or org.registrant_id == profile_id
|
||||
|
||||
|
||||
|
||||
class OrganizationDetailView(OrganizationMixin, DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
@ -65,7 +71,7 @@ class OrganizationDetailView(OrganizationMixin, DetailView):
|
|||
return self.render_to_response(context)
|
||||
|
||||
|
||||
class OrganizationList(TitleMixin, ListView):
|
||||
class OrganizationList(TitleMixin, ListView, OrganizationBase):
|
||||
model = Organization
|
||||
context_object_name = 'organizations'
|
||||
template_name = 'organization/list.html'
|
||||
|
@ -74,6 +80,15 @@ class OrganizationList(TitleMixin, ListView):
|
|||
def get_queryset(self):
|
||||
return super(OrganizationList, self).get_queryset().annotate(member_count=Count('member'))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(OrganizationList, self).get_context_data(**kwargs)
|
||||
context['my_organizations'] = set()
|
||||
|
||||
for organization in context['organizations']:
|
||||
if self.can_edit_organization(organization) or self.is_member(organization):
|
||||
context['my_organizations'].add(organization)
|
||||
|
||||
return context
|
||||
|
||||
class OrganizationHome(OrganizationDetailView):
|
||||
template_name = 'organization/home.html'
|
||||
|
@ -82,6 +97,23 @@ class OrganizationHome(OrganizationDetailView):
|
|||
context = super(OrganizationHome, self).get_context_data(**kwargs)
|
||||
context['title'] = self.object.name
|
||||
context['can_edit'] = self.can_edit_organization()
|
||||
context['is_member'] = self.is_member()
|
||||
context['new_problems'] = Problem.objects.filter(is_public=True, is_organization_private=True,
|
||||
organizations=self.object) \
|
||||
.order_by('-date', '-id')[:settings.DMOJ_BLOG_NEW_PROBLEM_COUNT]
|
||||
context['new_contests'] = Contest.objects.filter(is_visible=True, is_organization_private=True,
|
||||
organizations=self.object) \
|
||||
.order_by('-end_time', '-id')[:settings.DMOJ_BLOG_NEW_CONTEST_COUNT]
|
||||
|
||||
context['posts'] = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now(),
|
||||
is_organization_private=True, organizations=self.object) \
|
||||
.order_by('-sticky', '-publish_on') \
|
||||
.prefetch_related('authors__user', 'organizations')
|
||||
context['post_comment_counts'] = {
|
||||
int(page[2:]): count for page, count in
|
||||
Comment.objects.filter(page__in=['b:%d' % post.id for post in context['posts']], hidden=False)
|
||||
.values_list('page').annotate(count=Count('page')).order_by()
|
||||
}
|
||||
return context
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue