From 7c16beb6538ccf024b552d475a26b9345bf550ec Mon Sep 17 00:00:00 2001 From: mjfernez Date: Mon, 18 Oct 2021 19:53:03 -0400 Subject: Fixes is_hidden_path, RSS. Adds txt support, RSS This commit fixes the is_hidden_path function to work for subdirectories and also remove entries from the RSS file view_functions.py has been refactored use the siteconfig file instead of the app to remove unecessary imports and avoid circularly imports This also moves logic for the default site directories from server.py to siteconfig.py so it's accessible before the app runs. This has the benefit of cleaning up the server file, but the drawback of adding the os import to the siteconfig file. Settings have been moved around for (hopefully) easier reading --- rss_generator.py | 47 ++++++++++++++++++++++++++++++++++++++--------- server.py | 21 +++------------------ siteconfig.py | 38 +++++++++++++++++++++++++++----------- view_functions.py | 23 ++++++++++------------- 4 files changed, 78 insertions(+), 51 deletions(-) diff --git a/rss_generator.py b/rss_generator.py index 4397a45..f248003 100644 --- a/rss_generator.py +++ b/rss_generator.py @@ -1,12 +1,24 @@ import os from time import strftime, strptime, ctime from siteconfig import siteconfig +from view_functions import is_hidden_path class RSS_Item: + """ + RSS_Item - a (very) basic implementation of an object in an RSS + feed using only essential parameters as specified in: + https://www.rssboard.org/rss-specification#hrelementsOfLtitemgt + + Item data is generated from a given file path + """ PARAGRAPHS = siteconfig.rss_channel_config['DESCRIPTION_LENGTH'] class NotAFile(Exception): + """ + Throws an exception if an RSS_Item is made out of a + directory or invalid file + """ def __init__(self, path: str): self.path = path self.message = f"{path} not a file" @@ -18,6 +30,7 @@ class RSS_Item: self.FULL_PATH = path self.TITLE = path.rsplit('.', 1)[0].split('/')[-1] + self.FILE_TYPE = path.rsplit('.', 1)[1] self.DESCRIPTION = self.parse_file() self.LAST_UPDATE = self.file_last_modified() self.URI = self.get_uri() @@ -35,7 +48,8 @@ class RSS_Item: """ parse_file - reads the file at FULL_PATH and saves the content from when the first

tag is hit up to and including the - closing

tag. Expects an HTML style file + closing

tag. Other files are interpreted as text files + and, just reads the first 3 paragraphs (two new lines in a row) """ with open(self.FULL_PATH) as f: in_body = False @@ -45,13 +59,19 @@ class RSS_Item: if paragraphs >= self.PARAGRAPHS: break line = line.strip() - if line.startswith("

"): - in_body = True - if in_body: + if self.FILE_TYPE in ['html', 'html!']: + if line.startswith("

"): + in_body = True + if in_body: + description += line + if line.endswith("

"): + in_body = False + paragraphs += 1 + else: description += line - if line.endswith("

"): - in_body = False - paragraphs += 1 + # remember, we stripped the line + if line == '': + paragraphs += 1 return ''.join(description) @@ -59,16 +79,25 @@ class RSS_Item: return ctime(os.stat(self.FULL_PATH).st_ctime) def get_uri(self): + # return everything after "./templates/" return '/'.join(self.FULL_PATH.split('/')[2:]) def get_rss_channel(): + """ + get_rss_channel - list all files from the BASE_DIR, and if allowed, + add them as RSS_Items to populate feed.xml. Called by feed.xml view + """ items = [] + extensions = siteconfig.rss_channel_config['RSS_FILE_EXT'] for root, dirs, files in os.walk(siteconfig.BASE_DIR): for f in files: + # remember, path will be like "./templates/site/..." path = os.path.join(root, f) if ( - path.endswith(".html") or f.endswith(".html!") - ) and path not in siteconfig.RSS_OMIT: + path.split(".")[-1] in extensions + and path not in siteconfig.RSS_OMIT + and not is_hidden_path(path.split('.', 1)[1]) + ): items.append(RSS_Item(path)) return items diff --git a/server.py b/server.py index 14c4182..ab66ccc 100644 --- a/server.py +++ b/server.py @@ -1,7 +1,6 @@ """ server.py - sets up and runs the flask server """ -import os from flask import Flask from siteconfig import siteconfig from flask_compress import Compress @@ -20,23 +19,8 @@ def setup(): setup - sets up the app according to the settings specified (or not specified) in `siteconfig` """ - if siteconfig.SECRET_KEY: - app.config['SECRET_KEY'] = siteconfig.SECRET_KEY - else: - SECRET_KEY = os.urandom(32) - app.config['SECRET_KEY'] = SECRET_KEY - - if siteconfig.MAIN_SITE_DIRS: - app.config.update({'MAIN_SITE_DIRS': siteconfig.MAIN_SITE_DIRS}) - else: - s = "./templates/site/" - top_dirs = [ - x - for x in os.listdir(s) - if os.path.isdir(s + x) and not x.startswith(".") - ] - app.config.update({'MAIN_SITE_DIRS': sorted(top_dirs)}) - + app.config['SECRET_KEY'] = siteconfig.SECRET_KEY + app.config.update({'MAIN_SITE_DIRS': siteconfig.MAIN_SITE_DIRS}) app.config.update({'DOMAIN': siteconfig.DOMAIN}) app.config.update({'HOME_TITLE': siteconfig.HOME_TITLE}) app.config.update( @@ -45,6 +29,7 @@ def setup(): app.config.update({'RSS_CHANNEL': get_rss_channel()}) app.config.update({'TEMPLATES_AUTO_RELOAD': True}) + # Setup needs to come first to be compatible with wsgi setup() compress.init_app(app) diff --git a/siteconfig.py b/siteconfig.py index 4c60674..8917a20 100644 --- a/siteconfig.py +++ b/siteconfig.py @@ -1,28 +1,24 @@ """ siteconfig.py - user editable configuration file """ +import os class siteconfig: - # REQUIRED SETTINGS# - - DOMAIN = "example.net" # Your site here! - HOME_TITLE = "WELCOME" - LINKS_FILE = ".links" # ".lnx" if you like - DESC_FILE = ".description" # ".desc" - DEFAULT_MIMETYPE = "application/octet-stream" - # ^This usually prompts a browser to download a file if the mime - # type is unknown. A good alternative might be "text/plain" + # BACKGROUND SETTINGS # + # These don't need to be changed # This setting is required, don't change it unless you're running # things in different directories BASE_DIR = "./templates/site/" + # Add your desired mimetypes to the csv file MIMETYPES = {} with open('mimetypes.csv') as f: for line in f.readlines(): ext, mime = line.strip().split(',') MIMETYPES.update({ext: mime}) + # This reads your omit file. # Entries should be the full path from the site directory. # For example "dontread.txt" in this project is entered as @@ -32,16 +28,34 @@ class siteconfig: for line in f.readlines(): RSS_OMIT.append(BASE_DIR + line.strip()) + DEFAULT_SITE_DIRS = [] + for x in os.listdir(BASE_DIR): + if os.path.isdir(BASE_DIR + x) and not x.startswith("."): + DEFAULT_SITE_DIRS.append(x) + + # REQUIRED SETTINGS # + + DOMAIN = "example.net" # Your site here! + HOME_TITLE = "WELCOME" + LINKS_FILE = ".links" # ".lnx" if you like + DESC_FILE = ".description" # ".desc" + DEFAULT_MIMETYPE = "application/octet-stream" + # ^This usually prompts a browser to download a file if the mime + # type is unknown. A good alternative might be "text/plain" + # OPTIONAL SETTINGS # # Be default, ALL directories in the site toolbar are contained in # ./templates/site/. You can change this to only specific directories, but # these still have to be in ./templates/site - MAIN_SITE_DIRS = None # ["dir1", "dir2", "dir3"] + + MAIN_SITE_DIRS = sorted( + DEFAULT_SITE_DIRS + ) # ["dir1", "dir2", "dir3"] # Set a custom secret key. If not set, it will be generated # Most of the time, you don't need to set this! - SECRET_KEY = None # Something random. + SECRET_KEY = os.urandom(32) # replace with random number. # Options for Flask Compress # see here https://pypi.org/project/Flask-Compress/ @@ -90,4 +104,6 @@ class siteconfig: 'WEBMASTER': "webmaster@example.net", # Max amount of paragraphs to print in each description 'DESCRIPTION_LENGTH': 3, + # File extensions to include in RSS updates + 'RSS_FILE_EXT': ["txt", "html", "html!"], } diff --git a/view_functions.py b/view_functions.py index 64dcfe0..f86952d 100644 --- a/view_functions.py +++ b/view_functions.py @@ -4,7 +4,6 @@ about files and paths. """ import os from siteconfig import siteconfig -from server import app def default_context(): @@ -14,8 +13,8 @@ def default_context(): make up the navbar """ return { - 'domain': app.config['DOMAIN'], - 'navbar': sorted(app.config['MAIN_SITE_DIRS']), + 'domain': siteconfig.DOMAIN, + 'navbar': siteconfig.MAIN_SITE_DIRS, } @@ -49,14 +48,10 @@ def index_dir(path): links = f.readlines() elif obj == siteconfig.DESC_FILE: description = True - elif obj.startswith('.'): - continue - else: + elif not obj.startswith('.'): files.append(obj) elif os.path.isdir(path + '/' + obj): - if obj.startswith('.'): - continue - else: + if not obj.startswith('.'): dirs.append(obj) return sorted(dirs), sorted(files), sorted(links), description @@ -64,8 +59,10 @@ def index_dir(path): def is_hidden_path(path): """ - Tests if last object specified in `path` is hidden. - Inspired by Unix. On Windows, directories won't actually be "hidden" but - they are still not indexed by this program + Tests if the URI path requested by the user, has any hidden paths + which should not be rendered """ - return path.split('/')[-1].startswith('.') + for d in path.split('/'): + if d.startswith('.'): + return True + return False -- cgit v1.2.3