solutus 0.1.0 → 0.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ecaee3a0a58a45099f3c7c431fabb0f15c4b347a27120d45abeaa5640a59172
4
- data.tar.gz: fc5f357956eb4497b85b2aa54db9c70ff78fecbe08f9e84158008033d8617767
3
+ metadata.gz: 7de81fd93b57d53407c0e146559143c7ac95237bb21199afd0d3aeea017c5b94
4
+ data.tar.gz: 3dce67dce9f05d447159a53517311f1892efa92a719db866322fd055a6442920
5
5
  SHA512:
6
- metadata.gz: 1f1e2c125d11296e8f8eff2589b74c8fa2f76082690c6589ea6e0995454400f31897cedacba57228840093a2cc751d2b13f2e1510c873501949af3c7f1a62ca2
7
- data.tar.gz: 8715a7a6ee14eda0331261ef4a3c4745d721681ee6492a05b6cfe3cdb98119635b6caef17be9e41f27ac25f71cf6290e7f87d5e5444c414025dbf434e29832fb
6
+ metadata.gz: 4d9c10aa6739895a94e3949d123579393e3c38c4ef6f699f68479f2af6006dcde21a424b23d01c50e45a8e980568fa240e1df185fe786c354dd39efe71a3f340
7
+ data.tar.gz: 2664ed3b5e4c2c9edfe072cf2a9200173697d93154bc62ad5c47c6289df4d1d21641af1884178a526814c72ed3f9d3966d5f6a6983ae4ba6571d9d0977d74f0a
data/lib/solutus.rb CHANGED
@@ -5,6 +5,11 @@ require 'mustache'
5
5
  require 'redcarpet'
6
6
  require 'json'
7
7
  require 'nokogiri'
8
+ require 'digest'
9
+
10
+ #TODO: Add support for tags
11
+ #TODO: Make stress tests
12
+ #TODO: Fix bug : day being off with time in file
8
13
 
9
14
  class Solutus
10
15
  ARCHIVE = "archive"
@@ -18,8 +23,10 @@ class Solutus
18
23
  SITE_PAGES_PATH = File.join("pages", "site-pages")
19
24
  POSTS_PATH = File.join("pages", "posts")
20
25
  BLOG_DATE_FORMAT = "%b %e, %Y"
26
+ BLOG_DATE_DATA = "%Y-%m-%e %I:%M:%S"
21
27
  SETTINGS_FILE = "settings.yml"
22
28
  CACHE = ".solutus-cache"
29
+ PASSWORD_FILE = "password.txt"
23
30
  DAY_ENDINGS = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "st"]
24
31
 
25
32
  @@blog_urls = Hash.new
@@ -39,35 +46,70 @@ class Solutus
39
46
  YAML.load(yml_text)
40
47
  rescue
41
48
  puts "Not a Solutus Project (yet) --- settings.yml not found"
49
+ false
42
50
  end
43
51
  end
44
52
 
45
53
  class Solutus_Server < Sinatra::Base
54
+ enable :sessions
55
+
46
56
  set :public_folder, SITE_PATH
57
+ set :environment, :production
47
58
 
48
59
  configure do
49
60
  glob = Solutus.load_settings
50
61
  set :global_settings, glob
51
62
  end
52
63
 
64
+ class ServerStache < Mustache
65
+ end
66
+
53
67
  get "/" do
54
68
  send_file File.join(settings.public_folder, 'index.html')
55
69
  end
56
70
 
71
+ post "/login" do
72
+ if !params["password"]
73
+ return 'no password'
74
+ end
75
+ given_password_hash = Digest::MD5.hexdigest params["password"]
76
+ password = File.read(PASSWORD_FILE)
77
+ puts given_password_hash.to_s
78
+ puts "password on file:" + password.to_s
79
+ if given_password_hash.to_s == password.to_s
80
+ session[:verified] = true
81
+ redirect "/edit"
82
+ else
83
+ render_login("Wrong password")
84
+ end
85
+ end
86
+
57
87
  get "/reset" do
88
+ if !session[:verified]
89
+ return render_login("You were logged out.")
90
+ end
58
91
  Solutus.command("build")
59
92
  redirect "/"
60
93
  end
61
94
 
62
95
  get "/edit" do
96
+ if !session[:verified]
97
+ return render_login()
98
+ end
63
99
  render_index
64
100
  end
65
101
 
66
102
  get "/editpage" do
103
+ if !session[:verified]
104
+ return render_login("You were logged out.")
105
+ end
67
106
  render_edit_page(params["page-path"])
68
107
  end
69
108
 
70
109
  post "/savepage" do
110
+ if !session[:verified]
111
+ return render_login("You were logged out.")
112
+ end
71
113
  path = params["path"]
72
114
 
73
115
  f = File.open(path, "r")
@@ -90,16 +132,29 @@ class Solutus
90
132
  end
91
133
 
92
134
  post "/newpost" do
93
- path = Solutus.command("new", "post", params["post-title"])
94
- Solutus.build
135
+ if !session[:verified]
136
+ return render_login("You were logged out.")
137
+ end
138
+ title = params["post-title"].gsub(/[^a-zA-Z0-9\-]/,"").strip
139
+ if title.empty?
140
+ return "Error"
141
+ end
142
+ path = Solutus.command("new", "post", title)
143
+ Solutus.command("build")
95
144
  render_edit_post(path)
96
145
  end
97
146
 
98
147
  get "/editpost" do
148
+ if !session[:verified]
149
+ return render_login("You were logged out.")
150
+ end
99
151
  render_edit_post(*params["post-path"].split(","))
100
152
  end
101
153
 
102
154
  post "/savepost" do
155
+ if !session[:verified]
156
+ return render_login("You were logged out.")
157
+ end
103
158
  path = params["path"]
104
159
  f = File.open(path, "r")
105
160
  yml_text = ""
@@ -109,6 +164,7 @@ class Solutus
109
164
  f.close
110
165
  data = YAML.load(yml_text)
111
166
  data["title"] = params["title"]
167
+ data["subtitle"] = params["subtitle"]
112
168
  new_contents = YAML.dump(data)
113
169
  new_contents += "---\n"
114
170
  new_contents += params["markdown"]
@@ -137,7 +193,7 @@ class Solutus
137
193
  else
138
194
  send_404
139
195
  end
140
- end
196
+ end
141
197
 
142
198
  def send_404
143
199
  send_file File.join(path_to_resources, "404.html")
@@ -147,9 +203,6 @@ class Solutus
147
203
  File.join(File.dirname(File.expand_path(__FILE__)), '../resources')
148
204
  end
149
205
 
150
- class ServerStache < Mustache
151
- end
152
-
153
206
  def render_edit_page(path)
154
207
  f = File.open(File.join(path_to_resources, "editPage.html"), "r")
155
208
  template = ""
@@ -170,6 +223,11 @@ class Solutus
170
223
 
171
224
  result[:title] = data["title"]
172
225
  result[:path] = path
226
+ link = path.split("/")[-1].split(".")[0]
227
+ if ["error", "index"].include?(link)
228
+ link = link + ".html"
229
+ end
230
+ result[:link] = link
173
231
  settings.global_settings.each do |key, val|
174
232
  result[key.to_sym] = val
175
233
  end
@@ -200,10 +258,19 @@ class Solutus
200
258
  data = YAML.load(file_contents)
201
259
 
202
260
  result[:title] = data["title"]
261
+ result[:date] = data["date"].strftime(BLOG_DATE_FORMAT)
203
262
  result[:markdown] = file_contents.split("---")[-1]
204
263
  result[:link] = url
205
264
  result[:canview] = url != "/"
206
265
  result[:path] = path
266
+ result[:subtitle] = data["subtitle"]
267
+ result.render
268
+ end
269
+
270
+ def render_login(message="")
271
+ ServerStache.template = File.read(File.join(path_to_resources, "login.html"))
272
+ result = ServerStache.new
273
+ result[:message] = message
207
274
  result.render
208
275
  end
209
276
 
@@ -286,7 +353,9 @@ class Solutus
286
353
  deploy
287
354
  else
288
355
  if args.length >= 2
289
- if args[0] == "create"
356
+ if args[0] == "password"
357
+ create_password(args[1])
358
+ elsif args[0] == "create"
290
359
  new_project(args[1])
291
360
  else
292
361
  if args.length >= 3
@@ -328,6 +397,9 @@ class Solutus
328
397
  def self.build
329
398
  puts "Solutus is building the static site"
330
399
  settings_data = load_settings
400
+ if settings_data == false
401
+ return
402
+ end
331
403
 
332
404
  if File.directory?(BUILD_PATH)
333
405
  FileUtils.remove_dir(BUILD_PATH)
@@ -367,6 +439,7 @@ class Solutus
367
439
  yaml_data[entry] = contents
368
440
  data = YAML.load(contents)
369
441
  date = data["date"]
442
+ subtitle = data.fetch("subtitle", "")
370
443
  relative_dir = File.join(date.year.to_s, date.month.to_s, date.day.to_s, entry.split(".")[0])
371
444
  year = date.year.to_s
372
445
  if !@@blog_urls.key?(year)
@@ -376,7 +449,8 @@ class Solutus
376
449
  "title" => data["title"],
377
450
  "url" => File.join(ARCHIVE, relative_dir),
378
451
  "file_path" => path,
379
- "date" => date
452
+ "date" => date,
453
+ "subtitle" => subtitle
380
454
  })
381
455
  end
382
456
 
@@ -385,7 +459,16 @@ class Solutus
385
459
  get_recents(recents_count).reverse.each do |post|
386
460
  url = post["url"]
387
461
  title = post["title"]
388
- recents += "<div class=\"solutus-recent-posts\"><a href=\"/#{url}\">#{title}</a></div>"
462
+ date = prettify_date(post["date"])
463
+ subtitle = post["subtitle"]
464
+
465
+ block = <<HERE
466
+ <div class="solutus-recents-block">
467
+ <a class="solutus-blog-thumbnail" href="/#{url}">#{title}</a> - #{date}
468
+ <div class="solutus-blog-subtitle"><i>#{subtitle}</i></div>
469
+ </div>
470
+ HERE
471
+ recents += block
389
472
  end
390
473
 
391
474
  yaml_data.each do |entry, contents|
@@ -457,12 +540,13 @@ class Solutus
457
540
  sorted.each do |hash|
458
541
  url = hash["url"]
459
542
  title = hash["title"]
460
- pretty_date = hash["date"].strftime("%b %e") +
461
- DAY_ENDINGS[hash["date"].strftime("%e").to_i]
462
- content += "<div class=\"solutus-blog-block\">"
463
- content += "<div class=\"solutus-blog-title\"><a href=\"/#{url}\">#{title}</a></div>"
464
- content += "<div>#{pretty_date}</div>"
465
- content += "</div>"
543
+ pretty_date = prettify_date(hash["date"])
544
+ content += "<div class=\"solutus-blog-block\">\n"
545
+ content += " <div class=\"solutus-blog-title\">\n"
546
+ content += " <a href=\"/#{url}\">#{title}</a>\n"
547
+ content += " </div>\n"
548
+ content += " <div>#{pretty_date}</div>\n"
549
+ content += "</div>\n"
466
550
  end
467
551
  end
468
552
  path = File.join(SITE_PATH, ARCHIVE, "index.html")
@@ -507,8 +591,17 @@ class Solutus
507
591
  end
508
592
 
509
593
  def self.new_page(title)
510
- file_title = title.gsub(/\s+/, "")
511
- path = File.join(SITE_PAGES_PATH, file_title + ".yml")
594
+ title = title.gsub(/\s+/, "").gsub(/[^a-zA-Z0-9\-]/, "").strip
595
+ if title.empty?
596
+ puts "Bad name"
597
+ return
598
+ end
599
+ if ["edit"].include?(title)
600
+ puts "Can't use #{title} as a page name."
601
+ return
602
+ end
603
+
604
+ path = File.join(SITE_PAGES_PATH, title + ".yml")
512
605
  if File.file?(path)
513
606
  puts "Page with that name already exists."
514
607
  return
@@ -531,7 +624,7 @@ HERE
531
624
 
532
625
  def self.new_post(title)
533
626
  i = 1
534
- new_title = title.gsub(/\s+/, "")
627
+ new_title = title.gsub(/\s+/, "").gsub(/[^a-zA-Z0-9\-]/, "").strip
535
628
  if title.empty?
536
629
  puts "Empty title"
537
630
  return ""
@@ -545,11 +638,12 @@ HERE
545
638
  break
546
639
  end
547
640
  end
548
- date = Time.now.strftime("%Y-%m-%e %I:%M:%S")
641
+ date = Time.now.strftime(BLOG_DATE_DATA)
549
642
  text = <<HERE
550
643
  ---
551
644
  title: #{title}
552
645
  date: #{date}
646
+ subtitle: Another new blog post
553
647
  ---
554
648
  *This is a temporary blog post page!*
555
649
  HERE
@@ -574,6 +668,10 @@ HERE
574
668
  end
575
669
  end
576
670
 
671
+ def self.prettify_date(date)
672
+ date.strftime("%b %e") + DAY_ENDINGS[date.strftime("%e").to_i]
673
+ end
674
+
577
675
  def self.get_recents(count)
578
676
  res = Array.new
579
677
  @@blog_urls.each do |yr, arr|
@@ -601,7 +699,23 @@ HERE
601
699
  result
602
700
  end
603
701
 
702
+ def self.create_password(password)
703
+ content = Digest::MD5.hexdigest password
704
+ if File.file?(PASSWORD_FILE)
705
+ FileUtils.rm(PASSWORD_FILE)
706
+ end
707
+ f = File.new(PASSWORD_FILE, "w")
708
+ f.write(content)
709
+ f.close
710
+ puts "Wrote password to password.txt"
711
+ end
712
+
604
713
  def self.new_project(name)
714
+ name = name.gsub(/[^a-zA-Z0-9\-]/, "").strip
715
+ if name == ""
716
+ puts "Invalid name"
717
+ return
718
+ end
605
719
  if File.directory?(name)
606
720
  puts "Invalid name, already a folder with that name"
607
721
  print "Overwrite? [Y/n]"
@@ -664,6 +778,8 @@ HERE
664
778
  {{{date}}}
665
779
  {{/blog}}
666
780
  {{{content}}}
781
+ <h3>Recent Posts</h3>
782
+ {{{recents}}}
667
783
  <small style="display: block; text-align: center;">&copy; {{year}} {{author}}</small>
668
784
  {{{watermark}}}
669
785
  </div>
@@ -672,9 +788,11 @@ HERE
672
788
  </html>
673
789
  HERE
674
790
 
791
+ datetime = Time.now.strftime(BLOG_DATE_DATA)
675
792
  test_file = <<HERE
676
793
  title: Example Blog Post
677
- date: 2018-05-10 03:43:20
794
+ date: #{datetime}
795
+ subtitle: an example post...
678
796
  ---
679
797
  *This is an example blog post page!*
680
798
  HERE
@@ -692,6 +810,7 @@ HERE
692
810
 
693
811
  files = {
694
812
  File.join(name, "settings.yml") => settings_file,
813
+ File.join(name, ".gitignore") => PASSWORD_FILE + "\n",
695
814
  File.join(name, "static", "css", "styles.css") => styles_file,
696
815
  File.join(name, "templates", "default.html") => default_file,
697
816
  File.join(name, "pages", "posts", "test.md") => test_file,
@@ -16,6 +16,14 @@
16
16
  h1, h2, h3 {
17
17
  line-height:1.2
18
18
  }
19
+
20
+ input[type=text] {
21
+ width: 500px;
22
+ }
23
+ a {
24
+ text-decoration: none;
25
+ color: #4286f4;
26
+ }
19
27
  </style>
20
28
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
21
29
  <script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
@@ -23,29 +31,51 @@
23
31
  </head>
24
32
 
25
33
  <body>
26
- <h1>Editing <span style="border: 1px solid gray" id="title-input" contenteditable>{{title}}</span></h1>
27
- ({{path}})
28
- <p><a href="/edit">Back to Edit</a></p>
29
- {{#canview}}
30
- <p><a href="{{link}}">View on Site</a></p>
31
- {{/canview}}
32
- <p><button id="solutus-save-button" type="submit">Save</button></p>
33
- <textarea id="solutus-mde">{{{markdown}}}</textarea>
34
+ <h1>Editing <span style="border: 1px dotted gray" id="title-input" contenteditable>{{title}}</span></h1>
35
+ <code>{{path}}</code> ({{date}})
36
+ <p>
37
+ [<a href="/edit">Back to Edit</a>]
38
+ {{#canview}}
39
+ [<a href="{{link}}">View on Site</a>]
40
+ {{/canview}}
41
+ </p>
42
+ <hr>
43
+ <p>
44
+ <button id="solutus-save-button" type="submit">Save</button>
45
+ <b><span id="solutus-status">Saved.</span></b>
46
+ </p>
47
+
48
+ <p>Subtitle: <input type="text" value="{{subtitle}}" placeholder="Subtitle text" id="solutus-subtitle-input"/></p>
34
49
 
50
+ <textarea id="solutus-mde">{{{markdown}}}</textarea>
51
+ <p style="text-align: center;">
52
+ <small>Powered by <a href="https://github.com/jeromew21/solutus">Solutus</a></small>
53
+ </p>
35
54
  <script>
36
55
  var simplemde = new SimpleMDE({ element: document.getElementById("solutus-mde") });
37
56
 
57
+ function updateStatus(status) {
58
+ $("#solutus-status").html(status);
59
+ }
60
+
38
61
  function save() {
62
+ updateStatus("Saving...")
39
63
  $.post("/savepost", {
40
64
  "path": "{{path}}",
41
65
  "title": $("#title-input").html(),
42
- "markdown": simplemde.value()
66
+ "markdown": simplemde.value(),
67
+ "subtitle": $("#solutus-subtitle-input").val()
43
68
  }, function(response) {
44
69
  console.log(response);
70
+ updateStatus("Saved.");
45
71
  });
46
72
  }
47
73
 
48
74
  $("#solutus-save-button").click(save);
75
+
76
+ $(document).keydown(function() {
77
+ updateStatus("Unsaved changes.")
78
+ })
49
79
  </script>
50
80
  </body>
51
81
  </html>
@@ -4,36 +4,53 @@
4
4
 
5
5
  <style type="text/css">
6
6
  #solutus-editmenu {
7
- width: 100%;
8
- margin: 0;
9
- padding: 15px;
7
+ font-family: "Segoe UI", sans-serif;
8
+ display: table-row;
10
9
  background-color: #eeeeee;
11
- font-family: sans-serif;
10
+ }
11
+
12
+ .solutus-edit-block {
13
+ display: table-cell;
14
+ padding: 8px 12px 8px 12px;
12
15
  }
13
16
  </style>
14
17
  {{{stylesheets}}}
15
18
  </head>
16
19
 
17
20
  <body>
18
- <div id="solutus-editmenu">
19
- <a href="/edit">Back to Edit</a> &nbsp;&nbsp;&nbsp;&nbsp;
20
- Editing <b>{{title}}</b> @ <code>{{path}}</code> &nbsp;&nbsp;&nbsp;&nbsp;
21
- <button id="solutus-save-button">Save</button>
21
+ <div style="display: table;">
22
+ <div id="solutus-editmenu">
23
+ <div class="solutus-edit-block"><a href="/edit">Back to Edit</a></div>
24
+ <div class="solutus-edit-block">Editing <a href="{{link}}"><b>{{title}}</b></a> @ <code>{{path}}</code></div>
25
+ <div class="solutus-edit-block"><button type="button" class="btn btn-primary" id="solutus-save-button">Save</button></div>
26
+ <div class="solutus-edit-block"><b><span id="solutus-status"></span></b></div>
27
+ </div>
22
28
  </div>
23
29
  {{{everything}}}
24
30
  {{{scripts}}}
25
31
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
26
32
  <script>
33
+ function updateStatus(status) {
34
+ $("#solutus-status").html(status);
35
+ }
36
+
27
37
  function save() {
38
+ updateStatus("Saving...")
28
39
  $.post("/savepage", {
29
40
  "path": "{{path}}",
30
41
  "content": $("#solutus-content").html()
31
42
  }, function(response) {
32
43
  console.log(response);
44
+ updateStatus("Saved.");
33
45
  });
34
46
  }
35
47
 
36
48
  $("#solutus-save-button").click(save);
49
+ $("#solutus-content").keydown(function() {
50
+ updateStatus("Unsaved changes.")
51
+ })
52
+
53
+ updateStatus("Saved.");
37
54
  </script>
38
55
  </body>
39
56
  </html>
data/resources/index.html CHANGED
@@ -16,12 +16,17 @@
16
16
  h1, h2, h3 {
17
17
  line-height:1.2
18
18
  }
19
+
20
+ a {
21
+ text-decoration: none;
22
+ color: #4286f4;
23
+ }
19
24
  </style>
20
25
  </head>
21
26
 
22
27
  <body>
23
28
  <h1>Editing {{name}}</h1>
24
- <p><a href="/">View Site</a></p>
29
+ <p>[<a href="/">View Site</a>]</p>
25
30
  <p>
26
31
  <form method="get" action="/editpage">
27
32
  <fieldset>
@@ -49,5 +54,8 @@
49
54
  </fieldset>
50
55
  </form>
51
56
  </p>
57
+ <p style="text-align: center;">
58
+ <small>Powered by <a href="https://github.com/jeromew21/solutus">Solutus</a></small>
59
+ </p>
52
60
  </body>
53
61
  </html>
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Log in to edit</title>
4
+ <style type="text/css">
5
+ body {
6
+ margin:40px auto;
7
+ background-color: #eeeeee;
8
+ max-width:650px;
9
+ line-height:1.6;
10
+ font-size:18px;
11
+ color:#444;
12
+ padding:0 10px;
13
+ font-family: "Segoe UI", sans-serif;
14
+ }
15
+
16
+ h1, h2, h3 {
17
+ line-height:1.2
18
+ }
19
+ </style>
20
+ </head>
21
+
22
+ <body>
23
+ <h1>Login</h1>
24
+ {{message}}
25
+ <form method="post" action="/login">
26
+ <p>Password: <input type="password" name="password" />
27
+ </form>
28
+ </body>
29
+ </html>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solutus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerome Wei
@@ -79,6 +79,7 @@ files:
79
79
  - resources/editBlog.html
80
80
  - resources/editPage.html
81
81
  - resources/index.html
82
+ - resources/login.html
82
83
  homepage: https://github.com/jeromew21/solutus
83
84
  licenses:
84
85
  - MIT