def __init__(self, config, doc_src_path, site_config, logger=None): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self._extention = { "toc": { "depth": config["toc_depth"] if "toc_depth" in config else 3 }, "metadata": None, "fenced-code-blocks": None, "highlightjs-lang": None, "break-on-newline": None, "code-friendly": None, "cuddled-lists": None, "footnotes": None, "strike": None, "spoiler": None, "tables": None, "task_list": None }
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.multiprocess = multiprocess self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config mathjax_config = self.config["mathjax"] if "mathjax" in config: for k, v in config["mathjax"].items(): if type(v) != dict: mathjax_config[k] = v else: mathjax_config[k].update(v) self.config.update(config) self.config["mathjax"] = mathjax_config self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) if not self.multiprocess: from .renderer import create_markdown_parser from .parse_metadata import Meta_Parser self.create_markdown_parser = create_markdown_parser self.Meta_Parser = Meta_Parser
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.html_header_items = [] # set site_root_url env value if not "id" in config: self.logger.e('can not find config["id"] in plugin {}'.format( self.name)) return code_raw = ''' <script async src="https://www.googletagmanager.com/gtag/js?id={}"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){{dataLayer.push(arguments);}} gtag('js', new Date()); gtag('config', '{}'); </script>'''.format(config["id"], config["id"]) self.html_header_items = [code_raw]
def on_init(self, config, doc_src_path, site_config, logger = None, multiprocess = True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format(self.name, self.config)) # set site_root_url env value if not "code" in config: self.logger.e('can not find config["code"] in plugin {}'.format(self.name)) return baidu_tongji_code = '''<script> var _hmt = _hmt || []; (function() {{ var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?{}"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); }})(); </script>'''.format(config["code"]) self.html_header_items = [baidu_tongji_code]
def __init__(self, config, doc_src_path, site_config, logger=None): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_search") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.css = { "/static/css/search/style.css": os.path.join(self.assets_abs_path, "style.css"), } self.footer_js = { "/static/js/search/main.js": os.path.join(self.assets_abs_path, "main.js") } self.images = { "/static/image/search/close.svg": os.path.join(self.assets_abs_path, "close.svg"), "/static/image/search/search.svg": os.path.join(self.assets_abs_path, "search.svg"), } # set site_root_url env value if not "env" in config: config['env'] = {} config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = config["env"] self.css = self._update_file_var(self.css, vars, self.temp_dir) self.footer_js = self._update_file_var(self.footer_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} self.files_to_copy.update(self.css) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) self.html_js_items = self._generate_html_js_items() self.content = {"articles": {}, "pages": {}}
def on_init(self, config, doc_src_path, site_config, logger = None, multiprocess = True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format(self.name, self.config))
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.multiprocess = multiprocess self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_blog") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.assets = { "/static/js/plugin_blog/main.js": os.path.join(self.assets_abs_path, "main.js"), } vars = {"site_root_url": self.site_config["site_root_url"]} self.assets = self._update_file_var(self.assets, vars, self.temp_dir) self.files_to_copy = self.assets.copy() # must use copy blog_url = list(self.site_config["route"]["blog"].keys()) if len(blog_url) > 1: self.logger.e("only support one blog url path") raise Exception("only support one blog url path") self.blog_url = blog_url[0] self.blog_dir = os.path.join( self.doc_src_path, self.site_config["route"]["blog"][self.blog_url]).replace( "\\", "/") self.index_content = {"items": {}}
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.module_path = os.path.abspath( os.path.dirname(os.path.abspath(__file__))) self.assets_abs_path = os.path.join(self.module_path, "assets") self.temp_dir = self.get_temp_dir() self.footer_js = { # don't use ad(advertisement) keyword, may blocked by browser plugin "/static/js/add_hint/style.css": os.path.join(self.assets_abs_path, "style.css"), "/static/js/add_hint/main.js": os.path.join(self.assets_abs_path, "main.js") } self.html_footer_items = [] for url in self.footer_js: if url.endswith(".css"): item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( url) else: item = '<script src="{}"></script>'.format(url) self.html_footer_items.append(item) vars = self.config self.footer_js = self.update_file_var(self.footer_js, vars, self.temp_dir) self.files_to_copy = self.footer_js
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.module_path = os.path.abspath( os.path.dirname(os.path.abspath(__file__))) self.assets_abs_path = os.path.join(self.module_path, "assets")
class Plugin(Plugin_Base): name = "teedoc-plugin-markdown-parser" desc = "markdown parser plugin for teedoc" defautl_config = {"parse_files": ["md"]} def __init__(self, config, doc_src_path, site_config, logger=None): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self._extention = { "toc": { "depth": config["toc_depth"] if "toc_depth" in config else 3 }, "metadata": None, "fenced-code-blocks": None, "highlightjs-lang": None, "break-on-newline": None, "code-friendly": None, "cuddled-lists": None, "footnotes": None, "strike": None, "spoiler": None, "tables": None, "task_list": None } def on_parse_files(self, files): # result, format must be this result = {"ok": False, "msg": "", "htmls": OrderedDict()} # function parse md file is disabled if not "md" in self.config["parse_files"]: result[ "msg"] = "disabled markdown parse, but only support markdown" return result self.logger.d("-- plugin <{}> parse {} files".format( self.name, len(files))) # self.logger.d("files: {}".format(files)) for file in files: ext = os.path.splitext(file)[1].lower() if ext.endswith("md"): with open(file, encoding="utf-8") as f: content = f.read().strip() content = self._update_link(content) parser = markdown2.Markdown(extras=self._extention) parser._toc_html = "" html = parser.convert(content) if "title" in html.metadata: title = html.metadata["title"] else: title = "" if "keywords" in html.metadata: keywords = html.metadata["keywords"].split(",") else: keywords = [] if "tags" in html.metadata: tags = html.metadata["tags"].split(",") else: tags = [] if "desc" in html.metadata: desc = html.metadata["desc"] else: desc = [] result["htmls"][file] = { "title": title, "desc": desc, "keywords": keywords, "tags": tags, "body": html, "toc": html.toc_html if html.toc_html else "", "metadata": html.metadata, "raw": content } else: result["htmls"][file] = None result['ok'] = True return result def on_parse_pages(self, files): result = self.on_parse_files(files) return result def on_add_html_header_items(self): items = [] items.append( '<meta name="markdown-generator" content="teedoc-plugin-markdown-parser">' ) return items def _update_link(self, content): def re_del(c): ret = c[0].replace(".md", ".html") ret = re.sub("README.md", "index.html", c[0], flags=re.I) ret = re.sub(r".md", ".html", ret, re.I) return ret content = re.sub(r'\[.*?\]\(.*?\.md\)', re_del, content, flags=re.I) return content
class Plugin(Plugin_Base): name = "teedoc-plugin-jupyter-notebook-parser" desc = "jupyter notebook parser plugin for teedoc" defautl_config = { "parse_files": ["ipynb"] } def on_init(self, config, doc_src_path, site_config, logger = None, multiprocess = True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format(self.name, self.config)) def on_parse_files(self, files): # result, format must be this result = { "ok": False, "msg": "", "htmls": OrderedDict() } # function parse md file is disabled if not "ipynb" in self.config["parse_files"]: result["msg"] = "disabled notebook parse, but only support notebook" return result self.logger.d("-- plugin <{}> parse {} files".format(self.name, len(files))) # self.logger.d("files: {}".format(files)) for file in files: name = os.path.basename(file) # ignore temp file if name.startswith(".~"): result["htmls"][file] = None continue ext = os.path.splitext(file)[1].lower() if ext.endswith("ipynb"): html = convert_ipynb_to_html(file) html.body = self._update_link_html(html.body) metadata = html.metadata date = None ts = int(os.stat(file).st_mtime) if "date" in metadata: date = metadata["date"].strip().lower() # set date to false to disable date display if date and (date == "false" or date == "none"): date = "" else: GMT_FORMAT = '%Y-%m-%d' try: date_obj = datetime.strptime(date, GMT_FORMAT) ts = int(date_obj.timestamp()) except Exception as e: pass if "author" in metadata: author = metadata["author"] else: author = "" result["htmls"][file] = { "title": html.title, "desc": html.desc, "keywords": html.keywords, "tags": html.tags, "body": html.body, "author": author, "date": date, "ts": ts, "toc": html.toc, "metadata": metadata, "raw": html.raw } else: result["htmls"][file] = None result['ok'] = True return result def on_parse_pages(self, files): result = self.on_parse_files(files) return result def on_add_html_header_items(self, type_name): items = [] items.append('<meta name="html-generator" content="teedoc-plugin-jupyter-notebook-parser">') return items def _update_link_html(self, content): def re_del(c): ret = c[0] links = re.findall('href="(.*?)"', c[0]) if len(links) > 0: for link in links: if link.startswith(".") or os.path.isabs(link): ret = re.sub("README.md", "index.html", c[0], flags=re.I) ret = re.sub(r".md", ".html", ret, re.I) return ret return ret def re_del_ipynb(c): ret = c[0] links = re.findall('href="(.*?)"', c[0]) if len(links) > 0: for link in links: if link.startswith(".") or os.path.isabs(link): ret = re.sub("README.ipynb", "index.html", c[0], flags=re.I) ret = re.sub(r".ipynb", ".html", ret, re.I) return ret return ret # <a class="anchor-link" href="#链接"> </a></h2><p><a href="./syntax_markdown.md">markdown 语法</a> content = re.sub(r'\<a.*?href=.*?\.md.*?\</a\>', re_del, content, flags=re.I) content = re.sub(r'\<a.*?href=.*?\.ipynb.*?\</a\>', re_del_ipynb, content, flags=re.I) return content
class Plugin(Plugin_Base): name = "teedoc-plugin-theme-default" desc = "default theme for teedoc" defautl_config = { "light": True } def __init__(self, config, doc_src_path, site_config, logger = None): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format(self.name, self.config)) self.assets_abs_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets") self.dark_css = { "/static/css/theme_default/dark.css": os.path.join(self.assets_abs_path, "dark.css") } self.light_css = { "/static/css/theme_default/light.css": os.path.join(self.assets_abs_path, "light.css") } self.css = { "/static/css/theme_default/prism.min.css": os.path.join(self.assets_abs_path, "prism.min.css"), } # js files self.dark_js = { } self.light_js = { } self.header_js = { "/static/js/theme_default/jquery.min.js": os.path.join(self.assets_abs_path, "jquery.min.js"), "/static/js/theme_default/pre_main.js": os.path.join(self.assets_abs_path, "pre_main.js") } self.footer_js = { "/static/js/theme_default/main.js": os.path.join(self.assets_abs_path, "main.js"), "/static/css/theme_default/prism.min.js": os.path.join(self.assets_abs_path, "prism.min.js") } self.images = { "/static/image/theme_default/indicator.svg": os.path.join(self.assets_abs_path, "indicator.svg"), "/static/image/theme_default/menu.svg": os.path.join(self.assets_abs_path, "menu.svg"), "/static/image/theme_default/to-top.svg": os.path.join(self.assets_abs_path, "to-top.svg"), "/static/image/theme_default/light_mode.svg": os.path.join(self.assets_abs_path, "light_mode.svg"), "/static/image/theme_default/dark_mode.svg": os.path.join(self.assets_abs_path, "dark_mode.svg") } # set site_root_url env value if not "env" in config: config['env'] = {} config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = config["env"] self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_theme_default") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.dark_css = self._update_file_var(self.dark_css, vars, self.temp_dir) self.light_css = self._update_file_var(self.light_css, vars, self.temp_dir) self.css = self._update_file_var(self.css, vars, self.temp_dir) self.header_js = self._update_file_var(self.header_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} if self.config["dark"]: self.files_to_copy.update(self.dark_css) self.files_to_copy.update(self.dark_js) self.files_to_copy.update(self.light_css) self.files_to_copy.update(self.css) self.files_to_copy.update(self.light_js) self.files_to_copy.update(self.header_js) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) if self.config["dark"]: self.themes_btn = '<a id="themes" class="light"></a>' else: self.themes_btn = "" self.html_js_items = self._generate_html_js_items() def __del__(self): if os.path.exists(self.temp_dir): try: shutil.rmtree(self.temp_dir) except Exception: pass def _generate_html_header_items(self): items = [] # css for url in self.css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format(url) items.append(item) if self.config["dark"]: for url in self.dark_css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format(url) items.append(item) for url in self.light_css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format(url) items.append(item) if "css" in self.config: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format(self.config["css"]) items.append(item) # header_js for url in self.header_js: item = '<script src="{}"></script>'.format(url) items.append(item) if self.config["dark"]: for url in self.dark_js: item = '<script src="{}"></script>'.format(url) items.append(item) for url in self.light_js: item = '<script src="{}"></script>'.format(url) items.append(item) return items def _generate_html_js_items(self): items = [] for url in self.footer_js: item = '<script src="{}"></script>'.format(url) items.append(item) if "js" in self.config: item = '<script src="{}"></script>'.format(self.config["js"]) items.append(item) return items def _update_file_var(self, files, vars, temp_dir): for url, path in files.items(): with open(path, encoding='utf-8') as f: content = f.read() for k, v in vars.items(): content = content.replace("${}{}{}".format("{", k.strip(), "}"), v) temp_path = os.path.join(temp_dir, os.path.basename(path)) with open(temp_path, "w", encoding='utf-8') as fw: fw.write(content) files[url] = temp_path return files def on_add_html_header_items(self): return self.html_header_items def on_add_html_js_items(self): return self.html_js_items def on_add_navbar_items(self, new_config): items = [self.themes_btn] return items def on_copy_files(self): return self.files_to_copy
def __init__(self, config, doc_src_path, site_config, logger = None): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format(self.name, self.config)) self.assets_abs_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets") self.dark_css = { "/static/css/theme_default/dark.css": os.path.join(self.assets_abs_path, "dark.css") } self.light_css = { "/static/css/theme_default/light.css": os.path.join(self.assets_abs_path, "light.css") } self.css = { "/static/css/theme_default/prism.min.css": os.path.join(self.assets_abs_path, "prism.min.css"), } # js files self.dark_js = { } self.light_js = { } self.header_js = { "/static/js/theme_default/jquery.min.js": os.path.join(self.assets_abs_path, "jquery.min.js"), "/static/js/theme_default/pre_main.js": os.path.join(self.assets_abs_path, "pre_main.js") } self.footer_js = { "/static/js/theme_default/main.js": os.path.join(self.assets_abs_path, "main.js"), "/static/css/theme_default/prism.min.js": os.path.join(self.assets_abs_path, "prism.min.js") } self.images = { "/static/image/theme_default/indicator.svg": os.path.join(self.assets_abs_path, "indicator.svg"), "/static/image/theme_default/menu.svg": os.path.join(self.assets_abs_path, "menu.svg"), "/static/image/theme_default/to-top.svg": os.path.join(self.assets_abs_path, "to-top.svg"), "/static/image/theme_default/light_mode.svg": os.path.join(self.assets_abs_path, "light_mode.svg"), "/static/image/theme_default/dark_mode.svg": os.path.join(self.assets_abs_path, "dark_mode.svg") } # set site_root_url env value if not "env" in config: config['env'] = {} config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = config["env"] self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_theme_default") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.dark_css = self._update_file_var(self.dark_css, vars, self.temp_dir) self.light_css = self._update_file_var(self.light_css, vars, self.temp_dir) self.css = self._update_file_var(self.css, vars, self.temp_dir) self.header_js = self._update_file_var(self.header_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} if self.config["dark"]: self.files_to_copy.update(self.dark_css) self.files_to_copy.update(self.dark_js) self.files_to_copy.update(self.light_css) self.files_to_copy.update(self.css) self.files_to_copy.update(self.light_js) self.files_to_copy.update(self.header_js) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) if self.config["dark"]: self.themes_btn = '<a id="themes" class="light"></a>' else: self.themes_btn = "" self.html_js_items = self._generate_html_js_items()
def on_init(self, config, doc_src_path, site_config, logger = None, multiprocess = True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config # check config for key in self.config["env"]: if not key in config["env"]: self.logger.e('you MUST set env var "{}" for gitalk plugin in site_config'.format(key)) self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format(self.name, self.config)) self.files_to_copy = {} self.html_header_items = [] self.html_footer_items = [] for item in self.config["header_items"]: if item.startswith("/"): path = os.path.join(self.doc_src_path, item[1:]) if os.path.exists(path): if path.endswith(".js"): self.html_header_items.append(f'<script src="{item}"></script>') self.files_to_copy[item] = path elif path.endswith(".css"): self.html_header_items.append(f'<link rel="stylesheet" href="{item}" type="text/css"/>') self.files_to_copy[item] = path else: self.logger.e(f"config: url {item} not support! you can use html tag instead") else: self.logger.e(f"config: url {item} wrong, file {path} no found ") else: self.html_header_items.append(item) for item in self.config["footer_items"]: if item.startswith("/"): path = os.path.join(self.doc_src_path, item[1:]) if os.path.exists(path): if path.endswith(".js"): self.html_footer_items.append(f'<script src="{item}"></script>') self.files_to_copy[item] = path elif path.endswith(".css"): self.html_footer_items.append(f'<link rel="stylesheet" href="{item}" type="text/css"/>') self.files_to_copy[item] = path else: self.logger.e(f"config: url {item} not support! you can use html tag instead") else: self.logger.e(f"config: url {item} wrong, file {path} no found ") elif item.startswith("http"): if item.endswith(".js"): self.html_footer_items.append(f'<script src="{item}"></script>') elif item.endswith(".css"): self.html_footer_items.append(f'<link rel="stylesheet" href="{item}" type="text/css"/>') else: self.logger.e(f"config: url {item} not support! you can use html tag instead") else: self.html_footer_items.append(item) self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_assets") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.files_to_copy = self._update_file_var(self.files_to_copy, self.config["env"], self.temp_dir)
class Plugin(Plugin_Base): name = "teedoc-plugin-assets" desc = "add assets(css js) support for teedoc" defautl_config = { "header_items": [], "footer_items": [], "env":{} } def on_init(self, config, doc_src_path, site_config, logger = None, multiprocess = True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config # check config for key in self.config["env"]: if not key in config["env"]: self.logger.e('you MUST set env var "{}" for gitalk plugin in site_config'.format(key)) self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format(self.name, self.config)) self.files_to_copy = {} self.html_header_items = [] self.html_footer_items = [] for item in self.config["header_items"]: if item.startswith("/"): path = os.path.join(self.doc_src_path, item[1:]) if os.path.exists(path): if path.endswith(".js"): self.html_header_items.append(f'<script src="{item}"></script>') self.files_to_copy[item] = path elif path.endswith(".css"): self.html_header_items.append(f'<link rel="stylesheet" href="{item}" type="text/css"/>') self.files_to_copy[item] = path else: self.logger.e(f"config: url {item} not support! you can use html tag instead") else: self.logger.e(f"config: url {item} wrong, file {path} no found ") else: self.html_header_items.append(item) for item in self.config["footer_items"]: if item.startswith("/"): path = os.path.join(self.doc_src_path, item[1:]) if os.path.exists(path): if path.endswith(".js"): self.html_footer_items.append(f'<script src="{item}"></script>') self.files_to_copy[item] = path elif path.endswith(".css"): self.html_footer_items.append(f'<link rel="stylesheet" href="{item}" type="text/css"/>') self.files_to_copy[item] = path else: self.logger.e(f"config: url {item} not support! you can use html tag instead") else: self.logger.e(f"config: url {item} wrong, file {path} no found ") elif item.startswith("http"): if item.endswith(".js"): self.html_footer_items.append(f'<script src="{item}"></script>') elif item.endswith(".css"): self.html_footer_items.append(f'<link rel="stylesheet" href="{item}" type="text/css"/>') else: self.logger.e(f"config: url {item} not support! you can use html tag instead") else: self.html_footer_items.append(item) self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_assets") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.files_to_copy = self._update_file_var(self.files_to_copy, self.config["env"], self.temp_dir) def on_add_html_header_items(self, type_name): return self.html_header_items def on_add_html_footer_js_items(self, type_name): return self.html_footer_items def on_copy_files(self): res = self.files_to_copy self.files_to_copy = {} return res def on_del(self): if os.path.exists(self.temp_dir): try: shutil.rmtree(self.temp_dir) except Exception: pass def _update_file_var(self, files, vars, temp_dir): for url, path in files.items(): with open(path, encoding='utf-8') as f: content = f.read() for k, v in vars.items(): content = content.replace("${}{}{}".format("{", k.strip(), "}"), str(v)) temp_path = os.path.join(temp_dir, os.path.basename(path)) with open(temp_path, "w", encoding='utf-8') as fw: fw.write(content) files[url] = temp_path return files
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config env = self.config["env"] if "env" in config: env.update(config["env"]) self.config.update(config) self.config["env"] = env if self.config["mobile_navbar_collapsed"]: self.config["env"]["mobile_navbar_collapsed"] = "none" else: self.config["env"]["mobile_navbar_collapsed"] = "block" if self.config["dark"] and self.config["default_dark"]: self.config["env"]["default_theme"] = "dark" else: self.config["env"]["default_theme"] = "light" if self.config["show_print_page"]: self.config["env"]["show_print_page"] = "true" else: self.config["env"]["show_print_page"] = "false" self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.dark_css = { "/static/css/theme_default/dark.css": os.path.join(self.assets_abs_path, "dark.css") } self.light_css = { "/static/css/theme_default/light.css": os.path.join(self.assets_abs_path, "light.css") } # code hilight css file if "code_highlight_css" in self.config and self.config[ "code_highlight_css"]: self.css = {} self.code_highlight_css = self.config["code_highlight_css"] else: self.code_highlight_css = None self.css = { "/static/css/theme_default/prism.min.css": os.path.join(self.assets_abs_path, "prism.min.css"), } # image viewer self.css["/static/css/theme_default/viewer.min.css"] = os.path.join( self.assets_abs_path, "viewer.min.css") # js files self.dark_js = {} self.light_js = {} self.header_js = { "/static/js/theme_default/split.js": os.path.join(self.assets_abs_path, "split.js"), "/static/js/theme_default/jquery.min.js": os.path.join(self.assets_abs_path, "jquery.min.js"), "/static/js/theme_default/pre_main.js": os.path.join(self.assets_abs_path, "pre_main.js") } self.footer_js = { "/static/js/theme_default/tocbot.min.js": os.path.join(self.assets_abs_path, "tocbot.min.js"), "/static/js/theme_default/main.js": os.path.join(self.assets_abs_path, "main.js"), "/static/js/theme_default/viewer.min.js": os.path.join(self.assets_abs_path, "viewer.min.js") } # code hilight js file if "code_highlight_js" in self.config and self.config[ "code_highlight_js"]: self.code_highlight_js = self.config["code_highlight_js"] else: self.code_highlight_js = None self.footer_js[ "/static/css/theme_default/prism.min.js"] = os.path.join( self.assets_abs_path, "prism.min.js") self.images = { "/static/image/theme_default/indicator.svg": os.path.join(self.assets_abs_path, "indicator.svg"), "/static/image/theme_default/menu.svg": os.path.join(self.assets_abs_path, "menu.svg"), "/static/image/theme_default/to-top.svg": os.path.join(self.assets_abs_path, "to-top.svg"), "/static/image/theme_default/light_mode.svg": os.path.join(self.assets_abs_path, "light_mode.svg"), "/static/image/theme_default/dark_mode.svg": os.path.join(self.assets_abs_path, "dark_mode.svg"), "/static/image/theme_default/print.svg": os.path.join(self.assets_abs_path, "print.svg") } # set site_root_url env value self.config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = self.config["env"] self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_theme_default") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.dark_css = self._update_file_var(self.dark_css, vars, self.temp_dir) self.light_css = self._update_file_var(self.light_css, vars, self.temp_dir) self.css = self._update_file_var(self.css, vars, self.temp_dir) self.header_js = self._update_file_var(self.header_js, vars, self.temp_dir) self.footer_js = self._update_file_var(self.footer_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} if self.config["dark"]: self.files_to_copy.update(self.dark_css) self.files_to_copy.update(self.dark_js) self.files_to_copy.update(self.light_css) self.files_to_copy.update(self.css) self.files_to_copy.update(self.light_js) self.files_to_copy.update(self.header_js) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) if self.config["dark"]: self.themes_btn = '<a id="themes" class="light"></a>' else: self.themes_btn = "" self.html_js_items = self._generate_html_js_items()
class Plugin(Plugin_Base): name = "teedoc-plugin-markdown-parser" desc = "markdown parser plugin for teedoc" defautl_config = { "parse_files": ["md"], "mathjax": { "enable": True, "file_name": "tex-mml-chtml", # http://docs.mathjax.org/en/latest/web/components/index.html "config": { "loader": { "load": ['output/svg'] }, "tex": { "inlineMath": [['$', '$'], ['\\(', '\\)']] }, "svg": { "fontCache": 'global' } } } } def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.multiprocess = multiprocess self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config mathjax_config = self.config["mathjax"] if "mathjax" in config: for k, v in config["mathjax"].items(): if type(v) != dict: mathjax_config[k] = v else: mathjax_config[k].update(v) self.config.update(config) self.config["mathjax"] = mathjax_config self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) if not self.multiprocess: from .renderer import create_markdown_parser from .parse_metadata import Meta_Parser self.create_markdown_parser = create_markdown_parser self.Meta_Parser = Meta_Parser def on_new_process_init(self): ''' for multiple processing, for below func, will be called in new process, every time create a new process, this func will be invoke ''' from .renderer import create_markdown_parser from .parse_metadata import Meta_Parser self.md_parser = create_markdown_parser() self.meta_parser = Meta_Parser() def on_new_process_del(self): ''' for multiple processing, for below func, will be called in new process, every time exit a new process, this func will be invoke ''' del self.md_parser del self.meta_parser def on_parse_files(self, files): # result, format must be this result = {"ok": False, "msg": "", "htmls": OrderedDict()} # function parse md file is disabled if not "md" in self.config["parse_files"]: result[ "msg"] = "disabled markdown parse, but only support markdown" return result self.logger.d("-- plugin <{}> parse {} files".format( self.name, len(files))) # self.logger.d("files: {}".format(files)) for file in files: ext = os.path.splitext(file)[1].lower() if ext.endswith("md"): with open(file, encoding="utf-8") as f: content = f.read().strip() content = self._update_link(content) try: if not self.multiprocess: md_parser = self.create_markdown_parser() meta_parser = self.Meta_Parser() else: md_parser = self.md_parser meta_parser = self.meta_parser metadata, content_no_meta = meta_parser.parse_meta( content) html = md_parser(content_no_meta) except Exception as e: import io, traceback traceback.print_exc() self.logger.w( "parse markdown file {} fail, please check markdown content format" .format(file)) continue if "title" in metadata: title = metadata["title"] else: title = "" if "keywords" in metadata and not metadata[ "keywords"].strip() == "": keywords = metadata["keywords"].split(",") else: keywords = [] if "tags" in metadata and not metadata["tags"].strip( ) == "": tags = metadata["tags"].split(",") else: tags = [] if "desc" in metadata: desc = metadata["desc"] else: desc = "" date = None ts = int(os.stat(file).st_mtime) if "date" in metadata: date = metadata["date"].strip().lower() # set date to false to disable date display if date and (date == "false" or date == "none"): date = "" else: GMT_FORMAT = '%Y-%m-%d' try: date_obj = datetime.strptime(date, GMT_FORMAT) ts = int(date_obj.timestamp()) except Exception as e: pass if "author" in metadata: author = metadata["author"] else: author = "" result["htmls"][file] = { "title": title, "desc": desc, "keywords": keywords, "tags": tags, "body": html, "date": date, "ts": ts, "author": author, # "toc": html.toc_html if html.toc_html else "", "toc": "", # just empty, toc generated by js but not python "metadata": metadata, "raw": content } else: result["htmls"][file] = None result['ok'] = True return result def on_parse_pages(self, files): result = self.on_parse_files(files) return result def on_add_html_header_items(self, type_name): items = [] items.append( '<meta name="markdown-generator" content="teedoc-plugin-markdown-parser">' ) if self.config["mathjax"]["enable"]: items.append('''<script> MathJax = {}; </script>'''.format(json.dumps(self.config["mathjax"]["config"]))) items.append( '<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>' ) items.append( '<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/{}.js"></script>' .format(self.config["mathjax"]["file_name"])) return items def _update_link(self, content): def re_del(c): ret = c[0] links = re.findall('\((.*?)\)', c[0]) if len(links) > 0: for link in links: if link.startswith(".") or os.path.isabs(link): ret = re.sub("README.md", "index.html", c[0], flags=re.I) ret = re.sub(r".md", ".html", ret, re.I) return ret return ret def re_del_ipynb(c): ret = c[0] links = re.findall('\((.*?)\)', c[0]) if len(links) > 0: for link in links: if link.startswith(".") or os.path.isabs(link): ret = re.sub("README.ipynb", "index.html", c[0], flags=re.I) ret = re.sub(r".ipynb", ".html", ret, re.I) return ret return ret # <a class="anchor-link" href="#链接"> </a></h2><p><a href="./syntax_markdown.md">markdown 语法</a> content = re.sub(r'\[.*?\]\(.*?\.md\)', re_del, content, flags=re.I) content = re.sub(r'\[.*?\]\(.*?\.ipynb\)', re_del_ipynb, content, flags=re.I) return content
class Plugin(Plugin_Base): name = "teedoc-plugin-comments-gitalk" desc = "gitalk comment support for teedoc" defautl_config = { "contrainer": "comments-container", "env": { "clientID": 'GitHub Application Client ID', "clientSecret": 'GitHub Application Client Secret', "repo": 'GitHub repo', "owner": 'GitHub repo owner', "admin": [ 'GitHub repo owner and collaborators, only these guys can initialize github issues' ], # "id": location.pathname, // Ensure uniqueness and length less than 50 # "distractionFreeMode": false // Facebook-like distraction free mode # "main_color": "#4caf7d", # "second_color": "#0a7d43" }, } def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config # check config for key in self.config["env"]: if not key in config["env"]: self.logger.e( 'you MUST set env var "{}" for gitalk plugin in site_config' .format(key)) self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.files_to_copy = { "/static/js/gitalk/gitalk.min.js": os.path.join(self.assets_abs_path, "gitalk.min.js"), "/static/js/gitalk/main.js": os.path.join(self.assets_abs_path, "main.js"), "/static/css/gitalk/gitalk.css": os.path.join(self.assets_abs_path, "gitalk.css") } self.html_header_items = [ '<link rel="stylesheet" href="{}" type="text/css"/>'.format( "/static/css/gitalk/gitalk.css"), ] self.html_js_items = [ '<script src="{}"></script>'.format( "/static/js/gitalk/gitalk.min.js"), '<script src="{}"></script>'.format("/static/js/gitalk/main.js") ] self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_comments_gitalk") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) # custom main color custom_color_vars = {} if "main_color" in self.config["env"]: self.logger.i("-- plugin <{}> get main_color {}".format( self.name, self.config["env"]["main_color"])) self.html_header_items.append( '<link rel="stylesheet" href="{}" type="text/css"/>'.format( "/static/css/gitalk/custom_gitalk.css")) self.files_to_copy[ "/static/css/gitalk/custom_gitalk.css"] = os.path.join( self.assets_abs_path, "custom_gitalk.css") # remove color vars from env, for env used by js if not "second_color" in self.config["env"]: self.config["env"]["second_color"] = self.config["env"][ "main_color"] custom_color_vars["main_color"] = self.config["env"]["main_color"] custom_color_vars["second_color"] = self.config["env"][ "second_color"] self.config["env"].pop("main_color") self.config["env"].pop("second_color") else: self.logger.i("-- plugin <{}> use default color") vars = { "comment_contrainer_id": self.config["contrainer"], "config": json.dumps(self.config["env"]) } vars.update(custom_color_vars) self.files_to_copy = self._update_file_var(self.files_to_copy, vars, self.temp_dir) def on_add_html_header_items(self, type_name): return self.html_header_items def on_add_html_footer_js_items(self, type_name): return self.html_js_items def on_copy_files(self): res = self.files_to_copy self.files_to_copy = {} return res def on_del(self): if os.path.exists(self.temp_dir): try: shutil.rmtree(self.temp_dir) except Exception: pass def _update_file_var(self, files, vars, temp_dir): for url, path in files.items(): with open(path, encoding='utf-8') as f: content = f.read() for k, v in vars.items(): content = content.replace( "${}{}{}".format("{", k.strip(), "}"), str(v)) temp_path = os.path.join(temp_dir, os.path.basename(path)) with open(temp_path, "w", encoding='utf-8') as fw: fw.write(content) files[url] = temp_path return files
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) if not self.config["content_type"] in self.supported_content_type: self.logger.e( "-- plugin <{}> config content_type error: {}, should be in {}" .format(self.name, self.config["content_type"], self.supported_content_type)) if self.config["content_type"] == "raw": self.content_from = "raw" else: self.content_from = "body" self.module_path = os.path.abspath( os.path.dirname(os.path.abspath(__file__))) self.assets_abs_path = os.path.join(self.module_path, "assets") self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_search") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.css = { "/static/css/search/style.css": os.path.join(self.assets_abs_path, "style.css"), } self.footer_js = { "/static/js/search/search_main.js": os.path.join(self.assets_abs_path, "search_main.js") } self.images = { "/static/image/search/close.svg": os.path.join(self.assets_abs_path, "close.svg"), "/static/image/search/search.svg": os.path.join(self.assets_abs_path, "search.svg"), } # set site_root_url env value if not "env" in self.config: self.config['env'] = {} self.config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = self.config["env"] self.css = self._update_file_var(self.css, vars, self.temp_dir) self.footer_js = self._update_file_var(self.footer_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} self.files_to_copy.update(self.css) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) self.html_js_items = self._generate_html_js_items() self.content = {"articles": {}, "pages": {}} self.docs_name = {}
class Plugin(Plugin_Base): name = "teedoc-plugin-search" desc = "search support for teedoc" defautl_config = { "content_type": "raw", # supported_content_type "search_hint": "Search", "input_hint": "Keywords separated by space", "loading_hint": "Loading, wait please ...", "download_err_hint": "Download error, please check network and refresh again", "other_docs_result_hint": "Result from other docs", "curr_doc_result_hint": "Result from current doc", "env": { "main_color": "#4caf7d", "main_color_dark": "#1b4c33", "hint_shadow_color": "rgba(76, 175, 125, 0.38)" } } supported_content_type = ["raw", "html"] def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) if not self.config["content_type"] in self.supported_content_type: self.logger.e( "-- plugin <{}> config content_type error: {}, should be in {}" .format(self.name, self.config["content_type"], self.supported_content_type)) if self.config["content_type"] == "raw": self.content_from = "raw" else: self.content_from = "body" self.module_path = os.path.abspath( os.path.dirname(os.path.abspath(__file__))) self.assets_abs_path = os.path.join(self.module_path, "assets") self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_search") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.css = { "/static/css/search/style.css": os.path.join(self.assets_abs_path, "style.css"), } self.footer_js = { "/static/js/search/search_main.js": os.path.join(self.assets_abs_path, "search_main.js") } self.images = { "/static/image/search/close.svg": os.path.join(self.assets_abs_path, "close.svg"), "/static/image/search/search.svg": os.path.join(self.assets_abs_path, "search.svg"), } # set site_root_url env value if not "env" in self.config: self.config['env'] = {} self.config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = self.config["env"] self.css = self._update_file_var(self.css, vars, self.temp_dir) self.footer_js = self._update_file_var(self.footer_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} self.files_to_copy.update(self.css) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) self.html_js_items = self._generate_html_js_items() self.content = {"articles": {}, "pages": {}} self.docs_name = {} def on_del(self): if os.path.exists(self.temp_dir): try: shutil.rmtree(self.temp_dir) except Exception: pass def _generate_html_header_items(self): items = [] # css for url in self.css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( url) items.append(item) return items def _generate_html_js_items(self): items = [] for url in self.footer_js: item = '<script src="{}"></script>'.format(url) items.append(item) return items def _update_file_var(self, files, vars, temp_dir): for url, path in files.items(): with open(path, encoding='utf-8') as f: content = f.read() for k, v in vars.items(): content = content.replace( "${}{}{}".format("{", k.strip(), "}"), v) temp_path = os.path.join(temp_dir, os.path.basename(path)) with open(temp_path, "w", encoding='utf-8') as fw: fw.write(content) files[url] = temp_path return files def _get_conf(self, config, new_config, conf_name): if conf_name in new_config: return True, new_config[conf_name] return False, config[conf_name] def on_parse_start(self, type_name, url, dirs, doc_config, new_config): if not "name" in doc_config: self.logger.w(f'doc dir "{dirs[0]}"\'s config no "name" keyword') self.docs_name[url] = url else: self.docs_name[url] = doc_config["name"] self.doc_locale = doc_config[ "locale"] if "locale" in doc_config else None # self.new_config = copy.deepcopy(self.config) # self.new_config = update_config(self.new_config, new_config) self.new_config = new_config def on_add_html_header_items(self, type_name): return self.html_header_items def on_add_html_footer_js_items(self, type_name): return self.html_js_items def on_add_navbar_items(self): ''' @config config cover self.config ''' # i18n # set default or custom settings names = [ "search_hint", "input_hint", "loading_hint", "download_err_hint", "other_docs_result_hint", "curr_doc_result_hint" ] flags = [False] * len(names) # flags have costum settings configs = [""] * len(names) for i, conf in enumerate(names): flags[i], configs[i] = self._get_conf(self.config, self.new_config, conf) # get translation if don't have custom setting if False in flags and self.doc_locale: import gettext try: lang = gettext.translation('messages', localedir=os.path.join( self.module_path, 'locales'), languages=[self.doc_locale]) lang.install() _ = lang.gettext if not flags[0]: configs[0] = _("search_hint") if not flags[1]: configs[1] = _("search_input_hint") if not flags[2]: configs[2] = _("search_loading_hint") if not flags[3]: configs[3] = _("search_download_err_hint") if not flags[4]: configs[4] = _("search_other_docs_result_hint") if not flags[5]: configs[5] = _("search_curr_doc_result_hint") except Exception as e: pass configs = tuple(configs) search_btn = '''<a id="search"><span class="icon"></span><span class="placeholder">%s</span> <div id="search_hints"> <span id="search_input_hint">%s</span> <span id="search_loading_hint">%s</span> <span id="search_download_err_hint">%s</span> <span id="search_other_docs_result_hint">%s</span> <span id="search_curr_doc_result_hint">%s</span> </div></a>''' % configs items = [search_btn] return items def on_copy_files(self): res = self.files_to_copy self.files_to_copy = {} return res def on_htmls(self, htmls_files, htmls_pages, htmls_blog=None): ''' update htmls, may not all html, just partially htmls_files: { "/get_started/zh":{ "url":{ "title": "", "desc": "", "keywords": [], "body": html, "url": "", "raw": "" } } } ''' # for file, html in htmls_files.items(): # self.content["articles"][html["url"]] = html["raw"] # for file, html in htmls_pages.items(): # self.content["pages"][html["url"]] = html["raw"] self.logger.i("generate search index") if htmls_blog: htmls_pages.update(copy.deepcopy(htmls_blog)) docs_url = list(htmls_files.keys()) pages_url = list(htmls_pages.keys()) index_content = {} sub_index_path = [] generated_index_json = {} for i, url in enumerate(docs_url): index_content[url] = [ self.docs_name[url], "{}static/search_index/index_{}.json".format( self.site_config["site_root_url"], i) ] path = os.path.join(self.temp_dir, "index_{}.json".format(i)) sub_index_path.append(path) # write content to sub index file content = {} for page_url in htmls_files[url]: content[page_url] = { "title": htmls_files[url][page_url]["title"], "content": htmls_files[url][page_url][self.content_from] } with open(path, "w", encoding="utf-8") as f: json.dump(content, f, ensure_ascii=False) for i, url in enumerate(pages_url, len(docs_url)): index_content[url] = [ self.docs_name[url], "{}static/search_index/index_{}.json".format( self.site_config["site_root_url"], i) ] path = os.path.join(self.temp_dir, "index_{}.json".format(i)) sub_index_path.append(path) # write content to sub index file content = {} for page_url in htmls_pages[url]: content[page_url] = { "title": htmls_pages[url][page_url]["title"], "content": htmls_pages[url][page_url][self.content_from] } with open(path, "w", encoding="utf-8") as f: json.dump(content, f, ensure_ascii=False) # write content to files # index file index_path = os.path.join(self.temp_dir, "index.json") with open(index_path, "w", encoding="utf-8") as f: json.dump(index_content, f, ensure_ascii=False) # add to copy file list generated_index_json["/static/search_index/index.json"] = index_path for i, path in enumerate(sub_index_path): generated_index_json["/static/search_index/index_{}.json".format( i)] = path self.files_to_copy.update(generated_index_json) return True
class Plugin(Plugin_Base): name = "teedoc-plugin-google-translate" desc = "Google translate support for teedoc" defautl_config = { "lang": "auto", # source page language "doc_types": ["page", "doc", "blog"], "domain": "/" # translate.google.com / translate.google.cn } def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.module_path = os.path.abspath( os.path.dirname(os.path.abspath(__file__))) self.assets_abs_path = os.path.join(self.module_path, "assets") def on_del(self): pass def on_copy_files(self): res = { "/static/image/google_translate/translate.svg": os.path.join(self.assets_abs_path, "translate.svg"), "/static/image/google_translate/cleardot.gif": os.path.join(self.assets_abs_path, "cleardot.gif"), "/static/js/google_translate/main.js": os.path.join(self.assets_abs_path, "main.js"), "/static/js/google_translate/element_main.js": os.path.join(self.assets_abs_path, "element_main.js"), "/static/js/google_translate/element.js": os.path.join(self.assets_abs_path, "element.js"), } return res def on_parse_start(self, type_name, url, dirs, doc_config, new_config): # self.doc_locale = doc_config["locale"] if "locale" in doc_config else None self.new_config = copy.deepcopy(self.config) self.new_config = update_config(self.new_config, new_config) self.type_name = type_name def on_add_html_header_items(self, type_name): return [] def on_add_html_footer_js_items(self, type_name): if not type_name in self.new_config["doc_types"]: return [] lang = self.new_config["lang"] domain = self.new_config["domain"] return [ '''<script type="text/javascript"> var transLoaded = false; var loading = false; var domain = "''' + domain + '''"; var domainDefault = domain; var storeDomain = localStorage.getItem("googleTransDomain"); if(storeDomain){ domain = storeDomain; } function getUrl(domain){ if(domain == "/") return "/static/js/google_translate/element.js?cb=googleTranslateElementInit"; else return "https://" + domain + "/translate_a/element.js?cb=googleTranslateElementInit"; } var url = getUrl(domain); function googleTranslateElementInit() { new google.translate.TranslateElement({pageLanguage: "''' + lang + '''", layout: google.translate.TranslateElement.InlineLayout.SIMPLE}, 'google_translate_element'); } function loadJS( url, callback ){ var script = document.createElement('script'); fn = callback || function(){ }; script.type = 'text/javascript'; if(script.readyState){ script.onreadystatechange = function(){ if( script.readyState == 'loaded' || script.readyState == 'complete' ){ script.onreadystatechange = null; fn(); } }; }else{ script.onload = function(){ fn(); }; } script.src = url; document.getElementsByTagName('head')[0].appendChild(script); } function removeHint(){ var hint = document.getElementById("loadingTranslate"); if(hint){ hint.remove(); } } var btn = document.getElementById("google_translate_element"); btn.onclick = function(){ if(transLoaded) return; if(loading){ var flag = confirm("loading from " + domain + ", please wait, or change domain?"); if(flag){ newDomain = prompt("domain, default: " + domainDefault + ", now: " + domain); if(newDomain){ domain = newDomain; console.log(domain); url = getUrl(domain); loadJS(url, function(){ localStorage.setItem("googleTransDomain", domain); removeHint() transLoaded = true; }); } } return; } btn.innerHTML = '<span id="loadingTranslate"><img class="icon" src="/static/image/google_translate/translate.svg"/>Loading ...</span>'; loading = true; loadJS(url, function(){ localStorage.setItem("googleTransDomain", domain); removeHint() transLoaded = true; }); } </script> ''', # f'<script type="text/javascript" src="//{domain}/translate_a/element.js?cb=googleTranslateElementInit"></script>' ] def on_add_navbar_items(self): if not self.type_name in self.new_config["doc_types"]: return [] trans_btn = '<a id="google_translate_element"><img class="icon" src="/static/image/google_translate/translate.svg"/>Translate</a>' items = [trans_btn] return items
class Plugin(Plugin_Base): name = "teedoc-plugin-blog" desc = "markdown parser plugin for teedoc" defautl_config = {"parse_files": ["md"]} def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.multiprocess = multiprocess self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_blog") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.assets = { "/static/js/plugin_blog/main.js": os.path.join(self.assets_abs_path, "main.js"), } vars = {"site_root_url": self.site_config["site_root_url"]} self.assets = self._update_file_var(self.assets, vars, self.temp_dir) self.files_to_copy = self.assets.copy() # must use copy blog_url = list(self.site_config["route"]["blog"].keys()) if len(blog_url) > 1: self.logger.e("only support one blog url path") raise Exception("only support one blog url path") self.blog_url = blog_url[0] self.blog_dir = os.path.join( self.doc_src_path, self.site_config["route"]["blog"][self.blog_url]).replace( "\\", "/") self.index_content = {"items": {}} def on_new_process_init(self): ''' for multiple processing, for below func, will be called in new process, every time create a new process, this func will be invoke ''' self.md_parser = create_markdown_parser() self.meta_parser = Meta_Parser() def on_new_process_del(self): ''' for multiple processing, for below func, will be called in new process, every time exit a new process, this func will be invoke ''' del self.md_parser del self.meta_parser def on_parse_blog(self, files): # result, format must be this result = {"ok": False, "msg": "", "htmls": OrderedDict()} # function parse md file is disabled if not "md" in self.config["parse_files"]: result[ "msg"] = "disabled markdown parse, but only support markdown" return result self.logger.d("-- plugin <{}> parse {} files".format( self.name, len(files))) # self.logger.d("files: {}".format(files)) for file in files: ext = os.path.splitext(file)[1].lower() if ext.endswith("md"): with open(file, encoding="utf-8") as f: content = f.read().strip() content = self._update_link(content) blog_index_file_path = os.path.join( self.blog_dir, "readme.md").replace("\\", "/").lower() is_blog_index = file.lower() == blog_index_file_path if is_blog_index: content += '\n<div id="blog_list"></div>' if not self.multiprocess: md_parser = create_markdown_parser() meta_parser = Meta_Parser() else: md_parser = self.md_parser meta_parser = self.meta_parser metadata, content_no_meta = meta_parser.parse_meta(content) html = md_parser(content_no_meta) if "<!-- more -->" in html: brief = html[:html.find("<!-- more -->")].strip() else: brief = html[:500].strip() if "title" in metadata: title = metadata["title"] else: title = "" if "keywords" in metadata and not metadata[ "keywords"].strip() == "": keywords = metadata["keywords"].split(",") else: keywords = [] if "tags" in metadata and not metadata["tags"].strip( ) == "": tags = metadata["tags"].split(",") else: tags = [] if "desc" in metadata: desc = metadata["desc"] else: desc = "" html_str = '<span id="blog_start"></span>' + html # date default last edit time ts = int(os.stat(file).st_mtime) date_file_edit = time.strftime("%Y-%m-%d", time.localtime(ts)) if "date" in metadata: date = metadata["date"].strip().lower() # set date to false to disable date display if date and (date == "false" or date == "none"): date = "" else: GMT_FORMAT = '%Y-%m-%d' try: date_obj = datetime.strptime(date, GMT_FORMAT) ts = int(date_obj.timestamp()) except Exception as e: date = date_file_edit else: date = date_file_edit if "author" in metadata: author = metadata["author"] else: author = "" result["htmls"][file] = { "title": title, "desc": desc, "keywords": keywords, "tags": tags, "body": html_str, # "toc": html.toc_html if html.toc_html else "", "toc": "", # just empty, toc generated by js but not python "metadata": metadata, "raw": content, "date": date, "ts": ts, "author": author, "brief": brief } else: result["htmls"][file] = None result['ok'] = True return result def on_add_html_header_items(self, type_name): items = [] items.append( '<meta name="blog-generator" content="teedoc-plugin-blog">') return items def _update_link(self, content): def re_del(c): ret = c[0] links = re.findall('\((.*?)\)', c[0]) if len(links) > 0: for link in links: if link.startswith(".") or os.path.isabs(link): ret = re.sub("README.md", "index.html", c[0], flags=re.I) ret = re.sub(r".md", ".html", ret, re.I) return ret return ret def re_del_ipynb(c): ret = c[0] links = re.findall('\((.*?)\)', c[0]) if len(links) > 0: for link in links: if link.startswith(".") or os.path.isabs(link): ret = re.sub("README.ipynb", "index.html", c[0], flags=re.I) ret = re.sub(r".ipynb", ".html", ret, re.I) return ret return ret # <a class="anchor-link" href="#链接"> </a></h2><p><a href="./syntax_markdown.md">markdown 语法</a> content = re.sub(r'\[.*?\]\(.*?\.md\)', re_del, content, flags=re.I) content = re.sub(r'\[.*?\]\(.*?\.ipynb\)', re_del_ipynb, content, flags=re.I) return content def on_htmls(self, htmls_files, htmls_pages, htmls_blog=None): ''' update htmls, may not all html, just partially htmls_blog: { "/blog/":{ "url":{ "title": "", "desc": "", "keywords": [], "body": html, "tags": [], "url": "", "raw": "", "date": date, "ts": 12344566, "author": author, "brief": "", "metadata": {} } } } ''' if not htmls_blog: return True blog_url = list(htmls_blog.keys()) if len(blog_url) == 0: return True index_url = "" blog_url = blog_url[0] index_url = "{}static/blog_index/index.json".format( self.site_config["site_root_url"]) index_path = os.path.join(self.temp_dir, "index.json") for url, item in htmls_blog[blog_url].items(): # except blog index.html if url == os.path.join(blog_url, "index.html"): continue new_item = { "title": item["title"], "desc": item["desc"], "keywords": item["keywords"], "tags": item["tags"], "url": url, "date": item["date"], "ts": item["ts"], "author": item["author"], "brief": item["brief"], } self.index_content["items"][url] = new_item # sort by date self.index_content["items"] = OrderedDict( sorted(self.index_content["items"].items(), key=lambda v: v[1]["ts"], reverse=True)) # write content to sub index file with open(index_path, "w", encoding="utf-8") as f: json.dump(self.index_content, f, ensure_ascii=False) # add to copy file list generated_index_json = {"/static/blog_index/index.json": index_path} self.files_to_copy.update(generated_index_json) return True def on_copy_files(self): res = self.files_to_copy self.files_to_copy = {} return res def on_add_html_footer_js_items(self, type_name): for url in self.assets: html_js_items = ['<script src="{}"></script>'.format(url)] return html_js_items def _update_file_var(self, files, vars, temp_dir): for url, path in files.items(): with open(path, encoding='utf-8') as f: content = f.read() for k, v in vars.items(): content = content.replace( "${}{}{}".format("{", k.strip(), "}"), v) temp_path = os.path.join(temp_dir, os.path.basename(path)) with open(temp_path, "w", encoding='utf-8') as fw: fw.write(content) files[url] = temp_path return files
def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config # check config for key in self.config["env"]: if not key in config["env"]: self.logger.e( 'you MUST set env var "{}" for gitalk plugin in site_config' .format(key)) self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.files_to_copy = { "/static/js/gitalk/gitalk.min.js": os.path.join(self.assets_abs_path, "gitalk.min.js"), "/static/js/gitalk/main.js": os.path.join(self.assets_abs_path, "main.js"), "/static/css/gitalk/gitalk.css": os.path.join(self.assets_abs_path, "gitalk.css") } self.html_header_items = [ '<link rel="stylesheet" href="{}" type="text/css"/>'.format( "/static/css/gitalk/gitalk.css"), ] self.html_js_items = [ '<script src="{}"></script>'.format( "/static/js/gitalk/gitalk.min.js"), '<script src="{}"></script>'.format("/static/js/gitalk/main.js") ] self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_comments_gitalk") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) # custom main color custom_color_vars = {} if "main_color" in self.config["env"]: self.logger.i("-- plugin <{}> get main_color {}".format( self.name, self.config["env"]["main_color"])) self.html_header_items.append( '<link rel="stylesheet" href="{}" type="text/css"/>'.format( "/static/css/gitalk/custom_gitalk.css")) self.files_to_copy[ "/static/css/gitalk/custom_gitalk.css"] = os.path.join( self.assets_abs_path, "custom_gitalk.css") # remove color vars from env, for env used by js if not "second_color" in self.config["env"]: self.config["env"]["second_color"] = self.config["env"][ "main_color"] custom_color_vars["main_color"] = self.config["env"]["main_color"] custom_color_vars["second_color"] = self.config["env"][ "second_color"] self.config["env"].pop("main_color") self.config["env"].pop("second_color") else: self.logger.i("-- plugin <{}> use default color") vars = { "comment_contrainer_id": self.config["contrainer"], "config": json.dumps(self.config["env"]) } vars.update(custom_color_vars) self.files_to_copy = self._update_file_var(self.files_to_copy, vars, self.temp_dir)
class Plugin(Plugin_Base): name = "teedoc-plugin-ad-hint" desc = "advertisement adn hint support for teedoc" defautl_config = { "type": "hint", # new warning ad "label": "New", # "brief": "", "content": "", # "target": "_self", # "url": "#", "show_times": 2, # disapear after visit show_times pages, always show if <= 0 "show_after_s": 60 * 60 * 24 * 5, # show again after 5 days "date": None, # latest hint date, if changed, message will automatically show, if now datetime < date, hint always show "color": "#a0421d", "link_color": "#e53935", "link_bg_color": "#e6ae5c", "bg_color": "#ffcf89", "color_hover": "white", "bg_color_hover": "#f57c00", "close_color": "#eab971" } def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.module_path = os.path.abspath( os.path.dirname(os.path.abspath(__file__))) self.assets_abs_path = os.path.join(self.module_path, "assets") self.temp_dir = self.get_temp_dir() self.footer_js = { # don't use ad(advertisement) keyword, may blocked by browser plugin "/static/js/add_hint/style.css": os.path.join(self.assets_abs_path, "style.css"), "/static/js/add_hint/main.js": os.path.join(self.assets_abs_path, "main.js") } self.html_footer_items = [] for url in self.footer_js: if url.endswith(".css"): item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( url) else: item = '<script src="{}"></script>'.format(url) self.html_footer_items.append(item) vars = self.config self.footer_js = self.update_file_var(self.footer_js, vars, self.temp_dir) self.files_to_copy = self.footer_js def on_parse_start(self, type_name, url, dirs, doc_config, new_config): ''' call when start parse one doc @type_name canbe "doc" "page" "blog" #url doc url, e.g. /get_started/zh/ @doc_config config of doc, get from config.json or config.yaml @new_config this plugin's config from doc_config ''' # can update plugin config from site_config with new_config by teedoc.utils.update_config self.new_config = copy.deepcopy(self.config) self.new_config = update_config(self.new_config, new_config) def on_add_html_footer_js_items(self, type_name): return self.html_footer_items def on_copy_files(self): res = self.files_to_copy self.files_to_copy = {} return res def on_js_vars(self): return self.new_config
class Plugin(Plugin_Base): name = "teedoc-plugin-search" desc = "search support for teedoc" defautl_config = {} def __init__(self, config, doc_src_path, site_config, logger=None): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config self.config.update(config) self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_search") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.css = { "/static/css/search/style.css": os.path.join(self.assets_abs_path, "style.css"), } self.footer_js = { "/static/js/search/main.js": os.path.join(self.assets_abs_path, "main.js") } self.images = { "/static/image/search/close.svg": os.path.join(self.assets_abs_path, "close.svg"), "/static/image/search/search.svg": os.path.join(self.assets_abs_path, "search.svg"), } # set site_root_url env value if not "env" in config: config['env'] = {} config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = config["env"] self.css = self._update_file_var(self.css, vars, self.temp_dir) self.footer_js = self._update_file_var(self.footer_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} self.files_to_copy.update(self.css) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) self.html_js_items = self._generate_html_js_items() self.content = {"articles": {}, "pages": {}} def __del__(self): if os.path.exists(self.temp_dir): try: shutil.rmtree(self.temp_dir) except Exception: pass def _generate_html_header_items(self): items = [] # css for url in self.css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( url) items.append(item) return items def _generate_html_js_items(self): items = [] for url in self.footer_js: item = '<script src="{}"></script>'.format(url) items.append(item) return items def _update_file_var(self, files, vars, temp_dir): for url, path in files.items(): with open(path, encoding='utf-8') as f: content = f.read() for k, v in vars.items(): content = content.replace( "${}{}{}".format("{", k.strip(), "}"), v) temp_path = os.path.join(temp_dir, os.path.basename(path)) with open(temp_path, "w", encoding='utf-8') as fw: fw.write(content) files[url] = temp_path return files def on_add_html_header_items(self): return self.html_header_items def on_add_html_js_items(self): return self.html_js_items def on_add_navbar_items(self, new_config): ''' @config config cover self.config ''' search_hint = "Search" search_input_hint = "Keywords separated by space" search_loading_hint = "Loading, wait please ..." search_download_err_hint = "Download error, please check network and refresh again" search_other_docs_result_hint = "Result from other docs" search_curr_doc_result_hint = "Result from current doc" if "search_hint" in new_config: search_hint = new_config["search_hint"] elif "search_hint" in self.config: search_hint = self.config["search_hint"] if "input_hint" in new_config: search_input_hint = new_config["input_hint"] elif "input_hint" in self.config: search_input_hint = self.config["input_hint"] if "loading_hint" in new_config: search_loading_hint = new_config["loading_hint"] elif "loading_hint" in self.config: search_loading_hint = self.config["loading_hint"] if "download_err_hint" in new_config: search_download_err_hint = new_config["download_err_hint"] elif "download_err_hint" in self.config: search_download_err_hint = self.config["download_err_hint"] if "other_docs_result_hint" in new_config: search_other_docs_result_hint = new_config[ "other_docs_result_hint"] elif "other_docs_result_hint" in self.config: search_other_docs_result_hint = self.config[ "other_docs_result_hint"] if "curr_doc_result_hint" in new_config: search_curr_doc_result_hint = new_config["curr_doc_result_hint"] elif "curr_doc_result_hint" in self.config: search_curr_doc_result_hint = self.config["curr_doc_result_hint"] search_btn = '''<a id="search"><span class="icon"></span><span class="placeholder">{}</span> <div id="search_hints"> <span id="search_input_hint">{}</span> <span id="search_loading_hint">{}</span> <span id="search_download_err_hint">{}</span> <span id="search_other_docs_result_hint">{}</span> <span id="search_curr_doc_result_hint">{}</span> </div></a>'''.format( search_hint, search_input_hint, search_loading_hint, search_download_err_hint, search_other_docs_result_hint, search_curr_doc_result_hint) items = [search_btn] return items def on_copy_files(self): return self.files_to_copy def on_htmls(self, htmls_files, htmls_pages): ''' update htmls, may not all html, just partially htmls_files: { "/get_started/zh":{ "url":{ "title": "", "desc": "", "keywords": [], "body": html, "url": "", "raw": "" } } } ''' # for file, html in htmls_files.items(): # self.content["articles"][html["url"]] = html["raw"] # for file, html in htmls_pages.items(): # self.content["pages"][html["url"]] = html["raw"] docs_url = htmls_files.keys() pages_url = htmls_pages.keys() index_content = {} sub_index_path = [] generated_index_json = {} for i, url in enumerate(docs_url): index_content[url] = "{}static/search_index/index_{}.json".format( self.site_config["site_root_url"], i) path = os.path.join(self.temp_dir, "index_{}.json".format(i)) sub_index_path.append(path) # write content to sub index file with open(path, "w", encoding="utf-8") as f: htmls_files[url]["body"] = "" # remove body, only use raw json.dump(htmls_files[url], f, ensure_ascii=False) for i, url in enumerate(pages_url, len(docs_url)): index_content[url] = "{}static/search_index/index_{}.json".format( self.site_config["site_root_url"], i) path = os.path.join(self.temp_dir, "index_{}.json".format(i)) sub_index_path.append(path) # write content to sub index file with open(path, "w", encoding="utf-8") as f: htmls_pages[url]["body"] = "" # remove body, only use raw json.dump(htmls_pages[url], f, ensure_ascii=False) # write content to files # index file index_path = os.path.join(self.temp_dir, "index.json") with open(index_path, "w", encoding="utf-8") as f: json.dump(index_content, f, ensure_ascii=False) # add to copy file list generated_index_json["/static/search_index/index.json"] = index_path for i, path in enumerate(sub_index_path): generated_index_json["/static/search_index/index_{}.json".format( i)] = path self.files_to_copy.update(generated_index_json)
class Plugin(Plugin_Base): name = "teedoc-plugin-theme-default" desc = "default theme for teedoc" defautl_config = { "dark": True, "default_dark": False, "mobile_navbar_collapsed": True, "show_print_page": True, "env": { "main_color": "#4caf7d", "sidebar_width": "300px", "sidebar_scrollbar_color": "#b8b8b8" } } def on_init(self, config, doc_src_path, site_config, logger=None, multiprocess=True, **kw_args): ''' @config a dict object @logger teedoc.logger.Logger object ''' self.logger = Fake_Logger() if not logger else logger self.doc_src_path = doc_src_path self.site_config = site_config self.config = Plugin.defautl_config env = self.config["env"] if "env" in config: env.update(config["env"]) self.config.update(config) self.config["env"] = env if self.config["mobile_navbar_collapsed"]: self.config["env"]["mobile_navbar_collapsed"] = "none" else: self.config["env"]["mobile_navbar_collapsed"] = "block" if self.config["dark"] and self.config["default_dark"]: self.config["env"]["default_theme"] = "dark" else: self.config["env"]["default_theme"] = "light" if self.config["show_print_page"]: self.config["env"]["show_print_page"] = "true" else: self.config["env"]["show_print_page"] = "false" self.logger.i("-- plugin <{}> init".format(self.name)) self.logger.i("-- plugin <{}> config: {}".format( self.name, self.config)) self.assets_abs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets") self.dark_css = { "/static/css/theme_default/dark.css": os.path.join(self.assets_abs_path, "dark.css") } self.light_css = { "/static/css/theme_default/light.css": os.path.join(self.assets_abs_path, "light.css") } # code hilight css file if "code_highlight_css" in self.config and self.config[ "code_highlight_css"]: self.css = {} self.code_highlight_css = self.config["code_highlight_css"] else: self.code_highlight_css = None self.css = { "/static/css/theme_default/prism.min.css": os.path.join(self.assets_abs_path, "prism.min.css"), } # image viewer self.css["/static/css/theme_default/viewer.min.css"] = os.path.join( self.assets_abs_path, "viewer.min.css") # js files self.dark_js = {} self.light_js = {} self.header_js = { "/static/js/theme_default/split.js": os.path.join(self.assets_abs_path, "split.js"), "/static/js/theme_default/jquery.min.js": os.path.join(self.assets_abs_path, "jquery.min.js"), "/static/js/theme_default/pre_main.js": os.path.join(self.assets_abs_path, "pre_main.js") } self.footer_js = { "/static/js/theme_default/tocbot.min.js": os.path.join(self.assets_abs_path, "tocbot.min.js"), "/static/js/theme_default/main.js": os.path.join(self.assets_abs_path, "main.js"), "/static/js/theme_default/viewer.min.js": os.path.join(self.assets_abs_path, "viewer.min.js") } # code hilight js file if "code_highlight_js" in self.config and self.config[ "code_highlight_js"]: self.code_highlight_js = self.config["code_highlight_js"] else: self.code_highlight_js = None self.footer_js[ "/static/css/theme_default/prism.min.js"] = os.path.join( self.assets_abs_path, "prism.min.js") self.images = { "/static/image/theme_default/indicator.svg": os.path.join(self.assets_abs_path, "indicator.svg"), "/static/image/theme_default/menu.svg": os.path.join(self.assets_abs_path, "menu.svg"), "/static/image/theme_default/to-top.svg": os.path.join(self.assets_abs_path, "to-top.svg"), "/static/image/theme_default/light_mode.svg": os.path.join(self.assets_abs_path, "light_mode.svg"), "/static/image/theme_default/dark_mode.svg": os.path.join(self.assets_abs_path, "dark_mode.svg"), "/static/image/theme_default/print.svg": os.path.join(self.assets_abs_path, "print.svg") } # set site_root_url env value self.config['env']["site_root_url"] = self.site_config["site_root_url"] # replace variable in css with value vars = self.config["env"] self.temp_dir = os.path.join(tempfile.gettempdir(), "teedoc_plugin_theme_default") if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) os.makedirs(self.temp_dir) self.dark_css = self._update_file_var(self.dark_css, vars, self.temp_dir) self.light_css = self._update_file_var(self.light_css, vars, self.temp_dir) self.css = self._update_file_var(self.css, vars, self.temp_dir) self.header_js = self._update_file_var(self.header_js, vars, self.temp_dir) self.footer_js = self._update_file_var(self.footer_js, vars, self.temp_dir) # files to copy self.html_header_items = self._generate_html_header_items() self.files_to_copy = {} if self.config["dark"]: self.files_to_copy.update(self.dark_css) self.files_to_copy.update(self.dark_js) self.files_to_copy.update(self.light_css) self.files_to_copy.update(self.css) self.files_to_copy.update(self.light_js) self.files_to_copy.update(self.header_js) self.files_to_copy.update(self.footer_js) self.files_to_copy.update(self.images) if self.config["dark"]: self.themes_btn = '<a id="themes" class="light"></a>' else: self.themes_btn = "" self.html_js_items = self._generate_html_js_items() def on_del(self): if os.path.exists(self.temp_dir): try: shutil.rmtree(self.temp_dir) except Exception: pass def on_html_template(self, type_name): if type_name == "doc": return os.path.join(curr_path, "templates", "article.html") elif type_name == "page": return os.path.join(curr_path, "templates", "page.html") elif type_name == "blog": return os.path.join(curr_path, "templates", "article.html") return None def _generate_html_header_items(self): items = [] # css for url in self.css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( url) items.append(item) if self.code_highlight_css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( self.code_highlight_css) items.append(item) if self.config["dark"]: for url in self.dark_css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( url) items.append(item) for url in self.light_css: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( url) items.append(item) if "css" in self.config: item = '<link rel="stylesheet" href="{}" type="text/css"/>'.format( self.config["css"]) items.append(item) # header_js for url in self.header_js: item = '<script src="{}"></script>'.format(url) items.append(item) if self.config["dark"]: for url in self.dark_js: item = '<script src="{}"></script>'.format(url) items.append(item) for url in self.light_js: item = '<script src="{}"></script>'.format(url) items.append(item) return items def _generate_html_js_items(self): items = [] for url in self.footer_js: item = '<script src="{}"></script>'.format(url) items.append(item) if "js" in self.config: item = '<script src="{}"></script>'.format(self.config["js"]) items.append(item) if self.code_highlight_js: item = '<script src="{}"></script>'.format(self.code_highlight_js) items.append(item) return items def _update_file_var(self, files, vars, temp_dir): for url, path in files.items(): with open(path, encoding='utf-8') as f: content = f.read() for k, v in vars.items(): content = content.replace( "${}{}{}".format("{", k.strip(), "}"), str(v)) temp_path = os.path.join(temp_dir, os.path.basename(path)) with open(temp_path, "w", encoding='utf-8') as fw: fw.write(content) files[url] = temp_path return files def on_add_html_header_items(self, type_name): return self.html_header_items def on_add_html_footer_js_items(self, type_name): return self.html_js_items def on_add_navbar_items(self): items = [self.themes_btn] return items def on_copy_files(self): res = self.files_to_copy self.files_to_copy = {} return res