Add tool scripts used to convert Markdown to HTML.
This commit is contained in:
commit
48d4cf2a6d
|
@ -0,0 +1 @@
|
|||
This is most frequently asked questions.
|
|
@ -0,0 +1 @@
|
|||
Frequently Asked Questions and Howto documents
|
|
@ -0,0 +1,16 @@
|
|||
# Amavisd + SpamAssassin not working, no mail header (X-Spam-*) inserted.
|
||||
|
||||
Amavisd has below setting in its config file by default:
|
||||
File: /etc/amavisd/amavisd.conf
|
||||
|
||||
$sa_tag_level_deflt = 2.0;
|
||||
|
||||
That means Amavisd will insert `X-Spam-Flag` and other `X-Spam-*` headers when email score >= 2.0. If you want to let Amavisd always insert these headers, you can set it to a low score, for example:
|
||||
|
||||
$sa_tag_level_deflt = -999;
|
||||
|
||||
Amavisd's main config file is different on different Linux/BSD distributions:
|
||||
|
||||
* Red Hat, CentOS, OpenBSD: `/etc/amavisd/amavisd.conf`
|
||||
* Debian, Ubuntu: `/etc/amavis/conf.d/50-user` (and other config files under `/etc/amavs/conf.d/`)
|
||||
* FreeBSD: `/usr/local/etc/amavisd/amavisd.conf`
|
|
@ -0,0 +1,61 @@
|
|||
# How to enable SMTPS service (SMTP over SSL, port 465)
|
||||
|
||||
### Why iRedMail doesn't enable SMTPS (SMTP over SSL) by default
|
||||
|
||||
SMTPS is deprecated, so iRedMail disable it by default.
|
||||
Quote from wikipedia.org: http://en.wikipedia.org/wiki/SMTPS
|
||||
|
||||
> Originally, in early 1997, the Internet Assigned Numbers Authority registered 465 for SMTPS. By the end of 1998, this was revoked when STARTTLS has been specified. With STARTTLS, the same port can be used with or without TLS. SMTP was seen as particularly important, because clients of this protocol are often other mail servers, which can not know whether a server they wish to communicate with will have a separate port for TLS. The port 465 is now registered for Source-Specific Multicast audio and video.
|
||||
|
||||
### Why enable SMTPS since it's depreciated
|
||||
|
||||
Unfortunately, there're some popular mail clients don't support submission (SMTP over STARTTLS, port 587), the famous one is Microsoft Outlook. Quote from wikipedia.org:
|
||||
|
||||
> Even in 2013, there are still services that continue to offer the deprecated SMTPS interface on port 465 in addition to (or instead of!) the RFC-compliant message submission interface on the port 587 defined by RFC 6409. Service providers that maintain port 465 do so because older Microsoft applications (including Entourage v10.0) do not support STARTTLS, and thus not the smtp-submission standard (ESMTPS on port 587). The only way for service providers to offer those clients an encrypted connection is to maintain port 465.
|
||||
|
||||
### How to enable SMTPS
|
||||
|
||||
To enable SMTPS, you should configure Postfix to listen on port 465 first, then open port 465 in iptables.
|
||||
|
||||
Please find below lines in Postfix config file `/etc/postfix/master.cf` (Linux/OpenBSD) or `/usr/local/etc/postfix/master.cf` (FreeBSD):
|
||||
|
||||
#smtps inet n - n - - smtpd
|
||||
# -o smtpd_tls_wrappermode=yes
|
||||
# -o smtpd_sasl_auth_enable=yes
|
||||
# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
|
||||
# -o milter_macro_daemon_name=ORIGINATING
|
||||
|
||||
Uncomment first 4 lines, but leave the last one commented out (because iRedMail doesn't use Postfix milter at all):
|
||||
|
||||
smtps inet n - n - - smtpd
|
||||
-o smtpd_tls_wrappermode=yes
|
||||
-o smtpd_sasl_auth_enable=yes
|
||||
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
|
||||
# -o milter_macro_daemon_name=ORIGINATING
|
||||
|
||||
Restart Postfix service to enable SMTPS.
|
||||
|
||||
### Open port 465 in iptables
|
||||
|
||||
On RHEL/CentOS, please update iptables rule file `/etc/sysconfig/iptables`, add one rule (third line in below code) for port 465, then restart iptables service.
|
||||
|
||||
# File: /etc/sysconfig/iptables
|
||||
-A INPUT -p tcp --dport 25 -j ACCEPT
|
||||
-A INPUT -p tcp --dport 587 -j ACCEPT
|
||||
-A INPUT -p tcp --dport 465 -j ACCEPT
|
||||
|
||||
On Debian/Ubuntu, if you use iptables rule file provided by iRedMail, please update `/etc/default/iptables`, add one rule (third line in below code) for port 465, then restart iptables service.
|
||||
|
||||
File: /etc/sysconfig/iptables
|
||||
-A INPUT -p tcp --dport 25 -j ACCEPT
|
||||
-A INPUT -p tcp --dport 587 -j ACCEPT
|
||||
-A INPUT -p tcp --dport 465 -j ACCEPT
|
||||
|
||||
On OpenBSD, please append service 'smtps' in `/etc/pf.conf`, parameter `mail_services=`:
|
||||
|
||||
File: /etc/pf.conf
|
||||
mail_services="{www, https, submission, imap, imaps, pop3, pop3s, ssh, smtps}"
|
||||
|
||||
Reload PF rule file:
|
||||
|
||||
# pfctl -f /etc/pf.conf
|
|
@ -0,0 +1,16 @@
|
|||
如何用 Markdown 来写作电子书。主要仿 gitbooks.io
|
||||
|
||||
o 使用目录来区分各个章节。示例:
|
||||
|
||||
- 1-introduction/
|
||||
|- _title.md
|
||||
|- _description.md
|
||||
|- 1-what_is_iredmail.md
|
||||
|- 2-why_choose_iredmail.md
|
||||
|- 3-price.md
|
||||
- 2-faq/
|
||||
- 3-install/
|
||||
|
||||
o 章节的介绍用目录里的 README.md 提供章节标题及章节的介绍性内容。
|
||||
|
||||
o 使用脚本 convert.sh 自动生成电子书的 index.html 文件,将章节按照顺序排列,生成完整的索引,并列出章节内部的所有文章。
|
|
@ -0,0 +1,95 @@
|
|||
#
|
||||
# Syntax
|
||||
#
|
||||
# - Link: An [text](http://url.com/ "title")
|
||||
#
|
||||
|
||||
# TODO
|
||||
# - describe the directory structure and how it works
|
||||
# - remove '1-', '2-' in html file name
|
||||
# - incorrect CSS path in article html file
|
||||
# - better way to strip prefix in dir/file name: [number]-
|
||||
|
||||
# Directory used to store converted html files.
|
||||
PWD="$(PWD)"
|
||||
SOURCE_DIR="${PWD}/src"
|
||||
OUTPUT_DIR="${PWD}/output"
|
||||
INDEX_MD="${OUTPUT_DIR}/index.md"
|
||||
CMD_CONVERT="python ${PWD}/tools/markdown2html.py"
|
||||
|
||||
[ -d ${OUTPUT_DIR} ] || mkdir -p ${OUTPUT_DIR}
|
||||
|
||||
strip_name_prefix()
|
||||
{
|
||||
name="${1}"
|
||||
name="$(echo ${name/#\.\//})" # ./filename
|
||||
name="$(echo ${name/#[0-9][0-9][0-9]-/})" # nnn-
|
||||
name="$(echo ${name/#[0-9][0-9]-/})" # nn-
|
||||
name="$(echo ${name/#[0-9]-/})" # n-
|
||||
echo "${name}"
|
||||
}
|
||||
|
||||
# Get directories of chapters
|
||||
all_chapter_dirs="$(find . -d 1 -type d -iname '[0-9]*' | sort)"
|
||||
echo "* Found chapters:"
|
||||
for dl in ${all_chapter_dirs}; do
|
||||
echo " - $dl"
|
||||
done
|
||||
|
||||
# Get chapter info
|
||||
# - title: _title.md
|
||||
# - summary: _summary.md
|
||||
echo '' > ${INDEX_MD}
|
||||
for chapter_dir in ${all_chapter_dirs}; do
|
||||
# Get articles
|
||||
all_chapter_articles="$(find ${chapter_dir} -depth 1 -type f -iname '[0-9a-z]*.md')"
|
||||
|
||||
echo "* ${chapter_dir} articles:"
|
||||
for article in ${all_chapter_articles}; do
|
||||
echo " - ${article}"
|
||||
done
|
||||
|
||||
# Output directory.
|
||||
# Remove prefix '[number]-' in chapter directory name.
|
||||
_output_chapter_dir="${OUTPUT_DIR}/$(strip_name_prefix ${chapter_dir})"
|
||||
|
||||
_title_md="${chapter_dir}/_title.md"
|
||||
_summary_md="${chapter_dir}/_summary.md"
|
||||
|
||||
if [ -f ${_title_md} ]; then
|
||||
# generate index info of chapter
|
||||
echo "# [$(cat ${_title_md})](${_output_chapter_dir}/_summary.html)" >> ${INDEX_MD}
|
||||
fi
|
||||
|
||||
mkdir -p ${_output_chapter_dir} &>/dev/null
|
||||
|
||||
# Create ${_output_chapter_dir}/_summary.html
|
||||
if [ -f ${_summary_md} ]; then
|
||||
${CMD_CONVERT} ${_summary_md} ${_output_chapter_dir}
|
||||
fi
|
||||
|
||||
# Article info:
|
||||
# - title: first line (without '#') of markdown file
|
||||
for article_file in ${all_chapter_articles}; do
|
||||
article_file_basename="$(basename ${article_file})"
|
||||
article_html_file="$(strip_name_prefix ${article_file_basename})"
|
||||
|
||||
# Replace '.md' suffix by '.html'
|
||||
article_html_file="$(echo ${article_html_file/%.md/.html})"
|
||||
|
||||
# Get title.
|
||||
_article_title="$(head -1 ${article_file} | awk -F'#' '{print $2}')"
|
||||
#echo "article title: ${_article_title}"
|
||||
echo "## [${_article_title}](${_output_chapter_dir}/${article_html_file})" >> ${INDEX_MD}
|
||||
|
||||
${CMD_CONVERT} ${article_file} ${_output_chapter_dir}
|
||||
done
|
||||
done
|
||||
|
||||
cd ${OUTPUT_DIR}
|
||||
|
||||
# Generate index.html
|
||||
python ../tools/markdown2html.py ${INDEX_MD} .
|
||||
|
||||
# Cleanup
|
||||
rm -f ${INDEX_MD}
|
|
@ -0,0 +1,263 @@
|
|||
body{
|
||||
margin: 0 auto;
|
||||
font-family: Georgia, Palatino, serif;
|
||||
color: #444444;
|
||||
line-height: 1;
|
||||
max-width: 960px;
|
||||
padding: 30px;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
color: #111111;
|
||||
font-weight: 400;
|
||||
}
|
||||
h1, h2, h3, h4, h5, p {
|
||||
margin-bottom: 24px;
|
||||
padding: 0;
|
||||
}
|
||||
h1 {
|
||||
/*font-size: 48px;*/
|
||||
font-size: 24px;
|
||||
}
|
||||
h2 {
|
||||
/*font-size: 36px;*/
|
||||
/* The bottom margin is small. It's designed to be used with gray meta text
|
||||
* below a post title. */
|
||||
/*margin: 24px 0 6px;*/
|
||||
font-size: 16px;
|
||||
margin: 10px 0 6px 20px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 21px;
|
||||
}
|
||||
h5 {
|
||||
font-size: 18px;
|
||||
}
|
||||
a {
|
||||
color: #0099ff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
color: #ff6600;
|
||||
}
|
||||
a:visited {
|
||||
color: purple;
|
||||
}
|
||||
ul, ol {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
li {
|
||||
line-height: 24px;
|
||||
}
|
||||
li ul, li ul {
|
||||
margin-left: 24px;
|
||||
}
|
||||
p, ul, ol {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
max-width: 540px;
|
||||
}
|
||||
pre {
|
||||
padding: 0px 24px;
|
||||
max-width: 800px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
code {
|
||||
font-family: Consolas, Monaco, Andale Mono, monospace;
|
||||
line-height: 1.5;
|
||||
font-size: 13px;
|
||||
}
|
||||
aside {
|
||||
display: block;
|
||||
float: right;
|
||||
width: 390px;
|
||||
}
|
||||
blockquote {
|
||||
border-left:.5em solid #eee;
|
||||
padding: 0 2em;
|
||||
margin-left:0;
|
||||
max-width: 476px;
|
||||
}
|
||||
blockquote cite {
|
||||
font-size:14px;
|
||||
line-height:20px;
|
||||
color:#bfbfbf;
|
||||
}
|
||||
blockquote cite:before {
|
||||
content: '\2014 \00A0';
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
color: #666;
|
||||
max-width: 460px;
|
||||
}
|
||||
hr {
|
||||
width: 540px;
|
||||
text-align: left;
|
||||
margin: 0 auto 0 0;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Code below this line is copyright Twitter Inc. */
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 100%;
|
||||
margin: 0;
|
||||
vertical-align: baseline;
|
||||
*vertical-align: middle;
|
||||
}
|
||||
button, input {
|
||||
line-height: normal;
|
||||
*overflow: visible;
|
||||
}
|
||||
button::-moz-focus-inner, input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
button,
|
||||
input[type="button"],
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
cursor: pointer;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
input[type=checkbox], input[type=radio] {
|
||||
cursor: pointer;
|
||||
}
|
||||
/* override default chrome & firefox settings */
|
||||
input:not([type="image"]), textarea {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
label,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
line-height: normal;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
input[type=checkbox], input[type=radio] {
|
||||
cursor: pointer;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
textarea,
|
||||
select {
|
||||
display: inline-block;
|
||||
width: 210px;
|
||||
padding: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
height: 18px;
|
||||
color: #808080;
|
||||
border: 1px solid #ccc;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
select, input[type=file] {
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
}
|
||||
textarea {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* grey out placeholders */
|
||||
:-moz-placeholder {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
::-webkit-input-placeholder {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
select,
|
||||
textarea {
|
||||
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
}
|
||||
|
||||
/* buttons */
|
||||
button {
|
||||
display: inline-block;
|
||||
padding: 4px 14px;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
background-color: #0064cd;
|
||||
background-repeat: repeat-x;
|
||||
background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));
|
||||
background-image: -moz-linear-gradient(top, #049cdb, #0064cd);
|
||||
background-image: -ms-linear-gradient(top, #049cdb, #0064cd);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));
|
||||
background-image: -webkit-linear-gradient(top, #049cdb, #0064cd);
|
||||
background-image: -o-linear-gradient(top, #049cdb, #0064cd);
|
||||
background-image: linear-gradient(top, #049cdb, #0064cd);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid #004b9a;
|
||||
border-bottom-color: #003f81;
|
||||
-webkit-transition: 0.1s linear all;
|
||||
-moz-transition: 0.1s linear all;
|
||||
transition: 0.1s linear all;
|
||||
border-color: #0064cd #0064cd #003f81;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
button:hover {
|
||||
color: #fff;
|
||||
background-position: 0 -15px;
|
||||
text-decoration: none;
|
||||
}
|
||||
button:active {
|
||||
-webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
-moz-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
"""Convert Markdown to HTML file.
|
||||
|
||||
Required Markdown module: http://pypi.python.org/pypi/Markdown/2.1.1
|
||||
"""
|
||||
|
||||
# Usage:
|
||||
# shell> python markdown2html.py path/to/file.md path/to/output/dir
|
||||
|
||||
import sys
|
||||
import commands
|
||||
import web
|
||||
import markdown
|
||||
|
||||
# Markdown extensions
|
||||
MD_EXTENSIONS = ['toc', 'meta', 'extra', 'footnotes', ]
|
||||
|
||||
# Get file name
|
||||
filename = sys.argv[1]
|
||||
# Get file name without file extension
|
||||
filename_without_ext = filename.split('/')[-1].replace('.md', '')
|
||||
|
||||
# Get output directory
|
||||
output_dir = sys.argv[2]
|
||||
# Set output file name
|
||||
output_html_file = output_dir + '/' + filename_without_ext + '.html'
|
||||
|
||||
# Get article title
|
||||
title = commands.getoutput("""grep 'Title:' %s |awk -F'Title: ' '{print $2}'""" % filename)
|
||||
|
||||
# Set HTML head
|
||||
html = """\
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>%(title)s</title>
|
||||
<link href="../css/markdown.css" rel="stylesheet"></head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h3>%(title)s</h3>
|
||||
""" % {'title': title}
|
||||
|
||||
# Read markdown file and render as HTML body
|
||||
# Handle unicode characters with web.safeunicode
|
||||
orig_content = web.safeunicode(open(filename).read())
|
||||
html += markdown.markdown(orig_content, extensions=MD_EXTENSIONS)
|
||||
|
||||
# HTML foot
|
||||
'''
|
||||
html += """\
|
||||
<!-- Google Analytics -->
|
||||
<!--
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var pageTracker = _gat._getTracker("UA-3293801-14");
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}
|
||||
</script>
|
||||
-->
|
||||
<!-- End Google Analytics -->
|
||||
"""
|
||||
'''
|
||||
|
||||
html += '</body></html>'
|
||||
|
||||
# Write to file
|
||||
f = open(output_html_file, 'w')
|
||||
f.write(html)
|
||||
f.close()
|
Loading…
Reference in New Issue