|
| 1 | +# MetaTable 拡張機能 |
| 2 | +from markdown import Extension |
| 3 | +from markdown.postprocessors import Postprocessor |
| 4 | +import re |
| 5 | +import os |
| 6 | +import glob |
| 7 | +from natsort import natsorted |
| 8 | + |
| 9 | +class MetaTablesExtension(Extension): |
| 10 | + def extendMarkdown(self, md, md_globals): |
| 11 | + md.postprocessors.add('mtables', MetaTablesPostprocesser(self), '>raw_html') |
| 12 | + |
| 13 | +class Card: |
| 14 | + title="" |
| 15 | + content_dir="" |
| 16 | + summary="" |
| 17 | + date="" |
| 18 | + author="" |
| 19 | + dir_title="" |
| 20 | + order="" |
| 21 | + |
| 22 | +COL_NUM = 3 |
| 23 | + |
| 24 | +def get_thumbnail_element(dir, options, index_filename='index.md',pages_filename='.pages'): |
| 25 | + dir_title=read_property(f'docs/{dir}/{pages_filename}','title') |
| 26 | + filenames = natsorted(glob.glob(f'docs/{dir}/*.md')) |
| 27 | + contain_index = "include-index" in options |
| 28 | + contain_subdir = "include-subdir" in options |
| 29 | + meta_type = options[0] |
| 30 | + style_lite = "style-lite" in options |
| 31 | + smart_jump = "smart-jump" in options |
| 32 | + cards=list() |
| 33 | + for i, filename in enumerate(filenames): |
| 34 | + if read_property(filename, 'mt_type') == meta_type: |
| 35 | + overloads = count_property(filename, 'mt_title') |
| 36 | + if overloads <= 0: |
| 37 | + overloads = 1 |
| 38 | + for k in range(overloads): |
| 39 | + card=Card() |
| 40 | + card.title = read_property(filename,'mt_title', read_property(filename, 'title'), k) |
| 41 | + card.summary = read_property(filename,'mt_summary', read_property(filename, 'summary'), k) |
| 42 | + card.date = read_property(filename,'mt_date', read_property(filename, 'date'), k) |
| 43 | + card.order = read_property(filename,'mt_order', read_property(filename, 'order'), k) |
| 44 | + card.content_dir = remove_filename(filename) |
| 45 | + card.dir_title=dir_title |
| 46 | + if smart_jump: |
| 47 | + card.content_dir += f'#{convert_to_id(card.title)}' |
| 48 | + if not filename.endswith(index_filename): |
| 49 | + cards.append(card) |
| 50 | + elif contain_index: |
| 51 | + card.order = -1 |
| 52 | + cards.insert(0,card) |
| 53 | + |
| 54 | + # 指定されている場合はサブディレクトリも見る |
| 55 | + if contain_subdir: |
| 56 | + filenames = natsorted(glob.glob(f'docs/{dir}/*/index.md')) |
| 57 | + for i, filename in enumerate(filenames): |
| 58 | + if read_property(filename, 'mt_type') == meta_type: |
| 59 | + overloads = count_property(filename, 'mt_title') |
| 60 | + if overloads <= 0: |
| 61 | + overloads = 1 |
| 62 | + for k in range(overloads): |
| 63 | + card=Card() |
| 64 | + card.title = read_property(filename,'mt_title', read_property(filename, 'title'), k) |
| 65 | + card.summary = read_property(filename,'mt_summary', read_property(filename, 'summary'), k) |
| 66 | + card.date = read_property(filename,'mt_date', read_property(filename, 'date'), k) |
| 67 | + card.order = read_property(filename,'mt_order', read_property(filename, 'order'), k) |
| 68 | + card.content_dir = remove_filename(filename) |
| 69 | + card.dir_title=dir_title |
| 70 | + cards.append(card) |
| 71 | + |
| 72 | + if smart_jump: |
| 73 | + card.content_dir += f'#{convert_to_id(card.title)}' |
| 74 | + |
| 75 | + cards = natsorted(cards,key=lambda x:x.order) |
| 76 | + |
| 77 | + if style_lite: |
| 78 | + html = '<ul>' |
| 79 | + |
| 80 | + for card in cards: |
| 81 | + html += f'<li><a href="/{card.content_dir}/">{card.title}</a></li>' |
| 82 | + |
| 83 | + html += '</ul>' |
| 84 | + else: |
| 85 | + html = '<table class="table"><thead><th></th><th></th></thead><tbody>\n' |
| 86 | + for card in cards: |
| 87 | + html += '<tr>\n' |
| 88 | + html += f'<td><a href="/{card.content_dir}">{card.title}{card.order}</a></td>' |
| 89 | + html += f'<td>{card.summary}</td>' |
| 90 | + html += '</tr>' |
| 91 | + html += '</tbody></table>' |
| 92 | + return html |
| 93 | + |
| 94 | +def convert_to_id(name) -> str: |
| 95 | + name = name.lower() |
| 96 | + name = name.replace(' ', '-') |
| 97 | + name = name.replace('(', '') |
| 98 | + name = name.replace(')', '') |
| 99 | + name = name.replace(',', '') |
| 100 | + return name |
| 101 | + |
| 102 | +def remove_filename(filename): |
| 103 | + """ |
| 104 | + docs/***.mdのようなファイル名を**のみに切り出す関数 |
| 105 | + """ |
| 106 | + filename = filename.replace("docs/","",1) |
| 107 | + filename=filename[::-1].replace("dm.","",1) |
| 108 | + return filename[::-1] |
| 109 | + |
| 110 | +def count_property(filename,key): |
| 111 | + """記事や.pagesファイルにプロパティがいくつあるかを調べる関数 |
| 112 | + |
| 113 | + filename=ファイル名、key=プロパティの名前 |
| 114 | + """ |
| 115 | + with open(filename, "r", encoding="utf-8") as f: |
| 116 | + search=re.findall(key+'\ *:\ *(.+)*\n*', f.read()) |
| 117 | + if search != None: |
| 118 | + return len(search) |
| 119 | + else: |
| 120 | + return 0 |
| 121 | + |
| 122 | + |
| 123 | +def read_property(filename,key,default="",index=0): |
| 124 | + """記事や.pagesファイルからプロパティを読みだす関数 |
| 125 | + |
| 126 | + filename=ファイル名、key=プロパティの名前 |
| 127 | + """ |
| 128 | + with open(filename, "r", encoding="utf-8") as f: |
| 129 | + search=re.findall(key+'\ *:\ *(.+)*\n*', f.read()) |
| 130 | + if search != None and len(search) > 0: |
| 131 | + if len(search) > index: |
| 132 | + return search[index] |
| 133 | + else: |
| 134 | + return search[index] |
| 135 | + else: |
| 136 | + return default |
| 137 | + |
| 138 | + |
| 139 | +class MetaTablesPostprocesser(Postprocessor): |
| 140 | + _pattern = re.compile("=\!\\\"(.*)\\\"(\\|\\[(.*)\\])?\!=") |
| 141 | + #_pattern = re.compile(r"\[WS[0-9]{5}\]") |
| 142 | + def run(self, html): |
| 143 | + html = re.sub(self._pattern, self._replace_card, html) |
| 144 | + return html |
| 145 | + |
| 146 | + def _replace_card(self, match): |
| 147 | + return get_thumbnail_element(match.group(1),(match.group(3) or "").split(',')) |
| 148 | + |
| 149 | + |
| 150 | + |
| 151 | +def makeExtension(*args, **kwargs): |
| 152 | + return CardsExtension(*args, **kwargs) |
| 153 | + |
0 commit comments