├── .gitignore
├── elgg-services.php
├── .github
└── workflows
│ ├── lint.yml
│ ├── create-zip-release.yml
│ ├── phpunit.yml
│ └── tweet-release.yml
├── views
└── default
│ ├── forms
│ ├── widgets
│ │ ├── advanced.mjs
│ │ ├── save.php
│ │ └── advanced.php
│ └── widget_manager
│ │ ├── manage_widgets
│ │ ├── widget.css
│ │ └── widget.php
│ │ └── widget_page.php
│ ├── search
│ └── object
│ │ └── widget_page.php
│ ├── widget_manager
│ ├── widgets
│ │ ├── custom_more.php
│ │ └── settings.php
│ ├── group_tool_widgets.php
│ ├── lazy_loading.mjs
│ ├── forms
│ │ └── groups_widget_access.php
│ ├── site.css
│ └── fluid.mjs
│ ├── admin
│ └── widgets
│ │ ├── pages.php
│ │ ├── manage.mjs
│ │ ├── manage.php
│ │ └── cleanup.php
│ ├── page
│ └── layouts
│ │ ├── widgets
│ │ └── add_button.php
│ │ └── widgets.php
│ ├── resources
│ ├── widget_manager
│ │ ├── custom_index.php
│ │ └── widget_page.php
│ └── widgets
│ │ ├── add_panel.mjs
│ │ └── add_panel.php
│ ├── object
│ ├── widget_page.php
│ └── widget.php
│ ├── elgg
│ └── widgets.mjs
│ └── plugins
│ └── widget_manager
│ └── settings.php
├── actions
└── widget_manager
│ ├── fluid_order.php
│ ├── force_tool_widgets.php
│ ├── cleanup.php
│ ├── lazy_load_widgets.php
│ ├── groups
│ └── update_widget_access.php
│ ├── widget_page.php
│ └── manage_widgets.php
├── composer.json
├── classes
├── ColdTrick
│ └── WidgetManager
│ │ ├── Settings.php
│ │ ├── Menus
│ │ ├── Entity.php
│ │ ├── Title.php
│ │ └── AdminHeader.php
│ │ ├── Bootstrap.php
│ │ ├── Seeder.php
│ │ ├── WidgetsSettingsConfig.php
│ │ ├── Access.php
│ │ ├── Widgets.php
│ │ └── Groups.php
├── WidgetManagerWidget.php
└── WidgetPage.php
├── README.md
├── languages
├── fr.php
├── es.php
├── en.php
└── nl.php
├── elgg-plugin.php
├── composer.lock
└── CHANGES.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 |
--------------------------------------------------------------------------------
/elgg-services.php:
--------------------------------------------------------------------------------
1 | DI\get(WidgetsSettingsConfig::class),
7 | ];
8 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint checks
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | lint:
7 | name: Lint checks
8 | uses: ColdTrick/.github/.github/workflows/lint.yml@master
9 |
--------------------------------------------------------------------------------
/views/default/forms/widgets/advanced.mjs:
--------------------------------------------------------------------------------
1 | import 'jquery';
2 |
3 | $(document).on('open', '.elgg-tabs-component.widget-settings .elgg-tabs > li', function() {
4 | $(window).trigger('resize.lightbox');
5 | });
6 |
--------------------------------------------------------------------------------
/.github/workflows/create-zip-release.yml:
--------------------------------------------------------------------------------
1 | name: Create ZIP Release
2 |
3 | on:
4 | release:
5 | types: [created, edited]
6 |
7 | jobs:
8 | build:
9 | name: Build release
10 | uses: ColdTrick/.github/.github/workflows/create-zip-release.yml@master
11 |
--------------------------------------------------------------------------------
/.github/workflows/phpunit.yml:
--------------------------------------------------------------------------------
1 | name: PHPUnit Plugin Tests
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | phpunit:
7 | name: Run PHPUnit test suites
8 | uses: ColdTrick/.github/.github/workflows/phpunit.yml@master
9 | with:
10 | elgg_major_version: 6
11 |
--------------------------------------------------------------------------------
/.github/workflows/tweet-release.yml:
--------------------------------------------------------------------------------
1 | name: Tweet about a new release
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | tweet:
9 | name: Send a Tweet
10 | uses: ColdTrick/.github/.github/workflows/tweet-release.yml@master
11 | secrets: inherit
12 |
--------------------------------------------------------------------------------
/views/default/forms/widget_manager/manage_widgets/widget.css:
--------------------------------------------------------------------------------
1 | .widget-manager-manage-widgets-non-default,
2 | .widget-manager-manage-widgets-non-default label {
3 | color: orange;
4 | }
5 |
6 | .widget-manager-manage-widgets-toggle-multiple {
7 | white-space: nowrap;
8 | margin-bottom: 0;
9 | }
10 |
--------------------------------------------------------------------------------
/views/default/search/object/widget_page.php:
--------------------------------------------------------------------------------
1 | $guid) {
9 | $widget = elgg_call(ELGG_IGNORE_ACCESS, function() use ($guid) {
10 | return get_entity($guid);
11 | });
12 |
13 | if (!$widget instanceof \ElggWidget || !$widget->canEdit()) {
14 | continue;
15 | }
16 |
17 | $widget->column = 1;
18 | $widget->order = $index + 1;
19 | }
20 |
21 | return elgg_ok_response();
22 |
--------------------------------------------------------------------------------
/views/default/widget_manager/widgets/custom_more.php:
--------------------------------------------------------------------------------
1 | widget_manager_custom_more_title;
9 | $custom_more_url = $widget->widget_manager_custom_more_url;
10 |
11 | if (empty($custom_more_title) || empty($custom_more_url)) {
12 | return;
13 | }
14 |
15 | echo elgg_view('page/components/list/widget_more', ['widget_more' => elgg_view_url($custom_more_url, $custom_more_title)]);
16 |
--------------------------------------------------------------------------------
/views/default/admin/widgets/pages.php:
--------------------------------------------------------------------------------
1 | 'add_widget_page',
5 | 'text' => elgg_echo('add'),
6 | 'href' => 'ajax/form/widget_manager/widget_page',
7 | 'class' => 'elgg-lightbox elgg-button elgg-button-action',
8 | 'icon' => 'plus',
9 | ]);
10 |
11 | echo elgg_list_entities([
12 | 'type' => 'object',
13 | 'subtype' => 'widget_page',
14 | 'limit' => false,
15 | 'no_results' => true,
16 | 'sort_by' => [
17 | 'property' => 'url',
18 | 'direction' => 'asc',
19 | ],
20 | ]);
21 |
--------------------------------------------------------------------------------
/actions/widget_manager/force_tool_widgets.php:
--------------------------------------------------------------------------------
1 | 'group',
12 | 'limit' => false,
13 | 'batch' => true,
14 | ]);
15 |
16 | foreach ($groups as $group) {
17 | $group->save();
18 | $counter++;
19 | }
20 |
21 | return elgg_ok_response('', elgg_echo('widget_manager:action:force_tool_widgets:success', [$counter]));
22 |
--------------------------------------------------------------------------------
/actions/widget_manager/cleanup.php:
--------------------------------------------------------------------------------
1 | 'object',
12 | 'subtype' => 'widget',
13 | 'metadata_name_value_pairs' => [
14 | 'handler' => $handler,
15 | 'context' => $context,
16 | ],
17 | 'limit' => false,
18 | 'batch' => true,
19 | 'batch_inc_offset' => false,
20 | ]);
21 |
22 | foreach ($entities as $entity) {
23 | $entity->delete();
24 | }
25 |
26 | return elgg_ok_response('', elgg_echo('entity:delete:success', [elgg_echo('collection:object:widget')]));
27 |
--------------------------------------------------------------------------------
/views/default/admin/widgets/manage.mjs:
--------------------------------------------------------------------------------
1 | import 'jquery';
2 | import i18n from 'elgg/i18n';
3 | import Ajax from 'elgg/Ajax';
4 |
5 | $(document).on('click', '.widget-manager-unsupported-context .elgg-input-checkbox', function (e, elem) {
6 | if (!$(this).is(':checked')) {
7 | return;
8 | }
9 |
10 | if (!confirm(i18n.echo('widget_manager:forms:manage_widgets:unsupported_context:confirm'))) {
11 | return false;
12 | }
13 | });
14 |
15 | $(document).on('change', '.elgg-input-checkbox[name^="widgets_config"]', function (e, elem) {
16 | var ajax = new Ajax();
17 |
18 | ajax.action('widget_manager/manage_widgets', {
19 | data: {
20 | [$(this).attr('name')]: $(this).is(':checked') ? 1 : 0
21 | },
22 | showSuccessMessages: false
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "coldtrick/widget_manager",
3 | "description": "Manage your widgets",
4 | "homepage": "https://www.coldtrick.com",
5 | "type": "elgg-plugin",
6 | "keywords": ["elgg", "plugin", "widget"],
7 | "license": "GPL-2.0-only",
8 | "support": {
9 | "source": "https://github.com/ColdTrick/widget_manager",
10 | "issues": "https://github.com/ColdTrick/widget_manager/issues"
11 | },
12 | "require": {
13 | "mobiledetect/mobiledetectlib": "~4.8.0",
14 | "npm-asset/muuri": "~0.9.5"
15 | },
16 | "repositories": [{
17 | "type": "composer",
18 | "url": "https://asset-packagist.org"
19 | }
20 | ],
21 | "config": {
22 | "fxp-asset": {
23 | "enabled": false
24 | }
25 | },
26 | "conflict": {
27 | "elgg/elgg": "<6.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Settings.php:
--------------------------------------------------------------------------------
1 | getParam('plugin_id') !== 'widget_manager') {
19 | return null;
20 | }
21 |
22 | if ($event->getParam('name') !== 'index_managers') {
23 | return null;
24 | }
25 |
26 | $current_value = $event->getValue();
27 | if (!is_array($current_value)) {
28 | return null;
29 | }
30 |
31 | return implode(',', $current_value);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Widget Manager
2 | ==============
3 |
4 | 
5 | 
6 | [](https://packagist.org/packages/coldtrick/widget_manager)
7 | [](https://packagist.org/packages/coldtrick/widget_manager)
8 |
9 | A plugin for Elgg that adds more widget related features.
10 |
11 | Features
12 | --------
13 |
14 | - Index widgets (with a few different layouts)
15 | - Group widgets (instead of group profile modules)
16 | - Option to globally hide a specific widgettype (will even hide widget already placed on profile/dashboard)
17 | - Create group default widgets
18 | - Create extra pages with a widget layout
19 |
--------------------------------------------------------------------------------
/classes/WidgetManagerWidget.php:
--------------------------------------------------------------------------------
1 | widget_manager_custom_title ?: parent::getDisplayName();
15 | }
16 |
17 | /**
18 | * {@inheritdoc}
19 | */
20 | public function invalidateCache(): void {
21 | if (!$this->guid) {
22 | return;
23 | }
24 |
25 | parent::invalidateCache();
26 |
27 | if (!\ColdTrick\WidgetManager\Widgets::isCacheableWidget($this)) {
28 | return;
29 | }
30 |
31 | $languages = elgg()->translator->getAllowedLanguages();
32 | foreach ($languages as $language) {
33 | elgg_delete_system_cache("widget_cache_{$this->guid}_{$language}");
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/actions/widget_manager/lazy_load_widgets.php:
--------------------------------------------------------------------------------
1 | $guids,
23 | 'type' => 'object',
24 | 'subtype' => 'widget',
25 | 'limit' => false,
26 | ]);
27 |
28 | $result = [];
29 | foreach ($entities as $entity) {
30 | $result[$entity->guid] = elgg_view('object/widget/body', ['entity' => $entity]);
31 | }
32 |
33 | elgg_pop_context();
34 | elgg_set_context_stack($old_context_stack);
35 |
36 | return elgg_ok_response($result);
37 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Menus/Entity.php:
--------------------------------------------------------------------------------
1 | getEntityParam();
21 | if (!$entity instanceof \WidgetPage || !$entity->canEdit()) {
22 | return null;
23 | }
24 |
25 | $result = $event->getValue();
26 |
27 | $result[] = \ElggMenuItem::factory([
28 | 'name' => 'edit',
29 | 'text' => elgg_echo('edit'),
30 | 'icon' => 'edit',
31 | 'href' => "ajax/form/widget_manager/widget_page?guid={$entity->guid}",
32 | 'link_class' => 'elgg-lightbox',
33 | 'data-colorbox-opts' => json_encode([
34 | 'trapFocus' => false,
35 | ]),
36 | ]);
37 |
38 | return $result;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/views/default/widget_manager/group_tool_widgets.php:
--------------------------------------------------------------------------------
1 | [], 'disable' => []];
25 | $params = ['entity' => $page_owner];
26 |
27 | $result = (array) elgg_trigger_event_results('group_tool_widgets', 'widget_manager', $params, $result);
28 |
29 | if (empty($result)) {
30 | return;
31 | }
32 |
33 | $disable_widget_handlers = (array) elgg_extract('disable', $result, []);
34 | foreach ($disable_widget_handlers as $handler) {
35 | elgg_unregister_widget_type($handler);
36 | }
37 |
--------------------------------------------------------------------------------
/views/default/page/layouts/widgets/add_button.php:
--------------------------------------------------------------------------------
1 | elgg_get_context(),
8 | 'context_stack' => elgg_get_context_stack(),
9 | 'show_access' => elgg_extract('show_access', $vars, true),
10 | 'owner_guid' => elgg_extract('owner_guid', $vars, elgg_get_page_owner_guid()),
11 | 'new_widget_column' => elgg_extract('new_widget_column', $vars),
12 | 'new_widget_position' => elgg_extract('new_widget_position', $vars),
13 | ]);
14 |
15 | echo elgg_view_menu('title:widgets', [
16 | 'items' => [
17 | [
18 | 'name' => 'widgets_add',
19 | 'href' => false,
20 | 'text' => elgg_echo('widgets:add'),
21 | 'icon' => 'cog',
22 | 'link_class' => 'elgg-lightbox elgg-more',
23 | 'data-colorbox-opts' => json_encode([
24 | 'href' => $href,
25 | 'maxWidth' => '900px',
26 | 'maxHeight' => '90%',
27 | ]),
28 | ],
29 | ],
30 | 'class' => 'elgg-menu-hz',
31 | 'show_collapse_content' => elgg_extract('show_collapse_content', $vars, true),
32 | ]);
33 |
--------------------------------------------------------------------------------
/views/default/widget_manager/widgets/settings.php:
--------------------------------------------------------------------------------
1 | canEdit()) {
14 | return;
15 | }
16 |
17 | elgg_push_context($widget->context);
18 | elgg_push_context('widgets');
19 | elgg_set_page_owner_guid($widget->getOwnerGUID());
20 |
21 | $additional_class = preg_replace('/[^a-z0-9-]/i', '-', "elgg-form-widgets-save-{$widget->handler}");
22 |
23 | $body_vars = ['widget' => $widget, 'show_access' => $show_access];
24 | $form_vars = ['class' => $additional_class];
25 |
26 | $form = elgg_view_form('widgets/save', $form_vars, $body_vars);
27 |
28 | echo elgg_format_element('div', [
29 | 'class' => 'widget-manager-lightbox-edit',
30 | 'id' => "widget-edit-{$widget->guid}",
31 | ], $form);
32 |
33 | elgg_pop_context(); // undo widgets
34 | elgg_pop_context(); // undo $widget->context
35 |
--------------------------------------------------------------------------------
/actions/widget_manager/groups/update_widget_access.php:
--------------------------------------------------------------------------------
1 | canEdit()) {
19 | return elgg_error_response(elgg_echo('groups:cantedit'));
20 | }
21 |
22 | $widgets = elgg_get_entities([
23 | 'type' => 'object',
24 | 'subtype' => 'widget',
25 | 'owner_guid' => $group->guid,
26 | 'metadata_name' => 'context',
27 | 'metadata_value' => 'groups',
28 | 'limit' => false,
29 | ]);
30 |
31 | foreach ($widgets as $widget) {
32 | $widget->access_id = (int) $new_access;
33 | $widget->save();
34 | }
35 |
36 | return elgg_ok_response('', elgg_echo('widget_manager:action:groups:update_widget_access:success'), $group->getURL());
37 |
--------------------------------------------------------------------------------
/views/default/widget_manager/lazy_loading.mjs:
--------------------------------------------------------------------------------
1 | import 'jquery';
2 | import 'elgg';
3 | import 'elgg/widgets';
4 | import Ajax from 'elgg/Ajax';
5 |
6 | if ($('.elgg-module-widget.lazy-loading').length) {
7 | $('.elgg-layout-widgets').each(function(i, layout) {
8 | var $lazy_widgets = $(layout).find('.elgg-module-widget.lazy-loading');
9 | if (!$lazy_widgets.length) {
10 | return;
11 | }
12 |
13 | var guids = [];
14 |
15 | $lazy_widgets.each(function(index, item) {
16 | var guidString = $(item).attr('id');
17 | guids.push(guidString.substr(guidString.indexOf('elgg-widget-') + 'elgg-widget-'.length));
18 | });
19 |
20 | var ajax = new Ajax(false);
21 | ajax.action('widget_manager/lazy_load_widgets', {
22 | data: {
23 | guids: guids,
24 | page_owner_guid: $(layout).data().pageOwnerGuid,
25 | context_stack: $(layout).data().contextStack,
26 | },
27 | success: function (result) {
28 | $.each(result, function(guid, body) {
29 | $('#elgg-widget-' + guid + ' > .elgg-body').html(body);
30 | });
31 |
32 | $(layout).trigger({
33 | type: 'lazyLoaded',
34 | layout: $(layout)
35 | });
36 | }
37 | });
38 | });
39 | }
40 |
--------------------------------------------------------------------------------
/actions/widget_manager/widget_page.php:
--------------------------------------------------------------------------------
1 | canEdit()) {
11 | return elgg_error_response(elgg_echo('actionunauthorized'));
12 | }
13 | }
14 |
15 | $url = get_input('url');
16 | if (empty($url) && !($entity instanceof \WidgetPage && !elgg_is_admin_logged_in())) {
17 | return elgg_error_response(elgg_echo('error:missing_data'));
18 | }
19 |
20 | if (!$entity instanceof \WidgetPage) {
21 | if (!elgg_is_admin_logged_in()) {
22 | return elgg_error_response(elgg_echo('actionunauthorized'));
23 | }
24 |
25 | $entity = new \WidgetPage();
26 | }
27 |
28 | if (elgg_is_admin_logged_in()) {
29 | $entity->url = $url;
30 | }
31 |
32 | $entity->title = get_input('title');
33 | $entity->description = get_input('description');
34 | $entity->show_description = (bool) get_input('show_description');
35 | $entity->layout = get_input('layout');
36 |
37 | $entity->save();
38 |
39 | $entity->setManagers((array) get_input('manager', []));
40 |
41 | elgg_delete_system_cache('widget_pages');
42 |
43 | return elgg_ok_response('', elgg_echo('save:success'));
44 |
--------------------------------------------------------------------------------
/views/default/resources/widget_manager/custom_index.php:
--------------------------------------------------------------------------------
1 | guid); // site owns the index widgets
6 |
7 | $num_columns = 3;
8 |
9 | $layout = elgg_get_plugin_setting('widget_layout', 'widget_manager');
10 | if (!empty($layout)) {
11 | $num_columns = count(explode('|', $layout));
12 | }
13 |
14 | $classes = [];
15 | $column_classes = [];
16 |
17 | switch ($layout) {
18 | case 'fluid':
19 | $classes[] = 'widgets-1-columns';
20 | $classes[] = 'widgets-fluid-columns';
21 | $num_columns = 1;
22 |
23 | elgg_import_esm('widget_manager/fluid');
24 | break;
25 | case '33|33|33':
26 | $classes[] = 'widgets-3-columns';
27 | break;
28 | case '50|50':
29 | $classes[] = 'widgets-2-columns';
30 | break;
31 | default:
32 | $classes[] = "widgets-{$num_columns}-columns";
33 |
34 | $columns = array_reverse(explode('|', $layout));
35 | foreach ($columns as $column_index => $column_width) {
36 | $column_classes[$column_index + 1] = "col-width-{$column_width}";
37 | }
38 | break;
39 | }
40 |
41 | // draw the page
42 | $content = elgg_view_layout('widgets', [
43 | 'class' => $classes,
44 | 'num_columns' => $num_columns,
45 | 'column_classes' => $column_classes,
46 | 'exact_match' => true,
47 | ]);
48 |
49 | echo elgg_view_page('', ['content' => $content]);
50 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Menus/Title.php:
--------------------------------------------------------------------------------
1 | getParam('show_collapse_content', false)) {
26 | return null;
27 | }
28 |
29 | $result = $event->getValue();
30 |
31 | $result[] = \ElggMenuItem::factory([
32 | 'name' => 'hide-widget-contents',
33 | 'class' => 'elgg-more',
34 | 'text' => elgg_echo('widget_manager:layout:content:hide'),
35 | 'icon' => 'eye-slash',
36 | 'href' => false,
37 | 'priority' => 80,
38 | ]);
39 |
40 | $result[] = \ElggMenuItem::factory([
41 | 'name' => 'show-widget-contents',
42 | 'class' => 'elgg-more',
43 | 'item_class' => 'hidden',
44 | 'text' => elgg_echo('widget_manager:layout:content:show'),
45 | 'icon' => 'eye',
46 | 'href' => false,
47 | 'priority' => 81,
48 | ]);
49 |
50 | return $result;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/views/default/admin/widgets/manage.php:
--------------------------------------------------------------------------------
1 | name . '_' . $widget_definition->id;
34 | $widget_output[$key] = elgg_view('forms/widget_manager/manage_widgets/widget', [
35 | 'widget' => $widget_definition,
36 | 'contexts' => $contexts,
37 | ]);
38 | }
39 |
40 | ksort($widget_output);
41 |
42 | echo implode('', $widget_output);
43 |
--------------------------------------------------------------------------------
/views/default/object/widget_page.php:
--------------------------------------------------------------------------------
1 | getDisplayName() . " [{$entity->url}]";
12 |
13 | $vars['title'] = elgg_view_url($entity->getURL(), elgg_get_excerpt($title, 100));
14 |
15 | echo elgg_view('object/elements/summary', $vars);
16 | return;
17 | }
18 |
19 | $num_columns = $entity->getNumColumns();
20 | $layout = $entity->layout;
21 |
22 | $classes = [];
23 | $column_classes = [];
24 |
25 | switch ($layout) {
26 | case '33|33|33':
27 | $classes[] = 'widgets-3-columns';
28 | break;
29 | case '50|50':
30 | $classes[] = 'widgets-2-columns';
31 | break;
32 | default:
33 | $classes[] = "widgets-{$num_columns}-columns";
34 |
35 | $columns = array_reverse(explode('|', $layout));
36 | foreach ($columns as $column_index => $column_width) {
37 | $column_classes[$column_index + 1] = "col-width-{$column_width}";
38 | }
39 | break;
40 | }
41 |
42 | if ($entity->show_description !== false && !empty($entity->description)) {
43 | echo elgg_view('output/longtext', ['value' => $entity->description, 'class' => 'widget-page-description']);
44 | }
45 |
46 | echo elgg_view_layout('widgets', [
47 | 'class' => $classes,
48 | 'num_columns' => $num_columns,
49 | 'column_classes' => $column_classes,
50 | 'exact_match' => true,
51 | 'show_add_widgets' => false,
52 | ]);
53 |
--------------------------------------------------------------------------------
/views/default/admin/widgets/cleanup.php:
--------------------------------------------------------------------------------
1 | elgg_echo('admin:widgets:cleanup:info')]);
6 |
7 | $select = Select::fromTable('entities', 'e');
8 | $select->join('e', 'metadata', 'md_handler', 'md_handler.entity_guid = e.guid');
9 | $select->join('e', 'metadata', 'md_context', 'md_context.entity_guid = e.guid');
10 | $select->select('md_handler.value AS handler');
11 | $select->addSelect('md_context.value AS context');
12 | $select->addSelect('count(*) AS total');
13 | $select->where('e.type = "object"');
14 | $select->andWhere('e.subtype = "widget"');
15 | $select->andWhere('md_handler.name = "handler"');
16 | $select->andWhere('md_context.name = "context"');
17 | $select->groupBy('md_handler.value');
18 | $select->addGroupBy('md_context.value');
19 | $select->orderBy('md_handler.value, md_context.value');
20 |
21 | $res = elgg()->db->getData($select);
22 |
23 | $handler = '';
24 | foreach ($res as $row) {
25 | if ($row->handler !== $handler) {
26 | $handler = $row->handler;
27 | $title = elgg_format_element('b', [], $handler);
28 | echo elgg_format_element('div', ['class' => 'mtm'], $title);
29 | }
30 |
31 | $line = "{$row->context} ({$row->total})";
32 | $line .= elgg_view('output/url', [
33 | 'text' => elgg_echo('delete'),
34 | 'href' => elgg_generate_action_url('widget_manager/cleanup', [
35 | 'handler' => $handler,
36 | 'context' => $row->context,
37 | ]),
38 | 'confirm' => true,
39 | 'class' => 'mlm',
40 | ]);
41 |
42 | echo elgg_format_element('div', ['class' => 'pll'], $line);
43 | }
44 |
--------------------------------------------------------------------------------
/views/default/widget_manager/forms/groups_widget_access.php:
--------------------------------------------------------------------------------
1 | canEdit()) {
8 | return;
9 | }
10 |
11 | $group_enable = elgg_get_plugin_setting('group_enable', 'widget_manager');
12 | if (!in_array($group_enable, ['yes', 'forced'])) {
13 | return;
14 | }
15 |
16 | $widgets_count = elgg_count_entities([
17 | 'type' => 'object',
18 | 'subtype' => 'widget',
19 | 'owner_guid' => $group->guid,
20 | 'metadata_name' => 'context',
21 | 'metadata_value' => 'groups',
22 | ]);
23 |
24 | if (!$widgets_count) {
25 | // no widgets = no need for these actions
26 | return;
27 | }
28 |
29 | $access_options = elgg_get_write_access_array(0, false, [
30 | 'container_guid' => $group->guid,
31 | 'value' => $group->access_id,
32 | 'entity_type' => 'object',
33 | 'entity_subtype' => 'widget',
34 | 'purpose' => 'groups_widget_access',
35 | ]);
36 |
37 | if (empty($access_options)) {
38 | return;
39 | }
40 |
41 | $content = elgg_view('output/longtext', [
42 | 'value' => elgg_echo('widget_manager:forms:groups_widget_access:description'),
43 | ]);
44 |
45 | foreach ($access_options as $access_id => $option) {
46 | $content .= elgg_view('output/url', [
47 | 'href' => elgg_generate_action_url('widget_manager/groups/update_widget_access', [
48 | 'widget_access_level' => $access_id,
49 | 'group_guid' => $group->guid,
50 | ]),
51 | 'text' => $option,
52 | 'class' => [
53 | 'elgg-button',
54 | 'elgg-button-action',
55 | ],
56 | ]);
57 | }
58 |
59 | echo elgg_view_module('info', elgg_echo('widget_manager:forms:groups_widget_access:title'), $content);
60 |
--------------------------------------------------------------------------------
/views/default/widget_manager/site.css:
--------------------------------------------------------------------------------
1 | .widget_manager_hide_header {
2 | > .elgg-head {
3 | display: none;
4 | }
5 |
6 | &.widget_manager_hide_header_admin:hover {
7 | > .elgg-head {
8 | display: flex;
9 | }
10 | }
11 | }
12 |
13 | .elgg-module-widget {
14 | &.widget_manager_disable_widget_content_style {
15 | background: none;
16 | border: 0px;
17 |
18 | .elgg-widget-content {
19 | padding: 0px;
20 | }
21 |
22 | &:hover {
23 | box-shadow: none;
24 | }
25 | }
26 | }
27 |
28 | .elgg-widgets-grid {
29 | flex-wrap: wrap;
30 | }
31 |
32 | .elgg-widgets-hide-content .elgg-module-widget {
33 | >.elgg-head .elgg-widget-title {
34 | display: block;
35 | }
36 |
37 | > .elgg-body {
38 | visibility: hidden;
39 | height: 0px;
40 | }
41 | }
42 |
43 | .widgets-fluid-columns {
44 | .elgg-widgets-grid {
45 | display: block;
46 | visibility: hidden;
47 | }
48 |
49 | .elgg-widgets {
50 | padding: 0;
51 | position: relative;
52 | }
53 |
54 | .elgg-module-widget {
55 | position: absolute;
56 | margin: 14px;
57 |
58 | &.muuri-item-dragging {
59 | z-index: 3;
60 | }
61 | &.muuri-item-releasing {
62 | z-index: 2;
63 | }
64 | &.muuri-item-hidden {
65 | z-index: 0;
66 | }
67 | }
68 |
69 | .fluid-placeholder {
70 | border: 3px dashed $(border-color-mild);
71 | }
72 | }
73 |
74 | @media $(media-desktop-up) {
75 | .elgg-widgets {
76 | flex-basis: 0;
77 |
78 | &.col-width-25 {
79 | flex-grow: 25;
80 | }
81 | &.col-width-33 {
82 | flex-grow: 33;
83 | }
84 | &.col-width-40 {
85 | flex-grow: 40;
86 | }
87 | &.col-width-50 {
88 | flex-grow: 50;
89 | }
90 | &.col-width-60 {
91 | flex-grow: 60;
92 | }
93 | &.col-width-75 {
94 | flex-grow: 75;
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/views/default/resources/widget_manager/widget_page.php:
--------------------------------------------------------------------------------
1 | 'object',
9 | 'subtype' => \WidgetPage::SUBTYPE,
10 | 'metadata_name_value_pairs' => ['url' => $handler],
11 | 'limit' => 1,
12 | ]);
13 |
14 | if (empty($pages)) {
15 | throw new EntityNotFoundException();
16 | }
17 |
18 | $widget_page = $pages[0];
19 |
20 | elgg_push_context('index');
21 | elgg_set_page_owner_guid($widget_page->guid);
22 |
23 | if ($widget_page->canEdit()) {
24 | $href = elgg_generate_url('widgets:add_panel', [
25 | 'context' => elgg_get_context(),
26 | 'context_stack' => elgg_get_context_stack(),
27 | 'show_access' => true,
28 | 'owner_guid' => elgg_get_page_owner_guid(),
29 | ]);
30 |
31 | elgg_register_menu_item('title', [
32 | 'name' => 'widgets_add',
33 | 'href' => false,
34 | 'icon' => 'plus',
35 | 'text' => elgg_echo('widgets:add'),
36 | 'link_class' => ['elgg-lightbox', 'elgg-button', 'elgg-button-action'],
37 | 'data-colorbox-opts' => json_encode([
38 | 'href' => $href,
39 | 'maxWidth' => '900px',
40 | 'maxHeight' => '90%',
41 | ]),
42 | ]);
43 |
44 | elgg_register_menu_item('title', [
45 | 'name' => 'hide-widget-contents',
46 | 'link_class' => ['elgg-button', 'elgg-button-action'],
47 | 'text' => elgg_echo('widget_manager:layout:content:hide'),
48 | 'icon' => 'eye-slash',
49 | 'href' => false,
50 | 'priority' => 80,
51 | ]);
52 |
53 | elgg_register_menu_item('title', [
54 | 'name' => 'show-widget-contents',
55 | 'link_class' => ['elgg-button', 'elgg-button-action'],
56 | 'item_class' => 'hidden',
57 | 'text' => elgg_echo('widget_manager:layout:content:show'),
58 | 'icon' => 'eye',
59 | 'href' => false,
60 | 'priority' => 81,
61 | ]);
62 | }
63 |
64 | echo elgg_view_page($widget_page->getDisplayName(), [
65 | 'title' => $widget_page->title ?: false,
66 | 'content' => elgg_view_entity($widget_page),
67 | 'entity' => $widget_page,
68 | ]);
69 |
--------------------------------------------------------------------------------
/views/default/forms/widgets/save.php:
--------------------------------------------------------------------------------
1 | handler}/edit")) {
17 | $custom_form_section = elgg_view("widgets/{$widget->handler}/edit", ['entity' => $widget]);
18 | }
19 |
20 | $access = '';
21 | if (elgg_extract('show_access', $vars, true)) {
22 | $access = elgg_view_field([
23 | '#type' => 'access',
24 | '#label' => elgg_echo('access'),
25 | 'name' => 'params[access_id]',
26 | 'value' => $widget->access_id,
27 | 'entity' => $widget,
28 | ]);
29 | }
30 |
31 | $basic_content = $custom_form_section . $access;
32 |
33 | $advanced_content = elgg_view('forms/widgets/advanced', [
34 | 'entity' => $widget,
35 | 'widget_context' => $widget->context,
36 | ]);
37 |
38 | if (empty($advanced_content)) {
39 | echo $basic_content;
40 | } elseif (empty($basic_content)) {
41 | echo $advanced_content;
42 | } else {
43 | echo elgg_view('page/components/tabs', [
44 | 'class' => 'widget-settings',
45 | 'tabs' => [
46 | [
47 | 'text' => elgg_echo('settings'),
48 | 'content' => $basic_content,
49 | 'selected' => true,
50 | ],
51 | [
52 | 'text' => elgg_echo('widget_manager:widgets:edit:advanced'),
53 | 'content' => $advanced_content,
54 | ],
55 | ],
56 | ]);
57 |
58 | elgg_import_esm('forms/widgets/advanced');
59 | }
60 |
61 | echo elgg_view_field([
62 | '#type' => 'hidden',
63 | 'name' => 'guid',
64 | 'value' => $widget->guid,
65 | ]);
66 |
67 | if (elgg_in_context('default_widgets')) {
68 | echo elgg_view_field([
69 | '#type' => 'hidden',
70 | 'name' => 'default_widgets',
71 | 'value' => 1,
72 | ]);
73 | }
74 |
75 | $footer = elgg_view_field([
76 | '#type' => 'submit',
77 | 'text' => elgg_echo('save'),
78 | ]);
79 |
80 | elgg_set_form_footer($footer);
81 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Menus/AdminHeader.php:
--------------------------------------------------------------------------------
1 | getValue();
25 | foreach ($return_value as $menu_item) {
26 | if ($menu_item->getName() == 'default_widgets') {
27 | // move defaultwidgets menu item
28 | $menu_item->setParentName('widgets');
29 | }
30 | }
31 |
32 | $return_value[] = \ElggMenuItem::factory([
33 | 'name' => 'widgets',
34 | 'text' => elgg_echo('admin:widgets'),
35 | 'href' => false,
36 | 'parent_name' => 'configure',
37 | ]);
38 |
39 | $return_value[] = \ElggMenuItem::factory([
40 | 'name' => 'widgets:manage',
41 | 'href' => 'admin/widgets/manage',
42 | 'text' => elgg_echo('admin:widgets:manage'),
43 | 'parent_name' => 'widgets',
44 | ]);
45 | $return_value[] = \ElggMenuItem::factory([
46 | 'name' => 'widgets:cleanup',
47 | 'href' => 'admin/widgets/cleanup',
48 | 'text' => elgg_echo('admin:widgets:cleanup'),
49 | 'parent_name' => 'widgets',
50 | ]);
51 | $return_value[] = \ElggMenuItem::factory([
52 | 'name' => 'widgets:pages',
53 | 'href' => 'admin/widgets/pages',
54 | 'text' => elgg_echo('admin:widgets:pages'),
55 | 'parent_name' => 'widgets',
56 | ]);
57 |
58 | if (elgg_get_plugin_setting('custom_index', 'widget_manager') == '1|0') {
59 | // a special link to manage homepages that are only available if logged out
60 | $return_value[] = \ElggMenuItem::factory([
61 | 'name' => 'admin:widgets:manage:index',
62 | 'href' => elgg_get_site_url() . '?override=true',
63 | 'text' => elgg_echo('admin:widgets:manage:index'),
64 | 'parent_name' => 'widgets',
65 | ]);
66 | }
67 |
68 | return $return_value;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Bootstrap.php:
--------------------------------------------------------------------------------
1 | registerIndexRoute();
17 | $this->registerWidgetPagesRoutes();
18 | }
19 |
20 | /**
21 | * Conditionally registers a route for the index page
22 | *
23 | * @return void
24 | */
25 | protected function registerIndexRoute(): void {
26 | // @todo check -> need later priority to win over walledgarden
27 | $setting = elgg_get_plugin_setting('custom_index', 'widget_manager');
28 | if (empty($setting)) {
29 | return;
30 | }
31 |
32 | list($non_loggedin, $loggedin) = explode('|', $setting);
33 |
34 | if ((!elgg_is_logged_in() && !empty($non_loggedin)) || (elgg_is_logged_in() && !empty($loggedin)) || (elgg_is_admin_logged_in() && (get_input('override') == true))) {
35 | elgg_register_route('index', [
36 | 'path' => '/',
37 | 'resource' => 'widget_manager/custom_index',
38 | ]);
39 | }
40 | }
41 |
42 | /**
43 | * Registers routes for the extra contexts pages
44 | *
45 | * @return void
46 | */
47 | protected function registerWidgetPagesRoutes(): void {
48 | $urls = $this->getWidgetPagesUrls();
49 |
50 | foreach ($urls as $url) {
51 | elgg_register_route($url, [
52 | 'path' => $url,
53 | 'resource' => 'widget_manager/widget_page',
54 | ]);
55 | }
56 | }
57 |
58 | /**
59 | * Returns array of widget page urls
60 | *
61 | * @return array
62 | */
63 | protected function getWidgetPagesUrls(): array {
64 | $urls = elgg_load_system_cache('widget_pages');
65 | if ($urls) {
66 | return $urls;
67 | }
68 |
69 | $urls = [];
70 |
71 | $metadata = elgg_get_metadata([
72 | 'type' => 'object',
73 | 'subtype' => \WidgetPage::SUBTYPE,
74 | 'limit' => false,
75 | 'batch' => true,
76 | 'metadata_name' => 'url',
77 | ]);
78 |
79 | foreach ($metadata as $md) {
80 | $urls[] = $md->value;
81 | }
82 |
83 | elgg_save_system_cache('widget_pages', $urls);
84 |
85 | return $urls;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/actions/widget_manager/manage_widgets.php:
--------------------------------------------------------------------------------
1 | $widget_config) {
32 | $configured_widget = elgg_extract($widget_id, $configured_widgets);
33 | if (empty($configured_widget)) {
34 | unset($setting[$widget_id]);
35 | continue;
36 | }
37 |
38 | // only store if different
39 | if (isset($widget_config['multiple'])) {
40 | if ((bool) $widget_config['multiple'] == (bool) $configured_widget->originals['multiple']) {
41 | unset($setting[$widget_id]['multiple']);
42 | } else {
43 | $setting[$widget_id]['multiple'] = 1;
44 | }
45 | }
46 |
47 | if (isset($widget_config['contexts'])) {
48 | foreach ($widget_config['contexts'] as $context => $context_config) {
49 | foreach ($context_config as $option => $value) {
50 | $setting[$widget_id]['contexts'][$context][$option] = $value;
51 | }
52 | }
53 | }
54 | }
55 |
56 | $plugin = elgg_get_plugin_from_id('widget_manager');
57 | $plugin->setSetting('widgets_config', json_encode($setting));
58 |
59 | return elgg_ok_response('', elgg_echo('widget_manager:action:manage:success'));
60 |
--------------------------------------------------------------------------------
/views/default/resources/widgets/add_panel.mjs:
--------------------------------------------------------------------------------
1 | import 'jquery';
2 | import elgg from 'elgg';
3 | import Ajax from 'elgg/Ajax';
4 |
5 | /**
6 | * Adds a new widget
7 | *
8 | * Makes Ajax call to add new widget and inserts the widget html
9 | *
10 | * @param {Object} event
11 | * @return void
12 | */
13 | function addWidget(event) {
14 | event.preventDefault();
15 |
16 | var $item = $(this).closest('li');
17 |
18 | // if multiple instances not allow, disable this widget type add button
19 | if (!$item.is('.elgg-widget-multiple')) {
20 | $item.toggleClass('elgg-state-unavailable elgg-state-available');
21 | }
22 |
23 | var href = $(this).attr('href');
24 | var ajax = new Ajax();
25 |
26 | ajax.path(href).done(function(output) {
27 | var query_parts = elgg.parse_url(href, 'query', true);
28 | var selector = '';
29 | var context = query_parts['context'];
30 | var page_owner_guid = query_parts['page_owner_guid'];
31 | var new_widget_column = query_parts['new_widget_column'] || 1;
32 | var new_widget_position = query_parts['new_widget_position'] || 'top';
33 |
34 | if (context && page_owner_guid) {
35 | // target the correct widget layout
36 | selector = '.elgg-layout-widgets-' + context + '[data-page-owner-guid="' + page_owner_guid + '"] .elgg-widget-col-' + new_widget_column;
37 | } else {
38 | selector = '.elgg-widget-col-' + new_widget_column;
39 | }
40 |
41 | if (new_widget_position === 'top') {
42 | $(selector).prepend(output);
43 | } else {
44 | $(selector).append(output);
45 | }
46 |
47 | var $layout = $(selector).closest('.elgg-layout-widgets');
48 | $layout.trigger({
49 | type: 'widgetAdd',
50 | layout: $layout
51 | });
52 | });
53 | }
54 |
55 | $(document).on('click', '.elgg-widgets-add-panel .elgg-widgets-add-actions .elgg-button-submit', addWidget);
56 |
57 | $(document).on('keyup', '.elgg-widgets-add-panel input[name="widget_search"]', function() {
58 | var $container = $('.elgg-widgets-add-panel');
59 | var $items = $container.find('> .elgg-body > ul > li');
60 | var q = $(this).val();
61 |
62 | if (q === '') {
63 | $items.show();
64 | } else {
65 | $items.hide();
66 | $items.filter(function () {
67 | return $(this).text().toUpperCase().indexOf(q.toUpperCase()) >= 0;
68 | }).show();
69 | }
70 | });
71 |
--------------------------------------------------------------------------------
/views/default/object/widget.php:
--------------------------------------------------------------------------------
1 | getSetting($widget->handler, 'hide', (string) $widget->context)) {
24 | return true;
25 | }
26 |
27 | $widget_instance = preg_replace('/[^a-z0-9-]/i', '-', "elgg-widget-instance-{$widget->handler}");
28 | $widget_class = elgg_extract_class($vars, $widget_instance);
29 |
30 | $can_edit = $widget->canEdit();
31 | if ($can_edit) {
32 | $widget_class[] = 'elgg-state-draggable';
33 |
34 | if (!elgg_view_exists("widgets/{$widget->handler}/edit") && elgg_extract('show_access', $vars) === false) {
35 | // store for determining the edit menu item
36 | $vars['show_edit'] = false;
37 | }
38 | }
39 |
40 | if ($widget->widget_manager_custom_class) {
41 | $widget_class[] = $widget->widget_manager_custom_class; // optional custom class for this widget
42 | }
43 |
44 | if ($widget->widget_manager_hide_header == 'yes') {
45 | $widget_class[] = 'widget_manager_hide_header';
46 | if ($can_edit) {
47 | $widget_class[] = 'widget_manager_hide_header_admin';
48 | }
49 | }
50 |
51 | if ($widget->widget_manager_disable_widget_content_style == 'yes') {
52 | $widget_class[] = 'widget_manager_disable_widget_content_style';
53 | }
54 |
55 | if ($widget instanceof WidgetManagerWidget && WidgetsSettingsConfig::instance()->showLazyLoaded($widget, (array) elgg_extract('layout_info', $vars, []))) {
56 | elgg_import_esm('widget_manager/lazy_loading');
57 | $body = elgg_view('graphics/ajax_loader', ['hidden' => false]);
58 | $widget_class[] = 'lazy-loading';
59 | } else {
60 | $body = elgg_view('object/widget/body', $vars);
61 | }
62 |
63 | echo elgg_view_module('widget', '', $body, [
64 | 'class' => $widget_class,
65 | 'id' => "elgg-widget-{$widget->guid}",
66 | 'header' => elgg_view('object/widget/header', $vars),
67 | ]);
68 |
--------------------------------------------------------------------------------
/views/default/forms/widgets/advanced.php:
--------------------------------------------------------------------------------
1 | 'text',
11 | '#label' => elgg_echo('widget_manager:widgets:edit:custom_title'),
12 | 'name' => 'params[widget_manager_custom_title]',
13 | 'value' => $widget->widget_manager_custom_title,
14 | ],
15 | [
16 | '#type' => 'text',
17 | '#label' => elgg_echo('widget_manager:widgets:edit:custom_url'),
18 | 'name' => 'params[widget_manager_custom_url]',
19 | 'value' => $widget->widget_manager_custom_url,
20 | ],
21 | [
22 | '#type' => 'text',
23 | '#label' => elgg_echo('widget_manager:widgets:edit:custom_more_title'),
24 | 'name' => 'params[widget_manager_custom_more_title]',
25 | 'value' => $widget->widget_manager_custom_more_title,
26 | ],
27 | [
28 | '#type' => 'text',
29 | '#label' => elgg_echo('widget_manager:widgets:edit:custom_more_url'),
30 | 'name' => 'params[widget_manager_custom_more_url]',
31 | 'value' => $widget->widget_manager_custom_more_url,
32 | ],
33 | ];
34 |
35 | $advanced_context = elgg_trigger_event_results('advanced_context', 'widget_manager', ['entity' => $widget], ['index']);
36 |
37 | if (is_array($advanced_context) && in_array($widget_context, $advanced_context)) {
38 | $fields[] = [
39 | '#type' => 'checkbox',
40 | '#label' => elgg_echo('widget_manager:widgets:edit:hide_header'),
41 | 'name' => 'params[widget_manager_hide_header]',
42 | 'checked' => $widget->widget_manager_hide_header === 'yes',
43 | 'switch' => true,
44 | 'default' => 'no',
45 | 'value' => 'yes',
46 | ];
47 | $fields[] = [
48 | '#type' => 'checkbox',
49 | '#label' => elgg_echo('widget_manager:widgets:edit:disable_widget_content_style'),
50 | 'name' => 'params[widget_manager_disable_widget_content_style]',
51 | 'checked' => $widget->widget_manager_disable_widget_content_style === 'yes',
52 | 'switch' => true,
53 | 'default' => 'no',
54 | 'value' => 'yes',
55 | ];
56 | $fields[] = [
57 | '#type' => 'text',
58 | '#label' => elgg_echo('widget_manager:widgets:edit:custom_class'),
59 | 'name' => 'params[widget_manager_custom_class]',
60 | 'value' => $widget->widget_manager_custom_class,
61 | ];
62 |
63 | if ((bool) elgg_get_plugin_setting('lazy_loading_enabled', 'widget_manager') && !WidgetsSettingsConfig::instance()->getSetting($widget->handler, 'always_lazy_load', (string) $widget->context)) {
64 | $fields[] = [
65 | '#type' => 'switch',
66 | '#label' => elgg_echo('widget_manager:widgets:edit:lazy_load_content'),
67 | 'name' => 'params[widget_manager_lazy_load_content]',
68 | 'value' => $widget->widget_manager_lazy_load_content,
69 | ];
70 | }
71 | }
72 |
73 | echo elgg_view_field([
74 | '#type' => 'fieldset',
75 | 'fields' => $fields,
76 | ]);
77 |
--------------------------------------------------------------------------------
/views/default/forms/widget_manager/widget_page.php:
--------------------------------------------------------------------------------
1 | canEdit()) {
5 | throw new \Elgg\Exceptions\Http\EntityPermissionsException();
6 | }
7 |
8 | echo elgg_format_element('div', ['class' => 'elgg-subtext mbm'], elgg_echo('widget_manager:settings:extra_contexts:description'));
9 |
10 | echo elgg_view_field([
11 | '#type' => 'hidden',
12 | 'name' => 'guid',
13 | 'value' => $entity?->guid,
14 | ]);
15 |
16 | if (elgg_is_admin_logged_in()) {
17 | echo elgg_view_field([
18 | '#type' => 'text',
19 | '#label' => elgg_echo('widget_manager:settings:extra_contexts:page'),
20 | 'name' => 'url',
21 | 'value' => $entity?->url,
22 | 'required' => true,
23 | ]);
24 | }
25 |
26 | echo elgg_view_field([
27 | '#type' => 'text',
28 | '#label' => elgg_echo('title'),
29 | '#help' => elgg_echo('widget_manager:widget_page:title:help'),
30 | 'name' => 'title',
31 | 'value' => $entity?->title,
32 | ]);
33 |
34 | echo elgg_view_field([
35 | '#type' => 'longtext',
36 | '#label' => elgg_echo('description'),
37 | 'name' => 'description',
38 | 'value' => $entity?->description,
39 | ]);
40 |
41 | echo elgg_view_field([
42 | '#type' => 'checkbox',
43 | '#label' => elgg_echo('widget_manager:widget_page:show_description'),
44 | '#help' => elgg_echo('widget_manager:widget_page:show_description:help'),
45 | 'name' => 'show_description',
46 | 'checked' => $entity?->show_description !== false,
47 | 'switch' => true,
48 | 'default' => false,
49 | 'value' => true,
50 | ]);
51 |
52 | echo elgg_view_field([
53 | '#type' => 'select',
54 | '#label' => elgg_echo('widget_manager:settings:extra_contexts:layout'),
55 | '#help' => elgg_echo('widget_manager:settings:extra_contexts:layout:help'),
56 | 'name' => 'layout',
57 | 'options_values' => [
58 | '33|33|33' => elgg_echo('widget_manager:settings:widget_layout:33|33|33'),
59 | '50|25|25' => elgg_echo('widget_manager:settings:widget_layout:50|25|25'),
60 | '25|50|25' => elgg_echo('widget_manager:settings:widget_layout:25|50|25'),
61 | '25|25|50' => elgg_echo('widget_manager:settings:widget_layout:25|25|50'),
62 | '75|25' => elgg_echo('widget_manager:settings:widget_layout:75|25'),
63 | '60|40' => elgg_echo('widget_manager:settings:widget_layout:60|40'),
64 | '50|50' => elgg_echo('widget_manager:settings:widget_layout:50|50'),
65 | '40|60' => elgg_echo('widget_manager:settings:widget_layout:40|60'),
66 | '25|75' => elgg_echo('widget_manager:settings:widget_layout:25|75'),
67 | '100' => elgg_echo('widget_manager:settings:widget_layout:100'),
68 | ],
69 | 'value' => $entity?->layout,
70 | ]);
71 |
72 | echo elgg_view_field([
73 | '#type' => 'userpicker',
74 | '#label' => elgg_echo('widget_manager:settings:extra_contexts:manager'),
75 | '#help' => elgg_echo('widget_manager:settings:extra_contexts:manager:help'),
76 | 'name' => 'manager',
77 | 'value' => $entity?->getManagers(),
78 | 'show_friends' => false,
79 | ]);
80 |
81 | $footer = elgg_view_field([
82 | '#type' => 'submit',
83 | 'text' => $entity ? elgg_echo('update') : elgg_echo('save'),
84 | ]);
85 |
86 | elgg_set_form_footer($footer);
87 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Seeder.php:
--------------------------------------------------------------------------------
1 | advance($this->getCount());
31 |
32 | $site = elgg_get_site_entity();
33 |
34 | while ($this->getCount() < $this->limit) {
35 | try {
36 | /* @var $entity \WidgetPage */
37 | $entity = $this->createObject([
38 | 'subtype' => \WidgetPage::SUBTYPE,
39 | 'owner_guid' => $site->guid,
40 | 'container_guid' => $site->guid,
41 | 'layout' => $this->getRandomLayout(),
42 | ]);
43 | } catch (MaxAttemptsException $e) {
44 | // unable to create with the given options
45 | continue;
46 | }
47 |
48 | unset($entity->description);
49 |
50 | $entity->url = 'widgets-' . elgg_get_friendly_title($entity->title);
51 |
52 | $user_guids = [];
53 | for ($i = 0; $i < $this->faker()->numberBetween(0, 5); $i++) {
54 | $user = $this->getRandomUser($user_guids);
55 | $user_guids[] = $user->guid;
56 |
57 | $entity->addManager($user);
58 | }
59 |
60 | $this->advance();
61 | }
62 |
63 | // need to invalidate the cache of widget page urls
64 | elgg_delete_system_cache('widget_pages');
65 | }
66 |
67 | /**
68 | * {@inheritdoc}
69 | */
70 | public function unseed() {
71 | /* @var $entities \ElggBatch */
72 | $entities = elgg_get_entities([
73 | 'type' => 'object',
74 | 'subtype' => \WidgetPage::SUBTYPE,
75 | 'metadata_name' => '__faker',
76 | 'limit' => false,
77 | 'batch' => true,
78 | 'batch_inc_offset' => false,
79 | ]);
80 |
81 | /* @var $entity \WidgetPage */
82 | foreach ($entities as $entity) {
83 | if ($entity->delete()) {
84 | $this->log("Deleted widget page {$entity->guid}");
85 | } else {
86 | $this->log("Failed to delete widget page {$entity->guid}");
87 | $entities->reportFailure();
88 | continue;
89 | }
90 |
91 | $this->advance();
92 | }
93 |
94 | // need to invalidate the cache of widget page urls
95 | elgg_delete_system_cache('widget_pages');
96 | }
97 |
98 | /**
99 | * {@inheritdoc}
100 | */
101 | public static function getType(): string {
102 | return \WidgetPage::SUBTYPE;
103 | }
104 |
105 | /**
106 | * {@inheritdoc}
107 | */
108 | public static function getDefaultLimit(): int {
109 | return 5;
110 | }
111 |
112 | /**
113 | * {@inheritdoc}
114 | */
115 | protected function getCountOptions(): array {
116 | return [
117 | 'type' => 'object',
118 | 'subtype' => \WidgetPage::SUBTYPE,
119 | ];
120 | }
121 |
122 | /**
123 | * Get a random layout to add to the widget page
124 | *
125 | * @return string
126 | */
127 | protected function getRandomLayout(): string {
128 | $key = array_rand($this->layouts);
129 |
130 | return $this->layouts[$key];
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/views/default/widget_manager/fluid.mjs:
--------------------------------------------------------------------------------
1 | import 'jquery';
2 | import 'elgg/widgets';
3 | import 'muuri';
4 | import Ajax from 'elgg/Ajax';
5 |
6 | var grid;
7 | var grid_selector = '.widgets-fluid-columns .elgg-widget-col-1';
8 | var grid_options = {
9 | items: '.elgg-module-widget',
10 | dragEnabled: false,
11 | dragPlaceholder: {
12 | enabled: true,
13 | },
14 | itemPlaceholderClass: 'fluid-placeholder',
15 | layoutDuration: 0,
16 | showDuration: 0,
17 | layoutOnInit: false,
18 | dragHandle: '.elgg-widget-handle',
19 | dragStartPredicate: {
20 | distance: 10,
21 | delay: 10
22 | },
23 | };
24 |
25 | if ($('.widgets-fluid-columns.elgg-layout-can-edit').length) {
26 | grid_options.dragEnabled = true;
27 | }
28 |
29 | var ajax = new Ajax();
30 |
31 | // Returns a function, that, as long as it continues to be invoked, will not
32 | // be triggered. The function will be called after it stops being called for
33 | // N milliseconds. If `immediate` is passed, trigger the function on the
34 | // leading edge, instead of the trailing.
35 | var debounce = function(func, wait, immediate) {
36 | var timeout;
37 | return function() {
38 | var context = this, args = arguments;
39 | var later = function() {
40 | timeout = null;
41 | if (!immediate) func.apply(context, args);
42 | };
43 | var callNow = immediate && !timeout;
44 | clearTimeout(timeout);
45 | timeout = setTimeout(later, wait);
46 | if (callNow) func.apply(context, args);
47 | };
48 | }
49 |
50 | function setItemSizes($elem) {
51 | var container_width = $elem.width();
52 |
53 | var $widgets = $elem.find('.elgg-module-widget');
54 | if (container_width > 1200) {
55 | $widgets.css('width', 'calc(33% - 28px)');
56 | } else if (container_width > 800) {
57 | $widgets.css('width', 'calc(50% - 28px)');
58 | } else {
59 | $widgets.css('width', 'calc(100% - 28px)');
60 | }
61 | }
62 |
63 | function gridcheck() {
64 | if (grid) {
65 | setTimeout(function() {
66 | $(grid_selector).each(function() {
67 | setItemSizes($(this));
68 | grid.refreshItems().layout();
69 | });
70 | }, 200);
71 | }
72 | }
73 |
74 | function initGrid() {
75 | setTimeout(function() {
76 | // added bit of delay to allow images to load (also required for correct working of delete event)
77 | if (grid) {
78 | grid.destroy();
79 | }
80 |
81 | setItemSizes($(grid_selector));
82 |
83 | grid = new Muuri(grid_selector, grid_options);
84 |
85 | grid.on('dragEnd', function (item, event) {
86 | if (event.distance === 0) {
87 | return;
88 | }
89 |
90 | // update dom with new positions
91 | grid.synchronize();
92 |
93 | var guids = [];
94 | $.each(grid.getItems(), function(index, item) {
95 | var guidString = $(item._element).attr('id');
96 | guidString = guidString.substr(guidString.indexOf('elgg-widget-') + 'elgg-widget-'.length);
97 |
98 | guids.push(guidString);
99 | });
100 |
101 | ajax.action('widget_manager/fluid_order', {
102 | data: {
103 | guids: guids
104 | }
105 | });
106 | }).on('layoutEnd', function() {
107 | $(grid_selector).css('visibility', 'visible');
108 | }).layout();
109 | }, 200);
110 | }
111 |
112 | initGrid();
113 |
114 | $(window).resize(debounce(gridcheck, 50));
115 |
116 | $(document).on('saveSettings', '.elgg-layout-widgets .elgg-module-widget', gridcheck);
117 | $(document).on('lazyLoaded', '.elgg-layout-widgets', gridcheck);
118 |
119 | $(document).on('widgetAdd widgetRemove', '.elgg-layout-widgets', initGrid);
120 |
121 | // mmenu support
122 | $(document).on('mmenu.toggle', gridcheck);
123 |
--------------------------------------------------------------------------------
/classes/WidgetPage.php:
--------------------------------------------------------------------------------
1 | attributes['subtype'] = self::SUBTYPE;
24 | $this->attributes['access_id'] = ACCESS_PUBLIC;
25 | $this->attributes['owner_guid'] = elgg_get_site_entity()->guid;
26 | $this->attributes['container_guid'] = elgg_get_site_entity()->guid;
27 | }
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function canEdit(int $user_guid = 0): bool {
33 | $user_guid = $user_guid ?: elgg_get_logged_in_user_guid();
34 | if (in_array($user_guid, $this->getManagers())) {
35 | return true;
36 | }
37 |
38 | return parent::canEdit($user_guid);
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function getURL(): string {
45 | return elgg_normalize_url((string) $this->url);
46 | }
47 |
48 | /**
49 | * Adds a manager relationship for this page
50 | *
51 | * @param \ElggUser $user Manager
52 | *
53 | * @return bool
54 | */
55 | public function addManager(\ElggUser $user): bool {
56 | return $user->addRelationship($this->guid, self::MANAGER_RELATIONSHIP);
57 | }
58 |
59 | /**
60 | * Removes a manager relationship for this page
61 | *
62 | * @param \ElggUser $user Manager
63 | *
64 | * @return bool
65 | */
66 | public function removeManager(\ElggUser $user): bool {
67 | return $user->removeRelationship($this->guid, self::MANAGER_RELATIONSHIP);
68 | }
69 |
70 | /**
71 | * Replaces existing managers with a set of new managers
72 | *
73 | * @param array $guids new managers
74 | *
75 | * @return void
76 | */
77 | public function setManagers(array $guids = []): void {
78 | $current = $this->getManagers();
79 | foreach ($current as $guid) {
80 | if (!in_array($guid, $guids)) {
81 | $user = get_user((int) $guid);
82 | if ($user) {
83 | $this->removeManager($user);
84 | }
85 | }
86 | }
87 |
88 | $new_managers = array_diff($guids, $current);
89 | foreach ($new_managers as $guid) {
90 | $user = get_user((int) $guid);
91 | if ($user) {
92 | $this->addManager($user);
93 | }
94 | }
95 | }
96 |
97 | /**
98 | * Returns an array of guids of the current managers
99 | *
100 | * @return array
101 | */
102 | public function getManagers(): array {
103 | return (array) elgg_get_entities([
104 | 'type' => 'user',
105 | 'relationship' => self::MANAGER_RELATIONSHIP,
106 | 'relationship_guid' => $this->guid,
107 | 'inverse_relationship' => true,
108 | 'callback' => function($row) {
109 | return $row->guid;
110 | },
111 | ]);
112 | }
113 |
114 | /**
115 | * Returns number of columns based on layout config
116 | *
117 | * @return int
118 | */
119 | public function getNumColumns(): int {
120 | return count(explode('|', $this->layout));
121 | }
122 |
123 | /**
124 | * {@inheritdoc}
125 | */
126 | public function getDisplayName(): string {
127 | if (empty($this->title)) {
128 | return ucwords(str_replace('_', ' ', $this->url));
129 | }
130 |
131 | return parent::getDisplayName();
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/views/default/forms/widget_manager/manage_widgets/widget.php:
--------------------------------------------------------------------------------
1 | originals;
12 |
13 | $body = elgg_view('output/longtext', ['value' => $widget_definition->description]);
14 |
15 | $body .= '
';
16 | $body .= '';
17 | $body .= '| ' . elgg_echo('widget_manager:forms:manage_widgets:context') . ' | ';
18 | $body .= '' . elgg_echo('widget_manager:forms:manage_widgets:can_add') . ' | ';
19 | $body .= '' . elgg_echo('hide') . ' | ';
20 | $body .= '' . elgg_echo('widget_manager:forms:manage_widgets:always_lazy_load') . ' | ';
21 | $body .= '
';
22 | foreach ($contexts as $context) {
23 | $body .= '| ';
24 |
25 | $options = [
26 | 'name' => "widgets_config[{$widget_definition->id}][contexts][{$context}][enabled]",
27 | 'label' => $context,
28 | 'value' => 1,
29 | 'switch' => true,
30 | 'checked' => in_array($context, $widget_definition->context),
31 | 'label_class' => [],
32 | ];
33 |
34 | if (!in_array($context, $originals['context'])) {
35 | $options['label_class'][] = 'widget-manager-unsupported-context';
36 | }
37 |
38 | if (in_array($context, $widget_definition->context)) {
39 | if (!in_array($context, $originals['context'])) {
40 | $options['label_class'][] = 'widget-manager-manage-widgets-non-default';
41 | $options['title'] = elgg_echo('widget_manager:forms:manage_widgets:non_default');
42 | }
43 | } elseif (in_array($context, $originals['context'])) {
44 | $options['label_class'][] = 'widget-manager-manage-widgets-non-default';
45 | $options['title'] = elgg_echo('widget_manager:forms:manage_widgets:non_default');
46 | }
47 |
48 | $body .= elgg_view('input/checkbox', $options);
49 | $body .= ' | ';
50 | $body .= elgg_view('input/checkbox', [
51 | 'name' => "widgets_config[{$widget_definition->id}][contexts][{$context}][can_add]",
52 | 'value' => 1,
53 | 'checked' => WidgetsSettingsConfig::instance()->getSetting($widget_definition->id, 'can_add', $context),
54 | ]);
55 | $body .= ' | ';
56 | $body .= elgg_view('input/checkbox', [
57 | 'name' => "widgets_config[{$widget_definition->id}][contexts][{$context}][hide]",
58 | 'value' => 1,
59 | 'checked' => WidgetsSettingsConfig::instance()->getSetting($widget_definition->id, 'hide', $context),
60 | ]);
61 |
62 | $body .= ' | ';
63 | $body .= elgg_view('input/checkbox', [
64 | 'name' => "widgets_config[{$widget_definition->id}][contexts][{$context}][always_lazy_load]",
65 | 'value' => 1,
66 | 'checked' => WidgetsSettingsConfig::instance()->getSetting($widget_definition->id, 'always_lazy_load', $context),
67 | ]);
68 |
69 | $body .= ' |
';
70 | }
71 |
72 | $body .= '
';
73 |
74 | // multiple
75 | $multiple_options = [
76 | '#type' => 'checkbox',
77 | '#class' => 'widget-manager-manage-widgets-toggle-multiple',
78 | '#label' => elgg_echo('widget_manager:forms:manage_widgets:multiple'),
79 | 'name' => "widgets_config[{$widget_definition->id}][multiple]",
80 | 'value' => 1,
81 | 'switch' => true,
82 | 'checked' => $widget_definition->multiple,
83 | ];
84 |
85 | if ($widget_definition->multiple !== $originals['multiple']) {
86 | $multiple_options['label_class'] = 'widget-manager-manage-widgets-non-default';
87 | $multiple_options['title'] = elgg_echo('widget_manager:forms:manage_widgets:non_default');
88 | }
89 |
90 | echo elgg_view_module('info', $widget_definition->name . ' [' . $widget_definition->id . ']', $body, [
91 | 'menu' => elgg_view_field($multiple_options),
92 | ]);
93 |
--------------------------------------------------------------------------------
/views/default/elgg/widgets.mjs:
--------------------------------------------------------------------------------
1 | import 'jquery';
2 | import 'jquery-ui';
3 | import Ajax from 'elgg/Ajax';
4 | import lightbox from 'elgg/lightbox';
5 |
6 | /**
7 | * Persist the widget's new position
8 | *
9 | * @param {Object} event
10 | * @param {Object} ui
11 | *
12 | * @return void
13 | */
14 | function moveWidget(event, ui) {
15 | // elgg-widget-
16 | var guidString = ui.item.attr('id');
17 | guidString = guidString.substring(guidString.indexOf('elgg-widget-') + "elgg-widget-".length);
18 |
19 | var ajax = new Ajax(false);
20 | ajax.action('widgets/move', {
21 | data: {
22 | widget_guid: guidString,
23 | column: ui.item.parent().data('widgetColumn'),
24 | position: ui.item.index()
25 | }
26 | });
27 | };
28 |
29 | /**
30 | * Removes a widget from the layout
31 | *
32 | * Event callback the uses Ajax to delete the widget and removes its HTML
33 | *
34 | * @param {Object} event
35 | * @return void
36 | */
37 | function removeWidget(event) {
38 | event.preventDefault();
39 |
40 | var $layout = $(this).closest('.elgg-layout-widgets');
41 | var $widget = $(this).closest('.elgg-module-widget');
42 | $widget.remove();
43 |
44 | // delete the widget through ajax
45 | var ajax = new Ajax(false);
46 | ajax.action($(this).attr('href'));
47 |
48 | $layout.trigger({
49 | type: 'widgetRemove',
50 | layout: $layout,
51 | widget: $widget
52 | });
53 | };
54 |
55 | /**
56 | * Save a widget's settings
57 | *
58 | * Uses Ajax to save the settings and updates the HTML.
59 | *
60 | * @param {Object} event
61 | * @return void
62 | */
63 | function saveWidgetSettings(event) {
64 | event.preventDefault();
65 |
66 | var guid = $(this).find('[name="guid"]').val();
67 | var $widget = $('#elgg-widget-' + guid);
68 | var $widgetContent = $widget.find('.elgg-widget-content');
69 |
70 | var ajax = new Ajax();
71 | ajax.action('widgets/save', {
72 | data: ajax.objectify(this),
73 | success: function (result) {
74 | lightbox.close();
75 |
76 | $widgetContent.html(result.content);
77 | if (result.title !== '') {
78 | var $widgetTitle = $widget.find('.elgg-widget-title');
79 |
80 | var newWidgetTitle = result.title;
81 | if (result.href !== '') {
82 | newWidgetTitle = "" + newWidgetTitle + "";
83 | }
84 |
85 | $widgetTitle.html(newWidgetTitle);
86 | }
87 |
88 | $widget.trigger({
89 | type: 'saveSettings',
90 | widget: $widget
91 | });
92 | }
93 | });
94 | };
95 |
96 | $('.elgg-layout-widgets:not(.widgets-fluid-columns)').find('.elgg-widgets').each(function() {
97 |
98 | var opts = $(this).data().sortableOptions;
99 | var defaults = {
100 | items: '.elgg-module-widget.elgg-state-draggable',
101 | connectWith: '.elgg-widgets',
102 | handle: '.elgg-widget-handle',
103 | forcePlaceholderSize: true,
104 | placeholder: 'elgg-widget-placeholder',
105 | opacity: 0.8,
106 | revert: 500,
107 | stop: moveWidget
108 | };
109 | var settings = $.extend({}, defaults, opts);
110 |
111 | $(this).sortable(settings);
112 | });
113 |
114 | // regular layouts
115 | $(document).on('click', '.elgg-menu-title-widgets .elgg-menu-item-hide-widget-contents a, .elgg-menu-title-widgets .elgg-menu-item-show-widget-contents a', function() {
116 | var $layout = $(this).closest('.elgg-layout-widgets');
117 | $layout.find('.elgg-menu-item-hide-widget-contents, .elgg-menu-item-show-widget-contents').toggleClass('hidden');
118 |
119 | $layout.toggleClass('elgg-widgets-hide-content');
120 |
121 | return false;
122 | });
123 |
124 | // widget page layouts with a title menu
125 | $(document).on('click', '.elgg-menu-title .elgg-menu-item-hide-widget-contents a, .elgg-menu-title .elgg-menu-item-show-widget-contents a', function() {
126 | $('.elgg-menu-title').find('.elgg-menu-item-hide-widget-contents, .elgg-menu-item-show-widget-contents').toggleClass('hidden');
127 |
128 | $('.elgg-layout-widgets').toggleClass('elgg-widgets-hide-content');
129 |
130 | return false;
131 | });
132 |
133 | $(document).on('click', 'a.elgg-widget-delete-button', removeWidget);
134 | $(document).on('submit', '.elgg-form-widgets-save', saveWidgetSettings);
135 |
--------------------------------------------------------------------------------
/views/default/resources/widgets/add_panel.php:
--------------------------------------------------------------------------------
1 | guid);
33 |
34 | $widgets = elgg_get_widgets($owner->guid, $context);
35 | $widget_types = elgg_get_widget_types([
36 | 'context' => $context,
37 | 'container' => $owner,
38 | ]);
39 | uasort($widget_types, function ($a, $b) {
40 | return strcmp($a->name, $b->name);
41 | });
42 |
43 | $current_handlers = [];
44 | foreach ($widgets as $column_widgets) {
45 | foreach ($column_widgets as $widget) {
46 | $current_handlers[] = $widget->handler;
47 | }
48 | }
49 |
50 | $result = elgg_format_element('p', [
51 | 'class' => 'elgg-text-help',
52 | ], elgg_echo('widgets:add:description'));
53 |
54 | $list_items = '';
55 | $settings_service = WidgetsSettingsConfig::instance();
56 |
57 | foreach ($widget_types as $handler => $widget_type) {
58 | $can_add = $settings_service->getSetting($handler, 'can_add', $context);
59 | $hide = $settings_service->getSetting($handler, 'hide', $context);
60 |
61 | if (!$can_add || $hide) {
62 | // can not add or should be hidden
63 | continue;
64 | }
65 |
66 | $class = [];
67 | // check if widget added and only one instance allowed
68 | if (!$widget_type->multiple && in_array($handler, $current_handlers)) {
69 | $class[] = 'elgg-state-unavailable';
70 | } else {
71 | $class[] = 'elgg-state-available';
72 | }
73 |
74 | $class[] = $widget_type->multiple ? 'elgg-widget-multiple' : 'elgg-widget-single';
75 |
76 | $action = '';
77 | if (!$widget_type->multiple) {
78 | $action .= elgg_format_element('span', ['class' => 'elgg-quiet'], elgg_echo('widget:unavailable'));
79 | }
80 |
81 | $action .= elgg_view('output/url', [
82 | 'class' => ['elgg-button', 'elgg-button-submit', 'elgg-size-small'],
83 | 'text' => elgg_echo('add'),
84 | 'href' => elgg_generate_action_url('widgets/add', [
85 | 'handler' => $handler,
86 | 'page_owner_guid' => $owner_guid,
87 | 'context' => $context,
88 | 'show_access' => elgg_extract('show_access', $vars, get_input('show_access')),
89 | 'default_widgets' => elgg_in_context('default_widgets'),
90 | 'new_widget_column' => $new_widget_column,
91 | 'new_widget_position' => $new_widget_position,
92 | ]),
93 | ]);
94 | $action .= '
';
95 |
96 | $description = elgg_format_element('div', ['class' => 'elgg-loud'], $widget_type->name);
97 |
98 | if ($widget_type->description) {
99 | $description .= elgg_format_element('div', ['class' => 'elgg-quiet'], $widget_type->description);
100 | }
101 |
102 | $description = elgg_format_element('div', [
103 | 'class' => 'elgg-widgets-add-description',
104 | ], $description);
105 |
106 | $item_content = $description . $action;
107 |
108 | $list_items .= elgg_format_element('li', [
109 | 'class' => $class,
110 | 'data-elgg-widget-type' => $handler,
111 | ], $item_content);
112 | }
113 |
114 | $result .= elgg_format_element('ul', [], $list_items);
115 |
116 | $search_box = elgg_view('input/text', [
117 | 'name' => 'widget_search',
118 | 'title' => elgg_echo('search'),
119 | 'placeholder' => elgg_echo('search'),
120 | ]);
121 |
122 | echo elgg_view_module('info', elgg_echo('widgets:add'), $result, [
123 | 'class' => 'elgg-widgets-add-panel',
124 | 'menu' => $search_box,
125 | ]);
126 |
--------------------------------------------------------------------------------
/languages/fr.php:
--------------------------------------------------------------------------------
1 | 'Blocs d\'info',
9 | 'admin:widgets:manage' => 'Gérer',
10 | 'admin:widgets:manage:index' => 'Gérer l\'index',
11 | 'admin:statistics:widgets' => 'Utilisation de blocs d\'infos',
12 | 'widget_manager:widgets:edit:custom_title' => 'Votre titre',
13 | 'widget_manager:widgets:edit:custom_url' => 'Lien de votre titre',
14 | 'widget_manager:widgets:edit:custom_more_title' => 'Plus de texte',
15 | 'widget_manager:widgets:edit:custom_more_url' => 'Plus de liens',
16 | 'widget_manager:widgets:edit:hide_header' => 'Cacher l\'entête',
17 | 'widget_manager:widgets:edit:custom_class' => 'Classe CSS sur mesure',
18 | 'widget_manager:widgets:edit:disable_widget_content_style' => 'Pas de style',
19 | 'widget_manager:groups:enable_widget_manager' => 'Activer la gestion des blocs d\'info',
20 | 'widget_manager:settings:index' => 'Page d\'index',
21 | 'widget_manager:settings:group' => 'Groupe',
22 | 'widget_manager:settings:custom_index' => 'Utiliser Widget Manager custom index?',
23 | 'widget_manager:settings:custom_index:non_loggedin' => 'Pour les utilisateurs non logués seulement',
24 | 'widget_manager:settings:custom_index:loggedin' => 'Pour les utilisateurs logués seulement',
25 | 'widget_manager:settings:custom_index:all' => 'Pour tous les utilisateurs',
26 | 'widget_manager:settings:widget_layout' => 'Choisissez un format de page de widgets',
27 | 'widget_manager:settings:widget_layout:33|33|33' => 'Par défaut (33% par colonne)',
28 | 'widget_manager:settings:widget_layout:50|25|25' => 'Colonne gauche large (50%, 25%, 25%)',
29 | 'widget_manager:settings:widget_layout:25|50|25' => 'Colonne du milieu large (25%, 50%, 25%)',
30 | 'widget_manager:settings:widget_layout:25|25|50' => 'Colonne de droite large (25%, 25%, 50%)',
31 | 'widget_manager:settings:widget_layout:75|25' => '2 colonnes (75%, 25%)',
32 | 'widget_manager:settings:widget_layout:60|40' => '2 colonnes (60%, 40%)',
33 | 'widget_manager:settings:widget_layout:50|50' => '2 colonnes (50%, 50%)',
34 | 'widget_manager:settings:widget_layout:40|60' => '2 colonnes (40%, 60%)',
35 | 'widget_manager:settings:widget_layout:25|75' => '2 colonnes (25%, 75%)',
36 | 'widget_manager:settings:widget_layout:100' => '1 seule colonne (100%)',
37 | 'widget_manager:settings:group:enable' => 'Activer la gestion des blocs d\'infos pour les groupes',
38 | 'widget_manager:settings:group:enable:yes' => 'Oui, gérable par l\'option outils de groupes',
39 | 'widget_manager:settings:group:enable:forced' => 'Oui, toujours actif',
40 | 'widget_manager:settings:group:option_default_enabled' => 'Gestion des blocs d\'infos pour les groupe activé par défaut',
41 | 'widget_manager:settings:group:option_admin_only' => 'Seul l\'administrateur peut activer la gestion des blocs d\'infos dans les groupes',
42 | 'widget_manager:settings:group:force_tool_widgets' => 'Appliquer la gestion des blocs d\'infos pour tous les groupes',
43 | 'widget_manager:settings:group:force_tool_widgets:confirm' => 'Êtes vous sûr/e? Cette action s\'appliquera pour tous les groupes.',
44 | 'widget_manager:settings:extra_contexts:page' => 'Page',
45 | 'widget_manager:settings:extra_contexts:layout' => 'Design',
46 | 'widget_manager:settings:extra_contexts:manager' => 'Gestionnaire',
47 | 'widget_manager:forms:manage_widgets:context' => 'Disponible dans ce contexte',
48 | 'widget_manager:forms:manage_widgets:multiple' => 'Widget multiple autorisé',
49 | 'widget_manager:forms:manage_widgets:non_default' => 'Ce paramétrage est différent de la valeur par défaut',
50 | 'widget_manager:forms:manage_widgets:unsupported_context:confirm' => 'Êtes vous sûr d\'activer ce widget pour ce contexte? Si ce widget ne supporte pas ce contexte, il pourrait survenir des problèmes.',
51 | 'widget_manager:forms:groups_widget_access:title' => 'Accès aux blocs d\'infos',
52 | 'widget_manager:forms:groups_widget_access:description' => 'Cette action vous permet de mettre à jour le niveau d\'accès de tous les blocs d\'infos de ce groupe.',
53 | 'widget_manager:action:manage:success' => 'La configuration des blocs d\'infos a été sauvegardée',
54 | 'widget_manager:action:force_tool_widgets:error:not_enabled' => 'La gestionnaire de blocs d\'info pour les groupes n\'est pas activé',
55 | 'widget_manager:action:force_tool_widgets:success' => 'Activer l\'outil widgets pour les groupes %s',
56 | 'widget_manager:action:groups:update_widget_access:success' => 'L\'accès à tous les blocs d\'infos pour ce groupe a été mis à jour',
57 | 'widget_manager:widgets:edit:advanced' => 'Avancé',
58 | );
59 |
--------------------------------------------------------------------------------
/languages/es.php:
--------------------------------------------------------------------------------
1 | 'Widgets',
9 | 'widget_manager:widgets:edit:advanced' => 'Avanzado',
10 | 'widget_manager:action:groups:update_widget_access:success' => 'Se actualiza el acceso a todos los widgets de este grupo',
11 | 'widget_manager:action:force_tool_widgets:success' => 'Widgets específicos de herramientas forzadas para grupos %s',
12 | 'widget_manager:action:force_tool_widgets:error:not_enabled' => 'La administración de widgets para grupos no está habilitada',
13 | 'widget_manager:action:manage:success' => 'Configuración de widgets guardada correctamente',
14 | 'widget_manager:forms:groups_widget_access:description' => 'Esta acción le permite actualizar el nivel de acceso de todos los widgets de este grupo al nivel de acceso especificado.',
15 | 'widget_manager:forms:groups_widget_access:title' => 'Acceso de Widget',
16 | 'widget_manager:forms:manage_widgets:unsupported_context:confirm' => '¿Estás seguro de que quieres habilitar este widget para este contexto? Si el widget no es compatible con el contexto, esto podría causar problemas.',
17 | 'widget_manager:forms:manage_widgets:non_default' => 'Esta configuración es diferente de la configuración predeterminada',
18 | 'widget_manager:forms:manage_widgets:multiple' => 'Múltiples widgets permitidos',
19 | 'widget_manager:forms:manage_widgets:can_add' => 'Puede ser añadido',
20 | 'widget_manager:forms:manage_widgets:context' => 'Disponible en el contexto',
21 | 'widget_manager:forms:manage_widgets:no_widgets' => 'No hay widgets para administrar',
22 | 'widget_manager:settings:extra_contexts:manager' => 'Administrador',
23 | 'widget_manager:settings:extra_contexts:layout' => 'Diseño',
24 | 'widget_manager:settings:extra_contexts:page' => 'Pagina',
25 | 'widget_manager:settings:extra_contexts:description' => 'Introduzca el nombre del controlador de página de la nueva página que obtendrá un diseño similar al de la página de índice. Puedes agregar tantas páginas como necesites. Asegúrese de no agregar un manejador de página que ya esté en uso. También puede configurar el diseño de columna para esa página y, opcionalmente, asignar usuarios no administradores como administrador de la página introduciendo su nombre de usuario. Puede tener varios administradores separando su nombre de usuario por una coma.',
26 | 'widget_manager:settings:group:force_tool_widgets:confirm' => '¿Estás seguro? Esto agregará / eliminará todos los widgets específicos de una opción de herramienta para todos los grupos (donde la administración de widgets está habilitada).',
27 | 'widget_manager:settings:group:force_tool_widgets' => 'Aplicar widgets de herramientas de grupo',
28 | 'widget_manager:settings:group:option_admin_only' => 'Sólo el administrador puede habilitar widgets de grupo',
29 | 'widget_manager:settings:group:option_default_enabled' => 'Gestión de widgets para grupos activada por defecto',
30 | 'widget_manager:settings:group:enable:forced' => 'Sí, siempre en',
31 | 'widget_manager:settings:group:enable:yes' => 'Sí, manejable por opción de herramienta de grupo',
32 | 'widget_manager:settings:group:enable' => 'Habilitar el Administrador de widgets para grupos',
33 | 'widget_manager:settings:widget_layout:100' => 'Columna simple (100%)',
34 | 'widget_manager:settings:widget_layout:25|75' => 'Dos columnas (25%, 75%)',
35 | 'widget_manager:settings:widget_layout:40|60' => 'Dos columnas (40%, 60%)',
36 | 'widget_manager:settings:widget_layout:50|50' => 'Dos columnas (50%, 50%)',
37 | 'widget_manager:settings:widget_layout:60|40' => 'Dos columnas (60%, 40%)',
38 | 'widget_manager:settings:widget_layout:75|25' => 'Dos columna (75%, 25%)',
39 | 'widget_manager:settings:widget_layout:25|25|50' => 'Columna ancha derecha (25%, 25%, 50%)',
40 | 'widget_manager:settings:widget_layout:25|50|25' => 'Columna media ancha (25%, 50%, 25%)',
41 | 'widget_manager:settings:widget_layout:50|25|25' => 'Columna ancha izquierda (50%, 25%, 25%)',
42 | 'widget_manager:settings:widget_layout:33|33|33' => 'Diseño por defecto (33% por columna)',
43 | 'widget_manager:settings:widget_layout' => 'Elija un diseño de widget',
44 | 'widget_manager:settings:custom_index:all' => 'Para todos los usuarios',
45 | 'widget_manager:settings:custom_index:loggedin' => 'Sólo para usuarios registrados',
46 | 'widget_manager:settings:custom_index:non_loggedin' => 'Sólo para usuarios no registrados',
47 | 'widget_manager:settings:custom_index' => '¿Utilizar el índice personalizado de administrador de Widget?',
48 | 'widget_manager:settings:group' => 'Grupo',
49 | 'widget_manager:settings:index' => 'Índice',
50 | 'widget_manager:groups:enable_widget_manager' => 'Habilitar la administración de widgets',
51 | 'widget_manager:widgets:edit:disable_widget_content_style' => 'No hay estilo de widget',
52 | 'widget_manager:widgets:edit:custom_class' => 'Clase CSS personalizada',
53 | 'widget_manager:widgets:edit:hide_header' => 'Ocultar encabezado',
54 | 'widget_manager:widgets:edit:custom_more_url' => 'Enlace personalizado más',
55 | 'widget_manager:widgets:edit:custom_more_title' => 'Personalizar más texto',
56 | 'widget_manager:widgets:edit:custom_url' => 'Enlace de título personalizado',
57 | 'widget_manager:widgets:edit:custom_title' => 'Título Personalizado',
58 | 'admin:statistics:widgets' => 'Uso de Widget',
59 | 'admin:widgets:manage:index' => 'Administrar el índice',
60 | 'admin:widgets:manage' => 'Gestionar',
61 | );
62 |
--------------------------------------------------------------------------------
/views/default/plugins/widget_manager/settings.php:
--------------------------------------------------------------------------------
1 | 'select',
7 | '#label' => elgg_echo('widget_manager:settings:custom_index'),
8 | 'name' => 'params[custom_index]',
9 | 'value' => $plugin->custom_index,
10 | 'options_values' => [
11 | '0|0' => elgg_echo('option:no'),
12 | '1|0' => elgg_echo('widget_manager:settings:custom_index:non_loggedin'),
13 | '0|1' => elgg_echo('widget_manager:settings:custom_index:loggedin'),
14 | '1|1' => elgg_echo('widget_manager:settings:custom_index:all'),
15 | ],
16 | ]);
17 |
18 | $index_settings .= elgg_view_field([
19 | '#type' => 'select',
20 | '#label' => elgg_echo('widget_manager:settings:widget_layout'),
21 | 'name' => 'params[widget_layout]',
22 | 'value' => $plugin->widget_layout,
23 | 'options_values' => [
24 | 'fluid' => elgg_echo('widget_manager:settings:widget_layout:fluid'),
25 | '33|33|33' => elgg_echo('widget_manager:settings:widget_layout:33|33|33'),
26 | '50|25|25' => elgg_echo('widget_manager:settings:widget_layout:50|25|25'),
27 | '25|50|25' => elgg_echo('widget_manager:settings:widget_layout:25|50|25'),
28 | '25|25|50' => elgg_echo('widget_manager:settings:widget_layout:25|25|50'),
29 | '75|25' => elgg_echo('widget_manager:settings:widget_layout:75|25'),
30 | '60|40' => elgg_echo('widget_manager:settings:widget_layout:60|40'),
31 | '50|50' => elgg_echo('widget_manager:settings:widget_layout:50|50'),
32 | '40|60' => elgg_echo('widget_manager:settings:widget_layout:40|60'),
33 | '25|75' => elgg_echo('widget_manager:settings:widget_layout:25|75'),
34 | '100' => elgg_echo('widget_manager:settings:widget_layout:100'),
35 | ],
36 | ]);
37 |
38 | $index_settings .= elgg_view_field([
39 | '#type' => 'userpicker',
40 | '#label' => elgg_echo('widget_manager:settings:index_managers'),
41 | 'name' => 'params[index_managers]',
42 | 'value' => $plugin->index_managers ? explode(',', $plugin->index_managers) : null,
43 | ]);
44 |
45 | echo elgg_view_module('info', elgg_echo('widget_manager:settings:index'), $index_settings);
46 |
47 | if (elgg_is_active_plugin('groups')) {
48 | $group_settings = elgg_view_field([
49 | '#type' => 'select',
50 | '#label' => elgg_echo('widget_manager:settings:group:enable'),
51 | '#help' => elgg_view('output/url', [
52 | 'text' => elgg_echo('widget_manager:settings:group:force_tool_widgets'),
53 | 'href' => 'action/widget_manager/force_tool_widgets',
54 | 'confirm' => elgg_echo('widget_manager:settings:group:force_tool_widgets:confirm'),
55 | ]),
56 | 'name' => 'params[group_enable]',
57 | 'value' => $plugin->group_enable,
58 | 'options_values' => [
59 | 'no' => elgg_echo('option:no'),
60 | 'yes' => elgg_echo('widget_manager:settings:group:enable:yes'),
61 | 'forced' => elgg_echo('widget_manager:settings:group:enable:forced'),
62 | ],
63 | ]);
64 |
65 | $group_settings .= elgg_view_field([
66 | '#type' => 'checkbox',
67 | '#label' => elgg_echo('widget_manager:settings:group:option_default_enabled'),
68 | 'name' => 'params[group_option_default_enabled]',
69 | 'checked' => $plugin->group_option_default_enabled === 'yes',
70 | 'switch' => true,
71 | 'default' => 'no',
72 | 'value' => 'yes',
73 | ]);
74 |
75 | $group_settings .= elgg_view_field([
76 | '#type' => 'checkbox',
77 | '#label' => elgg_echo('widget_manager:settings:group:option_admin_only'),
78 | 'name' => 'params[group_option_admin_only]',
79 | 'checked' => $plugin->group_option_admin_only === 'yes',
80 | 'switch' => true,
81 | 'default' => 'no',
82 | 'value' => 'yes',
83 | ]);
84 |
85 | $group_settings .= elgg_view_field([
86 | '#type' => 'number',
87 | '#label' => elgg_echo('widget_manager:settings:group:group_column_count'),
88 | 'name' => 'params[group_column_count]',
89 | 'value' => $plugin->group_column_count,
90 | 'min' => 1,
91 | ]);
92 |
93 | echo elgg_view_module('info', elgg_echo('widget_manager:settings:group'), $group_settings);
94 | }
95 |
96 | $lazy_settings = elgg_view_field([
97 | '#type' => 'switch',
98 | '#label' => elgg_echo('widget_manager:settings:lazy_loading:enabled'),
99 | '#help' => elgg_echo('widget_manager:settings:lazy_loading:enabled:help'),
100 | 'name' => 'params[lazy_loading_enabled]',
101 | 'value' => $plugin->lazy_loading_enabled,
102 | ]);
103 |
104 | $lazy_settings .= elgg_view_field([
105 | '#type' => 'switch',
106 | '#label' => elgg_echo('widget_manager:settings:lazy_loading:lazy_loading_mobile_columns'),
107 | '#help' => elgg_echo('widget_manager:settings:lazy_loading:lazy_loading_mobile_columns:help'),
108 | 'name' => 'params[lazy_loading_mobile_columns]',
109 | 'value' => $plugin->lazy_loading_mobile_columns,
110 | ]);
111 |
112 | $lazy_settings .= elgg_view_field([
113 | '#type' => 'number',
114 | '#label' => elgg_echo('widget_manager:settings:lazy_loading:lazy_loading_under_fold'),
115 | '#help' => elgg_echo('widget_manager:settings:lazy_loading:lazy_loading_under_fold:help'),
116 | 'name' => 'params[lazy_loading_under_fold]',
117 | 'value' => $plugin->lazy_loading_under_fold,
118 | 'min' => 0,
119 | ]);
120 |
121 | echo elgg_view_module('info', elgg_echo('widget_manager:settings:lazy_loading'), $lazy_settings);
122 |
123 | $other_settings = elgg_view_field([
124 | '#type' => 'switch',
125 | '#label' => elgg_echo('widget_manager:settings:show_collapse_content'),
126 | '#help' => elgg_echo('widget_manager:settings:show_collapse_content:help'),
127 | 'name' => 'params[show_collapse_content]',
128 | 'value' => $plugin->show_collapse_content,
129 | ]);
130 |
131 | echo elgg_view_module('info', elgg_echo('widget_manager:settings:other'), $other_settings);
132 |
--------------------------------------------------------------------------------
/views/default/page/layouts/widgets.php:
--------------------------------------------------------------------------------
1 | guid !== $page_owner->guid) {
38 | elgg_set_page_owner_guid($owner->guid);
39 | }
40 |
41 | $can_edit_layout = elgg_can_edit_widget_layout($context);
42 |
43 | $classes = elgg_extract_class($vars, [
44 | 'elgg-layout-widgets',
45 | "elgg-layout-widgets-{$context}",
46 | ]);
47 |
48 | if ($can_edit_layout) {
49 | $classes[] = 'elgg-layout-can-edit';
50 | }
51 |
52 | $widgets = elgg_extract('widgets', $vars);
53 | if ($widgets === null) {
54 | $ignore_access = $can_edit_layout ? ELGG_IGNORE_ACCESS : 0;
55 | $widgets = elgg_call($ignore_access, function() use ($owner, $context) {
56 | return elgg_get_widgets($owner->guid, $context);
57 | });
58 | }
59 |
60 | $result = '';
61 | $no_widgets = elgg_extract('no_widgets', $vars);
62 | if (empty($widgets) && !empty($no_widgets)) {
63 | if ($no_widgets instanceof \Closure) {
64 | echo $no_widgets();
65 | } else {
66 | $result .= $no_widgets;
67 | }
68 | }
69 |
70 | // adjusts context to get correct widgets for special widget pages
71 | $available_widgets_context = elgg_trigger_event_results('available_widgets_context', 'widget_manager', [], $context);
72 | if ($widgets) {
73 | $widget_types = elgg_get_widget_types([
74 | 'context' => $available_widgets_context,
75 | 'container' => $owner,
76 | ]);
77 | }
78 |
79 | if ($show_add_widgets && $can_edit_layout) {
80 | $vars['show_collapse_content'] = !in_array('widgets-fluid-columns', $classes);
81 |
82 | $result .= elgg_view('page/layouts/widgets/add_button', $vars);
83 | }
84 |
85 | $show_empty_grid = (bool) elgg_extract('show_empty_grid', $vars, true);
86 | if (!$show_empty_grid && empty($result) && empty($widgets)) {
87 | // Restore original page owner
88 | if (empty($page_owner)) {
89 | elgg_set_page_owner_guid(false);
90 | } elseif ($owner->guid !== $page_owner->guid) {
91 | elgg_set_page_owner_guid($page_owner->guid);
92 | }
93 |
94 | return;
95 | }
96 |
97 | // push context after the add_button as add button uses current context
98 | elgg_push_context('widgets');
99 |
100 | // move hidden columns widgets to last visible column
101 | if (!isset($widgets[$num_columns])) {
102 | $widgets[$num_columns] = [];
103 | }
104 |
105 | foreach ($widgets as $index => $column_widgets) {
106 | if ($index <= $num_columns) {
107 | continue;
108 | }
109 |
110 | // append widgets to last column and retain order
111 | foreach ($column_widgets as $column_widget) {
112 | $widgets[$num_columns][] = $column_widget;
113 | }
114 |
115 | unset($widgets[$index]);
116 | }
117 |
118 | // remove unsupported widgets
119 | foreach ($widgets as $column_index => $column_widgets) {
120 | foreach ($column_widgets as $widget_index => $column_widget) {
121 | if (!array_key_exists($column_widget->handler, $widget_types)) {
122 | unset($widgets[$column_index][$widget_index]);
123 | }
124 | }
125 | }
126 |
127 | $grid_id = uniqid('elgg-widgets-grid-');
128 | $grid = '';
129 | for ($column_index = 1; $column_index <= $num_columns; $column_index++) {
130 | $column_widgets = (array) elgg_extract($column_index, $widgets, []);
131 |
132 | $widgets_content = '';
133 | foreach ($column_widgets as $widget) {
134 | $widgets_content .= elgg_view_entity($widget, [
135 | 'show_access' => $show_access,
136 | 'register_rss_link' => false,
137 | 'layout_info' => [
138 | 'widgets' => $widgets,
139 | 'classes' => $classes,
140 | ],
141 | ]);
142 | }
143 |
144 | $grid .= elgg_format_element('div', [
145 | 'class' => elgg_extract_class(elgg_extract('column_classes', $vars, []), ['elgg-widgets', "elgg-widget-col-{$column_index}"], $column_index),
146 | 'data-sortable-options' => json_encode([
147 | 'connectWith' => '#' . $grid_id . ' .elgg-widgets',
148 | ]),
149 | 'data-widget-column' => $column_index,
150 | ], $widgets_content);
151 | }
152 |
153 | $result .= elgg_format_element('div', [
154 | 'class' => 'elgg-widgets-grid',
155 | 'id' => $grid_id,
156 | ], $grid);
157 |
158 | elgg_pop_context();
159 |
160 | echo elgg_format_element('div', [
161 | 'class' => $classes,
162 | 'data-page-owner-guid' => $owner->guid,
163 | 'data-context-stack' => json_encode(elgg_get_context_stack()),
164 | ], $result);
165 |
166 | // Restore original page owner
167 | if (empty($page_owner)) {
168 | elgg_set_page_owner_guid(0);
169 | } elseif ($owner->guid !== $page_owner->guid) {
170 | elgg_set_page_owner_guid($page_owner->guid);
171 | }
172 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/WidgetsSettingsConfig.php:
--------------------------------------------------------------------------------
1 | config = [];
26 | } else {
27 | $this->config = json_decode($widgets_config, true);
28 | }
29 | }
30 |
31 | /**
32 | * Returns all save settings for a widget
33 | *
34 | * @param string $handler Widget to retrieve settings for
35 | * @param string|null $context optional context to return settings for
36 | *
37 | * @return array
38 | */
39 | public function getAll(string $handler, ?string $context = null): array {
40 | if (!isset($this->config[$handler]['contexts'])) {
41 | if (!isset($context)) {
42 | return [];
43 | }
44 |
45 | return ['contexts' => []];
46 | }
47 |
48 | if (empty($context)) {
49 | return $this->config[$handler];
50 | }
51 |
52 | return elgg_extract($context, $this->config[$handler]['contexts'], []);
53 | }
54 |
55 | /**
56 | * Returns a setting value for a specific widget handler
57 | *
58 | * @param string $handler Handler of the widget
59 | * @param string $setting Setting to retrieve
60 | * @param string $context Context for this setting (will default to current context)
61 | *
62 | * @return boolean|null
63 | */
64 | public function getSetting(string $handler, string $setting, string $context = ''): ?bool {
65 | $context = $this->getContext($context);
66 |
67 | $setting_value = elgg_extract($setting, $this->getAll($handler, $context));
68 | if (isset($setting_value)) {
69 | return (bool) $setting_value;
70 | }
71 |
72 | if (!in_array($setting, ['can_add', 'hide'])) {
73 | // unsupported setting
74 | return null;
75 | }
76 |
77 | if ($setting === 'can_add') {
78 | // default widgets can be added
79 | return true;
80 | }
81 |
82 | // default for unexisting setting value
83 | return false;
84 | }
85 |
86 | /**
87 | * Returns given context or detects current context
88 | *
89 | * @param string $context context
90 | *
91 | * @return string
92 | */
93 | protected function getContext(string $context = ''): string {
94 | return $context ?: (string) elgg_get_context();
95 | }
96 |
97 | /**
98 | * Returns registered service name
99 | * @return string
100 | */
101 | public static function name() {
102 | return 'widgets.settings';
103 | }
104 |
105 | /**
106 | * Returns whether or not the widget body should be lazy loaded
107 | *
108 | * @param \WidgetManagerWidget $widget Widget to check the lazy loading logic for
109 | * @param array $layout_info Additional info about the layout the widget is shown in
110 | *
111 | * @return bool
112 | */
113 | public function showLazyLoaded(\WidgetManagerWidget $widget, array $layout_info = []): bool {
114 | if (elgg_is_xhr()) {
115 | // no lazy loading body for ajax loaded widgets
116 | return false;
117 | }
118 |
119 | if (!(bool) elgg_get_plugin_setting('lazy_loading_enabled', 'widget_manager')) {
120 | return false;
121 | }
122 |
123 | $detect_lazy_loading = function() use ($widget, $layout_info) {
124 | // configured in advanced widget config
125 | if ($widget->widget_manager_lazy_load_content) {
126 | return true;
127 | }
128 |
129 | // configured as always lazy loaded handler for context
130 | if ($this->getSetting($widget->handler, 'always_lazy_load', (string) $widget->context)) {
131 | return true;
132 | }
133 |
134 | $widgets = elgg_extract('widgets', $layout_info, []);
135 |
136 | // under the fold
137 | $under_fold_limit = elgg_get_plugin_setting('lazy_loading_under_fold', 'widget_manager');
138 | if (!elgg_is_empty($under_fold_limit) && !empty($widgets)) {
139 | if (in_array('widgets-fluid-columns', elgg_extract('classes', $layout_info, []))) {
140 | $under_fold_limit = $under_fold_limit * 3;
141 | }
142 |
143 | // if invalid widgets are removed from the layout, the index could be messed up
144 | $column_widgets = array_values((array) elgg_extract($widget->column, $widgets, []));
145 |
146 | foreach ($column_widgets as $column_index => $column_widget) {
147 | // if position in column is over the fold limit than it should be lazy loaded
148 | if ($column_index >= (int) $under_fold_limit) {
149 | return true;
150 | }
151 |
152 | if ($column_widget->guid === $widget->guid) {
153 | // do not look further
154 | break;
155 | }
156 | }
157 | }
158 |
159 | // mobile columns
160 | if ((bool) elgg_get_plugin_setting('lazy_loading_mobile_columns', 'widget_manager') && !empty($widgets)) {
161 | $first_column_with_data = 3;
162 | while (empty($widgets[$first_column_with_data])) {
163 | $first_column_with_data--;
164 |
165 | if ($first_column_with_data === 1) {
166 | break;
167 | }
168 | }
169 |
170 | if ($widget->column < $first_column_with_data) {
171 | $mobile_detect = new \Detection\MobileDetect();
172 | if ($mobile_detect->hasUserAgent() && $mobile_detect->isMobile()) {
173 | return true;
174 | }
175 | }
176 | }
177 |
178 | return false;
179 | };
180 |
181 | return (bool) elgg_trigger_event_results('lazy_load', 'widget_manager', ['entity' => $widget, 'layout_info' => $layout_info], $detect_lazy_loading());
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/elgg-plugin.php:
--------------------------------------------------------------------------------
1 | [
17 | 'version' => '17.1.2',
18 | 'dependencies' => [
19 | 'profile' => [
20 | 'must_be_active' => false,
21 | 'position' => 'after',
22 | ],
23 | 'groups' => [
24 | 'must_be_active' => false,
25 | 'position' => 'after',
26 | ],
27 | ],
28 | ],
29 | 'bootstrap' => \ColdTrick\WidgetManager\Bootstrap::class,
30 | 'settings' => [
31 | 'group_column_count' => 2,
32 | 'widget_layout' => '33|33|33',
33 | 'lazy_loading_enabled' => 0,
34 | 'lazy_loading_mobile_columns' => 1,
35 | 'lazy_loading_under_fold' => 5,
36 | 'show_collapse_content' => false,
37 | ],
38 | 'entities' => [
39 | [
40 | 'type' => 'object',
41 | 'subtype' => 'widget',
42 | 'class' => 'WidgetManagerWidget',
43 | 'capabilities' => [
44 | 'commentable' => false,
45 | ],
46 | ],
47 | [
48 | 'type' => 'object',
49 | 'subtype' => 'widget_page',
50 | 'class' => 'WidgetPage',
51 | 'capabilities' => [
52 | 'commentable' => false,
53 | 'searchable' => true,
54 | ],
55 | ],
56 | ],
57 | 'actions' => [
58 | 'widget_manager/lazy_load_widgets' => [
59 | 'access' => 'public',
60 | ],
61 | 'widget_manager/groups/update_widget_access' => [],
62 | 'widget_manager/force_tool_widgets' => [
63 | 'access' => 'admin',
64 | ],
65 | 'widget_manager/manage_widgets' => [
66 | 'access' => 'admin',
67 | ],
68 | 'widget_manager/widget_page' => [],
69 | 'widget_manager/cleanup' => [
70 | 'access' => 'admin',
71 | ],
72 | 'widget_manager/fluid_order' => [],
73 | ],
74 | 'views' => [
75 | 'default' => [
76 | 'muuri.mjs' => $composer_path . 'vendor/npm-asset/muuri/dist/muuri.js',
77 | ],
78 | ],
79 | 'view_extensions' => [
80 | 'admin.css' => [
81 | 'forms/widget_manager/manage_widgets/widget.css' => [],
82 | ],
83 | 'elements/widgets.css' => [
84 | 'widget_manager/site.css' => [],
85 | ],
86 | 'groups/edit/settings' => [
87 | 'widget_manager/forms/groups_widget_access' => [],
88 | ],
89 | 'object/widget/elements/content' => [
90 | 'widget_manager/widgets/custom_more' => [],
91 | ],
92 | 'page/layouts/widgets/add_panel' => [
93 | 'widget_manager/group_tool_widgets' => ['priority' => 400],
94 | ],
95 | ],
96 | 'view_options' => [
97 | 'forms/widget_manager/widget_page' => ['ajax' => true],
98 | 'widget_manager/widgets/settings' => ['ajax' => true],
99 | 'widgets/user_search/content' => ['ajax' => true],
100 | ],
101 | 'events' => [
102 | 'access:collections:write' => [
103 | 'all' => [
104 | '\ColdTrick\WidgetManager\Access::setWriteAccess' => ['priority' => 999],
105 | ],
106 | ],
107 | 'access:collections:read' => [
108 | 'user' => [
109 | '\ColdTrick\WidgetManager\Access::addLoggedOutReadAccess' => [],
110 | ],
111 | ],
112 | 'action:validate' => [
113 | 'widgets/add' => [
114 | '\ColdTrick\WidgetManager\Access::moreRightsForWidgetManager' => [],
115 | ],
116 | 'widgets/delete' => [
117 | '\ColdTrick\WidgetManager\Access::moreRightsForWidgetManager' => [],
118 | ],
119 | 'widgets/move' => [
120 | '\ColdTrick\WidgetManager\Access::moreRightsForWidgetManager' => [],
121 | ],
122 | 'widgets/save' => [
123 | '\ColdTrick\WidgetManager\Access::moreRightsForWidgetManager' => [],
124 | ],
125 | ],
126 | 'create' => [
127 | 'object' => [
128 | '\ColdTrick\WidgetManager\Groups::addGroupWidget' => [],
129 | '\ColdTrick\WidgetManager\Widgets::fixPrivateAccess' => [],
130 | ],
131 | ],
132 | 'delete' => [
133 | 'object' => [
134 | '\ColdTrick\WidgetManager\Groups::deleteGroupWidget' => [],
135 | ],
136 | ],
137 | 'entity:url' => [
138 | 'object' => [
139 | '\ColdTrick\WidgetManager\Widgets::getWidgetURL' => ['priority' => 9999],
140 | ],
141 | ],
142 | 'get_list' => [
143 | 'default_widgets' => [
144 | '\ColdTrick\WidgetManager\Groups::addGroupsContextToDefaultWidgets' => [],
145 | ],
146 | ],
147 | 'group_tool_widgets' => [
148 | 'widget_manager' => [
149 | 'ColdTrick\WidgetManager\Widgets::groupToolWidgets' => [],
150 | ],
151 | ],
152 | 'handlers' => [
153 | 'widgets' => [
154 | '\ColdTrick\WidgetManager\Widgets::addDiscussionsWidgetToGroup' => [],
155 | '\ColdTrick\WidgetManager\Widgets::applyWidgetsConfig' => ['priority' => 9999],
156 | ],
157 | ],
158 | 'permissions_check' => [
159 | 'object' => [
160 | '\ColdTrick\WidgetManager\Access::canEditWidgetOnManagedLayout' => [],
161 | ],
162 | 'widget_layout' => [
163 | '\ColdTrick\WidgetManager\Widgets::layoutPermissionsCheck' => [],
164 | ],
165 | ],
166 | 'permissions_check:delete' => [
167 | 'object' => [
168 | '\ColdTrick\WidgetManager\Access::onlyAdminsCanDeleteWidgetPages' => [],
169 | ],
170 | ],
171 | 'register' => [
172 | 'menu:admin_header' => [
173 | '\ColdTrick\WidgetManager\Menus\AdminHeader::registerAdminHeaderMenu' => [],
174 | ],
175 | 'menu:entity' => [
176 | '\ColdTrick\WidgetManager\Menus\Entity::addWidgetPageEntityMenuItems' => [],
177 | ],
178 | 'menu:title:widgets' => [
179 | '\ColdTrick\WidgetManager\Menus\Title::addWidgetsContentToggle' => [],
180 | ],
181 | ],
182 | 'seeds' => [
183 | 'database' => [
184 | '\ColdTrick\WidgetManager\Seeder::register' => [],
185 | ],
186 | ],
187 | 'setting' => [
188 | 'plugin' => [
189 | '\ColdTrick\WidgetManager\Settings::implodeSettings' => [],
190 | ],
191 | ],
192 | 'tool_options' => [
193 | 'group' => [
194 | '\ColdTrick\WidgetManager\Groups::registerGroupWidgetsTool' => [],
195 | ],
196 | ],
197 | 'update' => [
198 | 'group' => [
199 | '\ColdTrick\WidgetManager\Groups::updateGroupWidgets' => [],
200 | ],
201 | ],
202 | 'view' => [
203 | 'object/widget/body' => [
204 | '\ColdTrick\WidgetManager\Widgets::saveContentInCache' => ['priority' => 9999],
205 | ],
206 | ],
207 | 'view_vars' => [
208 | 'groups/profile/widgets' => [
209 | '\ColdTrick\WidgetManager\Groups::getGroupWidgetsLayout' => [],
210 | ],
211 | 'object/widget/body' => [
212 | '\ColdTrick\WidgetManager\Widgets::getContentFromCache' => [],
213 | ],
214 | 'object/widget/edit' => [
215 | '\ColdTrick\WidgetManager\Access::allowPrivateWidgetEdit' => [],
216 | ],
217 | 'object/widget/elements/controls' => [
218 | '\ColdTrick\WidgetManager\Widgets::preventControls' => [],
219 | ],
220 | ],
221 | ],
222 | ];
223 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "868cf70c025943127026cbd2d7228857",
8 | "packages": [
9 | {
10 | "name": "mobiledetect/mobiledetectlib",
11 | "version": "4.8.09",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/serbanghita/Mobile-Detect.git",
15 | "reference": "a06fe2e546a06bb8c2639d6823d5250b2efb3209"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/a06fe2e546a06bb8c2639d6823d5250b2efb3209",
20 | "reference": "a06fe2e546a06bb8c2639d6823d5250b2efb3209",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "php": ">=8.0",
25 | "psr/cache": "^3.0",
26 | "psr/simple-cache": "^3"
27 | },
28 | "require-dev": {
29 | "friendsofphp/php-cs-fixer": "^v3.65.0",
30 | "phpbench/phpbench": "^1.2",
31 | "phpstan/phpstan": "^1.12.x-dev",
32 | "phpunit/phpunit": "^9.6.18",
33 | "squizlabs/php_codesniffer": "^3.11.1"
34 | },
35 | "type": "library",
36 | "autoload": {
37 | "psr-4": {
38 | "Detection\\": "src/"
39 | }
40 | },
41 | "notification-url": "https://packagist.org/downloads/",
42 | "license": [
43 | "MIT"
44 | ],
45 | "authors": [
46 | {
47 | "name": "Serban Ghita",
48 | "email": "serbanghita@gmail.com",
49 | "homepage": "http://mobiledetect.net",
50 | "role": "Developer"
51 | }
52 | ],
53 | "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.",
54 | "homepage": "https://github.com/serbanghita/Mobile-Detect",
55 | "keywords": [
56 | "detect mobile devices",
57 | "mobile",
58 | "mobile detect",
59 | "mobile detector",
60 | "php mobile detect"
61 | ],
62 | "support": {
63 | "issues": "https://github.com/serbanghita/Mobile-Detect/issues",
64 | "source": "https://github.com/serbanghita/Mobile-Detect/tree/4.8.09"
65 | },
66 | "funding": [
67 | {
68 | "url": "https://github.com/serbanghita",
69 | "type": "github"
70 | }
71 | ],
72 | "time": "2024-12-10T15:32:06+00:00"
73 | },
74 | {
75 | "name": "npm-asset/muuri",
76 | "version": "0.9.5",
77 | "dist": {
78 | "type": "tar",
79 | "url": "https://registry.npmjs.org/muuri/-/muuri-0.9.5.tgz"
80 | },
81 | "type": "npm-asset",
82 | "license": [
83 | "MIT"
84 | ]
85 | },
86 | {
87 | "name": "psr/cache",
88 | "version": "3.0.0",
89 | "source": {
90 | "type": "git",
91 | "url": "https://github.com/php-fig/cache.git",
92 | "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
93 | },
94 | "dist": {
95 | "type": "zip",
96 | "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
97 | "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
98 | "shasum": ""
99 | },
100 | "require": {
101 | "php": ">=8.0.0"
102 | },
103 | "type": "library",
104 | "extra": {
105 | "branch-alias": {
106 | "dev-master": "1.0.x-dev"
107 | }
108 | },
109 | "autoload": {
110 | "psr-4": {
111 | "Psr\\Cache\\": "src/"
112 | }
113 | },
114 | "notification-url": "https://packagist.org/downloads/",
115 | "license": [
116 | "MIT"
117 | ],
118 | "authors": [
119 | {
120 | "name": "PHP-FIG",
121 | "homepage": "https://www.php-fig.org/"
122 | }
123 | ],
124 | "description": "Common interface for caching libraries",
125 | "keywords": [
126 | "cache",
127 | "psr",
128 | "psr-6"
129 | ],
130 | "support": {
131 | "source": "https://github.com/php-fig/cache/tree/3.0.0"
132 | },
133 | "time": "2021-02-03T23:26:27+00:00"
134 | },
135 | {
136 | "name": "psr/simple-cache",
137 | "version": "3.0.0",
138 | "source": {
139 | "type": "git",
140 | "url": "https://github.com/php-fig/simple-cache.git",
141 | "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
142 | },
143 | "dist": {
144 | "type": "zip",
145 | "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
146 | "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
147 | "shasum": ""
148 | },
149 | "require": {
150 | "php": ">=8.0.0"
151 | },
152 | "type": "library",
153 | "extra": {
154 | "branch-alias": {
155 | "dev-master": "3.0.x-dev"
156 | }
157 | },
158 | "autoload": {
159 | "psr-4": {
160 | "Psr\\SimpleCache\\": "src/"
161 | }
162 | },
163 | "notification-url": "https://packagist.org/downloads/",
164 | "license": [
165 | "MIT"
166 | ],
167 | "authors": [
168 | {
169 | "name": "PHP-FIG",
170 | "homepage": "https://www.php-fig.org/"
171 | }
172 | ],
173 | "description": "Common interfaces for simple caching",
174 | "keywords": [
175 | "cache",
176 | "caching",
177 | "psr",
178 | "psr-16",
179 | "simple-cache"
180 | ],
181 | "support": {
182 | "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
183 | },
184 | "time": "2021-10-29T13:26:27+00:00"
185 | }
186 | ],
187 | "packages-dev": [],
188 | "aliases": [],
189 | "minimum-stability": "stable",
190 | "stability-flags": {},
191 | "prefer-stable": false,
192 | "prefer-lowest": false,
193 | "platform": {},
194 | "platform-dev": {},
195 | "plugin-api-version": "2.6.0"
196 | }
197 |
--------------------------------------------------------------------------------
/languages/en.php:
--------------------------------------------------------------------------------
1 | "Widget Page",
6 | 'collection:object:widget_page' => "Widget Pages",
7 |
8 | // admin menu items
9 | 'admin:widgets' => "Widgets",
10 | 'admin:widgets:manage' => "Manage",
11 | 'admin:widgets:cleanup' => "Cleanup",
12 | 'admin:widgets:pages' => "Widget Pages",
13 | 'admin:widgets:manage:index' => "Manage Index",
14 | 'admin:statistics:widgets' => "Widget Usage",
15 |
16 | // cleanup
17 | 'admin:widgets:cleanup:info' => "Here you can remove all widgets that are currently in the database, even those that are no longer available.",
18 |
19 | // widget edit wrapper
20 | 'widget_manager:widgets:edit:custom_title' => "Custom title",
21 | 'widget_manager:widgets:edit:custom_url' => "Custom title link",
22 | 'widget_manager:widgets:edit:custom_more_title' => "Custom more text",
23 | 'widget_manager:widgets:edit:custom_more_url' => "Custom more link",
24 | 'widget_manager:widgets:edit:hide_header' => "Hide header",
25 | 'widget_manager:widgets:edit:custom_class' => "Custom CSS class",
26 | 'widget_manager:widgets:edit:disable_widget_content_style' => "No widget style",
27 | 'widget_manager:widgets:edit:lazy_load_content' => "Lazy load the content",
28 |
29 | // group
30 | 'widget_manager:groups:enable_widget_manager' => "Enable management of widgets",
31 |
32 | // admin settings
33 | 'widget_manager:settings:index' => "Index",
34 | 'widget_manager:settings:group' => "Group",
35 | 'widget_manager:settings:lazy_loading' => "Lazy Loading",
36 | 'widget_manager:settings:other' => "Other",
37 |
38 | 'widget_manager:settings:custom_index' => "Use Widget Manager custom index?",
39 | 'widget_manager:settings:custom_index:non_loggedin' => "For non-loggedin users only",
40 | 'widget_manager:settings:custom_index:loggedin' => "For loggedin users only",
41 | 'widget_manager:settings:custom_index:all' => "For all users",
42 |
43 | 'widget_manager:settings:widget_layout' => "Choose a widget layout",
44 | 'widget_manager:settings:widget_layout:fluid' => "Fluid layout",
45 | 'widget_manager:settings:widget_layout:33|33|33' => "Default layout (33% per column)",
46 | 'widget_manager:settings:widget_layout:50|25|25' => "Wide left column (50%, 25%, 25%)",
47 | 'widget_manager:settings:widget_layout:25|50|25' => "Wide middle column (25%, 50%, 25%)",
48 | 'widget_manager:settings:widget_layout:25|25|50' => "Wide right column (25%, 25%, 50%)",
49 | 'widget_manager:settings:widget_layout:75|25' => "Two column (75%, 25%)",
50 | 'widget_manager:settings:widget_layout:60|40' => "Two column (60%, 40%)",
51 | 'widget_manager:settings:widget_layout:50|50' => "Two column (50%, 50%)",
52 | 'widget_manager:settings:widget_layout:40|60' => "Two column (40%, 60%)",
53 | 'widget_manager:settings:widget_layout:25|75' => "Two column (25%, 75%)",
54 | 'widget_manager:settings:widget_layout:100' => "Single column (100%)",
55 |
56 | 'widget_manager:settings:index_managers' => "Index managers",
57 | 'widget_manager:settings:show_collapse_content' => "Show widget content toggle on layout edit",
58 | 'widget_manager:settings:show_collapse_content:help' => "If enabled this will show a toggle next to the 'Add widget' button to toggle widget contents. This can help with organizing your widget layout.",
59 |
60 | 'widget_manager:settings:group:enable' => "Enable Widget Manager for groups",
61 | 'widget_manager:settings:group:enable:yes' => "Yes, manageable by group tool option",
62 | 'widget_manager:settings:group:enable:forced' => "Yes, always on",
63 | 'widget_manager:settings:group:option_default_enabled' => "Widget management for groups default enabled",
64 | 'widget_manager:settings:group:option_admin_only' => "Only administrator can enable group widgets",
65 | 'widget_manager:settings:group:force_tool_widgets' => "Enforce group tool widgets",
66 | 'widget_manager:settings:group:force_tool_widgets:confirm' => "Are you sure? This will add/remove all widgets specific to a tool option for all groups (where widget management is enabled).",
67 | 'widget_manager:settings:group:group_column_count' => "Number of widget columns in a group",
68 |
69 | 'widget_manager:settings:extra_contexts:description' => "Enter the page handler name of the new page which will get a layout similar to the index page. You can add as much pages as you need. Be sure not to add a page handler that is already in use. You can also configure the column layout for that page and optionally assign non-admin users as manager of the page.",
70 | 'widget_manager:settings:extra_contexts:page' => "Page",
71 | 'widget_manager:settings:extra_contexts:layout' => "Layout",
72 | 'widget_manager:settings:extra_contexts:layout:help' => "Set the number of columns",
73 | 'widget_manager:settings:extra_contexts:manager' => "Managers",
74 | 'widget_manager:settings:extra_contexts:manager:help' => "Configure the users that are allowed to manage the page",
75 |
76 | 'widget_manager:settings:lazy_loading:enabled' => "Enable Lazy Loading",
77 | 'widget_manager:settings:lazy_loading:enabled:help' => "Globally enables the lazy loading feature",
78 | 'widget_manager:settings:lazy_loading:lazy_loading_mobile_columns' => "Lazy Load columns on mobile",
79 | 'widget_manager:settings:lazy_loading:lazy_loading_mobile_columns:help' => "If on a mobile device the second and third column in a widget layout will always lazy load",
80 | 'widget_manager:settings:lazy_loading:lazy_loading_under_fold' => "Under the fold threshold",
81 | 'widget_manager:settings:lazy_loading:lazy_loading_under_fold:help' => "Widgets in the same column will lazy load if they are after this threshold",
82 |
83 | // views
84 | // settings
85 | 'widget_manager:forms:manage_widgets:no_widgets' => "No widgets to manage",
86 | 'widget_manager:forms:manage_widgets:context' => 'Available in the context',
87 | 'widget_manager:forms:manage_widgets:can_add' => "Can be added",
88 | 'widget_manager:forms:manage_widgets:multiple' => "Multiple widgets allowed",
89 | 'widget_manager:forms:manage_widgets:always_lazy_load' => "Always lazy load",
90 | 'widget_manager:forms:manage_widgets:non_default' => "This setting is different from the default setting",
91 | 'widget_manager:forms:manage_widgets:unsupported_context:confirm' => "Are you sure you wish to enable this widget for this context? If the widget does not support the context this could cause issues.",
92 |
93 | // groups widget access
94 | 'widget_manager:forms:groups_widget_access:title' => "Widget Access",
95 | 'widget_manager:forms:groups_widget_access:description' => "This action allows you to update the access level of all widgets in this group to the given access level.",
96 |
97 | 'widget_manager:widget_page:title:help' => "Setting a title, will display a title on the widget page. This is optional.",
98 | 'widget_manager:widget_page:show_description' => "Show description on the page",
99 | 'widget_manager:widget_page:show_description:help' => "If enabled the description will be shown on the widget page. If disabled you will probably only see it in listings like in search.",
100 |
101 | // actions
102 | // manage
103 | 'widget_manager:action:manage:success' => "Widget configuration saved successfully",
104 |
105 | // force tool widgets
106 | 'widget_manager:action:force_tool_widgets:error:not_enabled' => "Widget management for groups is not enabled",
107 | 'widget_manager:action:force_tool_widgets:success' => "Enforced tool specific widgets for %s groups",
108 |
109 | // groups update widget access
110 | 'widget_manager:action:groups:update_widget_access:success' => "Access to all widgets in this group is updated",
111 |
112 | // widgets
113 | 'widget_manager:widgets:edit:advanced' => "Advanced",
114 | 'widget_manager:layout:content:hide' => "Hide widget content",
115 | 'widget_manager:layout:content:show' => "Show widget content",
116 |
117 | ];
118 |
--------------------------------------------------------------------------------
/languages/nl.php:
--------------------------------------------------------------------------------
1 | 'Fluïde layout',
9 | 'widget_manager:settings:other' => 'Overig',
10 | 'widget_manager:settings:show_collapse_content' => 'Toon widget content verbergen optie bij bewerken layout',
11 | 'widget_manager:settings:show_collapse_content:help' => 'Indien geactiveerd toont er naast de \'Widget toevoegen\' knop een optie om de inhoud van widgets te verbergen. Dit kan helpen bij het organiseren van de widget layout.',
12 | 'widget_manager:layout:content:hide' => 'Verberg widget inhoud',
13 | 'widget_manager:layout:content:show' => 'Toon widget inhoud',
14 | 'widget_manager:settings:lazy_loading' => 'Lazy Loading',
15 | 'widget_manager:widgets:edit:lazy_load_content' => 'Laad widget content vertraagd in',
16 | 'widget_manager:settings:lazy_loading:enabled' => 'Lazy Loading activeren',
17 | 'widget_manager:settings:lazy_loading:enabled:help' => 'Activeert de functionaliteit om widget content vertraagd in te laden',
18 | 'widget_manager:settings:lazy_loading:lazy_loading_mobile_columns' => 'Kolommen vertraagd laden op mobiele devices',
19 | 'widget_manager:settings:lazy_loading:lazy_loading_mobile_columns:help' => 'Widgets in de 2e en 3e kolom zullen altijd vertraagd inladen op mobiele devices',
20 | 'widget_manager:settings:lazy_loading:lazy_loading_under_fold' => 'Onder de vouw limiet',
21 | 'widget_manager:forms:manage_widgets:always_lazy_load' => 'Altijd vertraagd laden',
22 | 'widget_manager:settings:lazy_loading:lazy_loading_under_fold:help' => 'Widgets in dezelfde kolom zullen vertraagd inladen indien gepositioneerd na deze waarde',
23 | 'item:object:widget_page' => 'Widget Pagina',
24 | 'collection:object:widget_page' => 'Widget Pagina\'s',
25 | 'admin:widgets:cleanup' => 'Opschonen',
26 | 'admin:widgets:pages' => 'Widget Pagina\'s',
27 | 'admin:widgets:cleanup:info' => 'Hier kun je alle widgets opruimen die zich op dit moment in de database bevinden, ook diegene die niet meer beschikbaar zijn.',
28 | 'widget_manager:settings:index_managers' => 'Voorpagina beheerders',
29 | 'widget_manager:widget_page:title:help' => 'Het optioneel opslaan van een titel toont deze op de widget pagina.',
30 | 'widget_manager:widget_page:show_description' => "Toon omschrijving op widget pagina",
31 | 'widget_manager:widget_page:show_description:help' => "Toont de omschrijving op de widget indien geactiveerd. Als het niet is geactiveerd zie je de omschrijving mogelijk enkel in lijsten en zoekresultaten.",
32 | 'widget_manager:forms:manage_widgets:context' => 'Beschikbaar in de context',
33 | 'widget_manager:forms:manage_widgets:can_add' => 'Kan worden toegevoegd',
34 | 'widget_manager:forms:manage_widgets:multiple' => 'Meerdere widgets toegestaan',
35 | 'widget_manager:forms:manage_widgets:non_default' => 'Deze instelling wijkt af van de standaard instelling',
36 | 'widget_manager:forms:manage_widgets:unsupported_context:confirm' => 'Weet je zeker dat je deze widget voor deze context wilt activeren? Indien de widget dit niet ondersteunt kan dat voor problemen zorgen.',
37 | 'widget_manager:forms:groups_widget_access:title' => 'Widget Zichtbaarheid',
38 | 'widget_manager:forms:groups_widget_access:description' => 'Deze actie geeft je de mogelijkheid om de zichtbaarheid van alle widgets in deze groep aan te passen naar het opgegeven toegangsniveau.',
39 | 'widget_manager:action:groups:update_widget_access:success' => 'Zichtbaarheid van alle widgets in deze groep is bijgewerkt',
40 | 'widget_manager:widgets:edit:custom_more_title' => 'Aangepaste lees meer tekst',
41 | 'widget_manager:widgets:edit:custom_more_url' => 'Aangepaste lees meer link',
42 | 'widget_manager:settings:group:enable:yes' => 'Ja, beheerbaar met groep tool optie',
43 | 'widget_manager:settings:group:enable:forced' => 'Ja, altijd aan',
44 | 'widget_manager:settings:group:force_tool_widgets' => 'Forceer groep tool widgets',
45 | 'widget_manager:settings:group:force_tool_widgets:confirm' => 'Weet je het zeker? Dit zal widgets toevoegen/verwijderen die beheerd worden door een groep tool optie (voor de groepen waar widget management is ingeschakeld)',
46 | 'widget_manager:settings:extra_contexts:page' => 'Pagina',
47 | 'widget_manager:settings:extra_contexts:layout' => 'Layout',
48 | 'widget_manager:settings:extra_contexts:layout:help' => 'Bepaald het aantal kolommen',
49 | 'widget_manager:settings:extra_contexts:manager' => 'Beheerders',
50 | 'widget_manager:settings:extra_contexts:manager:help' => 'Gebruikers die deze pagina kunnen beheren',
51 | 'widget_manager:action:force_tool_widgets:error:not_enabled' => 'Widget beheer voor groepen is niet ingeschakeld',
52 | 'widget_manager:action:force_tool_widgets:success' => 'Widgets voor groep tool geforceerd bij %s groepen',
53 | 'widget_manager:settings:extra_contexts:description' => 'Voer de url in van de nieuwe pagina welke een layout gelijk de widget manager startpagina krijgt. U kunt zoveel pagina\'s toevoegen als nodig is. Vergewis uzelve dat opgegeven pagina nog niet in gebruik is. Alle widgets die normaliter op de voorpagina beschikbaar zijn, zullen ook op deze nieuwe pagina beschikbaar komen. U kunt ook de kolom indeling kiezen en optioneel gewone gebruikers als manager van deze pagina toewijzen.',
54 | 'admin:widgets' => 'Widgets',
55 | 'admin:widgets:manage' => 'Beheren',
56 | 'admin:widgets:manage:index' => 'Beheer startpagina',
57 | 'admin:statistics:widgets' => 'Widget gebruik',
58 | 'widget_manager:widgets:edit:custom_url' => 'Aangepaste titel link',
59 | 'widget_manager:widgets:edit:custom_class' => 'Aangepaste CSS class',
60 | 'widget_manager:groups:enable_widget_manager' => 'Schakelen het beheer van widgets in',
61 | 'widget_manager:settings:index' => 'Startpagina',
62 | 'widget_manager:settings:group' => 'Groep',
63 | 'widget_manager:settings:group:enable' => 'Schakel Widget Manager in voor groepen',
64 | 'widget_manager:settings:group:option_default_enabled' => 'Widgets beheer voor groepen standaard ingeschakeld',
65 | 'widget_manager:settings:group:option_admin_only' => 'Alleen beheerders kunnen groep widgets inschakelen',
66 | 'widget_manager:settings:group:group_column_count' => 'Aantal widget kolommen in een groep',
67 | 'widget_manager:forms:manage_widgets:no_widgets' => 'Geen widgets om te beheren',
68 | 'widget_manager:widgets:edit:advanced' => 'Geavanceerd',
69 | 'widget_manager:widgets:edit:disable_widget_content_style' => 'Verwijder widget stijl',
70 | 'widget_manager:widgets:edit:custom_title' => 'Eigen titel',
71 | 'widget_manager:widgets:edit:hide_header' => 'Verberg titel',
72 | 'widget_manager:settings:custom_index' => 'Gebruik Widget Manager aangepaste index?',
73 | 'widget_manager:settings:custom_index:non_loggedin' => 'Alleen voor niet aangemelde gebruikers',
74 | 'widget_manager:settings:custom_index:loggedin' => 'Alleen voor aangemelde gebruikers',
75 | 'widget_manager:settings:custom_index:all' => 'Voor alle gebruikers',
76 | 'widget_manager:settings:widget_layout' => 'Kies een widget indeling',
77 | 'widget_manager:settings:widget_layout:33|33|33' => 'Standaard indeling (33% per kolom)',
78 | 'widget_manager:settings:widget_layout:50|25|25' => 'Brede linker kolom (50%, 25%, 25%)',
79 | 'widget_manager:settings:widget_layout:25|50|25' => 'Brede middelste kolom (25%, 50%, 25%)',
80 | 'widget_manager:settings:widget_layout:25|25|50' => 'Brede rechter kolom (25%, 25%, 50%)',
81 | 'widget_manager:settings:widget_layout:75|25' => 'Twee kolommen (75%, 25%)',
82 | 'widget_manager:settings:widget_layout:60|40' => 'Twee kolommen (60%, 40%)',
83 | 'widget_manager:settings:widget_layout:50|50' => 'Twee kolommen (50%, 50%)',
84 | 'widget_manager:settings:widget_layout:40|60' => 'Twee kolommen (40%, 60%)',
85 | 'widget_manager:settings:widget_layout:25|75' => 'Twee kolommen (25%, 75%)',
86 | 'widget_manager:settings:widget_layout:100' => 'Enkele kolom (100%)',
87 | 'widget_manager:action:manage:success' => 'Widget configuratie succesvol opgeslagen',
88 | );
89 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Access.php:
--------------------------------------------------------------------------------
1 | getParam('input_params', []);
22 | if (elgg_extract('entity_type', $input_params) !== 'object' || elgg_extract('entity_subtype', $input_params) !== 'widget') {
23 | return null;
24 | }
25 |
26 | $container = get_entity(elgg_extract('container_guid', $input_params));
27 | if (!$container instanceof \ElggEntity) {
28 | return null;
29 | }
30 |
31 | if ($container instanceof \ElggGroup) {
32 | $acl = $container->getOwnedAccessCollection('group_acl');
33 | if ($acl) {
34 | return [
35 | $acl->id => elgg_echo('groups:access:group'),
36 | ACCESS_LOGGED_IN => elgg_echo('access:label:logged_in'),
37 | ACCESS_PUBLIC => elgg_echo('access:label:public'),
38 | ];
39 | }
40 | } elseif ($container instanceof \ElggSite) {
41 | // special options for index widgets
42 |
43 | $widget = elgg_extract('entity', $input_params);
44 | if (!$widget instanceof \ElggWidget) {
45 | return null;
46 | }
47 |
48 | if (elgg_can_edit_widget_layout($widget->context)) {
49 | return [
50 | ACCESS_PRIVATE => elgg_echo('access:admin_only'),
51 | ACCESS_LOGGED_IN => elgg_echo('access:label:logged_in'),
52 | ACCESS_LOGGED_OUT => elgg_echo('access:label:logged_out'),
53 | ACCESS_PUBLIC => elgg_echo('access:label:public'),
54 | ];
55 | }
56 | }
57 |
58 | return null;
59 | }
60 |
61 | /**
62 | * Allow write access for index managers
63 | *
64 | * @param \Elgg\Event $event 'permissions_check', 'site'
65 | *
66 | * @return null|bool
67 | */
68 | public static function writeAccessForIndexManagers(\Elgg\Event $event): ?bool {
69 | $result = $event->getValue();
70 | if ($result) {
71 | return $result;
72 | }
73 |
74 | $entity = $event->getEntityParam();
75 | if (!$entity instanceof \ElggSite) {
76 | return null;
77 | }
78 |
79 | $user = $event->getUserParam();
80 | if (!$user instanceof \ElggUser) {
81 | return null;
82 | }
83 |
84 | $index_managers = explode(',', elgg_get_plugin_setting('index_managers', 'widget_manager', ''));
85 | return in_array($user->guid, $index_managers) ?: null;
86 | }
87 |
88 | /**
89 | * Creates the ability to see content only for logged_out users
90 | *
91 | * @param \Elgg\Event $event 'access:collections:read', 'user'
92 | *
93 | * @return null|array
94 | */
95 | public static function addLoggedOutReadAccess(\Elgg\Event $event): ?array {
96 |
97 | if (elgg_is_logged_in() && !elgg_is_admin_logged_in()) {
98 | return null;
99 | }
100 |
101 | $return_value = $event->getValue() ?: [];
102 |
103 | if (!is_array($return_value)) {
104 | $return_value = [$return_value];
105 | }
106 |
107 | $return_value[] = ACCESS_LOGGED_OUT;
108 |
109 | return $return_value;
110 | }
111 |
112 | /**
113 | * Checks if current user can edit a widget if it is in a context he/she can manage
114 | *
115 | * @param \Elgg\Event $event 'permissions_check', 'object'
116 | *
117 | * @return null|bool
118 | */
119 | public static function canEditWidgetOnManagedLayout(\Elgg\Event $event): ?bool {
120 | $user = $event->getUserParam();
121 | $entity = $event->getEntityParam();
122 |
123 | if ($event->getValue() || !$user instanceof \ElggUser || !$entity instanceof \ElggWidget) {
124 | return null;
125 | }
126 |
127 | if (!$entity->getOwnerEntity() instanceof \ElggSite) {
128 | // special permission is only for widget owned by site
129 | return null;
130 | }
131 |
132 | $context = $entity->context;
133 | return $context ? elgg_can_edit_widget_layout($context, $user->guid) : null;
134 | }
135 |
136 | /**
137 | * Registers the extra context permissions check event
138 | *
139 | * @param \Elgg\Event $event 'action:validate', 'widgets/[add|delete|move|save]'
140 | *
141 | * @return void
142 | */
143 | public static function moreRightsForWidgetManager(\Elgg\Event $event): void {
144 | if ($event->getType() === 'widgets/add') {
145 | elgg_register_event_handler('permissions_check', 'site', '\ColdTrick\WidgetManager\Access::writeAccessForIndexManagers');
146 | return;
147 | }
148 |
149 | $widget_guid = (int) get_input('widget_guid', get_input('guid'));
150 | if (empty($widget_guid)) {
151 | return;
152 | }
153 |
154 | $widget = elgg_call(ELGG_IGNORE_ACCESS, function() use ($widget_guid) {
155 | return get_entity($widget_guid);
156 | });
157 |
158 | if (!$widget instanceof \ElggWidget) {
159 | return;
160 | }
161 |
162 | if ($widget->canEdit()) {
163 | // the widgets action might not be able to get privately owned index widgets
164 | self::registerSQLBypass($widget_guid);
165 | }
166 |
167 | if ($event->getType() === 'widgets/move') {
168 | // allow 'index' widgets to be added to the same context as the current widget
169 | $widget_context = $widget->context;
170 |
171 | $index_widgets = elgg_get_widget_types('index');
172 |
173 | foreach ($index_widgets as $handler => $index_widget) {
174 | $contexts = $index_widget->context;
175 | $contexts[] = $widget_context;
176 | elgg_register_widget_type([
177 | 'id' => $handler,
178 | 'name' => $index_widget->name,
179 | 'description' => $index_widget->description,
180 | 'context' => $contexts,
181 | 'multiple' => $index_widget->multiple,
182 | ]);
183 | }
184 | }
185 | }
186 |
187 | /**
188 | * Registers a bypass sql suffix
189 | *
190 | * @param int $guid GUID of the entity to register sql bypass for
191 | *
192 | * @return void
193 | */
194 | protected static function registerSQLBypass(int $guid): void {
195 | elgg_register_event_handler('get_sql', 'access', function(\Elgg\Event $event) use ($guid) {
196 | if ($event->getParam('ignore_access')) {
197 | // no need to give extra access
198 | return null;
199 | }
200 |
201 | /**
202 | * @var QueryBuilder $qb
203 | */
204 | $qb = $event->getParam('query_builder');
205 | $table_alias = $event->getParam('table_alias');
206 | $guid_column = $event->getParam('guid_column');
207 |
208 | $alias = function ($column) use ($table_alias) {
209 | return $table_alias ? "{$table_alias}.{$column}" : $column;
210 | };
211 |
212 | $result = $event->getValue();
213 |
214 | $result['ors']['special_widget_access'] = $qb->compare($alias($guid_column), '=', $guid);
215 |
216 | return $result;
217 | });
218 | }
219 |
220 | /**
221 | * Only allow widget page delete by admins
222 | *
223 | * @param \Elgg\Event $event 'permissions_check:delete', 'object'
224 | *
225 | * @return boolean
226 | */
227 | public static function onlyAdminsCanDeleteWidgetPages(\Elgg\Event $event) {
228 | $user = $event->getUserParam();
229 | $entity = $event->getEntityParam();
230 |
231 | if (!$user instanceof \ElggUser || !$entity instanceof \WidgetPage) {
232 | return;
233 | }
234 |
235 | return $user->isAdmin();
236 | }
237 |
238 | /**
239 | * Only allow widget edit for private widgets
240 | *
241 | * @param \Elgg\Event $event 'view_vars', 'object/widget/edit'
242 | *
243 | * @return null|array
244 | */
245 | public static function allowPrivateWidgetEdit(\Elgg\Event $event): ?array {
246 | $result = $event->getValue();
247 | if (elgg_extract('entity', $result) instanceof \ElggEntity) {
248 | return $result;
249 | }
250 |
251 | $guid = (int) elgg_extract('guid', $result);
252 | $entity = elgg_call(ELGG_IGNORE_ACCESS, function() use ($guid) {
253 | return get_entity($guid);
254 | });
255 |
256 | if ($entity->canEdit()) {
257 | $result['entity'] = $entity;
258 | self::registerSQLBypass($guid);
259 | }
260 |
261 | return $result;
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Widgets.php:
--------------------------------------------------------------------------------
1 | getObject();
21 | if (!$object instanceof \ElggWidget) {
22 | return;
23 | }
24 |
25 | if ((int) $object->access_id !== ACCESS_PRIVATE) {
26 | return;
27 | }
28 |
29 | // Updates access for privately created widgets in a group or on site
30 | elgg_call(ELGG_IGNORE_ACCESS, function() use ($object) {
31 | $owner = $object->getOwnerEntity();
32 |
33 | if ($owner instanceof \ElggGroup) {
34 | $object->access_id = $owner->getOwnedAccessCollection('group_acl')->id;
35 | $object->save();
36 | } elseif ($owner instanceof \ElggSite) {
37 | $object->access_id = ACCESS_PUBLIC;
38 | $object->save();
39 | }
40 | });
41 | }
42 |
43 | /**
44 | * Applies the saved widgets config
45 | *
46 | * @param \Elgg\Event $event 'handlers', 'widgets'
47 | *
48 | * @return array
49 | */
50 | public static function applyWidgetsConfig(\Elgg\Event $event): array {
51 | $return_value = $event->getValue();
52 | foreach ($return_value as $id => $widget_definition) {
53 | if (!isset($widget_definition->originals)) {
54 | $widget_definition->originals = [
55 | 'multiple' => $widget_definition->multiple,
56 | 'context' => $widget_definition->context,
57 | ];
58 | }
59 |
60 | $widget_config = WidgetsSettingsConfig::instance()->getAll($widget_definition->id);
61 | if (empty($widget_config)) {
62 | continue;
63 | }
64 |
65 | // fix multiple
66 | if (isset($widget_config['multiple'])) {
67 | $widget_definition->multiple = (bool) elgg_extract('multiple', $widget_config);
68 | }
69 |
70 | // fix contexts
71 | $contexts = elgg_extract('contexts', $widget_config);
72 | if (empty($contexts)) {
73 | continue;
74 | }
75 |
76 | foreach ($contexts as $context => $context_config) {
77 | if (!isset($context_config['enabled'])) {
78 | continue;
79 | }
80 |
81 | $enabled = elgg_extract('enabled', $context_config);
82 | $existing_key = array_search($context, $widget_definition->context);
83 | if ($existing_key !== false) {
84 | // already existing in default contexts
85 | if (!$enabled) {
86 | // remove if disabled in config
87 | unset($widget_definition->context[$existing_key]);
88 | }
89 | } elseif ($enabled) {
90 | // add if not existing
91 | $widget_definition->context[] = $context;
92 | }
93 | }
94 |
95 | $return_value[$id] = $widget_definition;
96 | }
97 |
98 | return $return_value;
99 | }
100 |
101 | /**
102 | * Adds manage_widgets context so the widgets always show up in admin/manage/widgets
103 | *
104 | * @param \Elgg\Event $event 'handlers', 'widgets'
105 | *
106 | * @return array
107 | */
108 | public static function addManageWidgetsContext(\Elgg\Event $event): array {
109 | $return_value = $event->getValue();
110 | foreach ($return_value as $id => $widget_definition) {
111 | $widget_definition->context[] = 'manage_widgets';
112 | $return_value[$id] = $widget_definition;
113 | }
114 |
115 | return $return_value;
116 | }
117 |
118 | /**
119 | * Returns widget content from cache
120 | *
121 | * @param \Elgg\Event $event 'view_vars', 'object/widget/body'
122 | *
123 | * @return null|array
124 | */
125 | public static function getContentFromCache(\Elgg\Event $event): ?array {
126 | $widget = elgg_extract('entity', $event->getValue());
127 | if (!$widget instanceof \ElggWidget) {
128 | return null;
129 | }
130 |
131 | if (!self::isCacheableWidget($widget)) {
132 | return null;
133 | }
134 |
135 | $current_language = elgg_get_current_language();
136 | $cached_data = elgg_load_system_cache("widget_cache_{$widget->guid}_{$current_language}");
137 | if (empty($cached_data)) {
138 | return null;
139 | }
140 |
141 | $result = $event->getValue();
142 | $result[\Elgg\ViewsService::OUTPUT_KEY] = $cached_data;
143 |
144 | return $result;
145 | }
146 |
147 | /**
148 | * Prevent widget controls
149 | *
150 | * @param \Elgg\Event $event 'view_vars', 'object/widget/elements/controls'
151 | *
152 | * @return null|array
153 | */
154 | public static function preventControls(\Elgg\Event $event): ?array {
155 | $widget = elgg_extract('widget', $event->getValue());
156 | if (!$widget instanceof \ElggWidget) {
157 | return null;
158 | }
159 |
160 | if ($widget->widget_manager_hide_header !== 'yes') {
161 | return null;
162 | }
163 |
164 | if ($widget->canEdit()) {
165 | return null;
166 | }
167 |
168 | $result = $event->getValue();
169 | $result[\Elgg\ViewsService::OUTPUT_KEY] = '';
170 |
171 | return $result;
172 | }
173 |
174 | /**
175 | * Returns widget content from cache
176 | *
177 | * @param \Elgg\Event $event 'view', 'object/widget/body'
178 | *
179 | * @return void
180 | */
181 | public static function saveContentInCache(\Elgg\Event $event): void {
182 | $widget = elgg_extract('entity', elgg_extract('vars', $event->getParams()));
183 | if (!$widget instanceof \ElggWidget) {
184 | return;
185 | }
186 |
187 | if (!self::isCacheableWidget($widget)) {
188 | return;
189 | }
190 |
191 | $current_language = elgg_get_current_language();
192 | elgg_save_system_cache("widget_cache_{$widget->guid}_{$current_language}", $event->getValue());
193 | }
194 |
195 | /**
196 | * Checks if the provide widget is registered as a cacheable widget
197 | *
198 | * @param \ElggWidget $widget widget to check
199 | *
200 | * @return bool
201 | */
202 | public static function isCacheableWidget(\ElggWidget $widget): bool {
203 | static $cacheable_handlers;
204 | if (!isset($cacheable_handlers)) {
205 | $cacheable_handlers = elgg_trigger_event_results('cacheable_handlers', 'widget_manager', [], []);
206 | }
207 |
208 | return in_array($widget->handler, $cacheable_handlers);
209 | }
210 |
211 | /**
212 | * Fallback widget title urls for non widget manager widgets
213 | *
214 | * @param \Elgg\Event $event 'entity:url', 'object'
215 | *
216 | * @return null|string
217 | */
218 | public static function getWidgetURL(\Elgg\Event $event): ?string {
219 | $widget = $event->getEntityParam();
220 | if (!$widget instanceof \ElggWidget) {
221 | return null;
222 | }
223 |
224 | // custom urls always trump existing values
225 | return $widget->widget_manager_custom_url ?: null;
226 | }
227 |
228 | /**
229 | * Register the discussions widget to groups context
230 | *
231 | * @param \Elgg\Event $event 'handlers', 'widgets'
232 | *
233 | * @return null|\Elgg\WidgetDefinition[]
234 | */
235 | public static function addDiscussionsWidgetToGroup(\Elgg\Event $event): ?array {
236 |
237 | $context = $event->getParam('context');
238 | if ($context !== 'groups') {
239 | return null;
240 | }
241 |
242 | $container = $event->getParam('container');
243 | if (!$container instanceof \ElggGroup || !$container->isToolEnabled('forum')) {
244 | return null;
245 | }
246 |
247 | $return_value = $event->getValue();
248 |
249 | $return_value[] = WidgetDefinition::factory([
250 | 'id' => 'discussions',
251 | 'context' => ['groups'],
252 | ]);
253 |
254 | return $return_value;
255 | }
256 |
257 | /**
258 | * Add or remove widgets based on the group tool option
259 | *
260 | * @param \Elgg\Event $event 'group_tool_widgets', 'widget_manager'
261 | *
262 | * @return null|array
263 | */
264 | public static function groupToolWidgets(\Elgg\Event $event): ?array {
265 |
266 | $entity = $event->getEntityParam();
267 | if (!$entity instanceof \ElggGroup) {
268 | return null;
269 | }
270 |
271 | $return_value = $event->getValue();
272 | if (!is_array($return_value)) {
273 | return null;
274 | }
275 |
276 | // check different group tools for which we supply widgets
277 | if ($entity->isToolEnabled('forum')) {
278 | $return_value['enable'][] = 'discussions';
279 | } else {
280 | $return_value['disable'][] = 'discussions';
281 | }
282 |
283 | return $return_value;
284 | }
285 |
286 | /**
287 | * Checks if a user can manage current widget layout
288 | *
289 | * @param \Elgg\Event $event 'permissions_check', 'widget_layout'
290 | *
291 | * @return null|bool
292 | */
293 | public static function layoutPermissionsCheck(\Elgg\Event $event): ?bool {
294 | $user = $event->getUserParam();
295 | if (!$user instanceof \ElggUser || $event->getValue()) {
296 | return null;
297 | }
298 |
299 | // check if widgetpage manager can manage
300 | $page_owner = $event->getParam('page_owner');
301 | if ($page_owner instanceof \WidgetPage) {
302 | if ($page_owner->canEdit()) {
303 | return true;
304 | }
305 |
306 | return null;
307 | }
308 |
309 | // check if it is an index manager
310 | if ($event->getParam('context') !== 'index') {
311 | return null;
312 | }
313 |
314 | $index_managers = explode(',', elgg_get_plugin_setting('index_managers', 'widget_manager', ''));
315 | return in_array($user->guid, $index_managers) ?: null;
316 | }
317 | }
318 |
--------------------------------------------------------------------------------
/classes/ColdTrick/WidgetManager/Groups.php:
--------------------------------------------------------------------------------
1 | getValue();
31 |
32 | if (!is_array($return_value)) {
33 | $return_value = [];
34 | }
35 |
36 | $return_value[] = [
37 | 'name' => elgg_echo('groups'),
38 | 'widget_context' => 'groups',
39 | 'widget_columns' => 2,
40 | 'event' => 'create',
41 | 'entity_type' => 'group',
42 | 'entity_subtype' => null,
43 | ];
44 |
45 | return $return_value;
46 | }
47 |
48 | /**
49 | * Sets the widget manager tool option. This is needed because in some situation the tool option is not available.
50 | *
51 | * And add/remove tool enabled widgets
52 | *
53 | * @param \Elgg\Event $event 'update', 'group'
54 | *
55 | * @return void
56 | */
57 | public static function updateGroupWidgets(\Elgg\Event $event): void {
58 | $object = $event->getObject();
59 | if (!$object instanceof \ElggGroup || !elgg_is_active_plugin('groups')) {
60 | return;
61 | }
62 |
63 | $plugin_settings = elgg_get_plugin_setting('group_enable', 'widget_manager');
64 | if (!in_array($plugin_settings, ['yes', 'forced'])) {
65 | return;
66 | }
67 |
68 | if ($plugin_settings === 'forced') {
69 | // make widget management mandatory
70 | $object->widget_manager_enable = 'yes';
71 | } elseif ($object->widget_manager_enable !== 'yes') {
72 | // if optional but not enabled for this group
73 | return;
74 | }
75 |
76 | // add/remove tool enabled widgets
77 | $result = ['enable' => [], 'disable' => []];
78 | $params = ['entity' => $object];
79 | $result = (array) elgg_trigger_event_results('group_tool_widgets', 'widget_manager', $params, $result);
80 |
81 | if (empty($result)) {
82 | return;
83 | }
84 |
85 | // push an extra context for others to know what's going on
86 | elgg_push_context('widget_manager_group_tool_widgets');
87 |
88 | $current_widgets = elgg_get_widgets($object->guid, 'groups');
89 |
90 | // disable widgets
91 | $disable_widget_handlers = (array) elgg_extract('disable', $result, []);
92 | if (!empty($disable_widget_handlers) && !empty($current_widgets)) {
93 | foreach ($current_widgets as $column => $widgets) {
94 | if (!is_array($widgets) || empty($widgets)) {
95 | continue;
96 | }
97 |
98 | foreach ($widgets as $order => $widget) {
99 | // check if a widget should be removed
100 | if (!in_array($widget->handler, $disable_widget_handlers)) {
101 | continue;
102 | }
103 |
104 | // yes, so remove the widget
105 | $widget->delete();
106 |
107 | unset($current_widgets[$column][$order]);
108 | }
109 | }
110 | }
111 |
112 | // enable widgets
113 | $enable_widget_handlers = (array) elgg_extract('enable', $result, []);
114 | if (empty($enable_widget_handlers)) {
115 | // remove additional context
116 | elgg_pop_context();
117 |
118 | return;
119 | }
120 |
121 | $column_counts = [];
122 | $max_columns = elgg_trigger_event_results('groups:column_count', 'widget_manager', [], elgg_get_plugin_setting('group_column_count', 'widget_manager'));
123 | for ($i = 1; $i <= $max_columns; $i++) {
124 | $column_counts[$i] = 0;
125 | }
126 |
127 | // ignore access restrictions
128 | // because if a group is created with a visibility of only group members
129 | // the group owner is not yet added to the acl and thus can't edit the newly created widgets
130 | elgg_call(ELGG_IGNORE_ACCESS, function() use ($object, $current_widgets, $enable_widget_handlers, $column_counts) {
131 | if (!empty($current_widgets) && is_array($current_widgets)) {
132 | foreach ($current_widgets as $column => $widgets) {
133 | // count for later balancing
134 | $column_counts[$column] = count($widgets);
135 |
136 | if (empty($widgets) || !is_array($widgets)) {
137 | continue;
138 | }
139 |
140 | foreach ($widgets as $order => $widget) {
141 | // check if a widget which should be enabled isn't already enabled
142 | $enable_index = array_search($widget->handler, $enable_widget_handlers);
143 | if ($enable_index !== false) {
144 | // already enabled, do add duplicate
145 | unset($enable_widget_handlers[$enable_index]);
146 | }
147 | }
148 | }
149 | }
150 |
151 | $determine_target_column = function($column_counts) {
152 |
153 | $current_target = 1;
154 | $current_min = elgg_extract($current_target, $column_counts, 0);
155 |
156 | foreach ($column_counts as $column => $column_count) {
157 | if ($column_count < $current_min) {
158 | $current_target = $column;
159 | $current_min = $column_count;
160 | }
161 | }
162 |
163 | return $current_target;
164 | };
165 |
166 | // check blacklist
167 | $blacklist = $object->widget_manager_widget_blacklist;
168 | if (!empty($blacklist)) {
169 | $blacklist = json_decode($blacklist, true);
170 | foreach ($blacklist as $handler) {
171 | $enable_index = array_search($handler, $enable_widget_handlers);
172 | if ($enable_index === false) {
173 | // blacklisted item wasn't going to be added
174 | continue;
175 | }
176 |
177 | // widget was removed manually, don't add it automagically
178 | unset($enable_widget_handlers[$enable_index]);
179 | }
180 | }
181 |
182 | // add new widgets
183 | if (!empty($enable_widget_handlers)) {
184 | $widget_access_id = $object->access_id;
185 | if ($widget_access_id === ACCESS_PUBLIC && elgg_get_config('walled_garden')) {
186 | $widget_access_id = ACCESS_LOGGED_IN;
187 | }
188 |
189 | foreach ($enable_widget_handlers as $handler) {
190 | $widget_guid = elgg_create_widget($object->guid, $handler, 'groups', $widget_access_id);
191 | if (empty($widget_guid)) {
192 | continue;
193 | }
194 |
195 | $widget = get_entity($widget_guid);
196 |
197 | $target_column = $determine_target_column($column_counts);
198 |
199 | // move to the end of the target column
200 | $widget->move($target_column, 9000);
201 | $column_counts[$target_column]++;
202 | }
203 | }
204 | });
205 |
206 | // remove additional context
207 | elgg_pop_context();
208 | }
209 |
210 | /**
211 | * Bypasses the widgets content on the group profile
212 | *
213 | * @param \Elgg\Event $event 'view_vars', 'groups/profile/widgets'
214 | *
215 | * @return null|array
216 | */
217 | public static function getGroupWidgetsLayout(\Elgg\Event $event): ?array {
218 | $vars = $event->getValue();
219 | $group = elgg_extract('entity', $vars);
220 |
221 | if (!$group instanceof \ElggGroup) {
222 | return null;
223 | }
224 |
225 | $group_enable = elgg_get_plugin_setting('group_enable', 'widget_manager');
226 | if (!in_array($group_enable, ['forced', 'yes'])) {
227 | return null;
228 | }
229 |
230 | if ($group_enable === 'yes' && !$group->isToolEnabled('widget_manager')) {
231 | return null;
232 | }
233 |
234 | // need context = groups to fix the issue with the new group_profile context
235 | elgg_push_context('groups');
236 |
237 | $num_columns = (int) elgg_extract('num_columns', $vars, elgg_get_plugin_setting('group_column_count', 'widget_manager'));
238 |
239 | $vars[\Elgg\ViewsService::OUTPUT_KEY] = elgg_view_layout('widgets', [
240 | 'num_columns' => $num_columns,
241 | 'class' => [
242 | "widgets-{$num_columns}-columns",
243 | ],
244 | ]);
245 |
246 | elgg_pop_context();
247 |
248 | return $vars;
249 | }
250 |
251 | /**
252 | * Adds the group tool option
253 | *
254 | * @param \Elgg\Event $event 'tool_options', 'group'
255 | *
256 | * @return null|Collection
257 | */
258 | public static function registerGroupWidgetsTool(\Elgg\Event $event): ?Collection {
259 | $plugin = elgg_get_plugin_from_id('widget_manager');
260 | if ($plugin->getSetting('group_enable') !== 'yes') {
261 | return null;
262 | }
263 |
264 | $result = $event->getValue();
265 |
266 | if ($plugin->getSetting('group_option_admin_only') !== 'yes' || elgg_is_admin_logged_in()) {
267 | // add the tool option for group admins
268 | $result[] = new Tool('widget_manager', [
269 | 'label' => elgg_echo('widget_manager:groups:enable_widget_manager'),
270 | 'default_on' => $plugin->getSetting('group_option_default_enabled') === 'yes',
271 | ]);
272 | }
273 |
274 | return $result;
275 | }
276 |
277 | /**
278 | * Prepare for group widget blacklist update when adding a widget manually
279 | *
280 | * @param \Elgg\Event $event 'create', 'object'
281 | *
282 | * @return void
283 | */
284 | public static function addGroupWidget(\Elgg\Event $event): void {
285 | $object = $event->getObject();
286 | if (!$object instanceof \ElggWidget || elgg_in_context('widget_manager_group_tool_widgets') || !elgg_is_active_plugin('groups')) {
287 | return;
288 | }
289 |
290 | $group_enable = elgg_get_plugin_setting('group_enable', 'widget_manager');
291 | if (!in_array($group_enable, ['yes', 'forced'])) {
292 | return;
293 | }
294 |
295 | $owner = $object->getOwnerEntity();
296 | if (!$owner instanceof \ElggGroup) {
297 | // not a group widget
298 | return;
299 | }
300 |
301 | $blacklist = $owner->widget_manager_widget_blacklist;
302 | if (empty($blacklist)) {
303 | // no blacklisted widgets, so no cleanup needed
304 | return;
305 | }
306 |
307 | global $widget_manager_group_guids;
308 | if (!isset($widget_manager_group_guids)) {
309 | $widget_manager_group_guids = [];
310 |
311 | elgg_register_event_handler('shutdown', 'system', self::class . '::addGroupWidgetShutdown');
312 | }
313 |
314 | $widget_manager_group_guids[$owner->guid][] = $object->guid;
315 | }
316 |
317 | /**
318 | * Update the group widget blacklist when adding a widget manually
319 | *
320 | * @param \Elgg\Event $event 'shutdown', 'system'
321 | *
322 | * @return void
323 | */
324 | public static function addGroupWidgetShutdown(\Elgg\Event $event): void {
325 | global $widget_manager_group_guids;
326 |
327 | if (empty($widget_manager_group_guids)) {
328 | return;
329 | }
330 |
331 | elgg_call(ELGG_IGNORE_ACCESS, function() use ($widget_manager_group_guids) {
332 |
333 | foreach ($widget_manager_group_guids as $owner_guid => $widget_guids) {
334 | if (empty($widget_guids)) {
335 | continue;
336 | }
337 |
338 | $owner = get_entity($owner_guid);
339 | if (!$owner instanceof \ElggGroup) {
340 | continue;
341 | }
342 |
343 | $blacklist = $owner->widget_manager_widget_blacklist;
344 | if (empty($blacklist)) {
345 | // no blacklisted widgets, so no cleanup needed
346 | continue;
347 | }
348 |
349 | $blacklist = json_decode($blacklist, true);
350 |
351 | foreach ($widget_guids as $guid) {
352 | $widget = get_entity($guid);
353 | if (!$widget instanceof \ElggWidget) {
354 | continue;
355 | }
356 |
357 | if (!in_array($widget->handler, $blacklist)) {
358 | // not blacklisted, so no cleanup needed
359 | continue;
360 | }
361 |
362 | $key = array_search($widget->handler, $blacklist);
363 | unset($blacklist[$key]);
364 | }
365 |
366 | if (empty($blacklist)) {
367 | unset($owner->widget_manager_widget_blacklist);
368 | } else {
369 | $owner->widget_manager_widget_blacklist = json_encode($blacklist);
370 | }
371 | }
372 | });
373 | }
374 |
375 | /**
376 | * Update the group widget blacklist when removing a widget manually
377 | *
378 | * @param \Elgg\Event $event 'delete', 'object'
379 | *
380 | * @return void
381 | */
382 | public static function deleteGroupWidget(\Elgg\Event $event): void {
383 | $object = $event->getObject();
384 | if (!$object instanceof \ElggWidget || elgg_in_context('widget_manager_group_tool_widgets') || !elgg_is_active_plugin('groups')) {
385 | return;
386 | }
387 |
388 | $group_enable = elgg_get_plugin_setting('group_enable', 'widget_manager');
389 | if (!in_array($group_enable, ['yes', 'forced'])) {
390 | return;
391 | }
392 |
393 | $owner = $object->getOwnerEntity();
394 | if (!$owner instanceof \ElggGroup) {
395 | // not a group widget
396 | return;
397 | }
398 |
399 | $handlers = elgg_call(ELGG_IGNORE_ACCESS, function() use ($owner, $object) {
400 | // get all group widgets
401 | $group_widgets = elgg_get_widgets($owner->guid, 'groups');
402 | $handlers = [];
403 | if (!empty($group_widgets)) {
404 | foreach ($group_widgets as $column => $widgets) {
405 | if (empty($widgets)) {
406 | continue;
407 | }
408 |
409 | /* @var $widget \ElggWidget */
410 | foreach ($widgets as $widget) {
411 | if ($widget->guid === $object->guid) {
412 | // don't add yourself
413 | continue;
414 | }
415 |
416 | $handlers[] = $widget->handler;
417 | }
418 | }
419 |
420 | $handlers = array_unique($handlers);
421 | }
422 |
423 | return $handlers;
424 | });
425 |
426 | if (in_array($object->handler, $handlers)) {
427 | // not the last widget of it's type
428 | return;
429 | }
430 |
431 | $blacklist = $owner->widget_manager_widget_blacklist;
432 | if (!empty($blacklist)) {
433 | // blacklisted widgets
434 | $blacklist = json_decode($blacklist, true);
435 | } else {
436 | $blacklist = [];
437 | }
438 |
439 | if (in_array($object->handler, $blacklist)) {
440 | // already on the blacklist
441 | return;
442 | }
443 |
444 | $blacklist[] = $object->handler;
445 |
446 | // store new blacklist
447 | $owner->widget_manager_widget_blacklist = json_encode($blacklist);
448 | }
449 | }
450 |
--------------------------------------------------------------------------------
/CHANGES.txt:
--------------------------------------------------------------------------------
1 | Version history
2 | ===============
3 |
4 | 17.1.2 (2025-10-17):
5 |
6 | - fixed: PHP implicit null declaration
7 | - fixed: use correct return type in group tool event handler
8 | - fixed: widget managers should be able to manage private widgets
9 |
10 | 17.1.1 (2025-01-23):
11 |
12 | - fixed: use flex grow strategy for different column sizes
13 |
14 | 17.1 (2024-10-10):
15 |
16 | - added: widget cache now store cache per language
17 | - changed: invalidating widget cache using entity invalidateCache function
18 | - fixed: unable to create new widget page
19 |
20 | 16.2 (2024-08-07):
21 |
22 | - added: widget page manager can now edit page on the front end
23 |
24 | 17.0 (2024-06-28):
25 |
26 | - changed: updated for Elgg 6.0
27 |
28 | 16.1.1 (2024-06-05):
29 |
30 | - several small chores/fixes
31 |
32 | 16.1 (2024-04-03):
33 |
34 | - added: widget pages now can have a description and are searchable
35 |
36 | 16.0.2 (2024-03-14):
37 |
38 | - several small chores/fixes
39 |
40 | 16.0.1 (2024-01-12):
41 |
42 | - fixed: toggling widget body should not influence layout
43 |
44 | 16.0 (2023-11-30):
45 |
46 | - changed: updated for Elgg 5.1
47 |
48 | 15.1.1 (2023-07-26):
49 |
50 | - fixed: buttons have a discernible text
51 |
52 | 15.1 (2023-07-05):
53 |
54 | - added: widget page database seeder
55 | - fixed: return correct type
56 |
57 | 15.0 (2023-06-02):
58 |
59 | - added: migration for old widget settings
60 | - changed: conflict with Elgg < 5.0
61 | - changed: updated for Elgg 5
62 | - fixed: expand access to allow editing private widgets on index pages
63 | - fixed: prevent error in shutdown hook
64 |
65 | 14.1.1 (2022-10-27):
66 |
67 | - fixed: Dutch translation file encoding
68 |
69 | 14.1 (2022-10-07):
70 |
71 | - added: toggle link for widget layout editors to hide/show widget content
72 | - changed: fluid layout widget are lazy loaded after 3 * the fold limit
73 | - changed: widget page display name falls back to url
74 | - fixed: correctly pass show_access to newly added widgets
75 |
76 | 14.0 (2022-08-24):
77 |
78 | - changed: updated for Elgg 4.3
79 |
80 | 13.1 (2022-06-22):
81 |
82 | - removed: no longer provide widget toggle/collapse features
83 |
84 | 13.0 (2022-06-01):
85 |
86 | - added: discussions widget is now available in groups context
87 | - added: plugin setting to control number of columns in group widgets
88 | - changed: updated for Elgg 4.2
89 |
90 | 12.0.3 (2022-02-16):
91 |
92 | - fixed: cast widget context as string
93 |
94 | 12.0.2 (2021-10-27):
95 |
96 | - fixed: use route generation instead of fixed url
97 |
98 | 12.0.1 (2021-10-12):
99 |
100 | - fixed: added missing class in group widget layout
101 | - fixed: check for array when saving settings
102 | - fixed: get all widgets when checking for collapsed state
103 |
104 | 12.0 (2021-09-27):
105 |
106 | - changed: updated for Elgg 4
107 | - fix: prevent widgets being auto created public on walled garden site
108 | - removed: no longer supports a top row column in widget layout
109 | - removed: widgets can no longer be positioned fixed in a layout
110 |
111 | 11.4 (2021-07-06):
112 |
113 | - changed: replaced packer with muuri for fluid layouts
114 |
115 | 11.3 (2021-06-22):
116 |
117 | - added: fluid/packery/masonry index layout
118 |
119 | 11.2.1 (2021-06-08):
120 |
121 | - fixed: correctly pass widget entity in lazy loading hook
122 | - fixed: multiple layouts should not connect with eachother
123 |
124 | 11.2 (2021-05-25):
125 |
126 | - added: improved core widget logic if you have multiple layouts on a page
127 | - fixed: correctly detected if lazy loading is needed for a widget layout
128 |
129 | 11.1 (2021-01-05):
130 |
131 | - added: lazy loading widget features
132 |
133 | 11.0.1 (2020-12-08):
134 |
135 | - fixed: make sure custom link for widgets are always used
136 | - fixed: update widget title correctly with new title and href
137 |
138 | 11.0 (2020-05-26):
139 |
140 | - changed: increased minimal Elgg requirement to 3.3
141 | - changed: layouts now use the default layout
142 | - changed: manage widget now instant saves setting
143 | - changed: the widget settings config can now be used as a service
144 | - fixed: do not crash when viewing unsaved widgets
145 | - fixed: make sure all widgets are manageable
146 | - removed: unused function widget_manager_sort_widgets
147 |
148 | 10.0.2 (2019-10-15):
149 |
150 | - fixed: bulk update widget access in groups now show correct access opts
151 | - fixed: removed widgets blacklist correctly populated
152 |
153 | 10.0.1 (2019-09-18):
154 |
155 | - fixed: index manager now can manage admin only widgets
156 |
157 | 10.0 (2019-08-22):
158 |
159 | - changed: minimal Elgg requirement is now 3.1
160 | - changed: moved last hooks lib functions to classes
161 |
162 | 9.0.4 (2019-08-07):
163 |
164 | - fixed: correctly check if user is allowed to manage a widget page
165 |
166 | 9.0.3 (2019-07-11):
167 |
168 | - fixed: index manager can create widgets again
169 | - fixed: provide correct write access arrays
170 |
171 | 9.0.2 (2019-05-02):
172 |
173 | - changed: widget pages urls are stored in system cache for performance
174 |
175 | 9.0.1 (2019-04-17):
176 |
177 | - several small chores/fixes
178 |
179 | 9.0 (2019-03-27):
180 |
181 | - added: admin feature to delete widgets from the database
182 | - added: max columns for group tool based widgets can now be hooked
183 | - added: setting a widget page title sets it as a page title
184 | - added: widget pages are now their own entities
185 | - changed: advanced widget settings now show in a tab
186 | - changed: custom widget title is now returned from getDisplayName
187 | - changed: more changes for Elgg 3.0
188 | - changed: more changes for Elgg 3.0
189 | - changed: moved extra contexts page handler to route registration
190 | - changed: moved widget_manager_check_collapsed_state function into class
191 | - changed: started updating code for Elgg 3.0
192 | - changed: toggle form elements now are switches
193 | - changed: widget cache now uses system_cache
194 | - changed: widget settings save now uses elgg/Ajax
195 | - changed: widgets no longer cache private settings, now done by core
196 | - feat: added collapse/expand functions to widget class
197 | - fixed: force correct column widths for widgets
198 | - removed: no longer support fixed height setting on widgets
199 | - Spanish translation
200 |
201 | 8.3.2 (2018-10-04):
202 |
203 | - changed: fallback logic for widget setting
204 |
205 | 8.3.1 (2018-06-28):
206 |
207 | - changed: group tool widgets are less persistent
208 |
209 | 8.3 (2018-06-15):
210 |
211 | - added: plugin setting to allow normal users to manage index widgets
212 |
213 | 8.2.2 (2018-03-28):
214 |
215 | - fix: memcache could contain the incorrect entity class for new widgets
216 |
217 | 8.2.1.1 (2018-03-22):
218 |
219 | - several small chores/fixes
220 |
221 | 8.2.1 (2018-03-22):
222 |
223 | - changed: override getContext to be able to use cache
224 |
225 | 8.2 (2017-11-15):
226 |
227 | - added: widget collapse capability is now hookable
228 |
229 | 8.1 (2017-10-19):
230 |
231 | - changed: allow to set a custom more link on every widget on all contexts
232 | - changed: use elgg_view_field function for widget form fields
233 |
234 | 8.0 (2017-02-27):
235 |
236 | - changed: updated min requirements to Elgg 2.3
237 |
238 | 7.0.1 (2016-10-27):
239 |
240 | - fixed: it was not possible to add widgets to extra widgets pages
241 | - fixed: widget default collapsed state correctly set for logged out user
242 |
243 | 7.0 (2016-10-06):
244 |
245 | - added: new way of widget management with ability to change contexts
246 | - changed: refactored widget object view
247 | - fixed: replace usage of deprecated elgg.ui.widgets.add
248 | - fixed: require correct Elgg version for elgg/widgets AMD module
249 | - removed: moved multi_dashboard features to own plugin
250 | - removed: moved widgets to a separate widget_pack plugin
251 |
252 | 6.2.1 (2016-08-26):
253 |
254 | - fixed: php notice when page already routed
255 |
256 | 6.2 (2016-06-16):
257 |
258 | - changed: content_by_tag widget now preloads owners and containers
259 | - changed: do not include container object in results content_by_tag
260 | - changed: moved event functions to class files
261 | - changed: moved pagehandler functions to class functions
262 | - changed: moved pagesetup actions to separate class functions
263 | - changed: moved plugin hooks into the init function
264 | - changed: moved router hooks to class functions
265 | - changed: moved some more hooks to class functions
266 | - changed: moved the widget_manager_update_widget registration in init
267 | - changed: moved various hooks to class functions
268 | - changed: split widget_manager_init into multiple init functions
269 | - changed: use translatable short month/day in slim view content_by_tag
270 | - fixed: correctly get widget collapse state due to event order change
271 | - fixed: declare elgg/widgets js dependancy to prevent timing issues
272 |
273 | 6.1 (2016-05-26):
274 |
275 | - added: dashboard entity URLs can now be filtered
276 | - fixed: MultiDashboard::save() no longer sets attributes
277 | - fixed: only show edit icon if dashboard is editable
278 | - fixed: do not crash the loader if there are no private settings
279 | - fixed: moved elgg-widgets sortable modifications to system init event
280 |
281 | 6.0.1 (2016-05-04):
282 |
283 | - fixed: widget cache was not resetting correctly on cache flush
284 |
285 | 6.0 (2016-04-13):
286 |
287 | - added: cached data for cacheable widgets is now reset on cache flush
288 | - fixed: accidentally deactivated registration cacheable widgets
289 | - fixed: widget cache clear took too long
290 |
291 | 6.0-beta (2016-01-26):
292 |
293 | - added: support for Elgg 2.0 added
294 | - removed: no longer provide favorites widget
295 | - removed: no longer provide index_bookmarks widget
296 | - removed: no longer provide tagcloud widget
297 |
298 | 5.2 (2016-01-20):
299 |
300 | - added: ability for group owners to update all widgets access level
301 | - added: content by tag widget now supports static pages
302 | - added: option to alphabetically sort content in content_by_tag widget
303 | - added: support for poll and questions in content_by_tag widget
304 | - added: allow additional contexts based on default widgets config
305 | - added: use title menu for Add widgets button
306 | - changed: content_by_tag supported plugins list is now centralized
307 | - changed: moved favorites extras menu item to a register hook
308 | - changed: widget add panel contents are fetched on demand
309 | - changed: convert favorites widgets JS to an AMD module
310 | - changed: convert site and admin JS to AMD
311 | - changed: dutch translation
312 | - fixed: community favorites working again and fixed saving of title
313 | - fixed: incorrect access options available in specific widgets contexts
314 | - fixed: more specific selector for widget insert
315 | - fixed: only output icon if there is any
316 | - fixed: user search widget content not loaded when search with spaces
317 | - fixed: wrong composer include
318 |
319 | 5.1 (2015-07-08):
320 |
321 | - added: mechanisme to permanently cache widgets based on their handler
322 | - added: support for object/thewire entities
323 | - added: composer support
324 | - added: widget edit and delete icon now only show when hovering widget
325 | - changed: reorganized javascript
326 | - changed: Simplepie library is now loaded by composer
327 | - changed: delayed loading of the AMD modules to document ready to prevent conflicts
328 | - fixed: correctly ajax load search results for user_search widget
329 | - fixed: performance of rss server widget
330 | - fixed: replaced deprecate ->get calls with variable variables
331 | - fixed: replaced deprecated use of class in ElggMenuItem::factory
332 | - fixed: output/confirmlink deprecation notices
333 | - fixed: content by tag widget case insensitive tag search
334 | - fixed: moved favorites toggle action file to correct location
335 | - fixed: non admins can not correctly manage widgets on landing pages
336 | - fixed: twitter search embed code parsing failed
337 | - fixed: use input/text view to output search input on addpanel
338 |
339 | 5.0 (2014-12-08):
340 |
341 | - added: widget collapsed state is remember for logged in users in the database
342 | - added: widget advanced setting to set default collapse state
343 | - added: widget advanced setting to set availability of collapse toggle button
344 | - added: custom more link for widgets
345 | - added: group forum topics added to content by tag widget
346 | - added: content by tag support multi tag search link if search_advanced is available
347 | - added: widget setting is showed in a lightbox
348 | - added: support for bookmark_tools link setting
349 | - added: extra css class to widget layout so there is more styling control
350 | - added: client side rss widget using Google Feed API
351 | - added: plugin setting to force use of group widgets
352 | - added: hooks to have widgets added/removed when related group tool option is toggled
353 | - added: admin action to update widgets on all groups to verify group tool option
354 | - added: single column layout
355 | - added: iframe widget
356 | - added: rss widget now has option to show feed contents in lightbox
357 | - added: per rss widget setting of feed timeout
358 | - added: content by tag widget now also has an exclusion list of tags
359 | - added: extra context pages now also support top row config
360 | - changed: hide header widget option also hides for admins
361 | - changed: free html widget now uses long text so there is an editor
362 | - changed: updated for Elgg 1.9
363 | - changed: split css views
364 | - changed: updated to Elgg coding standards
365 | - changed: replaced some language keys with core versions
366 | - changed: relocated widgets code
367 | - changed: plugin settings view only shows applicable
368 | - fixed: widgets created in groups get unusable access #25
369 | - fixed: ability to globally hide a widget on the widgets manage page
370 | - fixed: issue with extra minus sign in content by tag widget #31
371 | - deprecated: widgets url hook is no longer usable, use the entity url hook if you want to set widget title links
372 | - removed: widget url links for non core plugins (plugins should provide themselves)
373 |
374 | 4.8 (2014-03-07):
375 |
376 | - added: ability to add custom pages with index widgets
377 | - added: content_by_tag widget now support multiple content_types selected
378 | - added: plugin setting to control default dashboard column layout
379 | - added: content_by_tag now also support event_manager, tasks and videolist entities
380 | - added: rss widget excerpt added to title of rss post link
381 | - changed: applied Elgg coding standards on almost all code
382 | - removed: obsolete group_news widget code (was already moved to group_tools)
383 | - fixed: content_by_tag widget bookmarks entities now have direct address link
384 |
385 | 4.7 (2013-08-30):
386 |
387 | - added: group default widget support (thanks to Connecting Conservation)
388 | - added: show avatar and show timestamp options to content_by_tag widget
389 | - fixed: respect limit set in tagcloud widget settings
390 |
391 | 4.6 (2013-06-27):
392 |
393 | - added: content_by_tag widget also support bookmarks
394 | - added: content_by_tag widget has an optional search link
395 | - added: optional fixed widget height
396 | - changed: twitter_search widget now uses twitters new widget embed code
397 | - changed: rss widget uses new version of SimplePie library
398 | - fixed: memcache reset on widget settings save
399 |
400 | 4.5 (2013-05-06):
401 |
402 | - added: admin users search widget (also searches disabled/blocked/unvalidated users and by email)
403 | - fixed: incorrect has_widget check for favorites widget
404 | - fixed: some php notices
405 |
406 | 4.4 (2013-04-05):
407 |
408 | - added: experimental likes widget
409 | - changed: use system cache for some widget settings
410 | - removed: widget statistics in favor of advanced_statistics plugin
411 | - fixed: access options should not show up after adding a widget to dashboard
412 | - fixed: tags are stripped from dashboard title
413 | - fixed: slider output not closing anchor when using link
414 |
415 | 4.3 (2012-07-26):
416 |
417 | - added: favorites widget
418 | - added: updates widget titles without page refresh (thanks to Matt Beckett)
419 | - added: new 'simple' layout in content_by_tag widget
420 | - changed: freehtml is now contained within a div with elgg-output class (better styling)
421 | - changed: manifest requirements (needs to be after twitter and tagcloud plugins)
422 | - changed: Natural sorting of widgets (for example in add panel)
423 | - changed: content by tag widget now allows to show all site content from within a group
424 | - removed: index_file and group_files widgets moved to file_tools plugin
425 | - removed: unused language keys
426 | - removed: index_pages widget (moved to pages_tools plugin)
427 | - removed: widgets_pagesetup event trigger
428 | - fixed: support for old widget settings in index_activity widget
429 | - fixed: add panel keeps correct layout if no widget description is given
430 | - fixed: some layout issues when using IE7
431 | - fixed: no need for flooding apache error logs with the error message that a rss feed could not be found (SimplePie)
432 |
433 | 4.2 (2012-05-10):
434 |
435 | - added: re-enabled index_activity widget
436 | - added: widget class now is able to handle arrays as widget settings
437 | - added: re-enabled entity statistics widget
438 | - changed: now admin can always drag and edit/remove fixed widgets
439 | - changed: better/extendable check for advanced widget features
440 | - changed: widget title links can now be set with a hook
441 | - changed: default hide feed title in rss widget
442 | - changed: reduced language keys by replacing them with default core language keys
443 | - changed: available widgets context is now adjustable with a plugin hook
444 | - changed: widgetmanager controls widget class
445 | - removed: unnecessary widget context check, we trust all contexts
446 | - removed: group related widgets (moved to group_tools plugin)
447 | - fixed: a lot of php notices / warnings
448 | - fixed: group top row missing
449 | - fixed: a nasty z-index in twitter search widget
450 | - fixed: 2 column index layout do not merge column 2 and 3
451 | - fixed: multidashboards are showing on the admin side
452 | - fixed: edit twitter search not remembering height param
453 | - fixed: some default core widget title urls
454 | - fixed: only show multidashboard extras button when logged in
455 |
456 | 4.1.1 (2012-03-19):
457 |
458 | - changed: button styling on advanced widget settings
459 | - changed: add tab is now a tab instead of a button
460 | - fixed: group admins can not add/edit widgets on a group profile page
461 |
462 | 4.1 (2012-02-12):
463 |
464 | - added: Multi Dashboard support
465 | - changed: updated the Dutch language file
466 | - changed: required Elgg version to 1.8.3
467 |
468 | 4.0 beta (2011-12-29):
469 |
470 | - added: support for Elgg 1.8
471 | - added: widget sortable tolerance changes to pointer instead of intersect (needed for index widgets top row)
472 | - added: widget usage statistics page
473 | - changed: temporarily disabled the following widgets (index_activity, entity_statistics, river_widget); awaiting core fix
474 | - changed: fix mechanisme for default widgets
475 | - removed: run_once fix for elgg widget object/subtype class (obsolete)
476 | - removed: run_once migration from group_custom_layout plugin
477 | - removed: lazy loading plugin setting
478 | - removed: target column setting
479 | - removed: auto-cleanup of broken widgets
480 | - removed: plugin option to show broken widgets
481 | - removed: option to configure if a widget is multiple or single (this is now handled correct by Elgg core)
482 | - removed: option to configure if a widget is deletable (use 'fix widget' instead)
483 | - removed: group_event_calendar widget
484 | - removed: top row 2 column right option for index page
485 | - removed: various widgets without a valid 1.8 version of their plugin (izap_videos, tidypics, videolist, tasks)
486 |
487 | 3.9 (2011-11-22):
488 |
489 | - added: messages widget (show new messages from inbox)
490 | - added: entity_statistics widget (index only)
491 | - added: images to rss feed items (only if showing excerpt) in rss widget
492 | - added: flexslider as a new slider type for image slider widget
493 | - changed: content_by_tag widget now also available as group widget
494 | - changed: activity widgets can be filtered by multiple type/subtypes
495 |
496 | 3.8 (2011-11-09):
497 |
498 | - added: new widget header layout (getting used to Elgg 1.8)
499 | - added: optional filter for index_activity and river_widget to filter content based on type/subtype
500 |
501 | 3.7.2 (2011-10-04):
502 |
503 | - fixed: icon error in content_by_tag widget in slim mode
504 | - fixed: group widget access pulldown not showing correct groupname
505 | - changed: moved event functions to different file
506 | - changed: moved pluginhook functions to different file
507 |
508 | 3.7.1 (2011-08-15):
509 |
510 | - removed: all the wire widgets (moved to thewire_tools plugin)
511 |
512 | 3.7 (2011-08-11):
513 |
514 | - changed: major performance gains
515 |
516 | 3.6.1 (2011-07-22):
517 |
518 | - changed: layout of tagcloud
519 | - changed: updated some js to be smaller
520 | - changed: loading of js and css of image_slider and twitter widget
521 | - fixed: wrong key when no data for tagcloud
522 |
523 | 3.6 (2011-06-21):
524 |
525 | - added: auto cleanup for broken widgets (if broken -> delete)
526 | - fixed: widget edit link is not showing if a widget is loaded collapsed
527 | - fixed: with broken widgets on some widgets crash the site
528 | - fixed: group widgets settings not being saved
529 | - fixed: fancybox ie alpha functions url
530 |
531 | 3.5 (2011-06-17):
532 |
533 | - added: group news widget
534 | - fixed: the_wire extend should not be visible when not loggedin
535 |
536 | 3.4 (2011-06-07):
537 |
538 | - added: group tasks widgets (tasks plugin)
539 | - added: tagcloud widget
540 | - added: optionally prepends the thewire widget with post form (default on)
541 | - changed: ordering of widgets in lightbox are now alphabetically
542 | - fixed: page crashes on default widgets configuration => no longer showing widget body
543 | - fixed: wrong class handler for object => widget
544 | - fixed: slider widget fix no text -> no slide over
545 | - removed: group_river_widget moved to group_tools plugin
546 |
547 | 3.3 (2011-05-24):
548 |
549 | - added: custom url for widget title added for group and index widgets
550 | - changed: widget options for content_by_tag and rss widget
551 | - fixed: free_html admin ignore option not working
552 | - fixed: coding error in rss widget view
553 | - fixed: Fancybox CSS errors in IE
554 |
555 | 3.2 (2011-05-06):
556 |
557 | - added: thewire_post widget to post directly to the wire from a widget
558 | - fixed: socialink layout in index_login widget
559 | - fixed: group_forum_topics widget when nog items
560 | - fixed: z-index to low with new fancybox
561 |
562 | 3.1 (2011-05-03):
563 |
564 | - added: new widget group_river_widget to show activity from a specific group
565 | - added: support for categories plugin to content_by_tag widget
566 | - added: default twitter widget view override to support https
567 | - added: option to enable group widget management by default
568 | - added: option to only allow admins to enable group widget management
569 | - added: group tool option to enable/disable widget manager group management
570 | - added: caching of rss feeds in dataroot + cron cache cleanup job
571 | - changed: custom title made available for all widgets on all contexts
572 | - changed: custom hook to set ACL options for widgets
573 | - changed: access input options on group widgets are limited to "group", "loggedin", "public"
574 | - changed: updated fancybox to 1.3.4 (be aware of conflicts with other plugins using fancybox)
575 | - changed: rss widget now available on profile and dashboard
576 | - fixed: default access on group widget is group_acl
577 | - fixed: dropping widgets on empty right column in group layout not possible
578 | - fixed: wrong context in lightbox when adding widgets to default profile/dashboard
579 | - fixed: input pulldown in widget edit panel need to be restricted to the edit panel
580 | - fixed: river_widget title link location (activity)
581 |
582 | 3.0 beta (2011-04-05):
583 |
584 | - added: new way to manage default widgets (profile/dashboard) (not perfect yet, but it works). It now allows default settings for widgets.
585 | - added: option to disallow editing / collapsing default widgets
586 | - added: manage group widgets with widget manager (enable in plugin settings). This replaces widget features from group custom layout plugin
587 | - added: group widgets
588 | - added: css class on fixed widget headers
589 | - added: search widgets in lightbox popup
590 | - added: various widgets to various contexts
591 | - changed: grouped admin only widget edit options into 'Advanced' section
592 | - changed: widgets are consolidated in widget_manager/widgets
593 | - fixed: calling undefined function in /lib/functions.php (line 234) when using Elgg < 1.7
594 | - fixed: adding widgets to other users profile uses wrong page_owner in lightbox
595 | - removed: support for dropdown list of widgets. Now always in a lightbox
596 | - removed: old default widget placement mechanism
597 |
598 | 2.1 (2011-01-24):
599 |
600 | - added: indicator for widget without header for admins
601 | - added: option to disable widget content styling
602 | - added: twitter_search widget
603 | - added: content_by_tag widget
604 | - added: first row on front page
605 | - added: custom_index widget links
606 | - changed: frontpage layout to div layout
607 | - fixed: widget edit options now more secure
608 |
609 | 2.0 (2011-01-13):
610 |
611 | - added: option to design custom index with widgets
612 | - added: custom index widget layouts (in admin options)
613 | - added: custom index widgets
614 | - added: hide widget title (on custom index only)
615 | - added: custom widget title (on custom index only)
616 | - fixed: issue with context when hiding broken widgets
617 |
618 | 1.1 (2010-12-24):
619 |
620 | - added: admin option to hide a widget (also existing widgets on profiles will be hidden with this feature)
621 | - fixed: using wrong functions for ignoring access on Elgg 1.7.x
622 | - fixed: widget titles for Elgg 1.7.5
623 |
624 | 1.0.1 beta (2010-08-27):
625 |
626 | - fixed: header also changes on group widgets (with some plugins)
627 | - fixed: adding non existing widgets
628 | - changed: create user widget timestamps
629 |
630 | 1.0 beta (2010-08-13):
631 |
632 | - replacement of Draggable widgets
--------------------------------------------------------------------------------