diff --git a/frontend/cmsmain.py b/frontend/cmsmain.py
index 820ca80..661c526 100644
--- a/frontend/cmsmain.py
+++ b/frontend/cmsmain.py
@@ -12,7 +12,7 @@ class CmsMain(WebserverCmd):
"""
ContentManagement System Main Program
"""
-
+
def getArgParser(self,description:str,version_msg)->ArgumentParser:
"""
override the default argparser call
diff --git a/frontend/frame.py b/frontend/frame.py
new file mode 100644
index 0000000..e015cfa
--- /dev/null
+++ b/frontend/frame.py
@@ -0,0 +1,124 @@
+class HtmlFrame:
+ """
+ A class to frame html content with a basic HTML document structure.
+
+ Attributes:
+ lang (str): Language of the HTML document.
+ title (str): Title of the HTML document.
+ """
+
+ def __init__(self, frontend,title: str, lang: str = "en") -> None:
+ """
+ Initialize HtmlFrame with a specified language and title.
+
+ Args:
+ title (str): Title for the HTML document.
+ lang (str, optional): Language of the HTML document. Defaults to "en".
+ """
+ self.frontend=frontend
+ self.lang = lang
+ self.title = title
+
+ def hamburger_menu(self) -> str:
+ """
+ Generate the HTML, CSS, and JavaScript for a hamburger menu.
+
+ Returns:
+ str: Hamburger menu HTML, CSS, and JavaScript.
+ """
+ menu_html = """
+
+
+
+
+
☰
+
+
+
+
+
+
+"""
+ return menu_html
+
+ def header(self) -> str:
+ """
+ Generate the header part of the HTML document.
+
+ Returns:
+ str: Header part of an HTML document as a string.
+ """
+ style_key=f"CMS/style"
+ style_html=self.frontend.cms_pages.get(style_key,"")
+ html = f"""
+
+
+
+
+ {self.title}
+ {style_html}
+
+
+"""
+ return html
+
+ def footer(self) -> str:
+ """
+ Generate the footer part of the HTML document.
+
+ Returns:
+ str: Footer part of an HTML document as a string.
+ """
+ footer_key=f"CMS/footer/{self.lang}"
+ footer_html=self.frontend.cms_pages.get(footer_key,"")
+ html = f"""{footer_html}
+
+
+"""
+ return html
+
+ def frame(self, content: str) -> str:
+ """
+ Frame the given HTML content with the header and footer of the document.
+
+ Args:
+ content (str): HTML content to be framed within the HTML structure.
+
+ Returns:
+ str: Complete HTML document as a string with the provided content framed.
+ """
+ html = f"""{self.header()}
+{self.hamburger_menu()}
+
+{content}
+
+{self.footer()}"""
+ return html
diff --git a/frontend/server.py b/frontend/server.py
index cec9fbd..b0f160d 100644
--- a/frontend/server.py
+++ b/frontend/server.py
@@ -193,7 +193,7 @@ def getStorePath(self,prefix:str="serverConfig")->str:
iniPath=self.homePath+"/.wikicms"
if not os.path.isdir(iniPath):
os.makedirs(iniPath)
- storePath="%s/%s" % (iniPath,prefix)
+ storePath=f"{iniPath}/{prefix}"
return storePath
def store(self):
@@ -245,7 +245,7 @@ def checkApacheConfiguration(self,conf,status='enabled')->str:
Returns:
a state symbol
'''
- path="/etc/apache2/sites-%s/%s.conf" % (status,conf)
+ path=f"/etc/apache2/sites-{status}/{conf}.conf"
confExists=os.path.isfile(path)
stateSymbol=self.stateSymbol(confExists)
return stateSymbol
diff --git a/frontend/webserver.py b/frontend/webserver.py
index 58fcb69..0f19979 100644
--- a/frontend/webserver.py
+++ b/frontend/webserver.py
@@ -56,7 +56,7 @@ async def wikis(client:Client):
return RedirectResponse('/login')
return await self.wikis()
- @app.get('/{frontend_name}/{page_path}')
+ @app.get('/{frontend_name}/{page_path:path}')
def render_path(frontend_name: str, page_path: str) -> HTMLResponse:
"""
Handles a GET request to render the path of the given frontend.
@@ -89,11 +89,8 @@ def render_path(self,frontend_name:str,page_path:str):
frontend=self.server.frontends.get(frontend_name,None)
if frontend is None:
raise HTTPException(status_code=404, detail=f"frontend {frontend_name} is not available")
- pagetitle,content,error=frontend.getContent(page_path)
- html=content
- if error:
- html=(f"error getting {pagetitle} for {frontend_name}:
{error}")
- return HTMLResponse(html)
+ response=frontend.get_path_response(f"/{page_path}")
+ return response
def enableSites(self, siteNames):
'''
diff --git a/frontend/wikicms.py b/frontend/wikicms.py
index 592ebfb..d6d8db8 100644
--- a/frontend/wikicms.py
+++ b/frontend/wikicms.py
@@ -6,30 +6,35 @@
from wikibot3rd.wikiclient import WikiClient
from wikibot3rd.smw import SMWClient
from frontend.site import Site
-from bs4 import BeautifulSoup
+from bs4 import BeautifulSoup, Comment
+import re
import traceback
import requests
-
+from fastapi import Response
+from fastapi.responses import HTMLResponse
+from frontend.frame import HtmlFrame
class Frontend(object):
"""
Wiki Content Management System Frontend
"""
- def __init__(self, siteName: str, debug: bool = False, filterKeys=None):
+ def __init__(self, site_name: str,parser:str="lxml",debug: bool = False, filterKeys=None):
"""
Constructor
Args:
- siteName(str): the name of the site this frontend is for
+ site_name(str): the name of the site this frontend is for
+ parser(str): the beautiful soup parser to use e.g. html.parser
debug: (bool): True if debugging should be on
filterKeys: (list): a list of keys for filters to be applied e.g. editsection
"""
-
- self.site = Site(siteName)
+ self.name=site_name
+ self.parser=parser
+ self.site = Site(site_name)
self.debug = debug
self.wiki = None
if filterKeys is None:
- self.filterKeys = ["editsection", "parser-output"]
+ self.filterKeys = ["editsection", "parser-output","parser-output"]
else:
self.filterKeys = []
@@ -85,6 +90,20 @@ def open(self, ws=None):
self.wiki.login()
self.smwclient = SMWClient(self.wiki.getSite())
self.site.open(ws)
+ self.cms_pages=self.get_cms_pages()
+
+ def get_cms_pages(self)->dict:
+ """
+ get the Content Management elements for this site
+ """
+ cms_pages={}
+ ask_query="[[Category:CMS]]"
+ page_records=self.smwclient.query(ask_query, "cms pages")
+ for page_title in list(page_records):
+ page_title,html,error=self.getContent(page_title)
+ if not error:
+ cms_pages[page_title]=html
+ return cms_pages
def errMsg(self, ex):
if self.debug:
@@ -157,7 +176,10 @@ def proxy(self, path: str) -> str:
return response
- def filter(self, html):
+ def filter(self, html:str)->str:
+ """
+ filter the given html
+ """
return self.doFilter(html, self.filterKeys)
def fixNode(self, node, attribute, prefix, delim=None):
@@ -204,62 +226,39 @@ def fixHtml(self, soup):
self.fixNode(a, "href", "/")
return soup
- def unwrap(self, soup):
+ def unwrap(self, soup)->str:
+ """
+ unwrap the soup
+ """
html = str(soup)
html = html.replace("", "")
html = html.replace("", "")
+ # Remove empty paragraphs
+ html = re.sub(r'\s*
', '', html)
+
+ # Replace multiple newline characters with a single newline character
+ html = re.sub(r'\n\s*\n', '\n', html)
return html
def doFilter(self, html, filterKeys):
# https://stackoverflow.com/questions/5598524/can-i-remove-script-tags-with-beautifulsoup
- soup = BeautifulSoup(html, "lxml")
+ soup = BeautifulSoup(html, self.parser)
if "parser-output" in filterKeys:
parserdiv = soup.find("div", {"class": "mw-parser-output"})
if parserdiv:
soup = parserdiv
+ inner_html = parserdiv.decode_contents()
+ # Parse the inner HTML string to create a new BeautifulSoup object
+ soup = BeautifulSoup(inner_html, self.parser)
pass
# https://stackoverflow.com/questions/5041008/how-to-find-elements-by-class
if "editsection" in filterKeys:
for s in soup.select("span.mw-editsection"):
s.extract()
+ for comments in soup.findAll(text=lambda text: isinstance(text, Comment)):
+ comments.extract()
return soup
- def getFrame(self, pageTitle):
- """
- get the frame template to be used for the given pageTitle#
-
- Args:
- pageTitle(str): the pageTitle to get the Property:Frame for
-
- Returns:
- str: the frame or None
- """
- askQuery = (
- """{{#ask: [[%s]]
-|mainlabel=-
-|?Frame=frame
-}}
-"""
- % pageTitle
- )
- frame = None
- frameResult = {}
- try:
- frameResult = self.smwclient.query(askQuery)
- except Exception as ex:
- if "invalid characters" in self.unwrap(ex):
- pass
- else:
- raise ex
- if pageTitle in frameResult:
- frameRow = frameResult[pageTitle]
- frame = frameRow["frame"]
- # legacy java handling
- if frame is not None:
- frame = frame.replace(".rythm", "")
- pass
- return frame
-
def getContent(self, pagePath: str):
"""get the content for the given pagePath
Args:
@@ -312,33 +311,34 @@ def toReveal(self, html):
html = self.unwrap(soup)
return html
- def render(self, path: str, **kwargs) -> str:
+ def get_path_response(self, path: str) -> str:
"""
- render the given path
+ get the repsonse for the the given path
Args:
path(str): the path to render the content for
- kwargs(): optional keyword arguments
-
+
Returns:
- str: the rendered result
+ Response: a FastAPI response
"""
if self.needsProxy(path):
- result = self.proxy(path)
+ html_response = self.proxy(path)
+ # Create a FastAPI response object
+ response=Response(content=html_response.content,
+ status_code=html_response.status_code,
+ headers=dict(html_response.headers))
else:
- pageTitle, content, error = self.getContent(path)
- frame = self.getFrame(pageTitle)
- if frame is not None:
- template = "%s.html" % frame
- if frame == "reveal" and error is None:
- content = self.toReveal(content)
- else:
- template = self.site.template
- if not "title" in kwargs:
- kwargs["title"] = pageTitle
- if not "content" in kwargs:
- kwargs["content"] = content
- if not "error" in kwargs:
- kwargs["error"] = error
- result = self.renderTemplate(template, **kwargs)
- return result
+ page_title, content, error = self.getContent(path)
+ frame=HtmlFrame(self,title=page_title)
+ html=content
+ # frame = self.getFrame(pageTitle)
+ # if frame is not None:
+ # template = "%s.html" % frame
+ if frame == "reveal" and error is None:
+ content = self.toReveal(content)
+ html=content
+ if error:
+ html=f"error getting {page_title} for {self.name}:
{error}"
+ framed_html=frame.frame(html)
+ response=HTMLResponse(framed_html)
+ return response
diff --git a/tests/test_frontend.py b/tests/test_frontend.py
index 97decd8..dbd5e48 100644
--- a/tests/test_frontend.py
+++ b/tests/test_frontend.py
@@ -3,6 +3,7 @@
@author: wf
"""
+import json
import unittest
from frontend.wikicms import Frontend
from tests.test_webserver import TestWebServer
@@ -176,6 +177,19 @@ def testFixHtml(self):
self.assertFalse("""srcset="/images""" in content)
self.assertTrue("""srcset="/www/images""" in content)
pass
+
+ def test_cms_pages(self):
+ """
+ test the content management pages
+ """
+ frontend = self.server.enableFrontend("www")
+ frontend.open()
+ cms_pages=frontend.get_cms_pages()
+ debug=self.debug
+ debug=True
+ if debug:
+ print(json.dumps(cms_pages,indent=2))
+ self.assertTrue("CMS/footer/de" in cms_pages)
if __name__ == "__main__":