Skip to content

Commit 2091241

Browse files
authored
IBX-9266: Allowed the selection of site access when creating a RTE link (#267)
1 parent f8659f1 commit 2091241

File tree

17 files changed

+207
-38
lines changed

17 files changed

+207
-38
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2580,30 +2580,12 @@ parameters:
25802580
count: 1
25812581
path: tests/lib/RichText/Converter/LinkTest.php
25822582

2583-
-
2584-
message: '#^Parameter \#1 \$locationService of class Ibexa\\FieldTypeRichText\\RichText\\Converter\\Link constructor expects Ibexa\\Contracts\\Core\\Repository\\LocationService, PHPUnit\\Framework\\MockObject\\MockObject given\.$#'
2585-
identifier: argument.type
2586-
count: 5
2587-
path: tests/lib/RichText/Converter/LinkTest.php
2588-
2589-
-
2590-
message: '#^Parameter \#2 \$contentService of class Ibexa\\FieldTypeRichText\\RichText\\Converter\\Link constructor expects Ibexa\\Contracts\\Core\\Repository\\ContentService, PHPUnit\\Framework\\MockObject\\MockObject given\.$#'
2591-
identifier: argument.type
2592-
count: 5
2593-
path: tests/lib/RichText/Converter/LinkTest.php
2594-
25952583
-
25962584
message: '#^Parameter \#2 \$function of class Ibexa\\Core\\Base\\Exceptions\\UnauthorizedException constructor expects string, int given\.$#'
25972585
identifier: argument.type
25982586
count: 4
25992587
path: tests/lib/RichText/Converter/LinkTest.php
26002588

2601-
-
2602-
message: '#^Parameter \#3 \$router of class Ibexa\\FieldTypeRichText\\RichText\\Converter\\Link constructor expects Symfony\\Component\\Routing\\RouterInterface, PHPUnit\\Framework\\MockObject\\MockObject given\.$#'
2603-
identifier: argument.type
2604-
count: 5
2605-
path: tests/lib/RichText/Converter/LinkTest.php
2606-
26072589
-
26082590
message: '#^Call to method expects\(\) on an unknown class Ibexa\\FieldTypeRichText\\RichText\\RendererInterface\.$#'
26092591
identifier: class.notFound

src/bundle/Resources/public/js/CKEditor/embed/image/embed-image-editing.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class IbexaEmbedImageEditing extends Plugin {
7777
defineSchema() {
7878
const { schema } = this.editor.model;
7979
const customClassesConfig = getCustomClassesConfig();
80-
const allowedAttributes = ['contentId', 'size', 'ibexaLinkHref', 'ibexaLinkTitle', 'ibexaLinkTarget'];
80+
const allowedAttributes = ['contentId', 'size', 'ibexaLinkHref', 'ibexaLinkTitle', 'ibexaLinkTarget', 'ibexaLinkSiteaccess'];
8181

8282
if (customClassesConfig.link) {
8383
allowedAttributes.push('ibexaLinkClasses');

src/bundle/Resources/public/js/CKEditor/link/link-command.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class IbexaLinkCommand extends Command {
3535
writer.setAttribute('ibexaLinkHref', linkData.href, element);
3636
writer.setAttribute('ibexaLinkTitle', linkData.title, element);
3737
writer.setAttribute('ibexaLinkTarget', linkData.target, element);
38+
writer.setAttribute('ibexaLinkSiteaccess', linkData.siteaccess, element);
3839
writer.setAttribute('ibexaLinkClasses', linkData.ibexaLinkClasses, element);
3940

4041
if (linkData.ibexaLinkAttributes) {

src/bundle/Resources/public/js/CKEditor/link/link-editing.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ class IbexaCustomTagEditing extends Plugin {
4141
view: (target, { writer: downcastWriter }) => downcastWriter.createAttributeElement('a', { target }),
4242
});
4343

44+
conversion.for('editingDowncast').attributeToElement({
45+
model: 'ibexaLinkSiteaccess',
46+
view: (siteaccess, { writer: downcastWriter }) => downcastWriter.createAttributeElement('a', { siteaccess }),
47+
});
48+
49+
conversion.for('dataDowncast').attributeToElement({
50+
model: 'ibexaLinkSiteaccess',
51+
view: (siteaccess, { writer: downcastWriter }) => downcastWriter.createAttributeElement('a', { siteaccess }),
52+
});
53+
4454
if (customClassesLinkConfig) {
4555
conversion.for('editingDowncast').attributeToElement({
4656
model: 'ibexaLinkClasses',
@@ -77,13 +87,16 @@ class IbexaCustomTagEditing extends Plugin {
7787
const ibexaLinkHref = data.viewItem.getAttribute('href');
7888
const ibexaLinkTitle = data.viewItem.getAttribute('title');
7989
const ibexaLinkTarget = data.viewItem.getAttribute('target');
90+
const ibexaLinkSiteaccess = data.viewItem.getAttribute('siteaccess');
91+
8092
const classes = data.viewItem.getAttribute('class');
8193

8294
conversionApi.writer.setAttributes(
8395
{
8496
ibexaLinkHref,
8597
ibexaLinkTitle,
8698
ibexaLinkTarget,
99+
ibexaLinkSiteaccess,
87100
},
88101
data.modelRange,
89102
);
@@ -115,6 +128,7 @@ class IbexaCustomTagEditing extends Plugin {
115128
this.editor.model.schema.extend('$text', { allowAttributes: 'ibexaLinkHref' });
116129
this.editor.model.schema.extend('$text', { allowAttributes: 'ibexaLinkTitle' });
117130
this.editor.model.schema.extend('$text', { allowAttributes: 'ibexaLinkTarget' });
131+
this.editor.model.schema.extend('$text', { allowAttributes: 'ibexaLinkSiteaccess' });
118132

119133
if (customAttributesLinkConfig) {
120134
const attributes = Object.keys(customAttributesLinkConfig);

src/bundle/Resources/public/js/CKEditor/link/link-ui.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class IbexaLinkUI extends Plugin {
3636
const formView = new IbexaLinkFormView({ locale: this.editor.locale, editor: this.editor });
3737

3838
this.listenTo(formView, 'save-link', () => {
39-
const { url, title, target, ibexaLinkClasses, ibexaLinkAttributes } = this.formView.getValues();
39+
const { url, title, target, ibexaLinkClasses, ibexaLinkAttributes, siteaccess } = this.formView.getValues();
4040
const { path: firstPosition } = this.editor.model.document.selection.getFirstPosition();
4141
const { path: lastPosition } = this.editor.model.document.selection.getLastPosition();
4242
const noRangeSelection = firstPosition[0] === lastPosition[0] && firstPosition[1] === lastPosition[1];
@@ -59,7 +59,7 @@ class IbexaLinkUI extends Plugin {
5959

6060
this.isNew = false;
6161

62-
this.editor.execute('insertIbexaLink', { href: url, title, target, ibexaLinkClasses, ibexaLinkAttributes });
62+
this.editor.execute('insertIbexaLink', { href: url, title, target, siteaccess, ibexaLinkClasses, ibexaLinkAttributes });
6363
this.hideForm();
6464
});
6565

@@ -78,6 +78,7 @@ class IbexaLinkUI extends Plugin {
7878
writer.removeAttribute('ibexaLinkHref', element);
7979
writer.removeAttribute('ibexaLinkTitle', element);
8080
writer.removeAttribute('ibexaLinkTarget', element);
81+
writer.removeAttribute('ibexaLinkSiteaccess', element);
8182

8283
if (customClassesLinkConfig) {
8384
writer.removeAttribute('ibexaLinkClasses', element);
@@ -121,6 +122,7 @@ class IbexaLinkUI extends Plugin {
121122
url: link?.getAttribute('href') ?? '',
122123
title: link?.getAttribute('title') ?? '',
123124
target: link?.getAttribute('target') ?? '',
125+
siteaccess: link?.getAttribute('siteaccess') ?? '',
124126
};
125127

126128
if (customClassesLinkConfig) {
@@ -172,7 +174,7 @@ class IbexaLinkUI extends Plugin {
172174

173175
if (!link) {
174176
this.editor.focus();
175-
this.editor.execute('insertIbexaLink', { href: '', title: '', target: '' });
177+
this.editor.execute('insertIbexaLink', { href: '', title: '', target: '', siteaccess: '' });
176178

177179
this.isNew = true;
178180
}

src/bundle/Resources/public/js/CKEditor/link/ui/link-form-view.js

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { createLabeledInputNumber } from '../../common/input-number/utils';
1313
import { addMultivalueSupport } from '../../common/multivalue-dropdown/utils';
1414
import { getCustomAttributesConfig, getCustomClassesConfig } from '../../custom-attributes/helpers/config-helper';
1515

16+
const { ibexa } = window;
17+
1618
class IbexaLinkFormView extends View {
1719
constructor(props) {
1820
super(props);
@@ -27,6 +29,7 @@ class IbexaLinkFormView extends View {
2729
this.urlInputView = this.createTextInput({ label: 'Link to' });
2830
this.titleView = this.createTextInput({ label: 'Title' });
2931
this.targetSwitcherView = this.createBoolean({ label: 'Open in tab' });
32+
this.siteAccessView = this.createDropdown({ label: 'Site access', choices: [] });
3033
this.attributeRenderMethods = {
3134
string: this.createTextInput,
3235
number: this.createNumberInput,
@@ -128,9 +131,10 @@ class IbexaLinkFormView extends View {
128131
this.listenTo(this.selectContentButtonView, 'execute', this.chooseContent);
129132
}
130133

131-
setValues({ url, title, target, ibexaLinkClasses, ibexaLinkAttributes = {} }) {
134+
setValues({ url, title, target, siteaccess, ibexaLinkClasses, ibexaLinkAttributes = {} }) {
132135
this.setStringValue(this.urlInputView, url);
133136
this.setStringValue(this.titleView, title);
137+
this.setChoiceValue(this.siteAccessView, siteaccess);
134138

135139
this.targetSwitcherView.fieldView.element.value = !!target;
136140
this.targetSwitcherView.fieldView.set('value', !!target);
@@ -141,6 +145,12 @@ class IbexaLinkFormView extends View {
141145
this.setChoiceValue(this.classesView, ibexaLinkClasses);
142146
}
143147

148+
if (url.includes('ezlocation://')) {
149+
const locationId = url.replace('ezlocation://', '');
150+
151+
this.fetchSiteaccesses(locationId);
152+
}
153+
144154
Object.entries(ibexaLinkAttributes).forEach(([name, value]) => {
145155
const attributeView = this.attributeViews[`ibexaLink${name}`];
146156
const setValueMethod = this.setValueMethods[this.customAttributes[name].type];
@@ -187,6 +197,7 @@ class IbexaLinkFormView extends View {
187197
url,
188198
title: this.titleView.fieldView.element.value,
189199
target: this.targetSwitcherView.fieldView.isOn ? '_blank' : '',
200+
siteaccess: this.siteAccessView.fieldView.element.value,
190201
};
191202
const customClassesValue = this.classesView?.fieldView.element.value;
192203
const customAttributesValue = Object.entries(this.attributeViews).reduce((output, [name, view]) => {
@@ -264,19 +275,17 @@ class IbexaLinkFormView extends View {
264275

265276
children.add(this.selectContentButtonView);
266277
children.add(this.urlInputView);
278+
children.add(this.siteAccessView);
267279
children.add(this.titleView);
268280
children.add(this.targetSwitcherView);
269281

270282
return children;
271283
}
272284

273-
createDropdown(config, isCustomAttribute = false) {
285+
createDropdownItemsList(config) {
274286
const Translator = getTranslator();
275-
const labeledDropdown = new LabeledFieldView(this.locale, createLabeledDropdown);
276287
const itemsList = new Collection();
277288

278-
labeledDropdown.label = config.label;
279-
280289
if (!config.multiple && !config.required) {
281290
itemsList.add({
282291
type: 'button',
@@ -299,6 +308,15 @@ class IbexaLinkFormView extends View {
299308
});
300309
});
301310

311+
return itemsList;
312+
}
313+
314+
createDropdown(config, isCustomAttribute = false) {
315+
const labeledDropdown = new LabeledFieldView(this.locale, createLabeledDropdown);
316+
const itemsList = this.createDropdownItemsList(config);
317+
318+
labeledDropdown.label = config.label;
319+
302320
addListToDropdown(labeledDropdown.fieldView, itemsList);
303321

304322
if (config.multiple) {
@@ -463,12 +481,42 @@ class IbexaLinkFormView extends View {
463481
this.urlInputView.fieldView.set('value', url);
464482
this.urlInputView.fieldView.set('isEmpty', !url);
465483

484+
this.fetchSiteaccesses(items[0].id);
485+
466486
this.editor.focus();
467487
}
468488

469489
cancelHandler() {
470490
this.editor.focus();
471491
}
492+
493+
fetchSiteaccesses(locationId) {
494+
const request = new Request(`/api/ibexa/v2/site-access/by-location/${locationId}?resolver_type=non_admin`, {
495+
method: 'GET',
496+
headers: {
497+
Accept: 'application/json',
498+
},
499+
mode: 'same-origin',
500+
credentials: 'same-origin',
501+
});
502+
503+
fetch(request)
504+
.then(ibexa.helpers.request.getJsonFromResponse)
505+
.then((response) => {
506+
const itemsList = this.createDropdownItemsList({
507+
choices: response.SiteAccessesList.values.map((siteaccess) => siteaccess.name),
508+
});
509+
510+
this.siteAccessView.fieldView.once(
511+
'change:isOpen',
512+
() => {
513+
this.siteAccessView.fieldView.panelView.children.clear();
514+
addListToDropdown(this.siteAccessView.fieldView, itemsList);
515+
},
516+
{ priority: 'highest' },
517+
);
518+
});
519+
}
472520
}
473521

474522
export default IbexaLinkFormView;

src/bundle/Resources/public/scss/_balloon-form.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
border: calculateRem(1px) solid $ibexa-color-dark-200;
2929
border-radius: calculateRem(5px);
3030
width: 100%;
31-
max-width: calculateRem(288px);
3231

3332
.ibexa-ckeditor-dropdown-selected-items {
3433
display: flex;

src/bundle/Resources/richtext/schemas/docbook/docbook.rng

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,13 @@
252252
<ref name="db.xlink.show.enumeration"/>
253253
</attribute>
254254
</define>
255+
<define name="db.xlink.siteaccess.attribute">
256+
<optional>
257+
<attribute name="xlink:siteaccess">
258+
<a:documentation>Specifies the siteaccess for the location</a:documentation>
259+
</attribute>
260+
</optional>
261+
</define>
255262
<define name="db.xlink.actuate.enumeration">
256263
<choice>
257264
<value>onLoad</value>
@@ -291,6 +298,9 @@
291298
<optional>
292299
<ref name="db.xlink.actuate.attribute"/>
293300
</optional>
301+
<optional>
302+
<ref name="db.xlink.siteaccess.attribute"/>
303+
</optional>
294304
</interleave>
295305
</define>
296306
<define name="db.xml.id.attribute">
@@ -6878,6 +6888,9 @@ O, the molecular formula for water)</a:documentation>
68786888
<optional>
68796889
<ref name="db.xlink.to.attribute"/>
68806890
</optional>
6891+
<optional>
6892+
<ref name="db.xlink.siteaccess.attribute"/>
6893+
</optional>
68816894
</interleave>
68826895
</define>
68836896
<define name="db.arc">

src/bundle/Resources/richtext/stylesheets/docbook/xhtml5/edit/core.xsl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@
253253
<xsl:value-of select="@xlink:title"/>
254254
</xsl:attribute>
255255
</xsl:if>
256+
<xsl:if test="@xlink:siteaccess">
257+
<xsl:attribute name="siteaccess">
258+
<xsl:value-of select="@xlink:siteaccess"/>
259+
</xsl:attribute>
260+
</xsl:if>
256261
<xsl:if test="@ezxhtml:class">
257262
<xsl:attribute name="class">
258263
<xsl:value-of select="@ezxhtml:class"/>
@@ -660,6 +665,11 @@
660665
<xsl:value-of select="@xlink:title"/>
661666
</xsl:attribute>
662667
</xsl:if>
668+
<xsl:if test="@xlink:siteaccess">
669+
<xsl:attribute name="siteaccess">
670+
<xsl:value-of select="@xlink:siteaccess"/>
671+
</xsl:attribute>
672+
</xsl:if>
663673
<xsl:if test="@ezxhtml:class">
664674
<xsl:attribute name="class">
665675
<xsl:value-of select="@ezxhtml:class"/>

src/bundle/Resources/richtext/stylesheets/docbook/xhtml5/output/core.xsl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@
255255
<xsl:value-of select="@xlink:title"/>
256256
</xsl:attribute>
257257
</xsl:if>
258+
<xsl:if test="not(normalize-space(@xlink:siteaccess) = '')">
259+
<xsl:attribute name="siteaccess">
260+
<xsl:value-of select="@xlink:siteaccess"/>
261+
</xsl:attribute>
262+
</xsl:if>
258263
<xsl:if test="@ezxhtml:class">
259264
<xsl:attribute name="class">
260265
<xsl:value-of select="@ezxhtml:class"/>

0 commit comments

Comments
 (0)