aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/guff.cr397
-rw-r--r--src/guff/views/admin-page.cr202
-rw-r--r--src/guff/views/blog/list-item.cr7
-rw-r--r--src/guff/views/blog/list.cr20
-rw-r--r--src/guff/views/dropdown/icon.cr11
-rw-r--r--src/guff/views/dropdown/item.cr21
-rw-r--r--src/guff/views/dropdown/menu.cr32
-rw-r--r--src/guff/views/html.cr53
-rw-r--r--src/guff/views/login-page.cr13
-rw-r--r--src/guff/views/logout-page.cr5
-rw-r--r--src/guff/views/tab.cr21
-rw-r--r--src/guff/views/view.cr8
12 files changed, 394 insertions, 396 deletions
diff --git a/src/guff.cr b/src/guff.cr
index f0e2671..4fde62b 100644
--- a/src/guff.cr
+++ b/src/guff.cr
@@ -38,401 +38,6 @@ private macro api_method_dispatch(modules)
end
module Guff
- module Views
- abstract class View
- def initialize(@context : Context)
- end
-
- def h(s : String) : String
- HTML.escape(s)
- end
- end
-
- class TabView < View
- def initialize(
- context : Context,
- @prefix : String,
- @tab : Hash(Symbol, String)
- )
- super(context)
- @id = h("%s-tab-%s" % [@prefix, @tab[:id]]) as String
- @target = h("%s-pane-%s" % [@prefix, @tab[:id]]) as String
- end
-
- private def v(id : Symbol) : String
- raise "unknown id: #{id}" unless @tab.has_key?(id)
- h(@tab[id])
- end
-
- ECR.def_to_s("src/views/tab.ecr")
- end
-
- module Dropdown
- module Icon
- ICON_TEMPLATE = "<i class='fa fa-fw %s'></i>"
-
- def self.icon(id : String?)
- if id && id.size > 0
- ICON_TEMPLATE % [HTML.escape(id.not_nil!)]
- else
- ""
- end
- end
- end
-
- class ItemView < View
- def initialize(
- context : Context,
- @active : Bool,
- @item : Hash(Symbol, String)
- )
- super(context)
- end
-
- private def v(id : Symbol)
- h(@item[id])
- end
-
- private def li_css
- @active ? "class='active'" : ""
- end
-
- ECR.def_to_s("src/views/dropdown/item.ecr")
- end
-
- class MenuView < View
- def initialize(
- context : Context,
- @id : String,
- @name : String,
- @text : String,
- @css : String,
- @icon : String,
- @default : String,
- @items : Array(Hash(Symbol, String))
- )
- super(context)
-
- @default_name = @items.reduce("") do |r, row|
- (row[:id]? == @default) ? row[:name] : r
- end as String
- end
-
- private def items
- String.build do |io|
- @items.each do |item|
- io << ItemView.new(
- context: @context,
- active: @default == item[:id]?,
- item: item
- ).to_s
- end
- end
- end
-
- ECR.def_to_s("src/views/dropdown/menu.ecr")
- end
- end
-
- abstract class HTMLView < View
- TEMPLATES = {
- script: "<script type='text/javascript' src='%s'></script>",
- style: "<link rel='stylesheet' type='text/css' href='%s'/>",
- }
-
- private def assets(key : Symbol, paths : Array(String))
- String.build do |io|
- paths.each do |path|
- io << TEMPLATES[key] % [h(path)]
- end
- end
- end
-
- def scripts(paths : Array(String))
- assets(:script, paths)
- end
-
- def styles(paths : Array(String))
- assets(:style, paths)
- end
-
- def tabs(prefix : String, rows : Array(Hash(Symbol, String)))
- String.build do |io|
- rows.each do |row|
- TabView.new(@context, prefix, row).to_s(io)
- end
- end
- end
-
- def dropdown(
- id : String,
- name : String,
- text : String,
- icon : String,
- css : String,
- default : String,
- items : Array(Hash(Symbol, String))
- )
- Dropdown::MenuView.new(
- context: @context,
- id: id,
- name: name,
- text: text,
- icon: icon,
- css: css,
- default: default,
- items: items
- ).to_s
- end
- end
-
- class AdminPageView < HTMLView
- TITLE = "Guff Admin"
-
- TABS = {
- "admin" => [{
- :id => "home",
- :css => "active",
- :icon => "fa-home",
- :name => "Home",
- :text => "View home tab.",
- }, {
- :id => "posts",
- :css => "",
- :icon => "fa-cubes",
- :name => "Posts",
- :text => "Manage blog, pages, and projects.",
- }, {
- :id => "files",
- :css => "",
- :icon => "fa-files-o",
- :name => "Files",
- :text => "Manage files.",
- }, {
- :id => "settings",
- :css => "",
- :icon => "fa-cogs",
- :name => "Settings",
- :text => "Configure settings.",
- }],
-
- "settings" => [{
- :id => "general",
- :css => "active",
- :icon => "fa-cog",
- :name => "General",
- :text => "Manage general settings.",
- }, {
- :id => "backups",
- :css => "",
- :icon => "fa-archive",
- :name => "Backups",
- :text => "Manage backups.",
- }, {
- :id => "import",
- :css => "",
- :icon => "fa-upload",
- :name => "Import / Export",
- :text => "Import and export posts.",
- }, {
- :id => "sites",
- :css => "",
- :icon => "fa-sitemap",
- :name => "Sites",
- :text => "Manage sites and domains.",
- }, {
- :id => "themes",
- :css => "",
- :icon => "fa-eye",
- :name => "Themes",
- :text => "Manage themes.",
- }, {
- :id => "users",
- :css => "",
- :icon => "fa-users",
- :name => "Users",
- :text => "Manage users and permissions.",
- }],
- }
-
- TEMPLATES = {
- :option => "
- <option value='%s'>%s</option>
- ",
-
- :new_post_button => "
- <a
- href='#'
- class='btn btn-primary'
- title='Create new blog post, page, or project.'
- data-toggle='dropdown'
- >
- <i class='fa fa-plus-circle'></i>
- Create
- <i class='fa fa-fw fa-caret-down'></i>
- </a>
-
- <ul class='dropdown-menu'>
- <li>
- <a
- href='#'
- title='Create new blog post.'
- class='add-post'
- data-type='blog'
- >
- <i class='fa fa-fw fa-sticky-note-o'></i>
- Blog Post
- </a>
- </li>
-
- <li>
- <a
- href='#'
- title='Create new page.'
- class='add-post'
- data-type='page'
- >
- <i class='fa fa-fw fa-bookmark-o'></i>
- Page
- </a>
- </li>
-
- <li>
- <a
- href='#'
- title='Create new project.'
- class='add-post'
- data-type='project'
- >
- <i class='fa fa-fw fa-cube'></i>
- Project
- </a>
- </li>
- </ul>
- ",
-
- :state_button => "
- <a
- href='#'
- class='btn btn-default'
- title='Mark as %s.'
- data-val='%s'
- >
- <i class='fa %s'></i>
- %s
- </a>
- ",
- }
-
- def tabs(id : String)
- super(id, TABS[id])
- end
-
- private def new_post_button
- TEMPLATES[:new_post_button]
- end
-
- private def role_options
- @role_options ||= String.build do |io|
- @context.models.role.get_roles.each do |row|
- io << TEMPLATES[:option] % %w{role name}.map { |key| h(row[key]) }
- end
- end
- end
-
- private def state_buttons
- @state_buttons ||= String.build do |io|
- @context.models.state.get_states.each do |row|
- io << TEMPLATES[:state_button] % [
- h(row["name"]),
- h(row["state"]),
- h(row["icon"]),
- h(row["name"])
- ]
- end
- end
- end
-
- private def authors_menu_items
- @context.models.user.get_users.map do |row|
- {
- :id => row["user_id"],
- :name => row["name"],
- :text => "Show author \"%s\"." % [row["name"]],
- }
- end
- end
-
- private def sites_menu_items
- @context.models.site.get_sites.map do |row|
- {
- :id => row["site_id"],
- :name => row["name"],
- :text => "Show site \"%s\"." % [row["name"]],
- }
- end
- end
-
- private def states_menu_items
- @context.models.state.get_states.map do |row|
- {
- :id => row["state"],
- :name => row["name"],
- :icon => row["icon"],
- :text => "Show state \"%s\"." % [row["name"]],
- }
- end
- end
-
- ECR.def_to_s("src/views/admin-page.ecr")
- end
-
- class LoginPageView < HTMLView
- def initialize(context : Context, @error : String? = nil)
- super(context)
- end
-
- def get_csrf_token
- @context.models.csrf.create_token
- end
-
- ECR.def_to_s("src/views/login-page.ecr")
- end
-
- class LogoutPageView < HTMLView
- ECR.def_to_s("src/views/logout-page.ecr")
- end
-
- class BlogListItemView < HTMLView
- def initialize(context : Context, @post_id : Int64)
- super(context)
- end
-
- ECR.def_to_s("src/views/blog/list-item.ecr")
- end
-
- #
- # TODO: add y/m/d/page
- #
- class BlogListView < HTMLView
- TITLE = "Blog List"
-
- def initialize(context : Context, @post_ids : Array(Int64))
- super(context)
- end
-
- def posts
- String.build do |io|
- @post_ids.each do |id|
- BlogListItemView.new(@context, id).to_s(io)
- end
- end
- end
-
- ECR.def_to_s("src/views/blog/list.ecr")
- end
- end
-
module Handlers
abstract class Handler < HTTP::Handler
def initialize(@context : Context)
@@ -906,7 +511,7 @@ module Guff
context.response.content_type = "text/html; charset=utf-8"
context.response.status_code = 200
- Views::BlogListView.new(@context, ids).to_s(context.response)
+ Views::Blog::ListView.new(@context, ids).to_s(context.response)
else
# unknown page
call_next(context)
diff --git a/src/guff/views/admin-page.cr b/src/guff/views/admin-page.cr
new file mode 100644
index 0000000..094b5f7
--- /dev/null
+++ b/src/guff/views/admin-page.cr
@@ -0,0 +1,202 @@
+require "./html"
+
+class Guff::Views::AdminPageView < Guff::Views::HTMLView
+ TITLE = "Guff Admin"
+
+ TABS = {
+ "admin" => [{
+ :id => "home",
+ :css => "active",
+ :icon => "fa-home",
+ :name => "Home",
+ :text => "View home tab.",
+ }, {
+ :id => "posts",
+ :css => "",
+ :icon => "fa-cubes",
+ :name => "Posts",
+ :text => "Manage blog, pages, and projects.",
+ }, {
+ :id => "files",
+ :css => "",
+ :icon => "fa-files-o",
+ :name => "Files",
+ :text => "Manage files.",
+ }, {
+ :id => "settings",
+ :css => "",
+ :icon => "fa-cogs",
+ :name => "Settings",
+ :text => "Configure settings.",
+ }],
+
+ "settings" => [{
+ :id => "general",
+ :css => "active",
+ :icon => "fa-cog",
+ :name => "General",
+ :text => "Manage general settings.",
+ }, {
+ :id => "backups",
+ :css => "",
+ :icon => "fa-archive",
+ :name => "Backups",
+ :text => "Manage backups.",
+ }, {
+ :id => "import",
+ :css => "",
+ :icon => "fa-upload",
+ :name => "Import / Export",
+ :text => "Import and export posts.",
+ }, {
+ :id => "sites",
+ :css => "",
+ :icon => "fa-sitemap",
+ :name => "Sites",
+ :text => "Manage sites and domains.",
+ }, {
+ :id => "themes",
+ :css => "",
+ :icon => "fa-eye",
+ :name => "Themes",
+ :text => "Manage themes.",
+ }, {
+ :id => "users",
+ :css => "",
+ :icon => "fa-users",
+ :name => "Users",
+ :text => "Manage users and permissions.",
+ }],
+ }
+
+ TEMPLATES = {
+ :option => "
+ <option value='%s'>%s</option>
+ ",
+
+ :new_post_button => "
+ <a
+ href='#'
+ class='btn btn-primary'
+ title='Create new blog post, page, or project.'
+ data-toggle='dropdown'
+ >
+ <i class='fa fa-plus-circle'></i>
+ Create
+ <i class='fa fa-fw fa-caret-down'></i>
+ </a>
+
+ <ul class='dropdown-menu'>
+ <li>
+ <a
+ href='#'
+ title='Create new blog post.'
+ class='add-post'
+ data-type='blog'
+ >
+ <i class='fa fa-fw fa-sticky-note-o'></i>
+ Blog Post
+ </a>
+ </li>
+
+ <li>
+ <a
+ href='#'
+ title='Create new page.'
+ class='add-post'
+ data-type='page'
+ >
+ <i class='fa fa-fw fa-bookmark-o'></i>
+ Page
+ </a>
+ </li>
+
+ <li>
+ <a
+ href='#'
+ title='Create new project.'
+ class='add-post'
+ data-type='project'
+ >
+ <i class='fa fa-fw fa-cube'></i>
+ Project
+ </a>
+ </li>
+ </ul>
+ ",
+
+ :state_button => "
+ <a
+ href='#'
+ class='btn btn-default'
+ title='Mark as %s.'
+ data-val='%s'
+ >
+ <i class='fa %s'></i>
+ %s
+ </a>
+ ",
+ }
+
+ def tabs(id : String)
+ super(id, TABS[id])
+ end
+
+ private def new_post_button
+ TEMPLATES[:new_post_button]
+ end
+
+ private def role_options
+ @role_options ||= String.build do |io|
+ @context.models.role.get_roles.each do |row|
+ io << TEMPLATES[:option] % %w{role name}.map { |key| h(row[key]) }
+ end
+ end
+ end
+
+ private def state_buttons
+ @state_buttons ||= String.build do |io|
+ @context.models.state.get_states.each do |row|
+ io << TEMPLATES[:state_button] % [
+ h(row["name"]),
+ h(row["state"]),
+ h(row["icon"]),
+ h(row["name"])
+ ]
+ end
+ end
+ end
+
+ private def authors_menu_items
+ @context.models.user.get_users.map do |row|
+ {
+ :id => row["user_id"],
+ :name => row["name"],
+ :text => "Show author \"%s\"." % [row["name"]],
+ }
+ end
+ end
+
+ private def sites_menu_items
+ @context.models.site.get_sites.map do |row|
+ {
+ :id => row["site_id"],
+ :name => row["name"],
+ :text => "Show site \"%s\"." % [row["name"]],
+ }
+ end
+ end
+
+ private def states_menu_items
+ @context.models.state.get_states.map do |row|
+ {
+ :id => row["state"],
+ :name => row["name"],
+ :icon => row["icon"],
+ :text => "Show state \"%s\"." % [row["name"]],
+ }
+ end
+ end
+
+ ECR.def_to_s("src/views/admin-page.ecr")
+end
diff --git a/src/guff/views/blog/list-item.cr b/src/guff/views/blog/list-item.cr
new file mode 100644
index 0000000..3ebe411
--- /dev/null
+++ b/src/guff/views/blog/list-item.cr
@@ -0,0 +1,7 @@
+class Guff::Views::Blog::ListItemView < Guff::Views::HTMLView
+ def initialize(context : Context, @post_id : Int64)
+ super(context)
+ end
+
+ ECR.def_to_s("src/views/blog/list-item.ecr")
+end
diff --git a/src/guff/views/blog/list.cr b/src/guff/views/blog/list.cr
new file mode 100644
index 0000000..31437a6
--- /dev/null
+++ b/src/guff/views/blog/list.cr
@@ -0,0 +1,20 @@
+#
+# TODO: add y/m/d/page
+#
+class Guff::Views::Blog::ListView < Guff::Views::HTMLView
+ TITLE = "Blog List"
+
+ def initialize(context : Context, @post_ids : Array(Int64))
+ super(context)
+ end
+
+ def posts
+ String.build do |io|
+ @post_ids.each do |id|
+ ListItemView.new(@context, id).to_s(io)
+ end
+ end
+ end
+
+ ECR.def_to_s("src/views/blog/list.ecr")
+end
diff --git a/src/guff/views/dropdown/icon.cr b/src/guff/views/dropdown/icon.cr
new file mode 100644
index 0000000..8d30b6e
--- /dev/null
+++ b/src/guff/views/dropdown/icon.cr
@@ -0,0 +1,11 @@
+module Guff::Views::Dropdown::Icon
+ ICON_TEMPLATE = "<i class='fa fa-fw %s'></i>"
+
+ def self.icon(id : String?)
+ if id && id.size > 0
+ ICON_TEMPLATE % [HTML.escape(id.not_nil!)]
+ else
+ ""
+ end
+ end
+end
diff --git a/src/guff/views/dropdown/item.cr b/src/guff/views/dropdown/item.cr
new file mode 100644
index 0000000..22c8d21
--- /dev/null
+++ b/src/guff/views/dropdown/item.cr
@@ -0,0 +1,21 @@
+require "../view"
+
+class Guff::Views::Dropdown::ItemView < Guff::Views::View
+ def initialize(
+ context : Context,
+ @active : Bool,
+ @item : Hash(Symbol, String)
+ )
+ super(context)
+ end
+
+ private def v(id : Symbol)
+ h(@item[id])
+ end
+
+ private def li_css
+ @active ? "class='active'" : ""
+ end
+
+ ECR.def_to_s("src/views/dropdown/item.ecr")
+end
diff --git a/src/guff/views/dropdown/menu.cr b/src/guff/views/dropdown/menu.cr
new file mode 100644
index 0000000..f533fc5
--- /dev/null
+++ b/src/guff/views/dropdown/menu.cr
@@ -0,0 +1,32 @@
+class Guff::Views::Dropdown::MenuView < Guff::Views::View
+ def initialize(
+ context : Context,
+ @id : String,
+ @name : String,
+ @text : String,
+ @css : String,
+ @icon : String,
+ @default : String,
+ @items : Array(Hash(Symbol, String))
+ )
+ super(context)
+
+ @default_name = @items.reduce("") do |r, row|
+ (row[:id]? == @default) ? row[:name] : r
+ end as String
+ end
+
+ private def items
+ String.build do |io|
+ @items.each do |item|
+ io << ItemView.new(
+ context: @context,
+ active: @default == item[:id]?,
+ item: item
+ ).to_s
+ end
+ end
+ end
+
+ ECR.def_to_s("src/views/dropdown/menu.ecr")
+end
diff --git a/src/guff/views/html.cr b/src/guff/views/html.cr
new file mode 100644
index 0000000..d31f183
--- /dev/null
+++ b/src/guff/views/html.cr
@@ -0,0 +1,53 @@
+require "./view"
+
+abstract class Guff::Views::HTMLView < Guff::Views::View
+ TEMPLATES = {
+ script: "<script type='text/javascript' src='%s'></script>",
+ style: "<link rel='stylesheet' type='text/css' href='%s'/>",
+ }
+
+ private def assets(key : Symbol, paths : Array(String))
+ String.build do |io|
+ paths.each do |path|
+ io << TEMPLATES[key] % [h(path)]
+ end
+ end
+ end
+
+ def scripts(paths : Array(String))
+ assets(:script, paths)
+ end
+
+ def styles(paths : Array(String))
+ assets(:style, paths)
+ end
+
+ def tabs(prefix : String, rows : Array(Hash(Symbol, String)))
+ String.build do |io|
+ rows.each do |row|
+ TabView.new(@context, prefix, row).to_s(io)
+ end
+ end
+ end
+
+ def dropdown(
+ id : String,
+ name : String,
+ text : String,
+ icon : String,
+ css : String,
+ default : String,
+ items : Array(Hash(Symbol, String))
+ )
+ Dropdown::MenuView.new(
+ context: @context,
+ id: id,
+ name: name,
+ text: text,
+ icon: icon,
+ css: css,
+ default: default,
+ items: items
+ ).to_s
+ end
+end
diff --git a/src/guff/views/login-page.cr b/src/guff/views/login-page.cr
new file mode 100644
index 0000000..892f3c1
--- /dev/null
+++ b/src/guff/views/login-page.cr
@@ -0,0 +1,13 @@
+require "./view"
+
+class Guff::Views::LoginPageView < Guff::Views::HTMLView
+ def initialize(context : Context, @error : String? = nil)
+ super(context)
+ end
+
+ def get_csrf_token
+ @context.models.csrf.create_token
+ end
+
+ ECR.def_to_s("src/views/login-page.ecr")
+end
diff --git a/src/guff/views/logout-page.cr b/src/guff/views/logout-page.cr
new file mode 100644
index 0000000..164faf7
--- /dev/null
+++ b/src/guff/views/logout-page.cr
@@ -0,0 +1,5 @@
+require "./view"
+
+class Guff::Views::LogoutPageView < Guff::Views::HTMLView
+ ECR.def_to_s("src/views/logout-page.ecr")
+end
diff --git a/src/guff/views/tab.cr b/src/guff/views/tab.cr
new file mode 100644
index 0000000..6a354b4
--- /dev/null
+++ b/src/guff/views/tab.cr
@@ -0,0 +1,21 @@
+require "./view"
+
+class Guff::Views::TabView < Guff::Views::View
+ def initialize(
+ context : Context,
+ @prefix : String,
+ @tab : Hash(Symbol, String)
+ )
+ super(context)
+ @id = h("%s-tab-%s" % [@prefix, @tab[:id]]) as String
+ @target = h("%s-pane-%s" % [@prefix, @tab[:id]]) as String
+ end
+
+ private def v(id : Symbol) : String
+ raise "unknown id: #{id}" unless @tab.has_key?(id)
+ h(@tab[id])
+ end
+
+ ECR.def_to_s("src/views/tab.ecr")
+end
+
diff --git a/src/guff/views/view.cr b/src/guff/views/view.cr
new file mode 100644
index 0000000..0fc92d7
--- /dev/null
+++ b/src/guff/views/view.cr
@@ -0,0 +1,8 @@
+abstract class Guff::Views::View
+ def initialize(@context : Context)
+ end
+
+ def h(s : String) : String
+ HTML.escape(s)
+ end
+end