mailtojekyll 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +44 -0
- data/LICENSE.txt +21 -0
- data/README.md +121 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/mailtojekyll +5 -0
- data/lib/jekyllemail.rb +137 -0
- data/lib/jekyllpost.rb +80 -0
- data/lib/mailtojekyll.rb +170 -0
- data/lib/mailtojekyll/version.rb +3 -0
- data/mailtojekyll.gemspec +24 -0
- data/pkg/mailtojekyll-0.1.0.gem +0 -0
- data/pkg/mailtojekyll-0.2.0.gem +0 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 614b720ae92b14999a17de7d36b60f156fc6b3ca
|
4
|
+
data.tar.gz: 0620950a2eb3e4dbd411c49152cddf60be8d8538
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6d2bacc9591d037f65110752e6fe4cd017d34a91120ccd3daef2216f2186fd1d984a61298a6c178f2708bd41f65714b89a6fcfad767285ed5ab9d554e0686d84
|
7
|
+
data.tar.gz: 632aca57334fb086b72ddf270e11647976947a8a1836f0ad3f9114bf5d72a10787c699a9eadeb0022b79858435db3c92db3d8668905c024e6e0e82beb000d031
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
mailtojekyll
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.0
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.2.5)
|
5
|
+
docile (1.1.5)
|
6
|
+
json (1.8.3)
|
7
|
+
mail (2.6.3)
|
8
|
+
mime-types (>= 1.16, < 3)
|
9
|
+
mime-types (2.6.1)
|
10
|
+
mini_portile (0.6.2)
|
11
|
+
nokogiri (1.6.6.2)
|
12
|
+
mini_portile (~> 0.6.0)
|
13
|
+
reverse_markdown (0.8.2)
|
14
|
+
nokogiri
|
15
|
+
rinku (1.7.3)
|
16
|
+
rspec (3.3.0)
|
17
|
+
rspec-core (~> 3.3.0)
|
18
|
+
rspec-expectations (~> 3.3.0)
|
19
|
+
rspec-mocks (~> 3.3.0)
|
20
|
+
rspec-core (3.3.0)
|
21
|
+
rspec-support (~> 3.3.0)
|
22
|
+
rspec-expectations (3.3.0)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.3.0)
|
25
|
+
rspec-mocks (3.3.0)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.3.0)
|
28
|
+
rspec-support (3.3.0)
|
29
|
+
simplecov (0.10.0)
|
30
|
+
docile (~> 1.1.0)
|
31
|
+
json (~> 1.8)
|
32
|
+
simplecov-html (~> 0.10.0)
|
33
|
+
simplecov-html (0.10.0)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
mail
|
40
|
+
nokogiri
|
41
|
+
reverse_markdown
|
42
|
+
rinku
|
43
|
+
rspec (~> 3.0)
|
44
|
+
simplecov
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Kate Klemp
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# mailtojekyll
|
2
|
+
|
3
|
+
TODO: Update rspec tests
|
4
|
+
|
5
|
+
## Motivation
|
6
|
+
|
7
|
+
`mailtojekyll` was written in order to allow clients to post to `jekyll` blogs via email, rather than manually creating markdown files, running `jekyll`, and uploading the generated static files themselves.
|
8
|
+
|
9
|
+
## What it does
|
10
|
+
|
11
|
+
Converts emailed blog posts into markdown and saves them inside a jekyll structure
|
12
|
+
|
13
|
+
## How it works
|
14
|
+
|
15
|
+
`mailtojekyll` connects to a dedicated email account using POP3; downloads all the emails; validates for content, secret word, and subject line; processes the emails into markdown; saves the attachments; and replaces image references with markdown-safe image links.
|
16
|
+
|
17
|
+
## How to use it
|
18
|
+
|
19
|
+
Users email a post to their dedicated email account for their `jekyll` blog. Most formatting will be stripped out. If the user wants to include images in the post, they should use the following syntax:
|
20
|
+
|
21
|
+
```
|
22
|
+
#image-filename.png#
|
23
|
+
```
|
24
|
+
|
25
|
+
`mailtojekyll` will replace that tag with the correct markdown image tag, including a reference to the relative image path so `jekyll` processes it correctly
|
26
|
+
|
27
|
+
### External Dependencies
|
28
|
+
- `jekyll` [http://www.jekyllrb.com/](http://www.jekyllrb.com)
|
29
|
+
- `mail` gem
|
30
|
+
- `nokogiri` gem
|
31
|
+
- `reverse_markdown` gem
|
32
|
+
- `rinku` gem
|
33
|
+
|
34
|
+
## Reference implementation
|
35
|
+
One option:
|
36
|
+
- `jekyll` installed locally
|
37
|
+
- `mailtojekyll` installed locally as an executable gem
|
38
|
+
- cron job to run `mailtojekyll`
|
39
|
+
- cron job to build `jekyll` site
|
40
|
+
- cron job to transfer `_site/` directory to server (i.e. FTP or sync)
|
41
|
+
- cron job to update and push `git` branches
|
42
|
+
|
43
|
+
### Managing services
|
44
|
+
|
45
|
+
#### [OS]
|
46
|
+
|
47
|
+
Ubuntu/Linux
|
48
|
+
|
49
|
+
#### [Libraries/Apps]
|
50
|
+
|
51
|
+
- `ruby`
|
52
|
+
- `rvm`
|
53
|
+
- `git`
|
54
|
+
|
55
|
+
#### [Web Server]
|
56
|
+
|
57
|
+
Hosting (dedicated or shared)
|
58
|
+
|
59
|
+
### Production Implementation
|
60
|
+
|
61
|
+
#### Where is this thing?
|
62
|
+
|
63
|
+
**TODO: Update this with our production stack (AWS, etc.)**
|
64
|
+
|
65
|
+
## For developers
|
66
|
+
|
67
|
+
Best practices
|
68
|
+
1. Set up a dedicated email account only for this purpose
|
69
|
+
2. Clone `mailtojekyll` to your local machine
|
70
|
+
3.
|
71
|
+
```
|
72
|
+
cd mailtojekyll
|
73
|
+
```
|
74
|
+
(this should create a new gemset if you're using `rvm`)
|
75
|
+
|
76
|
+
4. `rake install` to install gem executable
|
77
|
+
5. Install jekyll and create a new blog: [http://jekyllrb.com/docs/quickstart/](http://jekyllrb.com/docs/quickstart/)
|
78
|
+
6. Send a few emails to your dedicated email account
|
79
|
+
7. ```
|
80
|
+
mailtojekyll -j /home/user/repo -s pop.example.com -u example@example.com -p x123456789x -S secretword -i imgdir -P postdir
|
81
|
+
```
|
82
|
+
8. View your repo to see the created files.
|
83
|
+
|
84
|
+
### CRON JOB SETUP
|
85
|
+
|
86
|
+
Add required `rvm` path variables to the top of your crontab file:
|
87
|
+
```
|
88
|
+
rvm cron setup
|
89
|
+
```
|
90
|
+
Set up cron job
|
91
|
+
```
|
92
|
+
crontab -e
|
93
|
+
```
|
94
|
+
|
95
|
+
##### CRON JOB W/ POP
|
96
|
+
```
|
97
|
+
* * * * * /home/user/.rvm/gems/ruby-2.2.0@mailtojekyll/bin/mailtojekyll -j /home/user/repo -s pop.example.com -u example@example.com -p x123456789x -S secretword -i imgdir -P postdir >> /tmp/cron_debug_log.log 2>&1
|
98
|
+
```
|
99
|
+
|
100
|
+
##### CRON JOB W/ TEST EMAILS
|
101
|
+
```
|
102
|
+
* * * * * /home/user/.rvm/gems/ruby-2.2.0@mailtojekyll/bin/mailtojekyll -t /path/to/emails -j /home/user/repo -S secretword -i imgdir -P postdir >> /tmp/cron_debug_log.log 2>&1
|
103
|
+
```
|
104
|
+
|
105
|
+
### Deployment
|
106
|
+
|
107
|
+
Run `mailtojekyll` on your own machine with cron, or on a server with cron to gather emails. Manually build and upload your site, Deploy `jekyll` according to recommendations: [http://jekyllrb.com/docs/deployment-methods/](http://jekyllrb.com/docs/deployment-methods/)
|
108
|
+
|
109
|
+
## Contributors
|
110
|
+
|
111
|
+
* **M**anager: Ian Reynolds
|
112
|
+
* **O**wner: Kate Klemp
|
113
|
+
* **C**onsulted: Sunil Chopra
|
114
|
+
* **H**elper: Sunil Chopra
|
115
|
+
* **A**pprover: Sunil Chopra
|
116
|
+
|
117
|
+
## ATTRIBUTION
|
118
|
+
|
119
|
+
mailtojekyll is based heavily on the concepts of [JekyllMail](https://github.com/masukomi/JekyllMail), and is essentially a ground-up refactor of that existing app. It has been gemified for ease of use, and updated to work with the most recent versions of `mail`, `nokogiri`, `reverse_markdown`, and `rinku`
|
120
|
+
|
121
|
+
I developed this as a standalone because development on JekyllMail is 4 years old, so I wanted to make sure it was available as an app/gem
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mailtojekyll"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/mailtojekyll
ADDED
data/lib/jekyllemail.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'mail'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'reverse_markdown'
|
6
|
+
require 'rinku'
|
7
|
+
|
8
|
+
class JekyllEmail
|
9
|
+
|
10
|
+
include Mail
|
11
|
+
include Rinku
|
12
|
+
|
13
|
+
attr_reader :atts, :title, :body
|
14
|
+
|
15
|
+
def initialize(thismail)
|
16
|
+
# empties
|
17
|
+
@atts = {}
|
18
|
+
body = ""
|
19
|
+
|
20
|
+
# process w/ mail gem
|
21
|
+
# thismail = Mail.read(thismail)
|
22
|
+
|
23
|
+
# get the subject for validation & split to get title/secret
|
24
|
+
@subject = thismail.subject
|
25
|
+
(@title, @secret) = (@subject.split((/\|\|/))).collect { |x| x.strip } unless @subject.nil?
|
26
|
+
|
27
|
+
save_attachments(thismail)
|
28
|
+
|
29
|
+
# process the body with markdown and blanktest
|
30
|
+
if thismail.multipart?
|
31
|
+
body = thismail.text_part.decoded.gsub(/\n{2}/,"<br><br>").gsub(/\n{1}/,"<br>")
|
32
|
+
else
|
33
|
+
body = thismail.body.to_s.gsub(/\n{2}/,"<br><br>").gsub(/\n{1}/,"<br>")
|
34
|
+
end
|
35
|
+
body = find_links(body)
|
36
|
+
body = find_images(body)
|
37
|
+
body = auto_link(body)
|
38
|
+
body = markdown(body)
|
39
|
+
body = body.gsub(/\[\?\]/,"")
|
40
|
+
body = body.gsub(/[\u{00A9}\u{00AE}\u{203C}\u{2049}\u{2122}\u{2139}\u{2194}-\u{2199}\u{21A9}-\u{21AA}\u{231A}-\u{231B}\u{2328}\u{23CF}\u{23E9}-\u{23F3}\u{23F8}-\u{23FA}\u{24C2}\u{25AA}-\u{25AB}\u{25B6}\u{25C0}\u{25FB}-\u{25FE}\u{2600}-\u{2604}\u{260E}\u{2611}\u{2614}-\u{2615}\u{2618}\u{261D}\u{2620}\u{2622}-\u{2623}\u{2626}\u{262A}\u{262E}-\u{262F}\u{2638}-\u{263A}\u{2648}-\u{2653}\u{2660}\u{2663}\u{2665}-\u{2666}\u{2668}\u{267B}\u{267F}\u{2692}-\u{2694}\u{2696}-\u{2697}\u{2699}\u{269B}-\u{269C}\u{26A0}-\u{26A1}\u{26AA}-\u{26AB}\u{26B0}-\u{26B1}\u{26BD}-\u{26BE}\u{26C4}-\u{26C5}\u{26C8}\u{26CE}-\u{26CF}\u{26D1}\u{26D3}-\u{26D4}\u{26E9}-\u{26EA}\u{26F0}-\u{26F5}\u{26F7}-\u{26FA}\u{26FD}\u{2702}\u{2705}\u{2708}-\u{270D}\u{270F}\u{2712}\u{2714}\u{2716}\u{271D}\u{2721}\u{2728}\u{2733}-\u{2734}\u{2744}\u{2747}\u{274C}\u{274E}\u{2753}-\u{2755}\u{2757}\u{2763}-\u{2764}\u{2795}-\u{2797}\u{27A1}\u{27B0}\u{27BF}\u{2934}-\u{2935}\u{2B05}-\u{2B07}\u{2B1B}-\u{2B1C}\u{2B50}\u{2B55}\u{3030}\u{303D}\u{3297}\u{3299}\u{1F004}\u{1F0CF}\u{1F170}-\u{1F171}\u{1F17E}-\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F201}-\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}-\u{1F251}\u{1F300}-\u{1F321}\u{1F324}-\u{1F393}\u{1F396}-\u{1F397}\u{1F399}-\u{1F39B}\u{1F39E}-\u{1F3F0}\u{1F3F3}-\u{1F3F5}\u{1F3F7}-\u{1F4FD}\u{1F4FF}-\u{1F53D}\u{1F549}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F56F}-\u{1F570}\u{1F573}-\u{1F579}\u{1F587}\u{1F58A}-\u{1F58D}\u{1F590}\u{1F595}-\u{1F596}\u{1F5A5}\u{1F5A8}\u{1F5B1}-\u{1F5B2}\u{1F5BC}\u{1F5C2}-\u{1F5C4}\u{1F5D1}-\u{1F5D3}\u{1F5DC}-\u{1F5DE}\u{1F5E1}\u{1F5E3}\u{1F5EF}\u{1F5F3}\u{1F5FA}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CB}-\u{1F6D0}\u{1F6E0}-\u{1F6E5}\u{1F6E9}\u{1F6EB}-\u{1F6EC}\u{1F6F0}\u{1F6F3}\u{1F910}-\u{1F918}\u{1F980}-\u{1F984}\u{1F9C0}]/,"")
|
41
|
+
unless body.nil?
|
42
|
+
if blanktest(body)
|
43
|
+
thismail.has_attachments? ? body = " " : body = ""
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@body = body
|
47
|
+
end
|
48
|
+
|
49
|
+
# validate the subject; if valid, return title
|
50
|
+
def validate_subject
|
51
|
+
if @subject.nil?
|
52
|
+
puts "No subject, skipping"
|
53
|
+
raise StandardError, "No subject"
|
54
|
+
else
|
55
|
+
@title.strip
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# validate the secret; no return
|
60
|
+
def validate_secret(opt) # parse the title for the secret
|
61
|
+
unless @secret.nil?
|
62
|
+
(key, @secret) = @secret.split(/:\s?/)
|
63
|
+
@secret.strip!
|
64
|
+
end
|
65
|
+
if (@secret.nil? || @secret != opt)
|
66
|
+
puts "#{@subject}: Secret incorrect, skipping"
|
67
|
+
raise StandardError, "Secret incorrect or missing"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# validate the body
|
72
|
+
def validate_body
|
73
|
+
if @body == ""
|
74
|
+
puts "#{@subject}: No body text, skipping"
|
75
|
+
raise StandardError, "No body text"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def find_images(body)
|
80
|
+
find = body.scan(/\[cid:.*?\]/)
|
81
|
+
find.each do |cid|
|
82
|
+
body = body.gsub(cid,"")
|
83
|
+
end
|
84
|
+
find = body.scan(/\#.*?\#/)
|
85
|
+
find.each do |img|
|
86
|
+
body = body.gsub(img,"<img src='#{img[1...-1]}' />")
|
87
|
+
end
|
88
|
+
body.to_s
|
89
|
+
end
|
90
|
+
|
91
|
+
def find_links(body)
|
92
|
+
find = body.scan(/\<http:.*?\>/)
|
93
|
+
find.each do |link|
|
94
|
+
body = body.gsub(link,"")
|
95
|
+
end
|
96
|
+
body.to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
# convert body to markdown
|
100
|
+
def markdown(doc)
|
101
|
+
doc = Nokogiri::HTML(doc)
|
102
|
+
if doc.at("body").nil?
|
103
|
+
doc = ""
|
104
|
+
else
|
105
|
+
doc = ReverseMarkdown.convert(doc.at("body").inner_html)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# test body to see if it's empty
|
110
|
+
def blanktest(doc)
|
111
|
+
#strip out unicode character that gives us false blanks
|
112
|
+
badspc = Nokogiri::HTML("​").text
|
113
|
+
nbsp = Nokogiri::HTML(" ").text
|
114
|
+
isblank = doc.gsub(badspc,"").gsub(nbsp,"").gsub("\ ","").gsub(/\n/,"").gsub(/\s+/,"")
|
115
|
+
if isblank == ""
|
116
|
+
blank = true
|
117
|
+
else
|
118
|
+
blank = false
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# find and save attachments
|
123
|
+
def save_attachments(thismail)
|
124
|
+
if thismail.has_attachments?
|
125
|
+
# list the attachments & save them
|
126
|
+
thismail.attachments.each_with_index do |att,idx|
|
127
|
+
if att.content_type.start_with?("image/")
|
128
|
+
filename = att.filename.gsub(/[^0-9a-z. ]/i, ' ')
|
129
|
+
filename = filename.split(" ").join("-")
|
130
|
+
cid = att.content_id.to_s.delete("<>")
|
131
|
+
@atts["image#{idx}".to_sym] = {filename: filename, cid: cid, content: att.body.decoded, type: att.content_type}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
data/lib/jekyllpost.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
class JekyllPost
|
4
|
+
|
5
|
+
def initialize(title, body, atts, repo, imgdir, pstdir, meta)
|
6
|
+
time = Time.now
|
7
|
+
postday = time.strftime("%Y-%m-%d")
|
8
|
+
posttime = time.strftime("%H:%M:%S")
|
9
|
+
daydir = time.strftime("%Y/%m/%d")
|
10
|
+
imgdir = "#{imgdir}/#{daydir}"
|
11
|
+
path = make_slug(title,postday)
|
12
|
+
post_filename = "#{repo}/#{pstdir}/#{path}"
|
13
|
+
|
14
|
+
content = replace_images(body, atts, imgdir)
|
15
|
+
create_imgdir(repo,imgdir)
|
16
|
+
save_attachments(repo, imgdir, atts)
|
17
|
+
create_post(post_filename, meta, title, postday, posttime, atts, imgdir, content)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_post(post_filename, meta, title, postday, posttime, atts, imgdir, content)
|
21
|
+
open(post_filename, 'w') do |post|
|
22
|
+
post << "---\n"
|
23
|
+
post << "layout: #{meta[:layout]}\n"
|
24
|
+
post << "title: #{title}\n"
|
25
|
+
post << "date: #{postday} #{posttime}\n"
|
26
|
+
post << "categories: #{meta[:categories]}\n"
|
27
|
+
unless atts.empty?
|
28
|
+
key,val = atts[:image0][:filename]
|
29
|
+
post << "image: /#{imgdir}/#{key}\n"
|
30
|
+
end
|
31
|
+
post << "---\n"
|
32
|
+
post << "#{content}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def save_attachments(repo, imgdir, atts)
|
37
|
+
atts.each do |img,att|
|
38
|
+
if (att[:type].start_with?('image/'))
|
39
|
+
# extracting images for example...
|
40
|
+
filename = att[:filename]
|
41
|
+
begin
|
42
|
+
File.open("#{repo}/#{imgdir}/#{filename}", "w+b", 0644) {|f| f.write att[:content]}
|
43
|
+
rescue => e
|
44
|
+
puts "Unable to save data for #{filename} because #{e.message}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def replace_images(post, atts, imgdir)
|
51
|
+
atts.each do |img,vals|
|
52
|
+
srchimg = "![](#{vals[:filename]})"
|
53
|
+
unless post.nil?
|
54
|
+
post = post.gsub(srchimg,"![](/#{imgdir}/#{vals[:filename]})")
|
55
|
+
# if vals[:cid] == ""
|
56
|
+
# newimg = "![](/#{imgdir}/#{vals[:filename]})\n\n"
|
57
|
+
# post << newimg
|
58
|
+
# end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
post
|
62
|
+
end
|
63
|
+
|
64
|
+
def make_slug(title,time)
|
65
|
+
title = title.downcase.gsub(/\W+/, ' ')
|
66
|
+
title = title.split(" ").join("-")
|
67
|
+
title += ".md"
|
68
|
+
path = "#{time}-#{title}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_imgdir(repo,imgdir)
|
72
|
+
|
73
|
+
fullpath = "#{repo}/#{imgdir}"
|
74
|
+
|
75
|
+
unless Dir.exists?(fullpath)
|
76
|
+
FileUtils.mkdir_p(fullpath)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
data/lib/mailtojekyll.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "mailtojekyll/version"
|
4
|
+
require 'mail'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'optparse'
|
7
|
+
require_relative 'jekyllemail'
|
8
|
+
require_relative 'jekyllpost'
|
9
|
+
|
10
|
+
module Mailtojekyll
|
11
|
+
|
12
|
+
# create image and post directories if they don't exist
|
13
|
+
def self.create_dirs(images,posts)
|
14
|
+
unless Dir.exists?(images)
|
15
|
+
FileUtils.mkdir_p(images)
|
16
|
+
end
|
17
|
+
unless Dir.exists?(posts)
|
18
|
+
FileUtils.mkdir_p(posts)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# empties and initializers
|
23
|
+
home = Dir.pwd
|
24
|
+
options = {jekyll_repo: nil, retrieve: nil, test_source: nil, pop_server: nil, pop_user: nil, pop_password: nil, secret: nil, images_dir: nil, posts_dir: nil, layout: nil, categories: nil}
|
25
|
+
|
26
|
+
# command line options for cron job settings
|
27
|
+
parser = OptionParser.new do|opts|
|
28
|
+
opts.banner = "\nRequired flags are marked with an asterisk(*)\n\nUsage: mailtojekyll.rb [options]\n\n\n"
|
29
|
+
|
30
|
+
opts.on('-t','--test /path/to/emails', "Retrieves saved emails from this directory (local absolute path to test emails)\n\t\t\t\t When using the test flag, the POP flags are not required\n\n") do |param|
|
31
|
+
options[:retrieve] = "file"
|
32
|
+
options[:test_source] = param
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on('-j','--jekyll /path/to/repo', '*Local absolute path to the git repo for your jekyll installation') do |param|
|
36
|
+
options[:jekyll_repo] = param
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on('-s','--server pop.example.com', '*POP server for mail retrieval') do |param|
|
40
|
+
options[:pop_server] = param
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-u','--user example@example.com', '*POP username (may be a full email address)') do |param|
|
44
|
+
options[:pop_user] = param
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-p','--pass examplepassword', '*POP password') do |param|
|
48
|
+
options[:pop_password] = param
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-S','--secret secretword', '*Secret word mailtojekyll should look for in the subject line') do |param|
|
52
|
+
options[:secret] = param
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-i','--imgdir path/to/imgs', '*Image directory (path is relative to your jekyll repo root)') do |param|
|
56
|
+
options[:images_dir] = param
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('-P','--postdir path/to/posts', '*Posts directory (path is relative to your jekyll repo root)') do |param|
|
60
|
+
options[:posts_dir] = param
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('-l','--layout layout', 'Jekyll template layout (default is "post")') do |param|
|
64
|
+
options[:layout] = param
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on('-c','--cats "cat 1, cat 2, cat 3"', 'Jekyll post categories (default is "latest")') do |param|
|
68
|
+
options[:categories] = param.split(",")
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on('-h', '--help', "Displays Help\n\n") do
|
72
|
+
puts opts
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
ARGV.push('-h') if ARGV.empty?
|
78
|
+
|
79
|
+
parser.parse!
|
80
|
+
|
81
|
+
# default to pop method unless test is set by flag
|
82
|
+
if options[:retrieve].to_s.empty?
|
83
|
+
options[:retrieve] = "pop"
|
84
|
+
end
|
85
|
+
|
86
|
+
if options[:retrieve] == "pop"
|
87
|
+
if options[:pop_server].to_s.empty? || options[:pop_user].to_s.empty? || options[:pop_password].to_s.empty?
|
88
|
+
raise "All POP flags are required"
|
89
|
+
exit
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
options.each_pair do |k,v|
|
94
|
+
if k == :layout && options[k].to_s.empty?
|
95
|
+
options[k] = "post"
|
96
|
+
elsif k == :categories && options[k].to_s.empty?
|
97
|
+
options[k] = "latest"
|
98
|
+
elsif k == :test_source && options[:retrieve] == "pop"
|
99
|
+
next
|
100
|
+
else
|
101
|
+
if options[k].to_s.empty?
|
102
|
+
unless options[:retrieve] == "file" && (k == :pop_server || k == :pop_user || k == :pop_password)
|
103
|
+
raise "#{k} flag is required"
|
104
|
+
exit
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# get files or emails from pop server
|
111
|
+
puts "Checking for emails..."
|
112
|
+
if options[:retrieve] == "file"
|
113
|
+
Mail.defaults do
|
114
|
+
retriever_method :test
|
115
|
+
end
|
116
|
+
puts File.expand_path(options[:test_source])
|
117
|
+
Dir["#{options[:test_source]}/*.eml"].each do |mail|
|
118
|
+
Mail::TestRetriever.emails << Mail.read(mail)
|
119
|
+
end
|
120
|
+
elsif options[:retrieve] == "pop"
|
121
|
+
# set pop retrieval defaults for the Mail gem
|
122
|
+
Mail.defaults do
|
123
|
+
mail_settings = {
|
124
|
+
address: options[:pop_server],
|
125
|
+
port: 995,
|
126
|
+
user_name: options[:pop_user],
|
127
|
+
password: options[:pop_password],
|
128
|
+
enable_ssl: true
|
129
|
+
}
|
130
|
+
retriever_method :pop3, mail_settings
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
emails = Mail.all
|
135
|
+
|
136
|
+
if emails.empty?
|
137
|
+
puts "No new mails to process via #{options[:retrieve]} method"
|
138
|
+
exit
|
139
|
+
else
|
140
|
+
puts "Setting up directories..."
|
141
|
+
# empties, initializers & setup
|
142
|
+
create_dirs("#{options[:jekyll_repo]}/#{options[:images_dir]}","#{options[:jekyll_repo]}/#{options[:posts_dir]}")
|
143
|
+
meta = { layout: options[:layout], categories: options[:categories] }
|
144
|
+
|
145
|
+
puts "Fetching mails via #{options[:retrieve]} method..."
|
146
|
+
|
147
|
+
# test for validity and create posts
|
148
|
+
emails.each do |email|
|
149
|
+
|
150
|
+
# make a new email
|
151
|
+
email = JekyllEmail.new(email)
|
152
|
+
|
153
|
+
# validity tests
|
154
|
+
begin
|
155
|
+
email.validate_subject
|
156
|
+
email.validate_secret(options[:secret])
|
157
|
+
email.validate_body
|
158
|
+
rescue
|
159
|
+
next
|
160
|
+
end
|
161
|
+
|
162
|
+
puts "Creating post..."
|
163
|
+
# make a new post
|
164
|
+
post = JekyllPost.new(email.title, email.body, email.atts, options[:jekyll_repo], options[:images_dir], options[:posts_dir], meta)
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
puts "Finished!"
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mailtojekyll/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mailtojekyll"
|
8
|
+
spec.version = Mailtojekyll::VERSION
|
9
|
+
spec.authors = ["Kate Klemp"]
|
10
|
+
spec.email = ["kklemp@misdepartment.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Allows users to send emails to post to a jekyll blog}
|
13
|
+
spec.description = %q{Uses POP3 to connect to a dedicated email address, fetch emails, and parse them into markdown posts for Jekyll to process}
|
14
|
+
spec.homepage = "https://github.com/kateklemp/mailtojekyll"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
end
|
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mailtojekyll
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kate Klemp
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Uses POP3 to connect to a dedicated email address, fetch emails, and
|
42
|
+
parse them into markdown posts for Jekyll to process
|
43
|
+
email:
|
44
|
+
- kklemp@misdepartment.com
|
45
|
+
executables:
|
46
|
+
- mailtojekyll
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- ".gitignore"
|
51
|
+
- ".rspec"
|
52
|
+
- ".ruby-gemset"
|
53
|
+
- ".ruby-version"
|
54
|
+
- ".travis.yml"
|
55
|
+
- CODE_OF_CONDUCT.md
|
56
|
+
- Gemfile
|
57
|
+
- Gemfile.lock
|
58
|
+
- LICENSE.txt
|
59
|
+
- README.md
|
60
|
+
- Rakefile
|
61
|
+
- bin/console
|
62
|
+
- bin/setup
|
63
|
+
- exe/mailtojekyll
|
64
|
+
- lib/jekyllemail.rb
|
65
|
+
- lib/jekyllpost.rb
|
66
|
+
- lib/mailtojekyll.rb
|
67
|
+
- lib/mailtojekyll/version.rb
|
68
|
+
- mailtojekyll.gemspec
|
69
|
+
- pkg/mailtojekyll-0.1.0.gem
|
70
|
+
- pkg/mailtojekyll-0.2.0.gem
|
71
|
+
homepage: https://github.com/kateklemp/mailtojekyll
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.4.6
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Allows users to send emails to post to a jekyll blog
|
95
|
+
test_files: []
|