m2m 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,176 @@
1
+ #生成全站
2
+ require 'fileutils'
3
+
4
+ require_relative './scan'
5
+ require_relative './compiler'
6
+ require_relative './util'
7
+ require_relative './store'
8
+ require_relative './setup'
9
+
10
+ class Generator
11
+ def initialize
12
+ @util = Util.instance
13
+ @setup = Setup.instance
14
+
15
+ #删除目标目录
16
+ FileUtils.rmtree @setup.target_dir
17
+
18
+ #扫描文件
19
+ scan = Scan.new
20
+ scan.execute
21
+
22
+ #存储文件
23
+ @store = Store.new scan.files
24
+
25
+ @compiler = Compiler.new
26
+ @page_size = @setup.get_merged_config['page_size'] || 10
27
+ @page_size = 10 if @page_size.class != Integer
28
+
29
+ self.generate_articles
30
+ self.copy_theme_resource
31
+ #复制文件有问题
32
+ self.copy_workbench_resource
33
+ self.generate_home
34
+ self.generate_all_index @store.tree, '', true
35
+ puts 'Done...'
36
+ end
37
+
38
+ #创建首页
39
+ def generate_home
40
+ children = @store.get_children(@store.tree)
41
+ self.generate_index children, 1, 'home', ''
42
+ end
43
+
44
+ #创建所有的索引页, 如果有文件夹, 则根据配置创建文件夹中的索引页
45
+ #不会创建首页的index
46
+ def generate_all_index(node, dir, ignore_first_index)
47
+ this = self
48
+
49
+ #创建children的所有索引
50
+ children = @store.get_children node
51
+ page_count = (children.length.to_f / @page_size).ceil
52
+
53
+ #生成此文件夹下的所有索引页
54
+ (1..page_count).each { |page_index|
55
+ #忽略第一页的索引, 一般是首页的情况下
56
+ next if page_index == 1 and ignore_first_index
57
+
58
+ # puts dir, page_index
59
+ this.generate_index children, page_index, 'index', dir
60
+ }
61
+
62
+ #遍历所有的文件夹节点, 递归调用
63
+ node.each { |key, sub_node|
64
+ if not @store.is_children_key(key)
65
+ parent_dir = dir + key + '/'
66
+ this.generate_all_index sub_node, parent_dir, false
67
+ end
68
+ }
69
+ end
70
+
71
+ #创建所有文章页
72
+ def generate_articles
73
+ @store.articles.each { |key, article|
74
+ relative_url = article['relative_url']
75
+
76
+ data = {
77
+ 'article' => article
78
+ }
79
+ self.compiler(relative_url, 'article', data)
80
+ }
81
+ end
82
+
83
+ #创建索引页, 包括首页以及子目录的索引页
84
+ def generate_index(children, page_index, template_name, dir)
85
+ articles = []
86
+ start_index = page_index * @page_size - @page_size
87
+ end_index = start_index + @page_size
88
+ #总页数
89
+ page_count = (children.length.to_f / @page_size).ceil
90
+
91
+ path = dir + (page_index == 1 ? 'index.html' : "page-#{page_index}.html")
92
+
93
+ children[start_index..end_index].each { |current|
94
+ articles.push @store.articles[current]
95
+ }
96
+
97
+
98
+
99
+ data = {
100
+ 'articles' => articles,
101
+ 'nav' => self.get_nav(page_index, page_count)
102
+ }
103
+
104
+ self.compiler path, template_name, data
105
+ end
106
+
107
+ #处理上一页下一页
108
+ def get_nav(page_index, page_count)
109
+ nav = {}
110
+ #上一页
111
+ if(page_index == 2)
112
+ nav['previous'] = 'index.html'
113
+ elsif(page_index > 2)
114
+ nav['previous'] = "page-#{page_index - 1}.html"
115
+ end
116
+
117
+ #下一页
118
+ if(page_index < page_count)
119
+ nav['next'] = "page-#{page_index + 1}.html"
120
+ end
121
+
122
+ #以后要处理总的分页信息
123
+
124
+ nav
125
+ end
126
+
127
+ #编译模板
128
+ def compiler(filename, template_name, data)
129
+ data['product'] = @util.get_product
130
+ data['root/relative_path'] = @util.get_relative_dot(filename)
131
+
132
+ @compiler.execute template_name, data, true, filename
133
+ end
134
+
135
+ #复制theme中的资源到目录
136
+ def copy_theme_resource
137
+ this = self
138
+ theme_dir = @compiler.theme_dir
139
+
140
+ Dir::entries(theme_dir).each{ |filename|
141
+ #忽略掉以.开头的
142
+ next if (/^\.|(template)/i =~ filename) != nil
143
+
144
+ this.copy File::join(theme_dir, filename), filename
145
+ }
146
+ end
147
+
148
+ #复制文件到目标
149
+ def copy(source, filename)
150
+ target = File::join @setup.target_dir, filename
151
+
152
+ FileUtils.cp_r source, target
153
+ end
154
+
155
+ #复制工作目录的所有资源, 除忽略/.md/配置文件以外的
156
+ def copy_workbench_resource
157
+ this = self
158
+ Dir::entries(@util.workbench).each{ |filename|
159
+ #忽略掉以.开头的, 以及markdown文件, 还有用户忽略的文件
160
+ next if @util.is_shadow_file?(filename) or
161
+ @util.is_markdown_file?(filename) or
162
+ @util.local_theme_dir == filename or
163
+ @setup.is_user_ignore_file?(filename)
164
+
165
+ #当前的路径
166
+ current_path = @util.get_merge_path filename, @util.workbench
167
+
168
+ #build和内容退出
169
+ next if @setup.target_dir === current_path or
170
+ @setup.content_dir === current_path or
171
+ @util.is_config_file? current_path
172
+
173
+ this.copy File::join(@util.workbench, filename), filename
174
+ }
175
+ end
176
+ end
@@ -0,0 +1,235 @@
1
+ #将markdown发送为邮件
2
+ require 'mail'
3
+ require_relative './scan'
4
+ require_relative './compiler'
5
+ require_relative './util'
6
+ require_relative './store'
7
+ require_relative './setup'
8
+
9
+ class Mailer
10
+ def initialize
11
+ @util = Util.instance
12
+ @mail_config = Setup.instance.get_merged_config['mail']
13
+ #检查邮件的配置信息
14
+ Setup.instance.check_mail_setup
15
+
16
+ #扫描所有文件
17
+ scan = Scan.new
18
+ scan.execute
19
+
20
+ @store = Store.new scan.files
21
+ @compiler = Compiler.new
22
+ end
23
+
24
+ #添加附件, 并返回替换后的body
25
+ def add_pictures(mail, article, body)
26
+ images = body.scan(/<img.+src=["'](.+?)['"]/i)
27
+ return body if images.length == 0
28
+
29
+ root = Pathname.new File::dirname(article['file'])
30
+ #去重,并去掉非相对路径的
31
+ list = Hash.new
32
+ images.each { | line |
33
+ image = line[0]
34
+ next if not /^[\.\/]/ =~ image
35
+
36
+ #将全路径作为key,达到去重的效果
37
+ file = (root + Pathname.new(image)).to_s
38
+ list[file] = image
39
+ }
40
+
41
+ #遍历所有图片,添加到附件
42
+ list.each do |file, src|
43
+ mail.add_file file
44
+ cid = mail.attachments.last.cid
45
+ body = body.gsub(src, 'CID:' + cid)
46
+ end
47
+
48
+ body
49
+ end
50
+
51
+ #添加邮件的主体内容,包括body/处理附件等
52
+ def add_content(mail, article)
53
+ #获取body的内容
54
+ data = {
55
+ "article" => article
56
+ }
57
+ body = @compiler.execute 'mail', data, false
58
+ body = body + self.get_ad
59
+
60
+ #分析出图片列表
61
+ body = self.add_pictures mail, article, body
62
+
63
+ #添加邮件的HTML内容
64
+ mail.html_part = Mail::Part.new do
65
+ content_type 'text/html; charset=UTF-8'
66
+ body body
67
+ end
68
+ end
69
+
70
+ #获取用户的密码,如果用户使用了密码进行加密,则提示用户输入密钥
71
+ def get_password
72
+ safer = @mail_config['safer']
73
+ password = @mail_config['password']
74
+
75
+ encrypt_key = nil
76
+ if safer
77
+ message = "请输入加密您密码的钥匙"
78
+ encript_key = ask(message, String){|q|
79
+ q.echo = '*'
80
+ }
81
+ end
82
+
83
+ @util.decrypt password, encript_key
84
+ end
85
+
86
+ #设置邮件的默认配置
87
+ def set_mail_defaults
88
+ smtp_server = @mail_config['smtp_server']
89
+ port = @mail_config['port']
90
+ username = @mail_config['username']
91
+ ssl = @mail_config['ssl'] == 'y'
92
+ password = self.get_password
93
+
94
+ #配置邮件参数
95
+ Mail.defaults do
96
+ delivery_method :smtp, {
97
+ :address => smtp_server,
98
+ :port => port,
99
+ :user_name => username,
100
+ :password => password,
101
+ :ssl => ssl,
102
+ :enable_starttls_auto => true
103
+ }
104
+ end
105
+ end
106
+
107
+ #添加广告
108
+ def get_ad()
109
+ <<EOF
110
+ <div class="product" style="background-color: rgba(204, 204, 204, 0.26);padding: 4px 10px; text-align: right; font-size: 12px;">
111
+ 本邮件由
112
+ <a href="https://github.com/wvv8oo/m2m" target="_blank">m2m</a>
113
+ 根据Markdown自动转换并发送
114
+ </div>
115
+ EOF
116
+ end
117
+
118
+ #获取邮件接收人
119
+ def get_to(to, article)
120
+ meta = article['meta']
121
+
122
+ #优先取meta中的to
123
+ to = meta['to'] if not to and meta['to']
124
+ #如果meta没有to,且用户也没有指定to,则使用
125
+ to = @mail_config['to'] if not to
126
+ #依然没有找到收件人
127
+ return @util.error '邮件接收人无效,可使用-a参数指定收件人' if not to
128
+
129
+ to = [to] if to.class == String
130
+ to
131
+ end
132
+
133
+ #获取将要发送的markdown文件
134
+ def get_article(md_file)
135
+ items = @store.get_children()
136
+ return @util.error '没有找到任何的Markdown文件' if items.length == 0
137
+
138
+ #如果用户没有指定, 则取最新的
139
+ return @store.articles[items[0]] if not md_file
140
+
141
+ special_article = nil
142
+ index = 0
143
+ begin
144
+ key = items[index]
145
+ article = @store.articles[key]
146
+ file = article['file']
147
+ relative_path = @util.get_relative_path file, @util.workbench
148
+
149
+ special_article = article if relative_path == md_file
150
+
151
+
152
+ index += 1
153
+ end while special_article == nil and index < items.length
154
+
155
+ @util.error "当前目录下未找到Markdown文件 => #{md_file}" if not special_article
156
+ return special_article
157
+ end
158
+
159
+ #优先读取用户指定的,然后读取文章中指定的subject,再读取配置文件中的
160
+ def get_subject(subject, article)
161
+ meta = article['meta']
162
+ #读取文章中mate的,如果在命令行没有指定主题
163
+ subject = meta['subject'] if not subject and meta['subject']
164
+
165
+ #文章中没有,则使用配置文件中的
166
+ subject = @mail_config['subject'] if not subject
167
+
168
+ #配置文件也没有,则使用title,article无论如何都会有title的
169
+ subject = meta['title'] if not subject
170
+
171
+ self.covert_date_macro subject
172
+ end
173
+
174
+ def get_from
175
+ from = @mail_config['from']
176
+ from = @mail_config['account'] if not from
177
+ from
178
+ end
179
+
180
+ #将标题中的日期宏,转换为对应的日期
181
+ def covert_date_macro(subject)
182
+ format = @mail_config['format'] || '%Y-%m-%d'
183
+ subject = subject.gsub('$now', Date.today.strftime(format))
184
+ subject = subject.gsub('$last_week', (Date.today - 7).strftime(format))
185
+ subject
186
+ end
187
+
188
+ #警示用户,由用户确定是否发送
189
+ def alarm(relative_path, subject, to)
190
+ puts "您确定要发送这封邮件吗?"
191
+ puts "邮件标题:#{subject}"
192
+ puts "Markdown:#{relative_path}"
193
+ puts "收件人:#{to}"
194
+ puts ""
195
+
196
+ #提示用户是否需要发送
197
+ result = ask("确认发送请按y或者回车,取消请按其它键", lambda { |yn| yn.downcase[0] == ?y or yn == ''})
198
+
199
+ @util.error '您中止了邮件的发送' if not result
200
+ end
201
+
202
+ #发送邮件
203
+ def send(to, md_file, subject, silent = false)
204
+ article = self.get_article md_file
205
+
206
+ from = self.get_from
207
+ to = self.get_to to, article
208
+ subject = self.get_subject subject, article
209
+
210
+ relative_path = @util.get_relative_path article['file'], @util.workbench
211
+
212
+ #配置邮件信息
213
+ self.set_mail_defaults
214
+
215
+ #警示用户是否需要发送
216
+ self.alarm relative_path, subject, to if not silent
217
+
218
+ #创建一个mail的实例,以后再添加附件和html内容
219
+ mail = Mail.new do
220
+ from from
221
+ to to
222
+ subject subject
223
+ end
224
+
225
+ self.add_content mail, article
226
+
227
+ # @util.write_file './send.log', mail.parts.last.decoded
228
+ mail.deliver
229
+
230
+ puts "恭喜,您的邮件发送成功"
231
+ puts "邮件标题:#{subject}"
232
+ puts "Markdown:#{relative_path}"
233
+ puts "收件人:#{to}"
234
+ end
235
+ end
@@ -0,0 +1,48 @@
1
+ #分析meta值
2
+ # http://pythonhosted.org/Markdown/extensions/meta_data.html
3
+
4
+ class Meta
5
+ def initialize
6
+
7
+ end
8
+
9
+ #分析meta的部分
10
+ def analysis_meta(original)
11
+ return nil if not original
12
+
13
+ result = Hash.new
14
+ list = original.split(/[\r\n?]/)
15
+
16
+ # 提取meta值
17
+ list.each{ |line|
18
+ next if line == ''
19
+ next if (/^(\w+):(.+)/i =~ line) == nil
20
+
21
+ key = $1.lstrip.rstrip
22
+ value = $2.lstrip.rstrip
23
+
24
+ result[key] = value
25
+ }
26
+
27
+ result
28
+ end
29
+
30
+ #分析内容
31
+ def analysis(original)
32
+ result = Hash.new
33
+
34
+ pattern = /(\s+)?<!\-\-(.+?)\-\->(.+)?/m
35
+ matches = pattern.match(original)
36
+
37
+ #如果没有匹配到, 则body就是完整的original
38
+ if matches == nil
39
+ result['body'] = original
40
+ return result
41
+ end
42
+
43
+ #获取body内容
44
+ result['body'] = $3
45
+ result['meta'] = self.analysis_meta $2
46
+ result
47
+ end
48
+ end