package.py 10.9 KB
Newer Older
Jonny Lamb's avatar
Jonny Lamb committed
1
2
3
4
# -*- coding: utf-8 -*-
#
#   package.py — Package controller
#
Arno Töll's avatar
Arno Töll committed
5
#   This file is part of debexpo - https://alioth.debian.org/projects/debexpo/
Jonny Lamb's avatar
Jonny Lamb committed
6
#
Jonny Lamb's avatar
Jonny Lamb committed
7
#   Copyright © 2008 Jonny Lamb <jonny@debian.org>
8
#   Copyright © 2010 Jan Dittberner <jandd@debian.org>
9
#               2011 Arno Töll <debian@toell.net>
Jonny Lamb's avatar
Jonny Lamb committed
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#
#   Permission is hereby granted, free of charge, to any person
#   obtaining a copy of this software and associated documentation
#   files (the "Software"), to deal in the Software without
#   restriction, including without limitation the rights to use,
#   copy, modify, merge, publish, distribute, sublicense, and/or sell
#   copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following
#   conditions:
#
#   The above copyright notice and this permission notice shall be
#   included in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
#   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
#   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
#   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
#   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#   OTHER DEALINGS IN THE SOFTWARE.

"""
Holds the PackageController.
"""

__author__ = 'Jonny Lamb'
37
__copyright__ = 'Copyright © 2008 Jonny Lamb, Copyright © 2010 Jan Dittberner'
Jonny Lamb's avatar
Jonny Lamb committed
38
39
__license__ = 'MIT'

40
from datetime import datetime
Jonny Lamb's avatar
Jonny Lamb committed
41
42
43
44
import logging
import os

from debexpo.lib.base import *
Jan Dittberner's avatar
Jan Dittberner committed
45
from debexpo.lib import constants, form
Jonny Lamb's avatar
Jonny Lamb committed
46
from debexpo.lib.utils import get_package_dir
47
from debexpo.lib.email import Email
48
from debexpo.lib.filesystem import CheckFiles
Jan Dittberner's avatar
Jan Dittberner committed
49
from debexpo.lib.schemas import PackageSubscribeForm, PackageCommentForm
50
from debexpo.lib.plugins import Plugins
Jonny Lamb's avatar
Jonny Lamb committed
51
52
53
54
55
56
57
58
59
60

from debexpo.model import meta
from debexpo.model.packages import Package
from debexpo.model.package_versions import PackageVersion
from debexpo.model.users import User
from debexpo.model.package_comments import PackageComment
from debexpo.model.package_info import PackageInfo
from debexpo.model.source_packages import SourcePackage
from debexpo.model.binary_packages import BinaryPackage
from debexpo.model.package_files import PackageFile
61
from debexpo.model.package_subscriptions import PackageSubscription
Jonny Lamb's avatar
Jonny Lamb committed
62
63
64
65
66

log = logging.getLogger(__name__)

class PackageController(BaseController):

67
    def _get_package(self, packagename, from_controller=True):
Jonny Lamb's avatar
Jonny Lamb committed
68
69
        """
        """
70
        log.debug('Details of package "%s" requested' % packagename)
Jonny Lamb's avatar
Jonny Lamb committed
71
72
73

        package = meta.session.query(Package).filter_by(name=packagename).first()

74
        if package is None and from_controller:
75
            log.error('Could not get package information')
76
            redirect(url(controller='packages', action='index', packagename=None))
77
78
        if package is None and not from_controller:
            return None
Jonny Lamb's avatar
Jonny Lamb committed
79

80
81
82
83
	if from_controller:
            c.package = package
            c.config = config
            c.package_dir = get_package_dir(package.name)
84
85
        return package

86
    def index(self, packagename = None):
87
88
89
90
91
92
        """
        Entry point into the controller. Displays information about the package.

        ``packagename``
            Package name to look at.
        """
93

94
        package = self._get_package(packagename)
95

96
97
        c.session = session
        c.constants = constants
98
99
100
101
102
        c.outcomes = [
            (constants.PACKAGE_COMMENT_OUTCOME_UNREVIEWED, _('Unreviewed')),
            (constants.PACKAGE_COMMENT_OUTCOME_NEEDS_WORK, _('Needs work')),
            (constants.PACKAGE_COMMENT_OUTCOME_PERFECT, _('Perfect'))
        ]
103

104
        c.plugins = Plugins
105
        c.log = log
106

107
108
109
110
111
        if 'user_id' in session:
            c.user = meta.session.query(User).filter_by(id=session['user_id']).one()
        else:
            c.user = None

112
113
114
115
116
117
118
        c.plugins = dict()
        for package_version in package.package_versions:
            pv_plugins = Plugins('qa', package_version)
            pv_plugins.load_plugins()
            pv_plugins.load_results()
            c.plugins[package_version.id] = pv_plugins

119
        log.debug('Rendering page')
Jonny Lamb's avatar
Jonny Lamb committed
120
        return render('/package/index.mako')
121

122
123
    def subscribe(self, packagename):
        """
124
        Package subscription.
125
126
127
128
129
130
131
132

        ``packagename``
            Package name to look at.
        """
        if 'user_id' not in session:
            log.debug('Requires authentication')
            session['path_before_login'] = request.path_info
            session.save()
Jan Dittberner's avatar
Jan Dittberner committed
133
            redirect(url('login'))
134
135
136

        package = self._get_package(packagename)

Jan Dittberner's avatar
Jan Dittberner committed
137
138
        query = meta.session.query(PackageSubscription).filter_by(
            package=packagename).filter_by(user_id=session['user_id'])
139
140
        subscription = query.first()

Jan Dittberner's avatar
Jan Dittberner committed
141
        validation = False
142
143
        if request.method == 'POST':
            # The form has been submitted.
Jan Dittberner's avatar
Jan Dittberner committed
144
145
146
147
148
149
150
151
152
153
154
            try:
                fields = form.validate(PackageSubscribeForm,
                                       user_id=session['user_id'])
            except Exception, e:
                log.error('Failed validation')
                validation = e
            if not validation:
                if subscription is None:
                    # There is no previous subscription.
                    if fields['level'] != -1:
                        log.debug('Creating new subscription on %s' % packagename)
155
                        subscription = PackageSubscription(
Jan Dittberner's avatar
Jan Dittberner committed
156
157
158
                            package=packagename,
                            user_id=session['user_id'],
                            level=fields['level'])
159
                        meta.session.add(subscription)
160
                else:
Jan Dittberner's avatar
Jan Dittberner committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
                    # There is a previous subscription.
                    if fields['level'] != -1:
                        log.debug('Changing previous subscription on %s' % packagename)
                        subscription.level = fields['level']
                    else:
                        log.debug('Deleting previous subscription on %s' % packagename)
                        meta.session.delete(subscription)
                meta.session.commit()
                redirect(url('package', packagename=packagename))

        c.subscriptions = [
            (-1, _('No subscription')),
            (constants.SUBSCRIPTION_LEVEL_UPLOADS,
             _('Package upload notifications only')),
            (constants.SUBSCRIPTION_LEVEL_COMMENTS,
             _('Package upload and comment notifications'))]
177
178
179
180
181
182
183

        if subscription is None:
            c.current_subscription = -1
        else:
            c.current_subscription = subscription.level

        log.debug('Rendering page')
Jan Dittberner's avatar
Jan Dittberner committed
184
185
        if validation:
            return form.htmlfill(render('/package/subscribe.mako'), validation)
186
187
        return render('/package/subscribe.mako')

188
    def delete(self, packagename, key):
189
190
191
192
193
194
        """
        Delete package.

        ``packagename``
            Package name to delete.
        """
Jan Dittberner's avatar
Jan Dittberner committed
195
196
197
198
199
        if 'user_id' not in session:
            log.debug('Requires authentication')
            session['path_before_login'] = request.path_info
            session.save()
            redirect(url('login'))
200
201
202
        else:
            user = meta.session.query(User).filter_by(id=session['user_id']).one()

203
204
205
206
207
        package = self._get_package(packagename)

        if session['user_id'] != package.user_id:
            log.error("User %d is not allowed to change properties of foreign package %s" %(session['user_id'], packagename))
            abort(403)
208
209
210
211

        if user.get_upload_key() != key:
            log.error("Possible CSRF attack, upload key does not match user's session key")
            abort(402)
Jan Dittberner's avatar
Jan Dittberner committed
212

213
214
        # The user should have already been prompted with a nice dialog box
        # confirming their choice, so no mercy here.
215
        CheckFiles().delete_files_for_package(package)
216
        meta.session.delete(package)
Jan Dittberner's avatar
Jan Dittberner committed
217
        meta.session.commit()
218

219
        redirect(url(controller='packages', action='my'))
220

Christoph Haas's avatar
Christoph Haas committed
221
    @validate(schema=PackageCommentForm(), form='index')
222
    def _comment_submit(self, packagename):
223
224
225
226
227
228
        """
        Comment submission.

        ``packagename``
            Package name to look at.
        """
229
230
        log.debug("Comment form validation successful")

Jan Dittberner's avatar
Jan Dittberner committed
231
232
233
234
235
236
        if 'user_id' not in session:
            log.debug('Requires authentication')
            session['path_before_login'] = request.path_info
            session.save()
            redirect(url('login'))

237
238
239
        package = self._get_package(packagename)

        status = constants.PACKAGE_COMMENT_STATUS_NOT_UPLOADED
Jan Dittberner's avatar
Jan Dittberner committed
240

241
        if self.form_result['status']:
242
243
244
            status = constants.PACKAGE_COMMENT_STATUS_UPLOADED

        comment = PackageComment(user_id=session['user_id'],
245
246
            package_version_id=self.form_result['package_version'],
            text=self.form_result['text'],
247
            time=datetime.now(),
248
            outcome=self.form_result['outcome'],
249
250
            status=status)

Jan Dittberner's avatar
Jan Dittberner committed
251
        meta.session.add(comment)
252
253
        meta.session.commit()

Jan Dittberner's avatar
Jan Dittberner committed
254
255
        subscribers = meta.session.query(PackageSubscription).filter_by(
            package=packagename).filter(
Jonny Lamb's avatar
Jonny Lamb committed
256
            PackageSubscription.level <= constants.SUBSCRIPTION_LEVEL_COMMENTS).all()
257

258
        if len(subscribers) > 0:
259
260
261
            user = meta.session.query(User).filter_by(id=session['user_id']).one()

            email = Email('comment_posted')
262
            email.send([s.user.email for s in subscribers], package=packagename,
263
                comment=self.form_result['text'], user=user)
264

Jan Dittberner's avatar
Jan Dittberner committed
265
        redirect(url('package', packagename=packagename))
266

267
268
269
270
271
272
273
274
    def comment(self, packagename):
        if request.method == 'POST':
            log.debug("Comment form submitted")
            return self._comment_submit(packagename)
        else:
            #abort(405)
            redirect(url('package', packagename=packagename))

275
276
277
278
279
280
281
282
283
284
285
286
287
288
    def sponsor(self, packagename, key):
        if 'user_id' not in session:
            log.debug('Requires authentication')
            session['path_before_login'] = request.path_info
            session.save()
            redirect(url('login'))
        else:
            user = meta.session.query(User).filter_by(id=session['user_id']).one()

        if user.get_upload_key() != key:
            log.error("Possible CSRF attack, upload key does not match user's session key")
            abort(402)

        package = meta.session.query(Package).filter_by(name=packagename).one()
289
290
291
292
293

        if session['user_id'] != package.user_id:
            log.error("User %d is not allowed to change properties of foreign package %s" %(session['user_id'], packagename))
            abort(403)

294
295
296
297
        if package.needs_sponsor:
                package.needs_sponsor = 0
        else:
                package.needs_sponsor = 1
298
299
300
301
        log.debug("Toggle 'needs sponsor' flag = %d" % package.needs_sponsor)
        meta.session.commit()

        redirect(url('package', packagename=packagename))