diff --git a/group/migrations/0002_auto_20150811_1456.py b/group/migrations/0002_auto_20150811_1456.py new file mode 100644 index 00000000..2b6a65e6 --- /dev/null +++ b/group/migrations/0002_auto_20150811_1456.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='group', + name='name', + field=models.CharField(unique=True, max_length=30), + ), + migrations.AlterField( + model_name='joingrouprequest', + name='group', + field=models.ForeignKey(to='group.Group'), + ), + migrations.AlterUniqueTogether( + name='usergrouprelation', + unique_together=set([('group', 'user')]), + ), + ] diff --git a/group/migrations/0003_auto_20150811_1906.py b/group/migrations/0003_auto_20150811_1906.py new file mode 100644 index 00000000..242a62e2 --- /dev/null +++ b/group/migrations/0003_auto_20150811_1906.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0002_auto_20150811_1456'), + ] + + operations = [ + migrations.AlterField( + model_name='group', + name='join_group_setting', + field=models.IntegerField(default=1), + ), + ] diff --git a/group/models.py b/group/models.py index 8b15644b..b7f49c18 100644 --- a/group/models.py +++ b/group/models.py @@ -10,7 +10,7 @@ class Group(models.Model): create_time = models.DateTimeField(auto_now_add=True) admin = models.ForeignKey(User, related_name="my_groups") # 0是公开 1是需要申请后加入 2是不允许任何人加入 - join_group_setting = models.IntegerField() + join_group_setting = models.IntegerField(default=1) members = models.ManyToManyField(User, through="UserGroupRelation") # 解散小组后,这一项改为False visible = models.BooleanField(default=True) diff --git a/group/serializers.py b/group/serializers.py index 6c40eaec..065baf98 100644 --- a/group/serializers.py +++ b/group/serializers.py @@ -1,8 +1,9 @@ # coding=utf-8 from rest_framework import serializers +from account.models import User from account.serializers import UserSerializer -from .models import Group, UserGroupRelation +from .models import Group, UserGroupRelation, JoinGroupRequest class CreateGroupSerializer(serializers.Serializer): @@ -18,25 +19,58 @@ class EditGroupSerializer(serializers.Serializer): join_group_setting = serializers.IntegerField() -class JoinGroupRequestSerializer(serializers.Serializer): +class CreateJoinGroupRequestSerializer(serializers.Serializer): group_id = serializers.IntegerField() message = serializers.CharField(max_length=30) +class JoinGroupRequestSerializer(serializers.ModelSerializer): + class GroupSerializer(serializers.ModelSerializer): + class Meta: + model = Group + fields = ["id", "name"] + + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ["username"] + + group = GroupSerializer() + user = UserSerializer() + + class Meta: + model = JoinGroupRequest + + class GroupSerializer(serializers.ModelSerializer): + members_number = serializers.SerializerMethodField("_get_group_members_number") + + def _get_group_members_number(self, group): + return group.members.all().count() + class Meta: model = Group exclude = ["members"] class GroupMemberSerializer(serializers.ModelSerializer): + class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ["id", "username", "real_name"] + user = UserSerializer() class Meta: model = UserGroupRelation - exclude = ["id", "group"] + exclude = ["id"] class EditGroupMemberSerializer(serializers.Serializer): group_id = serializers.IntegerField() - members = serializers.ListField(child=serializers.IntegerField()) \ No newline at end of file + members = serializers.ListField(child=serializers.IntegerField()) + + +class PutJoinGroupRequestSerializer(serializers.Serializer): + request_id = serializers.IntegerField() + status = serializers.BooleanField() \ No newline at end of file diff --git a/group/views.py b/group/views.py index 54c1ec23..9edb9c6d 100644 --- a/group/views.py +++ b/group/views.py @@ -10,8 +10,9 @@ from account.decorators import login_required from .models import Group, JoinGroupRequest, UserGroupRelation from .serializers import (CreateGroupSerializer, EditGroupSerializer, - JoinGroupRequestSerializer, GroupSerializer, - GroupMemberSerializer, EditGroupMemberSerializer) + CreateJoinGroupRequestSerializer, GroupSerializer, + GroupMemberSerializer, EditGroupMemberSerializer, + JoinGroupRequestSerializer, PutJoinGroupRequestSerializer) class GroupAPIViewBase(object): @@ -101,6 +102,8 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase): return error_response(u"小组不存在") else: groups = self.get_groups(request) + if request.GET.get("keyword", None): + groups = groups.filter(name__contains=request.GET["keyword"]) return paginate(request, groups, GroupSerializer) @@ -149,14 +152,14 @@ def join_group(user, group): class JoinGroupAPIView(APIView): - @login_required + # @login_required def post(self, request): """ 加入某个小组的api --- - request_serializer: JoinGroupRequestSerializer + request_serializer: CreateJoinGroupRequestSerializer """ - serializer = JoinGroupRequestSerializer(data=request.data) + serializer = CreateJoinGroupRequestSerializer(data=request.data) if serializer.is_valid(): data = serializer.data try: @@ -169,7 +172,11 @@ class JoinGroupAPIView(APIView): else: return error_response(u"你已经是小组成员了") elif group.join_group_setting == 1: - JoinGroupRequest.objects.create(user=request.user, group=group, message=data["message"]) + try: + JoinGroupRequest.objects.get(user=request.user, group=group, status=False) + return error_response(u"你已经提交过申请了,请等待审核") + except JoinGroupRequest.DoesNotExist: + JoinGroupRequest.objects.create(user=request.user, group=group, message=data["message"]) return success_response(u"申请提交成功,请等待审核") elif group.join_group_setting == 2: return error_response(u"该小组不允许任何人加入") @@ -188,3 +195,43 @@ class JoinGroupAPIView(APIView): # 搜索包含这个关键词的 没有解散的 而且允许加入的小组 groups = Group.objects.filter(name__contains=keyword, visible=True, join_group_setting__lte=2) return paginate(request, groups, GroupSerializer) + + +class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase): + def get(self, request): + """ + 返回管理的群的加群请求 + --- + response_serializer: JoinGroupRequestSerializer + """ + requests = JoinGroupRequest.objects.filter(group=Group.objects.filter(admin=request.user, visible=True), + status=False) + return paginate(request, requests, JoinGroupRequestSerializer) + + def put(self, request): + """ + 同意或者拒绝加入小组请求 + --- + request_serializer: PutJoinGroupRequestSerializer + """ + serializer = PutJoinGroupRequestSerializer(data=request.data) + if serializer.is_valid(): + data = serializer.data + try: + join_request = JoinGroupRequest.objects.get(id=data["request_id"], group__admin=request.user, status=False) + except JoinGroupRequest.DoesNotExist: + return error_response(u"小组不存在") + + join_request.status = True + join_request.save() + + if data["status"]: + if join_group(join_request.user, join_request.group): + return success_response(u"加入成功") + else: + return error_response(u"加入失败,已经在本小组内") + else: + return success_response(u"已拒绝") + + else: + return serializer_invalid_response(serializer) diff --git a/install/views.py b/install/views.py index 521d8a6c..75fd7bd5 100644 --- a/install/views.py +++ b/install/views.py @@ -3,10 +3,21 @@ from django.shortcuts import render from django.http import HttpResponse from account.models import User +from group.models import Group, UserGroupRelation, JoinGroupRequest def install(request): - user = User.objects.create(username="root", admin_type=2) - user.set_password("root") - user.save() - return HttpResponse("success") \ No newline at end of file + for i in range(10): + user = User.objects.create(username="root" + str(i), admin_type=2, real_name="real_name", email="11111@qq.com") + user.set_password("root") + user.save() + for i in range(10): + group = Group.objects.create(name="group" + str(i), + description="description", + admin=User.objects.get(username="root0")) + for i in range(7): + UserGroupRelation.objects.create(user=User.objects.get(username="root" + str(i)), group=group) + for i in range(7, 10): + JoinGroupRequest.objects.create(user=User.objects.get(username="root" + str(i)), + group=group, message=u"你好啊") + return HttpResponse("success") diff --git a/oj/urls.py b/oj/urls.py index 4bee4a1d..e3fbcdaf 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -7,7 +7,9 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA UserChangePasswordAPIView, EmailCheckAPIView, UserAPIView, UserAdminAPIView) from announcement.views import AnnouncementAPIView, AnnouncementAdminAPIView -from group.views import GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView + +from group.views import GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView + from admin.views import AdminTemplateView from problem.views import ProblemAdminAPIView @@ -46,4 +48,7 @@ urlpatterns = [ url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^problem/(?P\d+)/my_solutions/', "problem.views.problem_my_solutions_list_page", name="problem_my_solutions_page"), url(r'^my_solution/(?P\d+)/$', "problem.views.my_solution", name="my_solution_page"), + + url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), name="join_group_request_admin_api"), + ] diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index 9c17e410..4584d9a9 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -24,11 +24,17 @@ define("admin", ["jquery", "avalon"], function ($, avalon) { var vm = avalon.define({ $id: "admin", template_url: "template/" + hash + ".html", + group_id: -1, hide_loading: function () { $("#loading-gif").hide(); } }); + vm.$watch("showGroupDetailPage", function(group_id){ + vm.group_id = group_id; + vm.template_url = "template/group/group_detail.html"; + }); + avalon.scan(); li_active("#li-" + hash.replace("/", "-")); diff --git a/static/src/js/app/admin/group/group.js b/static/src/js/app/admin/group/group.js new file mode 100644 index 00000000..9ba1ed88 --- /dev/null +++ b/static/src/js/app/admin/group/group.js @@ -0,0 +1,73 @@ +require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, avalon, csrfHeader, bs_alert) { + avalon.vmodels.group = null; + + // avalon:定义模式 group_list + avalon.ready(function () { + var vm = avalon.define({ + $id: "group", + //通用变量 + group_list: [], // 用户列表数据项 + previous_page: 0, // 之前的页数 + next_page: 0, // 之后的页数 + page: 1, // 当前页数 + page_count: 1, // 总页数 + keyword: "", + + getNext: function () { + if (!vm.next_page) + return; + getPageData(vm.page + 1); + }, + getPrevious: function () { + if (!vm.previous_page) + return; + getPageData(vm.page - 1); + }, + getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 + if (btn) { + return vm.next_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + else { + return vm.previous_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + }, + getPage: function (page_index) { + getPageData(page_index); + }, + getGroupSettingString: function(setting){ + return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting] + }, + showGroupDetailPage: function(group_id){ + vm.$fire("up!showGroupDetailPage", group_id); + } + }); + + avalon.scan(); + getPageData(1); //用户列表初始化 + //Ajax get数据 + function getPageData(page) { + var url = "/api/admin/group/?paging=true&page=" + page + "&page_size=10"; + if (vm.keyword != "") + url += "&keyword=" + vm.keyword; + $.ajax({ + beforeSend: csrfHeader, + url: url, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.group_list = data.data.results; + vm.page_count = data.data.total_page; + vm.previous_page = data.data.previous_page; + vm.next_page = data.data.next_page; + vm.page = page; + } + else { + bs_alert(data.data); + } + } + }); + } + }); + +}); \ No newline at end of file diff --git a/static/src/js/app/admin/group/group_detail.js b/static/src/js/app/admin/group/group_detail.js new file mode 100644 index 00000000..93a5d431 --- /dev/null +++ b/static/src/js/app/admin/group/group_detail.js @@ -0,0 +1,119 @@ +require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, avalon, csrfHeader, bs_alert) { + avalon.vmodels.group_detail = null; + + // avalon:定义模式 group_list + avalon.ready(function () { + var vm = avalon.define({ + $id: "group_detail", + //通用变量 + member_list: [], + previous_page: 0, + next_page: 0, + page: 1, + page_count: 1, + name: "", + description: "", + join_group_setting: {0: false, 1: false, 2: false}, + checked_setting: 0, + + getSettingChecked: function (setting) { + return setting == vm.join_group_setting; + }, + + updateGroupInfo: function () { + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/group/", + method: "put", + data: {group_id: avalon.vmodels.admin.group_id, name: vm.name, + description: vm.description, join_group_setting: vm.join_group_setting}, + dataType: "json", + success: function (data) { + if (!data.code) { + bs_alert("修改成功"); + } + else { + bs_alert(data.data); + } + } + }) + }, + + getNext: function () { + if (!vm.next_page) + return; + getPageData(vm.page + 1); + }, + getPrevious: function () { + if (!vm.previous_page) + return; + getPageData(vm.page - 1); + }, + getBtnClass: function (btn) { + if (btn) { + return vm.next_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + else { + return vm.previous_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + }, + getPage: function (page_index) { + getPageData(page_index); + }, + removeMember: function (relation) { + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/group_member/", + method: "put", + data: JSON.stringify({group_id: relation.group, members: [relation.user.id]}), + contentType: "application/json", + success: function (data) { + vm.member_list.remove(relation); + bs_alert(data.data); + } + }) + } + }); + + avalon.scan(); + getPageData(1); + function getPageData(page) { + var url = "/api/admin/group_member/?paging=true&page=" + page + + "&page_size=10&group_id=" + avalon.vmodels.admin.group_id; + $.ajax({ + url: url, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.member_list = data.data.results; + vm.page_count = data.data.total_page; + vm.previous_page = data.data.previous_page; + vm.next_page = data.data.next_page; + vm.page = page; + } + else { + bs_alert(data.data); + } + } + }); + } + + $.ajax({ + url: "/api/admin/group/?group_id=" + avalon.vmodels.admin.group_id, + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + vm.name = data.data.name; + vm.description = data.data.description; + vm.join_group_setting[data.data.join_group_setting] = true; + } + else { + bs_alert(data.data); + } + } + }) + }); + +}); \ No newline at end of file diff --git a/static/src/js/app/admin/group/join_group_request_list.js b/static/src/js/app/admin/group/join_group_request_list.js new file mode 100644 index 00000000..8bf2caab --- /dev/null +++ b/static/src/js/app/admin/group/join_group_request_list.js @@ -0,0 +1,148 @@ +require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, avalon, csrfHeader, bs_alert) { + avalon.vmodels.request_list = null; + + // avalon:定义模式 group_list + avalon.ready(function () { + var vm = avalon.define({ + $id: "request_list", + //通用变量 + request_list: [], // 列表数据项 + previous_page: 0, // 之前的页数 + next_page: 0, // 之后的页数 + page: 1, // 当前页数 + page_count: 1, // 总页数 + + getNext: function () { + if (!vm.next_page) + return; + getPageData(vm.page + 1); + }, + getPrevious: function () { + if (!vm.previous_page) + return; + getPageData(vm.page - 1); + }, + getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 + if (btn) { + return vm.next_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + else { + return vm.previous_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + }, + getPage: function (page_index) { + getPageData(page_index); + }, + processRequest: function(request_id, status){ + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/join_group_request/", + method: "put", + data: {request_id: request_id, status: status}, + success: function(data){ + bs_alert(data.data); + } + }) + } + }); + + avalon.scan(); + getPageData(1); + //Ajax get数据 + function getPageData(page) { + var url = "/api/admin/join_group_request/?paging=true&page=" + page + "&page_size=10"; + $.ajax({ + beforeSend: csrfHeader, + url: url, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.request_list = data.data.results; + vm.page_count = data.data.total_page; + vm.previous_page = data.data.previous_page; + vm.next_page = data.data.next_page; + vm.page = page; + } + else { + bs_alert(data.data); + } + } + }); + } + + /*$("#edit_user-form") + .formValidation({ + framework: "bootstrap", + fields: { + username: { + validators: { + notEmpty: { + message: "请填写用户名" + }, + stringLength: { + min: 3, + max: 30, + message: '用户名长度必须在3到30位之间' + } + } + }, + real_name: { + validators: { + notEmpty: { + message: "请填写真实姓名" + } + } + }, + email: { + validators: { + notEmpty: { + message: "请填写电子邮箱邮箱地址" + }, + emailAddress: { + message: "请填写有效的邮箱地址" + } + } + }, + password: { + validators: { + stringLength: { + min: 6, + max: 30, + message: '密码长度必须在6到30位之间' + } + } + } + } + } + ).on('success.form.fv', function (e) { + e.preventDefault(); + var data = { + username: vm.username, + real_name: vm.real_name, + email: vm.email, + id: vm.id, + admin_type: vm.admin_type + }; + if ($("#password").val() !== "") + data.password = $("#password").val(); + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/user/", + data: data, + dataType: "json", + method: "put", + success: function (data) { + if (!data.code) { + bs_alert("提交成功!"); + getPageData(1); + $("#password").val(""); + } else { + bs_alert(data.data); + } + } + }) + });*/ + }); + +}); \ No newline at end of file diff --git a/template/admin/admin.html b/template/admin/admin.html index ce1094e9..69aba419 100644 --- a/template/admin/admin.html +++ b/template/admin/admin.html @@ -104,6 +104,13 @@
  • 用户分组
  • +
  • 小组管理
  • +
  • + 小组列表 +
  • +
  • + 加入小组请求 +
  • diff --git a/template/admin/group/edit_group.html b/template/admin/group/edit_group.html new file mode 100644 index 00000000..e69de29b diff --git a/template/admin/group/group.html b/template/admin/group/group.html new file mode 100644 index 00000000..81a8e084 --- /dev/null +++ b/template/admin/group/group.html @@ -0,0 +1,43 @@ +
    +

    小组管理

    +
    +
    +
    + + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + +
    ID名称创建时间人数设置
    {{el.id}}{{el.name}}{{el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.members_number }}{{ getGroupSettingString(el.join_group_setting) }} + +
    + +
    + 页数:{{page}}/{{page_count}}   + 上一页 + 下一页 +
    + +
    + \ No newline at end of file diff --git a/template/admin/group/group_detail.html b/template/admin/group/group_detail.html new file mode 100644 index 00000000..d38b4f6b --- /dev/null +++ b/template/admin/group/group_detail.html @@ -0,0 +1,58 @@ +
    +

    小组成员管理

    + + + + + + + + + + + + + + + + +
    ID用户名真实姓名加入时间
    {{ el.user.id }}{{ el.user.username }}{{ el.user.real_name }}{{ el.join_time|date("yyyy-MM-dd HH:mm:ss")}} + +
    + +
    + 页数:{{ page }}/{{ page_count }}   + 上一页 + 下一页 +
    + +

    修改小组信息

    + +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + + 允许任何人加入 + 提交请求后管理员审核 + 不允许任何人加入 +
    +
    +
    + +
    + +
    + +
    + \ No newline at end of file diff --git a/template/admin/group/join_group_request_list.html b/template/admin/group/join_group_request_list.html new file mode 100644 index 00000000..5c74e639 --- /dev/null +++ b/template/admin/group/join_group_request_list.html @@ -0,0 +1,31 @@ +
    +

    加入小组请求管理

    + + + + + + + + + + + + + + + + +
    创建时间用户小组附加消息
    {{el.create_time|date("yyyy-MM-dd HH:mm:ss")}}{{ el.group.name }}{{ el.user.username }}{{ el.message }} + + +
    + +
    + 页数:{{page}}/{{page_count}}   + 上一页 + 下一页 +
    + +
    + \ No newline at end of file