diff --git a/daemon.py b/daemon.py index 820eaca92..1f2fd5104 100644 --- a/daemon.py +++ b/daemon.py @@ -80,6 +80,10 @@ from person import remove_account from person import can_remove_post from person import person_snooze from person import person_unsnooze +from posts import get_post_expiry_keep_dms +from posts import set_post_expiry_keep_dms +from posts import get_post_expiry_days +from posts import set_post_expiry_days from posts import get_original_post_from_announce_url from posts import save_post_to_box from posts import get_instance_actor_key @@ -6135,6 +6139,22 @@ class PubServer(BaseHTTPRequestHandler): del self.server.account_timezone[nickname] actor_changed = True + # set post expiry period in days + post_expiry_period_days = \ + get_post_expiry_days(base_dir, nickname, domain) + if fields.get('postExpiryPeriod'): + if fields['postExpiryPeriod'] != \ + str(post_expiry_period_days): + post_expiry_period_days = \ + fields['postExpiryPeriod'] + set_post_expiry_days(base_dir, nickname, domain, + post_expiry_period_days) + actor_changed = True + else: + if post_expiry_period_days > 0: + set_post_expiry_days(base_dir, nickname, domain, 0) + actor_changed = True + # change tox address current_tox_address = get_tox_address(actor_json) if fields.get('toxAddress'): @@ -6712,6 +6732,18 @@ class PubServer(BaseHTTPRequestHandler): approve_followers actor_changed = True + # keep DMs during post expiry + expire_keep_dms = False + if fields.get('expiryKeepDMs'): + if fields['expiryKeepDMs'] == 'on': + expire_keep_dms = True + curr_keep_dms = \ + get_post_expiry_keep_dms(base_dir, nickname, domain) + if curr_keep_dms != expire_keep_dms: + set_post_expiry_keep_dms(base_dir, nickname, domain, + expire_keep_dms) + actor_changed = True + # remove a custom font if fields.get('removeCustomFont'): if (fields['removeCustomFont'] == 'on' and diff --git a/deploy/i2p b/deploy/i2p index 02d728eba..74b5e2df3 100755 --- a/deploy/i2p +++ b/deploy/i2p @@ -7,10 +7,10 @@ if [[ "$1" == 'remove' ]]; then echo 'Removing Epicyon i2p instance' systemctl stop i2pd if [ -f /var/lib/i2pd/tunnels.d/epicyon ]; then - rm /var/lib/i2pd/tunnels.d/epicyon + rm /var/lib/i2pd/tunnels.d/epicyon fi if [ -f /etc/i2pd/tunnels.conf.d/epicyon ]; then - rm /etc/i2pd/tunnels.conf.d/epicyon + rm /etc/i2pd/tunnels.conf.d/epicyon fi rm /var/lib/i2pd/epicyon.dat systemctl restart i2pd @@ -27,14 +27,14 @@ fi if [[ "$1" == 'removei2p' ]]; then if [ -f /usr/bin/pacman ]; then - pacman -R --noconfirm i2pd + pacman -R --noconfirm i2pd else - apt-get -y remove --purge i2pd + apt-get -y remove --purge i2pd fi rm -rf /etc/i2pd rm -rf /var/lib/i2pd if [ -f /var/log/i2pd/i2pd.log ]; then - rm /var/log/i2pd/i2pd.log + rm /var/log/i2pd/i2pd.log fi fi @@ -56,20 +56,20 @@ if [ -f /usr/bin/pacman ]; then pacman -Syy pacman -S --noconfirm python-pip python-pysocks python-cryptography \ imagemagick python-pillow python-requests \ - perl-image-exiftool python-numpy python-dateutil \ - certbot flake8 git i2pd wget qrencode \ - proxychains midori bandit + perl-image-exiftool python-numpy python-dateutil \ + certbot flake8 git i2pd wget qrencode \ + proxychains midori bandit pip3 install pyLD pyqrcode pypng else apt-get update apt-get -y install imagemagick python3-cryptography \ - python3-dateutil python3-idna python3-requests \ - python3-numpy python3-pil.imagetk python3-pip \ - python3-setuptools python3-socks python3-idna \ - libimage-exiftool-perl python3-flake8 python3-pyld \ - python3-django-timezone-field nginx git i2pd wget \ - python3-pyqrcode qrencode python3-png \ - proxychains midori python3-bandit + python3-dateutil python3-idna python3-requests \ + python3-numpy python3-pil.imagetk python3-pip \ + python3-setuptools python3-socks python3-idna \ + libimage-exiftool-perl python3-flake8 python3-pyld \ + python3-django-timezone-field nginx git i2pd wget \ + python3-pyqrcode qrencode python3-png \ + proxychains midori python3-bandit fi if [ ! -d /etc/i2pd ]; then @@ -82,8 +82,8 @@ if [ ! -d ${install_destination} ]; then git clone https://gitlab.com/bashrc2/epicyon ${install_destination} if [ ! -d ${install_destination} ]; then - echo 'Epicyon repo failed to clone' - exit 3 + echo 'Epicyon repo failed to clone' + exit 3 fi fi @@ -142,8 +142,8 @@ sed -i "s|tunnelsdir =.*|tunnelsdir = $tunnels_dir|g" /etc/i2pd/i2pd.conf echo 'Enabling ipv6' if [ -f /etc/sysctl.conf ]; then if grep -q 'net.ipv6.conf.all.disable_ipv6' /etc/sysctl.conf; then - sed -i 's|net.ipv6.conf.all.disable_ipv6.*|net.ipv6.conf.all.disable_ipv6 = 0|g' /etc/sysctl.conf - /sbin/sysctl -p -q + sed -i 's|net.ipv6.conf.all.disable_ipv6.*|net.ipv6.conf.all.disable_ipv6 = 0|g' /etc/sysctl.conf + /sbin/sysctl -p -q fi fi sed -i 's|#ipv6 =|ipv6 =|g' /etc/i2pd/i2pd.conf @@ -161,7 +161,7 @@ sed -i 's|# nat =|nat =|g' /etc/i2pd/i2pd.conf sed -i 's|nat =.*|nat = true|g' /etc/i2pd/i2pd.conf if [ ! -d /run/i2pd ]; then - mkdir /run/i2pd + mkdir /run/i2pd fi chown -R i2pd:i2pd /run/i2pd @@ -300,7 +300,7 @@ if [ ! -f /etc/nginx/nginx.conf ]; then echo '}'; } > /etc/nginx/nginx.conf else if ! grep -q 'include /etc/nginx/sites-enabled' /etc/nginx/nginx.conf; then - echo 'include /etc/nginx/sites-enabled/*.conf;' >> /etc/nginx/nginx.conf + echo 'include /etc/nginx/sites-enabled/*.conf;' >> /etc/nginx/nginx.conf fi fi if [ ! -d /etc/nginx/conf.d ]; then @@ -315,25 +315,25 @@ fi if [ -f /usr/bin/pacman ]; then if [ ! -f /lib/systemd/system/nginx.service ]; then - echo 'Creating nginx daemon' - { echo '[Unit]'; - echo 'Description=A high performance web server and a reverse proxy server'; - echo 'Documentation=man:nginx(8)'; - echo 'After=network.target nss-lookup.target'; - echo '' - echo '[Service]'; - echo 'Type=forking'; - echo 'PIDFile=/run/nginx.pid'; - echo "ExecStartPre=$(which nginx) -t -q -g 'daemon on; master_process on;'"; - echo "ExecStart=$(which nginx) -g 'daemon on; master_process on;'"; - echo "ExecReload=$(which nginx) -g 'daemon on; master_process on;' -s reload"; - echo 'ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid'; - echo 'TimeoutStopSec=5'; - echo 'KillMode=mixed'; - echo ''; - echo '[Install]'; - echo 'WantedBy=multi-user.target'; } > /etc/systemd/system/nginx.service - systemctl enable nginx + echo 'Creating nginx daemon' + { echo '[Unit]'; + echo 'Description=A high performance web server and a reverse proxy server'; + echo 'Documentation=man:nginx(8)'; + echo 'After=network.target nss-lookup.target'; + echo '' + echo '[Service]'; + echo 'Type=forking'; + echo 'PIDFile=/run/nginx.pid'; + echo "ExecStartPre=$(which nginx) -t -q -g 'daemon on; master_process on;'"; + echo "ExecStart=$(which nginx) -g 'daemon on; master_process on;'"; + echo "ExecReload=$(which nginx) -g 'daemon on; master_process on;' -s reload"; + echo 'ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid'; + echo 'TimeoutStopSec=5'; + echo 'KillMode=mixed'; + echo ''; + echo '[Install]'; + echo 'WantedBy=multi-user.target'; } > /etc/systemd/system/nginx.service + systemctl enable nginx fi fi @@ -402,25 +402,25 @@ systemctl restart nginx echo 'localnet 127.0.0.0/255.0.0.0'; echo '[ProxyList]'; echo 'http 127.0.0.1 4444'; - echo 'socks5 127.0.0.1 4447'; } > /etc/proxychains.conf + echo 'socks5 127.0.0.1 4447'; } > /etc/proxychains.conf # set up a desktop icon for d in /home/*/ ; do CURRUSER=$(echo "$d" | awk -F '/' '{print $3}') if [ -d "/home/${CURRUSER}/Desktop" ]; then - { echo '#!/usr/bin/env xdg-open'; - echo '[Desktop Entry]'; - echo 'Name=Epicyon I2P'; - echo 'GenericName=P2P Social Network'; - echo 'Comment=P2P Social Network'; - echo "Exec=proxychains midori http://${I2P_DOMAIN}"; - echo 'Icon=org.midori_browser.Midori'; - echo 'Type=Application'; - echo 'Terminal=false'; - echo 'Categories=Internet;SocialNetwork;'; - echo 'StartupWMClass=Epicyon'; - echo 'Keywords=Epicyon;P2P;I2P;'; } > "/home/${CURRUSER}/Desktop/${username}.desktop" - chown "$CURRUSER":"$CURRUSER" "/home/${CURRUSER}/Desktop/${username}.desktop" + { echo '#!/usr/bin/env xdg-open'; + echo '[Desktop Entry]'; + echo 'Name=Epicyon I2P'; + echo 'GenericName=P2P Social Network'; + echo 'Comment=P2P Social Network'; + echo "Exec=proxychains midori http://${I2P_DOMAIN}"; + echo 'Icon=org.midori_browser.Midori'; + echo 'Type=Application'; + echo 'Terminal=false'; + echo 'Categories=Internet;SocialNetwork;'; + echo 'StartupWMClass=Epicyon'; + echo 'Keywords=Epicyon;P2P;I2P;'; } > "/home/${CURRUSER}/Desktop/${username}.desktop" + chown "$CURRUSER":"$CURRUSER" "/home/${CURRUSER}/Desktop/${username}.desktop" fi done diff --git a/epicyon.py b/epicyon.py index e530ded7e..c1752ad89 100644 --- a/epicyon.py +++ b/epicyon.py @@ -26,6 +26,7 @@ from roles import set_role from webfinger import webfinger_handle from bookmarks import send_bookmark_via_server from bookmarks import send_undo_bookmark_via_server +from posts import set_post_expiry_days from posts import get_instance_actor_key from posts import send_mute_via_server from posts import send_undo_mute_via_server @@ -37,6 +38,7 @@ from posts import send_block_via_server from posts import send_undo_block_via_server from posts import create_public_post from posts import delete_all_posts +from posts import expire_posts from posts import archive_posts from posts import send_post_via_server from posts import get_public_posts_of_person @@ -214,6 +216,10 @@ def _command_options() -> None: parser.add_argument('-p', '--port', dest='port', type=int, default=None, help='Port number to run on') + parser.add_argument('--expiryDays', dest='expiryDays', type=int, + default=None, + help='Number of days after which posts expire ' + + 'for the given account') parser.add_argument('--check-actor-timeout', dest='check_actor_timeout', type=int, default=2, help='Timeout in seconds used for checking is ' + @@ -1572,6 +1578,13 @@ def _command_options() -> None: time.sleep(1) sys.exit() + if argb.expiryDays is not None and argb.nickname and argb.domain: + set_post_expiry_days(base_dir, argb.nickname, argb.domain, + argb.expiryDays) + print('Post expiry for ' + argb.nickname + ' set to ' + + str(argb.expiryDays)) + sys.exit() + if argb.dav: if not argb.nickname: print('Please specify a nickname with --nickname') @@ -2773,10 +2786,17 @@ def _command_options() -> None: print('Archiving with deletion of old posts...') else: print('Archiving to ' + argb.archive + '...') + # archiving is for non-instance posts archive_media(base_dir, argb.archive, argb.archiveWeeks) archive_posts(base_dir, http_prefix, argb.archive, {}, argb.archiveMaxPosts) print('Archiving complete') + # expiry is for instance posts, where an expiry period + # has been set within profile settings + expired_count = expire_posts(base_dir, http_prefix, {}, + argb.debug) + if expired_count > 0: + print(str(expired_count) + ' posts expired') sys.exit() if not argb.domain and not domain: diff --git a/posts.py b/posts.py index 7c63c0eac..da4b1b274 100644 --- a/posts.py +++ b/posts.py @@ -32,6 +32,7 @@ from webfinger import webfinger_handle from httpsig import create_signed_header from siteactive import site_is_active from languages import understood_post_language +from utils import is_dm from utils import remove_eol from utils import text_in_file from utils import get_media_descriptions_from_post @@ -4233,6 +4234,172 @@ def archive_posts(base_dir: str, http_prefix: str, archive_dir: str, break +def _expire_posts_for_person(http_prefix: str, nickname: str, domain: str, + base_dir: str, recent_posts_cache: {}, + max_age_days: int, debug: bool, + keep_dms: bool) -> int: + """Removes posts older than some number of days + """ + expired_post_count = 0 + if max_age_days <= 0: + return expired_post_count + + boxname = 'outbox' + box_dir = create_person_dir(nickname, domain, base_dir, boxname) + + posts_in_box = os.scandir(box_dir) + for post_filename in posts_in_box: + post_filename = post_filename.name + if not post_filename.endswith('.json'): + continue + # Time of file creation + full_filename = os.path.join(box_dir, post_filename) + if not os.path.isfile(full_filename): + continue + content = '' + try: + with open(full_filename, 'r', encoding='utf-8') as fp_content: + content = fp_content.read() + except OSError: + print('EX: expire_posts_for_person unable to open content ' + + full_filename) + if '"published":' not in content: + continue + published_str = content.split('"published":')[1] + if '"' not in published_str: + continue + published_str = published_str.split('"')[1] + if not published_str.endswith('Z'): + continue + # get time difference + if not valid_post_date(published_str, max_age_days, debug): + if keep_dms: + post_json_object = load_json(full_filename) + if not post_json_object: + continue + if is_dm(post_json_object): + continue + delete_post(base_dir, http_prefix, nickname, domain, + full_filename, debug, recent_posts_cache, True) + expired_post_count += 1 + + return expired_post_count + + +def get_post_expiry_keep_dms(base_dir: str, nickname: str, domain: str) -> int: + """Returns true if dms should expire + """ + keep_dms = True + handle = nickname + '@' + domain + expire_dms_filename = \ + base_dir + '/accounts/' + handle + '/.expire_posts_dms' + if os.path.isfile(expire_dms_filename): + keep_dms = False + return keep_dms + + +def set_post_expiry_keep_dms(base_dir: str, nickname: str, domain: str, + keep_dms: bool) -> None: + """Sets whether to keep DMs during post expiry for an account + """ + handle = nickname + '@' + domain + expire_dms_filename = \ + base_dir + '/accounts/' + handle + '/.expire_posts_dms' + if keep_dms: + if os.path.isfile(expire_dms_filename): + try: + os.remove(expire_dms_filename) + except OSError: + print('EX: unable to write set_post_expiry_keep_dms False ' + + expire_dms_filename) + return + try: + with open(expire_dms_filename, 'w+', encoding='utf-8') as fp_expire: + fp_expire.write('\n') + except OSError: + print('EX: unable to write set_post_expiry_keep_dms True ' + + expire_dms_filename) + + +def expire_posts(base_dir: str, http_prefix: str, + recent_posts_cache: {}, debug: bool) -> int: + """Expires posts for instance accounts + """ + expired_post_count = 0 + for _, dirs, _ in os.walk(base_dir + '/accounts'): + for handle in dirs: + if '@' not in handle: + continue + nickname = handle.split('@')[0] + domain = handle.split('@')[1] + expire_posts_filename = \ + base_dir + '/accounts/' + handle + '/.expire_posts_days' + if not os.path.isfile(expire_posts_filename): + continue + keep_dms = get_post_expiry_keep_dms(base_dir, nickname, domain) + expire_days_str = None + try: + with open(expire_posts_filename, 'r', + encoding='utf-8') as fp_expire: + expire_days_str = fp_expire.read() + except OSError: + print('EX: expire_posts failed to read days file ' + + expire_posts_filename) + continue + if not expire_days_str: + continue + if not expire_days_str.isdigit(): + continue + max_age_days = int(expire_days_str) + if max_age_days <= 0: + continue + expired_post_count += \ + _expire_posts_for_person(http_prefix, + nickname, domain, base_dir, + recent_posts_cache, + max_age_days, debug, + keep_dms) + break + return expired_post_count + + +def get_post_expiry_days(base_dir: str, nickname: str, domain: str) -> int: + """Returns the post expiry period for the given account + """ + handle = nickname + '@' + domain + expire_posts_filename = \ + base_dir + '/accounts/' + handle + '/.expire_posts_days' + if not os.path.isfile(expire_posts_filename): + return 0 + days_str = None + try: + with open(expire_posts_filename, 'r', encoding='utf-8') as fp_expire: + days_str = fp_expire.read() + except OSError: + print('EX: unable to write post expire days ' + + expire_posts_filename) + if not days_str: + return 0 + if not days_str.isdigit(): + return 0 + return int(days_str) + + +def set_post_expiry_days(base_dir: str, nickname: str, domain: str, + max_age_days: int) -> None: + """Sets the number of days after which posts from an account will expire + """ + handle = nickname + '@' + domain + expire_posts_filename = \ + base_dir + '/accounts/' + handle + '/.expire_posts_days' + try: + with open(expire_posts_filename, 'w+', encoding='utf-8') as fp_expire: + fp_expire.write(str(max_age_days)) + except OSError: + print('EX: unable to write post expire days ' + + expire_posts_filename) + + def archive_posts_for_person(http_prefix: str, nickname: str, domain: str, base_dir: str, boxname: str, archive_dir: str, diff --git a/translations/ar.json b/translations/ar.json index ed8db5f38..778018259 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -571,5 +571,7 @@ "nowplaying": "الان العب", "NowPlaying": "الان العب", "Import and Export": "استيراد وتصدير", - "Import Follows": "يتبع الاستيراد" + "Import Follows": "يتبع الاستيراد", + "Post expiry period in days": "فترة ما بعد انتهاء الصلاحية بالأيام", + "Keep DMs during post expiry": "احتفظ بالرسائل الخاصّة أثناء انتهاء صلاحية النشر" } diff --git a/translations/bn.json b/translations/bn.json index 16ebc04e9..7337b1fb6 100644 --- a/translations/bn.json +++ b/translations/bn.json @@ -571,5 +571,7 @@ "nowplaying": "এখন চলছে", "NowPlaying": "এখন চলছে", "Import and Export": "আমদানি এবং রপ্তানি", - "Import Follows": "আমদানি অনুসরণ করে" + "Import Follows": "আমদানি অনুসরণ করে", + "Post expiry period in days": "দিনের মধ্যে মেয়াদ শেষ হওয়ার পরে", + "Keep DMs during post expiry": "পোস্টের মেয়াদ শেষ হওয়ার সময় সরাসরি বার্তা রাখুন" } diff --git a/translations/ca.json b/translations/ca.json index 984363360..0397a0a9f 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -571,5 +571,7 @@ "nowplaying": "arajugant", "NowPlaying": "AraJugant", "Import and Export": "Importació i Exportació", - "Import Follows": "Segueix la importació" + "Import Follows": "Segueix la importació", + "Post expiry period in days": "Període posterior a la caducitat en dies", + "Keep DMs during post expiry": "Conserveu els missatges directes durant la caducitat posterior" } diff --git a/translations/cy.json b/translations/cy.json index fc1506d12..88f200198 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -571,5 +571,7 @@ "nowplaying": "nawrynchwarae", "NowPlaying": "NawrYnChwarae", "Import and Export": "Mewnforio ac Allforio", - "Import Follows": "Mewnforio Dilyn" + "Import Follows": "Mewnforio Dilyn", + "Post expiry period in days": "Cyfnod ar ôl dod i ben mewn dyddiau", + "Keep DMs during post expiry": "Cadwch Negeseuon Uniongyrchol pan ddaw'r post i ben" } diff --git a/translations/de.json b/translations/de.json index 3acf03d1b..2fa28efb9 100644 --- a/translations/de.json +++ b/translations/de.json @@ -571,5 +571,7 @@ "nowplaying": "läuftgerade", "NowPlaying": "LäuftGerade", "Import and Export": "Import und Export", - "Import Follows": "Import folgt" + "Import Follows": "Import folgt", + "Post expiry period in days": "Nachablaufzeitraum in Tagen", + "Keep DMs during post expiry": "Bewahren Sie Direktnachrichten während des Ablaufs auf" } diff --git a/translations/el.json b/translations/el.json index 72b231aa8..803dcc6eb 100644 --- a/translations/el.json +++ b/translations/el.json @@ -571,5 +571,7 @@ "nowplaying": "τώραπαίζει", "NowPlaying": "ΤώραΠαίζει", "Import and Export": "Εισάγω και εξάγω", - "Import Follows": "Ακολουθεί εισαγωγή" + "Import Follows": "Ακολουθεί εισαγωγή", + "Post expiry period in days": "Η περίοδος μετά τη λήξη σε ημέρες", + "Keep DMs during post expiry": "Διατηρήστε τα άμεσα μηνύματα κατά τη λήξη της ανάρτησης" } diff --git a/translations/en.json b/translations/en.json index 9aa14b443..98baeccd4 100644 --- a/translations/en.json +++ b/translations/en.json @@ -571,5 +571,7 @@ "nowplaying": "nowplaying", "NowPlaying": "NowPlaying", "Import and Export": "Import and Export", - "Import Follows": "Import Follows" + "Import Follows": "Import Follows", + "Post expiry period in days": "Post expiry period in days", + "Keep DMs during post expiry": "Keep DMs during post expiry" } diff --git a/translations/es.json b/translations/es.json index 12229db9e..a8edbbca6 100644 --- a/translations/es.json +++ b/translations/es.json @@ -571,5 +571,7 @@ "nowplaying": "jugandoahora", "NowPlaying": "JugandoAhora", "Import and Export": "Importar y exportar", - "Import Follows": "Importar seguimientos" + "Import Follows": "Importar seguimientos", + "Post expiry period in days": "Período de vencimiento posterior en días", + "Keep DMs during post expiry": "Conservar los mensajes directos durante el vencimiento de la publicación" } diff --git a/translations/fr.json b/translations/fr.json index 19d2f1e08..09281533b 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -571,5 +571,7 @@ "nowplaying": "lectureencours", "NowPlaying": "LectureEnCours", "Import and Export": "Importer et exporter", - "Import Follows": "Importer suit" + "Import Follows": "Importer suit", + "Post expiry period in days": "Délai après expiration en jours", + "Keep DMs during post expiry": "Conserver les messages directs après l'expiration" } diff --git a/translations/ga.json b/translations/ga.json index 07fd0ac95..40ef0a67c 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -571,5 +571,7 @@ "nowplaying": "anoisagimirt", "NowPlaying": "AnoisAgImirt", "Import and Export": "Iompórtáil agus Easpórtáil", - "Import Follows": "Leanann Iompórtáil" + "Import Follows": "Leanann Iompórtáil", + "Post expiry period in days": "Tréimhse iar-éagtha i laethanta", + "Keep DMs during post expiry": "Coinnigh Teachtaireachtaí Díreacha nuair a rachaidh postáil in éag" } diff --git a/translations/hi.json b/translations/hi.json index d918f0c5c..2e5bd882e 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -571,5 +571,7 @@ "nowplaying": "अब खेल रहे हैं", "NowPlaying": "अब खेल रहे हैं", "Import and Export": "आयात और निर्यात", - "Import Follows": "आयात का अनुसरण करता है" + "Import Follows": "आयात का अनुसरण करता है", + "Post expiry period in days": "दिनों में समाप्ति अवधि पोस्ट करें", + "Keep DMs during post expiry": "समाप्ति के बाद सीधे संदेश रखें" } diff --git a/translations/it.json b/translations/it.json index 5628bde65..3dc18be99 100644 --- a/translations/it.json +++ b/translations/it.json @@ -571,5 +571,7 @@ "nowplaying": "ora giocando", "NowPlaying": "OraGiocando", "Import and Export": "Importazione e esportazione", - "Import Follows": "Importa segue" + "Import Follows": "Importa segue", + "Post expiry period in days": "Scadenza post in giorni", + "Keep DMs during post expiry": "Conserva i messaggi diretti durante la scadenza successiva" } diff --git a/translations/ja.json b/translations/ja.json index 841ae9a2e..88efc9f52 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -571,5 +571,7 @@ "nowplaying": "再生中", "NowPlaying": "再生中", "Import and Export": "インポートとエクスポート", - "Import Follows": "インポートフォロー" + "Import Follows": "インポートフォロー", + "Post expiry period in days": "投稿の有効期限 (日数)", + "Keep DMs during post expiry": "投稿の有効期限が切れるまでダイレクト メッセージを保持する" } diff --git a/translations/ko.json b/translations/ko.json index 3219cc6b1..979a70bf3 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -571,5 +571,7 @@ "nowplaying": "지금 재생", "NowPlaying": "지금 재생", "Import and Export": "가져오기 및 내보내기", - "Import Follows": "가져오기 팔로우" + "Import Follows": "가져오기 팔로우", + "Post expiry period in days": "사후 만료 기간(일)", + "Keep DMs during post expiry": "만료 후 쪽지 보관" } diff --git a/translations/ku.json b/translations/ku.json index 3bea33b45..ac561e035 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -571,5 +571,7 @@ "nowplaying": "nihadilîze", "NowPlaying": "NihaDilîze", "Import and Export": "Import û Export", - "Import Follows": "Import Follows" + "Import Follows": "Import Follows", + "Post expiry period in days": "Demjimêra qedandinê di çend rojan de", + "Keep DMs during post expiry": "Di dema qedandina postê de Peyamên Rasterast biparêzin" } diff --git a/translations/nl.json b/translations/nl.json index 83133657f..b1b25c01b 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -571,5 +571,7 @@ "nowplaying": "nuaanhetspelen", "NowPlaying": "NuAanHetSpelen", "Import and Export": "Importeren en exporteren", - "Import Follows": "Volgt importeren" + "Import Follows": "Volgt importeren", + "Post expiry period in days": "Na afloopperiode in dagen", + "Keep DMs during post expiry": "Directe berichten bewaren tijdens de vervaldatum" } diff --git a/translations/oc.json b/translations/oc.json index 0f442a0cc..0415c4a2a 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -567,5 +567,7 @@ "nowplaying": "nowplaying", "NowPlaying": "NowPlaying", "Import and Export": "Import and Export", - "Import Follows": "Import Follows" + "Import Follows": "Import Follows", + "Post expiry period in days": "Post expiry period in days", + "Keep DMs during post expiry": "Keep DMs during post expiry" } diff --git a/translations/pl.json b/translations/pl.json index edb23dfac..ac2f0ce92 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -571,5 +571,7 @@ "nowplaying": "terazgra", "NowPlaying": "TerazGra", "Import and Export": "Importuj i eksportuj", - "Import Follows": "Importuj obserwuje" + "Import Follows": "Importuj obserwuje", + "Post expiry period in days": "Okres po wygaśnięciu w dniach", + "Keep DMs during post expiry": "Zachowaj bezpośrednie wiadomości po wygaśnięciu" } diff --git a/translations/pt.json b/translations/pt.json index 7829ba381..684863d8a 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -571,5 +571,7 @@ "nowplaying": "agorajogando", "NowPlaying": "AgoraJogando", "Import and Export": "Importar e exportar", - "Import Follows": "Importar seguidores" + "Import Follows": "Importar seguidores", + "Post expiry period in days": "Prazo de expiração em dias", + "Keep DMs during post expiry": "Manter mensagens diretas durante a expiração da postagem" } diff --git a/translations/ru.json b/translations/ru.json index af2e9a37a..d9f2ecba4 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -571,5 +571,7 @@ "nowplaying": "сейчасиграет", "NowPlaying": "СейчасИграет", "Import and Export": "Импорт и экспорт", - "Import Follows": "Импорт подписок" + "Import Follows": "Импорт подписок", + "Post expiry period in days": "Срок действия в днях", + "Keep DMs during post expiry": "Сохраняйте личные сообщения в течение срока действия после истечения срока действия" } diff --git a/translations/sw.json b/translations/sw.json index e2848196c..aa4871bfd 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -571,5 +571,7 @@ "nowplaying": "inachezasasa", "NowPlaying": "InachezaSasa", "Import and Export": "Ingiza na Hamisha", - "Import Follows": "Ingiza Inafuata" + "Import Follows": "Ingiza Inafuata", + "Post expiry period in days": "Kipindi cha baada ya kumalizika kwa siku", + "Keep DMs during post expiry": "Weka Ujumbe wa Moja kwa Moja wakati wa kuisha kwa chapisho" } diff --git a/translations/tr.json b/translations/tr.json index a967b9c9c..e534302d2 100644 --- a/translations/tr.json +++ b/translations/tr.json @@ -571,5 +571,7 @@ "nowplaying": "şimdioynuyor", "NowPlaying": "ŞimdiOynuyor", "Import and Export": "İthalat ve ihracat", - "Import Follows": "Takipleri İçe Aktar" + "Import Follows": "Takipleri İçe Aktar", + "Post expiry period in days": "Gün olarak sona erme süresi", + "Keep DMs during post expiry": "Direkt Mesajları sona erme süresi boyunca saklayın" } diff --git a/translations/uk.json b/translations/uk.json index 4bf851e7d..b872e4018 100644 --- a/translations/uk.json +++ b/translations/uk.json @@ -571,5 +571,7 @@ "nowplaying": "заразграє", "NowPlaying": "ЗаразГрає", "Import and Export": "Імпорт та експорт", - "Import Follows": "Імпорт слідує" + "Import Follows": "Імпорт слідує", + "Post expiry period in days": "Термін після закінчення терміну дії в днях", + "Keep DMs during post expiry": "Зберігайте прямі повідомлення протягом терміну дії" } diff --git a/translations/yi.json b/translations/yi.json index 4145faad2..7f235498d 100644 --- a/translations/yi.json +++ b/translations/yi.json @@ -571,5 +571,7 @@ "nowplaying": "איצט פּלייַינג", "NowPlaying": "איצט פּלייַינג", "Import and Export": "אַרייַנפיר און עקספּאָרט", - "Import Follows": "אַרייַנפיר גייט" + "Import Follows": "אַרייַנפיר גייט", + "Post expiry period in days": "פּאָסטן עקספּיירי צייַט אין טעג", + "Keep DMs during post expiry": "האַלטן דירעקט אַרטיקלען בעשאַס פּאָסטן עקספּיירי" } diff --git a/translations/zh.json b/translations/zh.json index 702a1019f..eb560023f 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -571,5 +571,7 @@ "nowplaying": "现在玩", "NowPlaying": "现在玩", "Import and Export": "进出口", - "Import Follows": "导入关注" + "Import Follows": "导入关注", + "Post expiry period in days": "到期后天数", + "Keep DMs during post expiry": "在帖子到期期间保留直接消息" } diff --git a/webapp_profile.py b/webapp_profile.py index 585a2741b..65063361b 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -38,6 +38,8 @@ from theme import get_themes_list from person import person_box_json from person import get_actor_json from person import get_person_avatar_url +from posts import get_post_expiry_keep_dms +from posts import get_post_expiry_days from posts import get_person_box from posts import is_moderator from posts import parse_user_feed @@ -58,6 +60,7 @@ from filters import is_filtered from follow import is_follower_of_person from follow import get_follower_domains from webapp_frontscreen import html_front_screen +from webapp_utils import edit_number_field from webapp_utils import html_keyboard_navigation from webapp_utils import html_hide_from_screen_reader from webapp_utils import scheduled_posts_exist @@ -2183,6 +2186,17 @@ def _html_edit_profile_main(base_dir: str, display_nickname: str, bio_str: str, edit_text_field(translate['Time Zone'], 'timeZone', timezone, 'Europe/London') + post_expiry_period_days = \ + get_post_expiry_days(base_dir, nickname, domain) + edit_profile_form += \ + edit_number_field(translate['Post expiry period in days'], + 'postExpiryPeriod', post_expiry_period_days, + 0, 9999999999999999999999, 0) + keep_dms = get_post_expiry_keep_dms(base_dir, nickname, domain) + edit_profile_form += '
\n' + \ + edit_check_box(translate['Keep DMs during post expiry'], + 'expiryKeepDMs', keep_dms) + edit_profile_form += ' \n' return edit_profile_form