diff --git a/app/assets/javascripts/initializePage.js.erb b/app/assets/javascripts/initializePage.js.erb index 52335ea3f..e1dd22939 100644 --- a/app/assets/javascripts/initializePage.js.erb +++ b/app/assets/javascripts/initializePage.js.erb @@ -38,6 +38,7 @@ function callInitalizers(){ initializeAnalytics(); initializeCommentDropdown(); initializeSettings(); + initializeAdditionalContentBoxes(); if (!initializeLiveArticle.called){ initializeLiveArticle(); } diff --git a/app/assets/javascripts/initializers/initializeAdditionalContentBoxes.js b/app/assets/javascripts/initializers/initializeAdditionalContentBoxes.js new file mode 100644 index 000000000..278c1bbe2 --- /dev/null +++ b/app/assets/javascripts/initializers/initializeAdditionalContentBoxes.js @@ -0,0 +1,19 @@ +function initializeAdditionalContentBoxes() { + var el = document.getElementById("additional-content-area"); + if (el) { + window.fetch('/additional_content_boxes?article_id='+el.dataset.articleId, { + method: 'GET', + credentials: 'same-origin' + }).then(function (response) { + if (response.status === 200) { + response.text().then(function(html){ + el.innerHTML = html; + initializeReadingListIcons(); + initializeAllFollowButts(); + }) + } else { + // there's currently no errorCb. + } + }); + } +} \ No newline at end of file diff --git a/app/assets/javascripts/initializers/initializeSponsorshipVisibility.js b/app/assets/javascripts/initializers/initializeSponsorshipVisibility.js index 0af25133a..1d68c7451 100644 --- a/app/assets/javascripts/initializers/initializeSponsorshipVisibility.js +++ b/app/assets/javascripts/initializers/initializeSponsorshipVisibility.js @@ -1,6 +1,16 @@ function initializeSponsorshipVisibility() { - var el = document.getElementById("sponsorship-widget"); + var el = document.getElementById("sponsorship-widget") || document.getElementById("partner-content-display"); var user = userData(); + if (el) { + setTimeout(function(){ + if (window.ga) { + if (document.querySelectorAll('[data-partner-seen]').length === 0) { + ga('send', 'event', 'view', 'sponsor displayed on page', el.dataset.details, null); + el.dataset.partnerSeen = "true"; + } + } + }, 400) + } if (el && user && user.display_sponsors){ el.classList.add("showing"); setTimeout(function(){ diff --git a/app/assets/stylesheets/more-articles.scss b/app/assets/stylesheets/more-articles.scss index a3bcf4e2b..d60f16229 100644 --- a/app/assets/stylesheets/more-articles.scss +++ b/app/assets/stylesheets/more-articles.scss @@ -78,3 +78,165 @@ } } } + + +.show-page-content-display { + vertical-align:top; + font-family: $helvetica; + margin:40px auto; + position: relative; + overflow:hidden; + a{ + color: $black; + } + .content-classification{ + margin: 30px 20px 0px; + font-weight: bold; + text-align: left; + font-size:0.78em; + .content-classification-text{ + background: lighten($yellow, 12%); + display: inline-block; + padding: 6px 25px; + } + } + .main-content-display{ + padding:20px 0px; + width: calc(90% - 60px); + padding: 30px; + margin-bottom: 60px; + @media screen and ( min-width: 680px ){ + float: left; + width: calc(97% - 365px); + border-right: 2px solid $light-medium-gray; + margin-left: 3%; + margin-top:10px; + padding: 15px 45px 35px 15px; + } + h2{ + margin:0px auto 10px; + font-size:2.2em; + } + .content-author{ + + img{ + border-radius: 100%; + width: 28px; + vertical-align: -7px; + margin-right: 3px; + } + a{ + color: #666666; + } + } + p { + margin-bottom: 30px; + } + .cta{ + padding: 7px 20px; + font-size: 1.2em; + margin-top: 10px; + margin-right: 3px; + display: inline-block; + } + .engagement-count{ + font-family: $helvetica-condensed; + color: $medium-gray; + margin-top: -18px; + margin-bottom: -25px; + img{ + height: 20px; + min-width: 27px; + vertical-align: -3px; + margin-right: 3px; + &.comments-bubble{ + margin-left: 10px; + } + } + } + .bookmark-engage{ + font-family: $helvetica-condensed; + background: darken($purple, 26%); + color: white; + letter-spacing: 1px; + border: 0px; + font-size: 1.2em; + padding: 7px 16px; + margin-top: 10px; + .bm-success{ + display: none; + } + &.selected{ + background: white; + color: darken($purple, 26%); + .bm-success{ + display: inline-block; + img{ + width: 18px; + vertical-align: -2px; + } + } + .bm-initial{ + display: none; + } + } + } + } + .secondary-content-display{ + display: none; + @media screen and ( min-width: 680px ){ + display: block; + } + float: left; + width: 300px; + text-align:center; + padding:20px 0px; + .profile-pic-wrapper{ + width: 110px; + height: 110px; + border-radius: 100%; + margin: auto; + &.wide-profile-image-wrapper{ + height: 50px; + width: 150px; + margin-top: 50px; + margin-bottom: 10px; + } + img{ + vertical-align: middle; + &.profile-image{ + width: calc(100% - 8px); + height: calc(100% - 8px); + border-radius: 100%; + } + &.wide-image{ + width: calc(100% - 8px); + } + } + } + .org-name{ + font-weight: bold; + margin: 10px auto; + font-size: 1.1em; + } + .follow-action-button{ + background: $green; + color: $black; + border: 0; + font-size: 1em; + border: 0; + padding: 3px 6px; + border-radius: 3px; + width:170px; + } + .org-summary{ + font-size:0.88em; + width: 200px; + margin: 20px auto; + text-align:left; + a{ + color: $bold-blue; + } + } + } +} \ No newline at end of file diff --git a/app/controllers/additional_content_boxes_controller.rb b/app/controllers/additional_content_boxes_controller.rb new file mode 100644 index 000000000..988e3af70 --- /dev/null +++ b/app/controllers/additional_content_boxes_controller.rb @@ -0,0 +1,16 @@ +class AdditionalContentBoxesController < ApplicationController + + def index + articles_ids = params[:article_id].split(",") + @article = Article.find(articles_ids[0]) + @for_user_article = ClassicArticle. + new(current_user || @article, {not_ids: articles_ids}).get + if (!user_signed_in? || current_user&.display_sponsors) && @article.user.permit_adjacent_sponsors + @boosted_article = BoostedArticle. + new(current_user, @article, {not_ids: (articles_ids+[@for_user_article])}).get + end + @alt_classic = ClassicArticle. + new(@article, {not_ids: (articles_ids+[@for_user_article])}).get unless @boosted_article + render "boxes", layout: false + end +end \ No newline at end of file diff --git a/app/controllers/internal/articles_controller.rb b/app/controllers/internal/articles_controller.rb index 95e14cc02..70ae205fe 100644 --- a/app/controllers/internal/articles_controller.rb +++ b/app/controllers/internal/articles_controller.rb @@ -53,6 +53,22 @@ class Internal::ArticlesController < Internal::ApplicationController page(params[:page]). limited_columns_internal_select. per(50) + when "classic-candidate" + @articles = Article.where("positive_reactions_count > ?", ClassicArticle.new.minimum_reaction_count). + includes(:user). + order("positive_reactions_count DESC"). + where(featured: false). + page(params[:page]). + per(100). + limited_columns_internal_select + when "classic" + @articles = Article.where("positive_reactions_count > ?", ClassicArticle.new.minimum_reaction_count). + includes(:user). + order("positive_reactions_count DESC"). + where(featured: true). + page(params[:page]). + per(100). + limited_columns_internal_select else #MIX @articles = Article. where(published: true). @@ -96,6 +112,7 @@ class Internal::ArticlesController < Internal::ApplicationController def article_params params.require(:article).permit(:featured, :social_image, + :body_markdown, :approved, :live_now, :main_image_background_hex_color, diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index d3dfac676..e2ce2ac2c 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -43,6 +43,7 @@ class OrganizationsController < ApplicationController def organization_params params.require(:organization).permit(:name, :summary, + :tag_line, :slug, :url, :proof, diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb index 5cf86afa4..748cfde15 100644 --- a/app/controllers/stories_controller.rb +++ b/app/controllers/stories_controller.rb @@ -224,6 +224,7 @@ class StoriesController < ApplicationController assign_second_and_third_user not_found if permission_denied? set_surrogate_key_header @article.record_key + @classic_article = ClassicArticle.new(@article).get unless user_signed_in? response.headers["Surrogate-Control"] = "max-age=10000, stale-while-revalidate=30, stale-if-error=86400" end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6b773b299..8d1cf70cd 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -182,6 +182,7 @@ class UsersController < ApplicationController prefer_language_fr prefer_language_it display_sponsors + permit_adjacent_sponsors feed_admin_publish_permission] accessible << %i[password password_confirmation] unless params[:user][:password].blank? params.require(:user). diff --git a/app/labor/boosted_article.rb b/app/labor/boosted_article.rb new file mode 100644 index 000000000..fdc3ba18c --- /dev/null +++ b/app/labor/boosted_article.rb @@ -0,0 +1,18 @@ +class BoostedArticle + + attr_accessor :user, :article, :tags, :not_ids + def initialize(user, article, options) + @user = user + @article = article + @tags = (user&.cached_followed_tag_names.to_a + article.decorate.cached_tag_list_array).compact + @not_ids = options[:not_ids] + end + + def get + Article.where(boosted: true). + includes(:user). + includes(:organization). + where.not(id: not_ids, organization_id: nil). + tagged_with(tags, any: true).sample + end +end diff --git a/app/labor/classic_article.rb b/app/labor/classic_article.rb index 6c875d0fe..e94de888a 100644 --- a/app/labor/classic_article.rb +++ b/app/labor/classic_article.rb @@ -1,35 +1,53 @@ class ClassicArticle - attr_accessor :user - def initialize(user) - @user = user + attr_accessor :input, :not_ids + def initialize(input=nil, options={}) + @input = input + @not_ids = options[:not_ids] end def get - possible_articles = [] - 5.times do - possible_articles << cached_qualifying_article + if rand(5) == 1 + random_high_quality_article + else + qualifying_articles(random_supported_tag_names).where.not(id: not_ids).compact.sample || + random_high_quality_article end - possible_articles.compact.sample end - def cached_qualifying_article - Rails.cache.fetch("classic-article-for-tag-#{random_supported_tag_name}_#{rand(0..1)}", expires_in: 45.minutes) do - Article.tagged_with(random_supported_tag_name). + def qualifying_articles(tag_names) + tag_name = tag_names.sample + Rails.cache.fetch("classic-article-for-tag-#{tag_name}}", expires_in: 45.minutes) do + articles = Article.tagged_with(tag_name). + includes(:user). where(published: true, featured: true). where("positive_reactions_count > ?", minimum_reaction_count). where("published_at > ?", 10.months.ago). - order("RANDOM()"). - first + order("RANDOM()") end end - def random_supported_tag_name - user.decorate.cached_followed_tags.where(supported: true).where.not(name: "ama").sample&.name + def random_high_quality_article + Article.where(published: true, featured: true). + where("positive_reactions_count > ?", 75). + includes(:user). + order("RANDOM()"). + where.not(id: not_ids). + first + end + + def random_supported_tag_names + if input.class.name == "User" + input.decorate.cached_followed_tags.where(supported: true).where.not(name: "ama").pluck(:name) + elsif input.class.name == "Article" + Tag.where(supported: true, name: input.decorate.cached_tag_list_array).where.not(name: "ama").pluck(:name) + else + ["discuss"] + end end def minimum_reaction_count if Rails.env.production? - 36 + 45 else 1 end diff --git a/app/models/article.rb b/app/models/article.rb index 2f46acf29..f5df18619 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -67,7 +67,7 @@ class Article < ApplicationRecord :video, :user_id, :organization_id, :video_source_url, :video_code, :video_thumbnail_url, :video_closed_caption_track_url, :social_image, :published_from_feed, :crossposted_at, :published_at, :featured_number, - :live_now, :last_buffered, :facebook_last_buffered, :created_at + :live_now, :last_buffered, :facebook_last_buffered, :created_at, :body_markdown ) } @@ -329,7 +329,7 @@ class Article < ApplicationRecord end def evaluate_front_matter(front_matter) - token_msg = "From the DEV community. Sharing ideas that makes us all better developers." + token_msg = body_text[0..80] + "..." self.title = front_matter["title"] if front_matter["title"].present? if front_matter["tags"].present? ActsAsTaggableOn::Taggable::Cache.included(Article) @@ -402,7 +402,7 @@ class Article < ApplicationRecord end def set_last_comment_at - self.last_comment_at = Time.now if published && last_comment_at < 30.days.ago + self.last_comment_at = published_at.present? if published_at.present? && last_comment_at.blank? end def title_to_slug diff --git a/app/models/organization.rb b/app/models/organization.rb index 29752b6fe..0820dd7c0 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -14,6 +14,8 @@ class Organization < ApplicationRecord length: { maximum: 50 } validates :summary, length: { maximum: 250 } + validates :tag_line, + length: { maximum: 60 } validates :jobs_email, email: true, allow_blank: true validates :text_color_hex, format: /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/ validates :bg_color_hex, format: /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/ diff --git a/app/views/additional_content_boxes/_article_box.html.erb b/app/views/additional_content_boxes/_article_box.html.erb new file mode 100644 index 000000000..f5476a5d3 --- /dev/null +++ b/app/views/additional_content_boxes/_article_box.html.erb @@ -0,0 +1,13 @@ +
+ + <%= article.description %> + +
Facebook & LinkedIn
- - <% if article.facebook_last_buffered %> - - <% else %> - + <%= form_tag "/internal/buffer_updates" do %> + + +Facebook & LinkedIn
+ + <% if article.facebook_last_buffered %> + + <% else %> + + <% end %> <% end %> - <% end %> -