Skip to content

Commit

Permalink
Add ability for users to publish series (forem#1034)
Browse files Browse the repository at this point in the history
  • Loading branch information
benhalpern authored Oct 29, 2018
1 parent b6ac7a8 commit 359cc71
Show file tree
Hide file tree
Showing 17 changed files with 181 additions and 21 deletions.
14 changes: 7 additions & 7 deletions app/assets/stylesheets/article-show.scss
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,13 @@ header{
.article-collection-wrapper{
text-align:center;
font-family: $helvetica;
font-size:15px;
font-size:16px;
p{
border-bottom:2px solid $black;
width:120px;
max-width:90%;
margin:35px auto 15px;
padding:2px 0px;
font-weight:bold;
font-style: oblique;
}
.article-collection{
display:inline-block;
Expand All @@ -265,19 +265,19 @@ header{
margin-bottom: 1.1vw;
a{
color:$black;
padding:calc(0.8vw + 10px);
background:$lightest-gray;
padding:calc(0.3vw + 14px);
background:darken($lightest-gray, 12%);
display:inline-block;
position:relative;
z-index:4;
max-width:260px;
margin:0.2vw;
border-radius:5px;
border-radius:100px;
&:first-child{
// margin-left:-5px;
}
&:hover{
background:darken($lightest-gray,3%);
background:darken($lightest-gray,18%);
}
&.current-article{
background: $black;
Expand Down
13 changes: 13 additions & 0 deletions app/assets/stylesheets/preact/article-form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@
@media screen and ( min-width: 600px ){
width: calc(100% - 40px);
}
button {
margin-left: 5px;
background: $bold-blue;
}
}
.articleform__exitbutton{
position:absolute;
Expand All @@ -392,6 +396,15 @@
border-radius: 3px;
font-weight: bold;
}
.articleform__donebutton{
display: block;
font-size: 22px;
background: $green;
color: $black;
padding: 10px 40px;
margin: 20px auto;

}
img{
display: block;
margin: 10px auto;
Expand Down
5 changes: 4 additions & 1 deletion app/controllers/api/v0/articles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ def article_params
else
params["article"]["organization_id"] = nil
end
if params["article"]["series"].present?
params["article"]["collection_id"] = Collection.find_series(params["article"]["series"], current_user)&.id
end
params.require(:article).permit(
:title, :body_markdown, :user_id, :main_image, :published, :description,
:tag_list, :organization_id, :canonical_url
:tag_list, :organization_id, :canonical_url, :series, :collection_id
)
end
end
Expand Down
8 changes: 4 additions & 4 deletions app/controllers/articles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ def new
@article = if @tag.present? && @user&.editor_version == "v2"
authorize Article
Article.new(body_markdown: "", cached_tag_list: @tag.name,
processed_html: "")
processed_html: "", user_id: current_user&.id)
elsif @tag&.submission_template.present? && @user
authorize Article
Article.new(body_markdown: @tag.submission_template_customized(@user.name),
processed_html: "")
processed_html: "", user_id: current_user&.id)
else
skip_authorization
if @user&.editor_version == "v2"
Article.new
Article.new(user_id: current_user&.id)
else
Article.new(
body_markdown: "---\ntitle: \npublished: false\ndescription: \ntags: \n---\n\n",
processed_html: "",
processed_html: "", user_id: current_user&.id
)
end
end
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/article-form/articleForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export default class ArticleForm extends Component {
tagList: article.cached_tag_list || '',
description: '',
canonicalUrl: article.canonical_url || '',
series: article.series || '',
allSeries: article.all_series || [],
bodyMarkdown: article.body_markdown || '',
published: article.published || false,
previewShowing: false,
Expand Down Expand Up @@ -118,6 +120,7 @@ export default class ArticleForm extends Component {
};

handleConfigChange = e => {
e.preventDefault();
let newState = {}
newState[e.target.name] = e.target.value;
this.setState(newState)
Expand Down
1 change: 1 addition & 0 deletions app/javascript/article-form/elements/imageManagement.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export default class ImageManagement extends Component {
{mainImageArea}
<h2>Body Images</h2>
{inertionImageArea}
<div><button class="articleform__donebutton" onClick={onExit}>Done</button></div>
</div>

}
Expand Down
18 changes: 18 additions & 0 deletions app/javascript/article-form/elements/moreConfig.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,21 @@ export default class MoreConfig extends Component {
// this.state = {insertionImageUrl: null}
}

handleSeriesButtonClick = e => {
e.preventDefault();
this.props.onConfigChange(e);
}

render() {
const { onExit, passedData, onSaveDraft } = this.props;
let publishedField = '';
let seriesTip = <small>Will this post be part of a series? Give the series a unique name. (Series visible once it has multiple posts)</small>
if (passedData.allSeries.length > 0) {
const seriesNames = passedData.allSeries.map( name => {
return <button name='series' onClick={this.props.onConfigChange} value={name}>{name}</button>
})
seriesTip = <small>Existing series: {seriesNames}</small>
}
if (passedData.published) {
publishedField = <div>
<h4>Danger Zone</h4>
Expand All @@ -35,6 +47,12 @@ export default class MoreConfig extends Component {
<input type="text" value={passedData.canonicalUrl} name="canonicalUrl" onKeyUp={this.props.onConfigChange}/>
</div>
<small>Change meta tag <code>canonical_url</code> if this post was first published elsewhere (like your own blog)</small>
<div>
<label>Series Name</label>
<input type="text" value={passedData.series} name="series" onKeyUp={this.props.onConfigChange}/>
</div>
{seriesTip}
<div><button class="articleform__donebutton" onClick={onExit}>Done</button></div>
{publishedField}
</div>
}
Expand Down
20 changes: 19 additions & 1 deletion app/models/article.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Article < ApplicationRecord

acts_as_taggable_on :tags

attr_accessor :publish_under_org
attr_accessor :publish_under_org, :series

belongs_to :user
belongs_to :job_opportunity, optional: true
Expand All @@ -31,6 +31,7 @@ class Article < ApplicationRecord
validates :body_markdown, uniqueness: { scope: %i[user_id title] }
validate :validate_tag
validate :validate_video
validate :validate_collection_permission
validates :video_state, inclusion: { in: %w(PROGRESSING COMPLETED) }, allow_nil: true
validates :cached_tag_list, length: { maximum: 86 }
validates :main_image, url: { allow_blank: true, schemes: ["https", "http"] }
Expand Down Expand Up @@ -359,6 +360,16 @@ def async_score_calc
end
handle_asynchronously :async_score_calc

def series
# name of series article is part of
collection&.slug
end

def all_series
# all series names
user&.collections&.pluck(:slug)
end

private

# def send_to_moderator
Expand Down Expand Up @@ -387,6 +398,7 @@ def evaluate_front_matter(front_matter)
self.main_image = front_matter["cover_image"] if front_matter["cover_image"].present?
self.canonical_url = front_matter["canonical_url"] if front_matter["canonical_url"].present?
self.description = front_matter["description"] || token_msg
self.collection_id = Collection.find_series(front_matter["series"], user).id if front_matter["series"].present?
if front_matter["automatically_renew"].present? && tag_list.include?("hiring")
self.automatically_renew = front_matter["automatically_renew"]
end
Expand Down Expand Up @@ -418,6 +430,12 @@ def validate_video
end
end

def validate_collection_permission
if collection && collection.user_id != user_id
errors.add(:collection_id, "must be one you have permission to post to")
end
end

def create_slug
if slug.blank? && title.present? && !published
self.slug = title_to_slug + "-temp-slug-#{rand(10_000_000)}"
Expand Down
8 changes: 8 additions & 0 deletions app/models/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@ class Collection < ApplicationRecord
has_many :articles
belongs_to :user, optional: true
belongs_to :organization, optional: true

validates :user_id, presence: true
validates :slug, uniqueness: { scope: :user_id }

def self.find_series(slug, user)
series = Collection.where(slug: slug, user: user).first
series || Collection.create(slug: slug, user: user)
end
end
1 change: 1 addition & 0 deletions app/services/article_creation_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def create!
if user.organization_id.present? && article_params[:publish_under_org].to_i == 1
article.organization_id = user.organization_id
end
article.collection_id = Collection.find_series(article_params[:series], user).id if article_params[:series].present?
create_job_opportunity(article)
if article.save
if article.published
Expand Down
13 changes: 8 additions & 5 deletions app/views/articles/_collection.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<% if @article.collection.present? && @article.collection.articles.size > 1 %>
<% @collection = @article.collection %>
<% if @collection.present? && @collection.articles.where(published: true).size > 1 %>
<div class="article-collection-wrapper article-collection-wrapper-<%= position %>">
<p>Part of a series</p>
<% if @collection.slug.present? %>
<p>Part of "<%= @collection.slug %>" series</p>
<% else %>
<p>Part of a series</p>
<% end %>
<div class="article-collection">
<% @article.collection.articles.order("collection_position ASC").each_with_index do |article,i| %>
<% @collection.articles.where(published: true).order("published_at ASC").each_with_index do |article,i| %>
<a
href="<%= article.path if article.published %>"
class="<%= collection_link_class(@article,article) %>"
Expand Down
2 changes: 1 addition & 1 deletion app/views/articles/_v2_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="articleform"
id="article-form"
data-article="<%= @article.
to_json(only: [:id, :title, :cached_tag_list, :published, :body_markdown, :main_image, :organization_id, :canonical_url]) %>"
to_json(only: [:id, :title, :cached_tag_list, :published, :body_markdown, :main_image, :organization_id, :canonical_url], methods: [:series, :all_series]) %>"
data-organization="<%= @organization&.to_json(only: [:name, :bg_color_hex, :text_color_hex], methods: [:profile_image_90]) %>"
>

Expand Down
4 changes: 3 additions & 1 deletion app/views/articles/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@
<div class="body" data-article-id="<%= @article.id %>" id="article-body" itemprop="articleBody">
<%= @article.processed_html.html_safe %>
</div>
<%= render "articles/collection", position: "bottom" %>
<% if @article.body_markdown && @article.body_markdown.size > 900 %>
<%= render "articles/collection", position: "bottom" %>
<% end %>
<%= render 'articles/actions' %>
</section>
<% if @article.body_markdown && @article.body_markdown.size > 900 %>
Expand Down
3 changes: 2 additions & 1 deletion lib/tasks/fetch.rake
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ task remove_old_html_variant_data: :environment do
HtmlVariantTrial.where("created_at < ?", 1.week.ago).destroy_all
HtmlVariantSuccess.where("created_at < ?", 1.week.ago).destroy_all
HtmlVariant.find_each do |html_variant|
return if html_variant.html_variant_successes.size < 12
if html_variant.html_variant_successes.size > 3
html_variant.calculate_success_rate!
end
end
end
12 changes: 12 additions & 0 deletions spec/models/article_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -473,5 +473,17 @@ def build_and_validate_article(*args)
last_year = 1.year.ago.year % 100
expect(article.readable_publish_date.include?("'#{last_year}")).to eq(true)
end

it "is valid as part of a collection" do
collection = Collection.create(user_id: article.user.id, slug: "yoyoyo")
article.collection_id = collection.id
expect(article).to be_valid
end

it "is not valid as part of a collection that does not belong to user" do
collection = Collection.create(user_id: 32443, slug: "yoyoyo")
article.collection_id = collection.id
expect(article).not_to be_valid
end
end
# rubocop:enable RSpec/ExampleLength, RSpec/MultipleExpectations
66 changes: 66 additions & 0 deletions spec/requests/articles_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,70 @@
expect(JSON.parse(response.body)["title"]).to eq(article.title)
end
end

describe "POST /api/articles w/ current_user" do
before do
sign_in user1
end
it "creates ordinary article with proper params" do
new_title = "NEW TITLE #{rand(100)}"
post "/api/articles", params: {
article: { title: new_title, body_markdown: "Yo ho ho#{rand(100)}", tag_list: "yo" }
}
expect(Article.last.user_id).to eq(user1.id)
end
# rubocop:disable RSpec/ExampleLength
it "creates article with front matter params" do
post "/api/articles", params: {
article: {
body_markdown: "---\ntitle: hey hey hahuu\npublished: false\n---\nYo ho ho#{rand(100)}",
tag_list: "yo"
}
}
expect(Article.last.title).to eq("hey hey hahuu")
end
it "creates article w/ series param" do
new_title = "NEW TITLE #{rand(100)}"
post "/api/articles", params: {
article: { title: new_title,
body_markdown: "Yo ho ho#{rand(100)}",
tag_list: "yo",
series: "helloyo",
}
}
expect(Article.last.collection).to eq(Collection.find_by_slug("helloyo"))
expect(Article.last.collection.user_id).to eq(Article.last.user_id)
end
it "creates article with front matter params" do
post "/api/articles", params: {
article: {
body_markdown: "---\ntitle: hey hey hahuu\npublished: false\nseries: helloyo\n---\nYo ho ho#{rand(100)}",
tag_list: "yo"
}
}
expect(Article.last.collection).to eq(Collection.find_by_slug("helloyo"))
expect(Article.last.collection.user_id).to eq(Article.last.user_id)
end
end

describe "PUT /api/articles/:id w/ current_user" do
before do
sign_in user1
@article = create(:article, user: user1)
end
it "updates ordinary article with proper params" do
new_title = "NEW TITLE #{rand(100)}"
put "/api/articles/#{@article.id}", params: {
article: { title: new_title, body_markdown: "Yo ho ho#{rand(100)}", tag_list: "yo" }
}
expect(Article.last.title).to eq(new_title)
end
it "updates ordinary article with proper params" do
new_title = "NEW TITLE #{rand(100)}"
put "/api/articles/#{@article.id}", params: {
article: { title: new_title, body_markdown: "Yo ho ho#{rand(100)}", tag_list: "yo" }
}
expect(Article.last.title).to eq(new_title)
end
end
end
11 changes: 11 additions & 0 deletions spec/requests/articles_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,16 @@
}
expect(Article.last.job_opportunity.remoteness).to eq("on_premise")
end

it "creates series when series is created with frontmatter" do
new_title = "NEW TITLE #{rand(100)}"
post "/articles", params: {
article: {
title: new_title,
body_markdown: "---\ntitle: hey hey hahuu\npublished: false\nseries: helloyo\n---\nYo ho ho#{rand(100)}",
}
}
expect(Collection.last.slug).to eq("helloyo")
end
# rubocop:enable RSpec/ExampleLength
end

0 comments on commit 359cc71

Please sign in to comment.