waxx 0.1.2

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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE +201 -0
  4. data/README.md +879 -0
  5. data/bin/waxx +120 -0
  6. data/lib/waxx/app.rb +173 -0
  7. data/lib/waxx/conf.rb +54 -0
  8. data/lib/waxx/console.rb +204 -0
  9. data/lib/waxx/csrf.rb +14 -0
  10. data/lib/waxx/database.rb +80 -0
  11. data/lib/waxx/encrypt.rb +38 -0
  12. data/lib/waxx/error.rb +60 -0
  13. data/lib/waxx/html.rb +33 -0
  14. data/lib/waxx/http.rb +268 -0
  15. data/lib/waxx/init.rb +273 -0
  16. data/lib/waxx/irb.rb +44 -0
  17. data/lib/waxx/irb_env.rb +18 -0
  18. data/lib/waxx/json.rb +23 -0
  19. data/lib/waxx/mongodb.rb +221 -0
  20. data/lib/waxx/mysql2.rb +234 -0
  21. data/lib/waxx/object.rb +115 -0
  22. data/lib/waxx/patch.rb +138 -0
  23. data/lib/waxx/pdf.rb +69 -0
  24. data/lib/waxx/pg.rb +246 -0
  25. data/lib/waxx/process.rb +270 -0
  26. data/lib/waxx/req.rb +116 -0
  27. data/lib/waxx/res.rb +98 -0
  28. data/lib/waxx/server.rb +304 -0
  29. data/lib/waxx/sqlite3.rb +237 -0
  30. data/lib/waxx/supervisor.rb +47 -0
  31. data/lib/waxx/test.rb +162 -0
  32. data/lib/waxx/util.rb +57 -0
  33. data/lib/waxx/version.rb +3 -0
  34. data/lib/waxx/view.rb +389 -0
  35. data/lib/waxx/waxx.rb +73 -0
  36. data/lib/waxx/x.rb +103 -0
  37. data/lib/waxx.rb +50 -0
  38. data/skel/README.md +11 -0
  39. data/skel/app/app/app.rb +39 -0
  40. data/skel/app/app/error/app_error.rb +16 -0
  41. data/skel/app/app/error/dhtml.rb +9 -0
  42. data/skel/app/app/error/html.rb +8 -0
  43. data/skel/app/app/error/json.rb +8 -0
  44. data/skel/app/app/error/pdf.rb +13 -0
  45. data/skel/app/app/log/app_log.rb +13 -0
  46. data/skel/app/app.rb +20 -0
  47. data/skel/app/home/home.rb +16 -0
  48. data/skel/app/home/html.rb +145 -0
  49. data/skel/app/html.rb +192 -0
  50. data/skel/app/usr/email.rb +66 -0
  51. data/skel/app/usr/html.rb +115 -0
  52. data/skel/app/usr/list.rb +51 -0
  53. data/skel/app/usr/password.rb +54 -0
  54. data/skel/app/usr/record.rb +98 -0
  55. data/skel/app/usr/usr.js +67 -0
  56. data/skel/app/usr/usr.rb +277 -0
  57. data/skel/app/waxx/waxx.rb +109 -0
  58. data/skel/bin/README.md +1 -0
  59. data/skel/db/README.md +11 -0
  60. data/skel/db/app/0-init.sql +88 -0
  61. data/skel/lib/README.md +1 -0
  62. data/skel/log/README.md +1 -0
  63. data/skel/opt/dev/config.yaml +1 -0
  64. data/skel/opt/prod/config.yaml +1 -0
  65. data/skel/opt/stage/config.yaml +1 -0
  66. data/skel/opt/test/config.yaml +1 -0
  67. data/skel/private/README.md +1 -0
  68. data/skel/public/lib/site.css +202 -0
  69. data/skel/public/lib/waxx/w.ico +0 -0
  70. data/skel/public/lib/waxx/w.png +0 -0
  71. data/skel/public/lib/waxx/waxx.js +111 -0
  72. data/skel/tmp/pids/README.md +1 -0
  73. data.tar.gz.sig +0 -0
  74. metadata +140 -0
  75. metadata.gz.sig +3 -0
data/lib/waxx/x.rb ADDED
@@ -0,0 +1,103 @@
1
+ # Waxx Copyright (c) 2016-2017 ePark labs Inc. & Daniel J. Fitzpatrick <dan@eparklabs.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Waxx
16
+
17
+ ##
18
+ # The X Struct gets instanciated with each request (x)
19
+ # ```
20
+ # x.req # The request object (Instance of Waxx::Req)
21
+ # x.res # The response object (Instance of Waxx::Res)
22
+ # x.usr # The user session cookie
23
+ # x.usr['id'] # Get the id value.
24
+ # x.usr['name'] = value # Set a user session value
25
+ # x.ua # The User Agent / Client cookie
26
+ # x.ua['la'] # Get the last activity time of the user agent
27
+ # x.ua['name'] = value # Set a user session value
28
+ # x.db # The hash of database connections (0 or more)
29
+ # x.db.app.exec(sql, [arg1, arg2, argn]) # Run a query on the app database
30
+ # x.meth # The request method as a symbol: :get, :post, :put, :patch, :delete
31
+ # # Path parameters example.com/app/act/[*args]
32
+ # x.app # The (app or module) The first path parameter
33
+ # x.act # The act (defined in Object.runs())
34
+ # x.oid # The third arg as an int. Given example.com/person/record/42.json, then oid is 42
35
+ # x.args # An array of arguments after /app/act/. Given POST example.com/customer/bill/10/dev/350.json, then args is ['10','dev','350']
36
+ # # When defining a the run proc, args are splatted into the function. So given the example request above and:
37
+ # module Customer
38
+ # extend Waxx::Postgres
39
+ # extend self
40
+ # runs(
41
+ # bill: {
42
+ # desc: "Bill a customer",
43
+ # acl: "internal",
44
+ # post: -> (x, customer_id, category, amount) {
45
+ # # The variables here are:
46
+ # # customer_id = '10'
47
+ # # category = 'dev'
48
+ # # amount = '350'
49
+ # # Note: All passed in args are strings
50
+ # # but x.oid = 10 (as an int)
51
+ # }
52
+ # }
53
+ # )
54
+ # end
55
+ # x.ext # The extension of the request: .json, .html, .pdf, etc. Default defined in Waxx['default']['extension']
56
+ # # Background jobs (executed after the response is returned to the client. For example to deliver an email.)
57
+ # x.jobs # An array of jobs
58
+ # # Jobs are added as procs with optional arguments (proc, *args).
59
+ # x.job(->(x, id){ App::Email.deliver(x, id) }, x, id)
60
+ # ```
61
+ X = Struct.new(
62
+ :req,
63
+ :res,
64
+ :usr,
65
+ :ua,
66
+ :db,
67
+ :meth,
68
+ :app,
69
+ :act,
70
+ :oid,
71
+ :args,
72
+ :ext,
73
+ :jobs
74
+ ) do
75
+ def << str
76
+ res << str.to_s
77
+ end
78
+ def [](k)
79
+ req.post[k.to_s] || req.get[k.to_s]
80
+ end
81
+ def /(k)
82
+ req.post[k.to_s] || req.get[k.to_s]
83
+ end
84
+ def usr?
85
+ not (usr['id'].nil? or usr['id'].to_i == 0)
86
+ end
87
+ def write?
88
+ ['put', 'post', 'patch', 'delete'].include? meth
89
+ end
90
+ def group? g
91
+ return false unless usr?
92
+ usr['grp'].include? g.to_s
93
+ end
94
+ def groups?(*args)
95
+ args.inject(0){|total, g| total + (group?(g) ? 1 : 0)} == args.size
96
+ end
97
+ def job(j, *args)
98
+ jobs << [j, *args]
99
+ end
100
+ end
101
+
102
+ end
103
+
data/lib/waxx.rb ADDED
@@ -0,0 +1,50 @@
1
+ # Waxx Copyright (c) 2016-2017 ePark labs Inc. & Daniel J. Fitzpatrick <dan@eparklabs.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Libs (core & std)
16
+ require 'socket'
17
+ require 'thread'
18
+ require 'openssl'
19
+ require 'base64'
20
+ require 'json'
21
+ require 'time'
22
+ require 'fileutils'
23
+ require 'yaml'
24
+ # Require ruby files in waxx/ (except irb stuff)
25
+ require_relative 'waxx/waxx'
26
+ require_relative 'waxx/version'
27
+ require_relative 'waxx/x'
28
+ require_relative 'waxx/req'
29
+ require_relative 'waxx/res'
30
+ require_relative 'waxx/app'
31
+ require_relative 'waxx/conf'
32
+ require_relative 'waxx/console'
33
+ require_relative 'waxx/csrf'
34
+ require_relative 'waxx/database'
35
+ require_relative 'waxx/encrypt'
36
+ require_relative 'waxx/error'
37
+ require_relative 'waxx/html'
38
+ require_relative 'waxx/http'
39
+ require_relative 'waxx/json'
40
+ require_relative 'waxx/object'
41
+ require_relative 'waxx/patch'
42
+ require_relative 'waxx/pdf'
43
+ require_relative 'waxx/process'
44
+ require_relative 'waxx/pg'
45
+ require_relative 'waxx/mysql2'
46
+ require_relative 'waxx/sqlite3'
47
+ require_relative 'waxx/server'
48
+ require_relative 'waxx/supervisor'
49
+ require_relative 'waxx/util'
50
+ require_relative 'waxx/view'
data/skel/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Waxx App
2
+
3
+ This is a [waxx app](https://www.waxx.io/) generated with `waxx init {app}`.
4
+
5
+ Waxx is a Ruby web application development framework.
6
+
7
+ For help: `waxx help`
8
+
9
+ Start the app: `waxx on`
10
+
11
+ Edit config in `opt/dev/config.yaml`
@@ -0,0 +1,39 @@
1
+ module App::App
2
+ extend Waxx::Object
3
+ extend self
4
+
5
+ runs(
6
+ default: "js",
7
+ js: {
8
+ desc: "Serve the application javascript files.",
9
+ get: -> (x) {
10
+ x.res.no_cookies = true
11
+ x << "app = {uid:#{x.usr['id']},cid:#{x.usr['cid']}};\n"
12
+ # Read each file in the list and include it
13
+ %w(usr/usr.js).each{|f|
14
+ x << File.read("#{Waxx/:opts/:base}/app/#{f}")
15
+ }
16
+ # JS for logged in users
17
+ if x.usr?
18
+ %w().each{|f|
19
+ x << File.read("#{Waxx/:opts/:base}/app/#{f}")
20
+ }
21
+ end
22
+ }
23
+ },
24
+ ok: {
25
+ desc: "Ping the app to see if it is ok",
26
+ get: -> (x) {
27
+ if x.db.app.exec("select 1+1 as two").first['two'] == 2
28
+ x << 'true'
29
+ else
30
+ x.res.status = 500
31
+ x << false
32
+ end
33
+ }
34
+ },
35
+ )
36
+ end
37
+
38
+ require_relative 'log/app_log'
39
+ require_relative 'error/app_error'
@@ -0,0 +1,16 @@
1
+ module App::AppError
2
+ extend Waxx::Object
3
+ extend self
4
+ runs(
5
+ request: {
6
+ desc: "Display a request error (400)",
7
+ get: lambda{|x, title, message=""|
8
+ const_get(x.ext.capitalize).get(x, title, message)
9
+ }
10
+ }
11
+ )
12
+ end
13
+ require_relative 'dhtml'
14
+ require_relative 'html'
15
+ require_relative 'json'
16
+ require_relative 'pdf'
@@ -0,0 +1,9 @@
1
+ module App::AppError::Dhtml
2
+ extend Waxx::Html
3
+ extend self
4
+
5
+ def get(x, title, message)
6
+ x << %(<div><strong>#{h title}</strong></div>
7
+ <div><code>#{(h message).gsub("\n","<br>")}</code></div>)
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module App::AppError::Html
2
+ extend Waxx::Html
3
+ extend self
4
+
5
+ def get(x, title, message)
6
+ App::Html.page(x, title: title, content: h(message))
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module App::AppError::Json
2
+ extend Waxx::Json
3
+ extend self
4
+
5
+ def get(x, title, message)
6
+ x << {ok: false, title: title, message: message}.to_json
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ module App::AppError::Pdf
2
+ extend Waxx::Pdf
3
+ extend self
4
+
5
+ def get(x, title, message)
6
+ x.res['Content-Type'] = Waxx::Http.content_types[:pdf]
7
+ pdf = new_doc
8
+ pdf.text "<b>#{title}</b>"
9
+ pdf.text "#{message}"
10
+ render_file(x, pdf)
11
+ return_file(x)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module App::AppLog
2
+ extend Waxx::Object
3
+ extend self
4
+
5
+ def log(x, cat:'', name:'', value:'', id:nil)
6
+ x.db.exec("INSERT INTO app_log
7
+ (usr_id, category, name, value, related_id, ip_address)
8
+ VALUES ($1, $2, $3, $4, $5, $6)",
9
+ [x.usr['id'], cat, name, value, id, x.req.env['X-Forwarded-For']]
10
+ )
11
+ end
12
+
13
+ end
data/skel/app/app.rb ADDED
@@ -0,0 +1,20 @@
1
+ # Uncomment these to use them or add additional gems
2
+ # require 'pg'
3
+ # require 'mysql2'
4
+ # require 'sqlite3'
5
+ # require 'mongodb'
6
+ # require 'mail'
7
+
8
+ module App
9
+ extend Waxx::App
10
+ extend Waxx::Util
11
+ extend Waxx::Encrypt
12
+ extend Waxx::Server
13
+ extend self
14
+ init
15
+
16
+ # App methods here that your apps share
17
+ end
18
+
19
+ # Require layout engines
20
+ require_relative 'html'
@@ -0,0 +1,16 @@
1
+ module App::Home
2
+ extend Waxx::Object
3
+ extend self
4
+
5
+ runs(
6
+ default: 'welcome',
7
+ welcome: {
8
+ desc: "Welcome to Waxx default page",
9
+ get: -> (x) {
10
+ Html.welcome(x)
11
+ }
12
+ }
13
+ )
14
+ end
15
+
16
+ require_relative 'html'
@@ -0,0 +1,145 @@
1
+ module App::Home::Html
2
+ extend Waxx::Html
3
+ extend self
4
+
5
+ def welcome(x)
6
+ App::Html.render(x, title: "Welcome to Waxx", content: %(
7
+ #{carousel(x)}
8
+ <div class="container marketing">
9
+ #{main_points(x)}
10
+ #{featurettes(x)}
11
+ </div>
12
+ ))
13
+ end
14
+
15
+
16
+ def carousel(x)
17
+ %(
18
+ <!-- Carousel
19
+ ================================================== -->
20
+ <div id="myCarousel" class="carousel slide" data-ride="carousel">
21
+ <!-- Indicators -->
22
+ <ol class="carousel-indicators">
23
+ <li data-target="#myCarousel" data-slide-to="0" class="active"></li>
24
+ <li data-target="#myCarousel" data-slide-to="1"></li>
25
+ <li data-target="#myCarousel" data-slide-to="2"></li>
26
+ </ol>
27
+ <div class="carousel-inner" role="listbox">
28
+ <div class="item active">
29
+ <img class="first-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="First slide">
30
+ <div class="container">
31
+ <div class="carousel-caption">
32
+ <h1>Fast and Smooth</h1>
33
+ <p>Waxx makes web development fast and smooth. You can edit this page at <br><code>app/home/html.rb</code></p>
34
+ <p><a class="btn btn-lg btn-primary" href="#" role="button">Sign up today</a></p>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <div class="item">
39
+ <img class="second-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Second slide">
40
+ <div class="container">
41
+ <div class="carousel-caption">
42
+ <h1>Another example headline.</h1>
43
+ <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
44
+ <p><a class="btn btn-lg btn-primary" href="#" role="button">Learn more</a></p>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ <div class="item">
49
+ <img class="third-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Third slide">
50
+ <div class="container">
51
+ <div class="carousel-caption">
52
+ <h1>One more for good measure.</h1>
53
+ <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
54
+ <p><a class="btn btn-lg btn-primary" href="#" role="button">Browse gallery</a></p>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ <a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
60
+ <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
61
+ <span class="sr-only">Previous</span>
62
+ </a>
63
+ <a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
64
+ <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
65
+ <span class="sr-only">Next</span>
66
+ </a>
67
+ </div><!-- /.carousel -->
68
+ )
69
+ end
70
+
71
+ def main_points(x)
72
+ %(
73
+ <!-- Main Points
74
+ ================================================== -->
75
+
76
+ <!-- Three columns of text below the carousel -->
77
+ <div class="row">
78
+ <div class="col-lg-4">
79
+ <img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
80
+ <h2>Heading</h2>
81
+ <p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p>
82
+ <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
83
+ </div><!-- /.col-lg-4 -->
84
+ <div class="col-lg-4">
85
+ <img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
86
+ <h2>Heading</h2>
87
+ <p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.</p>
88
+ <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
89
+ </div><!-- /.col-lg-4 -->
90
+ <div class="col-lg-4">
91
+ <img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
92
+ <h2>Heading</h2>
93
+ <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
94
+ <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
95
+ </div><!-- /.col-lg-4 -->
96
+ </div><!-- /.row -->
97
+ )
98
+ end
99
+
100
+ def featurettes(x)
101
+ %(
102
+ <!-- START THE FEATURETTES -->
103
+
104
+ <hr class="featurette-divider">
105
+
106
+ <div class="row featurette">
107
+ <div class="col-md-7">
108
+ <h2 class="featurette-heading">First featurette heading. <span class="text-muted">It'll blow your mind.</span></h2>
109
+ <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
110
+ </div>
111
+ <div class="col-md-5">
112
+ <img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
113
+ </div>
114
+ </div>
115
+
116
+ <hr class="featurette-divider">
117
+
118
+ <div class="row featurette">
119
+ <div class="col-md-7 col-md-push-5">
120
+ <h2 class="featurette-heading">Oh yeah, it's that good. <span class="text-muted">See for yourself.</span></h2>
121
+ <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
122
+ </div>
123
+ <div class="col-md-5 col-md-pull-7">
124
+ <img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
125
+ </div>
126
+ </div>
127
+
128
+ <hr class="featurette-divider">
129
+
130
+ <div class="row featurette">
131
+ <div class="col-md-7">
132
+ <h2 class="featurette-heading">And lastly, this one. <span class="text-muted">Checkmate.</span></h2>
133
+ <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
134
+ </div>
135
+ <div class="col-md-5">
136
+ <img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
137
+ </div>
138
+ </div>
139
+
140
+ <hr class="featurette-divider">
141
+
142
+ <!-- /END THE FEATURETTES -->
143
+ )
144
+ end
145
+ end
data/skel/app/html.rb ADDED
@@ -0,0 +1,192 @@
1
+ module App::Html
2
+ extend Waxx::Html
3
+ extend self
4
+
5
+ def standard_css
6
+ [
7
+ "/lib/bootstrap/css/bootstrap.min.css",
8
+ "/lib/bootstrap/css/carousel.css",
9
+ ]
10
+ end
11
+
12
+ def standard_js
13
+ [
14
+ "/lib/jquery-3.1.0.min.js",
15
+ "/lib/bootstrap/js/bootstrap.min.js",
16
+ "/app/js.js",
17
+ ]
18
+ end
19
+
20
+ def render(x, title:"Untitled", content:nil, body_class: "", message:{type:nil, message:nil}, css:[], js:[], js_ready: nil)
21
+ x << head(x, title: title, css:css, body_class: body_class)
22
+ x << nav(x)
23
+ x.res.error.each{|e| x << alert(e[:type], e[:message]) }
24
+ x << content
25
+ x << app_modal(x)
26
+ x << foot(x)
27
+ x << script(x, js:js, js_ready: js_ready)
28
+ x << '</body></html>'
29
+ end
30
+
31
+ def page(x, title:nil, content:nil, message:{type:"alert", message: nil}, css:[], js:[], js_ready:nil)
32
+ render(x, title:title, message: message, css: css, js: js, js_ready: js_ready, content:%(
33
+ #{
34
+ #App::Website::Html.chrome
35
+ }
36
+ <div class="container">
37
+ <h1>#{h title}</h1>
38
+ #{alert(message[:type], message[:message]) if message[:message]}
39
+ #{content||page['content']}
40
+ </div>
41
+ ))
42
+ end
43
+
44
+ def head(x, title:"Untitled", description:"", author:"", css:[], body_class:"")
45
+ %(
46
+ <!DOCTYPE html>
47
+ <!-- Copyright (c) #{Time.new.year} #{h Waxx/:site/:name} -->
48
+ <meta charset="utf-8">
49
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
50
+ <title>#{h title}</title>
51
+ <meta name="description" content="#{h description.to_s.gsub('"',"'")}">
52
+ <meta name="author" content="#{h author}">
53
+ <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1.0">
54
+ <link rel="shortcut icon" type="image/x-icon" href="/lib/waxx/img/w.ico">
55
+ <link rel="apple-touch-icon" sizes="200x200" href="/lib/waxx/img/w.png">
56
+ #{(standard_css + css).map{|f| %(<link href="#{f}" rel="stylesheet">) }.join}
57
+ <body class="#{body_class}">
58
+ )
59
+ end
60
+
61
+ def nav(x)
62
+ %(
63
+ <div class="navbar-wrapper">
64
+ <div class="container">
65
+
66
+ <nav class="navbar navbar-inverse navbar-static-top">
67
+ <div class="container">
68
+ <div class="navbar-header">
69
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
70
+ <span class="sr-only">Toggle navigation</span>
71
+ <span class="icon-bar"></span>
72
+ <span class="icon-bar"></span>
73
+ <span class="icon-bar"></span>
74
+ </button>
75
+ <a class="navbar-brand" href="#">#{h Waxx/:site/:name}</a>
76
+ </div>
77
+ <div id="navbar" class="navbar-collapse collapse">
78
+ <ul class="nav navbar-nav">
79
+ <li class="active"><a href="#">Home</a></li>
80
+ <li><a href="#about">About</a></li>
81
+ <li><a href="#contact">Contact</a></li>
82
+ <li class="dropdown">
83
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
84
+ <ul class="dropdown-menu">
85
+ <li><a href="#">Action</a></li>
86
+ <li><a href="#">Another action</a></li>
87
+ <li><a href="#">Something else here</a></li>
88
+ <li role="separator" class="divider"></li>
89
+ <li class="dropdown-header">Nav header</li>
90
+ <li><a href="#">Separated link</a></li>
91
+ <li><a href="#">One more separated link</a></li>
92
+ </ul>
93
+ </li>
94
+ </ul>
95
+ </div>
96
+ </div>
97
+ </nav>
98
+
99
+ </div>
100
+ </div>
101
+ )
102
+ end
103
+
104
+ def usr_menu(x)
105
+ return "" #if not x.usr?
106
+ cls = %w(usr).include?(x.app) ? "active" : ""
107
+ cls = "active" if x.app == 'letter' and x.act == 'list'
108
+ %(<a class="#{cls}" href="/dashboard"><span class="glyphicon glyphicon-cog"></span></a>)
109
+ end
110
+
111
+ def admin(x, title:nil, content:nil, message:{type:"alert", message: nil}, css:[], js:[], js_ready:nil)
112
+ render(x, title:title, content_class: "container", message: message, css: css, js: js, js_ready: js_ready,
113
+ content: %(
114
+ <div style="background-color: white">
115
+ <div class="container">
116
+ #{admin_menu(x)}
117
+ <div id="admin-panel">#{content}</div>
118
+ </div>
119
+ </div>
120
+ )
121
+ )
122
+ end
123
+
124
+ def admin_menu(x)
125
+ re = [%(<!-- admin_menu -->\n<ul class="nav nav-tabs">)]
126
+ re << [
127
+ ["/admin","Admin","admin"],
128
+ ["/usr","People","admin"],
129
+ ["/content","Content","admin"],
130
+ ["/website_page","Webpages","admin"]
131
+ ].map{|m|
132
+ next if m[2] and not x.group? m[2]
133
+ cls = x.req.uri =~ /^#{m[0]}/ ? "active" : ""
134
+ %(<li role="presentation" class="#{cls}"><a href="#{m[0]}">#{h m[1]}</a></li>)
135
+ }.compact
136
+ re << "</ul>"
137
+ re.join("")
138
+ end
139
+
140
+ # Type can be: success info warning danger
141
+ def alert(type="info", message="")
142
+ return "" if message.empty?
143
+ signs = {success: "info", info: "info", warning: "warning", danger: "warning"}
144
+ %(<div class="alert alert-#{type}" role="alert"><strong>
145
+ <span class="glyphicon glyphicon-#{signs[type.to_sym]}-sign"></span> #{message.h}</strong></div>)
146
+ end
147
+
148
+ def app_modal(x)
149
+ %(
150
+ <div style="display: none;" class="modal fade in" id="apps-modal" tabindex="-1" role="dialog" aria-hidden="true">
151
+ <div class="modal-sm modal-dialog modal-dialog-top">
152
+ <div class="modal-content">
153
+ <div class="block block-themed block-transparent">
154
+ <div class="block-header bg-primary-dark">
155
+ <ul class="block-options">
156
+ <li>
157
+ <button data-dismiss="modal" type="button"><i class="si si-close"></i></button>
158
+ </li>
159
+ </ul>
160
+ <h3 class="block-title"></h3>
161
+ </div>
162
+ <div class="block-content"></div>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ )
168
+ end
169
+
170
+ def foot(x)
171
+ %(
172
+ <!-- FOOTER -->
173
+ <footer class="container">
174
+ <p class="pull-right"><a href="#">Back to top</a></p>
175
+ <p>&copy; #{Time.new.year} #{h Waxx/:site/:name} &middot; <a href="#">Privacy</a> &middot; <a href="#">Terms</a></p>
176
+ <p><a href="https://www.waxx.io/">Delivered by Waxx in #{'%.1f' % ((Time.new - x.req.start_time) * 1000)} ms</a>.</p>
177
+ </footer>
178
+ )
179
+ end
180
+
181
+ def script(x, js:[], js_ready:"")
182
+ %(
183
+ #{(standard_js + js).map{|f| %(<script src="#{f}" type="text/javascript"></script>) }.join}
184
+ <script type="text/javascript">
185
+ $(document).ready(function(){
186
+ #{js_ready}
187
+ })
188
+ </script>
189
+ )
190
+ end
191
+
192
+ end