1
|
|
|
# -*- coding: UTF-8 -*- |
2
|
|
|
# vim: set expandtab sw=4 ts=4 sts=4: |
3
|
|
|
# |
4
|
|
|
# phpMyAdmin web site |
5
|
|
|
# |
6
|
|
|
# Copyright (C) 2008 - 2016 Michal Cihar <[email protected]> |
7
|
|
|
# |
8
|
|
|
# This program is free software; you can redistribute it and/or modify |
9
|
|
|
# it under the terms of the GNU General Public License as published by |
10
|
|
|
# the Free Software Foundation; either version 2 of the License, or |
11
|
|
|
# (at your option) any later version. |
12
|
|
|
# |
13
|
|
|
# This program is distributed in the hope that it will be useful, |
14
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
15
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16
|
|
|
# GNU General Public License for more details. |
17
|
|
|
# |
18
|
|
|
# You should have received a copy of the GNU General Public License along |
19
|
|
|
# with this program; if not, write to the Free Software Foundation, Inc., |
20
|
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
21
|
|
|
|
22
|
|
|
import urllib2 |
23
|
|
|
from django.dispatch import receiver |
24
|
|
|
from django.db.models.signals import post_save |
25
|
|
|
from django.core.urlresolvers import reverse |
26
|
|
|
from django.db import models |
27
|
|
|
from django.conf import settings |
28
|
|
|
from django.utils import timezone |
29
|
|
|
import os.path |
30
|
|
|
from data.themes import CSSMAP |
31
|
|
|
from markupfield.fields import MarkupField |
32
|
|
|
from pmaweb.cdn import purge_cdn, purge_all_cdn |
33
|
|
|
|
34
|
|
|
# Naming of versions |
35
|
|
|
VERSION_INFO = ( |
36
|
|
|
('alpha1', ' First alpha version.'), |
37
|
|
|
('alpha2', ' Second alpha version.'), |
38
|
|
|
('alpha3', ' Third alpha version.'), |
39
|
|
|
('alpha4', ' Fourth alpha version.'), |
40
|
|
|
('beta1', ' First beta version.'), |
41
|
|
|
('beta2', ' Second beta version.'), |
42
|
|
|
('beta3', ' Third beta version.'), |
43
|
|
|
('beta4', ' Fourth beta version.'), |
44
|
|
|
('beta', ' Beta version.'), |
45
|
|
|
('rc1', ' First release candidate.'), |
46
|
|
|
('rc2', ' Second release candidate.'), |
47
|
|
|
('rc3', ' Third release candidate.'), |
48
|
|
|
('rc4', ' Fourth release candidate.'), |
49
|
|
|
('rc', ' Release candidate.'), |
50
|
|
|
) |
51
|
|
|
|
52
|
|
|
DOCKER_TRIGGER = \ |
53
|
|
|
'https://registry.hub.docker.com/u/phpmyadmin/phpmyadmin/trigger/{0}/' |
54
|
|
|
|
55
|
|
|
|
56
|
|
|
def get_current_releases(): |
57
|
|
|
delta = 1000000 |
58
|
|
|
result = [] |
59
|
|
|
|
60
|
|
|
for version in settings.LISTED_BRANCHES: |
61
|
|
|
min_vernum = Release.parse_version(version) |
62
|
|
|
max_vernum = min_vernum + delta |
63
|
|
|
stable_releases = Release.objects.filter( |
64
|
|
|
version_num__gte=min_vernum, |
65
|
|
|
version_num__lt=max_vernum, |
66
|
|
|
stable=True, |
67
|
|
|
) |
68
|
|
|
if stable_releases.exists(): |
69
|
|
|
result.append(stable_releases[0]) |
70
|
|
|
|
71
|
|
|
return result |
72
|
|
|
|
73
|
|
|
|
74
|
|
|
class Release(models.Model): |
75
|
|
|
version = models.CharField(max_length=50, unique=True) |
76
|
|
|
version_num = models.IntegerField(default=0, unique=True) |
77
|
|
|
release_notes = MarkupField(default_markup_type='markdown') |
78
|
|
|
stable = models.BooleanField(default=False, db_index=True) |
79
|
|
|
date = models.DateTimeField(db_index=True, default=timezone.now) |
80
|
|
|
|
81
|
|
|
class Meta(object): |
82
|
|
|
ordering = ['-version_num'] |
83
|
|
|
|
84
|
|
|
def __unicode__(self): |
85
|
|
|
return self.version |
86
|
|
|
|
87
|
|
|
@models.permalink |
88
|
|
|
def get_absolute_url(self): |
89
|
|
|
return ('release', (), {'version': self.version}) |
90
|
|
|
|
91
|
|
|
def simpledownload(self): |
92
|
|
|
try: |
93
|
|
|
return self.download_set.get( |
94
|
|
|
filename__endswith='-all-languages.zip' |
95
|
|
|
) |
96
|
|
|
except Download.DoesNotExist: |
97
|
|
|
try: |
98
|
|
|
return self.download_set.all()[0] |
99
|
|
|
except IndexError: |
100
|
|
|
return None |
101
|
|
|
|
102
|
|
|
@staticmethod |
103
|
|
|
def parse_version(version): |
104
|
|
|
if '-' in version: |
105
|
|
|
version, suffix = version.split('-') |
106
|
|
|
if suffix.startswith('alpha'): |
107
|
|
|
suffix_num = int(suffix[5:]) |
108
|
|
|
elif suffix.startswith('beta'): |
109
|
|
|
suffix_num = 10 + int(suffix[4:]) |
110
|
|
|
elif suffix.startswith('rc'): |
111
|
|
|
suffix_num = 50 + int(suffix[2:]) |
112
|
|
|
else: |
113
|
|
|
raise ValueError(version) |
114
|
|
|
else: |
115
|
|
|
suffix_num = 99 |
116
|
|
|
version = version |
117
|
|
|
parts = [int(x) for x in version.split('.')] |
118
|
|
|
if len(parts) == 2: |
119
|
|
|
parts.append(0) |
120
|
|
|
if len(parts) == 3: |
121
|
|
|
parts.append(0) |
122
|
|
|
assert len(parts) == 4 |
123
|
|
|
return ( |
124
|
|
|
100000000 * parts[0] + |
125
|
|
|
1000000 * parts[1] + |
126
|
|
|
10000 * parts[2] + |
127
|
|
|
100 * parts[3] + |
128
|
|
|
suffix_num |
129
|
|
|
) |
130
|
|
|
|
131
|
|
|
def save(self, *args, **kwargs): |
132
|
|
|
self.version_num = self.parse_version(self.version) |
133
|
|
|
self.stable = self.version_num % 100 == 99 |
134
|
|
|
super(Release, self).save(*args, **kwargs) |
135
|
|
|
|
136
|
|
|
def get_version_suffix(self): |
137
|
|
|
''' |
138
|
|
|
Returns suffix for a version. |
139
|
|
|
''' |
140
|
|
|
for match, result in VERSION_INFO: |
141
|
|
|
if self.version.find(match) != -1: |
142
|
|
|
return result |
143
|
|
|
return '' |
144
|
|
|
|
145
|
|
|
def get_php_versions(self): |
146
|
|
|
if self.version[:3] == '4.6': |
147
|
|
|
return '>=5.5,<7.1' |
148
|
|
|
elif self.version[:3] == '4.5': |
149
|
|
|
return '>=5.5,<7.1' |
150
|
|
|
elif self.version[:3] == '4.4': |
151
|
|
|
return '>=5.3,<7.1' |
152
|
|
|
elif self.version[:3] == '4.3': |
153
|
|
|
return '>=5.3,<7.0' |
154
|
|
|
elif self.version[:3] == '4.2': |
155
|
|
|
return '>=5.3,<7.0' |
156
|
|
|
elif self.version[:3] == '4.1': |
157
|
|
|
return '>=5.3,<7.0' |
158
|
|
|
elif self.version[:3] == '4.0': |
159
|
|
|
return '>=5.2,<5.3' |
160
|
|
|
|
161
|
|
|
def get_mysql_versions(self): |
162
|
|
|
if self.version[:3] == '4.6': |
163
|
|
|
return '>=5.5' |
164
|
|
|
elif self.version[:3] == '4.5': |
165
|
|
|
return '>=5.5' |
166
|
|
|
elif self.version[:3] == '4.4': |
167
|
|
|
return '>=5.5' |
168
|
|
|
elif self.version[:3] == '4.3': |
169
|
|
|
return '>=5.5' |
170
|
|
|
elif self.version[:3] == '4.2': |
171
|
|
|
return '>=5.5' |
172
|
|
|
elif self.version[:3] == '4.1': |
173
|
|
|
return '>=5.5' |
174
|
|
|
elif self.version[:3] == '4.0': |
175
|
|
|
return '>=5.0' |
176
|
|
|
|
177
|
|
|
def get_version_info(self): |
178
|
|
|
''' |
179
|
|
|
Returns description to the phpMyAdmin version. |
180
|
|
|
''' |
181
|
|
|
if self.version[:2] == '0.': |
182
|
|
|
text = 'Historical release.' |
183
|
|
|
elif self.version[:2] == '1.': |
184
|
|
|
text = 'Historical release.' |
185
|
|
|
elif self.version[:2] == '2.': |
186
|
|
|
text = 'Version compatible with PHP 4+ and MySQL 3+.' |
187
|
|
|
elif self.version[:2] == '3.': |
188
|
|
|
text = ( |
189
|
|
|
'Frames version not requiring Javascript. ' + |
190
|
|
|
'Requires PHP 5.2 and MySQL 5. ' + |
191
|
|
|
'Supported for security fixes only, until Jan 1, 2014.' |
192
|
|
|
) |
193
|
|
|
elif self.version[:3] == '4.6': |
194
|
|
|
text = ( |
195
|
|
|
'Current version compatible with PHP 5.5 to 7.0 and MySQL 5.5 and newer. ' |
196
|
|
|
) |
197
|
|
|
elif self.version[:3] == '4.5': |
198
|
|
|
text = ( |
199
|
|
|
'Current version compatible with PHP 5.5 to 7.0 and MySQL 5.5. ' + |
200
|
|
|
'Supported until April 1, 2016.' |
201
|
|
|
) |
202
|
|
|
elif self.version[:3] == '4.4': |
203
|
|
|
text = ( |
204
|
|
|
'Older version compatible with PHP 5.3.7 to 7.0 and MySQL 5.5. ' + |
205
|
|
|
'Supported for security fixes only, until October 1, 2016.' |
206
|
|
|
) |
207
|
|
|
elif self.version[:3] == '4.3': |
208
|
|
|
text = ( |
209
|
|
|
'Older version compatible with PHP 5.3 and MySQL 5.5. ' + |
210
|
|
|
'Supported for security fixes only, until October 1, 2015.' |
211
|
|
|
) |
212
|
|
|
elif self.version[:3] == '4.2': |
213
|
|
|
text = ( |
214
|
|
|
'Older version compatible with PHP 5.3 and MySQL 5.5. ' + |
215
|
|
|
'Supported for security fixes only, until July 1, 2015.' |
216
|
|
|
) |
217
|
|
|
elif self.version[:3] == '4.1': |
218
|
|
|
text = ( |
219
|
|
|
'Older version compatible with PHP 5.3 and MySQL 5.5. ' + |
220
|
|
|
'Supported for security fixes only, until January 1, 2015.' |
221
|
|
|
) |
222
|
|
|
elif self.version[:3] == '4.0': |
223
|
|
|
text = ( |
224
|
|
|
'Older version compatible with PHP 5.2 and MySQL 5. ' + |
225
|
|
|
'Supported for security fixes only, until April 1, 2017.' |
226
|
|
|
) |
227
|
|
|
text += self.get_version_suffix() |
228
|
|
|
|
229
|
|
|
return text |
230
|
|
|
|
231
|
|
|
def get_downloads(self): |
232
|
|
|
"""Lists downloads, making all-languages.zip first""" |
233
|
|
|
dlset = self.download_set |
234
|
|
|
return ( |
235
|
|
|
list(dlset.filter(filename__endswith='all-languages.zip')) + |
236
|
|
|
list(dlset.exclude(filename__endswith='all-languages.zip')) |
237
|
|
|
) |
238
|
|
|
|
239
|
|
|
|
240
|
|
|
class Download(models.Model): |
241
|
|
|
release = models.ForeignKey(Release) |
242
|
|
|
filename = models.CharField(max_length=50) |
243
|
|
|
size = models.IntegerField(default=0) |
244
|
|
|
sha1 = models.CharField(max_length=40) |
245
|
|
|
sha256 = models.CharField(max_length=64) |
246
|
|
|
signed = models.BooleanField(default=False) |
247
|
|
|
|
248
|
|
|
class Meta(object): |
249
|
|
|
ordering = ['-release__version_num', 'filename'] |
250
|
|
|
unique_together = ['release', 'filename'] |
251
|
|
|
|
252
|
|
|
def __unicode__(self): |
253
|
|
|
return '/phpMyAdmin/{0}/{1}'.format( |
254
|
|
|
self.release.version, |
255
|
|
|
self.filename |
256
|
|
|
) |
257
|
|
|
|
258
|
|
|
@property |
259
|
|
|
def size_k(self): |
260
|
|
|
return self.size / 1024 |
261
|
|
|
|
262
|
|
|
@property |
263
|
|
|
def size_m(self): |
264
|
|
|
return self.size / (1024 * 1024) |
265
|
|
|
|
266
|
|
|
def get_filesystem_path(self): |
267
|
|
|
return os.path.join( |
268
|
|
|
settings.FILES_PATH, |
269
|
|
|
'phpMyAdmin', |
270
|
|
|
self.release.version, |
271
|
|
|
self.filename |
272
|
|
|
) |
273
|
|
|
|
274
|
|
|
def get_absolute_url(self): |
275
|
|
|
return 'https://files.phpmyadmin.net{0}'.format( |
276
|
|
|
self.__unicode__() |
277
|
|
|
) |
278
|
|
|
|
279
|
|
|
def get_signed_url(self): |
280
|
|
|
if not self.signed: |
281
|
|
|
return '' |
282
|
|
|
return 'https://files.phpmyadmin.net{0}.asc'.format( |
283
|
|
|
self.__unicode__() |
284
|
|
|
) |
285
|
|
|
|
286
|
|
|
def get_alternate_url(self): |
287
|
|
|
return 'https://1126968067.rsc.cdn77.org{0}'.format( |
288
|
|
|
self.__unicode__() |
289
|
|
|
) |
290
|
|
|
|
291
|
|
|
@property |
292
|
|
|
def archive(self): |
293
|
|
|
return self.filename.rsplit('.', 1)[-1] |
294
|
|
|
|
295
|
|
|
@property |
296
|
|
|
def composer_type(self): |
297
|
|
|
ext = self.filename.rsplit('.', 1)[-1] |
298
|
|
|
if ext == 'zip': |
299
|
|
|
return 'zip' |
300
|
|
|
else: |
301
|
|
|
return 'tar' |
302
|
|
|
|
303
|
|
|
@property |
304
|
|
|
def is_featured(self): |
305
|
|
|
return self.filename.endswith('all-languages.zip') |
306
|
|
|
|
307
|
|
|
|
308
|
|
|
class Theme(models.Model): |
309
|
|
|
name = models.CharField(max_length=50) |
310
|
|
|
display_name = models.CharField(max_length=50) |
311
|
|
|
version = models.CharField(max_length=50) |
312
|
|
|
filename = models.CharField(max_length=100, unique=True) |
313
|
|
|
supported_versions = models.CharField(max_length=50) |
314
|
|
|
description = models.TextField() |
315
|
|
|
author = models.CharField(max_length=200) |
316
|
|
|
size = models.IntegerField(default=0) |
317
|
|
|
sha1 = models.CharField(max_length=40) |
318
|
|
|
sha256 = models.CharField(max_length=64) |
319
|
|
|
signed = models.BooleanField(default=False) |
320
|
|
|
date = models.DateTimeField(db_index=True, default=timezone.now) |
321
|
|
|
|
322
|
|
|
class Meta(object): |
323
|
|
|
ordering = ['name', 'version'] |
324
|
|
|
|
325
|
|
|
def __unicode__(self): |
326
|
|
|
return u'{0} {1}'.format(self.display_name, self.version) |
327
|
|
|
|
328
|
|
|
@property |
329
|
|
|
def imgname(self): |
330
|
|
|
return 'images/themes/{0}.png'.format(self.name) |
331
|
|
|
|
332
|
|
|
def get_absolute_url(self): |
333
|
|
|
return 'https://files.phpmyadmin.net/themes/{0}/{1}/{2}'.format( |
334
|
|
|
self.name, |
335
|
|
|
self.version, |
336
|
|
|
self.filename, |
337
|
|
|
) |
338
|
|
|
|
339
|
|
|
def get_signed_url(self): |
340
|
|
|
if not self.signed: |
341
|
|
|
return '' |
342
|
|
|
return 'https://files.phpmyadmin.net/themes/{0}/{1}/{2}.asc'.format( |
343
|
|
|
self.name, |
344
|
|
|
self.version, |
345
|
|
|
self.filename, |
346
|
|
|
) |
347
|
|
|
|
348
|
|
|
def get_filesystem_path(self): |
349
|
|
|
return os.path.join( |
350
|
|
|
settings.FILES_PATH, |
351
|
|
|
'themes', |
352
|
|
|
self.name, |
353
|
|
|
self.version, |
354
|
|
|
self.filename |
355
|
|
|
) |
356
|
|
|
|
357
|
|
|
@property |
358
|
|
|
def get_css(self): |
359
|
|
|
return CSSMAP[self.supported_versions] |
360
|
|
|
|
361
|
|
|
|
362
|
|
|
@receiver(post_save, sender=Release) |
363
|
|
|
def dockerhub_trigger(sender, instance, **kwargs): |
364
|
|
|
if settings.DOCKERHUB_TOKEN is None: |
365
|
|
|
return |
366
|
|
|
request = urllib2.Request( |
367
|
|
|
DOCKER_TRIGGER.format(settings.DOCKERHUB_TOKEN), |
368
|
|
|
'{"build": true}', |
369
|
|
|
{'Content-Type': 'application/json'} |
370
|
|
|
) |
371
|
|
|
handle = urllib2.urlopen(request) |
372
|
|
|
handle.read() |
373
|
|
|
|
374
|
|
|
|
375
|
|
|
@receiver(post_save, sender=Release) |
376
|
|
|
def purge_release(sender, instance, **kwargs): |
377
|
|
|
purge_cdn( |
378
|
|
|
# Pages with _littleboxes.html |
379
|
|
|
reverse('home'), |
380
|
|
|
reverse('news'), |
381
|
|
|
# Download lists |
382
|
|
|
reverse('files'), |
383
|
|
|
reverse('feed-files'), |
384
|
|
|
reverse('downloads'), |
385
|
|
|
# Version dumps |
386
|
|
|
'/downloads/list.txt', |
387
|
|
|
'/home_page/version.txt', |
388
|
|
|
'/home_page/version.js', |
389
|
|
|
'/home_page/version.json', |
390
|
|
|
'/downloads/phpMyAdmin-latest-all-languages.tar.bz2', |
391
|
|
|
'/downloads/phpMyAdmin-latest-all-languages.tar.gz', |
392
|
|
|
'/downloads/phpMyAdmin-latest-all-languages.tar.xz', |
393
|
|
|
'/downloads/phpMyAdmin-latest-all-languages.zip', |
394
|
|
|
'/downloads/phpMyAdmin-latest-english.tar.bz2', |
395
|
|
|
'/downloads/phpMyAdmin-latest-english.tar.gz', |
396
|
|
|
'/downloads/phpMyAdmin-latest-english.tar.xz', |
397
|
|
|
'/downloads/phpMyAdmin-latest-english.zip', |
398
|
|
|
reverse('doap'), |
399
|
|
|
reverse('pad'), |
400
|
|
|
# This release |
401
|
|
|
instance.get_absolute_url(), |
402
|
|
|
) |
403
|
|
|
# Purge all pages as every page contains download link |
404
|
|
|
purge_all_cdn() |
405
|
|
|
|
406
|
|
|
|
407
|
|
|
@receiver(post_save, sender=Download) |
408
|
|
|
def purge_download(sender, instance, **kwargs): |
409
|
|
|
purge_release(sender, instance.release) |
410
|
|
|
|
411
|
|
|
|
412
|
|
|
@receiver(post_save, sender=Theme) |
413
|
|
|
def purge_theme(sender, instance, **kwargs): |
414
|
|
|
purge_cdn(reverse('themes')) |
415
|
|
|
|