diff --git a/daemon.py b/daemon.py
index 3008469f4..cb1c75e2d 100644
--- a/daemon.py
+++ b/daemon.py
@@ -4050,6 +4050,7 @@ class PubServer(BaseHTTPRequestHandler):
postBytesStr = postBytes.decode('utf-8')
redirectPath = ''
checkNameAndBio = False
+ onFinalWelcomeScreen = False
if 'name="previewAvatar"' in postBytesStr:
redirectPath = '/welcome_profile'
elif 'name="initialWelcomeScreen"' in postBytesStr:
@@ -4061,6 +4062,7 @@ class PubServer(BaseHTTPRequestHandler):
redirectPath = '/' + self.server.defaultTimeline
welcomeScreenIsComplete(self.server.baseDir, nickname,
self.server.domain)
+ onFinalWelcomeScreen = True
# extract all of the text fields into a dict
fields = \
@@ -4717,15 +4719,20 @@ class PubServer(BaseHTTPRequestHandler):
nickname, domain)
# approve followers
- approveFollowers = False
- if fields.get('approveFollowers'):
- if fields['approveFollowers'] == 'on':
- approveFollowers = True
- if approveFollowers != \
- actorJson['manuallyApprovesFollowers']:
- actorJson['manuallyApprovesFollowers'] = \
- approveFollowers
+ if onFinalWelcomeScreen:
+ # Default setting created via the welcome screen
+ actorJson['manuallyApprovesFollowers'] = True
actorChanged = True
+ else:
+ approveFollowers = False
+ if fields.get('approveFollowers'):
+ if fields['approveFollowers'] == 'on':
+ approveFollowers = True
+ if approveFollowers != \
+ actorJson['manuallyApprovesFollowers']:
+ actorJson['manuallyApprovesFollowers'] = \
+ approveFollowers
+ actorChanged = True
# remove a custom font
if fields.get('removeCustomFont'):
@@ -4773,15 +4780,22 @@ class PubServer(BaseHTTPRequestHandler):
baseDir + '/accounts/' + \
nickname + '@' + domain + \
'/.followDMs'
- followDMsActive = False
- if fields.get('followDMs'):
- if fields['followDMs'] == 'on':
- followDMsActive = True
- with open(followDMsFilename, 'w+') as fFile:
- fFile.write('\n')
- if not followDMsActive:
- if os.path.isfile(followDMsFilename):
- os.remove(followDMsFilename)
+ if onFinalWelcomeScreen:
+ # initial default setting created via
+ # the welcome screen
+ with open(followDMsFilename, 'w+') as fFile:
+ fFile.write('\n')
+ actorChanged = True
+ else:
+ followDMsActive = False
+ if fields.get('followDMs'):
+ if fields['followDMs'] == 'on':
+ followDMsActive = True
+ with open(followDMsFilename, 'w+') as fFile:
+ fFile.write('\n')
+ if not followDMsActive:
+ if os.path.isfile(followDMsFilename):
+ os.remove(followDMsFilename)
# remove Twitter retweets
removeTwitterFilename = \
@@ -4822,16 +4836,22 @@ class PubServer(BaseHTTPRequestHandler):
os.remove(hideLikeButtonFile)
# notify about new Likes
- notifyLikesActive = False
- if fields.get('notifyLikes'):
- if fields['notifyLikes'] == 'on' and \
- not hideLikeButtonActive:
- notifyLikesActive = True
- with open(notifyLikesFilename, 'w+') as rFile:
- rFile.write('\n')
- if not notifyLikesActive:
- if os.path.isfile(notifyLikesFilename):
- os.remove(notifyLikesFilename)
+ if onFinalWelcomeScreen:
+ # default setting from welcome screen
+ with open(notifyLikesFilename, 'w+') as rFile:
+ rFile.write('\n')
+ actorChanged = True
+ else:
+ notifyLikesActive = False
+ if fields.get('notifyLikes'):
+ if fields['notifyLikes'] == 'on' and \
+ not hideLikeButtonActive:
+ notifyLikesActive = True
+ with open(notifyLikesFilename, 'w+') as rFile:
+ rFile.write('\n')
+ if not notifyLikesActive:
+ if os.path.isfile(notifyLikesFilename):
+ os.remove(notifyLikesFilename)
# this account is a bot
if fields.get('isBot'):
@@ -5751,6 +5771,52 @@ class PubServer(BaseHTTPRequestHandler):
return
self._404()
+ def _showHelpScreenImage(self, callingDomain: str, path: str,
+ baseDir: str,
+ GETstartTime, GETtimings: {}) -> None:
+ """Shows a help screen image
+ """
+ if not path.endswith('.jpg') and \
+ not path.endswith('.png') and \
+ not path.endswith('.webp') and \
+ not path.endswith('.avif') and \
+ not path.endswith('.gif'):
+ return
+ mediaStr = path.split('/helpimages/')[1]
+ if '/' not in mediaStr:
+ if not self.server.themeName:
+ theme = 'default'
+ else:
+ theme = self.server.themeName
+ iconFilename = mediaStr
+ else:
+ theme = mediaStr.split('/')[0]
+ iconFilename = mediaStr.split('/')[1]
+ mediaFilename = \
+ baseDir + '/theme/' + theme + '/helpimages/' + iconFilename
+ # if there is no theme-specific help image then use the default one
+ if not os.path.isfile(mediaFilename):
+ mediaFilename = \
+ baseDir + '/theme/default/helpimages/' + iconFilename
+ if self._etag_exists(mediaFilename):
+ # The file has not changed
+ self._304()
+ return
+ if os.path.isfile(mediaFilename):
+ with open(mediaFilename, 'rb') as avFile:
+ mediaBinary = avFile.read()
+ mimeType = mediaFileMimeType(mediaFilename)
+ self._set_headers_etag(mediaFilename,
+ mimeType,
+ mediaBinary, None,
+ self.server.domainFull)
+ self._write(mediaBinary)
+ self._benchmarkGETtimings(GETstartTime, GETtimings,
+ 'show files done',
+ 'help image shown')
+ return
+ self._404()
+
def _showCachedAvatar(self, callingDomain: str, path: str,
baseDir: str,
GETstartTime, GETtimings: {}) -> None:
@@ -9669,7 +9735,7 @@ class PubServer(BaseHTTPRequestHandler):
"""
imageExtensions = getImageExtensions()
for ext in imageExtensions:
- for bg in ('follow', 'options', 'login'):
+ for bg in ('follow', 'options', 'login', 'welcome'):
# follow screen background image
if path.endswith('/' + bg + '-background.' + ext):
bgFilename = \
@@ -9714,41 +9780,45 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> bool:
"""Show a shared item image
"""
- if self._pathIsImage(path):
- mediaStr = path.split('/sharefiles/')[1]
- mediaFilename = \
- baseDir + '/sharefiles/' + mediaStr
- if os.path.isfile(mediaFilename):
- if self._etag_exists(mediaFilename):
- # The file has not changed
- self._304()
- return True
+ if not self._pathIsImage(path):
+ self._404()
+ return True
- mediaFileType = 'png'
- if mediaFilename.endswith('.png'):
- mediaFileType = 'png'
- elif mediaFilename.endswith('.jpg'):
- mediaFileType = 'jpeg'
- elif mediaFilename.endswith('.webp'):
- mediaFileType = 'webp'
- elif mediaFilename.endswith('.avif'):
- mediaFileType = 'avif'
- elif mediaFilename.endswith('.svg'):
- mediaFileType = 'svg+xml'
- else:
- mediaFileType = 'gif'
- with open(mediaFilename, 'rb') as avFile:
- mediaBinary = avFile.read()
- self._set_headers_etag(mediaFilename,
- 'image/' + mediaFileType,
- mediaBinary, None,
- self.server.domainFull)
- self._write(mediaBinary)
- self._benchmarkGETtimings(GETstartTime, GETtimings,
- 'show media done',
- 'share files shown')
- return True
- self._404()
+ mediaStr = path.split('/sharefiles/')[1]
+ mediaFilename = \
+ baseDir + '/sharefiles/' + mediaStr
+ if not os.path.isfile(mediaFilename):
+ self._404()
+ return True
+
+ if self._etag_exists(mediaFilename):
+ # The file has not changed
+ self._304()
+ return True
+
+ mediaFileType = 'png'
+ if mediaFilename.endswith('.png'):
+ mediaFileType = 'png'
+ elif mediaFilename.endswith('.jpg'):
+ mediaFileType = 'jpeg'
+ elif mediaFilename.endswith('.webp'):
+ mediaFileType = 'webp'
+ elif mediaFilename.endswith('.avif'):
+ mediaFileType = 'avif'
+ elif mediaFilename.endswith('.svg'):
+ mediaFileType = 'svg+xml'
+ else:
+ mediaFileType = 'gif'
+ with open(mediaFilename, 'rb') as avFile:
+ mediaBinary = avFile.read()
+ self._set_headers_etag(mediaFilename,
+ 'image/' + mediaFileType,
+ mediaBinary, None,
+ self.server.domainFull)
+ self._write(mediaBinary)
+ self._benchmarkGETtimings(GETstartTime, GETtimings,
+ 'show media done',
+ 'share files shown')
return True
def _showAvatarOrBanner(self, callingDomain: str, path: str,
@@ -9756,59 +9826,62 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> bool:
"""Shows an avatar or banner or profile background image
"""
- if '/users/' in path:
- if self._pathIsImage(path):
- avatarStr = path.split('/users/')[1]
- if '/' in avatarStr and '.temp.' not in path:
- avatarNickname = avatarStr.split('/')[0]
- avatarFile = avatarStr.split('/')[1]
- avatarFileExt = avatarFile.split('.')[-1]
- # remove any numbers, eg. avatar123.png becomes avatar.png
- if avatarFile.startswith('avatar'):
- avatarFile = 'avatar.' + avatarFileExt
- elif avatarFile.startswith('banner'):
- avatarFile = 'banner.' + avatarFileExt
- elif avatarFile.startswith('search_banner'):
- avatarFile = 'search_banner.' + avatarFileExt
- elif avatarFile.startswith('image'):
- avatarFile = 'image.' + avatarFileExt
- elif avatarFile.startswith('left_col_image'):
- avatarFile = 'left_col_image.' + avatarFileExt
- elif avatarFile.startswith('right_col_image'):
- avatarFile = 'right_col_image.' + avatarFileExt
- avatarFilename = \
- baseDir + '/accounts/' + \
- avatarNickname + '@' + domain + '/' + avatarFile
- if os.path.isfile(avatarFilename):
- if self._etag_exists(avatarFilename):
- # The file has not changed
- self._304()
- return True
- mediaImageType = 'png'
- if avatarFile.endswith('.png'):
- mediaImageType = 'png'
- elif avatarFile.endswith('.jpg'):
- mediaImageType = 'jpeg'
- elif avatarFile.endswith('.gif'):
- mediaImageType = 'gif'
- elif avatarFile.endswith('.avif'):
- mediaImageType = 'avif'
- elif avatarFile.endswith('.svg'):
- mediaImageType = 'svg+xml'
- else:
- mediaImageType = 'webp'
- with open(avatarFilename, 'rb') as avFile:
- mediaBinary = avFile.read()
- self._set_headers_etag(avatarFilename,
- 'image/' + mediaImageType,
- mediaBinary, None,
- self.server.domainFull)
- self._write(mediaBinary)
- self._benchmarkGETtimings(GETstartTime, GETtimings,
- 'icon shown done',
- 'avatar background shown')
- return True
- return False
+ if '/users/' not in path:
+ return False
+ if not self._pathIsImage(path):
+ return False
+ avatarStr = path.split('/users/')[1]
+ if not ('/' in avatarStr and '.temp.' not in path):
+ return False
+ avatarNickname = avatarStr.split('/')[0]
+ avatarFile = avatarStr.split('/')[1]
+ avatarFileExt = avatarFile.split('.')[-1]
+ # remove any numbers, eg. avatar123.png becomes avatar.png
+ if avatarFile.startswith('avatar'):
+ avatarFile = 'avatar.' + avatarFileExt
+ elif avatarFile.startswith('banner'):
+ avatarFile = 'banner.' + avatarFileExt
+ elif avatarFile.startswith('search_banner'):
+ avatarFile = 'search_banner.' + avatarFileExt
+ elif avatarFile.startswith('image'):
+ avatarFile = 'image.' + avatarFileExt
+ elif avatarFile.startswith('left_col_image'):
+ avatarFile = 'left_col_image.' + avatarFileExt
+ elif avatarFile.startswith('right_col_image'):
+ avatarFile = 'right_col_image.' + avatarFileExt
+ avatarFilename = \
+ baseDir + '/accounts/' + \
+ avatarNickname + '@' + domain + '/' + avatarFile
+ if not os.path.isfile(avatarFilename):
+ return False
+ if self._etag_exists(avatarFilename):
+ # The file has not changed
+ self._304()
+ return True
+ mediaImageType = 'png'
+ if avatarFile.endswith('.png'):
+ mediaImageType = 'png'
+ elif avatarFile.endswith('.jpg'):
+ mediaImageType = 'jpeg'
+ elif avatarFile.endswith('.gif'):
+ mediaImageType = 'gif'
+ elif avatarFile.endswith('.avif'):
+ mediaImageType = 'avif'
+ elif avatarFile.endswith('.svg'):
+ mediaImageType = 'svg+xml'
+ else:
+ mediaImageType = 'webp'
+ with open(avatarFilename, 'rb') as avFile:
+ mediaBinary = avFile.read()
+ self._set_headers_etag(avatarFilename,
+ 'image/' + mediaImageType,
+ mediaBinary, None,
+ self.server.domainFull)
+ self._write(mediaBinary)
+ self._benchmarkGETtimings(GETstartTime, GETtimings,
+ 'icon shown done',
+ 'avatar background shown')
+ return True
def _confirmDeleteEvent(self, callingDomain: str, path: str,
baseDir: str, httpPrefix: str, cookie: str,
@@ -11042,6 +11115,14 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings)
return
+ # help screen images
+ # Note that this comes before the busy flag to avoid conflicts
+ if self.path.startswith('/helpimages/'):
+ self._showHelpScreenImage(callingDomain, self.path,
+ self.server.baseDir,
+ GETstartTime, GETtimings)
+ return
+
self._benchmarkGETtimings(GETstartTime, GETtimings,
'show files done',
'icon shown done')
diff --git a/defaultwelcome/help_dm_en.md b/defaultwelcome/help_dm_en.md
new file mode 100644
index 000000000..2f58489bf
--- /dev/null
+++ b/defaultwelcome/help_dm_en.md
@@ -0,0 +1,3 @@
+Direct messages will appear here, as a chronological timeline.
+
+To avoid spam and improve security, by default you will only be able to receive direct messages *from people that you're following*. You can turn this off within your profile settings if you need to, by selecting the top **banner** and then the **edit** icon.
diff --git a/defaultwelcome/help_inbox_en.md b/defaultwelcome/help_inbox_en.md
new file mode 100644
index 000000000..82b95bb05
--- /dev/null
+++ b/defaultwelcome/help_inbox_en.md
@@ -0,0 +1,19 @@
+Incoming posts will appear here, as a chronological timeline. If you send any posts they will also appear here.
+
+### The top banner
+At the top of the screen you can select the **banner** to switch to your profile, and edit it or log out.
+
+### Timeline buttons and icons
+The **buttons** below the top banner allow you to select different timelines. There are also **icons** on the right to **search**, view your **calendar** or create **new posts**.
+
+The **show/hide** icon allows more timeline buttons to be shown, along with moderator controls.
+
+### Left column
+Here you can add **useful links**. This only appears on desktop displays or devices with larger screens. It is similar to a *blogroll*. You can only add or edit links if you have an **administrator** or **editor** role.
+
+If you are on mobile then use the **links icon** at the top to read news.
+
+### Right column
+RSS feeds can be added in the right column, known as the *newswire*. This only appears on desktop displays or devices with larger screens. You can only add or edit feeds if you have an **administrator** or **editor** role, and incoming feed items can also be moderated.
+
+If you are on mobile then use the **newswire icon** at the top to read news.
diff --git a/defaultwelcome/help_outbox_en.md b/defaultwelcome/help_outbox_en.md
new file mode 100644
index 000000000..7651570d6
--- /dev/null
+++ b/defaultwelcome/help_outbox_en.md
@@ -0,0 +1 @@
+Your sent posts will appear here, as a cronological timeline.
diff --git a/defaultwelcome/help_tlbookmarks_en.md b/defaultwelcome/help_tlbookmarks_en.md
new file mode 100644
index 000000000..829c860dc
--- /dev/null
+++ b/defaultwelcome/help_tlbookmarks_en.md
@@ -0,0 +1 @@
+Any bookmarked posts appear here.
diff --git a/defaultwelcome/help_tlmedia_en.md b/defaultwelcome/help_tlmedia_en.md
new file mode 100644
index 000000000..b5daee390
--- /dev/null
+++ b/defaultwelcome/help_tlmedia_en.md
@@ -0,0 +1 @@
+Any incoming posts which contain **images**, **video** or **audio** files will appear here, together with their descriptions.
diff --git a/defaultwelcome/help_tlshares_en.md b/defaultwelcome/help_tlshares_en.md
new file mode 100644
index 000000000..e984d407c
--- /dev/null
+++ b/defaultwelcome/help_tlshares_en.md
@@ -0,0 +1,6 @@
+### Shared items
+These are typically physical objects or local services, exchanged or given away without use of money.
+
+For example, you may want to share **equipment** between members of a sports team on the same instance, share any surplus **clothing**, share **gadgets** which you are no longer using, or share plants and gardening **tools** between people using the same growing space.
+
+To avoid spam, shared items are not federated via ActivityPub and are local to members on the same instance.
diff --git a/defaultwelcome/welcome_ar.md b/defaultwelcome/welcome_ar.md
index 8e88deb0a..8d7170c4e 100644
--- a/defaultwelcome/welcome_ar.md
+++ b/defaultwelcome/welcome_ar.md
@@ -1,3 +1,4 @@
+
### مرحبًا بكم في INSTANCE
هذا خادم ActivityPub مصمم للاستضافة الذاتية السهلة لعدد قليل من الأشخاص على أنظمة منخفضة الطاقة ، مثل أجهزة الكمبيوتر ذات اللوحة الواحدة أو أجهزة الكمبيوتر المحمولة القديمة.
diff --git a/defaultwelcome/welcome_ca.md b/defaultwelcome/welcome_ca.md
index 0721e7e14..ada6129ed 100644
--- a/defaultwelcome/welcome_ca.md
+++ b/defaultwelcome/welcome_ca.md
@@ -1,3 +1,4 @@
+
### Benvingut a INSTANCE
Es tracta d’un servidor ActivityPub dissenyat per allotjar fàcilment algunes persones en sistemes de poca potència, com ara ordinadors de placa única o portàtils antics.
diff --git a/defaultwelcome/welcome_cy.md b/defaultwelcome/welcome_cy.md
index 7596a7927..cb4530faa 100644
--- a/defaultwelcome/welcome_cy.md
+++ b/defaultwelcome/welcome_cy.md
@@ -1,3 +1,4 @@
+
### Croeso i INSTANCE
Gweinydd ActivityPub yw hwn sydd wedi'i gynllunio ar gyfer hunan-letya ychydig o bobl ar systemau pŵer isel yn hawdd, fel cyfrifiaduron bwrdd sengl neu hen gliniaduron.
diff --git a/defaultwelcome/welcome_de.md b/defaultwelcome/welcome_de.md
index f1dedb95b..c6f8e1ac7 100644
--- a/defaultwelcome/welcome_de.md
+++ b/defaultwelcome/welcome_de.md
@@ -1,3 +1,4 @@
+
### Willkommen bei INSTANCE
Dies ist ein ActivityPub-Server, der für das einfache Selbsthosting einiger weniger Personen auf Systemen mit geringem Stromverbrauch wie Single-Board-Computern oder alten Laptops entwickelt wurde.
diff --git a/defaultwelcome/welcome_en.md b/defaultwelcome/welcome_en.md
index 5d942a9a5..1d1e8935f 100644
--- a/defaultwelcome/welcome_en.md
+++ b/defaultwelcome/welcome_en.md
@@ -1,3 +1,4 @@
+
### Welcome to INSTANCE
This is an ActivityPub server designed for easy self-hosting of a few people on low power systems, such as single board computers or old laptops.
diff --git a/defaultwelcome/welcome_es.md b/defaultwelcome/welcome_es.md
index f9ba98454..0c1474dc1 100644
--- a/defaultwelcome/welcome_es.md
+++ b/defaultwelcome/welcome_es.md
@@ -1,3 +1,4 @@
+
### Bienvenido a INSTANCE
Este es un servidor ActivityPub diseñado para el autohospedaje sencillo de algunas personas en sistemas de bajo consumo de energía, como computadoras de placa única o laptops antiguas.
diff --git a/defaultwelcome/welcome_fr.md b/defaultwelcome/welcome_fr.md
index 9fd72437e..7f6610630 100644
--- a/defaultwelcome/welcome_fr.md
+++ b/defaultwelcome/welcome_fr.md
@@ -1,3 +1,4 @@
+
### Bienvenue à INSTANCE
Il s'agit d'un serveur ActivityPub conçu pour l'auto-hébergement facile de quelques personnes sur des systèmes à faible consommation d'énergie, tels que des ordinateurs monocarte ou d'anciens ordinateurs portables.
diff --git a/defaultwelcome/welcome_ga.md b/defaultwelcome/welcome_ga.md
index c9a11c680..91dc36d64 100644
--- a/defaultwelcome/welcome_ga.md
+++ b/defaultwelcome/welcome_ga.md
@@ -1,3 +1,4 @@
+
### Fáilte go INSTANCE
Is freastalaí ActivityPub é seo atá deartha chun féin-óstáil éasca a dhéanamh ar chúpla duine ar chórais ísealchumhachta, mar ríomhairí boird aonair nó sean ríomhairí glúine.
diff --git a/defaultwelcome/welcome_hi.md b/defaultwelcome/welcome_hi.md
index 13923d095..fcffdfabd 100644
--- a/defaultwelcome/welcome_hi.md
+++ b/defaultwelcome/welcome_hi.md
@@ -1,3 +1,4 @@
+
### INSTANCE पर आपका स्वागत है
यह एक एक्टिविटीपब सर्वर है जो कम पावर सिस्टम पर सिंगल बोर्ड कंप्यूटर या पुराने लैपटॉप जैसे कुछ लोगों की आसान सेल्फ-होस्टिंग के लिए बनाया गया है।
diff --git a/defaultwelcome/welcome_it.md b/defaultwelcome/welcome_it.md
index 498caf115..b74a1e053 100644
--- a/defaultwelcome/welcome_it.md
+++ b/defaultwelcome/welcome_it.md
@@ -1,3 +1,4 @@
+
### Benvenuto in INSTANCE
Questo è un server ActivityPub progettato per un facile self-hosting di poche persone su sistemi a basso consumo, come computer a scheda singola o vecchi laptop.
diff --git a/defaultwelcome/welcome_ja.md b/defaultwelcome/welcome_ja.md
index 7e21a1821..dd26aa2ba 100644
--- a/defaultwelcome/welcome_ja.md
+++ b/defaultwelcome/welcome_ja.md
@@ -1,3 +1,4 @@
+
### INSTANCEへようこそ
これは、シングルボードコンピューターや古いラップトップなどの低電力システムで数人を簡単にセルフホスティングするために設計されたActivityPubサーバーです。
diff --git a/defaultwelcome/welcome_oc.md b/defaultwelcome/welcome_oc.md
index 2a90b3a52..520d07387 100644
--- a/defaultwelcome/welcome_oc.md
+++ b/defaultwelcome/welcome_oc.md
@@ -1,4 +1,5 @@
-# Welcome
+
+### Welcome
Epicyon is an ActivityPub server designed for easy self-hosting of a few people on low power systems, such as single board computers or old laptops.
Run your own social network presence the way you want to, and say goodbye to Big Tech.
diff --git a/defaultwelcome/welcome_pt.md b/defaultwelcome/welcome_pt.md
index f302f5aec..e0193f6a2 100644
--- a/defaultwelcome/welcome_pt.md
+++ b/defaultwelcome/welcome_pt.md
@@ -1,4 +1,5 @@
-# Bem-vindo a INSTANCE
+
+### Bem-vindo a INSTANCE
Este é um servidor ActivityPub projetado para fácil auto-hospedagem de algumas pessoas em sistemas de baixo consumo de energia, como computadores de placa única ou laptops antigos.
Administre sua própria presença na rede social do jeito que você quiser e diga adeus à Big Tech.
diff --git a/defaultwelcome/welcome_ru.md b/defaultwelcome/welcome_ru.md
index 65c82400d..857581ef4 100644
--- a/defaultwelcome/welcome_ru.md
+++ b/defaultwelcome/welcome_ru.md
@@ -1,3 +1,4 @@
+
### Добро пожаловать в INSTANCE
Это сервер ActivityPub, предназначенный для простого самостоятельного размещения нескольких человек в системах с низким энергопотреблением, таких как одноплатные компьютеры или старые ноутбуки.
diff --git a/defaultwelcome/welcome_zh.md b/defaultwelcome/welcome_zh.md
index 978734e94..1f9a62db6 100644
--- a/defaultwelcome/welcome_zh.md
+++ b/defaultwelcome/welcome_zh.md
@@ -1,3 +1,4 @@
+
### 欢迎来到INSTANCE
这是一个ActivityPub服务器,设计用于在低功耗系统(例如单板计算机或旧笔记本电脑)上轻松实现一些人的自我托管。
diff --git a/epicyon-profile.css b/epicyon-profile.css
index 384c60893..6ef938ec7 100644
--- a/epicyon-profile.css
+++ b/epicyon-profile.css
@@ -426,6 +426,10 @@ a:focus {
background-color: var(--timeline-posts-background-color);
}
+.container img.markdownImage {
+ width: 100%;
+}
+
.container img.timelineicon:hover {
filter: brightness(var(--icon-brightness-change));
}
diff --git a/epicyon-welcome.css b/epicyon-welcome.css
index 21a2ec8a1..89ccadb2c 100644
--- a/epicyon-welcome.css
+++ b/epicyon-welcome.css
@@ -149,6 +149,10 @@ img.avatar {
width: var(--welcome-avatar-width);
}
+.container img.markdownImage {
+ width: 100%;
+}
+
.container.next {
float: right;
}
@@ -192,7 +196,7 @@ span.psw {
margin: 8px 0;
border: none;
cursor: pointer;
- width: var(--welcome-button-width);
+ width: var(--welcome-button-width);
font-size: var(--welcome-font-size);
font-family: Arial, Helvetica, sans-serif;
}
@@ -232,7 +236,7 @@ span.psw {
margin: 8px 0;
border: none;
cursor: pointer;
- width: var(--welcome-button-width);
+ width: var(--welcome-button-width);
font-size: var(--welcome-font-size-mobile);
font-family: Arial, Helvetica, sans-serif;
}
diff --git a/tests.py b/tests.py
index ddab940d4..e4dace257 100644
--- a/tests.py
+++ b/tests.py
@@ -3288,6 +3288,18 @@ def testMarkdownToHtml():
markdown = 'This is just plain text'
assert markdownToHtml(markdown) == markdown
+ markdown = 'This is a quotation:\n' + \
+ '> Some quote or other'
+ assert markdownToHtml(markdown) == 'This is a quotation:
' + \
+ '
Some quote or other' + + markdown = 'This is a multi-line quotation:\n' + \ + '> The first line\n' + \ + '> The second line' + assert markdownToHtml(markdown) == \ + 'This is a multi-line quotation:
The first line The second line' + markdown = 'This is **bold**' assert markdownToHtml(markdown) == 'This is bold' @@ -3306,14 +3318,16 @@ def testMarkdownToHtml(): markdown = \ 'This is [a link](https://something.somewhere) to something.\n' + \ - 'And [something else](https://cat.pic).' + 'And [something else](https://cat.pic).\n' + \ + 'Or .' assert markdownToHtml(markdown) == \ 'This is ' + \ 'a link to something.
.'
def runAllTests():
diff --git a/theme/debian/helpimages/welcome.jpg b/theme/debian/helpimages/welcome.jpg
new file mode 100644
index 000000000..2dca20d97
Binary files /dev/null and b/theme/debian/helpimages/welcome.jpg differ
diff --git a/theme/default/helpimages/welcome.jpg b/theme/default/helpimages/welcome.jpg
new file mode 100644
index 000000000..77e97a3a6
Binary files /dev/null and b/theme/default/helpimages/welcome.jpg differ
diff --git a/theme/hacker/helpimages/welcome.jpg b/theme/hacker/helpimages/welcome.jpg
new file mode 100644
index 000000000..688320ac4
Binary files /dev/null and b/theme/hacker/helpimages/welcome.jpg differ
diff --git a/theme/henge/helpimages/welcome.jpg b/theme/henge/helpimages/welcome.jpg
new file mode 100644
index 000000000..6a417ba6b
Binary files /dev/null and b/theme/henge/helpimages/welcome.jpg differ
diff --git a/theme/light/helpimages/welcome.jpg b/theme/light/helpimages/welcome.jpg
new file mode 100644
index 000000000..327cf7adf
Binary files /dev/null and b/theme/light/helpimages/welcome.jpg differ
diff --git a/theme/night/helpimages/welcome.jpg b/theme/night/helpimages/welcome.jpg
new file mode 100644
index 000000000..e11b8089c
Binary files /dev/null and b/theme/night/helpimages/welcome.jpg differ
diff --git a/theme/pixel/helpimages/welcome.jpg b/theme/pixel/helpimages/welcome.jpg
new file mode 100644
index 000000000..0a5c37cfc
Binary files /dev/null and b/theme/pixel/helpimages/welcome.jpg differ
diff --git a/theme/purple/helpimages/welcome.jpg b/theme/purple/helpimages/welcome.jpg
new file mode 100644
index 000000000..328cfa37e
Binary files /dev/null and b/theme/purple/helpimages/welcome.jpg differ
diff --git a/theme/solidaric/helpimages/welcome.jpg b/theme/solidaric/helpimages/welcome.jpg
new file mode 100644
index 000000000..02e4eb006
Binary files /dev/null and b/theme/solidaric/helpimages/welcome.jpg differ
diff --git a/theme/starlight/helpimages/welcome.jpg b/theme/starlight/helpimages/welcome.jpg
new file mode 100644
index 000000000..970e080d3
Binary files /dev/null and b/theme/starlight/helpimages/welcome.jpg differ
diff --git a/theme/zen/helpimages/welcome.jpg b/theme/zen/helpimages/welcome.jpg
new file mode 100644
index 000000000..b12f111c4
Binary files /dev/null and b/theme/zen/helpimages/welcome.jpg differ
diff --git a/utils.py b/utils.py
index 25499145e..b0594c7ae 100644
--- a/utils.py
+++ b/utils.py
@@ -1274,7 +1274,7 @@ def _isReservedName(nickname: str) -> bool:
'accounts', 'channels', 'profile', 'u',
'updates', 'repeat', 'announce',
'shares', 'fonts', 'icons', 'avatars',
- 'welcome')
+ 'welcome', 'helpimages')
if nickname in reservedNames:
return True
return False
diff --git a/webapp_timeline.py b/webapp_timeline.py
index 1c3cf7f63..cf0e17719 100644
--- a/webapp_timeline.py
+++ b/webapp_timeline.py
@@ -8,12 +8,15 @@ __status__ = "Production"
import os
import time
+from shutil import copyfile
+from utils import dangerousMarkup
from utils import getConfigParam
from utils import getFullDomain
from utils import isEditor
from utils import removeIdEnding
from follow import followerApprovalActive
from person import isPersonSnoozed
+from webapp_utils import markdownToHtml
from webapp_utils import htmlKeyboardNavigation
from webapp_utils import htmlHideFromScreenReader
from webapp_utils import htmlPostSeparator
@@ -42,6 +45,42 @@ def _logTimelineTiming(enableTimingLog: bool, timelineStartTime,
boxName + ' ' + debugId + ' = ' + str(timeDiff))
+def _getHelpForTimeline(baseDir: str, boxName: str) -> str:
+ """Shows help text for the given timeline
+ """
+ # get the filename for help for this timeline
+ helpFilename = baseDir + '/accounts/help_' + boxName + '.md'
+ if not os.path.isfile(helpFilename):
+ language = \
+ getConfigParam(baseDir, 'language')
+ if not language:
+ language = 'en'
+ defaultFilename = \
+ baseDir + '/defaultwelcome/' + \
+ 'help_' + boxName + '_' + language + '.md'
+ if not os.path.isfile(defaultFilename):
+ defaultFilename = \
+ baseDir + '/defaultwelcome/help_' + boxName + '_en.md'
+ if os.path.isfile(defaultFilename):
+ copyfile(defaultFilename, helpFilename)
+
+ # show help text
+ if os.path.isfile(helpFilename):
+ instanceTitle = \
+ getConfigParam(baseDir, 'instanceTitle')
+ if not instanceTitle:
+ instanceTitle = 'Epicyon'
+ with open(helpFilename, 'r') as helpFile:
+ helpText = helpFile.read()
+ if dangerousMarkup(helpText, False):
+ return ''
+ helpText = helpText.replace('INSTANCE', instanceTitle)
+ return '' + lineStr + '\n' + result += lineStr + prevQuoteLine = lineStr + + if '\n' in result: + result = result.replace('\n', '') + + if result.endswith('\n') and \ + not markdown.endswith('\n'): + result = result[:len(result) - 1] + return result + + +def _markdownReplaceLinks(markdown: str, images=False) -> str: + """Replaces markdown links with html + Optionally replace image links """ - markdown = _markdownEmphasisHtml(markdown) - # replace markdown style links with html links replaceLinks = {} text = markdown - while '[' in text: + startChars = '[' + if images: + startChars = '![' + while startChars in text: if ')' not in text: break - text = text.split('[', 1)[1] - markdownLink = '[' + text.split(')')[0] + ')' + text = text.split(startChars, 1)[1] + markdownLink = startChars + text.split(')')[0] + ')' if ']' not in markdownLink or \ '(' not in markdownLink: text = text.split(')', 1)[1] continue - replaceLinks[markdownLink] = \ - '' + \ - markdownLink.split('[')[1].split(']')[0] + \ - '' + if not images: + replaceLinks[markdownLink] = \ + '' + \ + markdownLink.split(startChars)[1].split(']')[0] + \ + '' + else: + replaceLinks[markdownLink] = \ + '