diff options
author | Douwe Maan <douwe@gitlab.com> | 2015-12-25 13:41:55 +0000 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2015-12-25 13:41:55 +0000 |
commit | 7d5b51f3877eb209e1215c1154a976bb079b930d (patch) | |
tree | 035de55b0e3396315614bdcb65c2d63874203ae1 | |
parent | 9dc1a53bb29c902d7ffba99d5226312a4fe28815 (diff) | |
parent | 6d385e3365ffa68a3e4ddb7bf332522cceaccaff (diff) | |
download | gitlab-ce-7d5b51f3877eb209e1215c1154a976bb079b930d.tar.gz |
Merge branch 'rs-opengraph' into 'master'
Add Open Graph meta tags
See merge request !2192
-rw-r--r-- | app/assets/images/brand_logo.png | bin | 27059 -> 0 bytes | |||
-rw-r--r-- | app/assets/images/gitlab_logo.png | bin | 0 -> 5189 bytes | |||
-rw-r--r-- | app/helpers/page_layout_helper.rb | 74 | ||||
-rw-r--r-- | app/models/concerns/issuable.rb | 8 | ||||
-rw-r--r-- | app/views/layouts/_head.html.haml | 22 | ||||
-rw-r--r-- | app/views/projects/issues/show.html.haml | 5 | ||||
-rw-r--r-- | app/views/projects/merge_requests/_show.html.haml | 5 | ||||
-rw-r--r-- | app/views/projects/milestones/show.html.haml | 4 | ||||
-rw-r--r-- | app/views/users/show.html.haml | 5 | ||||
-rw-r--r-- | spec/helpers/page_layout_helper_spec.rb | 129 | ||||
-rw-r--r-- | spec/models/concerns/issuable_spec.rb | 18 |
11 files changed, 262 insertions, 8 deletions
diff --git a/app/assets/images/brand_logo.png b/app/assets/images/brand_logo.png Binary files differdeleted file mode 100644 index 9c564bb6141..00000000000 --- a/app/assets/images/brand_logo.png +++ /dev/null diff --git a/app/assets/images/gitlab_logo.png b/app/assets/images/gitlab_logo.png Binary files differnew file mode 100644 index 00000000000..0c157546b9c --- /dev/null +++ b/app/assets/images/gitlab_logo.png diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 9bf750124b2..791cb9e50bd 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -8,6 +8,80 @@ module PageLayoutHelper @page_title.join(" \u00b7 ") end + # Define or get a description for the current page + # + # description - String (default: nil) + # + # If this helper is called multiple times with an argument, only the last + # description will be returned when called without an argument. Descriptions + # have newlines replaced with spaces and all HTML tags are sanitized. + # + # Examples: + # + # page_description # => "GitLab Community Edition" + # page_description("Foo") + # page_description # => "Foo" + # + # page_description("<b>Bar</b>\nBaz") + # page_description # => "Bar Baz" + # + # Returns an HTML-safe String. + def page_description(description = nil) + @page_description ||= page_description_default + + if description.present? + @page_description = description.squish + else + sanitize(@page_description, tags: []).truncate_words(30) + end + end + + # Default value for page_description when one hasn't been defined manually by + # a view + def page_description_default + if @project + @project.description || brand_title + else + brand_title + end + end + + def page_image + default = image_url('gitlab_logo.png') + + if @project + @project.avatar_url || default + elsif @user + avatar_icon(@user) + else + default + end + end + + # Define or get attributes to be used as Twitter card metadata + # + # map - Hash of label => data pairs. Keys become labels, values become data + # + # Raises ArgumentError if given more than two attributes + def page_card_attributes(map = {}) + raise ArgumentError, 'cannot provide more than two attributes' if map.length > 2 + + @page_card_attributes ||= {} + @page_card_attributes = map.reject { |_,v| v.blank? } if map.present? + @page_card_attributes + end + + def page_card_meta_tags + tags = '' + + page_card_attributes.each_with_index do |pair, i| + tags << tag(:meta, property: "twitter:label#{i + 1}", content: pair[0]) + tags << tag(:meta, property: "twitter:data#{i + 1}", content: pair[1]) + end + + tags.html_safe + end + def header_title(title = nil, title_url = nil) if title @header_title = title diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index f56fd3e02d4..919833f6df5 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -161,6 +161,14 @@ module Issuable self.class.to_s.underscore end + # Returns a Hash of attributes to be used for Twitter card metadata + def card_attributes + { + 'Author' => author.try(:name), + 'Assignee' => assignee.try(:name) + } + end + def notes_with_associations notes.includes(:author, :project) end diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 74174a72f5a..2e0bd2007a3 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -1,10 +1,26 @@ -- page_title "GitLab" -%head +%head{prefix: "og: http://ogp.me/ns#"} %meta{charset: "utf-8"} %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'} - %meta{content: "GitLab Community Edition", name: "description"} %meta{name: 'referrer', content: 'origin-when-cross-origin'} + %meta{name: "description", content: page_description} + + -# Open Graph - http://ogp.me/ + %meta{property: 'og:type', content: "object"} + %meta{property: 'og:site_name', content: "GitLab"} + %meta{property: 'og:title', content: page_title} + %meta{property: 'og:description', content: page_description} + %meta{property: 'og:image', content: page_image} + %meta{property: 'og:url', content: request.base_url + request.fullpath} + + -# Twitter Card - https://dev.twitter.com/cards/types/summary + %meta{property: 'twitter:card', content: "summary"} + %meta{property: 'twitter:title', content: page_title} + %meta{property: 'twitter:description', content: page_description} + %meta{property: 'twitter:image', content: page_image} + = page_card_meta_tags + + - page_title "GitLab" %title= page_title = favicon_link_tag 'favicon.ico' diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index b6efa05a1ae..f548383008d 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,4 +1,7 @@ -- page_title "#{@issue.title} (##{@issue.iid})", "Issues" +- page_title "#{@issue.title} (##{@issue.iid})", "Issues" +- page_description @issue.description +- page_card_attributes @issue.card_attributes + = render "header_title" .issue diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index e9ffbd06be2..ba7c2c01e93 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,4 +1,7 @@ -- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" +- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" +- page_description @merge_request.description +- page_card_attributes @merge_request.card_attributes + = render "header_title" - if params[:view] == 'parallel' diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 7e73ae274e9..1670ea8741a 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,4 +1,6 @@ -- page_title @milestone.title, "Milestones" +- page_title @milestone.title, "Milestones" +- page_description @milestone.description + = render "header_title" .detail-page-header diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index b7a7eb4e6f7..0bca8177e14 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,5 +1,6 @@ -- page_title @user.name -- header_title @user.name, user_path(@user) +- page_title @user.name +- page_description @user.bio +- header_title @user.name, user_path(@user) = content_for :meta_tags do = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb new file mode 100644 index 00000000000..fd7107779f6 --- /dev/null +++ b/spec/helpers/page_layout_helper_spec.rb @@ -0,0 +1,129 @@ +require 'rails_helper' + +describe PageLayoutHelper do + describe 'page_description' do + it 'defaults to value returned by page_description_default helper' do + allow(helper).to receive(:page_description_default).and_return('Foo') + + expect(helper.page_description).to eq 'Foo' + end + + it 'returns the last-pushed description' do + helper.page_description('Foo') + helper.page_description('Bar') + helper.page_description('Baz') + + expect(helper.page_description).to eq 'Baz' + end + + it 'squishes multiple newlines' do + helper.page_description("Foo\nBar\nBaz") + + expect(helper.page_description).to eq 'Foo Bar Baz' + end + + it 'truncates' do + helper.page_description <<-LOREM.strip_heredoc + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo + ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis + dis parturient montes, nascetur ridiculus mus. Donec quam felis, + ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa + quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, + arcu. + LOREM + + expect(helper.page_description).to end_with 'quam felis,...' + end + + it 'sanitizes all HTML' do + helper.page_description("<b>Bold</b> <h1>Header</h1>") + + expect(helper.page_description).to eq 'Bold Header' + end + end + + describe 'page_description_default' do + it 'uses Project description when available' do + project = double(description: 'Project Description') + helper.instance_variable_set(:@project, project) + + expect(helper.page_description_default).to eq 'Project Description' + end + + it 'uses brand_title when Project description is nil' do + project = double(description: nil) + helper.instance_variable_set(:@project, project) + + expect(helper).to receive(:brand_title).and_return('Brand Title') + expect(helper.page_description_default).to eq 'Brand Title' + end + + it 'falls back to brand_title' do + allow(helper).to receive(:brand_title).and_return('Brand Title') + + expect(helper.page_description_default).to eq 'Brand Title' + end + end + + describe 'page_image' do + it 'defaults to the GitLab logo' do + expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + end + + context 'with @project' do + it 'uses Project avatar if available' do + project = double(avatar_url: 'http://example.com/uploads/avatar.png') + helper.instance_variable_set(:@project, project) + + expect(helper.page_image).to eq project.avatar_url + end + + it 'falls back to the default' do + project = double(avatar_url: nil) + helper.instance_variable_set(:@project, project) + + expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + end + end + + context 'with @user' do + it 'delegates to avatar_icon helper' do + user = double('User') + helper.instance_variable_set(:@user, user) + + expect(helper).to receive(:avatar_icon).with(user) + + helper.page_image + end + end + end + + describe 'page_card_attributes' do + it 'raises ArgumentError when given more than two attributes' do + map = { foo: 'foo', bar: 'bar', baz: 'baz' } + + expect { helper.page_card_attributes(map) }. + to raise_error(ArgumentError, /more than two attributes/) + end + + it 'rejects blank values' do + map = { foo: 'foo', bar: '' } + helper.page_card_attributes(map) + + expect(helper.page_card_attributes).to eq({ foo: 'foo' }) + end + end + + describe 'page_card_meta_tags' do + it 'returns the twitter:label and twitter:data tags' do + allow(helper).to receive(:page_card_attributes).and_return(foo: 'bar') + + tags = helper.page_card_meta_tags + + aggregate_failures do + expect(tags).to include %q(<meta property="twitter:label1" content="foo" />) + expect(tags).to include %q(<meta property="twitter:data1" content="bar" />) + end + end + end +end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 0f13c4410cd..f9d3c56750f 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -81,4 +81,22 @@ describe Issue, "Issuable" do expect(hook_data[:object_attributes]).to eq(issue.hook_attrs) end end + + describe '#card_attributes' do + it 'includes the author name' do + allow(issue).to receive(:author).and_return(double(name: 'Robert')) + allow(issue).to receive(:assignee).and_return(nil) + + expect(issue.card_attributes). + to eq({ 'Author' => 'Robert', 'Assignee' => nil }) + end + + it 'includes the assignee name' do + allow(issue).to receive(:author).and_return(double(name: 'Robert')) + allow(issue).to receive(:assignee).and_return(double(name: 'Douwe')) + + expect(issue.card_attributes). + to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' }) + end + end end |