Merge branch 'master' into issue#190

This commit is contained in:
Andrew Brown 2017-11-09 17:23:47 +01:00 committed by GitHub
commit 77f8fee363
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1009 changed files with 2805 additions and 145 deletions

12
Gemfile
View File

@ -1,4 +1,5 @@
source 'https://rubygems.org'
ruby "~> 2.3.0"
gem 'rails', '4.2.7.1'
gem 'puma', '~> 3.6.0'
@ -58,15 +59,22 @@ gem 'mixpanel-ruby'
gem 'social-share-button'
# Apps
gem 'easy_translate'
#gem 'easy_translate'
gem 'levenshtein-ffi'
# Forum
gem 'thredded', '~> 0.13.2'
gem 'rails-ujs'
gem 'delayed_job_active_record'
# Tech debt & hacks
gem 'binding_of_caller' # see has_changelog.rb
group :production do
gem 'rails_12factor'
gem 'uglifier', '>= 1.3.0'
gem 'newrelic_rpm'
end
group :test, :production do
@ -100,4 +108,4 @@ end
group :test, :development do
gem 'pry'
end
end

View File

@ -26,6 +26,8 @@ GEM
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
active_record_union (1.2.0)
activerecord (>= 4.0)
activejob (4.2.7.1)
activesupport (= 4.2.7.1)
globalid (>= 0.3.0)
@ -47,11 +49,17 @@ GEM
ast (2.3.0)
authority (3.2.2)
activesupport (>= 3.0.0)
autoprefixer-rails (7.1.2.3)
execjs
aws-sdk (1.66.0)
aws-sdk-v1 (= 1.66.0)
aws-sdk-v1 (1.66.0)
json (~> 1.4)
nokogiri (>= 1.4.4)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
bcrypt (3.1.11)
better_errors (2.1.1)
coderay (>= 1.0.0)
@ -94,6 +102,7 @@ GEM
tins (>= 1.6.0, < 2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.2)
cucumber (2.4.0)
builder (>= 2.1.2)
cucumber-core (~> 1.5.0)
@ -114,7 +123,14 @@ GEM
database_cleaner (1.5.3)
dateslices (0.0.4)
rails (> 4)
db_text_search (0.2.2)
activerecord (>= 4.1.15, < 6.0)
debug_inspector (0.0.2)
delayed_job (4.1.3)
activesupport (>= 3.0, < 5.2)
delayed_job_active_record (4.1.2)
activerecord (>= 3.0, < 5.2)
delayed_job (>= 3.0, < 5)
devise (4.2.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
@ -125,10 +141,6 @@ GEM
docile (1.1.5)
domain_name (0.5.20161021)
unf (>= 0.0.5, < 1.0.0)
easy_translate (0.5.0)
json
thread
thread_safe
erubis (2.7.0)
execjs (2.7.0)
factory_girl (4.7.0)
@ -136,9 +148,13 @@ GEM
factory_girl_rails (4.7.0)
factory_girl (~> 4.7.0)
railties (>= 3.0.0)
fast_blank (1.0.0)
ffi (1.9.14)
filesize (0.1.1)
formatador (0.2.5)
friendly_id (5.2.1)
activerecord (>= 4.0.0)
gemoji (2.1.0)
gherkin (4.0.0)
globalid (0.3.7)
activesupport (>= 4.1.0)
@ -159,13 +175,33 @@ GEM
guard (~> 2.0)
rubocop (~> 0.20)
hashdiff (0.3.2)
html-pipeline (2.6.0)
activesupport (>= 2)
nokogiri (>= 1.4)
htmlentities (4.3.4)
http-cookie (1.0.3)
domain_name (~> 0.5)
httparty (0.13.7)
json (~> 1.8)
multi_xml (>= 0.5.2)
i18n (0.8.0)
inline_svg (1.2.2)
activesupport (>= 3.0)
nokogiri (>= 1.6)
json (1.8.6)
kaminari (1.0.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.0.1)
kaminari-activerecord (= 1.0.1)
kaminari-core (= 1.0.1)
kaminari-actionview (1.0.1)
actionview
kaminari-core (= 1.0.1)
kaminari-activerecord (1.0.1)
activerecord
kaminari-core (= 1.0.1)
kaminari-core (1.0.1)
kramdown (1.14.0)
levenshtein-ffi (1.1.0)
ffi (~> 1.9)
libv8 (3.16.14.15)
@ -191,16 +227,29 @@ GEM
mini_portile2 (2.1.0)
minitest (5.10.1)
mixpanel-ruby (2.2.0)
moneta (1.0.0)
multi_json (1.12.1)
multi_test (0.1.2)
multi_xml (0.6.0)
mustache (1.0.5)
nenv (0.3.0)
netrc (0.11.0)
newrelic_rpm (4.5.0.337)
nokogiri (1.7.0.1)
mini_portile2 (~> 2.1.0)
nokogumbo (1.4.13)
nokogiri
notiffany (0.1.1)
nenv (~> 0.1)
shellany (~> 0.0)
onebox (1.8.16)
fast_blank (>= 1.0.0)
htmlentities (~> 4.3)
moneta (~> 1.0)
multi_json (~> 1.11)
mustache
nokogiri (~> 1.7)
sanitize
orm_adapter (0.5.0)
paperclip (4.2.4)
activemodel (>= 3.2.0)
@ -218,6 +267,8 @@ GEM
puma (3.6.0)
puma-heroku (1.0.0)
puma (~> 3.0)
pundit (1.1.0)
activesupport (>= 3.0.0)
rack (1.6.5)
rack-test (0.6.3)
rack (>= 1.0)
@ -244,6 +295,8 @@ GEM
rails-jquery-autocomplete (1.0.3)
rails (>= 3.2)
rails-perftest (0.0.6)
rails-ujs (0.1.0)
railties (>= 3.1)
rails_12factor (0.0.3)
rails_serve_static_assets
rails_stdout_logging
@ -261,6 +314,7 @@ GEM
json
rack
rb-fsevent (0.9.7)
rb-gravatar (1.0.5)
rb-inotify (0.9.7)
ffi (>= 0.5.0)
redcarpet (3.3.4)
@ -271,6 +325,7 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rinku (2.0.2)
rmagick (2.13.4)
rspec (3.5.0)
rspec-core (~> 3.5.0)
@ -307,6 +362,10 @@ GEM
ruby_dep (1.4.0)
rubyzip (1.2.0)
safe_yaml (1.0.4)
sanitize (4.5.0)
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4.1)
sass (3.4.22)
sass-rails (5.0.6)
railties (>= 4.0.0, < 6)
@ -333,6 +392,10 @@ GEM
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-es6 (0.9.2)
babel-source (>= 5.8.11)
babel-transpiler
sprockets (>= 3.0.0)
sprockets-rails (3.2.0)
actionpack (>= 4.0)
activesupport (>= 4.0)
@ -346,9 +409,30 @@ GEM
libv8 (~> 3.16.14.0)
ref
thor (0.19.4)
thread (0.2.2)
thread_safe (0.3.5)
thredded (0.13.2)
active_record_union (>= 1.2.0)
autoprefixer-rails
db_text_search (~> 0.2.0)
friendly_id
gemoji (~> 2.1.0)
html-pipeline
htmlentities
inline_svg
kaminari
kramdown
nokogiri
onebox (~> 1.8, >= 1.8.13)
pundit (>= 1.1.0)
rails (>= 4.2.0)
rb-gravatar
rinku
sanitize
sass (>= 3.4.21)
sprockets-es6
timeago_js
tilt (2.0.5)
timeago_js (3.0.2)
tins (1.12.0)
tzinfo (1.2.2)
thread_safe (~> 0.1)
@ -388,8 +472,8 @@ DEPENDENCIES
cucumber-rails
database_cleaner
dateslices
delayed_job_active_record
devise
easy_translate
factory_girl_rails
filesize
guard
@ -400,6 +484,7 @@ DEPENDENCIES
medium-editor-rails
meta-tags
mixpanel-ruby
newrelic_rpm
paperclip
pg
pry
@ -409,6 +494,7 @@ DEPENDENCIES
rails (= 4.2.7.1)
rails-jquery-autocomplete
rails-perftest
rails-ujs
rails_12factor
raygun4ruby
redcarpet
@ -427,9 +513,13 @@ DEPENDENCIES
sqlite3
stripe
therubyracer
thredded (~> 0.13.2)
tzinfo-data
uglifier (>= 1.3.0)
webmock
RUBY VERSION
ruby 2.3.1p112
BUNDLED WITH
1.14.4
1.16.0.pre.3

View File

@ -1,12 +1,12 @@
= notebook
= Notebook.ai
{<img src="https://travis-ci.org/indentlabs/notebook.png?branch=master" alt="Build Status" />}[https://travis-ci.org/indentlabs/notebook]
{<img src="https://codeclimate.com/github/indentlabs/notebook/badges/gpa.svg" />}[https://codeclimate.com/github/indentlabs/notebook]
{<img src="https://codeclimate.com/github/indentlabs/notebook/badges/coverage.svg" />}[https://codeclimate.com/github/indentlabs/notebook/coverage]
{<img src="http://inch-ci.org/github/indentlabs/notebook.svg?branch=master" alt="Inline docs" />}[http://inch-ci.org/github/indentlabs/notebook]
== What is notebook?
== What is Notebook.ai?
see {live website}[http://notebook.ai/]
notebook is a set of tools for writers, game designers, and roleplayers to create magnificent universes and everything within them.
From a simple interface in your browser, on your phone, or on your tablet, you can do everything you'd ever want to do while creating your own little (or big!) world.

View File

@ -1,3 +0,0 @@
// Place all the styles related to the Documents controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,3 +0,0 @@
// Place all the styles related to the Emails controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,3 +0,0 @@
// Place all the styles related to the Lab controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,3 +0,0 @@
// Place all the styles related to the Navigator controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,3 +0,0 @@
// Place all the styles related to the Voting controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,6 +1,7 @@
class CollectiveContentAuthorizer < ContentAuthorizer
def self.creatable_by? user
return true
return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email)
[
PermissionService.billing_plan_allows_collective_content?(user: user),
PermissionService.user_can_collaborate_in_universe_that_allows_collective_content?(user: user)

View File

@ -1,6 +1,7 @@
class CoreContentAuthorizer < ContentAuthorizer
def self.creatable_by? user
return true
return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email)
[
PermissionService.billing_plan_allows_core_content?(user: user)
].any?

View File

@ -1,6 +1,7 @@
class ExtendedContentAuthorizer < ContentAuthorizer
def self.creatable_by? user
return true
return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email)
[
PermissionService.billing_plan_allows_extended_content?(user: user),
PermissionService.user_can_collaborate_in_universe_that_allows_extended_content?(user: user)

View File

@ -1,6 +1,7 @@
class UniverseCoreContentAuthorizer < CoreContentAuthorizer
def self.creatable_by? user
return true
return false if ENV.key?('CONTENT_BLACKLIST') && ENV['CONTENT_BLACKLIST'].split(',').include?(user.email)
[
PermissionService.user_has_fewer_owned_universes_than_plan_limit?(user: user),
PermissionService.user_is_on_premium_plan?(user: user)

View File

@ -34,6 +34,8 @@ class ContentController < ApplicationController
# TODO: Secure this with content class whitelist lel
@content = content_type.find(params[:id])
return if ENV['CONTENT_BLACKLIST'].split(',').include?(@content.user.email)
if (current_user || User.new).can_read? @content
@question = @content.question if current_user.present? and current_user == @content.user

View File

@ -15,7 +15,7 @@ class RegistrationsController < Devise::RegistrationsController
end
def account_update_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :current_password, :email_updates, :fluid_preference)
params.require(:user).permit(:name, :email, :username, :password, :password_confirmation, :current_password, :email_updates, :fluid_preference)
end
protected

View File

@ -11,6 +11,8 @@ class User < ActiveRecord::Base
include HasContent
include Authority::UserAbilities
validates_uniqueness_of :username, allow_nil: true, allow_blank: true
has_many :subscriptions
has_many :billing_plans, through: :subscriptions
def on_premium_plan?
@ -139,4 +141,12 @@ class User < ActiveRecord::Base
:email
]
end
def forum_username
username = self.username.present? ? "@#{self.username}" : nil
username ||= self.name.present? ? self.name : nil
username ||= 'Anonymous Author'
username
end
end

View File

@ -83,20 +83,3 @@
</div>
<% end %>
<div class="card blue-grey lighten-1">
<div class="card-content white-text">
<span class="card-title">You've been upgraded to a free Premium account for the month of October!</span>
<p>
Notebook.ai just hit 10,000 users! To celebrate, every single user is free to create any kind of Notebook.ai page (like <%= content_type.humanize.downcase.pluralize %>!) for the entire month of October.
</p>
<br />
<p>
Whatever you create is yours forever &mdash; you'll never lose access to it, even after October.
Get ready for National Novel Writing Month and worldbuild away!
</p>
</div>
<div class="card-action">
<a href="https://medium.com/indent-labs/notebook-ai-hits-10-000-users-in-one-year-were-all-celebrating-with-a-free-month-ed082a464baf">Read more</a>
</div>
</div>

View File

@ -6,13 +6,18 @@
<div class="card-content">
<h4>Personal information</h4>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
<%= f.label 'Name (visible on your profile and forum posts)' %><br />
<%= f.text_field :name, autofocus: true %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
<%= f.label 'Email (always completely private)' %><br />
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.label 'Username (users can @mention you with your username on the forums)' %><br />
<%= f.text_field :username %>
</div>
</div>
</div>
@ -54,7 +59,7 @@
<div class="field">
<%= f.check_box :email_updates %>
<%= f.label :email_updates do %>
If this box is checked, you will receive occasional updates by email about new Notebook.ai features.
I want to receive occasional updates by email about new Notebook.ai features.
<% end %>
</div>
</div>

View File

@ -19,7 +19,7 @@
</li>
<li class="divider"></li>
<li>
<%= link_to privacy_policy_path do %>
<%= link_to main_app.privacy_policy_path do %>
<i class="material-icons left">verified_user</i>
Privacy policy
<% end %>
@ -57,14 +57,14 @@
</li>
<li>
<%= link_to subscription_path do %>
<%= link_to main_app.subscription_path do %>
<i class="material-icons left">credit_card</i>
Account billing
<span class="badge right">&nbsp;</span>
<% end %>
</li>
<li>
<%= link_to edit_user_registration_path do %>
<%= link_to main_app.edit_user_registration_path do %>
<i class="material-icons left">settings</i>
Account settings
<span class="badge right">&nbsp;</span>
@ -75,13 +75,13 @@
<!--
<li>
<%= link_to friends_landing_path do %>
<%= link_to main_app.friends_landing_path do %>
<i class="material-icons left">people</i>
Community Drive
<% end %>
</li>
<li>
<%= link_to community_voting_path do %>
<%= link_to main_app.community_voting_path do %>
<i class="material-icons left">star_outline</i>
Vote on features
<% end %>
@ -90,20 +90,20 @@
<li class="divider"></li>
-->
<li>
<%= link_to prompts_path do %>
<%= link_to main_app.prompts_path do %>
<i class="material-icons left">lightbulb_outline</i>
Worldbuilding prompts
<% end %>
</li>
<li>
<%= link_to notebook_export_path do %>
<%= link_to main_app.notebook_export_path do %>
<i class="material-icons left">file_download</i>
Notebook downloads
<% end %>
</li>
<li class="divider"></li>
<li>
<%= link_to destroy_user_session_path do %>
<%= link_to main_app.destroy_user_session_path do %>
<i class="material-icons left">power_settings_new</i>
Sign out
<% end %>
@ -111,20 +111,20 @@
</ul>
<ul id="login-slide" class="side-nav">
<li><%= link_to 'Sign in', new_user_session_path %></li>
<li><%= link_to 'Sign up', new_user_registration_path %></li>
<li><%= link_to 'Sign in', main_app.new_user_session_path %></li>
<li><%= link_to 'Sign up', main_app.new_user_registration_path %></li>
<li class="divider"></li>
<li><%= link_to 'Report a problem', 'https://docs.google.com/forms/d/e/1FAIpQLSe0jnqJlcPJDqwogGere5j8-8F1nSGGYkzbsI-XkOeMnGwLrA/viewform', target: '_new' %></li>
<li><%= link_to 'Request a feature', 'https://docs.google.com/forms/d/e/1FAIpQLScOhSMdDqWqJu9HGPQdmd0R9s90_AcSo0g6_Nc1qNjQIbSaXA/viewform', target: '_new' %></li>
<li><%= link_to 'Leave feedback', 'https://docs.google.com/forms/d/e/1FAIpQLScZWEVMgm8hBWIIVj1LPzo0GqflmWUrLQlc4TAYqsaS087oAA/viewform', target: '_new' %></li>
<li class="divider"></li>
<li><%= link_to 'Privacy policy', privacy_policy_path %></li>
<li><%= link_to 'Privacy policy', main_app.privacy_policy_path %></li>
</ul>
<div class="navbar-fixed">
<nav class="light-blue">
<div class="nav-wrapper">
<%= link_to 'Notebook.ai', root_url, class: 'brand-logo center' %>
<%= link_to 'Notebook.ai', main_app.root_url, class: 'brand-logo center' %>
<ul class="right">
<% if user_signed_in? %>
@ -140,8 +140,8 @@
</a>
</li>
<% else %>
<li class="hide-on-med-and-down"><%= link_to 'Sign in', new_user_session_path %></li>
<li class="hide-on-med-and-down"><%= link_to 'Sign up', new_user_registration_path %></li>
<li class="hide-on-med-and-down"><%= link_to 'Sign in', main_app.new_user_session_path %></li>
<li class="hide-on-med-and-down"><%= link_to 'Sign up', main_app.new_user_registration_path %></li>
<% end %>
</ul>
<ul class="left">
@ -175,7 +175,7 @@
<div id="search">
<button type="button" class="close red">×</button>
<%= form_tag search_path, method: :get do %>
<%= form_tag main_app.search_path, method: :get do %>
<input type="search" name="q" value="" placeholder="search your notebook" />
<button type="submit" class="btn btn-primary blue">Search everything</button>
<% end %>

View File

@ -31,6 +31,31 @@
<% end %>
</li>
<li>
<%= link_to thredded_path do %>
<i class="material-icons grey-text">
forum
</i>
Discussions
<%
unread_threads = Thredded::Topic
.followed_by(current_user)
.unread(current_user)
.count
private_messages = Thredded::PrivateTopic
.for_user(current_user)
.unread(current_user)
.count
%>
<% if unread_threads > 0 || private_messages > 0 %>
<span class="badge <%= 'blue white-text' if private_messages > 0 %>">
<%= unread_threads %><%= "/#{private_messages}" if private_messages > 0 %>
</span>
<% end %>
<% end %>
</li>
<li>
<%= link_to notes_path do %>
<i class="material-icons grey-text">
@ -147,4 +172,4 @@
$(document).ready(function () {
$(".button-collapse").sideNav();
})
</script>
</script>

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title><%= yield :thredded_page_title %> | Notebook.ai</title>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
<%= stylesheet_link_tag 'thredded', 'data-turbolinks-track': 'reload' %>
<%= csrf_meta_tag %>
<%= javascript_include_tag 'thredded',
async: !Rails.application.config.assets.debug,
defer: true,
'data-turbolinks-track': 'reload'%>
<%== Gravatar.prefetch_dns %>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body>
<%= render 'thredded/shared/nav/bar' %>
<%= yield %>
<%= render 'layouts/ganalytics' %>
</body>
</html>

View File

@ -14,27 +14,6 @@
<h4>Subscription Plan</h4>
<div class="card blue-grey lighten-1">
<div class="card-content white-text">
<span class="card-title">You've been upgraded to a free Premium account for the month of October!</span>
<p>
Notebook.ai just hit 10,000 users! To celebrate, every single user is free to create any kind of Notebook.ai page for the entire month of October.
</p>
<br />
<p>
Whatever you create is yours forever &mdash; you'll never lose access to it, even after October.
Get ready for National Novel Writing Month and worldbuild away!
</p>
<br />
<p>
Users that have paid for a premium membership in October will have the full amount credited back for use on any other month they choose.
</p>
</div>
<div class="card-action">
<a href="https://medium.com/indent-labs/notebook-ai-hits-10-000-users-in-one-year-were-all-celebrating-with-a-free-month-ed082a464baf">Read more</a>
</div>
</div>
<p>
<% if free_for_life_user %>
<p style="margin-top: 20px;">

View File

@ -0,0 +1 @@
<li class="<%= category.name.underscore %>"><%= category.name %></li>

View File

@ -0,0 +1,6 @@
<% content_for :thredded_page_title, @message %>
<% content_for :thredded_page_id, 'thredded--error-forbidden' %>
<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
<%= thredded_page do %>
<p class="thredded--alert thredded--alert-warning"><%= @message %></p>
<% end %>

View File

@ -0,0 +1,6 @@
<% content_for :thredded_page_title, @message %>
<% content_for :thredded_page_id, 'thredded--error-not-found' %>
<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
<%= thredded_page do %>
<p class="thredded--alert thredded--alert-danger"><%= @message %></p>
<% end %>

View File

@ -0,0 +1,11 @@
<%# Link to the "First" page
- available local variables
url: url to the first page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<span class="first">
<%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, :remote => remote %>
</span>

View File

@ -0,0 +1,8 @@
<%# Non-link tag that stands for skipped pages...
- available local variables
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<span class="page gap"><%= t('views.pagination.truncate').html_safe %></span>

View File

@ -0,0 +1,11 @@
<%# Link to the "Last" page
- available local variables
url: url to the last page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<span class="last">
<%= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, :remote => remote %>
</span>

View File

@ -0,0 +1,11 @@
<%# Link to the "Next" page
- available local variables
url: url to the next page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<span class="next">
<%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, :rel => 'next', :remote => remote %>
</span>

View File

@ -0,0 +1,12 @@
<%# Link showing page number
- available local variables
page: a page object for "this" page
url: url to this page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<span class="page<%= ' current' if page.current? %>">
<%= link_to_unless page.current?, page, url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} %>
</span>

View File

@ -0,0 +1,23 @@
<%# The container tag
- available local variables
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
paginator: the paginator that renders the pagination tags inside
-%>
<%= paginator.render do -%>
<nav class="pagination thredded--pagination">
<%= first_page_tag unless current_page.first? %>
<%= prev_page_tag unless current_page.first? %>
<% each_page do |page| -%>
<% if page.left_outer? || page.right_outer? || page.inside_window? -%>
<%= page_tag page %>
<% elsif !page.was_truncated? -%>
<%= gap_tag %>
<% end -%>
<% end -%>
<%= next_page_tag unless current_page.last? %>
<%= last_page_tag unless current_page.last? %>
</nav>
<% end -%>

View File

@ -0,0 +1,11 @@
<%# Link to the "Previous" page
- available local variables
url: url to the previous page
current_page: a page object for the currently displayed page
total_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
-%>
<span class="prev">
<%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, :rel => 'prev', :remote => remote %>
</span>

View File

@ -0,0 +1,28 @@
<% content_for :thredded_page_title, t('thredded.messageboard_group.create') %>
<% content_for :thredded_page_id, 'thredded--messageboard-groups-new' %>
<% content_for :thredded_breadcrumbs do %>
<ul class="thredded--navigation-breadcrumbs">
<li class="thredded--navigation-breadcrumbs--item-no-link">
<%= t('thredded.messageboard_group.create') %>
</li>
</ul>
<% end %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= form_for @messageboard_group, html: { class: 'thredded--form' } do |f| %>
<ul class="thredded--form-list">
<li>
<%= f.label :name %>
<%= f.text_field :name, required: true %>
</li>
<li>
<%= f.submit t('thredded.messageboard_group.create'),
class: 'thredded--form--submit',
'data-disable-with' => t('thredded.messageboard_group.form.create_btn_submitting') %>
</li>
</ul>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1,24 @@
<%= form_for messageboard, html: { class: 'thredded--form' } do |f| %>
<ul class="thredded--form-list">
<li>
<%= f.label t('thredded.messageboard.form.title_label') %>
<%= f.text_field :name, required: true %>
</li>
<li>
<%= f.label t('thredded.messageboard.form.description_label') %>
<%= f.text_field :description %>
</li>
<li>
<%= f.label t('thredded.messageboard.form.messageboard_group_id_label') %>
<%= f.collection_select :messageboard_group_id, Thredded::MessageboardGroup.all, :id, :name,
include_blank: t('thredded.messageboard.form.no_group') %>
</li>
<li>
<%= f.submit @messageboard.persisted? ? t('thredded.messageboard.update') : t('thredded.messageboard.create'),
class: 'thredded--form--submit',
'data-disable-with' => @messageboard.persisted? ?
t('thredded.messageboard.form.update_btn_submitting') :
t('thredded.messageboard.form.create_btn_submitting') %>
</li>
</ul>
<% end %>

View File

@ -0,0 +1,24 @@
<% if policy(messageboard).read? %>
<%= view_hooks.messageboards_index.messageboard.render self, messageboard: messageboard do %>
<%= link_to messageboard_topics_path(messageboard), class: 'thredded--messageboard' do %>
<header class="thredded--messageboard--header">
<h2 class="thredded--messageboard--title"><%= messageboard.name %></h2>
<h3 class="thredded--messageboard--meta">
<%= t 'thredded.messageboard.topics_and_posts_counts',
topics_count: number_with_delimiter(messageboard.topics_count),
posts_count: number_with_delimiter(messageboard.posts_count) %>
</h3>
</header>
<p class="thredded--messageboard--description"><%= messageboard.description %></p>
<% if messageboard.last_topic %>
<p class="thredded--messageboard--byline">
<%= t 'thredded.messageboard.last_updated_by_html',
time_ago: time_ago(messageboard.last_topic.last_post_at),
user: messageboard.last_user.thredded_display_name %>
</p>
<% end %>
<% end %>
<% end %>
<% end %>

View File

@ -0,0 +1,15 @@
<% content_for :thredded_page_title, t('thredded.nav.edit_messageboard') %>
<% content_for :thredded_page_id, 'thredded--messageboard-edit' %>
<% content_for :thredded_breadcrumbs do %>
<ul class="thredded--navigation-breadcrumbs">
<li class="thredded--navigation-breadcrumbs--item-no-link">
<%= t('thredded.nav.edit_messageboard') %>
</li>
</ul>
<% end %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= render 'form', messageboard: @messageboard %>
</section>
<% end %>

View File

@ -0,0 +1,34 @@
<% content_for :thredded_page_title, t('thredded.messageboard.index.page_title') %>
<% content_for :thredded_page_id, 'thredded--messageboards-index' %>
<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
<%= thredded_page do %>
<%= view_hooks.messageboards_index.container.render self, groups: @groups do %>
<section class="thredded--main-section thredded--messageboards">
<%= view_hooks.messageboards_index.list.render self, groups: @groups do %>
<% @groups.each do |group| %>
<% if group.name.present? %>
<h3 class="thredded--messageboards-group--title"><%= group.name %></h3>
<% end %>
<div class="thredded--messageboards-group">
<%= view_hooks.messageboards_index.group.render self, group: group do %>
<%= render group.messageboards %>
<% end %>
</div>
<% end %>
<% end %>
<div class="thredded--messageboards--actions">
<% if policy(Thredded::Messageboard.new).create? %>
<a class="thredded--button" href="<%= new_messageboard_path %>" rel="nofollow">
<%= t('thredded.messageboard.create') %>
</a>
<% end %>
<% if policy(Thredded::MessageboardGroup.new).create? %>
<a class="thredded--button" href="<%= new_messageboard_group_path %>" rel="nofollow">
<%= t('thredded.messageboard_group.create') %>
</a>
<% end %>
</div>
</section>
<% end %>
<% end %>

View File

@ -0,0 +1,15 @@
<% content_for :thredded_page_title, t('thredded.messageboard.create') %>
<% content_for :thredded_page_id, 'thredded--messageboards-new' %>
<% content_for :thredded_breadcrumbs do %>
<ul class="thredded--navigation-breadcrumbs">
<li class="thredded--navigation-breadcrumbs--item-no-link">
<%= t('thredded.messageboard.create') %>
</li>
</ul>
<% end %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= render 'form', messageboard: @messageboard %>
</section>
<% end %>

View File

@ -0,0 +1,19 @@
<% content_for :thredded_main_navigation do %>
<nav class="thredded--moderation-navigation">
<ul class="thredded--moderation-navigation--items">
<li class="thredded--moderation-navigation--item thredded--moderation-navigation--pending">
<%= link_to t('thredded.nav.moderation_pending'), pending_moderation_path %>
</li>
<li class="thredded--moderation-navigation--item thredded--moderation-navigation--history">
<%= link_to t('thredded.nav.moderation_history'), moderation_history_path %>
</li>
<li class="thredded--moderation-navigation--item thredded--moderation-navigation--activity">
<%= link_to t('thredded.nav.moderation_activity'), moderation_activity_path %>
</li>
<li class="thredded--moderation-navigation--item thredded--moderation-navigation--users">
<%= link_to t('thredded.nav.moderation_users'), users_moderation_path %>
</li>
</ul>
<%= render 'users_search_form' %>
</nav>
<% end %>

View File

@ -0,0 +1,19 @@
<% post, content = post_and_content if local_assigns.key?(:post_and_content) %>
<%= content_tag :article, id: dom_id(post), class: 'thredded--post thredded--post-moderation' do %>
<%= render 'thredded/posts_common/actions', post: post %>
<%= render 'thredded/posts_common/header_with_user_and_topic',
post: post,
post_user_link: if post.user
link_to(post.user.thredded_display_name, user_moderation_path(post.user.id))
else
content_tag :em, t('thredded.null_user_name')
end
%>
<%= content || render('thredded/posts/content', post: post) %>
<% if post.blocked? %>
<p class="thredded--alert thredded--alert-danger">
<%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %>
</p>
<% end %>
<%= render 'post_moderation_actions', post: post %>
<% end %>

View File

@ -0,0 +1,12 @@
<div class="thredded--post-moderation-actions">
<% unless post.approved? %>
<%= button_to t('thredded.moderation.approve_btn'), moderate_post_path,
class: 'thredded--button',
params: { id: post.to_model.id, moderation_state: 'approved' } %>
<% end %>
<% unless post.blocked? %>
<%= button_to t('thredded.moderation.block_btn'), moderate_post_path,
class: 'thredded--button',
params: { id: post.to_model.id, moderation_state: 'blocked' } %>
<% end %>
</div>

View File

@ -0,0 +1,46 @@
<%
record = post_moderation_record
post = record.post
if post
post_view = Thredded::PostView.new(post, policy(post))
end
moderation_state_notice_args = {
moderator: user_link(record.moderator),
time_ago: time_ago(record.created_at)
}
%>
<article class="thredded--post-moderation-record thredded--post-moderation-record-<%= record.moderation_state %>">
<header class="thredded--post-moderation-record--header">
<p class="thredded--post-moderation-record--moderation-state-notice">
<% if record.approved? %>
<%= t('thredded.moderation.post_approved_html', moderation_state_notice_args) %>
<% elsif record.blocked? %>
<%= t('thredded.moderation.post_blocked_html', moderation_state_notice_args) %>
<% end %>
</p>
<% if post && post.content != record.post_content %>
<p class="thredded--post-moderation-record--content-changed-notice">
<%= t('thredded.moderation.posts_content_changed_since_moderation_html', post_url: post_permalink_path(post)) %>
</p>
<% end %>
<%= content_tag :em, t('thredded.moderation.post_deleted_notice') unless post %>
</header>
<article class="thredded--post">
<% post_user_link = capture do %>
<% if record.post_user %>
<%= link_to record.post_user.thredded_display_name, user_moderation_path(record.post_user.id) %>
<% else %>
<%= safe_join [record.post_user_name, content_tag(:em, t('thredded.null_user_name'))].compact, ', ' %>
<% end %>
<% end %>
<% if post %>
<%= render 'thredded/posts_common/header_with_user_and_topic', post: post_view, post_user_link: post_user_link %>
<% else %>
<header><h2 class="thredded--post--user"><%= post_user_link %></h2></header>
<% end %>
<div class="thredded--post--content">
<%= Thredded::ContentFormatter.new(self).format_content(record.post_content) %>
</div>
</article>
<%= render 'post_moderation_actions', post: post if post %>
</article>

View File

@ -0,0 +1,3 @@
<% user_detail = user.thredded_user_detail %>
<%= user_detail.moderation_state.to_s.humanize %>
<%= time_ago user_detail.moderation_state_changed_at, default: '' %>

View File

@ -0,0 +1,12 @@
<% post, content = post_and_content if local_assigns.key?(:post_and_content) %>
<%= content_tag :article, id: dom_id(post), class: 'thredded--post thredded--post-moderation' do %>
<%= render 'thredded/posts_common/actions', post: post %>
<%= render 'thredded/posts_common/header_with_topic', post: post %>
<%= content || render('thredded/posts/content', post: post) %>
<% if post.blocked? %>
<p class="thredded--alert thredded--alert-danger">
<%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %>
</p>
<% end %>
<%= render 'post_moderation_actions', post: post %>
<% end %>

View File

@ -0,0 +1,13 @@
<%= form_tag users_moderation_path,
method: 'get',
class: 'thredded--form thredded--navigation--search thredded--navigation--search--right',
'data-thredded-turboform' => true do %>
<%= label_tag :q, t('thredded.moderation.search_users.form_label') %>
<%= text_field_tag :q, @query,
type: 'search',
required: true,
# If there are no results the user will likely want to change the query, so auto-focus.
autofocus: @query.presence && !@users.presence,
placeholder: t('thredded.moderation.search_users.form_placeholder') %>
<button type="submit"><%= t 'thredded.search.form.btn_submit' %></button>
<% end %>

View File

@ -0,0 +1,20 @@
<% content_for :thredded_page_title, t('thredded.nav.moderation') %>
<% content_for :thredded_page_id, 'thredded--moderation-activity' %>
<%= render 'nav' %>
<%= thredded_page do %>
<section class="thredded--main-section">
<h1><%= t 'thredded.recent_activity' %></h1>
<% if @last_moderated_record %>
<div class="thredded--moderated-notice">
<%= render 'post_moderation_record', post_moderation_record: @last_moderated_record %>
</div>
<% end %>
<% if @posts.present? %>
<%= render_posts @posts,
partial: 'thredded/moderation/post',
content_partial: 'thredded/posts/content' %>
<%= paginate @posts %>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1,13 @@
<% content_for :thredded_page_title,
safe_join([t('thredded.nav.moderation'), t('thredded.nav.moderation_history')], ': ') %>
<% content_for :thredded_page_id, 'thredded--moderation-history' %>
<%= render 'nav' %>
<%= thredded_page do %>
<section class="thredded--main-section">
<% if @post_moderation_records.present? %>
<%= render partial: 'post_moderation_record', collection: @post_moderation_records %>
<%= paginate @post_moderation_records %>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1,24 @@
<% content_for :thredded_page_title, t('thredded.nav.moderation') %>
<% content_for :thredded_page_id, 'thredded--pending-moderation' %>
<%= render 'nav' %>
<%= thredded_page do %>
<section class="thredded--main-section">
<% if @last_moderated_record %>
<div class="thredded--moderated-notice">
<%= render 'post_moderation_record', post_moderation_record: @last_moderated_record %>
</div>
<% end %>
<% if @posts.present? %>
<%= render_posts @posts,
partial: 'thredded/moderation/post',
content_partial: 'thredded/posts/content' %>
<%= paginate @posts %>
<% else %>
<div class="thredded--empty">
<h3 class="thredded--empty--title"><%= t 'thredded.moderation.pending.empty.title' %></h3>
<p><%= t 'thredded.moderation.pending.empty.content' %></p>
</div>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1,54 @@
<%
user = @user
user_detail = user.thredded_user_detail
%>
<% content_for :thredded_page_title, t('thredded.nav.moderation') %>
<% content_for :thredded_page_id, 'thredded--moderation-user' %>
<%= render 'nav' %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= view_hooks.moderation_user_page.user_title.render self, user: user do %>
<h1 class="thredded--moderation--user--title">
<%= image_tag Thredded.avatar_url.call(user), class: 'thredded--user--avatar' %><%= user.thredded_display_name %>
</h1>
<% end %>
<%= view_hooks.moderation_user_page.user_info.render self, user: user do %>
<ul class="thredded--moderation--user--info">
<%= view_hooks.moderation_user_page.user_info_list_items.render self, user: user do %>
<li><%= t 'thredded.users.user_since_html', time_ago: time_ago(user.created_at) %></li>
<% if user_detail.last_seen_at %>
<li><%= t 'thredded.users.last_active_html', time_ago: time_ago(user_detail.last_seen_at) %></li>
<% end %>
<% if user_detail.topics_count > 0 %>
<li><%= t 'thredded.users.started_topics_count', count: user_detail.topics_count %></li>
<% end %>
<% if user_detail.posts_count > 0 %>
<li><%= t 'thredded.users.posts_count', count: user_detail.posts_count %></li>
<% end %>
<li><%= render 'user_moderation_state', user: @user %></li>
<% end %>
</ul>
<% end %>
<%= view_hooks.moderation_user_page.user_moderation_actions.render self, user: user do %>
<div class="thredded--user--moderation-actions">
<% unless user_detail.approved? %>
<%= button_to t('thredded.moderation.approve_btn'), moderate_user_path,
class: 'thredded--button',
params: { id: user.to_model.id, moderation_state: 'approved' } %>
<% end %>
<% unless user_detail.blocked? %>
<%= button_to t('thredded.moderation.block_btn'), moderate_user_path,
class: 'thredded--button',
params: { id: user.to_model.id, moderation_state: 'blocked' } %>
<% end %>
</div>
<% end %>
<% if @posts.present? %>
<h2><%= t 'thredded.users.recent_activity' %></h2>
<%= render_posts @posts,
partial: 'thredded/moderation/user_post',
content_partial: 'thredded/posts/content' %>
<%= paginate @posts %>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1,41 @@
<% content_for :thredded_page_title, t('thredded.nav.moderation') %>
<% content_for :thredded_page_id, 'thredded--moderation-users' %>
<%= render 'nav' %>
<%= thredded_page do %>
<section class="thredded--main-section">
<% if @users.present? %>
<% if @query.present? %>
<p class="thredded--alert thredded--alert-success">
<%= t 'thredded.moderation.search_users.results_message', query: "'#{@query}'" %>
</p>
<% end %>
<table class="thredded--moderation--users-table thredded--table">
<caption><%= page_entries_info @users %></caption>
<thead>
<tr>
<th>User</th>
<th>Moderation state</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td>
<%= link_to user.thredded_display_name, user_moderation_path(user.id), class: 'thredded--link' %>
</td>
<td>
<%= render 'user_moderation_state', user: user %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= paginate @users %>
<% else %>
<p class="thredded--alert thredded--alert-danger">
<%= t 'thredded.moderation.search_users.no_results_message', query: "'#{@query}'" %>
</p>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1,23 @@
<div class="thredded--email">
<figure class="thredded--email-post">
<figcaption class="thredded--email-post--author">
<%= @post.user.thredded_display_name %> <%= link_to 'said', post_permalink_url(@post) %>:
</figcaption>
<% cache [@post, 'content-onebox-placeholders'] do %>
<%= render partial: 'thredded/posts/content',
locals: { post: @post, options: {onebox_placeholders: true} } %>
<% end %>
</figure>
<hr/>
<p>
This email was sent to you because you are following this topic
"<%= link_to @post.postable.title, post_permalink_url(@post.id) %>".
<%= link_to 'View the conversation here', topic_url(@post.postable) %>.
</p>
<p>
To unsubscribe from these emails, update your
<%= link_to 'preferences', edit_messageboard_preferences_url(@post.messageboard) %>.
</p>
</div>

View File

@ -0,0 +1,10 @@
<%= @post.content %>
---
This email was sent to you because you are following this topic
"<%= @post.postable.title %>". Go here to view the conversation:
<%= post_permalink_url @post %>
To unsubscribe from these emails, update your preferences here:
<%= edit_messageboard_preferences_url @post.messageboard %>

View File

@ -0,0 +1 @@
<%= @post.filtered_content(self) %>

View File

@ -0,0 +1 @@
<%= @post.filtered_content(self) %>

View File

@ -0,0 +1 @@
<%= render 'thredded/posts_common/content', post: post, options: local_assigns[:options] || {} %>

View File

@ -0,0 +1,5 @@
<%= render 'thredded/posts_common/form',
post: post,
content_label: t('thredded.posts.form.content_label'),
button_text: button_text,
button_submitting_text: local_assigns.key?(:button_submitting_text) ? button_submitting_text : nil %>

View File

@ -0,0 +1,13 @@
<% post, content = post_and_content if local_assigns.key?(:post_and_content) %>
<%= content_tag :article, id: dom_id(post), class: "thredded--post thredded--#{post.read_state}--post" do %>
<%= render 'thredded/posts_common/actions', post: post, actions: local_assigns[:actions] %>
<%= render 'thredded/posts_common/header', post: post %>
<%= content || render('thredded/posts/content', post: post) %>
<% if post.pending_moderation? && !Thredded.content_visible_while_pending_moderation %>
<p class="thredded--alert thredded--alert-warning"><%= t 'thredded.posts.pending_moderation_notice' %></p>
<% elsif post.blocked? && post.can_moderate? %>
<p class="thredded--alert thredded--alert-danger">
<%= render 'thredded/shared/content_moderation_blocked_state', moderation_record: post.last_moderation_record %>
</p>
<% end %>
<% end %>

View File

@ -0,0 +1,3 @@
<span class="posts_count">
<%= post.user_posts_count %> posts
</span>

View File

@ -0,0 +1,16 @@
<% content_for :thredded_page_title, t('thredded.nav.edit_post') %>
<% content_for :thredded_page_id, 'thredded--edit-post' %>
<% content_for :thredded_breadcrumbs do %>
<ul class="thredded--navigation-breadcrumbs">
<li><%= link_to t('thredded.nav.edit_post'), edit_post_path(@post_form.post) %></li>
</ul>
<% end %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= render 'thredded/posts/form',
post: @post_form,
button_text: t('thredded.posts.form.update_btn'),
button_submitting_text: t('thredded.posts.form.update_btn_submitting')%>
</section>
<% end %>

View File

@ -0,0 +1,15 @@
<% content_for :thredded_page_title, @post_form.topic.title %>
<% content_for :thredded_page_id, 'thredded--topic--new-post' %>
<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
<%= thredded_page do %>
<section class="thredded--main-section">
<div class="thredded--post-form--wrapper">
<h3 class="thredded--post-form--title"><%= t('thredded.posts.form.title_label') %></h3>
<%= render 'thredded/posts/form',
post: @post_form,
button_text: t('thredded.posts.form.create_btn'),
button_submitting_text: t('thredded.posts.form.create_btn_submitting') %>
</div>
</section>
<% end %>

View File

@ -0,0 +1,28 @@
<% actions ||= {} %>
<% actions_html = capture do %>
<%= view_hooks.post_common.actions.render self, post: post do %>
<% if actions[:quote] && post.can_reply? %>
<%= render 'thredded/posts_common/actions/quote', post: post %>
<% end %>
<% if post.can_update? %>
<%= render 'thredded/posts_common/actions/edit', post: post %>
<% end %>
<% if post.can_destroy? %>
<%= render 'thredded/posts_common/actions/delete', post: post %>
<% end %>
<% if post.read_state %>
<%= view_hooks.post_common.mark_as_unread.render self, post: post do %>
<%= render 'thredded/posts_common/actions/mark_as_unread', post: post %>
<% end %>
<% end %>
<% end %>
<% end %>
<%- if actions_html.present? %>
<div class='thredded--post--dropdown'>
<%= inline_svg 'thredded/three-dot-menu.svg', class: 'thredded--post--dropdown--toggle' %>
<div class='thredded--post--dropdown--actions'>
<%= actions_html %>
</div>
</div>
<% end %>

View File

@ -0,0 +1,3 @@
<div class="thredded--post--content">
<%= post.filtered_content(self, local_assigns[:options] || {}) %>
</div>

View File

@ -0,0 +1,22 @@
<%# locals: post, content_label, button_text, button_submitting_text. %>
<%= form_for post,
url: post.submit_path,
as: :post,
html: {
class: 'thredded--form thredded--post-form',
'data-thredded-post-form' => true,
'data-autocomplete-url' => autocomplete_users_path,
'data-autocomplete-min-length' => Thredded.autocomplete_min_length,
} do |form| %>
<ul class="thredded--form-list">
<%= render 'thredded/posts_common/form/content',
form: form, content_label: content_label, preview_url: post.preview_path %>
<li>
<% button_submitting_text ||=
post.persisted? ? t('thredded.form.update_btn_submitting') : t('thredded.form.create_btn_submitting') %>
<button type="submit" class="thredded--form--submit" data-disable-with="<%= button_submitting_text %>">
<%= button_text %>
</button>
</li>
</ul>
<% end %>

View File

@ -0,0 +1,8 @@
<%# @param post [Thredded::PostView] %>
<header>
<%= image_tag post.avatar_url, class: 'thredded--post--avatar' if post.user %>
<h2 class="thredded--post--user"><%= user_link post.user %></h2>
<a href="<%= post.permalink_path %>" rel="nofollow" class="thredded--link thredded--post--created-at">
<%= time_ago post.created_at %>
</a>
</header>

View File

@ -0,0 +1,15 @@
<%# @param post [Thredded::PostView] %>
<% topic = post.to_model.postable %>
<header>
<h2 class="thredded--post--topic">
<%=
topic_link = link_to(topic.title, post.permalink_path)
if topic.first_post == post.to_model
t 'thredded.users.started_topic_html', topic_link: topic_link
else
t 'thredded.users.posted_in_topic_html', topic_link: topic_link
end
%>
</h2>
<p class="thredded--post--created-at"><%= time_ago post.created_at %></p>
</header>

View File

@ -0,0 +1,18 @@
<%# @param post [Thredded::PostView] %>
<%# @param post_user_link [String] optional %>
<% topic = post.to_model.postable %>
<% post_user_link ||= user_link(post.user) %>
<header>
<%= image_tag post.avatar_url, class: 'thredded--post--avatar' if post.user %>
<h2 class="thredded--post--user-and-topic">
<%=
topic_link = link_to(topic.title, post.permalink_path)
if topic.first_post == post.to_model
t 'thredded.users.user_started_topic_html', user_link: post_user_link, topic_link: topic_link
else
t 'thredded.users.user_posted_in_topic_html', user_link: post_user_link, topic_link: topic_link
end
%>
</h2>
<p class="thredded--post--created-at"><%= time_ago post.created_at %></p>
</header>

View File

@ -0,0 +1,4 @@
<%= button_to t('thredded.posts.delete'), post.destroy_path,
method: :delete,
class: 'thredded--post--delete thredded--post--dropdown--actions--item',
data: {confirm: I18n.t('thredded.posts.delete_confirm')} %>

View File

@ -0,0 +1,3 @@
<%= link_to t('thredded.posts.edit'), post.edit_path,
class: 'thredded--post--edit thredded--post--dropdown--actions--item',
rel: 'nofollow' %>

View File

@ -0,0 +1,2 @@
<%= button_to(t('thredded.topics.mark_as_unread'), post.mark_unread_path, method: :post, class:
'thredded--post--mark-as-unread thredded--post--dropdown--actions--item') %>

View File

@ -0,0 +1,4 @@
<%= link_to t('thredded.posts.quote_btn'), url_for(post.quote_url_params),
'data-thredded-quote-post' => post.quote_path,
class: 'thredded--post--quote thredded--post--dropdown--actions--item',
rel: 'nofollow' %>

View File

@ -0,0 +1,7 @@
<%= render 'thredded/posts_common/form/content_field',
form: form,
content_label: content_label %>
<%= render 'thredded/posts_common/form/preview_area',
form: form,
preview_url: preview_url %>

View File

@ -0,0 +1,8 @@
<li>
<%= form.label :content, content_label %>
<%= view_hooks.post_form.content_text_area.render self, form: form, content_label: content_label do %>
<%= render 'thredded/posts_common/form/before_content', form: form %>
<%= form.text_area :content, {rows: 5, required: true} %>
<%= render 'thredded/posts_common/form/after_content', form: form %>
<% end %>
</li>

View File

@ -0,0 +1,16 @@
<%= view_hooks.post_form.preview_area.render self, form: form, preview_url: preview_url do %>
<li>
<noscript>
<button type="submit" class="thredded--button thredded--post--preview-btn"
formtarget="_blank" formaction="<%= preview_url %>">
<%= t 'thredded.form.preview' %>
</button>
</noscript>
<div class="thredded--preview-area"
data-thredded-preview-area data-thredded-preview-url="<%= preview_url %>">
<h4 class="thredded--preview-area--title"><%= t 'thredded.form.preview' %></h4>
<div class="thredded--preview-area--post thredded--post--content" data-thredded-preview-area-post></div>
</div>
</li>
<% end %>

View File

@ -0,0 +1,95 @@
<%# @type preferences [Thredded::UserPreferencesForm] %>
<%= form_for(preferences, method: :patch, url: preferences.update_path, html: {
class: 'thredded--form thredded--notification-preferences-form',
'data-thredded-user-preferences-form' => true
}) do |f| %>
<ul class="thredded--form-list">
<li>
<%= f.label :auto_follow_topics do %>
<%= f.check_box :auto_follow_topics,
'data-thredded-update-checkbox-on-change' =>
'user_preferences_form[messageboard_auto_follow_topics]' %>
<%= t 'thredded.preferences.form.auto_follow_topics.label' %>
<p class="thredded--form-list--hint">
<%= t 'thredded.preferences.form.auto_follow_topics.hint' %>
</p>
<% end %>
</li>
<li>
<%= f.label :follow_topics_on_mention do %>
<%= f.check_box :follow_topics_on_mention, 'data-thredded-bound-messageboard-pref' => 'user_preferences_form[messageboard_follow_topics_on_mention]' %>
<%= t 'thredded.preferences.form.follow_topics_on_mention.label' %>
<p class="thredded--form-list--hint">
<%= t 'thredded.preferences.form.follow_topics_on_mention.hint' %>
</p>
<% end %>
</li>
<% if Thredded.notifiers.present? %>
<li>
<label><%= t 'thredded.preferences.form.notifications_for_followed_topics.label' %></label>
<%= f.fields_for :notifications_for_followed_topics, preferences.notifications_for_followed_topics do |fn| %>
<%= fn.label :enabled do %>
<%= fn.hidden_field :notifier_key %>
<%= fn.check_box :enabled,
'data-thredded-bound-messageboard-pref' =>
"user_preferences_form[messageboard_notifications_for_followed_topics_attributes][#{fn.index}][enabled]" %>
<%= fn.object.notifier_human_name %>
<%- end %>
<%- end %>
</li>
<li>
<label><%= t 'thredded.preferences.form.notifications_for_private_topics.label' %></label>
<%= f.fields_for :notifications_for_private_topics, preferences.notifications_for_private_topics do |fn| %>
<%= fn.label :enabled do %>
<%= fn.hidden_field :notifier_key %>
<%= fn.check_box :enabled %>
<%= fn.object.notifier_human_name %>
<% end %>
<% end %>
</li>
<% end %>
</ul>
<% if preferences.messageboard %>
<h2 class="thredded--preferences--title">
<%= t 'thredded.preferences.messageboard_preferences_title_html', messageboard: messageboard.name %>
</h2>
<ul class="thredded--form-list" data-thredded-user-preferences-form-messageboard-fields>
<li>
<%= f.label :messageboard_auto_follow_topics do %>
<%= f.check_box :messageboard_auto_follow_topics %>
<%= t 'thredded.preferences.form.messageboard_auto_follow_topics.label' %>
<p class="thredded--form-list--hint">
<%= t 'thredded.preferences.form.messageboard_auto_follow_topics.hint' %>
</p>
<% end %>
</li>
<li>
<%= f.label :messageboard_follow_topics_on_mention do %>
<%= f.check_box :messageboard_follow_topics_on_mention %>
<%= t 'thredded.preferences.form.messageboard_follow_topics_on_mention.label' %>
<p class="thredded--form-list--hint">
<%= t 'thredded.preferences.form.messageboard_follow_topics_on_mention.hint' %>
</p>
<% end %>
</li>
<% if Thredded.notifiers.present? %>
<li>
<label><%= t 'thredded.preferences.form.messageboard_notifications_for_followed_topics.label' %></label>
<%= f.fields_for :messageboard_notifications_for_followed_topics,
preferences.messageboard_notifications_for_followed_topics do |fn| %>
<%= fn.label :enabled do %>
<%= fn.hidden_field :notifier_key %>
<%= fn.hidden_field :messageboard_id %>
<%= fn.check_box :enabled %>
<%= fn.object.notifier_human_name %>
<% end %>
<%- end %>
</li>
<% end %>
</ul>
<% end %>
<button type="submit" class="thredded--form--submit"
data-disable-with="<%= t 'thredded.preferences.form.update_btn_submitting' %>">
<%= t 'thredded.preferences.form.submit_btn' %>
</button>
<% end %>

View File

@ -0,0 +1,8 @@
<% preferences.messageboard_groups.each do |group| %>
<h3 class="thredded--messageboards-group--title"><%= group.name %></h3>
<div class="thredded--messageboards-group">
<%= render partial: 'thredded/preferences/messageboards_nav_item',
collection: group.messageboards,
as: :messageboard %>
</div>
<% end %>

View File

@ -0,0 +1,2 @@
<%= link_to messageboard.name, edit_messageboard_preferences_path(messageboard),
class: 'thredded--preferences--messageboards-nav--item thredded--messageboard' %>

View File

@ -0,0 +1,20 @@
<% content_for :thredded_page_title, t('thredded.preferences.edit.page_title') %>
<% content_for :thredded_page_id, 'thredded--preferences' %>
<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
<%= thredded_page do %>
<section class="thredded--main-section thredded--preferences">
<section class="thredded--preferences--form">
<h2 class="thredded--preferences--title"><%= t 'thredded.preferences.global_preferences_title' %></h2>
<%= render 'thredded/preferences/form', preferences: @preferences %>
</section>
<% unless preferences.messageboard %>
<section class="thredded--preferences--messageboards-nav">
<h2 class="thredded--preferences--title">
<%= t 'thredded.preferences.messageboard_preferences_nav_title' %>
</h2>
<%= render 'thredded/preferences/messageboards_nav', preferences: @preferences %>
</section>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1 @@
<%= @private_post.filtered_content(self) %>

View File

@ -0,0 +1 @@
<%= @private_post.filtered_content(self) %>

View File

@ -0,0 +1 @@
<%= render 'thredded/posts_common/content', post: post, options: local_assigns[:options] || {} %>

View File

@ -0,0 +1,6 @@
<%# TODO: For private topics, only autocomplete users in this private topic %>
<%= render 'thredded/posts_common/form',
post: post,
button_text: t('thredded.private_posts.form.create_btn'),
button_submitting_text: t('thredded.private_posts.form.create_btn_submitting'),
content_label: t('thredded.private_posts.form.content_label') %>

View File

@ -0,0 +1,6 @@
<% private_post, content = post_and_content if local_assigns.key?(:post_and_content) %>
<%= content_tag :article, id: dom_id(private_post), class: 'thredded--post' do %>
<%= render 'thredded/posts_common/actions', post: private_post, actions: local_assigns[:actions] %>
<%= render 'thredded/posts_common/header', post: private_post %>
<%= content || render('thredded/private_posts/content', post: post) %>
<% end %>

View File

@ -0,0 +1,16 @@
<% content_for :thredded_page_title, t('thredded.nav.edit_post') %>
<% content_for :thredded_page_id, 'thredded--edit-post' %>
<% content_for :thredded_breadcrumbs do %>
<ul class="thredded--navigation-breadcrumbs">
<li><%= link_to t('thredded.nav.edit_post'), edit_post_path(@post_form.post) %></li>
</ul>
<% end %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= render 'thredded/posts/form',
post: @post_form,
button_text: t('thredded.posts.form.update_btn'),
button_submitting_text: t('thredded.posts.form.update_btn_submitting')%>
</section>
<% end %>

View File

@ -0,0 +1,11 @@
<% private_topic = @post_form.topic %>
<% content_for :thredded_page_title, private_topic.title %>
<% content_for :thredded_page_id, 'thredded--private-topic--new_post' %>
<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= render 'thredded/private_posts/form',
post: @post_form %>
</section>
<% end %>

View File

@ -0,0 +1,20 @@
<div class="thredded--email">
<figure class="thredded--email-post">
<figcaption class="thredded--email-post--author">
<%= @post.user.thredded_display_name %> <%= link_to 'said', private_post_permalink_url(@post) %>:
</figcaption>
<% cache [@post, 'content-onebox-placeholders'] do %>
<%= render partial: 'thredded/private_posts/content',
locals: { post: @post, options: {onebox_placeholders: true} } %>
<% end %>
</figure>
<hr/>
<p>
This email was sent to you because <%= @topic.user.thredded_display_name %> included you in a
private topic, "<%= link_to @topic.title, private_topic_url(@topic) %>".
</p>
<p>
To unsubscribe from these emails, update your <%= link_to 'preferences', edit_preferences_url %>.
</p>
</div>

View File

@ -0,0 +1,12 @@
<%= @post.user.thredded_display_name %> said:
<%= @post.content %>
---
This email was sent to you because <%= @topic.user.thredded_display_name %>
included you in the private topic "<%= @topic.title %>".
Go here to view the conversation:
<%= private_post_permalink_url(@post) %>
To unsubscribe from these emails, update your preferences here:
<%= edit_preferences_url %>

View File

@ -0,0 +1 @@
<%= @private_post.filtered_content(self) %>

View File

@ -0,0 +1,4 @@
<ul class="thredded--navigation-breadcrumbs">
<li><%= link_to t('thredded.nav.all_messageboards'), messageboards_path -%></li>
<li><%= link_to t('thredded.nav.private_topics'), private_topics_path -%></li>
</ul>

View File

@ -0,0 +1,39 @@
<%= form_for private_topic,
url: private_topic.submit_path,
html: {
class: "thredded--form thredded--new-private-topic-form #{local_assigns[:css_class]}",
'data-thredded-topic-form' => true,
# TODO: only autocomplete users in this private topic
'data-autocomplete-url' => autocomplete_users_path,
'data-autocomplete-min-length' => Thredded.autocomplete_min_length,
} do |form| %>
<ul class="thredded--form-list on-top">
<li class="title">
<%= form.label :title, t('thredded.private_topics.form.title_label') %>
<%= form.text_field :title, placeholder: placeholder, required: true %>
</li>
<li>
<%= form.label :user_names, t('thredded.private_topics.form.users_label') %>
<%= form.text_area :user_names,
placeholder: t('thredded.private_topics.form.users_placeholder'),
'data-thredded-users-select' => true,
'data-autocomplete-url' => autocomplete_users_path,
'data-autocomplete-min-length' => Thredded.autocomplete_min_length,
rows: 1 %>
</li>
<%= render 'thredded/posts_common/form/content',
form: form,
content_label: t('thredded.private_topics.form.content_label'),
preview_url: private_topic.preview_path %>
<li>
<button type="submit" class="thredded--form--submit"
data-disable-with="<%= t 'thredded.private_topics.form.create_btn_submitting' %>">
<%= t('thredded.private_topics.form.create_btn') %>
</button>
</li>
</ul>
<% end %>

View File

@ -0,0 +1,17 @@
<header class="thredded--topic-header">
<h1 class="thredded--topic-header--title"><%= topic.title %></h1>
<cite class="thredded--topic-header--started-by">
<%= t 'thredded.topics.started_by_html',
time_ago: time_ago(topic.created_at),
user: user_link(topic.user) %>
</cite>
<span class="thredded--topic-header--participants">
<%= render partial: 'thredded/private_topics/header/participant',
collection: (topic.users - [topic.user]) %>
</span>
<% if topic.can_update? %>
<%= link_to t('thredded.private_topics.edit'), topic.edit_path,
class: 'thredded--topic-header--edit-topic',
rel: 'nofollow' %>
<% end %>
</header>

View File

@ -0,0 +1,6 @@
<div class="thredded--empty">
<h3 class="thredded--empty--title"><%= t 'thredded.private_topics.no_private_topics.title' %></h3>
<a class="thredded--button" href="<%= new_private_topic_path %>">
<%= t 'thredded.private_topics.no_private_topics.create_btn' %>
</a>
</div>

View File

@ -0,0 +1,23 @@
<%= content_tag :article,
id: dom_id(private_topic),
class: ['thredded--topics--topic', *topic_css_classes(private_topic)] do %>
<div class="thredded--topics--posts-count"><%= private_topic.posts_count %></div>
<h1 class="thredded--topics--title">
<%= link_to private_topic.title, private_topic.path %>
</h1>
<cite class="thredded--topics--updated-by">
<%= time_ago private_topic.last_post_at %>
<span class="thredded--topics--participants">
<%= render partial: 'thredded/private_topics/private_topic/participant',
collection: [private_topic.last_user, *(private_topic.users - [private_topic.last_user])] %>
</span>
</cite>
<cite class="thredded--topics--started-by">
<%= time_ago private_topic.created_at %>
<%= user_link private_topic.user %>
</cite>
<% end %>

View File

@ -0,0 +1,35 @@
<% content_for :thredded_page_title, t('thredded.nav.edit_private_topic') %>
<% content_for :thredded_page_id, 'thredded--edit-private-topic' %>
<% content_for :thredded_breadcrumbs do %>
<ul class="thredded--navigation-breadcrumbs">
<li><%= link_to t('thredded.nav.all_messageboards'), messageboards_path %></li>
<li><%= link_to t('thredded.nav.private_topics'), private_topics_path %></li>
<li><%= link_to @private_topic.title, topic_path(@private_topic) %></li>
<li><%= link_to t('thredded.nav.edit_private_topic'), edit_private_topic_path(@private_topic) %></li>
</ul>
<% end %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= form_for @private_topic,
url: private_topic_path(@private_topic),
html: { class: 'thredded--form', 'data-thredded-topic-form' => true } do |form| %>
<ul class="thredded--form-list on-top">
<li class="title">
<%= form.label :title, t('thredded.private_topics.form.title_label') %>
<%= form.text_field :title,
placeholder: t('thredded.private_topics.form.title_placeholder_new'),
autofocus: true,
required: true %>
</li>
<li>
<button type="submit" class="thredded--form--submit"
data-disable-with="<%= t 'thredded.private_topics.form.update_btn_submitting' %>">
<%= t('thredded.private_topics.form.update_btn') %>
</button>
</li>
</ul>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1 @@
<span class="thredded--topic-header--participants--participant"><%= user_link participant %></span>

View File

@ -0,0 +1,32 @@
<% content_for :thredded_page_title, t('thredded.nav.private_topics') %>
<% content_for :thredded_page_id, 'thredded--private-topics-index' %>
<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %>
<%= thredded_page do %>
<%= content_tag :section,
class: 'thredded--main-section thredded--private-topics',
'data-thredded-topics' => true do %>
<% if @private_topics.empty? -%>
<%= render 'thredded/private_topics/no_private_topics' %>
<% else -%>
<%= render 'thredded/private_topics/form',
private_topic: @new_private_topic,
css_class: 'thredded--is-compact',
placeholder: t('thredded.private_topics.form.title_placeholder_start') if @new_private_topic %>
<%= render @private_topics %>
<% unless unread_private_topics_count.zero? %>
<%= link_to t('thredded.nav.mark_all_read'),
mark_all_private_topics_read_path,
class: 'thredded--button thredded--button-light thredded--button-wide',
method: :put -%>
<% end %>
<% end -%>
<% end %>
<footer class="thredded--pagination-bottom">
<%= paginate @private_topics %>
</footer>
<% end %>

View File

@ -0,0 +1,11 @@
<% content_for :thredded_page_title, 'Create a New Private Topic' %>
<% content_for :thredded_page_id, 'thredded--new-private-topic' %>
<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %>
<%= thredded_page do %>
<section class="thredded--main-section">
<%= render 'thredded/private_topics/form',
private_topic: @private_topic,
placeholder: t('thredded.private_topics.form.title_placeholder_new') if @private_topic %>
</section>
<% end %>

View File

@ -0,0 +1 @@
<span class="thredded--topics--participants--participant"><%= user_link participant %></span>

View File

@ -0,0 +1,28 @@
<% private_topic = @posts.topic %>
<% content_for :thredded_page_title, private_topic.title %>
<% content_for :thredded_page_id, 'thredded--private-topic-show' %>
<% content_for :thredded_breadcrumbs, render('thredded/private_topics/breadcrumbs') %>
<%= thredded_page do %>
<%= content_tag :section,
id: dom_id(private_topic),
class: ['thredded--main-section', 'thredded--topic', *topic_css_classes(private_topic)] do %>
<%= render 'thredded/private_topics/header', topic: private_topic %>
<%= view_hooks.posts_common.pagination_top.render(self, posts: @posts) do %>
<footer class="thredded--pagination-top"><%= paginate @posts %></footer>
<% end %>
<%= render_posts @posts,
partial: 'thredded/private_posts/private_post',
content_partial: 'thredded/private_posts/content',
locals: { actions: { quote: true } } %>
<%= view_hooks.posts_common.pagination_bottom.render(self, posts: @posts) do %>
<footer class="thredded--pagination-bottom"><%= paginate @posts %></footer>
<% end %>
<% if policy(@new_post.post).create? %>
<%= render 'thredded/private_posts/form',
topic: private_topic,
post: @new_post %>
<% end %>
<% end %>
<% end %>

View File

@ -0,0 +1,13 @@
<%= form_tag search_path(messageboard),
method: 'get',
class: 'thredded--form thredded--navigation--search thredded--navigation--search-topics',
'data-thredded-turboform' => true do %>
<%= label_tag :q, t('thredded.search.form.label') %>
<%= text_field_tag :q, @query,
type: 'search',
required: true,
# If there are no results the user will likely want to change the query, so auto-focus.
autofocus: @query.presence && !@topics.presence,
placeholder: t('thredded.search.form.placeholder') %>
<button type="submit"><%= t 'thredded.search.form.btn_submit' %></button>
<% end %>

Some files were not shown because too many files have changed in this diff Show More