Example #1
0
class Plugin(Plugin_Base):
    name = "teedoc-plugin-google-analytics"
    desc = "google analytics support for teedoc"
    defautl_config = {"id": 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.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_add_html_header_items(self, type_name):
        return self.html_header_items
Example #2
0
class Plugin(Plugin_Base):
    name = "teedoc-plugin-baidu-tongji"
    desc = "baidu tongji support for teedoc"
    defautl_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.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 on_add_html_header_items(self, type_name):
        return self.html_header_items
Example #3
0
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
Example #4
0
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
Example #5
0
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
Example #6
0
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="#&#38142;&#25509;"> </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