Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow publishing entries with noindex robots meta tag #2032

Merged
merged 9 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading