Skip to content

Commit

Permalink
Merge pull request #2032 from tf/noindex
Browse files Browse the repository at this point in the history
Allow publishing entries with noindex robots meta tag
  • Loading branch information
tf authored Nov 29, 2023
2 parents 8c4c780 + 3ccd302 commit afc4c6d
Show file tree
Hide file tree
Showing 29 changed files with 369 additions and 7 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions app/assets/stylesheets/pageflow/admin/entries.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ $pageflow-published-revision-background-color: #beebb8 !default;
.publication_state_indicator {
height: 17px;
}

.tooltip_clue {
display: inline-block;
}
}

.legend {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ $pageflow-publication-state-indicator-size: 25px !default;
&.published_with_password_protection {
background-image: image-url("#{$dir}/published_with_password.svg");
}

&.published_with_noindex {
background-image: image-url("#{$dir}/published_with_noindex.svg");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ def build_entry_publication(entry)
end

def entry_publication_params
params.fetch(:entry_publication, {}).permit(:published_until, :password, :password_protected)
params
.fetch(:entry_publication, {})
.permit(:published_until,
:password, :password_protected,
:noindex)
end

def published_entries_quota(entry)
Expand Down
3 changes: 2 additions & 1 deletion app/helpers/pageflow/meta_tags_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ def meta_tags_data_for_entry(entry)
{
keywords: entry.keywords,
author: entry.author,
publisher: entry.publisher
publisher: entry.publisher,
noindex: entry.noindex?
}
end
end
Expand Down
9 changes: 9 additions & 0 deletions app/models/concerns/pageflow/entry_publication_states.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ module EntryPublicationStates
scope(:published_with_password_protection,
-> { published.merge(Revision.with_password_protection) })

scope(:published_without_noindex,
-> { published.merge(Revision.without_noindex) })

scope(:not_published,
lambda do
includes(:published_revision)
Expand All @@ -22,6 +25,8 @@ module EntryPublicationStates
def publication_state
if published_with_password_protection?
'published_with_password_protection'
elsif published? && published_revision.noindex?
'published_with_noindex'
elsif published?
'published_without_password_protection'
else
Expand All @@ -45,6 +50,10 @@ def published_until
published? ? published_revision.published_until : nil
end

def last_published_with_noindex?
!!revisions.publications.first&.noindex
end

module ClassMethods
def with_publication_state(state)
case state
Expand Down
2 changes: 2 additions & 0 deletions app/models/pageflow/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def publish(options = {})
revision.published_at = Time.now
revision.published_until = options[:published_until]
revision.password_protected = options[:password_protected]
revision.noindex = !!options[:noindex]
end
end
end
Expand All @@ -111,6 +112,7 @@ def restore(options)
revision.published_at = nil
revision.published_until = nil
revision.password_protected = nil
revision.noindex = nil
end
end

Expand Down
2 changes: 2 additions & 0 deletions app/models/pageflow/entry_at_revision.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def initialize(entry, revision, theme: nil)
:password_digest,
:to_model, :to_key, :to_param, :persisted?, :to_json,
:first_published_at, :published_until, :published?,
:last_published_with_noindex?,
:type_name,
to: :entry)

Expand All @@ -35,6 +36,7 @@ def initialize(entry, revision, theme: nil)
:locale,
:author, :publisher, :keywords,
:published_at,
:noindex?,
:configuration,
to: :revision)

Expand Down
2 changes: 2 additions & 0 deletions app/models/pageflow/revision.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class Revision < ApplicationRecord
scope(:with_password_protection, -> { where('password_protected IS TRUE') })
scope(:without_password_protection, -> { where('password_protected IS NOT TRUE') })

scope(:without_noindex, -> { where('noindex IS NOT TRUE') })

scope :editable, -> { where('frozen_at IS NULL') }
scope :frozen, -> { where('frozen_at IS NOT NULL') }

Expand Down
1 change: 1 addition & 0 deletions app/models/pageflow/sitemaps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def self.entries_for(site:)
site
.entries
.published_without_password_protection
.published_without_noindex
.order('first_published_at DESC')
)
end
Expand Down
8 changes: 8 additions & 0 deletions app/views/components/pageflow/admin/revisions_tab.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ def build(entry)
end
end

if revision.noindex?
span(class: 'publication_state_indicator published_with_noindex') do
span(class: 'tooltip_bubble') do
t('pageflow.admin.entries.noindex')
end
end
end

if revision.password_protected?
span(class: 'publication_state_indicator published_with_password_protection') do
span(class: 'tooltip_bubble') do
Expand Down
1 change: 1 addition & 0 deletions app/views/pageflow/editor/entries/_entry.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ json.default_file_rights entry.account.default_file_rights
json.published(entry.published?)
json.publishable(can?(:publish, entry.to_model))
json.password_protected(entry.password_digest.present?)
json.last_published_with_noindex(entry.last_published_with_noindex?)

json.metadata do
json.(entry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ json.entry do
json.(@entry_publication.entry, :published_until)
json.published(@entry_publication.entry.published?)
json.password_protected(@entry_publication.entry.password_digest.present?)
json.last_published_with_noindex(@entry_publication.entry.last_published_with_noindex?)
end
json.exhausted_html(render_html_partial('pageflow/editor/quotas/published_entries_exhausted',
entry: @entry_publication.entry,
Expand Down
1 change: 1 addition & 0 deletions app/views/pageflow/meta_tags/_entry.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
<% if keywords.present? %><meta name="keywords" content="<%= keywords %>"><% end %>
<% if author.present? %><meta name="author" content="<%= author %>"><% end %>
<% if publisher.present? %><meta name="publisher" content="<%= publisher %>"><% end %>
<% if noindex %><meta name="robots" content="noindex"><% end %>
18 changes: 18 additions & 0 deletions config/locales/new/noindex.de.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
de:
pageflow:
editor:
templates:
publish_entry:
noindex: Suchmaschinen ausschließen
noindex_help: |
Meta Tag setzen, dass Crawler von Suchmaschinen wie Google
oder Bing anweist, die URL des veröffentlichten Beitrags
nicht in den Index aufzunehmen.
admin:
entries:
noindex: Aus Suchmaschinen ausschließen
activerecord:
values:
pageflow/entry:
publication_states:
published_with_noindex: Veröffentlicht aber aus Suchmaschinen ausgeschlossen
18 changes: 18 additions & 0 deletions config/locales/new/noindex.en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
en:
pageflow:
editor:
templates:
publish_entry:
noindex: Exclude from search engines
noindex_help: |
Set a meta tag that instructs crawlers of search engines
like Google or Bing to not include the published entry in
the index.
admin:
entries:
noindex: Exclude from search engines
activerecord:
values:
pageflow/entry:
publication_states:
published_with_noindex: Published but excluded from search engines
5 changes: 5 additions & 0 deletions db/migrate/20231128124523_add_noindex_to_revisions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddNoindexToRevisions < ActiveRecord::Migration[5.2]
def change
add_column :pageflow_revisions, :noindex, :boolean
end
end
10 changes: 10 additions & 0 deletions package/spec/editor/models/Entry-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ describe('Entry', () => {

expect(entry.getFileCollection(testContext.imageFileType).first().get('state')).toBe('processed');
});

it('updates last_published_with_noindex attribute', () => {
const entry = support.factories.entry();

entry.parse({
last_published_with_noindex: true
});

expect(entry.get('last_published_with_noindex')).toEqual(true);
})
});

describe('file collection count attribute', () => {
Expand Down
76 changes: 75 additions & 1 deletion package/spec/editor/views/PublishEntryView-spec.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import {PublishEntryView} from 'pageflow/editor';

import Backbone from 'backbone';
import $ from 'jquery';

import {useFakeTranslations} from 'pageflow/testHelpers';
import {within} from '@testing-library/dom';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';

describe('PublishEntryView', () => {
useFakeTranslations({
'pageflow.editor.templates.publish_entry.date': 'Published until date'
'pageflow.editor.templates.publish_entry.unlimited': 'Unlimited',
'pageflow.editor.templates.publish_entry.date': 'Published until date',
'pageflow.editor.templates.publish_entry.noindex': 'Set noindex',
'pageflow.editor.templates.publish_entry.noindex_help': '',
'pageflow.editor.templates.publish_entry.publish': 'Publish'
});

afterEach(() => {
jest.useRealTimers();
});

it('sets published until date based on passed duration if not set yet', () => {
Expand Down Expand Up @@ -68,4 +78,68 @@ describe('PublishEntryView', () => {

expect(getByLabelText('Published until date')).toHaveValue('31.05.2022');
});

it('checks noindex if last published with noindex', async () => {
const entryPublication = {
publish: jest.fn()
};
entryPublication.publish.mockReturnValue(new $.Deferred().promise());
const view = new PublishEntryView({
model: new Backbone.Model({
last_published_with_noindex: true
}),
entryPublication,
account: new Backbone.Model(),
config: {}
});

const {getByLabelText} = within(view.render().el);

expect(getByLabelText('Set noindex')).toBeChecked();
});

it('does not pass noindex flag by default', async () => {
const entryPublication = {
publish: jest.fn()
};
entryPublication.publish.mockReturnValue(new $.Deferred().promise());
const view = new PublishEntryView({
model: new Backbone.Model(),
entryPublication,
account: new Backbone.Model(),
config: {}
});

const user = userEvent.setup();
const {getByRole, getByLabelText} = within(view.render().el);
await user.click(getByLabelText('Unlimited'));
await user.click(getByRole('button', {name: 'Publish'}));

expect(entryPublication.publish).toHaveBeenCalledWith(expect.objectContaining({
noindex: false
}));
});

it('passes noindex flag when check box checked', async () => {
const entryPublication = {
publish: jest.fn()
};
entryPublication.publish.mockReturnValue(new $.Deferred().promise());
const view = new PublishEntryView({
model: new Backbone.Model(),
entryPublication,
account: new Backbone.Model(),
config: {}
});

const user = userEvent.setup();
const {getByRole, getByLabelText} = within(view.render().el);
await user.click(getByLabelText('Unlimited'));
await user.click(getByLabelText('Set noindex'));
await user.click(getByRole('button', {name: 'Publish'}));

expect(entryPublication.publish).toHaveBeenCalledWith(expect.objectContaining({
noindex: true
}));
});
});
4 changes: 3 additions & 1 deletion package/src/editor/models/Entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ export const Entry = Backbone.Model.extend({

parse: function(response, options) {
if (response) {
this.set(_.pick(response, 'published', 'published_until', 'password_protected'));
this.set(_.pick(response,
'published', 'published_until',
'password_protected', 'last_published_with_noindex'));
this._setFiles(response, {
add: false,
remove: false,
Expand Down
12 changes: 12 additions & 0 deletions package/src/editor/templates/publishEntry.jst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
</label>
</div>

<div class="check_box_input">
<input id="publish_with_noindex" type="checkbox" name="noindex" value="1">
<label for="publish_with_noindex">
<span class="name">
<%= I18n.t('pageflow.editor.templates.publish_entry.noindex') %>
</span>
<span class="inline_help">
<%= I18n.t('pageflow.editor.templates.publish_entry.noindex_help') %>
</span>
</label>
</div>

<div class="check_box_input">
<input id="publish_password_protected" type="checkbox" name="password_protected" value="1">
<label for="publish_password_protected">
Expand Down
7 changes: 5 additions & 2 deletions package/src/editor/views/PublishEntryView.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const PublishEntryView = Marionette.ItemView.extend({
passwordFields: '.password_fields',
userNameField: 'input[name=user_name]',
passwordField: 'input[name=password]',
noindexCheckBox: 'input[name=noindex]',
alreadyPublishedWithPassword: '.already_published_with_password',
previouslyPublishedWithPassword: '.previously_published_with_password',
alreadyPublishedWithoutPassword: '.already_published_without_password',
Expand Down Expand Up @@ -99,6 +100,8 @@ export const PublishEntryView = Marionette.ItemView.extend({
this.ui.passwordField.val(this.randomPassword());
}

this.ui.noindexCheckBox.prop('checked', this.model.get('last_published_with_noindex'));

this.ui.alreadyPublishedWithPassword.toggle(this.model.get('published') && this.model.get('password_protected'));
this.ui.previouslyPublishedWithPassword.toggle(!this.model.get('published') && this.model.get('password_protected'));
this.ui.alreadyPublishedWithoutPassword.toggle(this.model.get('published') && !this.model.get('password_protected'));
Expand All @@ -119,7 +122,6 @@ export const PublishEntryView = Marionette.ItemView.extend({
if (this.$el.hasClass('publishing')) {
return;
}

if (this.ui.publishUntilRadioBox.is(':checked')) {
publishedUntil = this.ui.publishUntilField.datepicker('getDate');
setTime(publishedUntil, this.ui.publishUntilTimeField.val());
Expand All @@ -142,7 +144,8 @@ export const PublishEntryView = Marionette.ItemView.extend({
this.options.entryPublication.publish({
published_until: publishedUntil,
password_protected: this.ui.passwordProtectedCheckBox.is(':checked'),
password: this.ui.passwordField.val()
password: this.ui.passwordField.val(),
noindex: this.ui.noindexCheckBox.is(':checked')
})
.fail(function() {
alert('Beim Veröffentlichen ist ein Fehler aufgetreten');
Expand Down
Loading

0 comments on commit afc4c6d

Please sign in to comment.