1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-13 13:50:07 +00:00

[api2] Add apis for contacts and user messages

This commit is contained in:
poetwang
2014-01-17 23:27:45 +08:00
parent 26557ab2e6
commit a8beff8dfc
8 changed files with 465 additions and 95 deletions

View File

@@ -30,7 +30,13 @@
</div> </div>
</div><!-- wrapper --> </div><!-- wrapper -->
<script type="text/javascript" src="{{ MEDIA_URL }}js/jq.min.js?t=1369028460"></script> <script type="text/javascript" src="{{ MEDIA_URL }}js/jq.min.js?t=1369028460"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/base.js?t=1369028460"></script>
<script type="text/javascript">
function e(str) {
return encodeURIComponent(str);
}
</script>
{% block extra_script %}{% endblock %} {% block extra_script %}{% endblock %}
</body> </body>
</html> </html>

View File

@@ -0,0 +1,15 @@
{% load seahub_tags avatar_tags i18n %}
<li class="msg w100 ovhd">
{% avatar msg.from_email 48 %}
<div class="txt">
<div class="msg-main">
<div class="msg-hd w100 ovhd">
<span class="author" >{{ msg.from_email|email2nickname }}</a>
<span class="time">{{ msg.timestamp|translate_seahub_time }}</span>
</div>
<p class="msg-con">{{ msg.message|seahub_urlize|find_at|linebreaksbr }}</p>
<span class="say"></span>
</div>
</div>
</li>

View File

@@ -0,0 +1,34 @@
{% load seahub_tags avatar_tags i18n %}
{% load url from future %}
{% if person_msgs %}
{% for msg in person_msgs.object_list %}
<li class="msg w100 ovhd">
{% avatar msg.from_email 48 %}
<div class="txt">
<div class="msg-main">
<div class="msg-hd w100 ovhd">
<span class="author" >{{ msg.from_email|email2nickname }}</a>
<span class="time">{{ msg.timestamp|translate_seahub_time }}</span>
</div>
<p class="msg-con">{{ msg.message|seahub_urlize|find_at|linebreaksbr }}</p>
{% if msg.attachments %}
<ul class="msg-attachment">
{% for att in msg.attachments %}
<li>
<img src="{{ MEDIA_URL }}img/file/{{ att.name|file_icon_filter }}" alt="{% trans "File"%}" height="18" class="vam" />
<span class="name vam">{{ att.name }}</span>
</li>
{% endfor %}
</ul>
{% endif %}
<span class="say"></span>
</div>
</div>
</li>
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,84 @@
{% extends "api2/base.html" %}
{% load seahub_tags avatar_tags i18n %}
{% load url from future %}
{% block sub_title %}{% trans "Messages" %} - {% endblock %}
{% block main_panel %}
<h3 class="hd">{% blocktrans with name=to_email|email2nickname%}Messages with {{name}}{% endblocktrans %}</h3>
<div id="personal-msg-panel" class="msg-panel personal-msg-panel">
<ul class="msg-list">
{% if person_msgs %}
{% include "api2/user_msg_body.html" %}
{% else %}
<div id="placeholder" style="margin-top:20%;">
<p style="text-align:center">{% trans "No messages." %}</p>
</div>
{% endif %}
</ul>
{% if person_msgs.has_other_pages %}
<div id="loading-icon" data-next="{{ person_msgs.next_page_number }}"><img src="{{MEDIA_URL}}img/loading-icon.gif" alt="{% trans 'Loading...' %}" /></div>
<p id="loading-error" class="error hide"></p>
{% endif %}
</div>
{% endblock %}
{% block extra_script %}{{block.super}}
<script type="text/javascript">
var g_token = "TOKEN";
function setToken(token) {
g_token = token;
}
function getToken() {
return g_token;
}
function addMessage(html) {
$('.msg-list').prepend(html);
}
{% if person_msgs.has_next %}
var g_loading = false;
$(document).scroll(function() {
var loading_icon = $('#loading-icon');
if (loading_icon.data('next') && $(window).height() + $(window).scrollTop() == $(document).height() && !g_loading) {
g_loading = true;
loading_icon.show();
$.ajax({
url:'{% url 'api_more_usermsgs' to_email %}?page=' + e(loading_icon.data('next')),
dataType: 'json',
cache: false,
headers:{Authorization:'Token '+g_token},
success: function(data) {
loading_icon.data('next', data['next_page']);
$('.msg-list').append(data['html']);
if (!data['next_page']) {
loading_icon.hide();
$('.msg:last-child').css({'border-bottom':0});
}
g_loading = false;
},
error: function(jqXHR, textStatus, errorThrown) {
loading_icon.hide();
g_loading = false;
if (!jqXHR.responseText) {
$('#loading-error').html("{% trans "Failed. Please check the network." %}").removeClass('hide');
}
}
});
}
});
{% endif %}
</script>
{% endblock %}

View File

@@ -12,7 +12,7 @@ urlpatterns = patterns('',
url(r'^accounts/$', Accounts.as_view(), name="accounts"), url(r'^accounts/$', Accounts.as_view(), name="accounts"),
url(r'^accounts/(?P<email>\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/$', Account.as_view(), name="api2-account"), url(r'^accounts/(?P<email>\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/$', Account.as_view(), name="api2-account"),
url(r'^account/info/$', AccountInfo.as_view()), url(r'^account/info/$', AccountInfo.as_view()),
# url(r'^regdevice/$', RegDevice.as_view(), name="regdevice"), url(r'^regdevice/$', RegDevice.as_view(), name="regdevice"),
url(r'^search/$', Search.as_view(), name='api_search'), url(r'^search/$', Search.as_view(), name='api_search'),
url(r'^repos/$', Repos.as_view(), name="api2-repos"), url(r'^repos/$', Repos.as_view(), name="api2-repos"),
url(r'^repos/(?P<repo_id>[-0-9a-f]{36})/$', Repo.as_view(), name="api2-repo"), url(r'^repos/(?P<repo_id>[-0-9a-f]{36})/$', Repo.as_view(), name="api2-repo"),
@@ -40,7 +40,9 @@ urlpatterns = patterns('',
url(r'^shared-files/$', SharedFilesView.as_view()), url(r'^shared-files/$', SharedFilesView.as_view()),
url(r'^virtual-repos/$', VirtualRepos.as_view()), url(r'^virtual-repos/$', VirtualRepos.as_view()),
url(r'^groups/$', Groups.as_view()), #url(r'^groups/$', Groups.as_view()),
url(r'^groups/$', Contacts.as_view()),
url(r'^contacts/$', Contacts.as_view()),
url(r'^events/$', EventsView.as_view()), url(r'^events/$', EventsView.as_view()),
url(r'^unseen_messages/$', UnseenMessagesCountView.as_view()), url(r'^unseen_messages/$', UnseenMessagesCountView.as_view()),
url(r'^avatars/(?P<user>\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/resized/(?P<size>[0-9]+)/$', AvatarView.as_view()), url(r'^avatars/(?P<user>\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/resized/(?P<size>[0-9]+)/$', AvatarView.as_view()),
@@ -51,6 +53,8 @@ urlpatterns = patterns('',
url(r'^html/discussion/(?P<msg_id>\d+)/$', DiscussionHtml.as_view(), name="api_discussion"), url(r'^html/discussion/(?P<msg_id>\d+)/$', DiscussionHtml.as_view(), name="api_discussion"),
url(r'^html/more_discussions/(?P<group_id>\d+)/$', AjaxDiscussions.as_view(), name="more_discussions"), url(r'^html/more_discussions/(?P<group_id>\d+)/$', AjaxDiscussions.as_view(), name="more_discussions"),
url(r'^html/newreply/$', NewReplyHtml.as_view()), url(r'^html/newreply/$', NewReplyHtml.as_view()),
url(r'^html/usermsgs/(?P<id_or_email>[^/]+)/$', UserMsgsHtml.as_view()),
url(r'^html/more_usermsgs/(?P<id_or_email>[^/]+)/$', AjaxUserMsgs.as_view(), name="api_more_usermsgs"),
# Folowing is only for debug, will be removed # Folowing is only for debug, will be removed
#url(r'^html/newreply2/$', api_new_replies), #url(r'^html/newreply2/$', api_new_replies),
@@ -61,6 +65,9 @@ urlpatterns = patterns('',
#url(r'^html/discussions2/(?P<group_id>\d+)/$', discussions2, name="api_discussions2"), #url(r'^html/discussions2/(?P<group_id>\d+)/$', discussions2, name="api_discussions2"),
#url(r'^html/discussion/(?P<msg_id>\d+)/$', discussion2, name="api_discussion2"), #url(r'^html/discussion/(?P<msg_id>\d+)/$', discussion2, name="api_discussion2"),
#url(r'^html/more_discussions/(?P<group_id>\d+)/$', more_discussions2, name="more_discussions"), #url(r'^html/more_discussions/(?P<group_id>\d+)/$', more_discussions2, name="more_discussions"),
#url(r'^html/usermsgs2/(?P<id_or_email>[^/]+)/$', api_usermsgs),
#url(r'^html/more_usermsgs/(?P<id_or_email>[^/]+)/$', api_more_usermsgs, name="api_more_usermsgs"),
# Deprecated # Deprecated
url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/fileops/delete/$', OpDeleteView.as_view()), url(r'^repos/(?P<repo_id>[-0-9-a-f]{36})/fileops/delete/$', OpDeleteView.as_view()),

View File

@@ -31,7 +31,7 @@ from serializers import AuthTokenSerializer, AccountSerializer
from utils import is_repo_writable, is_repo_accessible from utils import is_repo_writable, is_repo_accessible
from seahub.base.accounts import User from seahub.base.accounts import User
from seahub.base.models import FileDiscuss, UserStarredFiles, \ from seahub.base.models import FileDiscuss, UserStarredFiles, \
DirFilesLastModifiedInfo DirFilesLastModifiedInfo, DeviceToken
from seahub.share.models import FileShare from seahub.share.models import FileShare
from seahub.views import access_to_repo, validate_owner, is_registered_user, \ from seahub.views import access_to_repo, validate_owner, is_registered_user, \
group_events_data, get_diff, create_default_library group_events_data, get_diff, create_default_library
@@ -47,13 +47,15 @@ try:
from seahub.settings import CLOUD_MODE from seahub.settings import CLOUD_MODE
except ImportError: except ImportError:
CLOUD_MODE = False CLOUD_MODE = False
from seahub.notifications.models import UserNotification, DeviceToken from seahub.notifications.models import UserNotification
from seahub.utils.paginator import Paginator from seahub.utils.paginator import Paginator
from seahub.group.models import GroupMessage, MessageReply, MessageAttachment from seahub.group.models import GroupMessage, MessageReply, MessageAttachment
from seahub.group.signals import grpmsg_added, grpmsg_reply_added from seahub.group.signals import grpmsg_added, grpmsg_reply_added
from seahub.group.views import group_check from seahub.group.views import group_check
from seahub.contacts.models import Contact
from seahub.signals import repo_created, share_file_to_user_successful from seahub.signals import repo_created, share_file_to_user_successful
from seahub.share.models import PrivateFileDirShare from seahub.share.models import PrivateFileDirShare
from seahub.message.models import UserMessage, UserMsgAttachment
from seahub.utils import EVENTS_ENABLED, \ from seahub.utils import EVENTS_ENABLED, \
api_convert_desc_link, get_file_type_and_ext, \ api_convert_desc_link, get_file_type_and_ext, \
HAS_FILE_SEARCH, gen_file_share_link, gen_dir_share_link HAS_FILE_SEARCH, gen_file_share_link, gen_dir_share_link
@@ -163,7 +165,7 @@ class Account(APIView):
def get(self, request, email, format=None): def get(self, request, email, format=None):
if not is_valid_username(email): if not is_valid_username(email):
return api_error(status.HTTP_404_NOT_FOUND, 'User not found.') return api_error(status.HTTP_404_NOT_FOUND, 'User not found.')
# query account info # query account info
try: try:
user = User.objects.get(email=email) user = User.objects.get(email=email)
@@ -190,7 +192,7 @@ class Account(APIView):
def put(self, request, email, format=None): def put(self, request, email, format=None):
if not is_valid_username(email): if not is_valid_username(email):
return api_error(status.HTTP_404_NOT_FOUND, 'User not found.') return api_error(status.HTTP_404_NOT_FOUND, 'User not found.')
# create or update account # create or update account
copy = request.DATA.copy() copy = request.DATA.copy()
copy.update({'email': email}) copy.update({'email': email})
@@ -219,7 +221,7 @@ class Account(APIView):
def delete(self, request, email, format=None): def delete(self, request, email, format=None):
if not is_valid_username(email): if not is_valid_username(email):
return api_error(status.HTTP_404_NOT_FOUND, 'User not found.') return api_error(status.HTTP_404_NOT_FOUND, 'User not found.')
# delete account # delete account
try: try:
user = User.objects.get(email=email) user = User.objects.get(email=email)
@@ -260,13 +262,26 @@ class RegDevice(APIView):
throttle_classes = (UserRateThrottle, ) throttle_classes = (UserRateThrottle, )
def post(self, request, format=None): def post(self, request, format=None):
version = request.POST.get('version')
platform = request.POST.get('platform')
pversion = request.POST.get('pversion')
devicetoken = request.POST.get('deviceToken') devicetoken = request.POST.get('deviceToken')
if not devicetoken: if not devicetoken or not version or not platform or not pversion:
return api_error(status.HTTP_400_BAD_REQUEST, "Missing argument") return api_error(status.HTTP_400_BAD_REQUEST, "Missing argument")
token, created = DeviceToken.objects.get_or_create( token, modified = DeviceToken.objects.get_or_create(
token=devicetoken, user=request.user.username) token=devicetoken, user=request.user.username)
if created: if token.version != version:
token.version = version
modified = True
if token.pversion != pversion:
token.pversion = pversion
modified = True
if token.platform != platform:
token.platform = platform
modified = True
if modified:
token.save() token.save()
return Response("success") return Response("success")
@@ -1331,7 +1346,7 @@ class FileRevert(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, 'Server error') return api_error(status.HTTP_400_BAD_REQUEST, 'Server error')
return HttpResponse(json.dumps({"ret": ret}), status=200, content_type=json_content_type) return HttpResponse(json.dumps({"ret": ret}), status=200, content_type=json_content_type)
class FileRevision(APIView): class FileRevision(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
@@ -1612,7 +1627,7 @@ class DirDownloadView(APIView):
redirect_url = gen_file_get_url(token, dirname) redirect_url = gen_file_get_url(token, dirname)
return HttpResponse(json.dumps(redirect_url), status=200, content_type=json_content_type) return HttpResponse(json.dumps(redirect_url), status=200, content_type=json_content_type)
class DirShareView(APIView): class DirShareView(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
@@ -1631,7 +1646,7 @@ class DirShareView(APIView):
for email in [e.strip() for e in emails if e.strip()]: for email in [e.strip() for e in emails if e.strip()]:
if not is_registered_user(email): if not is_registered_user(email):
continue continue
if s_type == 'f': if s_type == 'f':
pfds = PrivateFileDirShare.objects.add_read_only_priv_file_share( pfds = PrivateFileDirShare.objects.add_read_only_priv_file_share(
username, email, repo_id, path) username, email, repo_id, path)
@@ -1644,7 +1659,7 @@ class DirShareView(APIView):
# send a signal when sharing file successful # send a signal when sharing file successful
share_file_to_user_successful.send(sender=None, priv_share_obj=pfds) share_file_to_user_successful.send(sender=None, priv_share_obj=pfds)
return HttpResponse(json.dumps({}), status=200, content_type=json_content_type) return HttpResponse(json.dumps({}), status=200, content_type=json_content_type)
class DirSubRepoView(APIView): class DirSubRepoView(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
@@ -1658,14 +1673,13 @@ class DirSubRepoView(APIView):
if it does not have, create one if it does not have, create one
''' '''
content_type = 'application/json; charset=utf-8'
result = {} result = {}
path = request.GET.get('p') path = request.GET.get('p')
name = request.GET.get('name') name = request.GET.get('name')
if not (path and name): if not (path and name):
result['error'] = 'Argument missing' result['error'] = 'Argument missing'
return HttpResponse(json.dumps(result), status=400, content_type=content_type) return HttpResponse(json.dumps(result), status=400, content_type=json_content_type)
username = request.user.username username = request.user.username
@@ -1674,7 +1688,7 @@ class DirSubRepoView(APIView):
sub_repo = seafile_api.get_virtual_repo(repo_id, path, username) sub_repo = seafile_api.get_virtual_repo(repo_id, path, username)
except SearpcError, e: except SearpcError, e:
result['error'] = e.msg result['error'] = e.msg
return HttpResponse(json.dumps(result), status=500, content_type=content_type) return HttpResponse(json.dumps(result), status=500, content_type=json_content_type)
if sub_repo: if sub_repo:
result['sub_repo_id'] = sub_repo.id result['sub_repo_id'] = sub_repo.id
@@ -1686,9 +1700,9 @@ class DirSubRepoView(APIView):
result['sub_repo_id'] = sub_repo_id result['sub_repo_id'] = sub_repo_id
except SearpcError, e: except SearpcError, e:
result['error'] = e.msg result['error'] = e.msg
return HttpResponse(json.dumps(result), status=500, content_type=content_type) return HttpResponse(json.dumps(result), status=500, content_type=json_content_type)
return HttpResponse(json.dumps(result), content_type=content_type) return HttpResponse(json.dumps(result), content_type=json_content_type)
class SharedRepos(APIView): class SharedRepos(APIView):
""" """
@@ -1778,7 +1792,7 @@ class SharedFilesView(APIView):
for e in priv_share_in: for e in priv_share_in:
e.file_or_dir = os.path.basename(e.path.rstrip('/')) e.file_or_dir = os.path.basename(e.path.rstrip('/'))
e.repo = seafile_api.get_repo(e.repo_id) e.repo = seafile_api.get_repo(e.repo_id)
return HttpResponse(json.dumps({"priv_share_out": list(priv_share_out), "priv_share_in": list(priv_share_in)}, cls=PrivateFileDirShareEncoder), return HttpResponse(json.dumps({"priv_share_out": list(priv_share_out), "priv_share_in": list(priv_share_in)}, cls=PrivateFileDirShareEncoder),
status=200, content_type=json_content_type) status=200, content_type=json_content_type)
@@ -1789,25 +1803,24 @@ class SharedFilesView(APIView):
pfs = PrivateFileDirShare.objects.get_priv_file_dir_share_by_token(token) pfs = PrivateFileDirShare.objects.get_priv_file_dir_share_by_token(token)
except PrivateFileDirShare.DoesNotExist: except PrivateFileDirShare.DoesNotExist:
return api_error(status.HTTP_404_NOT_FOUND, "Token does not exist") return api_error(status.HTTP_404_NOT_FOUND, "Token does not exist")
from_user = pfs.from_user from_user = pfs.from_user
to_user = pfs.to_user to_user = pfs.to_user
username = request.user.username username = request.user.username
if username == from_user or username == to_user: if username == from_user or username == to_user:
pfs.delete() pfs.delete()
return HttpResponse(json.dumps({}), status=200, content_type=json_content_type) return HttpResponse(json.dumps({}), status=200, content_type=json_content_type)
else: else:
return api_error(status.HTTP_403_FORBIDDEN, return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to get repo.') 'You do not have permission to get repo.')
class VirtualRepos(APIView): class VirtualRepos(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, ) throttle_classes = (UserRateThrottle, )
def get(self, request, format=None): def get(self, request, format=None):
content_type = 'application/json; charset=utf-8'
result = {} result = {}
username = request.user.username username = request.user.username
@@ -1815,10 +1828,10 @@ class VirtualRepos(APIView):
result['virtual-repos'] = seafile_api.get_virtual_repos_by_owner(username) result['virtual-repos'] = seafile_api.get_virtual_repos_by_owner(username)
except SearpcError, e: except SearpcError, e:
result['error'] = e.msg result['error'] = e.msg
return HttpResponse(json.dumps(result), status=500, content_type=content_type) return HttpResponse(json.dumps(result), status=500, content_type=json_content_type)
return HttpResponse(json.dumps(result, cls=SearpcObjEncoder), content_type=json_content_type)
return HttpResponse(json.dumps(result, cls=SearpcObjEncoder), content_type=content_type)
class FileShareEncoder(json.JSONEncoder): class FileShareEncoder(json.JSONEncoder):
def default(self, obj): def default(self, obj):
if not isinstance(obj, FileShare): if not isinstance(obj, FileShare):
@@ -1849,7 +1862,7 @@ class SharedLinksView(APIView):
fs.delete() fs.delete()
continue continue
fs.filename = os.path.basename(fs.path) fs.filename = os.path.basename(fs.path)
fs.shared_link = gen_file_share_link(fs.token) fs.shared_link = gen_file_share_link(fs.token)
else: else:
if seafile_api.get_dir_id_by_path(r.id, fs.path) is None: if seafile_api.get_dir_id_by_path(r.id, fs.path) is None:
fs.delete() fs.delete()
@@ -1863,7 +1876,7 @@ class SharedLinksView(APIView):
def delete(self, request, format=None): def delete(self, request, format=None):
token = request.GET.get('t') token = request.GET.get('t')
FileShare.objects.filter(token=token).delete() FileShare.objects.filter(token=token).delete()
return HttpResponse(json.dumps({}), status=200, content_type=json_content_type) return HttpResponse(json.dumps({}), status=200, content_type=json_content_type)
class DefaultRepoView(APIView): class DefaultRepoView(APIView):
""" """
@@ -1925,7 +1938,7 @@ class SharedRepo(APIView):
if not is_valid_username(user): if not is_valid_username(user):
return api_error(status.HTTP_400_BAD_REQUEST, return api_error(status.HTTP_400_BAD_REQUEST,
'User is not valid') 'User is not valid')
group_id = request.GET.get('group_id', '') group_id = request.GET.get('group_id', '')
if not (share_type and user and group_id): if not (share_type and user and group_id):
return api_error(status.HTTP_400_BAD_REQUEST, return api_error(status.HTTP_400_BAD_REQUEST,
@@ -2054,7 +2067,7 @@ def events(request):
return HttpResponse(json.dumps({'html':html, 'events_more':events_more, return HttpResponse(json.dumps({'html':html, 'events_more':events_more,
'new_start': start}), 'new_start': start}),
content_type='application/json; charset=utf-8') content_type=json_content_type)
@group_check @group_check
@@ -2093,7 +2106,7 @@ def group_discuss(request, group):
ctx['msg'] = message ctx['msg'] = message
html = render_to_string("api2/discussion_posted.html", ctx) html = render_to_string("api2/discussion_posted.html", ctx)
serialized_data = json.dumps({"html": html}) serialized_data = json.dumps({"html": html})
return HttpResponse(serialized_data, content_type="application/json; charset=utf-8") return HttpResponse(serialized_data, content_type=json_content_type)
group_msgs = get_group_msgs(group.id, page=1, username=request.user.username) group_msgs = get_group_msgs(group.id, page=1, username=request.user.username)
# remove user notifications # remove user notifications
@@ -2104,6 +2117,42 @@ def group_discuss(request, group):
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
def user_messages(request, id_or_email):
try:
uid = int(id_or_email)
try:
user = User.objects.get(id=uid)
except User.DoesNotExist:
user = None
if not user:
return api_error(status.HTTP_404_NOT_FOUND, 'User not found.')
to_email = user.email
except ValueError:
to_email = id_or_email
username = request.user.username
if request.method == 'POST':
mass_msg = request.POST.get('message')
if not mass_msg:
return api_error(status.HTTP_400_BAD_REQUEST, "Missing argument")
usermsg = UserMessage.objects.add_unread_message(username, to_email, mass_msg)
ctx = { 'msg' : usermsg }
html = render_to_string("api2/user_msg.html", ctx)
serialized_data = json.dumps({"html": html})
return HttpResponse(serialized_data, content_type=json_content_type)
UserNotification.objects.seen_user_msg_notices(username, to_email)
UserMessage.objects.update_unread_messages(to_email, username)
person_msgs = get_person_msgs(to_email, 1, username)
return render_to_response("api2/user_msg_list.html", {
"person_msgs": person_msgs,
"to_email": to_email,
}, context_instance=RequestContext(request))
def get_group_msgs(groupid, page, username): def get_group_msgs(groupid, page, username):
# Show 15 group messages per page. # Show 15 group messages per page.
@@ -2166,9 +2215,70 @@ def get_group_msgs(groupid, page, username):
return group_msgs return group_msgs
def get_person_msgs(to_email, page, username):
# Show 15 group messages per page.
paginator = Paginator(UserMessage.objects.get_messages_between_users(username, to_email).order_by('-timestamp'), 15)
# If page request (9999) is out of range, return None
try:
person_msgs = paginator.page(page)
except (EmptyPage, InvalidPage):
person_msgs = paginator.page(paginator.num_pages)
# Force evaluate queryset to fix some database error for mysql.
person_msgs.object_list = list(person_msgs.object_list)
attachments = UserMsgAttachment.objects.list_attachments_by_user_msgs(person_msgs.object_list)
for msg in person_msgs.object_list:
msg.attachments = []
for att in attachments:
if att.user_msg != msg:
continue
pfds = att.priv_file_dir_share
if pfds is None: # in case that this attachment is unshared.
continue
att.repo_id = pfds.repo_id
att.path = pfds.path
att.name = os.path.basename(pfds.path.rstrip('/'))
att.token = pfds.token
msg.attachments.append(att)
return person_msgs
def more_usermsgs(request, id_or_email):
try:
page = int(request.GET.get('page'))
except ValueError:
page = 2
try:
uid = int(id_or_email)
try:
user = User.objects.get(id=uid)
except User.DoesNotExist:
user = None
if not user:
return api_error(status.HTTP_404_NOT_FOUND, 'User not found.')
to_email = user.email
except ValueError:
to_email = id_or_email
person_msgs = get_person_msgs(to_email, page, request.user.username)
if person_msgs.has_next():
next_page = person_msgs.next_page_number()
else:
next_page = None
html = render_to_string('api2/user_msg_body.html', {"person_msgs": person_msgs}, context_instance=RequestContext(request))
return HttpResponse(json.dumps({"html": html, 'next_page': next_page}), content_type=json_content_type)
@group_check @group_check
def more_discussions(request, group): def more_discussions(request, group):
content_type = 'application/json; charset=utf-8'
try: try:
page = int(request.GET.get('page')) page = int(request.GET.get('page'))
except ValueError: except ValueError:
@@ -2181,7 +2291,7 @@ def more_discussions(request, group):
next_page = None next_page = None
html = render_to_string('api2/discussions_body.html', {"group_msgs": group_msgs}, context_instance=RequestContext(request)) html = render_to_string('api2/discussions_body.html', {"group_msgs": group_msgs}, context_instance=RequestContext(request))
return HttpResponse(json.dumps({"html": html, 'next_page': next_page}), content_type=content_type) return HttpResponse(json.dumps({"html": html, 'next_page': next_page}), content_type=json_content_type)
def discussion(request, msg_id): def discussion(request, msg_id):
try: try:
@@ -2231,7 +2341,6 @@ def discussion(request, msg_id):
def msg_reply(request, msg_id): def msg_reply(request, msg_id):
"""Show group message replies, and process message reply in ajax""" """Show group message replies, and process message reply in ajax"""
content_type = 'application/json; charset=utf-8'
ctx = {} ctx = {}
try: try:
group_msg = GroupMessage.objects.get(id=msg_id) group_msg = GroupMessage.objects.get(id=msg_id)
@@ -2255,26 +2364,25 @@ def msg_reply(request, msg_id):
ctx['r'] = msg_reply ctx['r'] = msg_reply
html = render_to_string("api2/reply.html", ctx) html = render_to_string("api2/reply.html", ctx)
serialized_data = json.dumps({"html": html}) serialized_data = json.dumps({"html": html})
return HttpResponse(serialized_data, content_type=content_type) return HttpResponse(serialized_data, content_type=json_content_type)
def repo_history_changes(request, repo_id): def repo_history_changes(request, repo_id):
changes = {} changes = {}
content_type = 'application/json; charset=utf-8'
if not access_to_repo(request, repo_id, ''): if not access_to_repo(request, repo_id, ''):
return HttpResponse(json.dumps({"err": 'Permission denied'}), status=400, content_type=content_type) return HttpResponse(json.dumps({"err": 'Permission denied'}), status=400, content_type=json_content_type)
repo = get_repo(repo_id) repo = get_repo(repo_id)
if not repo: if not repo:
return HttpResponse(json.dumps({"err": 'Library does not exist'}), status=400, content_type=content_type) return HttpResponse(json.dumps({"err": 'Library does not exist'}), status=400, content_type=json_content_type)
if repo.encrypted and not is_passwd_set(repo_id, request.user.username): if repo.encrypted and not is_passwd_set(repo_id, request.user.username):
return HttpResponse(json.dumps({"err": 'Library is encrypted'}), status=400, content_type=content_type) return HttpResponse(json.dumps({"err": 'Library is encrypted'}), status=400, content_type=json_content_type)
commit_id = request.GET.get('commit_id', '') commit_id = request.GET.get('commit_id', '')
if not commit_id: if not commit_id:
return HttpResponse(json.dumps({"err": 'Invalid argument'}), status=400, content_type=content_type) return HttpResponse(json.dumps({"err": 'Invalid argument'}), status=400, content_type=json_content_type)
changes = get_diff(repo_id, '', commit_id) changes = get_diff(repo_id, '', commit_id)
@@ -2293,7 +2401,7 @@ def repo_history_changes(request, repo_id):
changes[k] = [f.replace ('a href="/', 'a class="normal" href="api://') for f in changes[k] ] changes[k] = [f.replace ('a href="/', 'a class="normal" href="api://') for f in changes[k] ]
html = render_to_string('api2/event_details.html', {'changes': changes}) html = render_to_string('api2/event_details.html', {'changes': changes})
return HttpResponse(json.dumps({"html": html}), content_type=content_type) return HttpResponse(json.dumps({"html": html}), content_type=json_content_type)
def msg_reply_new(request): def msg_reply_new(request):
@@ -2343,51 +2451,129 @@ def msg_reply_new(request):
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
def get_groups(email):
group_json = []
joined_groups = get_personal_groups_by_user(email)
grpmsgs = {}
for g in joined_groups:
grpmsgs[g.id] = 0
notes = UserNotification.objects.get_user_notifications(email, seen=False)
replynum = 0;
for n in notes:
if n.is_group_msg():
try:
gid = n.group_message_detail_to_dict().get('group_id')
except UserNotification.InvalidDetailError:
continue
if gid not in grpmsgs:
continue
grpmsgs[gid] = grpmsgs[gid] + 1
elif n.is_grpmsg_reply():
replynum = replynum + 1
for g in joined_groups:
msg = GroupMessage.objects.filter(group_id=g.id).order_by('-timestamp')[:1]
mtime = 0
if len(msg) >= 1:
mtime = int(time.mktime(msg[0].timestamp.timetuple()))
group = {
"id":g.id,
"name":g.group_name,
"creator":g.creator_name,
"ctime":g.timestamp,
"mtime":mtime,
"msgnum":grpmsgs[g.id],
}
group_json.append(group)
return group_json, replynum
def get_contacts(email):
group_json = []
contacts_json = []
gmsgnum = umsgnum = replynum = 0
contacts = Contact.objects.filter(user_email=email)
joined_groups = get_personal_groups_by_user(email)
gmsgnums = umsgnums = {}
notes = UserNotification.objects.get_user_notifications(email, seen=False)
for n in notes:
if n.is_group_msg():
try:
gid = n.group_message_detail_to_dict().get('group_id')
except UserNotification.InvalidDetailError:
continue
gmsgnums[gid] = gmsgnums.get(gid, 0) + 1
elif n.is_grpmsg_reply():
replynum = replynum + 1
elif n.is_user_message():
umsgnums[n.detail] = umsgnums.get(n.detail, 0) + 1
for g in joined_groups:
msg = GroupMessage.objects.filter(group_id=g.id).order_by('-timestamp')[:1]
mtime = 0
if len(msg) >= 1:
mtime = int(time.mktime(msg[0].timestamp.timetuple()))
group = {
"id":g.id,
"name":g.group_name,
"creator":g.creator_name,
"ctime":g.timestamp,
"mtime":mtime,
"msgnum":gmsgnums.get(g.id, 0),
}
gmsgnum = gmsgnum + gmsgnums.get(g.id, 0)
group_json.append(group)
for contact in contacts:
msg = UserMessage.objects.get_messages_related_to_user(
contact.contact_email).order_by('-timestamp')[:1]
mtime = 0
if len(msg) >= 1:
mtime = int(time.mktime(msg[0].timestamp.timetuple()))
c = {
'email' : contact.contact_email,
'name' : email2nickname(contact.contact_email),
"msgnum" : umsgnums.get(contact.contact_email, 0),
"mtime" : mtime,
}
umsgnum = umsgnum + umsgnums.get(contact.contact_email, 0)
contacts_json.append(c)
return contacts_json, umsgnum, group_json, replynum, gmsgnum
class Groups(APIView): class Groups(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, ) throttle_classes = (UserRateThrottle, )
def get(self, request, format=None): def get(self, request, format=None):
email = request.user.username group_json, replynum = get_groups(request.user.username)
group_json = []
joined_groups = get_personal_groups_by_user(email)
grpmsgs = {}
for g in joined_groups:
grpmsgs[g.id] = 0
notes = UserNotification.objects.get_user_notifications(request.user.username, seen=False)
replynum = 0;
for n in notes:
if n.is_group_msg():
try:
gid = n.group_message_detail_to_dict().get('group_id')
except UserNotification.InvalidDetailError:
continue
if gid not in grpmsgs:
continue
grpmsgs[gid] = grpmsgs[gid] + 1
elif n.is_grpmsg_reply():
replynum = replynum + 1
for g in joined_groups:
msg = GroupMessage.objects.filter(group_id=g.id).order_by('-timestamp')[:1]
mtime = 0
if len(msg) >= 1:
mtime = int(time.mktime(msg[0].timestamp.timetuple()))
group = {
"id":g.id,
"name":g.group_name,
"creator":g.creator_name,
"ctime":g.timestamp,
"mtime":mtime,
"msgnum":grpmsgs[g.id],
}
group_json.append(group)
res = {"groups": group_json, "replynum":replynum} res = {"groups": group_json, "replynum":replynum}
return Response(res) return Response(res)
class Contacts(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
contacts, umsgnum, group_json, replynum, gmsgnum = get_contacts(request.user.username)
res = {
"groups": group_json,
"contacts": contacts,
"replynum": replynum,
"umsgnum" : umsgnum,
"gmsgnum" : gmsgnum,
}
return Response(res)
class AjaxEvents(APIView): class AjaxEvents(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
@@ -2404,6 +2590,15 @@ class AjaxDiscussions(APIView):
def get(self, request, group_id, format=None): def get(self, request, group_id, format=None):
return more_discussions(request, group_id) return more_discussions(request, group_id)
class AjaxUserMsgs(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, id_or_email, format=None):
return more_usermsgs(request, id_or_email)
class EventsView(APIView): class EventsView(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
@@ -2519,6 +2714,16 @@ class DiscussionsHtml(APIView):
def post(self, request, group_id, format=None): def post(self, request, group_id, format=None):
return group_discuss(request, group_id) return group_discuss(request, group_id)
class UserMsgsHtml(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, id_or_email, format=None):
return user_messages(request, id_or_email)
def post(self, request, id_or_email, format=None):
return user_messages(request, id_or_email)
class DiscussionHtml(APIView): class DiscussionHtml(APIView):
authentication_classes = (TokenAuthentication, ) authentication_classes = (TokenAuthentication, )
@@ -2574,3 +2779,11 @@ def api_msg_reply(request, msg_id):
@login_required @login_required
def api_new_replies(request): def api_new_replies(request):
return msg_reply_new(request) return msg_reply_new(request)
@login_required
def api_usermsgs(request, id_or_email):
return user_messages(request, id_or_email)
@login_required
def api_more_usermsgs(request, id_or_email):
return more_usermsgs(request, id_or_email)

View File

@@ -10,6 +10,8 @@ from seaserv import seafile_api
from seahub.auth.signals import user_logged_in from seahub.auth.signals import user_logged_in
from seahub.group.models import GroupMessage from seahub.group.models import GroupMessage
from seahub.utils import calc_file_path_hash from seahub.utils import calc_file_path_hash
from fields import LowerCaseCharField
# Get an instance of a logger # Get an instance of a logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -552,4 +554,19 @@ class InnerPubMsgReply(models.Model):
timestamp = models.DateTimeField(default=datetime.datetime.now) timestamp = models.DateTimeField(default=datetime.datetime.now)
class DeviceToken(models.Model):
"""
The iOS device token model.
"""
token = models.CharField(max_length=80)
user = LowerCaseCharField(max_length=255)
platform = LowerCaseCharField(max_length=32)
version = LowerCaseCharField(max_length=16)
pversion = LowerCaseCharField(max_length=16)
class Meta:
unique_together = (("token", "user"),)
def __unicode__(self):
return "/".join(self.user, self.token)

View File

@@ -16,20 +16,6 @@ from seahub.base.fields import LowerCaseCharField
from seahub.base.templatetags.seahub_tags import email2nickname from seahub.base.templatetags.seahub_tags import email2nickname
class DeviceToken(models.Model):
"""
The iOS device token model.
"""
token = models.CharField(max_length=80)
user = LowerCaseCharField(max_length=255)
class Meta:
unique_together = (("token", "user"),)
def __unicode__(self):
return "/".join(self.user, self.token)
########## system notification ########## system notification
class Notification(models.Model): class Notification(models.Model):
message = models.CharField(max_length=512) message = models.CharField(max_length=512)
@@ -170,6 +156,14 @@ class UserNotificationManager(models.Manager):
except UserNotification.InvalidDetailError: except UserNotification.InvalidDetailError:
continue continue
def seen_user_msg_notices(self, to_user, from_user):
"""Mark group message notices of a user as seen.
"""
user_notices = super(UserNotificationManager, self).filter(
detail=from_user, to_user=to_user, msg_type=MSG_TYPE_USER_MESSAGE)
for notice in user_notices:
notice.is_seen()
def remove_group_msg_notices(self, to_user, group_id): def remove_group_msg_notices(self, to_user, group_id):
"""Remove group message notices of a user. """Remove group message notices of a user.
""" """