\n'
+
if editor:
# show the edit icon
htmlStr += \
@@ -163,6 +165,15 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
translate['Edit Links'] + ' | " title="' + \
translate['Edit Links'] + '" src="/icons/edit.png" />\n'
+ if artist:
+ # show the theme designer icon
+ htmlStr += \
+ '
' + \
+ '![' + \
+ translate['Theme Designer'] + ' ' + \
+ translate['Theme Designer'] + ' |](/icons/theme.png)
\n'
+
# RSS icon
if nickname != 'news':
# rss feed for this account
@@ -358,8 +369,10 @@ def htmlLinksMobile(cssCache: {}, baseDir: str,
# is the user a site editor?
if nickname == 'news':
editor = False
+ artist = False
else:
editor = isEditor(baseDir, nickname)
+ artist = isArtist(baseDir, nickname)
domain = removeDomainPort(domainFull)
@@ -383,7 +396,7 @@ def htmlLinksMobile(cssCache: {}, baseDir: str,
htmlStr += \
getLeftColumnContent(baseDir, nickname, domainFull,
httpPrefix, translate,
- editor,
+ editor, artist,
False, timelinePath,
rssIconAtTop, False, False,
theme, accessKeys,
diff --git a/webapp_frontscreen.py b/webapp_frontscreen.py
index 508e39bde..dbf243ec1 100644
--- a/webapp_frontscreen.py
+++ b/webapp_frontscreen.py
@@ -155,7 +155,8 @@ def htmlFrontScreen(signingPrivateKeyPem: str,
profileHeaderStr += \
getLeftColumnContent(baseDir, 'news', domainFull,
httpPrefix, translate,
- False, False, None, rssIconAtTop, True,
+ False, False,
+ False, None, rssIconAtTop, True,
True, theme, accessKeys,
sharedItemsFederatedDomains)
profileHeaderStr += \
diff --git a/webapp_moderation.py b/webapp_moderation.py
index 39b62e0d5..a21532111 100644
--- a/webapp_moderation.py
+++ b/webapp_moderation.py
@@ -8,6 +8,7 @@ __status__ = "Production"
__module_group__ = "Moderation"
import os
+from utils import isArtist
from utils import isAccountDir
from utils import getFullDomain
from utils import isEditor
@@ -57,6 +58,7 @@ def htmlModeration(cssCache: {}, defaultTimeline: str,
"""Show the moderation feed as html
This is what you see when selecting the "mod" timeline
"""
+ artist = isArtist(baseDir, nickname)
return htmlTimeline(cssCache, defaultTimeline,
recentPostsCache, maxRecentPosts,
translate, pageNumber,
@@ -66,7 +68,7 @@ def htmlModeration(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, False, False, positiveVoting,
+ newswire, False, False, artist, positiveVoting,
showPublishAsIcon, fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, moderationActionStr, theme,
diff --git a/webapp_profile.py b/webapp_profile.py
index e155c3c46..241188ece 100644
--- a/webapp_profile.py
+++ b/webapp_profile.py
@@ -878,6 +878,10 @@ def htmlProfile(signingPrivateKeyPem: str,
menuSkills: userPathStr + '/skills#timeline',
menuLogout: '/logout'
}
+ if isArtist(baseDir, nickname):
+ menuThemeDesigner = \
+ htmlHideFromScreenReader('🎨') + ' ' + translate['Theme Designer']
+ navLinks[menuThemeDesigner] = userPathStr + '/themedesigner'
navAccessKeys = {}
for variableName, key in accessKeys.items():
if not locals().get(variableName):
diff --git a/webapp_themeDesigner.py b/webapp_themeDesigner.py
new file mode 100644
index 000000000..36de7b4da
--- /dev/null
+++ b/webapp_themeDesigner.py
@@ -0,0 +1,262 @@
+__filename__ = "webapp_themeDesigner.py"
+__author__ = "Bob Mottram"
+__license__ = "AGPL3+"
+__version__ = "1.2.0"
+__maintainer__ = "Bob Mottram"
+__email__ = "bob@libreserver.org"
+__status__ = "Production"
+__module_group__ = "Web Interface"
+
+import os
+from utils import loadJson
+from utils import getConfigParam
+from webapp_utils import htmlHeaderWithExternalStyle
+from webapp_utils import htmlFooter
+from webapp_utils import getBannerFile
+
+
+color_to_hex = {
+ "aliceblue": "#f0f8ff",
+ "antiquewhite": "#faebd7",
+ "aqua": "#00ffff",
+ "aquamarine": "#7fffd4",
+ "azure": "#f0ffff",
+ "beige": "#f5f5dc",
+ "bisque": "#ffe4c4",
+ "black": "#000000",
+ "blanchedalmond": "#ffebcd",
+ "blue": "#0000ff",
+ "blueviolet": "#8a2be2",
+ "brown": "#a52a2a",
+ "burlywood": "#deb887",
+ "cadetblue": "#5f9ea0",
+ "chartreuse": "#7fff00",
+ "chocolate": "#d2691e",
+ "coral": "#ff7f50",
+ "cornflowerblue": "#6495ed",
+ "cornsilk": "#fff8dc",
+ "crimson": "#dc143c",
+ "cyan": "#00ffff",
+ "darkblue": "#00008b",
+ "darkcyan": "#008b8b",
+ "darkgoldenrod": "#b8860b",
+ "darkgray": "#a9a9a9",
+ "darkgrey": "#a9a9a9",
+ "darkgreen": "#006400",
+ "darkkhaki": "#bdb76b",
+ "darkmagenta": "#8b008b",
+ "darkolivegreen": "#556b2f",
+ "darkorange": "#ff8c00",
+ "darkorchid": "#9932cc",
+ "darkred": "#8b0000",
+ "darksalmon": "#e9967a",
+ "darkseagreen": "#8fbc8f",
+ "darkslateblue": "#483d8b",
+ "darkslategray": "#2f4f4f",
+ "darkslategrey": "#2f4f4f",
+ "darkturquoise": "#00ced1",
+ "darkviolet": "#9400d3",
+ "deeppink": "#ff1493",
+ "deepskyblue": "#00bfff",
+ "dimgray": "#696969",
+ "dimgrey": "#696969",
+ "dodgerblue": "#1e90ff",
+ "firebrick": "#b22222",
+ "floralwhite": "#fffaf0",
+ "forestgreen": "#228b22",
+ "fuchsia": "#ff00ff",
+ "gainsboro": "#dcdcdc",
+ "ghostwhite": "#f8f8ff",
+ "gold": "#ffd700",
+ "goldenrod": "#daa520",
+ "gray": "#808080",
+ "grey": "#808080",
+ "green": "#008000",
+ "greenyellow": "#adff2f",
+ "honeydew": "#f0fff0",
+ "hotpink": "#ff69b4",
+ "indianred": "#cd5c5c",
+ "indigo": "#4b0082",
+ "ivory": "#fffff0",
+ "khaki": "#f0e68c",
+ "lavender": "#e6e6fa",
+ "lavenderblush": "#fff0f5",
+ "lawngreen": "#7cfc00",
+ "lemonchiffon": "#fffacd",
+ "lightblue": "#add8e6",
+ "lightcoral": "#f08080",
+ "lightcyan": "#e0ffff",
+ "lightgoldenrodyellow": "#fafad2",
+ "lightgray": "#d3d3d3",
+ "lightgrey": "#d3d3d3",
+ "lightgreen": "#90ee90",
+ "lightpink": "#ffb6c1",
+ "lightsalmon": "#ffa07a",
+ "lightseagreen": "#20b2aa",
+ "lightskyblue": "#87cefa",
+ "lightslategray": "#778899",
+ "lightslategrey": "#778899",
+ "lightsteelblue": "#b0c4de",
+ "lightyellow": "#ffffe0",
+ "lime": "#00ff00",
+ "limegreen": "#32cd32",
+ "linen": "#faf0e6",
+ "magenta": "#ff00ff",
+ "maroon": "#800000",
+ "mediumaquamarine": "#66cdaa",
+ "mediumblue": "#0000cd",
+ "mediumorchid": "#ba55d3",
+ "mediumpurple": "#9370db",
+ "mediumseagreen": "#3cb371",
+ "mediumslateblue": "#7b68ee",
+ "mediumspringgreen": "#00fa9a",
+ "mediumturquoise": "#48d1cc",
+ "mediumvioletred": "#c71585",
+ "midnightblue": "#191970",
+ "mintcream": "#f5fffa",
+ "mistyrose": "#ffe4e1",
+ "moccasin": "#ffe4b5",
+ "navajowhite": "#ffdead",
+ "navy": "#000080",
+ "oldlace": "#fdf5e6",
+ "olive": "#808000",
+ "olivedrab": "#6b8e23",
+ "orange": "#ffa500",
+ "orangered": "#ff4500",
+ "orchid": "#da70d6",
+ "palegoldenrod": "#eee8aa",
+ "palegreen": "#98fb98",
+ "paleturquoise": "#afeeee",
+ "palevioletred": "#db7093",
+ "papayawhip": "#ffefd5",
+ "peachpuff": "#ffdab9",
+ "peru": "#cd853f",
+ "pink": "#ffc0cb",
+ "plum": "#dda0dd",
+ "powderblue": "#b0e0e6",
+ "purple": "#800080",
+ "red": "#ff0000",
+ "rosybrown": "#bc8f8f",
+ "royalblue": "#4169e1",
+ "saddlebrown": "#8b4513",
+ "salmon": "#fa8072",
+ "sandybrown": "#f4a460",
+ "seagreen": "#2e8b57",
+ "seashell": "#fff5ee",
+ "sienna": "#a0522d",
+ "silver": "#c0c0c0",
+ "skyblue": "#87ceeb",
+ "slateblue": "#6a5acd",
+ "slategray": "#708090",
+ "slategrey": "#708090",
+ "snow": "#fffafa",
+ "springgreen": "#00ff7f",
+ "steelblue": "#4682b4",
+ "tan": "#d2b48c",
+ "teal": "#008080",
+ "thistle": "#d8bfd8",
+ "tomato": "#ff6347",
+ "turquoise": "#40e0d0",
+ "violet": "#ee82ee",
+ "wheat": "#f5deb3",
+ "white": "#ffffff",
+ "whitesmoke": "#f5f5f5",
+ "yellow": "#ffff00",
+ "yellowgreen": "#9acd32",
+}
+
+
+def htmlThemeDesigner(cssCache: {}, baseDir: str,
+ nickname: str, domain: str,
+ translate: {}, defaultTimeline: str,
+ themeName: str, accessKeys: {}) -> str:
+ """Edit theme settings
+ """
+ themeFilename = baseDir + '/theme/' + themeName + '/theme.json'
+ themeJson = {}
+ if os.path.isfile(themeFilename):
+ themeJson = loadJson(themeFilename)
+
+ # set custom theme parameters
+ customVariablesFile = baseDir + '/accounts/theme.json'
+ if os.path.isfile(customVariablesFile):
+ customThemeParams = loadJson(customVariablesFile, 0)
+ if customThemeParams:
+ for variableName, value in customThemeParams.items():
+ themeJson[variableName] = value
+
+ themeForm = ''
+ cssFilename = baseDir + '/epicyon-profile.css'
+ if os.path.isfile(baseDir + '/epicyon.css'):
+ cssFilename = baseDir + '/epicyon.css'
+
+ instanceTitle = \
+ getConfigParam(baseDir, 'instanceTitle')
+ themeForm = \
+ htmlHeaderWithExternalStyle(cssFilename, instanceTitle, None)
+ bannerFile, bannerFilename = \
+ getBannerFile(baseDir, nickname, domain, themeName)
+ themeForm += \
+ '
' + \
+ '![]()
\n'
+ themeForm += '
\n'
+
+ themeForm += \
+ '
' + translate['Theme Designer'] + '
\n'
+
+ themeForm += '
\n'
+ themeForm += '
\n'
+ themeForm += htmlFooter()
+ return themeForm
diff --git a/webapp_timeline.py b/webapp_timeline.py
index 29ee85fa0..b8bb13c87 100644
--- a/webapp_timeline.py
+++ b/webapp_timeline.py
@@ -10,6 +10,7 @@ __module_group__ = "Timeline"
import os
import time
from shutil import copyfile
+from utils import isArtist
from utils import dangerousMarkup
from utils import getConfigParam
from utils import getFullDomain
@@ -430,7 +431,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
twitterReplacementDomain: str,
showPublishedDateOnly: bool,
newswire: {}, moderator: bool,
- editor: bool,
+ editor: bool, artist: bool,
positiveVoting: bool,
showPublishAsIcon: bool,
fullWidthTimelineButtonHeader: bool,
@@ -744,7 +745,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
leftColumnStr = \
getLeftColumnContent(baseDir, nickname, domainFull,
httpPrefix, translate,
- editor, False, None, rssIconAtTop,
+ editor, artist, False, None, rssIconAtTop,
True, False, theme, accessKeys,
sharedItemsFederatedDomains)
tlStr += '
str:
"""Show the DM timeline as html
"""
+ artist = isArtist(baseDir, nickname)
return htmlTimeline(cssCache, defaultTimeline,
recentPostsCache, maxRecentPosts,
translate, pageNumber,
@@ -1373,7 +1379,7 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, False, False, positiveVoting,
+ newswire, False, False, artist, positiveVoting,
showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
@@ -1413,6 +1419,7 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
CWlists: {}, listsEnabled: str) -> str:
"""Show the replies timeline as html
"""
+ artist = isArtist(baseDir, nickname)
return htmlTimeline(cssCache, defaultTimeline,
recentPostsCache, maxRecentPosts,
translate, pageNumber,
@@ -1424,7 +1431,7 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, False, False,
+ newswire, False, False, artist,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
@@ -1464,6 +1471,7 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
CWlists: {}, listsEnabled: str) -> str:
"""Show the media timeline as html
"""
+ artist = isArtist(baseDir, nickname)
return htmlTimeline(cssCache, defaultTimeline,
recentPostsCache, maxRecentPosts,
translate, pageNumber,
@@ -1475,7 +1483,7 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, False, False,
+ newswire, False, False, artist,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
@@ -1515,6 +1523,7 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
CWlists: {}, listsEnabled: str) -> str:
"""Show the blogs timeline as html
"""
+ artist = isArtist(baseDir, nickname)
return htmlTimeline(cssCache, defaultTimeline,
recentPostsCache, maxRecentPosts,
translate, pageNumber,
@@ -1526,7 +1535,7 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, False, False,
+ newswire, False, False, artist,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
@@ -1578,7 +1587,7 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, False, False,
+ newswire, False, False, False,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
@@ -1601,7 +1610,7 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str,
YTReplacementDomain: str,
twitterReplacementDomain: str,
showPublishedDateOnly: bool,
- newswire: {}, moderator: bool, editor: bool,
+ newswire: {}, moderator: bool, editor: bool, artist: bool,
positiveVoting: bool, showPublishAsIcon: bool,
fullWidthTimelineButtonHeader: bool,
iconsAsButtons: bool,
@@ -1629,7 +1638,7 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, moderator, editor,
+ newswire, moderator, editor, artist,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
@@ -1671,6 +1680,7 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str,
"""
manuallyApproveFollowers = \
followerApprovalActive(baseDir, nickname, domain)
+ artist = isArtist(baseDir, nickname)
return htmlTimeline(cssCache, defaultTimeline,
recentPostsCache, maxRecentPosts,
translate, pageNumber,
@@ -1682,7 +1692,7 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str,
YTReplacementDomain,
twitterReplacementDomain,
showPublishedDateOnly,
- newswire, False, False, positiveVoting,
+ newswire, False, False, artist, positiveVoting,
showPublishAsIcon, fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
|