populate-me 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +655 -0
- data/Rakefile +14 -0
- data/example/config.ru +100 -0
- data/lib/populate_me.rb +2 -0
- data/lib/populate_me/admin.rb +157 -0
- data/lib/populate_me/admin/__assets__/css/asmselect.css +63 -0
- data/lib/populate_me/admin/__assets__/css/jquery-ui.min.css +6 -0
- data/lib/populate_me/admin/__assets__/css/main.css +244 -0
- data/lib/populate_me/admin/__assets__/img/help/children.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/create.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/delete.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/edit.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/form.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/list.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/login.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/logout.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/menu.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/overview.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/save.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/sort.png +0 -0
- data/lib/populate_me/admin/__assets__/img/help/sublist.png +0 -0
- data/lib/populate_me/admin/__assets__/js/asmselect.js +412 -0
- data/lib/populate_me/admin/__assets__/js/columnav.js +87 -0
- data/lib/populate_me/admin/__assets__/js/jquery-ui.min.js +7 -0
- data/lib/populate_me/admin/__assets__/js/main.js +388 -0
- data/lib/populate_me/admin/__assets__/js/mustache.js +578 -0
- data/lib/populate_me/admin/__assets__/js/sortable.js +2 -0
- data/lib/populate_me/admin/views/help.erb +94 -0
- data/lib/populate_me/admin/views/page.erb +189 -0
- data/lib/populate_me/api.rb +124 -0
- data/lib/populate_me/attachment.rb +186 -0
- data/lib/populate_me/document.rb +192 -0
- data/lib/populate_me/document_mixins/admin_adapter.rb +149 -0
- data/lib/populate_me/document_mixins/callbacks.rb +125 -0
- data/lib/populate_me/document_mixins/outcasting.rb +83 -0
- data/lib/populate_me/document_mixins/persistence.rb +95 -0
- data/lib/populate_me/document_mixins/schema.rb +198 -0
- data/lib/populate_me/document_mixins/typecasting.rb +70 -0
- data/lib/populate_me/document_mixins/validation.rb +44 -0
- data/lib/populate_me/file_system_attachment.rb +40 -0
- data/lib/populate_me/grid_fs_attachment.rb +103 -0
- data/lib/populate_me/mongo.rb +160 -0
- data/lib/populate_me/s3_attachment.rb +120 -0
- data/lib/populate_me/variation.rb +38 -0
- data/lib/populate_me/version.rb +4 -0
- data/populate-me.gemspec +34 -0
- data/test/helper.rb +37 -0
- data/test/test_admin.rb +183 -0
- data/test/test_api.rb +246 -0
- data/test/test_attachment.rb +167 -0
- data/test/test_document.rb +128 -0
- data/test/test_document_admin_adapter.rb +221 -0
- data/test/test_document_callbacks.rb +151 -0
- data/test/test_document_outcasting.rb +247 -0
- data/test/test_document_persistence.rb +83 -0
- data/test/test_document_schema.rb +280 -0
- data/test/test_document_typecasting.rb +128 -0
- data/test/test_grid_fs_attachment.rb +239 -0
- data/test/test_mongo.rb +324 -0
- data/test/test_s3_attachment.rb +281 -0
- data/test/test_variation.rb +91 -0
- data/test/test_version.rb +11 -0
- metadata +294 -0
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
task :default => :test
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.pattern = 'test/test_*.rb'
|
8
|
+
unless ENV['TESTONLY'].nil?
|
9
|
+
t.pattern = t.pattern.sub(/\*/, ENV['TESTONLY'])
|
10
|
+
end
|
11
|
+
t.options = '--pride'
|
12
|
+
t.warning = false
|
13
|
+
end
|
14
|
+
|
data/example/config.ru
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# We use this example for running real tests on top of unit tests.
|
2
|
+
# This is not meant to be an example to learn how PopulateMe would work.
|
3
|
+
# Real examples will come later and this file will be removed.
|
4
|
+
|
5
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
6
|
+
|
7
|
+
# Models ##########
|
8
|
+
|
9
|
+
require 'populate_me/document'
|
10
|
+
|
11
|
+
# MONGO = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'blog-populate-me-test')
|
12
|
+
# DB = MONGO.database
|
13
|
+
# PopulateMe::Mongo.set :db, DB
|
14
|
+
|
15
|
+
require 'populate_me/attachment'
|
16
|
+
PopulateMe::Document.set :default_attachment_class, PopulateMe::Attachment
|
17
|
+
# require 'populate_me/file_system_attachment'
|
18
|
+
# PopulateMe::Document.set :default_attachment_class, PopulateMe::FileSystemAttachment
|
19
|
+
#
|
20
|
+
# require 'populate_me/grid_fs_attachment'
|
21
|
+
# PopulateMe::Mongo.set :default_attachment_class, PopulateMe::GridFS
|
22
|
+
# PopulateMe::GridFS.set :db, DB
|
23
|
+
|
24
|
+
# require 'populate_me/s3_attachment'
|
25
|
+
# s3_resource = Aws::S3::Resource.new
|
26
|
+
# s3_bucket = s3_resource.bucket(ENV['BUCKET'])
|
27
|
+
# PopulateMe::Document.set :default_attachment_class, PopulateMe::S3Attachment
|
28
|
+
# PopulateMe::S3Attachment.set :bucket, s3_bucket
|
29
|
+
|
30
|
+
|
31
|
+
class BlogPost < PopulateMe::Document
|
32
|
+
field :title, required: true
|
33
|
+
field :thumbnail, type: :attachment, max_size: 600*1024, variations: [
|
34
|
+
PopulateMe::Variation.new_image_magick_job(:populate_me_thumb, :jpg, "-resize '400x225^' -gravity center -extent 400x225")
|
35
|
+
]
|
36
|
+
field :content, type: :text
|
37
|
+
field :authors, type: :list
|
38
|
+
field :published, type: :boolean
|
39
|
+
field :date_now, type: :date, input_attributes: {type: :date}
|
40
|
+
field :datetime_now, type: :datetime, input_attributes: {type: :datetime}
|
41
|
+
relationship :comments
|
42
|
+
def validate
|
43
|
+
error_on(:content,'Cannot be blank') if WebUtils.blank?(self.content)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
class BlogPost::Author < PopulateMe::Document
|
48
|
+
# nested
|
49
|
+
field :name
|
50
|
+
def validate
|
51
|
+
error_on(:name, 'Cannot be shit') if self.name=='shit'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
class BlogPost::Comment < PopulateMe::Document
|
55
|
+
# not nested
|
56
|
+
field :author, default: 'Anonymous'
|
57
|
+
field :content, type: :text
|
58
|
+
field :blog_post_id, type: :hidden
|
59
|
+
position_field scope: :blog_post_id
|
60
|
+
end
|
61
|
+
|
62
|
+
class Article < PopulateMe::Document
|
63
|
+
field :title
|
64
|
+
field :content, type: :text
|
65
|
+
field :yes_or_no, type: :select, select_options: [:yes,:no]
|
66
|
+
field :tags, type: :select, multiple: true, select_options: ['art','sport','science']
|
67
|
+
field :price, type: :price
|
68
|
+
position_field
|
69
|
+
|
70
|
+
relationship :sections
|
71
|
+
|
72
|
+
after :save do
|
73
|
+
puts self.inspect
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class Article::Section < PopulateMe::Document
|
78
|
+
|
79
|
+
field :short, only_for: 'Nice Short'
|
80
|
+
field :long, only_for: 'Nice Long'
|
81
|
+
field :article_id, type: :hidden
|
82
|
+
position_field scope: :article_id
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
# Admin ##########
|
87
|
+
|
88
|
+
require "populate_me/admin"
|
89
|
+
class Admin < PopulateMe::Admin
|
90
|
+
set :menu, [
|
91
|
+
['Blog Posts', '/list/blog-post'],
|
92
|
+
['Articles', '/list/article'],
|
93
|
+
]
|
94
|
+
end
|
95
|
+
|
96
|
+
use PopulateMe::Attachment::Middleware
|
97
|
+
# use PopulateMe::FileSystemAttachment::Middleware
|
98
|
+
# use PopulateMe::GridFS::Middleware
|
99
|
+
run Admin
|
100
|
+
|
data/lib/populate_me.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'populate_me/api'
|
2
|
+
require 'json'
|
3
|
+
require 'tilt/erb'
|
4
|
+
begin
|
5
|
+
require "rack/cerberus"
|
6
|
+
rescue LoadError
|
7
|
+
puts "Cerberus not loaded."
|
8
|
+
end
|
9
|
+
|
10
|
+
class PopulateMe::Admin < Sinatra::Base
|
11
|
+
|
12
|
+
# Settings
|
13
|
+
set :app_file, nil # Need to be set when subclassed
|
14
|
+
set :show_exceptions, false
|
15
|
+
set :meta_title, 'Populate Me'
|
16
|
+
set :index_path, '/menu'
|
17
|
+
enable :cerberus
|
18
|
+
set :cerberus_active, Proc.new{
|
19
|
+
cerberus_available? &&
|
20
|
+
!!cerberus_pass &&
|
21
|
+
settings.cerberus?
|
22
|
+
}
|
23
|
+
set :logout_path, Proc.new{ settings.cerberus_active ? '/logout' : false }
|
24
|
+
|
25
|
+
# Load API helpers
|
26
|
+
helpers PopulateMe::API::Helpers
|
27
|
+
helpers do
|
28
|
+
|
29
|
+
def user_name
|
30
|
+
return 'Anonymous' if session.nil?||session[:populate_me_user].nil?
|
31
|
+
session[:populate_me_user]
|
32
|
+
end
|
33
|
+
|
34
|
+
def help_img desc, filename
|
35
|
+
"<img src='#{request.script_name}/__assets__/img/help/#{filename}' alt='#{desc}' />"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# Make all templates in admin/views accessible with their basename
|
41
|
+
Dir["#{File.expand_path('../admin/views',__FILE__)}/*.erb"].each do |f|
|
42
|
+
template File.basename(f,'.erb').to_sym do
|
43
|
+
File.read(f)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
before do
|
48
|
+
content_type :json
|
49
|
+
end
|
50
|
+
|
51
|
+
get '/?' do
|
52
|
+
content_type :html
|
53
|
+
erb :page, layout: false
|
54
|
+
end
|
55
|
+
|
56
|
+
get '/menu/?*?' do
|
57
|
+
page_title = 'Menu'
|
58
|
+
current_level = settings.menu.dup
|
59
|
+
levels = params[:splat][0].split('/')
|
60
|
+
levels.each do |l|
|
61
|
+
page_title, current_level = current_level.find{|item| slugify(item[0])==l}
|
62
|
+
end
|
63
|
+
items = current_level.map do |l|
|
64
|
+
href = l[1].is_a?(String) ? l[1] : "#{request.script_name}/menu#{levels.map{|level|'/'+level}.join}/#{slugify(l[0])}"
|
65
|
+
{ title: l[0], href: href, new_page: (not href.start_with?('/')) }
|
66
|
+
end
|
67
|
+
if request.path_info=='/menu'
|
68
|
+
items.push({title: '?', href: "#{request.script_name}/help", new_page: false})
|
69
|
+
end
|
70
|
+
{
|
71
|
+
template: 'template_menu',
|
72
|
+
page_title: page_title,
|
73
|
+
items: items
|
74
|
+
}.to_json
|
75
|
+
end
|
76
|
+
|
77
|
+
get '/list/:class_name' do
|
78
|
+
@model_class = resolve_model_class params[:class_name]
|
79
|
+
@model_class.to_admin_list(request: request, params: params).to_json
|
80
|
+
end
|
81
|
+
|
82
|
+
get '/form/?:class_name?/?:id?' do
|
83
|
+
@model_class = resolve_model_class params[:class_name]
|
84
|
+
if params[:id].nil?
|
85
|
+
@model_instance = @model_class.new.set_defaults
|
86
|
+
@model_instance.set_from_hash(params[:data], typecast: true) unless params[:data].nil?
|
87
|
+
else
|
88
|
+
@model_instance = resolve_model_instance @model_class, params[:id]
|
89
|
+
end
|
90
|
+
@model_instance.to_admin_form(
|
91
|
+
request: request,
|
92
|
+
params: params,
|
93
|
+
nested: params[:nested]=='true',
|
94
|
+
input_name_prefix: params[:input_name_prefix]
|
95
|
+
).to_json
|
96
|
+
end
|
97
|
+
|
98
|
+
get '/help' do
|
99
|
+
content_type :html
|
100
|
+
erb :help, layout: false
|
101
|
+
end
|
102
|
+
|
103
|
+
not_found do
|
104
|
+
response.headers['X-Cascade'] = 'pass'
|
105
|
+
{'success'=>false,'message'=>'Not Found'}.to_json
|
106
|
+
end
|
107
|
+
|
108
|
+
error do
|
109
|
+
puts
|
110
|
+
puts env['sinatra.error'].inspect
|
111
|
+
puts
|
112
|
+
{'success'=>false,'message'=>env['sinatra.error'].message}.to_json
|
113
|
+
end
|
114
|
+
|
115
|
+
class << self
|
116
|
+
|
117
|
+
def cerberus_pass
|
118
|
+
# Method = overridable = testable
|
119
|
+
ENV['CERBERUS_PASS']
|
120
|
+
end
|
121
|
+
def cerberus_available?
|
122
|
+
# Method = overridable = testable
|
123
|
+
Rack.const_defined?(:Cerberus)
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def setup_default_middleware builder
|
129
|
+
# Override the Sinatra method
|
130
|
+
super builder
|
131
|
+
setup_populate_me_middleware builder
|
132
|
+
end
|
133
|
+
|
134
|
+
def setup_populate_me_middleware builder
|
135
|
+
# Authentication
|
136
|
+
setup_cerberus builder
|
137
|
+
# Mount assets on /__assets__
|
138
|
+
builder.use Rack::Static, :urls=>['/__assets__'], :root=>File.expand_path('../admin',__FILE__)
|
139
|
+
# Mount the API on /api
|
140
|
+
builder.use Rack::Builder do
|
141
|
+
map('/api'){ run PopulateMe::API }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def setup_cerberus builder
|
146
|
+
return unless settings.cerberus_active
|
147
|
+
cerberus_settings = settings.cerberus==true ? {} : settings.cerberus
|
148
|
+
cerberus_settings[:session_key] = 'populate_me_user'
|
149
|
+
builder.use Rack::Cerberus, cerberus_settings do |user,pass,req|
|
150
|
+
pass==cerberus_pass
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
.asmContainer {
|
2
|
+
/* container that surrounds entire asmSelect widget */
|
3
|
+
}
|
4
|
+
|
5
|
+
.asmSelect {
|
6
|
+
/* the newly created regular 'select' */
|
7
|
+
display: inline;
|
8
|
+
}
|
9
|
+
|
10
|
+
.asmOptionDisabled {
|
11
|
+
/* disabled options in new select */
|
12
|
+
color: #999;
|
13
|
+
}
|
14
|
+
|
15
|
+
.asmHighlight {
|
16
|
+
/* the highlight span */
|
17
|
+
padding: 0;
|
18
|
+
margin: 0 0 0 1em;
|
19
|
+
}
|
20
|
+
|
21
|
+
.asmList {
|
22
|
+
/* html list that contains selected items */
|
23
|
+
margin: 0.25em 0 1em 0;
|
24
|
+
position: relative;
|
25
|
+
display: block;
|
26
|
+
padding-left: 0;
|
27
|
+
list-style: none;
|
28
|
+
}
|
29
|
+
|
30
|
+
.asmListItem {
|
31
|
+
/* li item from the html list above */
|
32
|
+
position: relative;
|
33
|
+
margin-left: 0;
|
34
|
+
padding-left: 0;
|
35
|
+
list-style: none;
|
36
|
+
background: #ddd;
|
37
|
+
border: 1px solid #bbb;
|
38
|
+
width: 100%;
|
39
|
+
margin: 0 0 -1px 0;
|
40
|
+
line-height: 1em;
|
41
|
+
}
|
42
|
+
|
43
|
+
.asmListItem:hover {
|
44
|
+
background-color: #e5e5e5;
|
45
|
+
}
|
46
|
+
|
47
|
+
.asmListItemLabel {
|
48
|
+
/* this is a span that surrounds the text in the item, except for the remove link */
|
49
|
+
padding: 5px;
|
50
|
+
display: block;
|
51
|
+
}
|
52
|
+
|
53
|
+
.asmListSortable .asmListItemLabel {
|
54
|
+
cursor: move;
|
55
|
+
}
|
56
|
+
|
57
|
+
.asmListItemRemove {
|
58
|
+
/* the remove link in each list item */
|
59
|
+
position: absolute;
|
60
|
+
right: 0;
|
61
|
+
top: 0;
|
62
|
+
padding: 5px;
|
63
|
+
}
|
@@ -0,0 +1,244 @@
|
|
1
|
+
/* General */
|
2
|
+
|
3
|
+
html, body {
|
4
|
+
margin: 0;
|
5
|
+
padding: 0;
|
6
|
+
color: #002b36;
|
7
|
+
background-color: #eee8d5;
|
8
|
+
height: 100%;
|
9
|
+
font-family: 'Avenir', sans-serif;
|
10
|
+
}
|
11
|
+
|
12
|
+
body {
|
13
|
+
line-height: 1.5em;
|
14
|
+
}
|
15
|
+
|
16
|
+
#finder {
|
17
|
+
height: 100%;
|
18
|
+
}
|
19
|
+
|
20
|
+
.column {
|
21
|
+
background: #fdf6e3;
|
22
|
+
height: 100%;
|
23
|
+
min-width: 250px;
|
24
|
+
overflow: auto;
|
25
|
+
box-shadow: 0px 0px 10px #586e75;
|
26
|
+
}
|
27
|
+
|
28
|
+
.column > h1, .column > ol,
|
29
|
+
.column > form, .column > p { /* Column padding */
|
30
|
+
margin-left: 20px;
|
31
|
+
margin-right: 20px;
|
32
|
+
}
|
33
|
+
|
34
|
+
.column > h1,
|
35
|
+
.column > .text-page h1 {
|
36
|
+
font-size: 1.5em;
|
37
|
+
font-weight: normal;
|
38
|
+
margin-top: 1em;
|
39
|
+
margin-bottom: 1.5em;
|
40
|
+
}
|
41
|
+
|
42
|
+
.column > .text-page {
|
43
|
+
padding: 0 20px 20px 20px;
|
44
|
+
overflow: hidden;
|
45
|
+
width: 50%; width: 50vw;
|
46
|
+
max-width: 600px;
|
47
|
+
background-color: #ffffff;
|
48
|
+
}
|
49
|
+
.column > .text-page h2 {
|
50
|
+
font-size: 1.5em;
|
51
|
+
}
|
52
|
+
.column > .text-page img {
|
53
|
+
width: 100%;
|
54
|
+
}
|
55
|
+
.column > .text-page a {
|
56
|
+
color: #268bd2;
|
57
|
+
}
|
58
|
+
.column > .text-page a:hover {
|
59
|
+
text-decoration: underline;
|
60
|
+
}
|
61
|
+
.column > .text-page .frame {
|
62
|
+
padding: 1em;
|
63
|
+
border: 1px solid #002b36;
|
64
|
+
}
|
65
|
+
|
66
|
+
ol {
|
67
|
+
list-style: none;
|
68
|
+
margin: 0;
|
69
|
+
padding: 0;
|
70
|
+
}
|
71
|
+
|
72
|
+
a {
|
73
|
+
color: #002b36;
|
74
|
+
text-decoration: none;
|
75
|
+
/* border-bottom: 1px solid #002b36; */
|
76
|
+
}
|
77
|
+
a:hover, a.selected {
|
78
|
+
/* Blue */
|
79
|
+
color: #268bd2;
|
80
|
+
/* Green/Blue */
|
81
|
+
/* color: #2aa198; */
|
82
|
+
}
|
83
|
+
|
84
|
+
button { cursor: pointer; }
|
85
|
+
|
86
|
+
/* Lists */
|
87
|
+
|
88
|
+
.new-document-btn-wrap > * {
|
89
|
+
vertical-align: middle;
|
90
|
+
}
|
91
|
+
|
92
|
+
.new-document-btn, .new-nested-document-btn {
|
93
|
+
border: 0;
|
94
|
+
font-size: 1.615em;
|
95
|
+
margin-bottom: 1em;
|
96
|
+
}
|
97
|
+
|
98
|
+
button.admin-delete, button.admin-delete-nested, .handle-button {
|
99
|
+
display: inline-block; vertical-align: middle;
|
100
|
+
font-size: 1em; line-height: 1em;
|
101
|
+
width: auto; height: auto;
|
102
|
+
margin: 0;
|
103
|
+
padding: 0.5em 0.5em 0.5em 0;
|
104
|
+
border: 0px;
|
105
|
+
}
|
106
|
+
button.admin-delete, button.admin-delete-nested {
|
107
|
+
color: #dc322f;
|
108
|
+
background-color: transparent;
|
109
|
+
}
|
110
|
+
button.admin-delete:focus, button.admin-delete-nested:focus, .handle-button:focus {
|
111
|
+
outline: 0;
|
112
|
+
}
|
113
|
+
.handle {
|
114
|
+
cursor: move;
|
115
|
+
cursor: -moz-grab;
|
116
|
+
cursor: -webkit-grab;
|
117
|
+
cursor: grab;
|
118
|
+
}
|
119
|
+
.handle:active {
|
120
|
+
cursor: -moz-grabbing;
|
121
|
+
cursor: -webkit-grabbing;
|
122
|
+
cursor: grabbing;
|
123
|
+
}
|
124
|
+
|
125
|
+
[type=submit] {
|
126
|
+
border: 0px;
|
127
|
+
cursor: pointer;
|
128
|
+
color: #fdf6e3;
|
129
|
+
background-color: #859900;
|
130
|
+
}
|
131
|
+
[type=submit]:hover {
|
132
|
+
background-color: #268bd2;
|
133
|
+
}
|
134
|
+
|
135
|
+
.documents, .nested-documents {
|
136
|
+
-webkit-touch-callout: none;
|
137
|
+
-webkit-user-select: none;
|
138
|
+
-khtml-user-select: none;
|
139
|
+
-moz-user-select: none;
|
140
|
+
-ms-user-select: none;
|
141
|
+
user-select: none;
|
142
|
+
}
|
143
|
+
.documents.grid {
|
144
|
+
width: 90%; width: 90vw;
|
145
|
+
}
|
146
|
+
.documents > li, .nested-documents > li {
|
147
|
+
display: block;
|
148
|
+
margin-bottom: 1em;
|
149
|
+
}
|
150
|
+
.documents.grid > li {
|
151
|
+
display: inline-block;
|
152
|
+
vertical-align: top;
|
153
|
+
width: 200px;
|
154
|
+
margin-right: 1em;
|
155
|
+
}
|
156
|
+
.documents.grid > li > a {
|
157
|
+
word-break: break-word;
|
158
|
+
}
|
159
|
+
.documents.grid > li img {
|
160
|
+
width: 200px;
|
161
|
+
}
|
162
|
+
.documents > li:hover, .nested-documents > li:hover {
|
163
|
+
background-color: #eee8d5;
|
164
|
+
outline: 0.5em solid #eee8d5;
|
165
|
+
}
|
166
|
+
.nested-documents > li > header {
|
167
|
+
background: #002b36;
|
168
|
+
height: 1.5em;
|
169
|
+
margin-bottom: 1em;
|
170
|
+
}
|
171
|
+
.documents .sortable-placeholder, .nested-documents .sortable-placeholder {
|
172
|
+
border: 1px dashed #839496;
|
173
|
+
background-color: #eee8d5;
|
174
|
+
}
|
175
|
+
.local-menu { font-size: 0.7em; }
|
176
|
+
|
177
|
+
/* Forms */
|
178
|
+
|
179
|
+
form {
|
180
|
+
margin-bottom: 2em;
|
181
|
+
}
|
182
|
+
|
183
|
+
.field {
|
184
|
+
margin-bottom: 1em;
|
185
|
+
}
|
186
|
+
|
187
|
+
input[type=text], input[type=email],
|
188
|
+
textarea {
|
189
|
+
border: 0px;
|
190
|
+
color: #839496;
|
191
|
+
background: #ffffff;
|
192
|
+
width: 400px;
|
193
|
+
padding: 0.5em;
|
194
|
+
font-size: 1em;
|
195
|
+
}
|
196
|
+
input[type=text]:focus, input[type=email]:focus,
|
197
|
+
textarea:focus {
|
198
|
+
/* color: #002b36; */
|
199
|
+
}
|
200
|
+
|
201
|
+
textarea { height: 6em; resize: vertical; }
|
202
|
+
textarea.oneline { height: 1em; resize: none; }
|
203
|
+
|
204
|
+
fieldset {
|
205
|
+
border: 0;
|
206
|
+
padding-left: 3em; padding-right: 0;
|
207
|
+
}
|
208
|
+
|
209
|
+
.invalid > label {
|
210
|
+
color: #dc322f;
|
211
|
+
}
|
212
|
+
|
213
|
+
/* asmSelect */
|
214
|
+
|
215
|
+
.asmListItem {
|
216
|
+
background-color: #ffffff;
|
217
|
+
border: 1px solid #fdf6e3;
|
218
|
+
}
|
219
|
+
.asmListItem:hover { background-color: #eee8d5; }
|
220
|
+
.asmListItemLabel { padding-right: 2em; }
|
221
|
+
.asmListSortable .asmListItemLabel {
|
222
|
+
cursor: move;
|
223
|
+
cursor: -moz-grab;
|
224
|
+
cursor: -webkit-grab;
|
225
|
+
cursor: grab;
|
226
|
+
}
|
227
|
+
.asmListSortable .asmListItemLabel:active {
|
228
|
+
cursor: -moz-grabbing;
|
229
|
+
cursor: -webkit-grabbing;
|
230
|
+
cursor: grabbing;
|
231
|
+
}
|
232
|
+
.asmListItemRemove, .asmListItemRemove:hover { color: #dc322f; }
|
233
|
+
|
234
|
+
|
235
|
+
/\
|
236
|
+
/ *\
|
237
|
+
/ # \
|
238
|
+
/ * \
|
239
|
+
/ # \
|
240
|
+
/ # * \
|
241
|
+
/ * # \
|
242
|
+
/______________\
|
243
|
+
|
244
|
+
|