railswiki 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +185 -0
- data/Rakefile +24 -0
- data/app/assets/config/railswiki_manifest.js +2 -0
- data/app/assets/javascripts/railswiki/application.js +13 -0
- data/app/assets/javascripts/railswiki/histories.js +2 -0
- data/app/assets/javascripts/railswiki/invites.js +2 -0
- data/app/assets/javascripts/railswiki/pages.js +2 -0
- data/app/assets/javascripts/railswiki/sessions.js +2 -0
- data/app/assets/javascripts/railswiki/uploaded_files.js +2 -0
- data/app/assets/javascripts/railswiki/users.js +2 -0
- data/app/assets/stylesheets/railswiki/application.css +15 -0
- data/app/assets/stylesheets/railswiki/histories.css +4 -0
- data/app/assets/stylesheets/railswiki/invites.css +4 -0
- data/app/assets/stylesheets/railswiki/pages.scss +55 -0
- data/app/assets/stylesheets/railswiki/sessions.css +4 -0
- data/app/assets/stylesheets/railswiki/uploaded_files.scss +54 -0
- data/app/assets/stylesheets/railswiki/users.css +4 -0
- data/app/controllers/railswiki/application_controller.rb +126 -0
- data/app/controllers/railswiki/histories_controller.rb +48 -0
- data/app/controllers/railswiki/invites_controller.rb +61 -0
- data/app/controllers/railswiki/pages_controller.rb +141 -0
- data/app/controllers/railswiki/sessions_controller.rb +75 -0
- data/app/controllers/railswiki/uploaded_files_controller.rb +100 -0
- data/app/controllers/railswiki/users_controller.rb +55 -0
- data/app/helpers/railswiki/application_helper.rb +26 -0
- data/app/helpers/railswiki/histories_helper.rb +4 -0
- data/app/helpers/railswiki/invites_helper.rb +4 -0
- data/app/helpers/railswiki/pages_helper.rb +76 -0
- data/app/helpers/railswiki/sessions_helper.rb +4 -0
- data/app/helpers/railswiki/title_helper.rb +7 -0
- data/app/helpers/railswiki/uploaded_files_helper.rb +4 -0
- data/app/helpers/railswiki/users_helper.rb +4 -0
- data/app/helpers/railswiki/wiki_helper.rb +189 -0
- data/app/jobs/railswiki/application_job.rb +4 -0
- data/app/mailers/railswiki/application_mailer.rb +6 -0
- data/app/models/railswiki/application_record.rb +5 -0
- data/app/models/railswiki/history.rb +14 -0
- data/app/models/railswiki/invite.rb +20 -0
- data/app/models/railswiki/page.rb +56 -0
- data/app/models/railswiki/uploaded_file.rb +32 -0
- data/app/models/railswiki/user.rb +27 -0
- data/app/uploaders/railswiki/file_uploader.rb +56 -0
- data/app/views/layouts/railswiki/application.html.erb +25 -0
- data/app/views/railswiki/histories/index.html.erb +33 -0
- data/app/views/railswiki/histories/show.html.erb +17 -0
- data/app/views/railswiki/invites/_form.html.erb +38 -0
- data/app/views/railswiki/invites/index.html.erb +39 -0
- data/app/views/railswiki/invites/new.html.erb +7 -0
- data/app/views/railswiki/invites/show.html.erb +34 -0
- data/app/views/railswiki/pages/_form.html.erb +144 -0
- data/app/views/railswiki/pages/edit.html.erb +8 -0
- data/app/views/railswiki/pages/history.html.erb +11 -0
- data/app/views/railswiki/pages/index.html.erb +72 -0
- data/app/views/railswiki/pages/new.html.erb +7 -0
- data/app/views/railswiki/pages/show.html.erb +36 -0
- data/app/views/railswiki/sessions/no_invite.erb +7 -0
- data/app/views/railswiki/sessions/not_authorized.html.erb +12 -0
- data/app/views/railswiki/uploaded_files/_form.html.erb +31 -0
- data/app/views/railswiki/uploaded_files/_inline.html.erb +5 -0
- data/app/views/railswiki/uploaded_files/edit.html.erb +8 -0
- data/app/views/railswiki/uploaded_files/file_dialog.html.erb +11 -0
- data/app/views/railswiki/uploaded_files/image_dialog.html.erb +11 -0
- data/app/views/railswiki/uploaded_files/index.html.erb +36 -0
- data/app/views/railswiki/uploaded_files/new.html.erb +7 -0
- data/app/views/railswiki/uploaded_files/show.html.erb +29 -0
- data/app/views/railswiki/users/_form.html.erb +29 -0
- data/app/views/railswiki/users/edit.html.erb +8 -0
- data/app/views/railswiki/users/index.html.erb +37 -0
- data/app/views/railswiki/users/show.html.erb +59 -0
- data/app/views/shared/_formatting.md.erb +29 -0
- data/app/views/shared/_histories.html.erb +21 -0
- data/app/views/shared/_layout.html.erb +17 -0
- data/app/views/shared/_menu.html.erb +15 -0
- data/app/views/shared/_meta.html.erb +1 -0
- data/app/views/shared/_notices.html.erb +3 -0
- data/app/views/shared/_roles.html.erb +12 -0
- data/app/views/shared/_search.md.erb +5 -0
- data/config/initializers/carrierwave.rb +6 -0
- data/config/initializers/omniauth.rb +16 -0
- data/config/initializers/session_store.rb +1 -0
- data/config/routes.rb +25 -0
- data/db/migrate/20170420000841_create_railswiki_pages.rb +10 -0
- data/db/migrate/20170420010111_add_sessions_table.rb +12 -0
- data/db/migrate/20170420010147_create_railswiki_users.rb +14 -0
- data/db/migrate/20170420021039_add_lowercase_title_to_page.rb +5 -0
- data/db/migrate/20170420021840_create_railswiki_histories.rb +11 -0
- data/db/migrate/20170420235420_add_email_and_image_to_user.rb +8 -0
- data/db/migrate/20170421000333_add_last_login_to_user.rb +5 -0
- data/db/migrate/20170421010945_add_role_to_user.rb +7 -0
- data/db/migrate/20170421020932_create_railswiki_uploaded_files.rb +10 -0
- data/db/migrate/20170421030140_add_title_to_uploaded_file.rb +5 -0
- data/db/migrate/20170517224700_create_railswiki_invites.rb +12 -0
- data/db/migrate/20170517234452_add_role_to_invite.rb +5 -0
- data/db/migrate/20170622033540_set_all_mysql_tables_to_utf8.rb +54 -0
- data/lib/railswiki.rb +5 -0
- data/lib/railswiki/engine.rb +14 -0
- data/lib/railswiki/version.rb +3 -0
- data/lib/tasks/railswiki_tasks.rake +4 -0
- metadata +255 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
require "email_validator"
|
2
|
+
|
3
|
+
module Railswiki
|
4
|
+
class User < ApplicationRecord
|
5
|
+
ROLE_ADMIN = "admin"
|
6
|
+
ROLE_EDITOR = "editor"
|
7
|
+
ROLE_DEFAULT = "user"
|
8
|
+
|
9
|
+
AVAILABLE_ROLES = [ROLE_ADMIN, ROLE_EDITOR, ROLE_DEFAULT]
|
10
|
+
|
11
|
+
has_many :histories, dependent: :nullify, foreign_key: "author_id"
|
12
|
+
has_many :uploaded_files, dependent: :nullify
|
13
|
+
has_many :invites, dependent: :nullify, foreign_key: "inviting_user_id"
|
14
|
+
has_one :invite, dependent: :destroy, foreign_key: "invited_user_id"
|
15
|
+
|
16
|
+
validates :provider, presence: true
|
17
|
+
validates :email, presence: true, uniqueness: true, email: true
|
18
|
+
validates :role, presence: true, inclusion: { in: AVAILABLE_ROLES }
|
19
|
+
|
20
|
+
def expose_json
|
21
|
+
{
|
22
|
+
id: id,
|
23
|
+
name: name,
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_dependency "carrierwave"
|
2
|
+
|
3
|
+
module Railswiki
|
4
|
+
class FileUploader < CarrierWave::Uploader::Base
|
5
|
+
|
6
|
+
# Include RMagick or MiniMagick support:
|
7
|
+
# include CarrierWave::RMagick
|
8
|
+
# include CarrierWave::MiniMagick
|
9
|
+
|
10
|
+
# Choose what kind of storage to use for this uploader:
|
11
|
+
storage :file
|
12
|
+
# storage :fog
|
13
|
+
|
14
|
+
# Override the directory where uploaded files will be stored.
|
15
|
+
# This is a sensible default for uploaders that are meant to be mounted:
|
16
|
+
def store_dir
|
17
|
+
"public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def cache_dir
|
21
|
+
Rails.root.join 'tmp/uploads/cache'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Provide a default URL as a default if there hasn't been a file uploaded:
|
25
|
+
# def default_url
|
26
|
+
# # For Rails 3.1+ asset pipeline compatibility:
|
27
|
+
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
|
28
|
+
#
|
29
|
+
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
|
30
|
+
# end
|
31
|
+
|
32
|
+
# Process files as they are uploaded:
|
33
|
+
# process scale: [200, 300]
|
34
|
+
#
|
35
|
+
# def scale(width, height)
|
36
|
+
# # do something
|
37
|
+
# end
|
38
|
+
|
39
|
+
# Create different versions of your uploaded files:
|
40
|
+
# version :thumb do
|
41
|
+
# process resize_to_fit: [50, 50]
|
42
|
+
# end
|
43
|
+
|
44
|
+
# Add a white list of extensions which are allowed to be uploaded.
|
45
|
+
# For images you might use something like this:
|
46
|
+
# def extension_whitelist
|
47
|
+
# %w(jpg jpeg gif png)
|
48
|
+
# end
|
49
|
+
|
50
|
+
# Override the filename of the uploaded files:
|
51
|
+
# Avoid using model.id or version_name here, see uploader/store.rb for details.
|
52
|
+
# def filename
|
53
|
+
# "file#{File.extname(super)}" if super
|
54
|
+
# end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= content_for?(:title) ? yield(:title) : 'Railswiki' %></title>
|
5
|
+
<%= stylesheet_link_tag "railswiki/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "railswiki/application" %>
|
7
|
+
|
8
|
+
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
9
|
+
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
|
10
|
+
|
11
|
+
<%= javascript_pack_tag "application" %>
|
12
|
+
<%= stylesheet_pack_tag "application" %>
|
13
|
+
|
14
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
15
|
+
|
16
|
+
<%= csrf_meta_tags %>
|
17
|
+
|
18
|
+
<%= render partial: "shared/meta" %>
|
19
|
+
</head>
|
20
|
+
<body>
|
21
|
+
|
22
|
+
<%= render partial: "shared/layout" %>
|
23
|
+
|
24
|
+
</body>
|
25
|
+
</html>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<% title ["Recent changes"] %>
|
2
|
+
|
3
|
+
<div class="meta-content">
|
4
|
+
<h1>Histories</h1>
|
5
|
+
|
6
|
+
<table class="list-table">
|
7
|
+
<thead>
|
8
|
+
<tr>
|
9
|
+
<th>Title</th>
|
10
|
+
<th>Latest version</th>
|
11
|
+
<% if user_can?(:see_page_author) %>
|
12
|
+
<th>Author</th>
|
13
|
+
<% end %>
|
14
|
+
<th colspan="3"></th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
|
18
|
+
<tbody>
|
19
|
+
<% @histories.order(created_at: :desc).limit(100).each do |history| %>
|
20
|
+
<tr>
|
21
|
+
<td><%= link_to history.page.title, wiki_path(history.page) %></td>
|
22
|
+
<td><%= time_ago_in_words(history.created_at) %> ago</td>
|
23
|
+
<% if user_can?(:see_page_author) %>
|
24
|
+
<td><%= user_link history.author %></td>
|
25
|
+
<% end %>
|
26
|
+
<td><%= link_to "json", history_path(history, format: :json) %></td>
|
27
|
+
<td><%= link_to 'Destroy', history, method: :delete, data: { confirm: 'Are you sure?' } if user_can?(:delete_history) %></td>
|
28
|
+
</tr>
|
29
|
+
<% end %>
|
30
|
+
</tbody>
|
31
|
+
</table>
|
32
|
+
|
33
|
+
</div>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<% title ["#{@history.page.title} (as at #{@history.created_at})"] %>
|
2
|
+
|
3
|
+
<h1>
|
4
|
+
<%= link_to @history.page.title, wiki_path(@history.page) %>
|
5
|
+
<small><%= link_to ".json", history_path(@history, format: :json) %></small>
|
6
|
+
as at <%= time_ago @history.created_at %></h1>
|
7
|
+
|
8
|
+
<div class="wiki-content">
|
9
|
+
<%= raw markdown.render @history.body %>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<p>
|
13
|
+
<strong>Edited by</strong>
|
14
|
+
<%= link_to @history.author.name, @history.author %>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<%= link_to 'Back', histories_path %>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<%= form_for(invite) do |f| %>
|
2
|
+
<% if invite.errors.any? %>
|
3
|
+
<div id="error_explanation">
|
4
|
+
<h2><%= pluralize(invite.errors.count, "error") %> prohibited this invite from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<% invite.errors.full_messages.each do |message| %>
|
8
|
+
<li><%= message %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<div class="field">
|
15
|
+
<%= f.label :email %>
|
16
|
+
<%= f.text_field :email %>
|
17
|
+
|
18
|
+
<small class="tip">
|
19
|
+
Invited users need to sign in using the same Google Account
|
20
|
+
registered to this e-mail address.
|
21
|
+
</small>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="field">
|
25
|
+
<%= f.label :inviting_user %>
|
26
|
+
<%= user_link invite.inviting_user %>
|
27
|
+
|
28
|
+
<small class="tip">
|
29
|
+
That's you!
|
30
|
+
</small>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<%= render("shared/roles", form: f) %>
|
34
|
+
|
35
|
+
<div class="actions">
|
36
|
+
<%= f.submit %>
|
37
|
+
</div>
|
38
|
+
<% end %>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<% title ["Invites"] %>
|
2
|
+
|
3
|
+
<div class="meta-content">
|
4
|
+
<h1>Invites</h1>
|
5
|
+
|
6
|
+
<table class="list-table">
|
7
|
+
<thead>
|
8
|
+
<tr>
|
9
|
+
<th>Email</th>
|
10
|
+
<th>Invited By</th>
|
11
|
+
<th>Invited User</th>
|
12
|
+
<th>Invited At</th>
|
13
|
+
<th>Accepted At</th>
|
14
|
+
<th colspan="2"></th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
|
18
|
+
<tbody>
|
19
|
+
<% @invites.each do |invite| %>
|
20
|
+
<tr>
|
21
|
+
<td><%= link_to invite.email, invite %></td>
|
22
|
+
<td><%= user_link invite.inviting_user %></td>
|
23
|
+
<td><%= user_link invite.invited_user %></td>
|
24
|
+
<td><%= time_ago invite.created_at %></td>
|
25
|
+
<td><%= time_ago invite.accepted_at %></td>
|
26
|
+
<td><%= link_to 'Show', invite %></td>
|
27
|
+
<td><%= link_to 'Destroy', invite, method: :delete, data: { confirm: 'Are you sure?' } if user_can?(:delete_invite) %></td>
|
28
|
+
</tr>
|
29
|
+
<% end %>
|
30
|
+
</tbody>
|
31
|
+
</table>
|
32
|
+
|
33
|
+
<br>
|
34
|
+
|
35
|
+
<%= link_to "Invited users", invites_path if user_can?(:list_invites) %>
|
36
|
+
|
37
|
+
<%= link_to "Invite new user", new_invite_path if user_can?(:create_invite) %>
|
38
|
+
|
39
|
+
</div>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<% title ["Invite for #{@invite.email}"] %>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<strong>Email:</strong>
|
5
|
+
<%= @invite.email %>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<strong>Invited by:</strong>
|
10
|
+
<%= user_link @invite.inviting_user %>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
<strong>Invited user:</strong>
|
15
|
+
<%= user_link @invite.invited_user %>
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<p>
|
19
|
+
<strong>Created at:</strong>
|
20
|
+
<%= time_ago @invite.created_at %>
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
<strong>Accepted at:</strong>
|
25
|
+
<%= time_ago @invite.accepted_at %>
|
26
|
+
</p>
|
27
|
+
|
28
|
+
<p>
|
29
|
+
<strong>Invite this user to sign up at:</strong>
|
30
|
+
<%= link_to main_app.login_url %>
|
31
|
+
</p>
|
32
|
+
|
33
|
+
<%= link_to 'Destroy', @invite, method: :delete, data: { confirm: 'Are you sure?' } if user_can?(:delete_invite) %>
|
34
|
+
<%= link_to 'Back', invites_path %>
|
@@ -0,0 +1,144 @@
|
|
1
|
+
<%= form_for(page) do |f| %>
|
2
|
+
<% if page.errors.any? %>
|
3
|
+
<div id="error_explanation">
|
4
|
+
<h2><%= pluralize(page.errors.count, "error") %> prohibited this page from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<% page.errors.full_messages.each do |message| %>
|
8
|
+
<li><%= message %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<div class="field">
|
15
|
+
<%= f.label :title %>
|
16
|
+
<%= f.text_field :title %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="field">
|
20
|
+
<%= f.label :content %>
|
21
|
+
<%= f.text_area :content %>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="actions">
|
25
|
+
<%= f.submit data: { disable_with: false } %>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<script type="text/javascript">
|
30
|
+
var select_image = {
|
31
|
+
name: "select-image",
|
32
|
+
action: function create_select_image_popup(editor) {
|
33
|
+
var refresh_links = function refresh_links() {
|
34
|
+
$(".insert-image li img").on('click', function(e) {
|
35
|
+
pos = simplemde.codemirror.getCursor();
|
36
|
+
simplemde.codemirror.setSelection(pos, pos);
|
37
|
+
var text = "![" + $(e.target).parent("li").attr("data-title") + "](" + $(e.target).parent("li").attr("data-url") + ")";
|
38
|
+
simplemde.codemirror.replaceSelection(text);
|
39
|
+
modal.close();
|
40
|
+
});
|
41
|
+
};
|
42
|
+
|
43
|
+
var request_content = function request_content() {
|
44
|
+
$.ajax({
|
45
|
+
url: "<%= j image_dialog_uploaded_files_path %>",
|
46
|
+
success: function success(data, textStatus, jqXHR) {
|
47
|
+
modal.setContent(data);
|
48
|
+
refresh_links();
|
49
|
+
}
|
50
|
+
});
|
51
|
+
};
|
52
|
+
|
53
|
+
var modal = new tingle.modal({
|
54
|
+
cssClass: ['insert-image'],
|
55
|
+
onOpen: function() {
|
56
|
+
request_content();
|
57
|
+
}
|
58
|
+
});
|
59
|
+
modal.setContent("<span class=\"loading\">loading...</span>");
|
60
|
+
modal.open();
|
61
|
+
},
|
62
|
+
className: "fa fa-picture-o",
|
63
|
+
title: "Insert Image"
|
64
|
+
};
|
65
|
+
|
66
|
+
var select_file = {
|
67
|
+
name: "select-file",
|
68
|
+
action: function create_select_file_popup(editor) {
|
69
|
+
var refresh_links = function refresh_links() {
|
70
|
+
$(".insert-file li span").on('click', function(e) {
|
71
|
+
pos = simplemde.codemirror.getCursor();
|
72
|
+
simplemde.codemirror.setSelection(pos, pos);
|
73
|
+
var text = "[" + $(e.target).parent("li").attr("data-title") + "](" + $(e.target).parent("li").attr("data-url") + ")";
|
74
|
+
simplemde.codemirror.replaceSelection(text);
|
75
|
+
modal.close();
|
76
|
+
});
|
77
|
+
};
|
78
|
+
|
79
|
+
var request_content = function request_content() {
|
80
|
+
$.ajax({
|
81
|
+
url: "<%= j file_dialog_uploaded_files_path %>",
|
82
|
+
success: function success(data, textStatus, jqXHR) {
|
83
|
+
modal.setContent(data);
|
84
|
+
refresh_links();
|
85
|
+
}
|
86
|
+
});
|
87
|
+
};
|
88
|
+
|
89
|
+
var modal = new tingle.modal({
|
90
|
+
cssClass: ['insert-file'],
|
91
|
+
onOpen: function() {
|
92
|
+
request_content();
|
93
|
+
}
|
94
|
+
});
|
95
|
+
modal.setContent("<span class=\"loading\">loading...</span>");
|
96
|
+
modal.open();
|
97
|
+
},
|
98
|
+
className: "fa fa-file-o",
|
99
|
+
title: "Insert File"
|
100
|
+
};
|
101
|
+
|
102
|
+
var targetElement = document.getElementById("page_content");
|
103
|
+
|
104
|
+
var simplemde = new SimpleMDE({
|
105
|
+
element: targetElement,
|
106
|
+
toolbar: ["bold", "italic", "strikethrough", "heading", "|",
|
107
|
+
"code", "quote", "table", "horizontal-rule", "|",
|
108
|
+
"link", select_image, select_file, "unordered-list", "ordered-list", "|",
|
109
|
+
"preview", "side-by-side", "fullscreen"],
|
110
|
+
|
111
|
+
// a JS alert window appears asking for the link or image URL
|
112
|
+
promptURLs: true,
|
113
|
+
|
114
|
+
// force the textarea to stay up to date
|
115
|
+
forceSync: true,
|
116
|
+
});
|
117
|
+
|
118
|
+
simplemde.originalContent = $(targetElement).val();
|
119
|
+
|
120
|
+
var thereAreUnsavedChanges = function() {
|
121
|
+
return !window.confirmedNavigatingAway && $(targetElement).val() != simplemde.originalContent;
|
122
|
+
}
|
123
|
+
|
124
|
+
$(window).bind("beforeunload", function(event) {
|
125
|
+
if (thereAreUnsavedChanges()) {
|
126
|
+
event.returnValue = "You have unsaved changes that may be lost.";
|
127
|
+
return event.returnValue;
|
128
|
+
}
|
129
|
+
});
|
130
|
+
|
131
|
+
$("input[type=submit]").on("click", function() {
|
132
|
+
// if we're clicking Submit, we always want to navigate away
|
133
|
+
window.confirmedNavigatingAway = true;
|
134
|
+
});
|
135
|
+
|
136
|
+
// Turbolinks 4 & 5
|
137
|
+
$(document).on("page:before-change turbolinks:before-visit", function() {
|
138
|
+
if (thereAreUnsavedChanges()) {
|
139
|
+
window.confirmedNavigatingAway = confirm("You have unsaved changes that may be lost.");
|
140
|
+
return window.confirmedNavigatingAway;
|
141
|
+
}
|
142
|
+
});
|
143
|
+
</script>
|
144
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% title [@page.title, "History"] %>
|
2
|
+
|
3
|
+
<h1>
|
4
|
+
History of
|
5
|
+
<%= link_to @page.title, wiki_path(@page) %>
|
6
|
+
<small><%= link_to ".json", page_history_path(@page, format: :json) %></small>
|
7
|
+
</h1>
|
8
|
+
|
9
|
+
<%= render "shared/histories", histories: @page.histories %>
|
10
|
+
|
11
|
+
<%= link_to 'Back', users_path %>
|