@plone/volto 17.0.0-alpha.1 → 17.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +396 -16
- package/CONTRIBUTING.md +1 -1
- package/README.md +12 -15
- package/addon-registry.js +34 -0
- package/create-theme-addons-loader.js +79 -0
- package/cypress/support/commands.js +25 -0
- package/locales/ca/LC_MESSAGES/volto.po +187 -6
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +206 -25
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +186 -5
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +187 -6
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +187 -6
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +4792 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +187 -6
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +187 -6
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +187 -6
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +842 -649
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +187 -6
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +195 -14
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +187 -6
- package/locales/ro.json +1 -1
- package/locales/volto.pot +187 -6
- package/locales/zh_CN/LC_MESSAGES/volto.po +187 -6
- package/locales/zh_CN.json +1 -1
- package/package-why.json +0 -1
- package/package.json +9 -8
- package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
- package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
- package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
- package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
- package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
- package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
- package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
- package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
- package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
- package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/blocks/Table/index.js +2 -0
- package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
- package/packages/volto-slate/src/editor/deserialize.js +0 -1
- package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
- package/razzle.config.js +28 -0
- package/src/actions/index.js +6 -0
- package/src/actions/language/language.js +9 -8
- package/src/actions/querystringsearch/querystringsearch.js +20 -14
- package/src/actions/relations/rebuild.js +25 -0
- package/src/actions/relations/relations.js +86 -0
- package/src/actions/relations/relations.test.js +15 -0
- package/src/components/index.js +1 -0
- package/src/components/manage/Add/Add.jsx +2 -2
- package/src/components/manage/BlockChooser/BlockChooser.jsx +14 -5
- package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
- package/src/components/manage/Blocks/Listing/Edit.jsx +0 -19
- package/src/components/manage/Blocks/Listing/ListingBody.jsx +77 -61
- package/src/components/manage/Blocks/Listing/View.jsx +0 -4
- package/src/components/manage/Blocks/Listing/getAsyncData.js +10 -2
- package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -13
- package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +5 -4
- package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
- package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
- package/src/components/manage/Blocks/Search/components/Facets.jsx +58 -2
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +24 -11
- package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
- package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
- package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
- package/src/components/manage/Blocks/Search/schema.js +16 -1
- package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
- package/src/components/manage/Contents/Contents.jsx +69 -33
- package/src/components/manage/Contents/ContentsItem.jsx +6 -0
- package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +3 -3
- package/src/components/manage/Controlpanels/Controlpanels.jsx +199 -224
- package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
- package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
- package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
- package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
- package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
- package/src/components/manage/DragDropList/DragDropList.jsx +63 -42
- package/src/components/manage/Form/BlocksToolbar.jsx +5 -1
- package/src/components/manage/Form/Form.jsx +11 -5
- package/src/components/manage/Form/InlineForm.jsx +39 -9
- package/src/components/manage/Form/InlineFormState.js +8 -0
- package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
- package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
- package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
- package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
- package/src/components/manage/Toast/Toast.jsx +1 -1
- package/src/components/manage/Toolbar/Types.jsx +2 -2
- package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
- package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
- package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
- package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
- package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
- package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +7 -2
- package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
- package/src/components/manage/Widgets/SelectUtils.js +1 -1
- package/src/components/manage/Widgets/SelectWidget.jsx +1 -1
- package/src/components/theme/Footer/Footer.jsx +2 -13
- package/src/components/theme/Header/Header.jsx +37 -63
- package/src/components/theme/Header/Header.test.jsx +18 -0
- package/src/components/theme/Icon/Icon.jsx +2 -2
- package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
- package/src/components/theme/Login/Login.jsx +1 -0
- package/src/components/theme/Logo/Logo.jsx +2 -1
- package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
- package/src/components/theme/Navigation/NavItem.jsx +4 -2
- package/src/components/theme/NotFound/NotFound.jsx +55 -41
- package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -4
- package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +1 -1
- package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
- package/src/components/theme/View/DefaultView.jsx +1 -1
- package/src/components/theme/View/EventDatesInfo.jsx +2 -1
- package/src/components/theme/View/RenderBlocks.jsx +7 -1
- package/src/components/theme/Widgets/DateWidget.jsx +2 -1
- package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
- package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
- package/src/config/ControlPanels.js +2 -0
- package/src/config/Widgets.jsx +1 -0
- package/src/config/index.js +2 -0
- package/src/constants/ActionTypes.js +4 -0
- package/src/constants/Languages.js +8 -4
- package/src/express-middleware/sitemap.js +36 -4
- package/src/helpers/Api/Api.js +1 -1
- package/src/helpers/FormValidation/FormValidation.js +11 -2
- package/src/helpers/FormValidation/FormValidation.test.js +73 -0
- package/src/helpers/Html/Html.jsx +3 -1
- package/src/helpers/Html/Html.test.jsx +5 -0
- package/src/helpers/MessageLabels/MessageLabels.js +72 -0
- package/src/helpers/Robots/Robots.js +24 -6
- package/src/helpers/Sitemap/Sitemap.js +44 -2
- package/src/helpers/Url/Url.js +27 -6
- package/src/helpers/Url/Url.test.js +26 -0
- package/src/helpers/Utils/Utils.js +38 -13
- package/src/helpers/Utils/Utils.test.js +4 -4
- package/src/helpers/index.js +7 -2
- package/src/hooks/userSession/useToken.js +5 -0
- package/src/middleware/Api.test.js +54 -0
- package/src/middleware/api.js +8 -4
- package/src/reducers/actions/actions.js +1 -1
- package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
- package/src/reducers/index.js +2 -0
- package/src/reducers/navigation/navigation.js +1 -1
- package/src/reducers/relations/relations.js +173 -0
- package/src/reducers/types/types.js +1 -1
- package/src/routes.js +5 -0
- package/src/server.jsx +28 -23
- package/test-setup-config.js +1 -0
- package/theme/themes/pastanaga/extras/contents.less +1 -0
- package/theme/themes/pastanaga/extras/main.less +80 -1
- package/theme/themes/pastanaga/extras/search.less +6 -0
- package/theme/themes/pastanaga/extras/sidebar.less +4 -0
- package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
- package/.changelog.draft +0 -28
- package/.editorconfig +0 -36
- package/.storybook/main.js +0 -127
- package/.storybook/manager.js +0 -15
- package/.storybook/preview.js +0 -21
- package/.storybook/static/previewImage.svg +0 -48
- package/.yarnrc.yml +0 -5
- package/jsdoc.json +0 -16
- package/netlify.toml +0 -5
- package/pyvenv.cfg +0 -3
- package/share/man/man1/ttx.1 +0 -225
- package/src/components/theme/Header/Header.md +0 -27
- package/towncrier.toml +0 -33
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { updateIntl } from 'react-intl-redux';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
toGettextLang,
|
|
4
|
+
toReactIntlLang,
|
|
5
|
+
getCookieOptions,
|
|
6
|
+
} from '@plone/volto/helpers';
|
|
3
7
|
import Cookies from 'universal-cookie';
|
|
4
8
|
|
|
5
9
|
export function changeLanguageCookies(language, req) {
|
|
@@ -7,18 +11,15 @@ export function changeLanguageCookies(language, req) {
|
|
|
7
11
|
|
|
8
12
|
const cookieOptions = getCookieOptions({
|
|
9
13
|
secure: req?.protocol?.startsWith('https') ? true : false,
|
|
14
|
+
sameSite: 'strict',
|
|
10
15
|
});
|
|
11
16
|
|
|
12
17
|
if (!req) {
|
|
13
|
-
cookies.set(
|
|
14
|
-
'I18N_LANGUAGE',
|
|
15
|
-
normalizeLanguageName(language) || '',
|
|
16
|
-
cookieOptions,
|
|
17
|
-
);
|
|
18
|
+
cookies.set('I18N_LANGUAGE', toGettextLang(language) || '', cookieOptions);
|
|
18
19
|
} else {
|
|
19
20
|
req.universalCookies.set(
|
|
20
21
|
'I18N_LANGUAGE',
|
|
21
|
-
|
|
22
|
+
toGettextLang(language) || '',
|
|
22
23
|
cookieOptions,
|
|
23
24
|
);
|
|
24
25
|
}
|
|
@@ -35,7 +36,7 @@ export function changeLanguage(language, locale, req) {
|
|
|
35
36
|
changeLanguageCookies(language, req);
|
|
36
37
|
|
|
37
38
|
return updateIntl({
|
|
38
|
-
locale: language,
|
|
39
|
+
locale: toReactIntlLang(language),
|
|
39
40
|
messages: locale,
|
|
40
41
|
});
|
|
41
42
|
}
|
|
@@ -31,24 +31,30 @@ export function getQueryStringResults(path, data, subrequest, page) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
const query = {
|
|
35
|
+
...requestData,
|
|
36
|
+
...(!requestData.b_size && {
|
|
37
|
+
b_size: settings.defaultPageSize,
|
|
38
|
+
}),
|
|
39
|
+
...(page && {
|
|
40
|
+
b_start: requestData.b_size
|
|
41
|
+
? data.b_size * (page - 1)
|
|
42
|
+
: settings.defaultPageSize * (page - 1),
|
|
43
|
+
}),
|
|
44
|
+
query: requestData?.query,
|
|
45
|
+
};
|
|
46
|
+
|
|
34
47
|
return {
|
|
35
48
|
type: GET_QUERYSTRING_RESULTS,
|
|
36
49
|
subrequest,
|
|
37
50
|
request: {
|
|
38
|
-
op: 'post',
|
|
39
|
-
path: `${path}/@querystring-search
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
...(page && {
|
|
46
|
-
b_start: requestData.b_size
|
|
47
|
-
? data.b_size * (page - 1)
|
|
48
|
-
: settings.defaultPageSize * (page - 1),
|
|
49
|
-
}),
|
|
50
|
-
query: requestData?.query,
|
|
51
|
-
},
|
|
51
|
+
op: settings.querystringSearchGet ? 'get' : 'post',
|
|
52
|
+
path: `${path}/@querystring-search${
|
|
53
|
+
settings.querystringSearchGet
|
|
54
|
+
? `?query=${encodeURIComponent(JSON.stringify(query))}`
|
|
55
|
+
: ''
|
|
56
|
+
}`,
|
|
57
|
+
data: settings.querystringSearchGet ? null : query,
|
|
52
58
|
},
|
|
53
59
|
};
|
|
54
60
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuild relations action.
|
|
3
|
+
* @module actions/relations/rebuild
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { REBUILD_RELATIONS } from '@plone/volto/constants/ActionTypes';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Rebuild relation function.
|
|
10
|
+
* @function rebuildRelations
|
|
11
|
+
* @param {Boolean} flush Flush intids
|
|
12
|
+
* @returns {Object} Rebuild relation action.
|
|
13
|
+
*/
|
|
14
|
+
export function rebuildRelations(flush = false) {
|
|
15
|
+
let path = '/@relations';
|
|
16
|
+
var searchParams = new URLSearchParams();
|
|
17
|
+
searchParams.append('rebuild', '1');
|
|
18
|
+
flush && searchParams.append('flush', '1');
|
|
19
|
+
const searchParamsToString = searchParams.toString();
|
|
20
|
+
path += `?${searchParamsToString}`;
|
|
21
|
+
return {
|
|
22
|
+
type: REBUILD_RELATIONS,
|
|
23
|
+
request: { op: 'get', path: path },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relations actions.
|
|
3
|
+
* @module actions/relations/relations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
CREATE_RELATIONS,
|
|
8
|
+
DELETE_RELATIONS,
|
|
9
|
+
LIST_RELATIONS,
|
|
10
|
+
} from '@plone/volto/constants/ActionTypes';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create relation function.
|
|
14
|
+
* @function createRelations
|
|
15
|
+
* @param {Object|Array} content Relation data.
|
|
16
|
+
* @returns {Object} Create relation action.
|
|
17
|
+
*/
|
|
18
|
+
export function createRelations(content) {
|
|
19
|
+
return {
|
|
20
|
+
type: CREATE_RELATIONS,
|
|
21
|
+
request: {
|
|
22
|
+
op: 'post',
|
|
23
|
+
path: '/@relations',
|
|
24
|
+
data: {
|
|
25
|
+
items: content,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Delete relation function.
|
|
33
|
+
* @function deleteRelations
|
|
34
|
+
* @param {string} id Relation id
|
|
35
|
+
* @returns {Object} Delete relation action.
|
|
36
|
+
*/
|
|
37
|
+
export function deleteRelations(content) {
|
|
38
|
+
return {
|
|
39
|
+
type: DELETE_RELATIONS,
|
|
40
|
+
request: {
|
|
41
|
+
op: 'del',
|
|
42
|
+
path: `/@relations`,
|
|
43
|
+
data: {
|
|
44
|
+
items: content,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Query relations
|
|
52
|
+
* @function queryRelations
|
|
53
|
+
* @param {string} relation Name of relation
|
|
54
|
+
* @param {boolean} onlyBroken
|
|
55
|
+
* @returns {Object} List relations action
|
|
56
|
+
*/
|
|
57
|
+
export function queryRelations(
|
|
58
|
+
relation = null,
|
|
59
|
+
onlyBroken = false,
|
|
60
|
+
subrequest = null,
|
|
61
|
+
source = null,
|
|
62
|
+
target = null,
|
|
63
|
+
query_source = null,
|
|
64
|
+
query_target = null,
|
|
65
|
+
) {
|
|
66
|
+
let path = '/@relations';
|
|
67
|
+
var searchParams = new URLSearchParams();
|
|
68
|
+
relation && searchParams.append('relation', relation);
|
|
69
|
+
onlyBroken && searchParams.append('onlyBroken', onlyBroken);
|
|
70
|
+
source && searchParams.append('source', source);
|
|
71
|
+
target && searchParams.append('target', target);
|
|
72
|
+
query_source && searchParams.append('query_source', query_source);
|
|
73
|
+
query_target && searchParams.append('query_target', query_target);
|
|
74
|
+
const searchParamsToString = searchParams.toString();
|
|
75
|
+
if (searchParamsToString) {
|
|
76
|
+
path += `?${searchParamsToString}`;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
type: LIST_RELATIONS,
|
|
80
|
+
subrequest,
|
|
81
|
+
request: {
|
|
82
|
+
op: 'get',
|
|
83
|
+
path: path,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { queryRelations } from './relations';
|
|
2
|
+
import { LIST_RELATIONS } from '@plone/volto/constants/ActionTypes';
|
|
3
|
+
|
|
4
|
+
describe('Relations action', () => {
|
|
5
|
+
describe('queryRelations', () => {
|
|
6
|
+
it('should create an action to get relations of type "relatedItems"', () => {
|
|
7
|
+
const relation = 'relatedItems';
|
|
8
|
+
const action = queryRelations(relation);
|
|
9
|
+
|
|
10
|
+
expect(action.type).toEqual(LIST_RELATIONS);
|
|
11
|
+
expect(action.request.op).toEqual('get');
|
|
12
|
+
expect(action.request.path).toEqual(`/@relations?relation=${relation}`);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
});
|
package/src/components/index.js
CHANGED
|
@@ -89,6 +89,7 @@ export ContentTypeSchema from '@plone/volto/components/manage/Controlpanels/Cont
|
|
|
89
89
|
export ContentTypesActions from '@plone/volto/components/manage/Controlpanels/ContentTypesActions';
|
|
90
90
|
export UsersControlpanel from '@plone/volto/components/manage/Controlpanels/Users/UsersControlpanel';
|
|
91
91
|
export UserGroupMembershipControlPanel from '@plone/volto/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel';
|
|
92
|
+
export Relations from '@plone/volto/components/manage/Controlpanels/Relations/Relations';
|
|
92
93
|
export GroupsControlpanel from '@plone/volto/components/manage/Controlpanels/Groups/GroupsControlpanel';
|
|
93
94
|
export RulesControlpanel from '@plone/volto/components/manage/Controlpanels/Rules/Rules';
|
|
94
95
|
export AddRuleControlpanel from '@plone/volto/components/manage/Controlpanels/Rules/AddRule';
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
getBlocksLayoutFieldname,
|
|
34
34
|
getLanguageIndependentFields,
|
|
35
35
|
langmap,
|
|
36
|
-
|
|
36
|
+
toGettextLang,
|
|
37
37
|
} from '@plone/volto/helpers';
|
|
38
38
|
|
|
39
39
|
import { preloadLazyLibs } from '@plone/volto/helpers/Loadable';
|
|
@@ -219,7 +219,7 @@ class Add extends Component {
|
|
|
219
219
|
onCancel() {
|
|
220
220
|
if (this.props.location?.state?.translationOf) {
|
|
221
221
|
const language = this.props.location.state.languageFrom;
|
|
222
|
-
const langFileName =
|
|
222
|
+
const langFileName = toGettextLang(language);
|
|
223
223
|
import('@root/../locales/' + langFileName + '.json').then((locale) => {
|
|
224
224
|
this.props.changeLanguage(language, locale.default);
|
|
225
225
|
});
|
|
@@ -33,17 +33,22 @@ const BlockChooser = ({
|
|
|
33
33
|
properties = {},
|
|
34
34
|
}) => {
|
|
35
35
|
const intl = useIntl();
|
|
36
|
-
const
|
|
36
|
+
const hasAllowedBlocks = !isEmpty(allowedBlocks);
|
|
37
37
|
|
|
38
38
|
const filteredBlocksConfig = filter(blocksConfig, (item) => {
|
|
39
|
+
// Check if the block is well formed (has at least id and title)
|
|
40
|
+
const blockIsWellFormed = Boolean(item.title && item.id);
|
|
41
|
+
if (!blockIsWellFormed) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
39
44
|
if (showRestricted) {
|
|
40
|
-
if (
|
|
45
|
+
if (hasAllowedBlocks) {
|
|
41
46
|
return allowedBlocks.includes(item.id);
|
|
42
47
|
} else {
|
|
43
48
|
return true;
|
|
44
49
|
}
|
|
45
50
|
} else {
|
|
46
|
-
if (
|
|
51
|
+
if (hasAllowedBlocks) {
|
|
47
52
|
return allowedBlocks.includes(item.id);
|
|
48
53
|
} else {
|
|
49
54
|
// Overload restricted as a function, so we can decide the availability of a block
|
|
@@ -89,14 +94,18 @@ const BlockChooser = ({
|
|
|
89
94
|
function blocksAvailableFilter(blocks) {
|
|
90
95
|
return blocks.filter(
|
|
91
96
|
(block) =>
|
|
92
|
-
getFormatMessage(block.title)
|
|
97
|
+
getFormatMessage(block.title)
|
|
98
|
+
.toLowerCase()
|
|
99
|
+
.includes(filterValue.toLowerCase()) ||
|
|
93
100
|
filterVariations(block)?.length,
|
|
94
101
|
);
|
|
95
102
|
}
|
|
96
103
|
function filterVariations(block) {
|
|
97
104
|
return block.variations?.filter(
|
|
98
105
|
(variation) =>
|
|
99
|
-
getFormatMessage(variation.title)
|
|
106
|
+
getFormatMessage(variation.title)
|
|
107
|
+
.toLowerCase()
|
|
108
|
+
.includes(filterValue.toLowerCase()) &&
|
|
100
109
|
!variation.title.toLowerCase().includes('default'),
|
|
101
110
|
);
|
|
102
111
|
}
|
|
@@ -26,35 +26,16 @@ const messages = defineMessages({
|
|
|
26
26
|
const Edit = React.memo(
|
|
27
27
|
(props) => {
|
|
28
28
|
const { data, onChangeBlock, block, selected, pathname } = props;
|
|
29
|
-
|
|
30
29
|
const intl = useIntl();
|
|
31
|
-
|
|
32
|
-
// componentDidMount
|
|
33
|
-
React.useEffect(() => {
|
|
34
|
-
if (!data.query) {
|
|
35
|
-
onChangeBlock(block, {
|
|
36
|
-
...data,
|
|
37
|
-
query: [],
|
|
38
|
-
block,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
/* eslint-disable react-hooks/exhaustive-deps */
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
30
|
const placeholder =
|
|
45
31
|
data.placeholder ||
|
|
46
32
|
(data?.querystring?.query?.length
|
|
47
33
|
? intl.formatMessage(messages.results)
|
|
48
34
|
: intl.formatMessage(messages.items));
|
|
49
35
|
|
|
50
|
-
const HeadlineTag = data.headlineTag || 'h2';
|
|
51
|
-
|
|
52
36
|
return (
|
|
53
37
|
<>
|
|
54
38
|
<p className="items-preview">{placeholder}</p>
|
|
55
|
-
{data.headline && (
|
|
56
|
-
<HeadlineTag className="headline">{data.headline}</HeadlineTag>
|
|
57
|
-
)}
|
|
58
39
|
<ListingBody {...props} path={getBaseUrl(pathname)} isEditMode />
|
|
59
40
|
<SidebarPortal selected={selected}>
|
|
60
41
|
<ListingData
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { createRef } from 'react';
|
|
2
2
|
import { FormattedMessage, injectIntl } from 'react-intl';
|
|
3
|
+
import cx from 'classnames';
|
|
3
4
|
import { Pagination, Dimmer, Loader } from 'semantic-ui-react';
|
|
4
5
|
import { Icon } from '@plone/volto/components';
|
|
5
6
|
import config from '@plone/volto/registry';
|
|
@@ -44,70 +45,85 @@ const ListingBody = withQuerystringResults((props) => {
|
|
|
44
45
|
? variation.noResultsComponent
|
|
45
46
|
: config.blocks?.blocksConfig['listing'].noResultsComponent;
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
prevItem={{
|
|
68
|
-
content: <Icon name={paginationLeftSVG} size="18px" />,
|
|
69
|
-
icon: true,
|
|
70
|
-
'aria-disabled': !prevBatch,
|
|
71
|
-
className: !prevBatch ? 'disabled' : null,
|
|
72
|
-
}}
|
|
73
|
-
nextItem={{
|
|
74
|
-
content: <Icon name={paginationRightSVG} size="18px" />,
|
|
75
|
-
icon: true,
|
|
76
|
-
'aria-disabled': !nextBatch,
|
|
77
|
-
className: !nextBatch ? 'disabled' : null,
|
|
78
|
-
}}
|
|
48
|
+
const HeadlineTag = data.headlineTag || 'h2';
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
{data.headline && (
|
|
53
|
+
<HeadlineTag
|
|
54
|
+
className={cx('headline', {
|
|
55
|
+
emptyListing: !listingItems?.length > 0,
|
|
56
|
+
})}
|
|
57
|
+
>
|
|
58
|
+
{data.headline}
|
|
59
|
+
</HeadlineTag>
|
|
60
|
+
)}
|
|
61
|
+
{listingItems?.length > 0 ? (
|
|
62
|
+
<div ref={listingRef}>
|
|
63
|
+
<ListingBodyTemplate
|
|
64
|
+
items={listingItems}
|
|
65
|
+
isEditMode={isEditMode}
|
|
66
|
+
{...data}
|
|
67
|
+
{...variation}
|
|
79
68
|
/>
|
|
69
|
+
{totalPages > 1 && (
|
|
70
|
+
<div className="pagination-wrapper">
|
|
71
|
+
<Pagination
|
|
72
|
+
activePage={currentPage}
|
|
73
|
+
totalPages={totalPages}
|
|
74
|
+
onPageChange={(e, { activePage }) => {
|
|
75
|
+
!isEditMode &&
|
|
76
|
+
listingRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
77
|
+
onPaginationChange(e, { activePage });
|
|
78
|
+
}}
|
|
79
|
+
firstItem={null}
|
|
80
|
+
lastItem={null}
|
|
81
|
+
prevItem={{
|
|
82
|
+
content: <Icon name={paginationLeftSVG} size="18px" />,
|
|
83
|
+
icon: true,
|
|
84
|
+
'aria-disabled': !prevBatch,
|
|
85
|
+
className: !prevBatch ? 'disabled' : null,
|
|
86
|
+
}}
|
|
87
|
+
nextItem={{
|
|
88
|
+
content: <Icon name={paginationRightSVG} size="18px" />,
|
|
89
|
+
icon: true,
|
|
90
|
+
'aria-disabled': !nextBatch,
|
|
91
|
+
className: !nextBatch ? 'disabled' : null,
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
) : isEditMode ? (
|
|
98
|
+
<div className="listing message" ref={listingRef}>
|
|
99
|
+
{isFolderContentsListing && (
|
|
100
|
+
<FormattedMessage
|
|
101
|
+
id="No items found in this container."
|
|
102
|
+
defaultMessage="No items found in this container."
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
105
|
+
{hasLoaded && NoResults && (
|
|
106
|
+
<NoResults isEditMode={isEditMode} {...data} />
|
|
107
|
+
)}
|
|
108
|
+
<Dimmer active={!hasLoaded} inverted>
|
|
109
|
+
<Loader indeterminate size="small">
|
|
110
|
+
<FormattedMessage id="loading" defaultMessage="Loading" />
|
|
111
|
+
</Loader>
|
|
112
|
+
</Dimmer>
|
|
113
|
+
</div>
|
|
114
|
+
) : (
|
|
115
|
+
<div className="emptyListing">
|
|
116
|
+
{hasLoaded && NoResults && (
|
|
117
|
+
<NoResults isEditMode={isEditMode} {...data} />
|
|
118
|
+
)}
|
|
119
|
+
<Dimmer active={!hasLoaded} inverted>
|
|
120
|
+
<Loader indeterminate size="small">
|
|
121
|
+
<FormattedMessage id="loading" defaultMessage="Loading" />
|
|
122
|
+
</Loader>
|
|
123
|
+
</Dimmer>
|
|
80
124
|
</div>
|
|
81
125
|
)}
|
|
82
|
-
|
|
83
|
-
) : isEditMode ? (
|
|
84
|
-
<div className="listing message" ref={listingRef}>
|
|
85
|
-
{isFolderContentsListing && (
|
|
86
|
-
<FormattedMessage
|
|
87
|
-
id="No items found in this container."
|
|
88
|
-
defaultMessage="No items found in this container."
|
|
89
|
-
/>
|
|
90
|
-
)}
|
|
91
|
-
{hasLoaded && NoResults && (
|
|
92
|
-
<NoResults isEditMode={isEditMode} {...data} />
|
|
93
|
-
)}
|
|
94
|
-
<Dimmer active={!hasLoaded} inverted>
|
|
95
|
-
<Loader indeterminate size="small">
|
|
96
|
-
<FormattedMessage id="loading" defaultMessage="Loading" />
|
|
97
|
-
</Loader>
|
|
98
|
-
</Dimmer>
|
|
99
|
-
</div>
|
|
100
|
-
) : (
|
|
101
|
-
<div>
|
|
102
|
-
{hasLoaded && NoResults && (
|
|
103
|
-
<NoResults isEditMode={isEditMode} {...data} />
|
|
104
|
-
)}
|
|
105
|
-
<Dimmer active={!hasLoaded} inverted>
|
|
106
|
-
<Loader indeterminate size="small">
|
|
107
|
-
<FormattedMessage id="loading" defaultMessage="Loading" />
|
|
108
|
-
</Loader>
|
|
109
|
-
</Dimmer>
|
|
110
|
-
</div>
|
|
126
|
+
</>
|
|
111
127
|
);
|
|
112
128
|
});
|
|
113
129
|
|
|
@@ -7,15 +7,11 @@ import { ListingBlockBody as ListingBody } from '@plone/volto/components';
|
|
|
7
7
|
|
|
8
8
|
const View = (props) => {
|
|
9
9
|
const { data, path, pathname, className } = props;
|
|
10
|
-
const HeadlineTag = data.headlineTag || 'h2';
|
|
11
10
|
|
|
12
11
|
return (
|
|
13
12
|
<div
|
|
14
13
|
className={cx('block listing', data.variation || 'default', className)}
|
|
15
14
|
>
|
|
16
|
-
{data.headline && (
|
|
17
|
-
<HeadlineTag className="headline">{data.headline}</HeadlineTag>
|
|
18
|
-
)}
|
|
19
15
|
<ListingBody {...props} path={path ?? pathname} />
|
|
20
16
|
</div>
|
|
21
17
|
);
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { getQueryStringResults } from '@plone/volto/actions';
|
|
2
2
|
import { resolveBlockExtensions } from '@plone/volto/helpers';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const getListingBlockAsyncData = ({
|
|
5
|
+
dispatch,
|
|
6
|
+
id,
|
|
7
|
+
data,
|
|
8
|
+
path,
|
|
9
|
+
blocksConfig,
|
|
10
|
+
}) => {
|
|
5
11
|
const { resolvedExtensions } = resolveBlockExtensions(data, blocksConfig);
|
|
6
12
|
|
|
7
13
|
return [
|
|
@@ -14,8 +20,10 @@ export default ({ dispatch, data, path, blocksConfig }) => {
|
|
|
14
20
|
? { fullobjects: 1 }
|
|
15
21
|
: { metadata_fields: '_all' }),
|
|
16
22
|
},
|
|
17
|
-
|
|
23
|
+
id,
|
|
18
24
|
),
|
|
19
25
|
),
|
|
20
26
|
];
|
|
21
27
|
};
|
|
28
|
+
|
|
29
|
+
export default getListingBlockAsyncData;
|
|
@@ -14,11 +14,16 @@ function getDisplayName(WrappedComponent) {
|
|
|
14
14
|
|
|
15
15
|
export default function withQuerystringResults(WrappedComponent) {
|
|
16
16
|
function WithQuerystringResults(props) {
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
data = {},
|
|
19
|
+
id = data.block,
|
|
20
|
+
properties: content,
|
|
21
|
+
path,
|
|
22
|
+
variation,
|
|
23
|
+
} = props;
|
|
18
24
|
const { settings } = config;
|
|
19
25
|
const querystring = data.querystring || data; // For backwards compat with data saved before Blocks schema. Note, this is also how the Search block passes data to ListingBody
|
|
20
26
|
|
|
21
|
-
const { block } = data;
|
|
22
27
|
const { b_size = settings.defaultPageSize } = querystring; // batchsize
|
|
23
28
|
|
|
24
29
|
// save the path so it won't trigger dispatch on eager router location change
|
|
@@ -45,32 +50,32 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
45
50
|
|
|
46
51
|
const folderItems = content?.is_folderish ? content.items : [];
|
|
47
52
|
const hasQuery = querystring?.query?.length > 0;
|
|
48
|
-
const hasLoaded = hasQuery ? !querystringResults?.[
|
|
53
|
+
const hasLoaded = hasQuery ? !querystringResults?.[id]?.loading : true;
|
|
49
54
|
|
|
50
55
|
const listingItems =
|
|
51
|
-
querystring?.query?.length > 0 && querystringResults?.[
|
|
52
|
-
? querystringResults?.[
|
|
56
|
+
querystring?.query?.length > 0 && querystringResults?.[id]
|
|
57
|
+
? querystringResults?.[id]?.items || []
|
|
53
58
|
: folderItems;
|
|
54
59
|
|
|
55
60
|
const showAsFolderListing = !hasQuery && content?.items_total > b_size;
|
|
56
61
|
const showAsQueryListing =
|
|
57
|
-
hasQuery && querystringResults?.[
|
|
62
|
+
hasQuery && querystringResults?.[id]?.total > b_size;
|
|
58
63
|
|
|
59
64
|
const totalPages = showAsFolderListing
|
|
60
65
|
? Math.ceil(content.items_total / b_size)
|
|
61
66
|
: showAsQueryListing
|
|
62
|
-
? Math.ceil(querystringResults[
|
|
67
|
+
? Math.ceil(querystringResults[id].total / b_size)
|
|
63
68
|
: 0;
|
|
64
69
|
|
|
65
70
|
const prevBatch = showAsFolderListing
|
|
66
71
|
? content.batching?.prev
|
|
67
72
|
: showAsQueryListing
|
|
68
|
-
? querystringResults[
|
|
73
|
+
? querystringResults[id].batching?.prev
|
|
69
74
|
: null;
|
|
70
75
|
const nextBatch = showAsFolderListing
|
|
71
76
|
? content.batching?.next
|
|
72
77
|
: showAsQueryListing
|
|
73
|
-
? querystringResults[
|
|
78
|
+
? querystringResults[id].batching?.next
|
|
74
79
|
: null;
|
|
75
80
|
|
|
76
81
|
const isImageGallery =
|
|
@@ -80,7 +85,7 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
80
85
|
useDeepCompareEffect(() => {
|
|
81
86
|
if (hasQuery) {
|
|
82
87
|
dispatch(
|
|
83
|
-
getQueryStringResults(initialPath, adaptedQuery,
|
|
88
|
+
getQueryStringResults(initialPath, adaptedQuery, id, currentPage),
|
|
84
89
|
);
|
|
85
90
|
} else if (isImageGallery && !hasQuery) {
|
|
86
91
|
// when used as image gallery, it doesn't need a query to list children
|
|
@@ -98,14 +103,14 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
98
103
|
},
|
|
99
104
|
],
|
|
100
105
|
},
|
|
101
|
-
|
|
106
|
+
id,
|
|
102
107
|
),
|
|
103
108
|
);
|
|
104
109
|
} else {
|
|
105
110
|
dispatch(getContent(initialPath, null, null, currentPage));
|
|
106
111
|
}
|
|
107
112
|
}, [
|
|
108
|
-
|
|
113
|
+
id,
|
|
109
114
|
isImageGallery,
|
|
110
115
|
adaptedQuery,
|
|
111
116
|
hasQuery,
|
|
@@ -118,7 +123,7 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
118
123
|
<WrappedComponent
|
|
119
124
|
{...props}
|
|
120
125
|
onPaginationChange={(e, { activePage }) => setCurrentPage(activePage)}
|
|
121
|
-
total={querystringResults?.[
|
|
126
|
+
total={querystringResults?.[id]?.total}
|
|
122
127
|
batch_size={b_size}
|
|
123
128
|
currentPage={currentPage}
|
|
124
129
|
totalPages={totalPages}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import useDeepCompareEffect from 'use-deep-compare-effect';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
3
2
|
import { defineMessages } from 'react-intl';
|
|
4
3
|
import { compose } from 'redux';
|
|
5
4
|
|
|
@@ -60,9 +59,11 @@ const SearchBlockEdit = (props) => {
|
|
|
60
59
|
};
|
|
61
60
|
|
|
62
61
|
const { query = {} } = data || {};
|
|
63
|
-
|
|
62
|
+
// We don't need deep compare here, as this is just json serializable data.
|
|
63
|
+
const deepQuery = JSON.stringify(query);
|
|
64
|
+
useEffect(() => {
|
|
64
65
|
onTriggerSearch();
|
|
65
|
-
}, [
|
|
66
|
+
}, [deepQuery, onTriggerSearch]);
|
|
66
67
|
|
|
67
68
|
return (
|
|
68
69
|
<>
|
|
@@ -57,7 +57,7 @@ const applyDefaults = (data, root) => {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
const SearchBlockView = (props) => {
|
|
60
|
-
const { data, searchData, mode = 'view', variation } = props;
|
|
60
|
+
const { id, data, searchData, mode = 'view', variation } = props;
|
|
61
61
|
|
|
62
62
|
const Layout = variation.view;
|
|
63
63
|
|
|
@@ -89,6 +89,7 @@ const SearchBlockView = (props) => {
|
|
|
89
89
|
setSelectedView={setSelectedView}
|
|
90
90
|
>
|
|
91
91
|
<ListingBody
|
|
92
|
+
id={id}
|
|
92
93
|
variation={{ ...data, ...listingBodyVariation }}
|
|
93
94
|
data={listingBodyData}
|
|
94
95
|
path={props.path}
|