mirror of https://gitlab.com/bashrc2/epicyon
Importing themes
parent
a8351756d2
commit
e7ae1c0561
27
content.py
27
content.py
|
@ -948,7 +948,8 @@ def saveMediaInFormPOST(mediaBytes, debug: bool,
|
||||||
'mp4': 'video/mp4',
|
'mp4': 'video/mp4',
|
||||||
'ogv': 'video/ogv',
|
'ogv': 'video/ogv',
|
||||||
'mp3': 'audio/mpeg',
|
'mp3': 'audio/mpeg',
|
||||||
'ogg': 'audio/ogg'
|
'ogg': 'audio/ogg',
|
||||||
|
'zip': 'application/zip'
|
||||||
}
|
}
|
||||||
detectedExtension = None
|
detectedExtension = None
|
||||||
for extension, contentType in extensionList.items():
|
for extension, contentType in extensionList.items():
|
||||||
|
@ -960,7 +961,8 @@ def saveMediaInFormPOST(mediaBytes, debug: bool,
|
||||||
extension = 'jpg'
|
extension = 'jpg'
|
||||||
elif extension == 'mpeg':
|
elif extension == 'mpeg':
|
||||||
extension = 'mp3'
|
extension = 'mp3'
|
||||||
filename = filenameBase + '.' + extension
|
if filenameBase:
|
||||||
|
filename = filenameBase + '.' + extension
|
||||||
attachmentMediaType = \
|
attachmentMediaType = \
|
||||||
searchStr.decode().split('/')[0].replace('Content-Type: ', '')
|
searchStr.decode().split('/')[0].replace('Content-Type: ', '')
|
||||||
detectedExtension = extension
|
detectedExtension = extension
|
||||||
|
@ -979,16 +981,17 @@ def saveMediaInFormPOST(mediaBytes, debug: bool,
|
||||||
break
|
break
|
||||||
|
|
||||||
# remove any existing image files with a different format
|
# remove any existing image files with a different format
|
||||||
extensionTypes = getImageExtensions()
|
if detectedExtension != 'zip':
|
||||||
for ex in extensionTypes:
|
extensionTypes = getImageExtensions()
|
||||||
if ex == detectedExtension:
|
for ex in extensionTypes:
|
||||||
continue
|
if ex == detectedExtension:
|
||||||
possibleOtherFormat = \
|
continue
|
||||||
filename.replace('.temp', '').replace('.' +
|
possibleOtherFormat = \
|
||||||
detectedExtension, '.' +
|
filename.replace('.temp', '').replace('.' +
|
||||||
ex)
|
detectedExtension, '.' +
|
||||||
if os.path.isfile(possibleOtherFormat):
|
ex)
|
||||||
os.remove(possibleOtherFormat)
|
if os.path.isfile(possibleOtherFormat):
|
||||||
|
os.remove(possibleOtherFormat)
|
||||||
|
|
||||||
fd = open(filename, 'wb')
|
fd = open(filename, 'wb')
|
||||||
if not fd:
|
if not fd:
|
||||||
|
|
33
daemon.py
33
daemon.py
|
@ -256,6 +256,7 @@ from cache import checkForChangedActor
|
||||||
from cache import storePersonInCache
|
from cache import storePersonInCache
|
||||||
from cache import getPersonFromCache
|
from cache import getPersonFromCache
|
||||||
from httpsig import verifyPostHeaders
|
from httpsig import verifyPostHeaders
|
||||||
|
from theme import importTheme
|
||||||
from theme import exportTheme
|
from theme import exportTheme
|
||||||
from theme import isNewsThemeName
|
from theme import isNewsThemeName
|
||||||
from theme import getTextModeBanner
|
from theme import getTextModeBanner
|
||||||
|
@ -4042,7 +4043,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
profileMediaTypes = ('avatar', 'image',
|
profileMediaTypes = ('avatar', 'image',
|
||||||
'banner', 'search_banner',
|
'banner', 'search_banner',
|
||||||
'instanceLogo',
|
'instanceLogo',
|
||||||
'left_col_image', 'right_col_image')
|
'left_col_image', 'right_col_image',
|
||||||
|
'submitImportTheme')
|
||||||
profileMediaTypesUploaded = {}
|
profileMediaTypesUploaded = {}
|
||||||
for mType in profileMediaTypes:
|
for mType in profileMediaTypes:
|
||||||
# some images can only be changed by the admin
|
# some images can only be changed by the admin
|
||||||
|
@ -4054,18 +4056,18 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: profile update extracting ' + mType +
|
print('DEBUG: profile update extracting ' + mType +
|
||||||
' image or font from POST')
|
' image, zip or font from POST')
|
||||||
mediaBytes, postBytes = \
|
mediaBytes, postBytes = \
|
||||||
extractMediaInFormPOST(postBytes, boundary, mType)
|
extractMediaInFormPOST(postBytes, boundary, mType)
|
||||||
if mediaBytes:
|
if mediaBytes:
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: profile update ' + mType +
|
print('DEBUG: profile update ' + mType +
|
||||||
' image or font was found. ' +
|
' image, zip or font was found. ' +
|
||||||
str(len(mediaBytes)) + ' bytes')
|
str(len(mediaBytes)) + ' bytes')
|
||||||
else:
|
else:
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: profile update, no ' + mType +
|
print('DEBUG: profile update, no ' + mType +
|
||||||
' image or font was found in POST')
|
' image, zip or font was found in POST')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Note: a .temp extension is used here so that at no
|
# Note: a .temp extension is used here so that at no
|
||||||
|
@ -4074,6 +4076,13 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if mType == 'instanceLogo':
|
if mType == 'instanceLogo':
|
||||||
filenameBase = \
|
filenameBase = \
|
||||||
baseDir + '/accounts/login.temp'
|
baseDir + '/accounts/login.temp'
|
||||||
|
elif mType == 'submitImportTheme':
|
||||||
|
if not os.path.isdir(baseDir + '/imports'):
|
||||||
|
os.mkdir(baseDir + '/imports')
|
||||||
|
filenameBase = \
|
||||||
|
baseDir + '/imports/newtheme.zip'
|
||||||
|
if os.path.isfile(filenameBase):
|
||||||
|
os.remove(filenameBase)
|
||||||
else:
|
else:
|
||||||
filenameBase = \
|
filenameBase = \
|
||||||
baseDir + '/accounts/' + \
|
baseDir + '/accounts/' + \
|
||||||
|
@ -4085,10 +4094,19 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
filenameBase)
|
filenameBase)
|
||||||
if filename:
|
if filename:
|
||||||
print('Profile update POST ' + mType +
|
print('Profile update POST ' + mType +
|
||||||
' media or font filename is ' + filename)
|
' media, zip or font filename is ' + filename)
|
||||||
else:
|
else:
|
||||||
print('Profile update, no ' + mType +
|
print('Profile update, no ' + mType +
|
||||||
' media or font filename in POST')
|
' media, zip or font filename in POST')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if mType == 'submitImportTheme':
|
||||||
|
if nickname == adminNickname or \
|
||||||
|
isArtist(baseDir, nickname):
|
||||||
|
if importTheme(baseDir, filename):
|
||||||
|
print(nickname + ' uploaded a theme')
|
||||||
|
else:
|
||||||
|
print('Only admin or artist can import a theme')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
postImageFilename = filename.replace('.temp', '')
|
postImageFilename = filename.replace('.temp', '')
|
||||||
|
@ -4108,7 +4126,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
filename, postImageFilename, city)
|
filename, postImageFilename, city)
|
||||||
if os.path.isfile(postImageFilename):
|
if os.path.isfile(postImageFilename):
|
||||||
print('profile update POST ' + mType +
|
print('profile update POST ' + mType +
|
||||||
' image or font saved to ' + postImageFilename)
|
' image, zip or font saved to ' +
|
||||||
|
postImageFilename)
|
||||||
if mType != 'instanceLogo':
|
if mType != 'instanceLogo':
|
||||||
lastPartOfImageFilename = \
|
lastPartOfImageFilename = \
|
||||||
postImageFilename.split('/')[-1]
|
postImageFilename.split('/')[-1]
|
||||||
|
|
45
theme.py
45
theme.py
|
@ -10,11 +10,56 @@ import os
|
||||||
from utils import loadJson
|
from utils import loadJson
|
||||||
from utils import saveJson
|
from utils import saveJson
|
||||||
from utils import getImageExtensions
|
from utils import getImageExtensions
|
||||||
|
from utils import copytree
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from shutil import make_archive
|
from shutil import make_archive
|
||||||
|
from shutil import unpack_archive
|
||||||
from content import dangerousCSS
|
from content import dangerousCSS
|
||||||
|
|
||||||
|
|
||||||
|
def importTheme(baseDir: str, filename: str) -> bool:
|
||||||
|
"""Imports a theme
|
||||||
|
"""
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
return False
|
||||||
|
tempThemeDir = baseDir + '/imports/files'
|
||||||
|
if not os.path.isdir(tempThemeDir):
|
||||||
|
os.mkdir(tempThemeDir)
|
||||||
|
unpack_archive(filename, tempThemeDir, 'zip')
|
||||||
|
essentialThemeFiles = ('name.txt', 'theme.json')
|
||||||
|
for themeFile in essentialThemeFiles:
|
||||||
|
if not os.path.isfile(tempThemeDir + '/' + themeFile):
|
||||||
|
print('WARN: ' + themeFile +
|
||||||
|
' missing from imported theme')
|
||||||
|
return False
|
||||||
|
newThemeName = None
|
||||||
|
with open(tempThemeDir + '/name.txt', 'r') as fp:
|
||||||
|
newThemeName = fp.read().replace('\n', '').replace('\r', '')
|
||||||
|
if len(newThemeName) > 20:
|
||||||
|
print('WARN: Imported theme name is too long')
|
||||||
|
return False
|
||||||
|
if len(newThemeName) < 2:
|
||||||
|
print('WARN: Imported theme name is too short')
|
||||||
|
return False
|
||||||
|
newThemeName = newThemeName.lower()
|
||||||
|
forbiddenChars = (
|
||||||
|
' ', ';', '/', '\\', '?', '!', '#', '@',
|
||||||
|
':', '%', '&', '"', '+', '<', '>', '$'
|
||||||
|
)
|
||||||
|
for ch in forbiddenChars:
|
||||||
|
if ch in newThemeName:
|
||||||
|
print('WARN: theme name contains forbidden character')
|
||||||
|
return False
|
||||||
|
if not newThemeName:
|
||||||
|
return False
|
||||||
|
themeDir = baseDir + '/theme/' + newThemeName
|
||||||
|
if not os.path.isdir(themeDir):
|
||||||
|
os.mkdir(themeDir)
|
||||||
|
copytree(tempThemeDir, themeDir)
|
||||||
|
os.remove(tempThemeDir)
|
||||||
|
return os.path.isfile(themeDir + '/theme.json')
|
||||||
|
|
||||||
|
|
||||||
def exportTheme(baseDir: str, theme: str) -> bool:
|
def exportTheme(baseDir: str, theme: str) -> bool:
|
||||||
"""Exports a theme as a zip file
|
"""Exports a theme as a zip file
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
blue
|
|
@ -0,0 +1 @@
|
||||||
|
debian
|
|
@ -0,0 +1 @@
|
||||||
|
default
|
|
@ -0,0 +1 @@
|
||||||
|
hacker
|
|
@ -0,0 +1 @@
|
||||||
|
henge
|
|
@ -0,0 +1 @@
|
||||||
|
indymediaclassic
|
|
@ -0,0 +1 @@
|
||||||
|
indymediamodern
|
|
@ -0,0 +1 @@
|
||||||
|
lcd
|
|
@ -0,0 +1 @@
|
||||||
|
light
|
|
@ -0,0 +1 @@
|
||||||
|
night
|
|
@ -0,0 +1 @@
|
||||||
|
pixel
|
|
@ -0,0 +1 @@
|
||||||
|
purple
|
|
@ -0,0 +1 @@
|
||||||
|
rc3
|
|
@ -0,0 +1 @@
|
||||||
|
solidaric
|
|
@ -0,0 +1 @@
|
||||||
|
starlight
|
|
@ -0,0 +1 @@
|
||||||
|
zen
|
|
@ -1317,9 +1317,12 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
|
||||||
themesDropdown += ' <select id="themeDropdown" ' + \
|
themesDropdown += ' <select id="themeDropdown" ' + \
|
||||||
'name="themeDropdown" class="theme">'
|
'name="themeDropdown" class="theme">'
|
||||||
for themeName in themes:
|
for themeName in themes:
|
||||||
|
translatedThemeName = themeName
|
||||||
|
if translate.get(themeName):
|
||||||
|
translatedThemeName = translate[themeName]
|
||||||
themesDropdown += ' <option value="' + \
|
themesDropdown += ' <option value="' + \
|
||||||
themeName.lower() + '">' + \
|
themeName.lower() + '">' + \
|
||||||
translate[themeName] + '</option>'
|
translatedThemeName + '</option>'
|
||||||
themesDropdown += ' </select><br>'
|
themesDropdown += ' </select><br>'
|
||||||
if os.path.isfile(baseDir + '/fonts/custom.woff') or \
|
if os.path.isfile(baseDir + '/fonts/custom.woff') or \
|
||||||
os.path.isfile(baseDir + '/fonts/custom.woff2') or \
|
os.path.isfile(baseDir + '/fonts/custom.woff2') or \
|
||||||
|
@ -1340,7 +1343,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
|
||||||
' <label class="labels">' + \
|
' <label class="labels">' + \
|
||||||
translate['Import Theme'] + '</label>\n'
|
translate['Import Theme'] + '</label>\n'
|
||||||
graphicsStr += ' <input type="file" id="importTheme" '
|
graphicsStr += ' <input type="file" id="importTheme" '
|
||||||
graphicsStr += 'name="importTheme" '
|
graphicsStr += 'name="submitImportTheme" '
|
||||||
graphicsStr += 'accept="' + themeFormats + '">\n'
|
graphicsStr += 'accept="' + themeFormats + '">\n'
|
||||||
graphicsStr += \
|
graphicsStr += \
|
||||||
' <label class="labels">' + \
|
' <label class="labels">' + \
|
||||||
|
|
Loading…
Reference in New Issue