chef-server-api 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +201 -0
- data/README.rdoc +92 -0
- data/Rakefile +59 -0
- data/app/controllers/application.rb +289 -0
- data/app/controllers/clients.rb +111 -0
- data/app/controllers/cookbooks.rb +213 -0
- data/app/controllers/data.rb +75 -0
- data/app/controllers/data_item.rb +108 -0
- data/app/controllers/exceptions.rb +40 -0
- data/app/controllers/main.rb +18 -0
- data/app/controllers/nodes.rb +102 -0
- data/app/controllers/roles.rb +73 -0
- data/app/controllers/search.rb +60 -0
- data/app/controllers/users.rb +77 -0
- data/app/helpers/application_helper.rb +163 -0
- data/app/helpers/exceptions_helper.rb +6 -0
- data/app/helpers/global_helpers.rb +25 -0
- data/app/helpers/nodes_helper.rb +26 -0
- data/app/helpers/roles_helper.rb +5 -0
- data/app/helpers/tarball_helper.rb +82 -0
- data/app/views/exceptions/bad_request.json.erb +1 -0
- data/app/views/exceptions/internal_server_error.html.erb +216 -0
- data/app/views/exceptions/not_acceptable.html.haml +5 -0
- data/app/views/exceptions/not_found.html.erb +47 -0
- data/app/views/exceptions/standard_error.html.erb +217 -0
- data/app/views/layout/chef_server_api.html.haml +23 -0
- data/app/views/main/index.html.haml +5 -0
- data/config/init.rb +45 -0
- data/config/router.rb +6 -0
- data/lib/chef-server-api.rb +158 -0
- data/lib/chef-server-api/merbtasks.rb +103 -0
- data/lib/chef-server-api/slicetasks.rb +20 -0
- data/lib/chef-server-api/spectasks.rb +53 -0
- data/public/images/avatar.png +0 -0
- data/public/images/indicator.gif +0 -0
- data/public/images/merb.jpg +0 -0
- data/public/stylesheets/base.css +336 -0
- data/public/stylesheets/chef.css +157 -0
- data/public/stylesheets/themes/bec-green/style.css +290 -0
- data/public/stylesheets/themes/bec/style.css +301 -0
- data/public/stylesheets/themes/blue/style.css +280 -0
- data/public/stylesheets/themes/default/style.css +267 -0
- data/public/stylesheets/themes/djime-cerulean/style.css +298 -0
- data/public/stylesheets/themes/kathleene/style.css +272 -0
- data/public/stylesheets/themes/orange/style.css +263 -0
- data/public/stylesheets/themes/reidb-greenish/style.css +301 -0
- data/stubs/app/controllers/application.rb +2 -0
- data/stubs/app/controllers/main.rb +2 -0
- metadata +193 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
!!! XML
|
2
|
+
!!!
|
3
|
+
%html
|
4
|
+
%head
|
5
|
+
%meta{ "http-equiv" => "content-type", :content => "text/html; charset=utf-8" }
|
6
|
+
%title Chef Server
|
7
|
+
= css_include_tag "base", "themes/djime-cerulean/style", "chef"
|
8
|
+
%body
|
9
|
+
#container
|
10
|
+
#header
|
11
|
+
%h1= link_to "Chef REST API", absolute_slice_url(:top)
|
12
|
+
#main-navigation
|
13
|
+
.clear
|
14
|
+
#wrapper
|
15
|
+
#main
|
16
|
+
= catch_content :for_layout
|
17
|
+
#footer
|
18
|
+
.block
|
19
|
+
%p Copyright © 2009 Opscode, Inc.
|
20
|
+
#sidebar
|
21
|
+
.block.notice#sidebar_block_notice= catch_content :sidebar_block_notice
|
22
|
+
.block#sidebar_block= catch_content :sidebar_block
|
23
|
+
.clear
|
data/config/init.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# ==== Standalone Chefserver configuration
|
3
|
+
#
|
4
|
+
# This configuration/environment file is only loaded by bin/slice, which can be
|
5
|
+
# used during development of the slice. It has no effect on this slice being
|
6
|
+
# loaded in a host application. To run your slice in standalone mode, just
|
7
|
+
# run 'slice' from its directory. The 'slice' command is very similar to
|
8
|
+
# the 'merb' command, and takes all the same options, including -i to drop
|
9
|
+
# into an irb session for example.
|
10
|
+
#
|
11
|
+
# The usual Merb configuration directives and init.rb setup methods apply,
|
12
|
+
# including use_orm and before_app_loads/after_app_loads.
|
13
|
+
#
|
14
|
+
# If you need need different configurations for different environments you can
|
15
|
+
# even create the specific environment file in config/environments/ just like
|
16
|
+
# in a regular Merb application.
|
17
|
+
#
|
18
|
+
# In fact, a slice is no different from a normal # Merb application - it only
|
19
|
+
# differs by the fact that seamlessly integrates into a so called 'host'
|
20
|
+
# application, which in turn can override or finetune the slice implementation
|
21
|
+
# code and views.
|
22
|
+
#
|
23
|
+
|
24
|
+
$: << File.join(File.dirname(__FILE__), "..", "..", "chef", "lib")
|
25
|
+
require 'chef'
|
26
|
+
|
27
|
+
merb_gems_version = " > 1.0"
|
28
|
+
dependency "merb-assets", merb_gems_version
|
29
|
+
dependency "merb-helpers", merb_gems_version
|
30
|
+
dependency "chef", :immediate=>true unless defined?(Chef)
|
31
|
+
|
32
|
+
require 'rubygems'
|
33
|
+
|
34
|
+
Merb::Config.use do |c|
|
35
|
+
c[:session_id_key] = '_chef_server_session_id'
|
36
|
+
c[:session_secret_key] = Chef::Config.manage_secret_key
|
37
|
+
c[:session_store] = 'cookie'
|
38
|
+
c[:exception_details] = true
|
39
|
+
c[:reload_classes] = true
|
40
|
+
c[:log_level] = Chef::Config[:log_level]
|
41
|
+
if Chef::Config[:log_location].kind_of?(String)
|
42
|
+
c[:log_file] = Chef::Config[:log_location]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/config/router.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
if defined?(Merb::Plugins)
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "..", "chef-solr", "lib")
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "..", "chef", "lib")
|
5
|
+
|
6
|
+
dependency 'merb-slices', :immediate => true
|
7
|
+
dependency 'chef', :immediate=>true unless defined?(Chef)
|
8
|
+
dependency 'bunny', :immediate=>true
|
9
|
+
dependency 'uuidtools', :immediate=>true
|
10
|
+
|
11
|
+
require 'chef/role'
|
12
|
+
require 'chef/data_bag'
|
13
|
+
require 'chef/data_bag_item'
|
14
|
+
require 'chef/api_client'
|
15
|
+
require 'chef/webui_user'
|
16
|
+
require 'chef/certificate'
|
17
|
+
|
18
|
+
require 'mixlib/authentication'
|
19
|
+
|
20
|
+
require 'chef/data_bag'
|
21
|
+
require 'chef/data_bag_item'
|
22
|
+
require 'ohai'
|
23
|
+
require 'openssl'
|
24
|
+
|
25
|
+
Merb::Plugins.add_rakefiles "chef-server-api/merbtasks", "chef-server-api/slicetasks", "chef-server-api/spectasks"
|
26
|
+
|
27
|
+
# Register the Slice for the current host application
|
28
|
+
Merb::Slices::register(__FILE__)
|
29
|
+
|
30
|
+
Merb.disable :json
|
31
|
+
|
32
|
+
# Slice configuration - set this in a before_app_loads callback.
|
33
|
+
# By default a Slice uses its own layout, so you can switch to
|
34
|
+
# the main application layout or no layout at all if needed.
|
35
|
+
#
|
36
|
+
# Configuration options:
|
37
|
+
# :layout - the layout to use; defaults to :chefserverslice
|
38
|
+
# :mirror - which path component types to use on copy operations; defaults to all
|
39
|
+
Merb::Slices::config[:chef_server_api][:layout] ||= :chef_server_api
|
40
|
+
|
41
|
+
# All Slice code is expected to be namespaced inside a module
|
42
|
+
module ChefServerApi
|
43
|
+
# Slice metadata
|
44
|
+
self.description = "ChefServerApi.. serving up some piping hot infrastructure!"
|
45
|
+
self.version = Chef::VERSION
|
46
|
+
self.author = "Opscode"
|
47
|
+
|
48
|
+
# Stub classes loaded hook - runs before LoadClasses BootLoader
|
49
|
+
# right after a slice's classes have been loaded internally.
|
50
|
+
def self.loaded
|
51
|
+
Chef::Log.info("Compiling routes... (totally normal to see 'Cannot find resource model')")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Initialization hook - runs before AfterAppLoads BootLoader
|
55
|
+
def self.init
|
56
|
+
end
|
57
|
+
|
58
|
+
# Activation hook - runs after AfterAppLoads BootLoader
|
59
|
+
def self.activate
|
60
|
+
Mixlib::Authentication::Log.logger = Ohai::Log.logger = Chef::Log.logger
|
61
|
+
|
62
|
+
unless Merb::Config.environment == "test"
|
63
|
+
# create the couch design docs for nodes, roles, and databags
|
64
|
+
Chef::CouchDB.new.create_id_map
|
65
|
+
Chef::Node.create_design_document
|
66
|
+
Chef::Role.create_design_document
|
67
|
+
Chef::DataBag.create_design_document
|
68
|
+
Chef::ApiClient.create_design_document
|
69
|
+
Chef::WebUIUser.create_design_document
|
70
|
+
|
71
|
+
Chef::Log.info('Loading roles')
|
72
|
+
Chef::Role.sync_from_disk_to_couchdb
|
73
|
+
|
74
|
+
# Create the signing key and certificate
|
75
|
+
Chef::Certificate.generate_signing_ca
|
76
|
+
|
77
|
+
# Generate the validation key
|
78
|
+
Chef::Certificate.gen_validation_key
|
79
|
+
|
80
|
+
# Generate the Web UI Key
|
81
|
+
Chef::Certificate.gen_validation_key(Chef::Config[:web_ui_client_name], Chef::Config[:web_ui_key])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Deactivation hook - triggered by Merb::Slices.deactivate(Chefserver)
|
86
|
+
def self.deactivate
|
87
|
+
end
|
88
|
+
|
89
|
+
# Setup routes inside the host application
|
90
|
+
#
|
91
|
+
# @param scope<Merb::Router::Behaviour>
|
92
|
+
# Routes will be added within this scope (namespace). In fact, any
|
93
|
+
# router behaviour is a valid namespace, so you can attach
|
94
|
+
# routes at any level of your router setup.
|
95
|
+
#
|
96
|
+
# @note prefix your named routes with :chefserverslice_
|
97
|
+
# to avoid potential conflicts with global named routes.
|
98
|
+
def self.setup_router(scope)
|
99
|
+
# Users
|
100
|
+
scope.resources :users
|
101
|
+
|
102
|
+
# Nodes
|
103
|
+
scope.resources :nodes, :id => /[^\/]+/
|
104
|
+
scope.match('/nodes/:id/cookbooks',
|
105
|
+
:id => /[^\/]+/,
|
106
|
+
:method => 'get').
|
107
|
+
to(:controller => "nodes", :action => "cookbooks")
|
108
|
+
# Roles
|
109
|
+
scope.resources :roles
|
110
|
+
|
111
|
+
# Status
|
112
|
+
scope.match("/status").to(:controller => "status", :action => "index").name(:status)
|
113
|
+
|
114
|
+
# Clients
|
115
|
+
scope.match("/clients", :method=>"post").to(:controller=>'clients', :action=>'create')
|
116
|
+
scope.match("/clients", :method=>"get").to(:controller=>'clients', :action=>'index').name(:clients)
|
117
|
+
scope.match("/clients/:id", :id => /[\w\.-]+/, :method=>"get").to(:controller=>'clients', :action=>'show').name(:client)
|
118
|
+
scope.match("/clients/:id", :id => /[\w\.-]+/, :method=>"put").to(:controller=>'clients', :action=>'update')
|
119
|
+
scope.match("/clients/:id", :id => /[\w\.-]+/, :method=>"delete").to(:controller=>'clients', :action=>'destroy')
|
120
|
+
|
121
|
+
# Search
|
122
|
+
scope.resources :search
|
123
|
+
scope.match('/search/reindex', :method => 'post').to(:controller => "search", :action => "reindex")
|
124
|
+
|
125
|
+
# Cookbooks
|
126
|
+
scope.match('/nodes/:id/cookbooks', :method => 'get').to(:controller => "nodes", :action => "cookbooks")
|
127
|
+
|
128
|
+
scope.resources :cookbooks
|
129
|
+
scope.match("/cookbooks/:cookbook_id/_content", :method => 'get', :cookbook_id => /[\w\.]+/).to(:controller => "cookbooks", :action => "get_tarball")
|
130
|
+
scope.match("/cookbooks/:cookbook_id/_content", :method => 'put', :cookbook_id => /[\w\.]+/).to(:controller => "cookbooks", :action => "update")
|
131
|
+
scope.match("/cookbooks/:cookbook_id/:segment", :cookbook_id => /[\w\.]+/).to(:controller => "cookbooks", :action => "show_segment").name(:cookbook_segment)
|
132
|
+
|
133
|
+
# Data
|
134
|
+
scope.match("/data/:data_bag_id/:id", :method => 'get').to(:controller => "data_item", :action => "show").name("data_bag_item")
|
135
|
+
scope.match("/data/:data_bag_id", :method => 'post').to(:controller => "data_item", :action => "create").name("create_data_bag_item")
|
136
|
+
scope.match("/data/:data_bag_id/:id", :method => 'put').to(:controller => "data_item", :action => "update").name("update_data_bag_item")
|
137
|
+
scope.match("/data/:data_bag_id/:id", :method => 'delete').to(:controller => "data_item", :action => "destroy").name("destroy_data_bag_item")
|
138
|
+
scope.resources :data
|
139
|
+
|
140
|
+
scope.match('/').to(:controller => 'main', :action =>'index').name(:top)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
# Setup the slice layout for ChefServerApi
|
146
|
+
#
|
147
|
+
# Use ChefServerApi.push_path and ChefServerApi.push_app_path
|
148
|
+
# to set paths to chefserver-level and app-level paths. Example:
|
149
|
+
#
|
150
|
+
# ChefServerApi.push_path(:application, ChefServerApi.root)
|
151
|
+
# ChefServerApi.push_app_path(:application, Merb.root / 'slices' / 'chefserverslice')
|
152
|
+
# ...
|
153
|
+
#
|
154
|
+
# Any component path that hasn't been set will default to ChefServerApi.root
|
155
|
+
#
|
156
|
+
# Or just call setup_default_structure! to setup a basic Merb MVC structure.
|
157
|
+
ChefServerApi.setup_default_structure!
|
158
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :chefserverslice do
|
3
|
+
|
4
|
+
desc "Install Chefserver"
|
5
|
+
task :install => [:preflight, :setup_directories, :copy_assets, :migrate]
|
6
|
+
|
7
|
+
desc "Test for any dependencies"
|
8
|
+
task :preflight do # see slicetasks.rb
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Setup directories"
|
12
|
+
task :setup_directories do
|
13
|
+
puts "Creating directories for host application"
|
14
|
+
ChefServerApi.mirrored_components.each do |type|
|
15
|
+
if File.directory?(ChefServerApi.dir_for(type))
|
16
|
+
if !File.directory?(dst_path = ChefServerApi.app_dir_for(type))
|
17
|
+
relative_path = dst_path.relative_path_from(Merb.root)
|
18
|
+
puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}"
|
19
|
+
mkdir_p(dst_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# desc "Copy stub files to host application"
|
26
|
+
# task :stubs do
|
27
|
+
# puts "Copying stubs for ChefServerApi - resolves any collisions"
|
28
|
+
# copied, preserved = ChefServerApi.mirror_stubs!
|
29
|
+
# puts "- no files to copy" if copied.empty? && preserved.empty?
|
30
|
+
# copied.each { |f| puts "- copied #{f}" }
|
31
|
+
# preserved.each { |f| puts "! preserved override as #{f}" }
|
32
|
+
# end
|
33
|
+
|
34
|
+
# desc "Copy stub files and views to host application"
|
35
|
+
# task :patch => [ "stubs", "freeze:views" ]
|
36
|
+
|
37
|
+
desc "Copy public assets to host application"
|
38
|
+
task :copy_assets do
|
39
|
+
puts "Copying assets for ChefServerApi - resolves any collisions"
|
40
|
+
copied, preserved = ChefServerApi.mirror_public!
|
41
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
42
|
+
copied.each { |f| puts "- copied #{f}" }
|
43
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Migrate the database"
|
47
|
+
task :migrate do # see slicetasks.rb
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Freeze ChefServerApi into your app (only chefserverslice/app)"
|
51
|
+
task :freeze => [ "freeze:app" ]
|
52
|
+
|
53
|
+
namespace :freeze do
|
54
|
+
|
55
|
+
# desc "Freezes ChefServerApi by installing the gem into application/gems"
|
56
|
+
# task :gem do
|
57
|
+
# ENV["GEM"] ||= "chefserverslice"
|
58
|
+
# Rake::Task['slices:install_as_gem'].invoke
|
59
|
+
# end
|
60
|
+
|
61
|
+
desc "Freezes ChefServerApi by copying all files from chefserverslice/app to your application"
|
62
|
+
task :app do
|
63
|
+
puts "Copying all chefserverslice/app files to your application - resolves any collisions"
|
64
|
+
copied, preserved = ChefServerApi.mirror_app!
|
65
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
66
|
+
copied.each { |f| puts "- copied #{f}" }
|
67
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "Freeze all views into your application for easy modification"
|
71
|
+
task :views do
|
72
|
+
puts "Copying all view templates to your application - resolves any collisions"
|
73
|
+
copied, preserved = ChefServerApi.mirror_files_for :view
|
74
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
75
|
+
copied.each { |f| puts "- copied #{f}" }
|
76
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
77
|
+
end
|
78
|
+
|
79
|
+
desc "Freeze all models into your application for easy modification"
|
80
|
+
task :models do
|
81
|
+
puts "Copying all models to your application - resolves any collisions"
|
82
|
+
copied, preserved = ChefServerApi.mirror_files_for :model
|
83
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
84
|
+
copied.each { |f| puts "- copied #{f}" }
|
85
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "Freezes ChefServerApi as a gem and copies over chefserver/app"
|
89
|
+
task :app_with_gem => [:gem, :app]
|
90
|
+
|
91
|
+
desc "Freezes ChefServerApi by unpacking all files into your application"
|
92
|
+
task :unpack do
|
93
|
+
puts "Unpacking ChefServerApi files to your application - resolves any collisions"
|
94
|
+
copied, preserved = ChefServerApi.unpack_slice!
|
95
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
96
|
+
copied.each { |f| puts "- copied #{f}" }
|
97
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :chefserverslice do
|
3
|
+
|
4
|
+
# add your own chefserver tasks here
|
5
|
+
|
6
|
+
# # Uncomment the following lines and edit the pre defined tasks
|
7
|
+
#
|
8
|
+
# # implement this to test for structural/code dependencies
|
9
|
+
# # like certain directories or availability of other files
|
10
|
+
# desc "Test for any dependencies"
|
11
|
+
# task :preflight do
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # implement this to perform any database related setup steps
|
15
|
+
# desc "Migrate the database"
|
16
|
+
# task :migrate do
|
17
|
+
# end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :chefserverslice do
|
3
|
+
|
4
|
+
desc "Run slice specs within the host application context"
|
5
|
+
task :spec => [ "spec:explain", "spec:default" ]
|
6
|
+
|
7
|
+
namespace :spec do
|
8
|
+
|
9
|
+
slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
10
|
+
|
11
|
+
task :explain do
|
12
|
+
puts "\nNote: By running ChefServerApi specs inside the application context any\n" +
|
13
|
+
"overrides could break existing specs. This isn't always a problem,\n" +
|
14
|
+
"especially in the case of views. Use these spec tasks to check how\n" +
|
15
|
+
"well your application conforms to the original slice implementation."
|
16
|
+
end
|
17
|
+
|
18
|
+
Spec::Rake::SpecTask.new('default') do |t|
|
19
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
20
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
|
24
|
+
Spec::Rake::SpecTask.new('model') do |t|
|
25
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
26
|
+
if(ENV['MODEL'])
|
27
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
|
28
|
+
else
|
29
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Run all request specs, run a spec for a specific request with REQUEST=MyRequest"
|
34
|
+
Spec::Rake::SpecTask.new('request') do |t|
|
35
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
36
|
+
if(ENV['REQUEST'])
|
37
|
+
t.spec_files = Dir["#{slice_root}/spec/requests/**/#{ENV['REQUEST']}_spec.rb"].sort
|
38
|
+
else
|
39
|
+
t.spec_files = Dir["#{slice_root}/spec/requests/**/*_spec.rb"].sort
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Run all specs and output the result in html"
|
44
|
+
Spec::Rake::SpecTask.new('html') do |t|
|
45
|
+
t.spec_opts = ["--format", "html"]
|
46
|
+
t.libs = ['lib', 'server/lib' ]
|
47
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,336 @@
|
|
1
|
+
* {margin:0;padding:0}
|
2
|
+
.clear { clear: both; height: 0; }
|
3
|
+
|
4
|
+
h1 { margin: 15px 0; font-size: 22px; font-weight: normal; }
|
5
|
+
h2 { font-size: 22px; margin: 15px 0; font-weight: normal;}
|
6
|
+
h3 { font-size: 18px; margin: 10px 0; font-weight: normal;}
|
7
|
+
h4 { font-size: 16px; margin: 10px 0; font-weight: normal;}
|
8
|
+
hr {height: 1px; border: 0; }
|
9
|
+
p { margin: 15px 0;}
|
10
|
+
a img { border: none; }
|
11
|
+
|
12
|
+
body {
|
13
|
+
font-size: 12px;
|
14
|
+
font-family: sans-serif;
|
15
|
+
}
|
16
|
+
|
17
|
+
#container {
|
18
|
+
min-width: 960px;
|
19
|
+
}
|
20
|
+
|
21
|
+
#header, #wrapper {
|
22
|
+
padding: 0 20px;
|
23
|
+
}
|
24
|
+
|
25
|
+
#header {
|
26
|
+
position: relative;
|
27
|
+
padding-top: 1px;
|
28
|
+
}
|
29
|
+
|
30
|
+
#header h1 {
|
31
|
+
margin: 0;
|
32
|
+
padding: 10px 0;
|
33
|
+
font-size: 30px;
|
34
|
+
}
|
35
|
+
|
36
|
+
#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
|
37
|
+
text-decoration: none;
|
38
|
+
}
|
39
|
+
|
40
|
+
#main {
|
41
|
+
width: 80%;
|
42
|
+
float: left;
|
43
|
+
}
|
44
|
+
|
45
|
+
.actions-bar {
|
46
|
+
padding: 10px 1px;
|
47
|
+
}
|
48
|
+
|
49
|
+
.actions-bar .actions {
|
50
|
+
float: left;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
.actions-bar .pagination {
|
55
|
+
float: right;
|
56
|
+
padding: 1px 0;
|
57
|
+
}
|
58
|
+
|
59
|
+
#sidebar {
|
60
|
+
width: 15%;
|
61
|
+
float: right;
|
62
|
+
}
|
63
|
+
|
64
|
+
#sidebar h3 {
|
65
|
+
padding: 10px 15px;
|
66
|
+
margin: 0;
|
67
|
+
font-size: 13px;
|
68
|
+
}
|
69
|
+
|
70
|
+
#sidebar .block {
|
71
|
+
margin-bottom: 20px;
|
72
|
+
padding-bottom: 10px;
|
73
|
+
}
|
74
|
+
|
75
|
+
#sidebar .block .content {
|
76
|
+
padding: 0 15px;
|
77
|
+
}
|
78
|
+
|
79
|
+
#sidebar ul.navigation li a:link, #sidebar ul.navigation li a:visited {
|
80
|
+
display: block;
|
81
|
+
padding: 10px 15px;
|
82
|
+
}
|
83
|
+
|
84
|
+
#sidebar .block .sidebar-block, #sidebar .notice {
|
85
|
+
padding:10px;
|
86
|
+
}
|
87
|
+
|
88
|
+
#wrapper {
|
89
|
+
padding-top: 20px;
|
90
|
+
}
|
91
|
+
|
92
|
+
#main .block {
|
93
|
+
margin-bottom: 20px;
|
94
|
+
padding-top: 1px;
|
95
|
+
}
|
96
|
+
|
97
|
+
#main .block .content .inner {
|
98
|
+
padding: 0 15px 15px;
|
99
|
+
}
|
100
|
+
|
101
|
+
#main .main p.first {
|
102
|
+
margin-top: 0;
|
103
|
+
}
|
104
|
+
|
105
|
+
#user-navigation {
|
106
|
+
position: absolute;
|
107
|
+
top: 0px;
|
108
|
+
right: 20px;
|
109
|
+
}
|
110
|
+
|
111
|
+
#main-navigation {
|
112
|
+
width: 100%;
|
113
|
+
}
|
114
|
+
|
115
|
+
#user-navigation ul, #main-navigation ul, .secondary-navigation ul, #sidebar ul.navigation {
|
116
|
+
margin: 0;
|
117
|
+
padding: 0;
|
118
|
+
list-style-type: none;
|
119
|
+
}
|
120
|
+
|
121
|
+
#user-navigation ul li, #main-navigation ul li, .secondary-navigation ul li {
|
122
|
+
float: left;
|
123
|
+
}
|
124
|
+
|
125
|
+
#main-navigation ul li {
|
126
|
+
margin-right: 5px;
|
127
|
+
}
|
128
|
+
|
129
|
+
#user-navigation ul li {
|
130
|
+
padding: 5px 10px;
|
131
|
+
}
|
132
|
+
|
133
|
+
#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
|
134
|
+
.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
|
135
|
+
#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
|
136
|
+
text-decoration: none;
|
137
|
+
}
|
138
|
+
|
139
|
+
#main-navigation ul li a {
|
140
|
+
font-size: 15px;
|
141
|
+
display: block;
|
142
|
+
padding: 8px 15px;
|
143
|
+
}
|
144
|
+
|
145
|
+
.secondary-navigation {
|
146
|
+
font-size: 13px;
|
147
|
+
border-bottom-width: 10px;
|
148
|
+
border-bottom-style: solid;
|
149
|
+
}
|
150
|
+
|
151
|
+
.secondary-navigation ul li a {
|
152
|
+
display: block;
|
153
|
+
padding: 10px 15px;
|
154
|
+
}
|
155
|
+
|
156
|
+
#footer {
|
157
|
+
padding-bottom: 20px;
|
158
|
+
}
|
159
|
+
|
160
|
+
/* pagination */
|
161
|
+
|
162
|
+
.pagination a, .pagination span {
|
163
|
+
padding: 2px 5px;
|
164
|
+
margin-right: 5px;
|
165
|
+
display: block;
|
166
|
+
float: left;
|
167
|
+
border-style: solid;
|
168
|
+
border-width: 1px;
|
169
|
+
}
|
170
|
+
|
171
|
+
.pagination span.current {
|
172
|
+
font-weight: bold;
|
173
|
+
}
|
174
|
+
|
175
|
+
.pagination a {
|
176
|
+
text-decoration: none;
|
177
|
+
}
|
178
|
+
|
179
|
+
/* tables */
|
180
|
+
.table {
|
181
|
+
width: 100%;
|
182
|
+
border-collapse: collapse;
|
183
|
+
margin-bottom: 15px;
|
184
|
+
}
|
185
|
+
|
186
|
+
.table th {
|
187
|
+
padding: 10px;
|
188
|
+
font-weight: bold;
|
189
|
+
text-align: left;
|
190
|
+
}
|
191
|
+
|
192
|
+
.table th.first {
|
193
|
+
width: 30px;
|
194
|
+
}
|
195
|
+
|
196
|
+
.table th.last {
|
197
|
+
width: 200px;
|
198
|
+
}
|
199
|
+
|
200
|
+
.table .checkbox {
|
201
|
+
margin-left: 10px;
|
202
|
+
}
|
203
|
+
|
204
|
+
.table td {
|
205
|
+
padding: 10px;
|
206
|
+
}
|
207
|
+
|
208
|
+
.table td.last {
|
209
|
+
text-align: right;
|
210
|
+
}
|
211
|
+
|
212
|
+
/* forms */
|
213
|
+
|
214
|
+
input.checkbox {
|
215
|
+
margin: 0;
|
216
|
+
padding: 0;
|
217
|
+
}
|
218
|
+
|
219
|
+
.form .group {
|
220
|
+
margin-bottom: 15px;
|
221
|
+
}
|
222
|
+
|
223
|
+
.form div.left {
|
224
|
+
width: 20%;
|
225
|
+
float: left;
|
226
|
+
}
|
227
|
+
|
228
|
+
.form div.right {
|
229
|
+
width: 75%;
|
230
|
+
float: right;
|
231
|
+
}
|
232
|
+
|
233
|
+
.form .columns .column {
|
234
|
+
width: 48%;
|
235
|
+
}
|
236
|
+
|
237
|
+
.form .columns .left {
|
238
|
+
float: left;
|
239
|
+
}
|
240
|
+
|
241
|
+
.form .columns .right {
|
242
|
+
float: right;
|
243
|
+
}
|
244
|
+
|
245
|
+
.form label.label, .form input.text_field, .form textarea.text_area {
|
246
|
+
font-size: 1.2em;
|
247
|
+
padding: 1px 0;
|
248
|
+
margin: 0;
|
249
|
+
}
|
250
|
+
|
251
|
+
.form label.right {
|
252
|
+
text-align: right;
|
253
|
+
}
|
254
|
+
|
255
|
+
.form input.checkbox, .form input.radio {
|
256
|
+
margin-right: 5px;
|
257
|
+
}
|
258
|
+
|
259
|
+
.form label.checkbox, .form label.radio {
|
260
|
+
line-height: 1.5em;
|
261
|
+
}
|
262
|
+
|
263
|
+
.form label.label {
|
264
|
+
display: block;
|
265
|
+
padding-bottom: 2px;
|
266
|
+
font-weight: bold;
|
267
|
+
}
|
268
|
+
|
269
|
+
.form div.fieldWithErrors label.label {
|
270
|
+
display: inline;
|
271
|
+
}
|
272
|
+
|
273
|
+
.form .fieldWithErrors .error {
|
274
|
+
color: red;
|
275
|
+
}
|
276
|
+
|
277
|
+
.form input.text_field, .form textarea.text_area {
|
278
|
+
width: 100%;
|
279
|
+
border-width: 1px;
|
280
|
+
border-style: solid;
|
281
|
+
}
|
282
|
+
|
283
|
+
/* lists */
|
284
|
+
|
285
|
+
ul.list {
|
286
|
+
margin: 0;
|
287
|
+
padding: 0;
|
288
|
+
list-style-type: none;
|
289
|
+
}
|
290
|
+
|
291
|
+
ul.list li {
|
292
|
+
clear: left;
|
293
|
+
padding-bottom: 5px;
|
294
|
+
}
|
295
|
+
|
296
|
+
ul.list li .left {
|
297
|
+
float: left;
|
298
|
+
}
|
299
|
+
|
300
|
+
ul.list li .left .avatar {
|
301
|
+
width: 50px;
|
302
|
+
height: 50px;
|
303
|
+
}
|
304
|
+
|
305
|
+
ul.list li .item {
|
306
|
+
margin-left: 80px;
|
307
|
+
}
|
308
|
+
|
309
|
+
ul.list li .item .avatar {
|
310
|
+
float: left;
|
311
|
+
margin: 0 5px 5px 0;
|
312
|
+
width: 30px;
|
313
|
+
height: 30px;
|
314
|
+
}
|
315
|
+
|
316
|
+
/* box */
|
317
|
+
|
318
|
+
#box {
|
319
|
+
width: 500px;
|
320
|
+
margin: 50px auto;
|
321
|
+
}
|
322
|
+
|
323
|
+
#box .block {
|
324
|
+
margin-bottom: 20px;
|
325
|
+
}
|
326
|
+
|
327
|
+
#box .block h2 {
|
328
|
+
padding: 10px 15px;
|
329
|
+
margin: 0;
|
330
|
+
}
|
331
|
+
|
332
|
+
#box .block .content {
|
333
|
+
padding: 10px 20px;
|
334
|
+
}
|
335
|
+
|
336
|
+
|