yodel_development_environment 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/lib/layouts/common.html +25 -0
- data/lib/layouts/common/default_user.html +7 -0
- data/lib/layouts/common/remote.html +13 -0
- data/lib/layouts/common/remotes_page.html +33 -0
- data/lib/layouts/common/setup.html +10 -0
- data/lib/layouts/common/setup_remote.html +12 -0
- data/lib/layouts/common/site.html +9 -0
- data/lib/layouts/common/sites_page.html +75 -0
- data/lib/migrations/site/01_sites_page_model.rb +11 -0
- data/lib/migrations/site/02_remotes_page_model.rb +11 -0
- data/lib/migrations/site/03_default_user_model.rb +13 -0
- data/lib/migrations/site/04_page_structure.rb +68 -0
- data/lib/migrations/yodel/01_record_model.rb +29 -0
- data/lib/migrations/yodel/02_page_model.rb +40 -0
- data/lib/migrations/yodel/03_layout_model.rb +38 -0
- data/lib/migrations/yodel/04_group_model.rb +61 -0
- data/lib/migrations/yodel/05_user_model.rb +23 -0
- data/lib/migrations/yodel/06_snippet_model.rb +13 -0
- data/lib/migrations/yodel/07_search_page_model.rb +32 -0
- data/lib/migrations/yodel/08_default_site_options.rb +21 -0
- data/lib/migrations/yodel/09_security_page_models.rb +36 -0
- data/lib/migrations/yodel/10_record_proxy_page_model.rb +17 -0
- data/lib/migrations/yodel/11_email_model.rb +28 -0
- data/lib/migrations/yodel/12_api_call_model.rb +23 -0
- data/lib/migrations/yodel/13_redirect_page_model.rb +13 -0
- data/lib/migrations/yodel/14_menu_model.rb +20 -0
- data/lib/models/development_sites_page.rb +177 -0
- data/lib/models/remotes_page.rb +42 -0
- data/lib/public/css/screen.css +59 -0
- data/lib/public/js/main.js +18 -0
- data/lib/site_template/attachments/.gitkeep +0 -0
- data/lib/site_template/gitignore +2 -0
- data/lib/site_template/layouts/page.html +18 -0
- data/lib/site_template/layouts/page/.gitkeep +0 -0
- data/lib/site_template/migrations/extensions/.gitkeep +0 -0
- data/lib/site_template/migrations/site/.gitkeep +0 -0
- data/lib/site_template/partials/.gitkeep +0 -0
- data/lib/site_template/public/.gitkeep +0 -0
- data/lib/site_template/public/css/main.css +1 -0
- data/lib/site_template/public/js/main.js +1 -0
- data/lib/yodel_development_environment.rb +3 -0
- data/yodel_development_environment.gemspec +22 -0
- metadata +90 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<link rel="stylesheet" href="/core/css/reset.css" type="text/css">
|
5
|
+
<link rel="stylesheet" href="/core/css/core.css" type="text/css">
|
6
|
+
<link rel="stylesheet" href="/css/screen.css" type="text/css">
|
7
|
+
<script src="/core/js/jquery.min.js"></script>
|
8
|
+
<script src="/core/js/json2.js"></script>
|
9
|
+
<script src="/core/js/yodel_jquery.js"></script>
|
10
|
+
<script src="/js/main.js"></script>
|
11
|
+
<title>Yodel</title>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<article>
|
15
|
+
<header>
|
16
|
+
<div id="lip"></div>
|
17
|
+
<h1>yodel</h1>
|
18
|
+
<% if site.default_users.count > 0 %>
|
19
|
+
<%= menu :nav %>
|
20
|
+
<% end %>
|
21
|
+
</header>
|
22
|
+
<%= content %>
|
23
|
+
</article>
|
24
|
+
</body>
|
25
|
+
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<h1><%= record.name %></h1>
|
2
|
+
|
3
|
+
<% form_for record, remote: true do |form| %>
|
4
|
+
<%= form.field_row :name %>
|
5
|
+
<%= form.field_row :url %>
|
6
|
+
<%= form.field_row :username %>
|
7
|
+
<%= form.field_row :password %>
|
8
|
+
<input type="submit" value='Save'>
|
9
|
+
<button onclick="window.location = '/remotes'">Cancel</button>
|
10
|
+
<% form.success do %>
|
11
|
+
window.location = '/remotes';
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<ul>
|
2
|
+
<% if Remote.count > 0 %>
|
3
|
+
<% Remote.all.each do |remote| %>
|
4
|
+
<li>
|
5
|
+
<a href="<%= path %>?id=<%= remote.id %>"><%= remote.name %></a>
|
6
|
+
<%= delete_link 'Delete', wrap: ["<input type='hidden' name='id' value='#{remote.id}'>", nil], class: 'action', confirm: 'Are you sure you want to delete this remote?' %>
|
7
|
+
</li>
|
8
|
+
<% end %>
|
9
|
+
<% else %>
|
10
|
+
<li>No remotes</li>
|
11
|
+
<% end %>
|
12
|
+
</ul>
|
13
|
+
<a class="action" href="#" id="add_remote">Add Remote</a>
|
14
|
+
|
15
|
+
<div id="new_remote" style="display: none; padding-top: 30px; padding-bottom: 30px">
|
16
|
+
<% form_for_new_record remote: true do |form| %>
|
17
|
+
<%= form.field_row :name %>
|
18
|
+
<%= form.field_row :url %>
|
19
|
+
<%= form.field_row :username %>
|
20
|
+
<%= form.field_row :password %>
|
21
|
+
<input type="submit" value='Add'>
|
22
|
+
<% form.success do %>
|
23
|
+
window.location = '/remotes';
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<script>
|
29
|
+
$('#add_remote').click(function(event) {
|
30
|
+
$('#new_remote').slideToggle();
|
31
|
+
event.preventDefault();
|
32
|
+
})
|
33
|
+
</script>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<p>All Yodel sites have an admin panel which requires a username and password to access. Enter the details you would like to use to login. You can change these details on a site by site basis, and for all new sites by visiting setup again.</p>
|
2
|
+
<% page('/default-user').form_for_new_record remote: true do |form| %>
|
3
|
+
<%= form.field_row :name %>
|
4
|
+
<%= form.field_row :email %>
|
5
|
+
<%= form.field_row :password %>
|
6
|
+
<input type="submit" value="Save">
|
7
|
+
<% form.success do %>
|
8
|
+
window.location = '/setup-remote';
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<p>You can publish your sites to a remote Yodel server by entering your details below. You can skip this step and add a remote server later.</p>
|
2
|
+
<% page('/remotes').form_for Remote.new(name: 'Yodel CMS', url: 'http://yodelcms.com/', username: site.default_users.first.email), remote: true do |form| %>
|
3
|
+
<%= form.field_row :name %>
|
4
|
+
<%= form.field_row :url %>
|
5
|
+
<%= form.field_row :username %>
|
6
|
+
<%= form.field_row :password %>
|
7
|
+
<input type="submit" value="Save">
|
8
|
+
<% form.success do %>
|
9
|
+
window.location = '/sites';
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
12
|
+
<a href="/sites" class="action">Skip to Sites</a>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<h1><%= record.name %></h1>
|
2
|
+
<p>Enter the root directory for this site. The previous root directory was:<br><%= record.root_directory %></p>
|
3
|
+
<% form_for record, remote: true do |form| %>
|
4
|
+
<%= form.field_row :root_directory %>
|
5
|
+
<input type="submit" value="Save">
|
6
|
+
<% form.success do %>
|
7
|
+
window.location = 'http://<%= record.domains.first %><% if request.port != 80 %>:<%= request.port %><% end %>';
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
@@ -0,0 +1,75 @@
|
|
1
|
+
<h1>Local Sites</h1>
|
2
|
+
<ul>
|
3
|
+
<% if Site.all.count == 1 %>
|
4
|
+
<li>No sites</li>
|
5
|
+
<% else %>
|
6
|
+
<% Site.all.each do |site| %>
|
7
|
+
<% next if site.name == 'yodel' %>
|
8
|
+
<li class="site" data-site-id="<%= site.id %>">
|
9
|
+
<a href="http://<%= site.domains.first %><% if request.port != 80 %>:<%= request.port %><% end %>/"><%= site.name %></a>
|
10
|
+
<a class="action" href="http://<%= site.domains.first %><% if request.port != 80 %>:<%= request.port %><% end %>/admin/">Visit Admin</a>
|
11
|
+
<%= delete_link 'Delete', wrap: ["<input type='hidden' name='id' value='#{site.id}'>", nil], class: 'action', confirm: 'Are you sure you want to delete this site? Unless you have deployed it to a remote server, you will not be able to recover it.' %>
|
12
|
+
</li>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
</ul>
|
16
|
+
<h1>Remote Sites</h1>
|
17
|
+
<ul class="remotes">
|
18
|
+
<% if Remote.count > 0 %>
|
19
|
+
<% Remote.all.each do |remote| %>
|
20
|
+
<li data-remote="<%= remote.id %>" data-remote-name="<%= remote.name %>">Loading <%= remote.name %>... <img src="/core/images/spinner.gif"></li>
|
21
|
+
<% end %>
|
22
|
+
<% else %>
|
23
|
+
<li>No remote sites</li>
|
24
|
+
<% end %>
|
25
|
+
</ul>
|
26
|
+
|
27
|
+
<a class="action" href="#" id="add_site">Create Site</a>
|
28
|
+
|
29
|
+
<div id="new_site" style="<% if flash[:error].nil? %>display: none; <% end %>padding-top: 30px; padding-bottom: 30px">
|
30
|
+
<form action="/sites" method="post">
|
31
|
+
<div>
|
32
|
+
<label>Name</label>
|
33
|
+
<div>
|
34
|
+
<input type="text" name="name"> <input type="submit" value='Create'>
|
35
|
+
<% if flash[:error] %>
|
36
|
+
<span class="yodel-form-activity invalid"><%= flash[:error] %></span>
|
37
|
+
<% end %>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
</form>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<script>
|
44
|
+
$('#add_site').click(function(event) {
|
45
|
+
$('#new_site').slideToggle();
|
46
|
+
event.preventDefault();
|
47
|
+
});
|
48
|
+
|
49
|
+
var sitesRemaining = $('li[data-remote]').length;
|
50
|
+
var sites = 0;
|
51
|
+
// FIXME: need error handler here
|
52
|
+
$('li[data-remote]').each(function(index, row) {
|
53
|
+
row = $(row);
|
54
|
+
$.ajax('/remotes.json?id=' + row.attr('data-remote'), {dataType: 'json', success: function(data) {
|
55
|
+
if(data.success) {
|
56
|
+
for(var i = 0; i < data.sites.length; i++) {
|
57
|
+
var site = data.sites[i];
|
58
|
+
if($('li[data-site-id=' + site.id + ']').length == 0) {
|
59
|
+
$('.remotes').append("<li class='remote_site'>" + site.name + " <span class='remote_name'>(" + row.attr('data-remote-name') + ")</span><a href='#' data-remote='" + row.attr('data-remote') + "' data-remote-id='" + site.id + "' data-site-name='" + site.name + "' class='action'>Clone</a></li>");
|
60
|
+
sites += 1;
|
61
|
+
}
|
62
|
+
}
|
63
|
+
row.remove();
|
64
|
+
} else {
|
65
|
+
row.html(row.attr('data-remote-name') + ": " + data.reason);
|
66
|
+
}
|
67
|
+
}, complete: function() {
|
68
|
+
sitesRemaining -= 1;
|
69
|
+
if(sitesRemaining == 0 && sites == 0) {
|
70
|
+
$('.remotes').append("<li>No uncloned remote sites</li>");
|
71
|
+
}
|
72
|
+
}});
|
73
|
+
});
|
74
|
+
</script>
|
75
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class SitesPageModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.record_proxy_pages.create_model :sites_page do |sites_pages|
|
4
|
+
sites_pages.record_class_name = 'DevelopmentSitesPage'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.down(site)
|
9
|
+
site.sites_pages.destroy
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class RemoteModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.record_proxy_pages.create_model :remotes_page do |remotes_pages|
|
4
|
+
remotes_pages.record_class_name = 'RemotesPage'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.down(site)
|
9
|
+
sites.remotes_pages.destroy
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class DefaultUserModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :default_user do |default_users|
|
4
|
+
add_field :name, :string, validations: {required: {}}
|
5
|
+
add_field :email, :email, validations: {required: {}}
|
6
|
+
add_field :password, :password, validations: {required: {}}
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down(site)
|
11
|
+
site.default_users.destroy
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class PageStructureMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
# remove the default home page
|
4
|
+
home = site.pages.where(path: '/').first
|
5
|
+
home.destroy
|
6
|
+
|
7
|
+
# home redirects to sites
|
8
|
+
home = site.redirect_pages.new
|
9
|
+
home.title = 'Home'
|
10
|
+
home.url = '/sites'
|
11
|
+
home.save
|
12
|
+
|
13
|
+
# sites
|
14
|
+
sites = site.sites_pages.new
|
15
|
+
sites.title = 'Sites'
|
16
|
+
sites.parent = home
|
17
|
+
sites.show_record_layout = 'site'
|
18
|
+
sites.save
|
19
|
+
|
20
|
+
# remotes
|
21
|
+
remotes = site.remotes_pages.new
|
22
|
+
remotes.title = 'Remotes'
|
23
|
+
remotes.parent = home
|
24
|
+
remotes.show_record_layout = 'remote'
|
25
|
+
remotes.save
|
26
|
+
|
27
|
+
# default user
|
28
|
+
default_user = site.record_proxy_pages.new
|
29
|
+
default_user.title = 'Default User'
|
30
|
+
default_user.parent = home
|
31
|
+
default_user.record_model = site.default_users
|
32
|
+
default_user.page_layout = 'default_user'
|
33
|
+
default_user.after_update_page = default_user
|
34
|
+
default_user.save
|
35
|
+
|
36
|
+
# second setup step (done first because the first step refns this)
|
37
|
+
setup_two = site.pages.new
|
38
|
+
setup_two.title = 'Setup Remote'
|
39
|
+
setup_two.parent = home
|
40
|
+
setup_two.page_layout = 'setup_remote'
|
41
|
+
setup_two.save
|
42
|
+
|
43
|
+
# initial setup
|
44
|
+
setup = site.pages.new
|
45
|
+
setup.title = 'Setup'
|
46
|
+
setup.parent = home
|
47
|
+
setup.page_layout = 'setup'
|
48
|
+
setup.save
|
49
|
+
|
50
|
+
# menu
|
51
|
+
nav = site.menus.new
|
52
|
+
nav.name = 'nav'
|
53
|
+
nav.root = home
|
54
|
+
s1 = nav.exceptions.new
|
55
|
+
s1.page = setup
|
56
|
+
s1.show = false
|
57
|
+
s1.save
|
58
|
+
s2 = nav.exceptions.new
|
59
|
+
s2.page = setup_two
|
60
|
+
s2.show = false
|
61
|
+
s2.save
|
62
|
+
nav.save
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.down(site)
|
66
|
+
site.pages.all.each(&:destroy)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class RecordModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
records = Model.new(site, name: 'Record')
|
4
|
+
records.modify do |records|
|
5
|
+
# identity, hierarchy and search
|
6
|
+
add_many :children, model: :record, foreign_key: 'parent', order: 'index asc', display: false
|
7
|
+
add_field :index, :integer, validations: {required: {}}, display: false
|
8
|
+
add_one :owner, model: :user, display: false
|
9
|
+
add_field :name, :string
|
10
|
+
add_field :show_in_search, :boolean, default: true, section: 'Options'
|
11
|
+
add_field :search_keywords, :array, of: :string, display: false
|
12
|
+
add_field :search_title, :string, display: false
|
13
|
+
|
14
|
+
# modelling
|
15
|
+
add_one :eigenmodel, model: :model, destroy: true, display: false
|
16
|
+
add_one :parent, model: :record, index: true, display: false
|
17
|
+
add_one :model, index: true, display: false
|
18
|
+
records.descendants = [records]
|
19
|
+
end
|
20
|
+
|
21
|
+
site.model_types['records'] = records.id
|
22
|
+
site.model_plural_names['Record'] = 'records'
|
23
|
+
site.save
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.down(site)
|
27
|
+
site.records.destroy
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class PageModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :pages do |pages|
|
4
|
+
# core page attributes
|
5
|
+
add_field :permalink, :string, validations: {required: {}}, index: true, searchable: false, display: false
|
6
|
+
add_field :path, :string, validations: {required: {}}, index: true, searchable: false, display: false
|
7
|
+
add_field :created_at, :time, display: false
|
8
|
+
add_field :title, :string, validations: {required: {}}
|
9
|
+
add_field :content, :html
|
10
|
+
|
11
|
+
# options section
|
12
|
+
add_field :show_in_menus, :boolean, default: true, section: 'Options'
|
13
|
+
add_field :description, :text, section: 'Options', searchable: false
|
14
|
+
add_field :keywords, :text, section: 'Options', searchable: false
|
15
|
+
add_field :custom_meta_tags, :text, section: 'Options', searchable: false
|
16
|
+
add_one :new_child_page, model: :page, section: 'Options', show_blank: true, blank_text: 'None'
|
17
|
+
|
18
|
+
# layout
|
19
|
+
add_field :page_layout, :string, section: 'Options', default: nil, searchable: false
|
20
|
+
add_one :page_layout_record, model: :layout, display: false
|
21
|
+
add_field :edit_layout, :string, searchable: false, section: 'Options'
|
22
|
+
add_one :edit_layout_record, model: :layout, display: false
|
23
|
+
|
24
|
+
add_field :name, :alias, of: :title
|
25
|
+
pages.default_child_model = pages.id
|
26
|
+
pages.allowed_children = [pages]
|
27
|
+
pages.allowed_parents = [pages]
|
28
|
+
pages.record_class_name = 'Page'
|
29
|
+
end
|
30
|
+
|
31
|
+
# default root page
|
32
|
+
page = site.pages.new
|
33
|
+
page.title = "Home"
|
34
|
+
page.save
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.down(site)
|
38
|
+
site.pages.destroy
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class LayoutModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :layouts do |layouts|
|
4
|
+
add_field :name, :string, validations: {required: {}}, index: true
|
5
|
+
add_field :mime_type, :string, validations: {required: {}}, index: true
|
6
|
+
|
7
|
+
layouts.allowed_children = []
|
8
|
+
layouts.allowed_parents = []
|
9
|
+
layouts.searchable = false
|
10
|
+
layouts.record_class_name = 'Layout'
|
11
|
+
end
|
12
|
+
|
13
|
+
site.layouts.create_model :persistent_layouts do |persistent_layouts|
|
14
|
+
add_field :markup, :html, validations: {required: {}}
|
15
|
+
add_many :pages, foreign_key: 'page_layout_record'
|
16
|
+
|
17
|
+
persistent_layouts.allowed_children = [persistent_layouts]
|
18
|
+
persistent_layouts.allowed_parents = [persistent_layouts]
|
19
|
+
persistent_layouts.searchable = false
|
20
|
+
persistent_layouts.record_class_name = 'PersistentLayout'
|
21
|
+
end
|
22
|
+
|
23
|
+
site.layouts.create_model :file_layouts do |file_layouts|
|
24
|
+
add_field :path, :string, validations: {required: {}}
|
25
|
+
|
26
|
+
file_layouts.allowed_children = [file_layouts]
|
27
|
+
file_layouts.allowed_parents = [file_layouts]
|
28
|
+
file_layouts.searchable = false
|
29
|
+
file_layouts.record_class_name = 'FileLayout'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.down(site)
|
34
|
+
site.layouts.destroy
|
35
|
+
site.persistent_layouts.destroy
|
36
|
+
site.file_layouts.destroy
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class GroupModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :groups do |groups|
|
4
|
+
add_field :name, :string, validations: {required: {}}
|
5
|
+
add_many :users, store: false
|
6
|
+
groups.icon = '/admin/images/group_icon.png'
|
7
|
+
groups.record_class_name = 'Group'
|
8
|
+
end
|
9
|
+
site.reload
|
10
|
+
|
11
|
+
# a special singleton group representing an 'owner' of a record
|
12
|
+
site.groups.create_model :owner_groups do |group|
|
13
|
+
group.record_class_name = 'OwnerGroup'
|
14
|
+
end
|
15
|
+
site.reload
|
16
|
+
|
17
|
+
# a special singleton group representing no one
|
18
|
+
site.groups.create_model :noone_groups do |group|
|
19
|
+
group.record_class_name = 'NooneGroup'
|
20
|
+
end
|
21
|
+
site.reload
|
22
|
+
|
23
|
+
# a special singleton group representing 'everyone'
|
24
|
+
site.groups.create_model :guest_groups do |group|
|
25
|
+
group.record_class_name = 'GuestsGroup'
|
26
|
+
end
|
27
|
+
site.reload
|
28
|
+
|
29
|
+
|
30
|
+
# permissions are based on a hierarchy of groups. branches are permitted.
|
31
|
+
noone = site.noone_groups.new(name: 'No One')
|
32
|
+
noone.save
|
33
|
+
|
34
|
+
devs = site.groups.new(name: 'Developers')
|
35
|
+
devs.parent = noone
|
36
|
+
devs.save
|
37
|
+
|
38
|
+
admins = site.groups.new(name: 'Administrators')
|
39
|
+
admins.parent = devs
|
40
|
+
admins.save
|
41
|
+
|
42
|
+
owner = site.owner_groups.new(name: 'Owner')
|
43
|
+
owner.parent = admins
|
44
|
+
owner.save
|
45
|
+
|
46
|
+
users = site.groups.new(name: 'Users')
|
47
|
+
users.parent = owner
|
48
|
+
users.save
|
49
|
+
|
50
|
+
guests = site.guest_groups.new(name: 'Guests')
|
51
|
+
guests.parent = users
|
52
|
+
guests.save
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.down(site)
|
56
|
+
site.groups.destroy
|
57
|
+
site.owner_groups.destroy
|
58
|
+
site.noone_groups.destroy
|
59
|
+
site.guest_groups.destroy
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class UserModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :users do |users|
|
4
|
+
add_field :first_name, :string
|
5
|
+
add_field :last_name, :string
|
6
|
+
add_field :email, :email, validations: {required: {}, unique: {}}, searchable: false
|
7
|
+
add_field :oauth_id, :string, index: true, searchable: false
|
8
|
+
add_field :username, :string, index: true, validations: {required: {}, unique: {}}, searchable: false
|
9
|
+
add_field :password, :password, validations: {required: {}}, searchable: false
|
10
|
+
add_field :password_salt, :string, display: false, searchable: false
|
11
|
+
add_many :groups, default: [site.groups['Users'].id]
|
12
|
+
add_field :owner, :self
|
13
|
+
|
14
|
+
add_field :name, :function, fn: 'format("{{first_name}} {{last_name}}").strip()'
|
15
|
+
users.icon = '/admin/images/user_icon.png'
|
16
|
+
users.record_class_name = 'User'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.down(site)
|
21
|
+
site.users.destroy
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class SnippetModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :snippets do |snippets|
|
4
|
+
add_field :name, :string, validations: {required: {}}, index: true
|
5
|
+
add_field :content, :text
|
6
|
+
snippets.searchable = false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down(site)
|
11
|
+
site.snippets.destroy
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class SearchPageModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
operators = [ 'Equals', 'Not Equal', 'Greater Than',
|
4
|
+
'Less Than', 'Greater Than or Equal To',
|
5
|
+
'Less Than or Equal To', 'In']
|
6
|
+
|
7
|
+
site.pages.create_model :search_pages do |search_pages|
|
8
|
+
add_field :sort, :string, searchable: false
|
9
|
+
add_field :limit, :integer
|
10
|
+
add_field :skip, :integer
|
11
|
+
add_one :type, model: :model
|
12
|
+
|
13
|
+
add_embed_many :conditions do
|
14
|
+
add_field :name, :string
|
15
|
+
add_field :value, :string
|
16
|
+
add_field :operator, :enum, options: operators
|
17
|
+
end
|
18
|
+
|
19
|
+
add_embed_many :user_conditions, default: [{name: 'search_keywords', as: 'query', operator: 'In'}] do
|
20
|
+
add_field :name, :string
|
21
|
+
add_field :as, :string
|
22
|
+
add_field :operator, :enum, options: operators
|
23
|
+
end
|
24
|
+
|
25
|
+
search_pages.record_class_name = 'SearchPage'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.down(site)
|
30
|
+
site.search_pages.destroy
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class DefaultSiteOptionsMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.options = {
|
4
|
+
pages: {
|
5
|
+
permalink_character: {
|
6
|
+
description: 'When Yodel creates a URL for a page by using the title of the page, there are sometimes characters (such as spaces) that need to be replaced. This character will be used in their place. e.g "About Us" would become "about-us".',
|
7
|
+
type: 'String',
|
8
|
+
default: '-',
|
9
|
+
value: '-'
|
10
|
+
}
|
11
|
+
},
|
12
|
+
icon: nil
|
13
|
+
}
|
14
|
+
site.save
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.down(site)
|
18
|
+
site.options = {}
|
19
|
+
site.save
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class SecurityPageModelsMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.pages.create_model :login_pages do |login_pages|
|
4
|
+
add_field :username_field, :string, validations: {required: {}}, default: 'username'
|
5
|
+
add_field :password_field, :string, validations: {required: {}}, default: 'password'
|
6
|
+
add_one :redirect_to, model: :page
|
7
|
+
login_pages.record_class_name = 'LoginPage'
|
8
|
+
end
|
9
|
+
|
10
|
+
site.pages.create_model :logout_pages do |logout_pages|
|
11
|
+
add_one :redirect_to, model: :page
|
12
|
+
logout_pages.record_class_name = 'LogoutPage'
|
13
|
+
end
|
14
|
+
|
15
|
+
site.pages.create_model :password_reset_pages do |password_reset_pages|
|
16
|
+
add_field :success, :html, default: 'Thank you, your password has been emailed to your email address.'
|
17
|
+
add_field :email_field, :string, validations: {required: {}}, default: 'email'
|
18
|
+
password_reset_pages.record_class_name = 'PasswordResetPage'
|
19
|
+
end
|
20
|
+
|
21
|
+
site.pages.create_model :facebook_login_pages do |facebook_login_pages|
|
22
|
+
add_field :callback_uri, :string
|
23
|
+
add_field :app_id, :string
|
24
|
+
add_field :app_secret, :string
|
25
|
+
add_one :join_page, model: :page
|
26
|
+
add_one :after_login_page, model: :page
|
27
|
+
facebook_login_pages.record_class_name = 'FacebookLoginPage'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.down(site)
|
32
|
+
site.login_pages.destroy
|
33
|
+
site.logout_pages.destroy
|
34
|
+
site.password_reset_pages.destroy
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class RecordProxyPageModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.pages.create_model :record_proxy_pages do |record_proxy_pages|
|
4
|
+
add_one :record_model, model: :model
|
5
|
+
add_one :after_create_page, model: :page
|
6
|
+
add_one :after_delete_page, model: :page
|
7
|
+
add_one :after_update_page, model: :page
|
8
|
+
add_field :show_record_layout, :string
|
9
|
+
add_one :show_record_layout_record, model: :layout, display: false
|
10
|
+
record_proxy_pages.record_class_name = 'RecordProxyPage'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.down(site)
|
15
|
+
site.record_proxy_pages.destroy
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class EmailModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :emails do |emails|
|
4
|
+
add_field :name, :string
|
5
|
+
add_field :from, :string
|
6
|
+
add_field :to, :string
|
7
|
+
add_field :cc, :string
|
8
|
+
add_field :bcc, :string
|
9
|
+
add_field :subject, :string
|
10
|
+
add_field :text_body, :text
|
11
|
+
add_field :html_body, :html
|
12
|
+
add_field :html_layout, :string
|
13
|
+
emails.record_class_name = 'Email'
|
14
|
+
end
|
15
|
+
|
16
|
+
# template password reset email
|
17
|
+
password_reset_email = site.emails.new
|
18
|
+
password_reset_email.name = 'password_reset'
|
19
|
+
password_reset_email.from = 'admin@site.com'
|
20
|
+
password_reset_email.subject = 'Password Reset'
|
21
|
+
password_reset_email.text_body = 'Hi <%= options["first_name"] %>, your password has been reset and is now: <%= options["new_password"] %>.'
|
22
|
+
password_reset_email.save
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.down(site)
|
26
|
+
site.emails.destroy
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class APICallModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :api_calls do |api_calls|
|
4
|
+
add_field :name, :string
|
5
|
+
add_field :http_method, :string
|
6
|
+
add_field :domain, :string
|
7
|
+
add_field :port, :integer, default: 80
|
8
|
+
add_field :path, :string
|
9
|
+
add_field :username, :string
|
10
|
+
add_field :password, :string
|
11
|
+
add_field :authentication, :enum, options: %w{basic digest}
|
12
|
+
add_field :mime_type, :string, default: 'json'
|
13
|
+
add_field :body, :text
|
14
|
+
add_field :body_layout, :string
|
15
|
+
add_field :function, :string
|
16
|
+
api_calls.record_class_name = 'APICall'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.down(site)
|
21
|
+
site.api_calls.destroy
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class RedirectPageModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.pages.create_model :redirect_page do |redirect_pages|
|
4
|
+
add_field :url, :string, searchable: false
|
5
|
+
add_one :page, show_blank: true, blank_text: 'None'
|
6
|
+
redirect_pages.record_class_name = 'RedirectPage'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down(site)
|
11
|
+
site.redirect_pages.destroy
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class MenuModelMigration < Migration
|
2
|
+
def self.up(site)
|
3
|
+
site.records.create_model :menu do |menus|
|
4
|
+
add_one :root, model: :page, validations: {required: {}}
|
5
|
+
add_field :include_root, :boolean, default: false
|
6
|
+
add_field :include_all_children, :boolean, default: true
|
7
|
+
add_field :depth, :integer, default: 0, validations: {required: {}}
|
8
|
+
add_embed_many :exceptions do
|
9
|
+
add_one :page
|
10
|
+
add_field :show, :boolean, default: false
|
11
|
+
add_field :depth, :integer, default: 0, validations: {required: {}}
|
12
|
+
end
|
13
|
+
menus.record_class_name = 'Menu'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.down(site)
|
18
|
+
site.menus.destroy
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
class DevelopmentSitesPage < RecordProxyPage
|
2
|
+
REMOTE_NAME = 'yodel'
|
3
|
+
|
4
|
+
# record proxy pages deal with site models (site.model_name). Override the methods it uses
|
5
|
+
# to interact with these models we can edit Sites (a non site model)
|
6
|
+
def record
|
7
|
+
@record ||= Site.find(BSON::ObjectId.from_string(params['id']))
|
8
|
+
end
|
9
|
+
|
10
|
+
def records
|
11
|
+
@records ||= Site.all
|
12
|
+
end
|
13
|
+
|
14
|
+
# a default user is required before any sites or remotes can be created
|
15
|
+
respond_to :get do
|
16
|
+
with :html do
|
17
|
+
if site.default_users.count == 0
|
18
|
+
response.redirect '/setup'
|
19
|
+
else
|
20
|
+
super()
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# create a site
|
26
|
+
respond_to :post do
|
27
|
+
# clone a site from a remote server
|
28
|
+
with :json do
|
29
|
+
# ensure yodel is set up
|
30
|
+
default_user = site.default_users.first
|
31
|
+
return {success: false, reason: 'No default user has been created'} if site.default_users.count == 0
|
32
|
+
|
33
|
+
# extract the site name and generate an identifier to be used as the site's domain and folder name
|
34
|
+
site_name = params['name'].to_s
|
35
|
+
identifier = site_name.downcase.gsub(/[^a-z0-9]+/, '_')
|
36
|
+
return {success: false, reason: 'No site name provided'} if site_name.blank? || identifier.blank?
|
37
|
+
|
38
|
+
# find the remote to clone from
|
39
|
+
remote = Remote.find(BSON::ObjectId.from_string(params['remote']))
|
40
|
+
return {success: false, reason: 'Remote could not be found'} if remote.nil?
|
41
|
+
|
42
|
+
# construct the git url used to clone the site
|
43
|
+
remote_id = params['remote_id']
|
44
|
+
git_url = remote.git_url(remote_id)
|
45
|
+
return {success: false, reason: 'Git URL could not be constructed'} if git_url.blank?
|
46
|
+
|
47
|
+
# find an unused site folder name
|
48
|
+
site_folder = identifier
|
49
|
+
counter = 0
|
50
|
+
while File.exist?(File.join(Yodel.config.sites_root, site_folder))
|
51
|
+
counter += 1
|
52
|
+
site_folder = "#{identifier}_#{counter}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# clone the repos locally
|
56
|
+
Dir.chdir(Yodel.config.sites_root) do
|
57
|
+
result = `#{Yodel.config.git_path} clone -o #{REMOTE_NAME} #{git_url} #{site_folder}`
|
58
|
+
return {success: false, reason: 'Git error: ' + $1} if result =~ /error: (.+)$/
|
59
|
+
end
|
60
|
+
|
61
|
+
# create a new site from the cloned site.yml file
|
62
|
+
site_yml = File.join(Yodel.config.sites_root, site_folder, Yodel::SITE_YML_FILE_NAME)
|
63
|
+
return {success: false, reason: 'Site yml file was not cloned successfully'} unless File.exist?(site_yml)
|
64
|
+
new_site = Site.load_from_site_yaml(site_yml)
|
65
|
+
|
66
|
+
# find an unused default local domain
|
67
|
+
domain = "#{identifier}.yodel"
|
68
|
+
counter = 0
|
69
|
+
while Site.exists?(domains: domain)
|
70
|
+
counter += 1
|
71
|
+
domain = "#{identifier}-#{counter}.yodel"
|
72
|
+
end
|
73
|
+
|
74
|
+
# add the remote and a new default local domain
|
75
|
+
new_site.domains.unshift(domain)
|
76
|
+
new_site.remote_id = remote_id
|
77
|
+
new_site.remote = remote
|
78
|
+
new_site.save
|
79
|
+
|
80
|
+
# initialise the site
|
81
|
+
Migration.run_migrations(new_site)
|
82
|
+
create_default_user(new_site, default_user)
|
83
|
+
{success: true, url: new_site_url(new_site)}
|
84
|
+
end
|
85
|
+
|
86
|
+
# create a new local site
|
87
|
+
with :html do
|
88
|
+
# the default user is required for the git repos, and creating an
|
89
|
+
# admin account in the new site
|
90
|
+
default_user = site.default_users.first
|
91
|
+
if default_user.nil?
|
92
|
+
response.redirect '/setup'
|
93
|
+
return
|
94
|
+
end
|
95
|
+
|
96
|
+
name = cleanse_name(params['name'].sub('.yodel', ''))
|
97
|
+
if name.blank?
|
98
|
+
flash[:error] = 'You must enter a name for this site'
|
99
|
+
response.redirect '/sites'
|
100
|
+
return
|
101
|
+
end
|
102
|
+
|
103
|
+
# create a new folder for the site
|
104
|
+
site_dir = File.join(Yodel.config.sites_root, name)
|
105
|
+
FileUtils.cp_r(File.join(File.dirname(__FILE__), '..', 'site_template'), site_dir)
|
106
|
+
|
107
|
+
# rename the gitignore file so it becomes active
|
108
|
+
FileUtils.mv(File.join(site_dir, 'gitignore'), File.join(site_dir, '.gitignore'))
|
109
|
+
|
110
|
+
# create the new site
|
111
|
+
new_site = Site.new
|
112
|
+
new_site.name = name
|
113
|
+
new_site.root_directory = site_dir
|
114
|
+
new_site.domains << "#{name}.yodel"
|
115
|
+
|
116
|
+
# copy core yodel migrations
|
117
|
+
yodel_migrations_dir = File.join(site_dir, Yodel::MIGRATIONS_DIRECTORY_NAME, Yodel::YODEL_MIGRATIONS_DIRECTORY_NAME)
|
118
|
+
FileUtils.cp_r(Yodel.config.yodel_migration_directory, yodel_migrations_dir)
|
119
|
+
|
120
|
+
# copy extension migrations
|
121
|
+
extension_migrations_dir = File.join(site_dir, Yodel::MIGRATIONS_DIRECTORY_NAME, Yodel::EXTENSION_MIGRATIONS_DIRECTORY_NAME)
|
122
|
+
Yodel.config.extensions.each do |extension|
|
123
|
+
FileUtils.cp_r(extension.migrations_dir, File.join(extension_migrations_dir, extension.name)) if File.directory?(extension.migrations_dir)
|
124
|
+
new_site.extensions << extension.name
|
125
|
+
end
|
126
|
+
|
127
|
+
# create the repository and perform the first commit
|
128
|
+
if Yodel.config.owner_user
|
129
|
+
if Yodel.config.owner_group != 0
|
130
|
+
FileUtils.chown_R(Yodel.config.owner_user, Yodel.config.owner_group, site_dir)
|
131
|
+
else
|
132
|
+
FileUtils.chown_R(Yodel.config.owner_user, nil, site_dir)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
repos = Git.init(site_dir)
|
136
|
+
repos.config('user.name', default_user.name)
|
137
|
+
repos.config('user.email', default_user.email)
|
138
|
+
repos.config('http.postBuffer', (200 * 1024 * 1024))
|
139
|
+
repos.add([Yodel::LAYOUTS_DIRECTORY_NAME, Yodel::MIGRATIONS_DIRECTORY_NAME, Yodel::PARTIALS_DIRECTORY_NAME, Yodel::PUBLIC_DIRECTORY_NAME, Yodel::ATTACHMENTS_DIRECTORY_NAME])
|
140
|
+
repos.commit_all('New yodel site')
|
141
|
+
|
142
|
+
# save and initialise the site
|
143
|
+
new_site.save
|
144
|
+
Migration.run_migrations(new_site)
|
145
|
+
create_default_user(new_site, default_user)
|
146
|
+
|
147
|
+
# redirect to the new site
|
148
|
+
response.redirect new_site_url(new_site)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
def cleanse_name(name)
|
154
|
+
name.downcase.gsub(/[^a-z0-9]+/, '_')
|
155
|
+
end
|
156
|
+
|
157
|
+
def create_default_user(new_site, default_user)
|
158
|
+
user = new_site.users.new
|
159
|
+
user.first_name = default_user.name
|
160
|
+
user.email = default_user.email
|
161
|
+
user.username = default_user.email
|
162
|
+
user.password = Password.hashed_password(nil, default_user.password)
|
163
|
+
user.groups << new_site.groups['Developers']
|
164
|
+
user.save
|
165
|
+
|
166
|
+
# because of the before_create callback, we need to override
|
167
|
+
# the salt and password manually by saving again
|
168
|
+
user.password_salt = nil
|
169
|
+
user.password = Password.hashed_password(nil, default_user.password)
|
170
|
+
user.save_without_validation
|
171
|
+
end
|
172
|
+
|
173
|
+
def new_site_url(new_site)
|
174
|
+
port = (request.port == 80 ? nil : request.port)
|
175
|
+
"http://#{new_site.domains.first}#{':' if port}#{port}/admin/pages"
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class RemotesPage < RecordProxyPage
|
2
|
+
# record proxy pages deal with site models (site.model_name). Override the methods it uses
|
3
|
+
# to interact with these models we can edit Remotes (a non site model)
|
4
|
+
def record
|
5
|
+
@record ||= Remote.find(BSON::ObjectId.from_string(params['id']))
|
6
|
+
end
|
7
|
+
|
8
|
+
def records
|
9
|
+
@records ||= Remote.all
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_record
|
13
|
+
Remote.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def form_for_new_record(options={}, &block)
|
17
|
+
form_for(Remote.new, options, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
# a default user is required before any sites or remotes can be created
|
21
|
+
respond_to :get do
|
22
|
+
with :html do
|
23
|
+
if site.default_users.count == 0
|
24
|
+
response.redirect '/setup'
|
25
|
+
else
|
26
|
+
super()
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
with :json do
|
31
|
+
result = record.site_list
|
32
|
+
|
33
|
+
if result['success']
|
34
|
+
result['sites'] = result['sites'].reject do |remote_site|
|
35
|
+
Site.exists?(remote_id: remote_site['id'])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
result
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
h2 {
|
2
|
+
margin-top: 30px;
|
3
|
+
}
|
4
|
+
|
5
|
+
ul {
|
6
|
+
border-top: 1px solid #eee;
|
7
|
+
color: #777;
|
8
|
+
}
|
9
|
+
|
10
|
+
ul li {
|
11
|
+
border-bottom: 1px solid #eee;
|
12
|
+
line-height: 35px;
|
13
|
+
}
|
14
|
+
|
15
|
+
.remotes li img {
|
16
|
+
position: relative;
|
17
|
+
top: 6px;
|
18
|
+
left: 20px;
|
19
|
+
}
|
20
|
+
|
21
|
+
a {
|
22
|
+
text-decoration: none;
|
23
|
+
}
|
24
|
+
|
25
|
+
a:hover {
|
26
|
+
text-decoration: underline;
|
27
|
+
}
|
28
|
+
|
29
|
+
a.action {
|
30
|
+
float: right;
|
31
|
+
font-size: 12px;
|
32
|
+
margin-left: 15px;
|
33
|
+
}
|
34
|
+
|
35
|
+
h1 + form, p + form {
|
36
|
+
margin-top: 20px;
|
37
|
+
}
|
38
|
+
|
39
|
+
#new_site form {
|
40
|
+
width: 600px;
|
41
|
+
}
|
42
|
+
|
43
|
+
#new_site div div {
|
44
|
+
width: 500px;
|
45
|
+
}
|
46
|
+
|
47
|
+
#new_site div div input[type=text] {
|
48
|
+
width: 300px;
|
49
|
+
}
|
50
|
+
|
51
|
+
.remote_name {
|
52
|
+
font-size: 11px;
|
53
|
+
color: #aaa;
|
54
|
+
}
|
55
|
+
|
56
|
+
.remote_site a.cloning {
|
57
|
+
background: center center url(/core/images/spinner.gif) no-repeat;
|
58
|
+
color: transparent;
|
59
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
$('.remote_site a').live('click', function(event) {
|
2
|
+
var link = $(this);
|
3
|
+
link.addClass('cloning');
|
4
|
+
|
5
|
+
jQuery.ajax('/sites.json', {data: {remote: link.attr('data-remote'), remote_id: link.attr('data-remote-id'), name: link.attr('data-site-name')}, type: 'POST', success: function(data) {
|
6
|
+
if(data.success)
|
7
|
+
if(data.url)
|
8
|
+
window.location = data.url;
|
9
|
+
else
|
10
|
+
alert("The site was created successfully, but no default url was returned. Please try refreshing this page, and visiting the site manually.");
|
11
|
+
else
|
12
|
+
alert("Sorry, an error occurred while cloninng this site: " + data.reason);
|
13
|
+
}, error: function(jqXHR, textStatus, errorThrown) {
|
14
|
+
alert("Sorry, an error occurred while cloning this site.");
|
15
|
+
}, complete: function(jqXHR, textStatus) {
|
16
|
+
link.removeClass('cloning');
|
17
|
+
}});
|
18
|
+
});
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= page.title %></title>
|
5
|
+
<link rel='stylesheet' href='/core/css/reset.css' type='text/css'>
|
6
|
+
<link rel='stylesheet' href='/css/main.css' type='text/css'>
|
7
|
+
<meta name='description' content='<%= first_non_blank_response_to(:description) %>'>
|
8
|
+
<meta name='keywords' content='<%= first_non_blank_response_to(:keywords) %>'>
|
9
|
+
<%= first_non_blank_response_to(:custom_meta_tags) %>
|
10
|
+
<script src='/core/js/jquery.min.js'></script>
|
11
|
+
<script src='/core/js/json2.js'></script>
|
12
|
+
<script src='/core/js/yodel_jquery.js'></script>
|
13
|
+
<script src='/js/main.js'></script>
|
14
|
+
</head>
|
15
|
+
<body>
|
16
|
+
<%= content %>
|
17
|
+
</body>
|
18
|
+
</html>
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
/* site css */
|
@@ -0,0 +1 @@
|
|
1
|
+
/* site js */
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'yodel_development_environment'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'yodel_development_environment'
|
7
|
+
s.version = YodelDevelopmentEnvironment::VERSION
|
8
|
+
s.authors = ['Will Cannings']
|
9
|
+
s.email = ['me@willcannings.com']
|
10
|
+
s.homepage = 'http://yodelcms.com'
|
11
|
+
s.summary = 'Yodel Development Environment'
|
12
|
+
s.description = 'Yodel Development Environment'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
# s.add_development_dependency "rspec"
|
21
|
+
# s.add_runtime_dependency "rest-client"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yodel_development_environment
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Will Cannings
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-09 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Yodel Development Environment
|
15
|
+
email:
|
16
|
+
- me@willcannings.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- Gemfile
|
23
|
+
- Rakefile
|
24
|
+
- lib/layouts/common.html
|
25
|
+
- lib/layouts/common/default_user.html
|
26
|
+
- lib/layouts/common/remote.html
|
27
|
+
- lib/layouts/common/remotes_page.html
|
28
|
+
- lib/layouts/common/setup.html
|
29
|
+
- lib/layouts/common/setup_remote.html
|
30
|
+
- lib/layouts/common/site.html
|
31
|
+
- lib/layouts/common/sites_page.html
|
32
|
+
- lib/migrations/site/01_sites_page_model.rb
|
33
|
+
- lib/migrations/site/02_remotes_page_model.rb
|
34
|
+
- lib/migrations/site/03_default_user_model.rb
|
35
|
+
- lib/migrations/site/04_page_structure.rb
|
36
|
+
- lib/migrations/yodel/01_record_model.rb
|
37
|
+
- lib/migrations/yodel/02_page_model.rb
|
38
|
+
- lib/migrations/yodel/03_layout_model.rb
|
39
|
+
- lib/migrations/yodel/04_group_model.rb
|
40
|
+
- lib/migrations/yodel/05_user_model.rb
|
41
|
+
- lib/migrations/yodel/06_snippet_model.rb
|
42
|
+
- lib/migrations/yodel/07_search_page_model.rb
|
43
|
+
- lib/migrations/yodel/08_default_site_options.rb
|
44
|
+
- lib/migrations/yodel/09_security_page_models.rb
|
45
|
+
- lib/migrations/yodel/10_record_proxy_page_model.rb
|
46
|
+
- lib/migrations/yodel/11_email_model.rb
|
47
|
+
- lib/migrations/yodel/12_api_call_model.rb
|
48
|
+
- lib/migrations/yodel/13_redirect_page_model.rb
|
49
|
+
- lib/migrations/yodel/14_menu_model.rb
|
50
|
+
- lib/models/development_sites_page.rb
|
51
|
+
- lib/models/remotes_page.rb
|
52
|
+
- lib/public/css/screen.css
|
53
|
+
- lib/public/js/main.js
|
54
|
+
- lib/site_template/attachments/.gitkeep
|
55
|
+
- lib/site_template/gitignore
|
56
|
+
- lib/site_template/layouts/page.html
|
57
|
+
- lib/site_template/layouts/page/.gitkeep
|
58
|
+
- lib/site_template/migrations/extensions/.gitkeep
|
59
|
+
- lib/site_template/migrations/site/.gitkeep
|
60
|
+
- lib/site_template/partials/.gitkeep
|
61
|
+
- lib/site_template/public/.gitkeep
|
62
|
+
- lib/site_template/public/css/main.css
|
63
|
+
- lib/site_template/public/js/main.js
|
64
|
+
- lib/yodel_development_environment.rb
|
65
|
+
- yodel_development_environment.gemspec
|
66
|
+
homepage: http://yodelcms.com
|
67
|
+
licenses: []
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.10
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Yodel Development Environment
|
90
|
+
test_files: []
|