Skip to content

Commit 24b5dcb

Browse files
committed
Beta feature encrypted toc
1 parent 987a774 commit 24b5dcb

File tree

4 files changed

+121
-6
lines changed

4 files changed

+121
-6
lines changed

README.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Install the package from source with pip:
3434
```bash
3535
cd mkdocs-encryptcontent-plugin/
3636
python3 setup.py sdist bdist_wheel
37-
pip3 install dist/mkdocs_encryptcontent_plugin-0.0.10-py3-none-any.whl
37+
pip3 install dist/mkdocs_encryptcontent_plugin-0.0.11-py3-none-any.whl
3838
```
3939

4040
Enable the plugin in your `mkdocs.yml`:
@@ -188,6 +188,47 @@ plugins:
188188
password_button_text: 'custome_text_button'
189189
```
190190
191+
### Encrypt Table Of Content
192+
193+
Related to [issue #9](https://github.com/CoinK0in/mkdocs-encryptcontent-plugin/issues/9)
194+
195+
You can add `encrypted_toc: True` in plugin config variable, to encrypt the table of contents.
196+
197+
You **have to** enable [feature tag encrypt page](https://github.com/CoinK0in/mkdocs-encryptcontent-plugin#tag-encrypted-page)
198+
for this feature to work properly, cause HTML generation of the Table Of Content is done after the rendering process.
199+
200+
When this feature is enabled, we search for your table of content based on `id=mkdocs-encrypted-toc` and encrypt all HTML content.
201+
202+
```yaml
203+
plugins:
204+
- encryptcontent:
205+
tag_encrypted_page: True
206+
encrypted_toc: True`
207+
```
208+
209+
Adding `id=mkdocs-encrypted-toc` in the parent element of your table of content, so that the child html contained to be encrypted.
210+
It possible to use conditional tag `page.encrypted` to add or not the id.
211+
212+
```jinja
213+
{%- if page.toc|count > 0 %}
214+
<div class=".." {% if page.encrypted %}id="mkdocs-encrypted-toc"{% endif %}>
215+
<ul class="..">
216+
{%- for toc_item in page.toc %}
217+
<li class=".."><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
218+
{%- for toc_item in toc_item.children %}
219+
<li><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
220+
{%- endfor %}
221+
{%- endfor %}
222+
</ul>
223+
</div>
224+
{%- endif %}
225+
```
226+
227+
When the feature is enabled and you use any methods *(password, button, cookie)* to decrypt the page, the table of contents will also be decrypted.
228+
229+
By default the encrypted ToC have `style=display:none` to hide encrypted content.
230+
231+
191232
## Contributing
192233

193234
From reporting a bug to submitting a pull request: every contribution is appreciated and welcome.

encryptcontent/decrypt-form.tpl.html

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,34 @@ <h1>{{ summary }}</h1>
8585
{% if password_button %}
8686
decrypt_button = document.getElementById("mkdocs-decrypt-button"),
8787
{% endif %}
88+
{% if encrypted_toc %}
89+
encrypted_toc = document.getElementById('mkdocs-encrypted-toc'),
90+
{% endif %}
8891
decrypt_form = document.getElementById('mkdocs-decrypt-form');
8992
// Adjust password field width to placeholder length
9093
let input = document.getElementById("mkdocs-content-password");
9194
input.setAttribute('size', input.getAttribute('placeholder').length);
95+
{% if encrypted_toc %}
96+
// Decrypt Table Of Content
97+
var decrypt_toc = function() {
98+
// grab the cipher ToC bundle
99+
var parts = encrypted_toc.innerHTML.split(';');
100+
// decrypt it
101+
var content = decrypt_content(
102+
password_input.value,
103+
parts[0],
104+
parts[1],
105+
parts[2]
106+
);
107+
if (content) {
108+
// success; display the decrypted ToC
109+
encrypted_toc.innerHTML = content;
110+
encrypted_toc.style.display = null;
111+
// any post processing on the decrypted toc should be done here
112+
}
113+
}
114+
{% endif %}
115+
// Decrypt content
92116
var decrypt_action = function() {
93117
// grab the ciphertext bundle
94118
var parts = encrypted_content.innerHTML.split(';');
@@ -109,9 +133,6 @@ <h1>{{ summary }}</h1>
109133
hljs.highlightBlock(block);
110134
});
111135
{% endif %}
112-
//{% if save_password_in_local_storage %}
113-
//localStorage.setItem('password', password_input.value);
114-
//{% endif %}
115136
} else {
116137
// create HTML element for the inform message
117138
var decrypt_msg = document.createElement('p');
@@ -134,13 +155,19 @@ <h1>{{ summary }}</h1>
134155
if (password_cookie) {
135156
password_input.value = password_cookie
136157
decrypt_action();
158+
{% if encrypted_toc %}
159+
decrypt_toc();
160+
{% endif %}
137161
}
138162
{% endif %}
139163
{% if password_button %}
140164
if (decrypt_button) {
141165
decrypt_button.onclick = function(event) {
142166
event.preventDefault();
143167
decrypt_action();
168+
{% if encrypted_toc %}
169+
decrypt_toc();
170+
{% endif %}
144171
};
145172
}
146173
{% endif %}
@@ -158,7 +185,10 @@ <h1>{{ summary }}</h1>
158185
}
159186
{% endif %}
160187
event.preventDefault();
161-
decrypt_action()
188+
decrypt_action();
189+
{% if encrypted_toc %}
190+
decrypt_toc();
191+
{% endif %}
162192
}
163193
});
164194
};

encryptcontent/plugin.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from Crypto import Random
88
from jinja2 import Template
99
from Crypto.Cipher import AES
10+
from bs4 import BeautifulSoup
1011
from mkdocs.plugins import BasePlugin
1112

1213
try:
@@ -56,6 +57,7 @@ class encryptContentPlugin(BasePlugin):
5657
('tag_encrypted_page', mkdocs.config.config_options.Type(bool, default=False)),
5758
('password_button', mkdocs.config.config_options.Type(bool, default=False)),
5859
('password_button_text', mkdocs.config.config_options.Type(string_types, default=str(settings['password_button_text']))),
60+
('encrypted_toc', mkdocs.config.config_options.Type(bool, default=False)),
5961
)
6062

6163
def __hash_md5__(self, text):
@@ -100,6 +102,7 @@ def __encrypt_content__(self, content):
100102
'hljs': self.hljs,
101103
'remember_password': self.remember_password,
102104
'disable_cookie_protection': self.disable_cookie_protection,
105+
'encrypted_toc': self.encrypted_toc,
103106
})
104107
return decrypt_form
105108

@@ -164,6 +167,12 @@ def on_pre_build(self, config):
164167
if 'password_button_text' in plugin_config.keys():
165168
password_button_text = plugin_config.get('password_button_text')
166169
setattr(self, 'password_button_text', password_button_text)
170+
# Check if encrypted_toc feature is enable: encrypt table of content (PoC)
171+
setattr(self, 'encrypted_toc', False)
172+
if 'encrypted_toc' in plugin_config.keys():
173+
encrypted_toc = self.config.get('encrypted_toc')
174+
setattr(self, 'encrypted_toc', encrypted_toc)
175+
167176

168177
def on_page_markdown(self, markdown, page, config, **kwargs):
169178
"""
@@ -212,5 +221,39 @@ def on_page_content(self, html, page, config, **kwargs):
212221
if self.tag_encrypted_page:
213222
# Set attribute on page to identify encrypted page on template rendering
214223
setattr(page, 'encrypted', True)
224+
if self.encrypted_toc:
225+
# Set attributes on page to retrieve password on POST context
226+
setattr(page, 'password', self.password)
215227
html = self.__encrypt_content__(html)
216228
return html
229+
230+
def on_post_page(self, output_content, page, config, **kwargs):
231+
"""
232+
The post_page event is called after the template is rendered,
233+
but before it is written to disc and can be used to alter the output of the page.
234+
If an empty string is returned, the page is skipped and nothing is written to disc.
235+
:param output_content: output of rendered template as string
236+
:param page: mkdocs.nav.Page instance
237+
:param config: global configuration object
238+
:return: output of rendered template as string
239+
"""
240+
# limit this process only if encrypted_toc feature is enable *(speedup x4)*
241+
if self.encrypted_toc and hasattr(page, 'encrypted'):
242+
soup = BeautifulSoup(output_content, 'html.parser')
243+
toc_search = soup.find("div", { "id" : "mkdocs-encrypted-toc" })
244+
if toc_search is not None and len(toc_search.contents) > 0:
245+
# Remove '\n', ' ' useless content generated by bs4 parsing...
246+
toc_search.contents = [content for content in toc_search.contents if not content in ['\n', ' ']]
247+
# Select childs items on tags div and encrypt all content with page password
248+
ciphertoc_bundle = self.__encrypt_text_aes__(toc_search.contents[0], page.password)
249+
encrypted_toc = b';'.join(ciphertoc_bundle).decode('ascii')
250+
toc_search.contents[0].replaceWith(encrypted_toc)
251+
if toc_search.has_attr('style'):
252+
if isinstance(toc_search['style'], list):
253+
toc_search['style'].append("display:none")
254+
else:
255+
toc_search['style'] = toc_search['class'] + "display:none"
256+
else:
257+
toc_search['style'] = "display:none"
258+
output_content = str(soup)
259+
return output_content

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def read(fname):
1111

1212
setup(
1313
name='mkdocs-encryptcontent-plugin',
14-
version='0.0.10',
14+
version='0.0.11',
1515
author='CoinK0in',
1616
author_email='[email protected]',
1717
description='A MkDocs plugin that encrypt/decrypt markdown content with AES',
@@ -25,6 +25,7 @@ def read(fname):
2525
'mkdocs',
2626
'pyyaml',
2727
'pycryptodome',
28+
'beautifulsoup4',
2829
],
2930
classifiers=[
3031
'Development Status :: 5 - Production/Stable',

0 commit comments

Comments
 (0)