1# Copyright (c) 2012-2016 Seafile Ltd.
2import logging
3from datetime import datetime
4
5from rest_framework.authentication import SessionAuthentication
6from rest_framework.permissions import IsAuthenticated
7from rest_framework.response import Response
8from rest_framework.views import APIView
9from rest_framework import status
10
11from seaserv import seafile_api
12
13from seahub.api2.throttling import UserRateThrottle
14from seahub.api2.authentication import TokenAuthentication
15from seahub.api2.utils import api_error
16from seahub.utils import get_file_history
17from seahub.utils.timeutils import utc_datetime_to_isoformat_timestr, timestamp_to_isoformat_timestr
18from seahub.utils.file_revisions import get_file_revisions_within_limit
19from seahub.views import check_folder_permission
20from seahub.avatar.templatetags.avatar_tags import api_avatar_url
21from seahub.base.templatetags.seahub_tags import email2nickname, \
22        email2contact_email
23
24logger = logging.getLogger(__name__)
25
26def get_new_file_history_info(ent, avatar_size):
27
28    info = {}
29
30    creator_name = ent.op_user
31    url, is_default, date_uploaded = api_avatar_url(creator_name, avatar_size)
32
33    info['creator_avatar_url'] = url
34    info['creator_email'] = creator_name
35    info['creator_name'] = email2nickname(creator_name)
36    info['creator_contact_email'] = email2contact_email(creator_name)
37    info['op_type'] = ent.op_type
38    info['ctime'] = utc_datetime_to_isoformat_timestr(ent.timestamp)
39    info['commit_id'] = ent.commit_id
40    info['size'] = ent.size
41    info['rev_file_id'] = ent.file_id
42    info['old_path'] = ent.old_path if hasattr(ent, 'old_path') else ''
43    info['path'] = ent.path
44
45    return info
46
47def get_file_history_info(commit, avatar_size):
48
49    info = {}
50
51    creator_name = commit.creator_name
52    url, is_default, date_uploaded = api_avatar_url(creator_name, avatar_size)
53
54    info['creator_avatar_url'] = url
55    info['creator_email'] = creator_name
56    info['creator_name'] = email2nickname(creator_name)
57    info['creator_contact_email'] = email2contact_email(creator_name)
58    info['ctime'] = timestamp_to_isoformat_timestr(commit.ctime)
59    info['description'] = commit.desc
60    info['commit_id'] = commit.id
61    info['size'] = commit.rev_file_size
62    info['rev_file_id'] = commit.rev_file_id
63    info['rev_renamed_old_path'] = commit.rev_renamed_old_path
64
65    return info
66
67
68class FileHistoryView(APIView):
69    authentication_classes = (TokenAuthentication, SessionAuthentication)
70    permission_classes = (IsAuthenticated,)
71    throttle_classes = (UserRateThrottle,)
72
73    def get(self, request, repo_id):
74        """ Get file history within certain commits.
75        Controlled by path(rev_renamed_old_path), commit_id and next_start_commit.
76        """
77        # argument check
78        path = request.GET.get('path', '')
79        if not path:
80            error_msg = 'path invalid.'
81            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
82
83        # resource check
84        repo = seafile_api.get_repo(repo_id)
85        if not repo:
86            error_msg = 'Library %s not found.' % repo_id
87            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
88
89        commit_id = request.GET.get('commit_id', '')
90        if not commit_id:
91            commit_id = repo.head_cmmt_id
92
93        try:
94            avatar_size = int(request.GET.get('avatar_size', 32))
95        except ValueError:
96            avatar_size = 32
97
98        # Don't use seafile_api.get_file_id_by_path()
99        # if path parameter is `rev_renamed_old_path`.
100        # seafile_api.get_file_id_by_path() will return None.
101        file_id = seafile_api.get_file_id_by_commit_and_path(repo_id,
102                commit_id, path)
103        if not file_id:
104            error_msg = 'File %s not found.' % path
105            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
106
107        # permission check
108        if not check_folder_permission(request, repo_id, '/'):
109            error_msg = 'Permission denied.'
110            return api_error(status.HTTP_403_FORBIDDEN, error_msg)
111
112        # get repo history limit
113        try:
114            keep_days = seafile_api.get_repo_history_limit(repo_id)
115        except Exception as e:
116            logger.error(e)
117            error_msg = 'Internal Server Error'
118            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
119
120        # get file history
121        limit = request.GET.get('limit', 50)
122        try:
123            limit = 50 if int(limit) < 1 else int(limit)
124        except ValueError:
125            limit = 50
126
127        try:
128            file_revisions, next_start_commit = get_file_revisions_within_limit(
129                    repo_id, path, commit_id, limit)
130        except Exception as e:
131            logger.error(e)
132            error_msg = 'Internal Server Error'
133            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
134
135        result = []
136        present_time = datetime.utcnow()
137        for commit in file_revisions:
138            history_time = datetime.utcfromtimestamp(commit.ctime)
139            if (keep_days != -1) and ((present_time - history_time).days > keep_days):
140                next_start_commit = False
141                break
142            info = get_file_history_info(commit, avatar_size)
143            info['path'] = path
144            result.append(info)
145
146        return Response({
147            "data": result,
148            "next_start_commit": next_start_commit or False
149            })
150
151
152class NewFileHistoryView(APIView):
153    authentication_classes = (TokenAuthentication, SessionAuthentication)
154    permission_classes = (IsAuthenticated,)
155    throttle_classes = (UserRateThrottle,)
156
157    def get(self, request, repo_id):
158        """ Get file history within certain commits.
159
160        Controlled by path(rev_renamed_old_path), commit_id and next_start_commit.
161        """
162        # argument check
163        path = request.GET.get('path', '')
164        if not path:
165            error_msg = 'path invalid.'
166            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
167
168        # resource check
169        repo = seafile_api.get_repo(repo_id)
170        if not repo:
171            error_msg = 'Library %s not found.' % repo_id
172            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
173
174        commit_id = repo.head_cmmt_id
175
176        try:
177            avatar_size = int(request.GET.get('avatar_size', 32))
178            page = int(request.GET.get('page', '1'))
179            per_page = int(request.GET.get('per_page', '25'))
180        except ValueError:
181            avatar_size = 32
182            page = 1
183            per_page = 25
184
185        # Don't use seafile_api.get_file_id_by_path()
186        # if path parameter is `rev_renamed_old_path`.
187        # seafile_api.get_file_id_by_path() will return None.
188        file_id = seafile_api.get_file_id_by_commit_and_path(repo_id,
189                commit_id, path)
190        if not file_id:
191            error_msg = 'File %s not found.' % path
192            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
193
194        # permission check
195        if not check_folder_permission(request, repo_id, '/'):
196            error_msg = 'Permission denied.'
197            return api_error(status.HTTP_403_FORBIDDEN, error_msg)
198
199        # get repo history limit
200        try:
201            history_limit = seafile_api.get_repo_history_limit(repo_id)
202        except Exception as e:
203            logger.error(e)
204            error_msg = 'Internal Server Error'
205            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
206
207        start = (page - 1) * per_page
208        count = per_page
209
210        try:
211            file_revisions, total_count = get_file_history(repo_id, path, start, count, history_limit)
212        except Exception as e:
213            logger.error(e)
214            error_msg = 'Internal Server Error'
215            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
216
217        data = [get_new_file_history_info(ent, avatar_size) for ent in file_revisions]
218        result = {
219            "data": data,
220            "page": page,
221            "total_count": total_count
222            }
223
224        return Response(result)
225