mirror of
https://github.com/haiwen/seahub.git
synced 2025-04-27 11:01:14 +00:00
Send notification to cloud user (#6415)
* send subscription expire notification * optimize code --------- Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
This commit is contained in:
parent
0b8aa00f4d
commit
8cc5815107
@ -8,6 +8,7 @@ import { Utils, isMobile } from './utils/utils';
|
||||
import SystemNotification from './components/system-notification';
|
||||
import EventBus from './components/common/event-bus';
|
||||
import Header from './components/header';
|
||||
import SystemUserNotification from './components/system-user-notification';
|
||||
import SidePanel from './components/side-panel';
|
||||
import ResizeBar from './components/resize-bar';
|
||||
import {
|
||||
@ -278,6 +279,7 @@ class App extends Component {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SystemNotification />
|
||||
<SystemUserNotification />
|
||||
<Header
|
||||
isSidePanelClosed={isSidePanelClosed}
|
||||
onCloseSidePanel={this.onCloseSidePanel}
|
||||
|
38
frontend/src/components/system-user-notification-item.js
Normal file
38
frontend/src/components/system-user-notification-item.js
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { gettext } from '../utils/constants';
|
||||
import { notificationAPI } from '../utils/notification-api';
|
||||
import '../css/system-notification.css';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class SystemUserNotificationItem extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isClosed: false
|
||||
};
|
||||
}
|
||||
|
||||
close = () => {
|
||||
this.setState({ isClosed: true });
|
||||
notificationAPI.setSysUserNotificationToSeen(this.props.notificationID);
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.isClosed) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div id="info-bar" className="d-flex justify-content-between">
|
||||
<span className="mr-3" aria-hidden="true"></span>
|
||||
<p id="info-bar-info" className="m-0" dangerouslySetInnerHTML={{ __html: this.props.msg }}></p>
|
||||
<button className="close sf2-icon-x1" title={gettext('Close')} aria-label={gettext('Close')} onClick={this.close}></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SystemUserNotificationItem.propTypes = {
|
||||
msg: PropTypes.string.isRequired,
|
||||
notificationID: PropTypes.number.isRequired,
|
||||
};
|
||||
export default SystemUserNotificationItem;
|
43
frontend/src/components/system-user-notification.js
Normal file
43
frontend/src/components/system-user-notification.js
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import '../css/system-notification.css';
|
||||
import SystemUserNotificationItem from './system-user-notification-item';
|
||||
import { notificationAPI } from '../utils/notification-api';
|
||||
|
||||
|
||||
class SystemUserNotification extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
userNoteMsgs: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
notificationAPI.listSysUserUnseenNotifications().then((res) => {
|
||||
this.setState({
|
||||
userNoteMsgs: res.data.notifications
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let { userNoteMsgs } = this.state;
|
||||
if (!userNoteMsgs) {
|
||||
return null;
|
||||
}
|
||||
const userNoteMsgItem = userNoteMsgs.map((item, index) => {
|
||||
return (
|
||||
<SystemUserNotificationItem
|
||||
key={index}
|
||||
notificationItem={item}
|
||||
msg={item.msg_format}
|
||||
notificationID={item.id}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return userNoteMsgItem;
|
||||
}
|
||||
}
|
||||
|
||||
export default SystemUserNotification;
|
65
frontend/src/utils/notification-api.js
Normal file
65
frontend/src/utils/notification-api.js
Normal file
@ -0,0 +1,65 @@
|
||||
import axios from 'axios';
|
||||
import cookie from 'react-cookies';
|
||||
import { siteRoot } from './constants';
|
||||
|
||||
class NotificationAPI {
|
||||
|
||||
init({ server, username, password, token }) {
|
||||
this.server = server;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.token = token;
|
||||
if (this.token && this.server) {
|
||||
this.req = axios.create({
|
||||
baseURL: this.server,
|
||||
headers: { 'Authorization': 'Token ' + this.token },
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
initForSeahubUsage({ siteRoot, xcsrfHeaders }) {
|
||||
if (siteRoot && siteRoot.charAt(siteRoot.length - 1) === '/') {
|
||||
var server = siteRoot.substring(0, siteRoot.length - 1);
|
||||
this.server = server;
|
||||
} else {
|
||||
this.server = siteRoot;
|
||||
}
|
||||
|
||||
this.req = axios.create({
|
||||
headers: {
|
||||
'X-CSRFToken': xcsrfHeaders,
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
_sendPostRequest(url, form) {
|
||||
if (form.getHeaders) {
|
||||
return this.req.post(url, form, {
|
||||
headers: form.getHeaders()
|
||||
});
|
||||
} else {
|
||||
return this.req.post(url, form);
|
||||
}
|
||||
}
|
||||
|
||||
listSysUserUnseenNotifications() {
|
||||
const url = this.server + '/api/v2.1/sys-user-notifications/unseen/';
|
||||
return this.req.get(url);
|
||||
|
||||
}
|
||||
|
||||
setSysUserNotificationToSeen(notificationID) {
|
||||
const url = this.server + 'api/v2.1/sys-user-notifications/' + notificationID + '/seen/';
|
||||
return this.req.put(url);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
let notificationAPI = new NotificationAPI();
|
||||
let xcsrfHeaders = cookie.load('sfcsrftoken');
|
||||
notificationAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });
|
||||
|
||||
export { notificationAPI };
|
@ -22,10 +22,12 @@ from seahub.base.accounts import User
|
||||
from seahub.base.models import OrgLastActivityTime
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.utils import api_error
|
||||
from seahub.api2.utils import api_error, to_python_boolean
|
||||
from seahub.api2.permissions import IsProVersion
|
||||
from seahub.role_permissions.utils import get_available_roles
|
||||
from seahub.organizations.models import OrgSAMLConfig
|
||||
from seahub.utils.ccnet_db import CcnetDB
|
||||
|
||||
|
||||
try:
|
||||
from seahub.settings import ORG_MEMBER_QUOTA_ENABLED
|
||||
@ -516,6 +518,7 @@ class AdminOrganizationsBaseInfo(APIView):
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
org_ids = request.GET.getlist('org_ids',[])
|
||||
include_org_staffs = to_python_boolean(request.GET.get('include_org_staffs', 'false'))
|
||||
orgs = []
|
||||
for org_id in org_ids:
|
||||
try:
|
||||
@ -525,5 +528,13 @@ class AdminOrganizationsBaseInfo(APIView):
|
||||
except Exception:
|
||||
continue
|
||||
base_info = {'org_id': org.org_id, 'org_name': org.org_name}
|
||||
staffs = []
|
||||
if include_org_staffs:
|
||||
try:
|
||||
ccnet_db = CcnetDB()
|
||||
staffs = ccnet_db.get_org_staffs(int(org_id))
|
||||
except Exception:
|
||||
pass
|
||||
base_info['org_staffs'] = staffs
|
||||
orgs.append(base_info)
|
||||
return Response({'organization_list': orgs})
|
||||
|
@ -12,8 +12,9 @@ from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.utils import api_error
|
||||
|
||||
from seahub.notifications.models import Notification
|
||||
from seahub.notifications.models import Notification, SysUserNotification
|
||||
from seahub.notifications.settings import NOTIFICATION_CACHE_TIMEOUT
|
||||
from seahub.base.accounts import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -168,3 +169,98 @@ class AdminSysNotificationView(APIView):
|
||||
|
||||
return Response({'success': True})
|
||||
|
||||
|
||||
class AdminSysUserNotificationsView(APIView):
|
||||
"""
|
||||
admin notifications of designated user
|
||||
"""
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
permission_classes = (IsAdminUser,)
|
||||
|
||||
def get(self, request):
|
||||
try:
|
||||
page = int(request.GET.get('page', 1))
|
||||
per_page = int(request.GET.get('per_page', 25))
|
||||
except Exception as e:
|
||||
error_msg = 'per_page or page invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
start, end = (page - 1) * per_page, page * per_page
|
||||
try:
|
||||
notifications = SysUserNotification.objects.all().order_by('-id')
|
||||
notifications_count = notifications.count()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({
|
||||
'notifications': [n.to_dict() for n in notifications[start: end]],
|
||||
"total_count": notifications_count
|
||||
})
|
||||
|
||||
def post(self,request):
|
||||
msg = request.data.get('msg', '')
|
||||
username = request.data.get('username', '')
|
||||
|
||||
# arguments check
|
||||
if not msg:
|
||||
error_msg = 'msg invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not username:
|
||||
error_msg = 'user invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
# resource check
|
||||
try:
|
||||
User.objects.get(email=username)
|
||||
except User.DoesNotExist:
|
||||
error_msg = 'User %s not found.' % username
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
try:
|
||||
notification = SysUserNotification.objects.create_sys_user_notificatioin(msg, username)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({'notification': notification.to_dict()})
|
||||
|
||||
class AdminSysUserNotificationView(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
permission_classes = (IsAdminUser,)
|
||||
|
||||
def delete(self, request, nid):
|
||||
"""
|
||||
delete a system-to-user notification
|
||||
Permission checking:
|
||||
1.login and is admin user.
|
||||
"""
|
||||
|
||||
try:
|
||||
nid = int(nid)
|
||||
except ValueError:
|
||||
error_msg = 'nid invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if nid <= 0:
|
||||
error_msg = 'nid invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
notification = SysUserNotification.objects.filter(id=nid).first()
|
||||
if not notification:
|
||||
error_msg = 'notification %s not found.' % nid
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
try:
|
||||
notification.delete()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({'success': True})
|
||||
|
@ -10,7 +10,7 @@ from rest_framework import status
|
||||
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.notifications.models import UserNotification
|
||||
from seahub.notifications.models import UserNotification, SysUserNotification
|
||||
|
||||
from seahub.notifications.utils import update_notice_detail, update_sdoc_notice_detail
|
||||
from seahub.api2.utils import api_error
|
||||
@ -370,3 +370,61 @@ class AllNotificationsView(APIView):
|
||||
|
||||
|
||||
return Response({'success': True})
|
||||
|
||||
class SysUserNotificationUnseenView(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
get the unseen sys-user-notifications by login user
|
||||
"""
|
||||
username = request.user.username
|
||||
notifications = SysUserNotification.objects.unseen_notes(username)
|
||||
return Response({
|
||||
'notifications': [n.to_dict() for n in notifications],
|
||||
})
|
||||
|
||||
class SysUserNotificationSeenView(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def put(self, request, nid):
|
||||
"""
|
||||
mark a sys-user-notification seen by login user
|
||||
Permission checking:
|
||||
1. login user.
|
||||
"""
|
||||
# arguments check
|
||||
username = request.user.username
|
||||
try:
|
||||
nid = int(nid)
|
||||
except ValueError:
|
||||
error_msg = 'nid invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if nid <= 0:
|
||||
error_msg = 'nid invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
# resouce check
|
||||
notification = SysUserNotification.objects.filter(id=nid).first()
|
||||
if not notification:
|
||||
error_msg = 'notification %s not found.' % nid
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
if notification.to_user != username:
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
# set notification to seen
|
||||
try:
|
||||
notification.update_notification_to_seen()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({'notification': notification.to_dict()})
|
||||
|
@ -9,7 +9,7 @@ from django.urls import reverse
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.forms import ModelForm, Textarea
|
||||
from django.utils.html import escape
|
||||
from django.utils.html import escape, urlize
|
||||
from django.utils.translation import gettext as _
|
||||
from django.core.cache import cache
|
||||
from django.template.loader import render_to_string
|
||||
@ -17,8 +17,9 @@ from django.template.loader import render_to_string
|
||||
from seaserv import seafile_api, ccnet_api
|
||||
|
||||
from seahub.base.fields import LowerCaseCharField
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
|
||||
from seahub.invitations.models import Invitation
|
||||
from seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||
from seahub.utils import normalize_cache_key, get_site_scheme_and_netloc
|
||||
from seahub.constants import HASH_URLS
|
||||
from seahub.file_participants.utils import list_file_participants
|
||||
@ -42,6 +43,9 @@ class NotificationManager(models.Manager):
|
||||
|
||||
########## system notification
|
||||
class Notification(models.Model):
|
||||
"""
|
||||
global system notification
|
||||
"""
|
||||
message = models.CharField(max_length=512)
|
||||
primary = models.BooleanField(default=False, db_index=True)
|
||||
objects = NotificationManager()
|
||||
@ -61,6 +65,62 @@ class NotificationForm(ModelForm):
|
||||
'message': Textarea(),
|
||||
}
|
||||
|
||||
|
||||
class SysUserNotificationManager(models.Manager):
|
||||
def create_sys_user_notificatioin(self, msg, user):
|
||||
notification = self.create(
|
||||
message = msg,
|
||||
to_user = user,
|
||||
)
|
||||
return notification
|
||||
|
||||
def unseen_notes(self, user):
|
||||
notes = self.filter(to_user=user, seen=0)
|
||||
return notes
|
||||
|
||||
|
||||
class SysUserNotification(models.Model):
|
||||
"""
|
||||
system notification to designated user
|
||||
"""
|
||||
message = models.TextField(null=False, blank=False)
|
||||
to_user = models.CharField(max_length=255, db_index=True)
|
||||
seen = models.BooleanField(default=False, db_index=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||
objects = SysUserNotificationManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
|
||||
def update_notification_to_seen(self):
|
||||
self.seen = True
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def format_msg(self):
|
||||
return urlize(self.message, autoescape=True)
|
||||
|
||||
def to_dict(self):
|
||||
email = self.to_user
|
||||
orgs = ccnet_api.get_orgs_by_user(email)
|
||||
org_name = ''
|
||||
try:
|
||||
if orgs:
|
||||
org_name = orgs[0].org_name
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return {
|
||||
'id': self.id,
|
||||
'msg': self.message,
|
||||
'username': self.to_user,
|
||||
'name': email2nickname(self.to_user),
|
||||
'contact_email': email2contact_email(self.to_user),
|
||||
'seen': self.seen,
|
||||
'org_name': org_name,
|
||||
'created_at': datetime_to_isoformat_timestr(self.created_at),
|
||||
'msg_format': self.format_msg
|
||||
}
|
||||
|
||||
########## user notification
|
||||
MSG_TYPE_GROUP_JOIN_REQUEST = 'group_join_request'
|
||||
MSG_TYPE_ADD_USER_TO_GROUP = 'add_user_to_group'
|
||||
|
@ -92,7 +92,8 @@ from seahub.api2.endpoints.invitations import InvitationsView, InvitationsBatchV
|
||||
from seahub.api2.endpoints.invitation import InvitationView, InvitationRevokeView
|
||||
from seahub.api2.endpoints.repo_share_invitations import RepoShareInvitationsView, RepoShareInvitationsBatchView
|
||||
from seahub.api2.endpoints.repo_share_invitation import RepoShareInvitationView
|
||||
from seahub.api2.endpoints.notifications import NotificationsView, NotificationView, SdocNotificationView, SdocNotificationsView, AllNotificationsView
|
||||
from seahub.api2.endpoints.notifications import NotificationsView, NotificationView, SdocNotificationView, SdocNotificationsView, \
|
||||
SysUserNotificationSeenView, AllNotificationsView, SysUserNotificationUnseenView
|
||||
from seahub.api2.endpoints.repo_file_uploaded_bytes import RepoFileUploadedBytesView
|
||||
from seahub.api2.endpoints.user_avatar import UserAvatarView
|
||||
from seahub.api2.endpoints.wikis import WikisView, WikiView
|
||||
@ -192,7 +193,8 @@ from seahub.api2.endpoints.admin.group_owned_libraries import AdminGroupOwnedLib
|
||||
from seahub.api2.endpoints.admin.user_activities import UserActivitiesView
|
||||
from seahub.api2.endpoints.admin.file_scan_records import AdminFileScanRecords
|
||||
from seahub.api2.endpoints.admin.notifications import AdminNotificationsView
|
||||
from seahub.api2.endpoints.admin.sys_notifications import AdminSysNotificationsView, AdminSysNotificationView
|
||||
from seahub.api2.endpoints.admin.sys_notifications import AdminSysNotificationsView, AdminSysNotificationView, \
|
||||
AdminSysUserNotificationView, AdminSysUserNotificationsView
|
||||
from seahub.api2.endpoints.admin.logs import AdminLogsLoginLogs, AdminLogsFileAccessLogs, AdminLogsFileUpdateLogs, \
|
||||
AdminLogsSharePermissionLogs, AdminLogsFileTransferLogs, AdminLogGroupMemberAuditLogs
|
||||
from seahub.api2.endpoints.admin.terms_and_conditions import AdminTermsAndConditions, AdminTermAndCondition
|
||||
@ -530,6 +532,8 @@ urlpatterns = [
|
||||
re_path(r'^api/v2.1/sdoc-notification/$', SdocNotificationView.as_view(), name='api-v2.1-notification'),
|
||||
re_path(r'^api/v2.1/all-notifications/$', AllNotificationsView.as_view(), name='api-v2.1-all-notification'),
|
||||
|
||||
re_path(r'^api/v2.1/sys-user-notifications/(?P<nid>\d+)/seen/$', SysUserNotificationSeenView.as_view(), name='api-v2.1-notification-seen'),
|
||||
re_path(r'^api/v2.1/sys-user-notifications/unseen/$', SysUserNotificationUnseenView.as_view(), name='api-v2.1-notification-unseen'),
|
||||
## user::invitations
|
||||
re_path(r'^api/v2.1/invitations/$', InvitationsView.as_view()),
|
||||
re_path(r'^api/v2.1/invitations/batch/$', InvitationsBatchView.as_view()),
|
||||
@ -796,7 +800,8 @@ urlpatterns = [
|
||||
re_path(r'^api/v2.1/admin/notifications/$', AdminNotificationsView.as_view(), name='api-2.1-admin-notifications'),
|
||||
re_path(r'^api/v2.1/admin/sys-notifications/$', AdminSysNotificationsView.as_view(), name='api-2.1-admin-sys-notifications'),
|
||||
re_path(r'^api/v2.1/admin/sys-notifications/(?P<nid>\d+)/$', AdminSysNotificationView.as_view(),name='api-2.1-admin-sys-notification'),
|
||||
|
||||
re_path(r'^api/v2.1/admin/sys-user-notifications/$', AdminSysUserNotificationsView.as_view(), name='api-2.1-admin-sys-user-notifications'),
|
||||
re_path(r'^api/v2.1/admin/sys-user-notifications/(?P<nid>\d+)/$', AdminSysUserNotificationView.as_view(), name='api-2.1-admin-sys-user-notification'),
|
||||
## admin::terms and conditions
|
||||
re_path(r'^api/v2.1/admin/terms-and-conditions/$', AdminTermsAndConditions.as_view(), name='api-v2.1-admin-terms-and-conditions'),
|
||||
re_path(r'^api/v2.1/admin/terms-and-conditions/(?P<term_id>\d+)/$', AdminTermAndCondition.as_view(), name='api-v2.1-admin-term-and-condition'),
|
||||
|
@ -244,3 +244,15 @@ class CcnetDB:
|
||||
'is_manual_set': is_manual_set
|
||||
}
|
||||
return CcnetUserRole(**params)
|
||||
|
||||
def get_org_staffs(self, org_id):
|
||||
sql = f"""
|
||||
SELECT email
|
||||
FROM `{self.db_name}`.`OrgUser`
|
||||
WHERE org_id={org_id} AND is_staff=1
|
||||
"""
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(sql)
|
||||
staffs = cursor.fetchall()
|
||||
|
||||
return [s[0] for s in staffs]
|
||||
|
@ -1641,3 +1641,15 @@ CREATE TABLE `group_member_audit` (
|
||||
KEY `idx_group_member_audit_user` (`user`),
|
||||
KEY `idx_group_member_audit_group_id` (`group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE `notifications_sysusernotification` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`message` longtext NOT NULL,
|
||||
`to_user` varchar(255) NOT NULL,
|
||||
`seen` tinyint(1) NOT NULL,
|
||||
`created_at` datetime(6) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `notifications_sysusernotification_to_user_e0c9101e` (`to_user`),
|
||||
KEY `notifications_sysusernotification_seen_9d851bf7` (`seen`),
|
||||
KEY `notifications_sysusernotification_created_at_56ffd2a0` (`created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
Loading…
Reference in New Issue
Block a user